OpenGL9-彩色立方体

311 阅读4分钟
01、为立方体每个表面绘制不同的颜色
  • 之前绘制的图形都是同一个颜色值,且是在glsl代码中是写好的固定值,在绘制出的同一颜色立方体展示效果也特别不理想。
  • 今天来为立方体每个表面填充一种颜色值,这块主要涉及到两块知识点,一个是顶点数据的组织方式,还有一个是颜色值如何传递给片元着色器?
  • 最终实现的立方体效果如下:

02、顶点数据组织方式
  • 在讲解ebo章节中,我们是以顶点为粒度传递数据的,只需要传递8个顶点数据,现在要使得同一个面绘制一种颜色值,就需要以一个表面为单位进行传值。
  • 例如想把立方体的前表面绘制成蓝色,前表面由顶点v0,v1,v2,v3组成,那么就需要将这四个顶点都指定为蓝色
  • 并且需要使用两个数组,分别表示顶点坐标数据,和顶点颜色数据,两份数据都通过vbo传递给顶点着色器

  • 代码中数据组织
   // 6个面24个顶点
   float vPositions[72] = {
       1.0, 1.0, 1.0,  -1.0, 1.0, 1.0,  -1.0,-1.0, 1.0,   1.0,-1.0, 1.0,  // v0-v1-v2-v3 front
       1.0, 1.0, 1.0,   1.0,-1.0, 1.0,   1.0,-1.0,-1.0,   1.0, 1.0,-1.0,  // v0-v3-v4-v5 right
       1.0, 1.0, 1.0,   1.0, 1.0,-1.0,  -1.0, 1.0,-1.0,  -1.0, 1.0, 1.0,  // v0-v5-v6-v1 up
       -1.0, 1.0, 1.0,  -1.0, 1.0,-1.0,  -1.0,-1.0,-1.0,  -1.0,-1.0, 1.0,  // v1-v6-v7-v2 left
       -1.0,-1.0,-1.0,   1.0,-1.0,-1.0,   1.0,-1.0, 1.0,  -1.0,-1.0, 1.0,  // v7-v4-v3-v2 down
       1.0,-1.0,-1.0,  -1.0,-1.0,-1.0,  -1.0, 1.0,-1.0,   1.0, 1.0,-1.0   // v4-v7-v6-v5 back
   };
   
   // 6个面的颜色
   float colors[72] = {
       0.4, 0.4, 1.0,  0.4, 0.4, 1.0,  0.4, 0.4, 1.0,  0.4, 0.4, 1.0,  // v0-v1-v2-v3 front(blue)
       0.4, 1.0, 0.4,  0.4, 1.0, 0.4,  0.4, 1.0, 0.4,  0.4, 1.0, 0.4,  // v0-v3-v4-v5 right(green)
       1.0, 0.4, 0.4,  1.0, 0.4, 0.4,  1.0, 0.4, 0.4,  1.0, 0.4, 0.4,  // v0-v5-v6-v1 up(red)
       1.0, 1.0, 0.4,  1.0, 1.0, 0.4,  1.0, 1.0, 0.4,  1.0, 1.0, 0.4,  // v1-v6-v7-v2 left
       1.0, 1.0, 1.0,  1.0, 1.0, 1.0,  1.0, 1.0, 1.0,  1.0, 1.0, 1.0,  // v7-v4-v3-v2 down
       0.4, 1.0, 1.0,  0.4, 1.0, 1.0,  0.4, 1.0, 1.0,  0.4, 1.0, 1.0   // v4-v7-v6-v5 back
   };
   
   // 顶点索引
   int indices[36] = {
       0, 1, 2,   0, 2, 3,   // front
       4, 5, 6,   4, 6, 7,   // right
       8, 9,10,   8,10,11,   // up
       12,13,14,  12,14,15,   // left
       16,17,18,  16,18,19,   // down
       20,21,22,  20,22,23     // back
   };
   
   // 创建vao,并绑定
   glGenVertexArrays(1, vao);
   glBindVertexArray(vao[0]);
   
   // 创建顶点缓冲对象vbo,有两块内存
   glGenBuffers(2,vbo);
   // 绑定缓冲区
   glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
   // 将数据填充到缓冲区对象中
   glBufferData(GL_ARRAY_BUFFER,sizeof(vPositions),vPositions,GL_STATIC_DRAW);
   
   // 颜色数据保存在顶点缓冲区数组的第二块内存区
   glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
   glBufferData(GL_ARRAY_BUFFER,sizeof(colors),colors,GL_STATIC_DRAW);
   
   // ebo
   glGenBuffers(1,ebo);
   glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo[0]);
   glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(indices),indices,GL_STATIC_DRAW);
}
03、varying变量
  • 还有一个问题,我们是通过vbo将颜色数据传递给了顶点着色器,但是颜色值最终是要在是片元着色器上进行使用,将已经传递到顶点着色器的数据最后交给片元着色器进行使用,可通过varying变量实现。
  • varying变量意思是“变化的”数据,使用的时候,在顶点着色器代码中,通过in修饰符接受顶点数据传入的颜色值(in vec3 a_Color;),并定义一个out修饰符定义的颜色值变量(out vec4 v_colors)
  • out修饰符表示这个变量最终会将数据内容进行输出到渲染管道的下一阶段,也就是将颜色值传递到片元着色器中
  • 在片元着色器代码中,同样定义相同变量v_colors用于接收数据,他使用的是in作为修饰符表示接收数据(in vec4 v_colors;)
  • glsl代码
   const char *vertex_shader =
   "#version 410          \n"
   "layout(location=0) in vec3 a_Position;  \n"
   "layout(location=1) in vec3 a_Color;  \n"   // 通过vbo传递颜色值
   "uniform mat4 u_ModelMatirx; \n"   // 模型矩阵
   "uniform mat4 u_ViewMatirx; \n"     // 视图矩阵
   "uniform mat4 u_ProjMatirx; \n"     // 投影矩阵
   "out vec4 v_colors; \n"
   " void main() {"
   "  gl_Position = u_ProjMatirx * u_ViewMatirx * u_ModelMatirx * vec4(a_Position, 1.0); \n"
   "  v_colors = vec4(a_Color.x, a_Color.y, a_Color.z, 1.0); \n"
   "}";
   
   const char *fragment_shader =
   "#version 410      \n"
   "in vec4 v_colors; \n"
   "out vec4 color;    \n"                 // color是固定值,不可改动
   "void main(void){  \n"
   "  color = v_colors; \n"
   "} \n";
-   绘制的时候,将vbo中的颜色值,分配给着色器变量a_Color,并进行绑定激活
   glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
   // 颜色数据保存位置在vbo[1]中,通过glVertexAttribPointer方法将该缓存数据分配给着色器代码
   // 中定义的a_Color变量(对应layout(location=1)),所以该方法的第一个参数为1
   glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float), 0);
   glEnableVertexAttribArray(1);
04、glVertexAttribPointer方法
glVertexAttribPointer(
                    GLuint index,
                    GLint size, 
                    GLenum type,
                    GLboolean normalized, 
                    GLsizei stride, 
                   const void *pointer);
  • 解析:vbo缓冲区中的数据使用,glVertexAttribPointer方法是将缓冲区中的数据具体分配给顶点着色器中的变量,其中
    • GLuint index:指定了我们想将顶点数据分配给到的具体变量,其值是int类型,对应着色器代码中layout(location = 1)位置值(Location)的内容,例如上面我们传入的顶点颜色值使用的就是1
    • GLint size:指定顶点属性的大小,顶点属性是一个vec3,它由3个值组成,所以大小是3
    • GLenum type:指定数据的类型,这里是GL_FLOAT(GLSL中vec*都是由浮点数值组成的)
    • GLboolean normalized:定义我们是否希望数据被标准化(Normalize),如果我们设置为GL_TRUE,所有数据都会被映射到0(对于有符号型signed数据是-1)到1之间。我们把它设置为GL_FALSE
    • GLsizei stride:步长(Stride),它告诉我们在连续的顶点属性组之间的间隔。由于下个组位置数据在3个float之后,我们把步长设置为3 * sizeof(float)
    • const void *pointer:数据指针,如果一个顶点数据中包含了多种类型的数据,每种类型的数据需要拆分,在获取的时候,则通过该参数进行数据偏移。

项目源码: