首页 > 代码库 > [原]OpenGL基础教程(四)VBO+纹理绘制四边形

[原]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];        // red127                 out->rgba[pos++] = row_pointers[row][col + 1];    // green128                 out->rgba[pos++] = row_pointers[row][col + 2];    // blue129                 out->rgba[pos++] = row_pointers[row][col + 3];    // alpha130             }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];        // red154                 out->rgba[pos++] = row_pointers[row][col + 1];    // green155                 out->rgba[pos++] = row_pointers[row][col + 2];    // blue156             }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.frag17 #version 33018 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();
}

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

技术分享

仅使用纹理时候的效果:

技术分享

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

技术分享

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