[原]OpenGL基础教程(四)VBO+纹理绘制四边形

工程下载地址:http://pan.baidu.com/s/1ntr7NHv  提取码:yf1h

一、本文牵扯知识点梳理:

(1)VBO

(2)纹理

(3)libpng(加载png)

(4)shader

1、VBO(Vertex Buffer Objec)

//顶点坐标
   glEnableVertexAttribArray(0);//激活顶点属性数组
  glGenBuffers(1, &VertexID);创建句柄
   glBindBuffer(GL_ARRAY_BUFFER, VertexID);设置句柄类型
   glBufferData(GL_ARRAY_BUFFER, sizeof(positionData),positionData,GL_STATIC_DRAW);上传数据
   glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, 0, 0);Set up our vertex attributes pointer 具体给shader传输数据
   
   //顶点uv
  glEnableVertexAttribArray(1);//顶点uv
  glGenBuffers(1, &UVID);
   glBindBuffer(GL_ARRAY_BUFFER,UVID);
   glBufferData(GL_ARRAY_BUFFER,sizeof(uvData),uvData,GL_STATIC_DRAW);
   glVertexAttribPointer( 1, 2, GL_FLOAT, GL_FALSE, 0, 0 );
   
   //顶点颜色
   glEnableVertexAttribArray(2);//顶点color
   glGenBuffers(1, &ColorID);
   glBindBuffer(GL_ARRAY_BUFFER,ColorID);
   glBufferData(GL_ARRAY_BUFFER,sizeof(colorData),colorData,GL_STATIC_DRAW);
   glVertexAttribPointer( 2, 3, GL_FLOAT, GL_FALSE, 0, 0 );
   
   //顶点索引
   glGenBuffers(1, &IndexID);
   glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,IndexID);
   glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indexs),indexs,GL_STATIC_DRAW);

2、纹理使用

 glGenTextures(1,&textureID);
    glBindTexture(GL_TEXTURE_2D,textureID); //将纹理绑定到名字
    glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,w,h,0,GL_RGBA,GL_UNSIGNED_BYTE,picdata->rgba); //w  h 纹理的宽高 picdata->rgba纹理的数据
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);//纹理的过滤模式
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);  

  纹理坐标:

3、libpng 开源的png加载库,可以将png转化为byte数组;下载地址  备注libpng依赖于zlib库,需要下载两个库并将它们放在相同目录

读取png图片代码:

 1 typedef struct _pic_data pic_data;
  2 struct _pic_data
  3 {
  4     int width, height; /* 尺寸 */
  5     int bit_depth;  /* 位深 */
  6     int flag;   /* 一个标志,表示是否有alpha通道 */
  7 
  8     unsigned char *rgba; /* 图片数组 */
  9 };
 10 int detect_png(char *filepath,  pic_data* out)
 11     /* 用于解码png图片 */
 12 {
 13     unsigned char header[8];     //8
 14     int k;   //用于循环
 15     int width, height; //记录图片到宽和高
 16     png_byte color_type; //图片到类型(可能会用在是否是开启来通道)
 17     png_byte bit_depth; //字节深度
 18 
 19     png_structp png_ptr; //图片
 20     png_infop info_ptr; //图片的信息
 21     int number_of_passes; //隔行扫描
 22     png_bytep * row_pointers;//图片的数据内容
 23     int row,col,pos,channels,size;  //用于改变png像素排列的问题。
 24 
 25     FILE *fp=fopen(filepath,"rb");//以只读形式打开文件名为file_name的文件
 26     if(!fp)//做出相应可能的错误处理
 27     {
 28         fclose(fp);//关闭打开的文件!给出默认贴图
 29         return 0;//此处应该调用一个生成默认贴图返回ID的函数
 30     }
 31     //读取文件头判断是否所png图片.不是则做出相应处理
 32     fread(header, 1, 8, fp);
 33     if(png_sig_cmp(header,0,8))
 34     {
 35         fclose(fp);
 36         return 0; //每个错误处理都是一样的!这样报错之后锁定就要花点小时间来!
 37     }
 38 
 39     //根据libpng的libpng-manual.txt的说明使用文档 接下来必须初始化png_structp 和 png_infop
 40     png_ptr=png_create_read_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL); //后三个是绑定错误以及警告的函数这里设置为空
 41     if(!png_ptr)//做出相应到初始化失败的处理
 42     { 
 43         fclose(fp);
 44         return 0;
 45     }
 46     //根据初始化的png_ptr初始化png_infop
 47     info_ptr=png_create_info_struct(png_ptr);
 48 
 49     if(!info_ptr)
 50     {
 51         //初始化失败以后销毁png_structp
 52         png_destroy_read_struct(&png_ptr,(png_infopp)NULL,(png_infopp)NULL);
 53         fclose(fp);
 54         return 0;
 55     }
 56 
 57 
 58     //老老实实按照libpng给到的说明稳定步骤来  错误处理!
 59     if (setjmp(png_jmpbuf(png_ptr)))
 60 
 61     {
 62         //释放占用的内存!然后关闭文件返回一个贴图ID此处应该调用一个生成默认贴图返回ID的函数
 63 
 64         png_destroy_read_struct(&png_ptr,(png_infopp)NULL,(png_infopp)NULL);
 65 
 66         fclose(fp);
 67 
 68         return 0;
 69 
 70     }
 71     //你需要确保是通过2进制打开的文件。通过i/o定制函数png_init_io
 72     png_init_io(png_ptr,fp);
 73     //似乎是说要告诉libpng文件从第几个开始missing
 74     png_set_sig_bytes(png_ptr, 8);
 75     //如果你只想简单的操作你现在可以实际读取图片信息了!
 76     png_read_info(png_ptr, info_ptr);
 77     //获得图片到信息 width height 颜色类型  字节深度
 78     channels      = png_get_channels(png_ptr, info_ptr); /*获取通道数*/
 79     out->width = png_get_image_width(png_ptr, info_ptr);
 80     out->height = png_get_image_height(png_ptr, info_ptr);
 81     color_type = png_get_color_type(png_ptr, info_ptr);
 82     //如果图片带有alpha通道就需要
 83     // if (color_type == PNG_COLOR_TYPE_RGB_ALPHA)
 84 
 85     // png_set_swap_alpha(png_ptr);
 86     out->bit_depth = png_get_bit_depth(png_ptr, info_ptr);
 87     //隔行扫描图片  这个必须要调用才能进行
 88     number_of_passes = png_set_interlace_handling(png_ptr);
 89     //将读取到的信息更新到info_ptr
 90     png_read_update_info(png_ptr, info_ptr);
 91 
 92     //读文件
 93     if (setjmp(png_jmpbuf(png_ptr))){
 94         fclose(fp);
 95         return 0;
 96     }
 97 
 98     row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * out->height);
 99 
100     size = out->height*out->width;
101     //通过扫描流里面的每一行将得到的数据赋值给动态数组       
102     for (k=0; k<out->height; k++)
103         //row_pointers[k] = (png_byte*) malloc(png_get_rowbytes(png_ptr,info_ptr));
104             row_pointers[k] = (png_bytep)png_malloc(png_ptr, png_get_rowbytes(png_ptr,
105             info_ptr));
106     //由于png他的像素是由 左-右-从顶到底 而贴图需要的像素都是从左-右-底到顶的所以在这里需要把像素内容进行一个从新排列
107     //读图片
108     png_read_image(png_ptr, row_pointers);
109     if(channels == 4 || color_type == PNG_COLOR_TYPE_RGB_ALPHA)
110     {/*如果是RGB+alpha通道,或者RGB+其它字节*/
111 
112         size *= 4;//*sizeof(unsigned char); /* 每个像素点占3个字节内存 */
113         pos = size - (4 * out->width);
114         out->flag = HAVE_ALPHA;    /* 标记 */
115         out->rgba = (unsigned char*) malloc(size);
116         if(out->rgba == NULL)
117         {/* 如果分配内存失败 */
118             fclose(fp);
119             puts("错误(png):无法分配足够的内存供存储数据!");
120             return 1;
121         }
122         for( row = 0; row < out->height; row++)
123         {
124             for( col = 0; col < (4 * out->width); col += 4)
125             {
126                 out->rgba[pos++] = row_pointers[row][col];        // red
127                 out->rgba[pos++] = row_pointers[row][col + 1];    // green
128                 out->rgba[pos++] = row_pointers[row][col + 2];    // blue
129                 out->rgba[pos++] = row_pointers[row][col + 3];    // alpha
130             }
131             pos=pos - 4*out->width*2;
132         }
133     }
134     else if(channels == 3 || color_type == PNG_COLOR_TYPE_RGB)
135     {/* 如果是RGB通道 */
136         size *= (3*sizeof(unsigned char)); /* 每个像素点占3个字节内存 */
137         pos = size - (3* out->width);
138         out->flag = NO_ALPHA;    /* 标记 */
139         out->rgba = (unsigned char*) malloc(size);
140 
141 
142         if(out->rgba == NULL)
143         {/* 如果分配内存失败 */
144             fclose(fp);
145             puts("错误(png):无法分配足够的内存供存储数据!");
146             return 1;
147         }
148 
149         for( row = 0; row < height; row++)
150         {
151             for( col = 0; col < (3 * width); col += 3)
152             {
153                 out->rgba[pos++] = row_pointers[row][col];        // red
154                 out->rgba[pos++] = row_pointers[row][col + 1];    // green
155                 out->rgba[pos++] = row_pointers[row][col + 2];    // blue
156             }
157             pos=pos - 3*out->width*2;
158         }
159     }
160     else return 1;
161     /* 撤销数据占用的内存 */
162     png_destroy_read_struct(&png_ptr, &info_ptr, 0);
163     return 0;
164 }
View Code

4、shader使用:

(1)创建着色器对象glCreateShader(type)  顶点和片段着色器对象

(2)把着色器代码和对象关联起来 glShaderSource(。。。)

(3)着色器源代码编译为目标代码 glComplieShader(....)

(4)检查是否编译成功glGetShaderiv(.....)

(5)创建着色器程序 glCreateProgram()

(6)将着色器对象链接到所创建的程序中 glAttachShader(....)

(7)将这些对象链接成一个可执行程序 glLinkProgram(.....)

(8)验证连接成功glGetProgramiv(...)

(9)使用着色器进行顶点或片段处理glUseProgram(.....);

(10)glBindAttribLocation(着色器程序对象, 对应glVertexAttribPointer里面绑定的值, 对应shader里面的属性); 顶点坐标,uv,颜色对应到shader属性 必须在glLinkProgram之前使用

(11)GLuint samplerA = glGetUniformLocation(programHandle,"gSampler"); glUniform1i(samplerA, 0);  在shader中为uniform gSampler纹理赋值

(12)gWVPLocation = glGetUniformLocation(programHandle,"gWVP"); 同上 设置正交投影矩阵

(13)正交投影矩阵赋值(在VBO+shader中gluOrtho2D不好使,不知道是不是自己使用不当)

float l= -100.0f,r=100.0f,t=100.0f,b=-100.0f,n=-100.0f,f=100.0f; // l:left r:right t:top b:bottom n:near f:far
float mat[16] = {2/(r-l),0,0,-(r+l)/(r-l),  //正交投影矩阵  http://blog.csdn.net/popy007/article/details/4126809
    0,2/(t-b),0,-(t+b)/(t-b),
    0,0,2/(n-f),(n+f)/(n-f),
    0,0,0,1};

glUniformMatrix4fv(gWVPLocation, 1, GL_FALSE, (const GLfloat*)mat);

(14)顶点和片段源码:

 1 //basic.vert
 2 #version 330
 3 in vec3 Position;
 4 in vec3 Color ;
 5 in vec2 TexCoord ;
 6 uniform mat4 gWVP;
 7 out vec3 out_color;
 8 out vec2 out_texcoord;
 9 void main()
10 {
11    gl_Position = gWVP*vec4(Position, 1.0);
12    out_color = Color;
13    out_texcoord = TexCoord;
14 }
15 
16 //basic.frag
17 #version 330
18 out vec4 FragColor;
19 in vec3 out_color;
20 in vec2 out_texcoord;
21 uniform sampler2D gSampler;
22 void main()
23 {
24     vec4 c= texture2D(gSampler,out_texcoord);
25   FragColor = vec4(out_color+c.rgb, 1.0);
26 }

简单串一下流程:

void init()

{

  initVBO();

  initShader("basic.vert","basic.frag");

   glUniformMatrix4fv(gWVPLocation, 1, GL_FALSE, (const GLfloat*)mat);//正交投影矩阵不发生变化 所以可以放在初始化里面

  initTexture();

}

void display()
{
    glClear(GL_COLOR_BUFFER_BIT);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,IndexID);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, textureID);
    glDrawElements(GL_TRIANGLES,6, GL_UNSIGNED_INT, 0);
    glutSwapBuffers();
}

 效果图:只使用颜色时候的效果:

仅使用纹理时候的效果:

纹理和颜色都使用的效果:

posted @ 2015-01-06 23:22  U_探索  阅读(2603)  评论(0编辑  收藏  举报