前面,我们已经成功创建了一个可执行的着色程序。现在就可使用它来绘制一个最简单的三角形。
一、什么是顶点属性数据?
顶点属性数据也简称顶点数据。例如顶点的坐标,颜色,法线,以及顶点的纹理坐标等,都是顶点属性数据。
顶点属性数据,分为顶点数组和常量顶点属性两种。
二、简单的着色程序
编写OpenGL着色程序所使用的着色语言,一般称为GLSL(OpenGL Shading Language)。它是一种强类型语言,所有变量都必须提前声明,并且具有相应的类型。
(1)顶点着色器
顶点着色器是运行在GPU上的小程序,它是用来处理顶点数据的,图形有多少个顶点,该程序就会执行多少次。
//顶点Shader源码 attribute vec4 vPosition; void main() { gl_Position = vPosition; }
第2行:声明了一个类型为vec4的输入变量,后续OpenGL的绘图函数会将每一个顶点坐标赋给vPosition变量。前面的attribute为属性变量的修饰符,它只能在顶点shader中使用,表示我们声明了一个属性变量。
第3行:定义一个main函数,每一个着色程序都是从它开始执行的。
第5行:将顶点坐标vPosition变量,直接赋给了gl_Position变量。其中gl_Position是一个内置的输出变量,每一个顶点shader必须将变换后的顶点坐标输出到gl_Position变量,以供渲染管线下一步使用。
(2)片段着色器
片段着色器,也叫像素着色器,暂时可以把片段理解成像素,只不过它是带了很多属性的像素。
//片段Shader源码 void main() { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); //红色 }
第4行:gl_FragColor是一个内置的输出变量,表示片段shader最终输出到颜色。
三、向shader发送顶点数组
在绘制三角形之前,需要在OpenGL窗口类的DrawGLScene()函数中,先定义一个三角形的顶点数组:
// 三角形的3个顶点坐标 GLfloat vVertices[] = { 0.0f, 0.5f, 0.0f, //第1个点,依次为xyz -0.5f, -0.5f, 0.0f, //第2个点 0.5f, -0.5f, 0.0f //第3个点 };
(1)获取顶点属性的索引
为了将三角形的顶点坐标输入到顶点着色器的vPosition变量,必选先调用glGetAttribLocation函数,获取vPosition变量对应的顶点属性索引。
GLint glGetAttribLocation(GLuint program, const GLchar *name)
-
program — 着色程序句柄
-
name — 属性变量的名称
glGetAttribLocation函数是用来获得顶点着色器中,属性变量对应的顶点属性索引。如果给定的属性变量名称不存在,或者着色程序句柄无效,将发生错误,返回-1;否则,返回顶点属性的索引。
顶点属性的索引,在着色程序创建成功后就生成了,而且不再会发生改变。因此可以在OpenGL窗口初始化函数中,获取顶点属性的索引:
//获取vPostion属性变量的索引,保存到成员变量m_posLoc m_posLoc = GL30::glGetAttribLocation(m_program.GetHandle(), "vPosition");
成功获得顶点属性的索引之后,就可以借助于它将三角形的顶点数组,赋给顶点着色器中的vPositon变量。
(2)指定顶点属性的数据
OpenGL采用了客户端—服务器(client-server)的体系结构。我们编写的程序就是客户端,它运行在CPU上;图形硬件制造商提供的OpenGL实现就是服务器,它运行在GPU上,而GPU进行图形渲染,只能从显存中读取相关的所有数据。
目前,我们所以定义的顶点数组,它的内存位于应用程序(也就是OpenGL的客户端)空间内。所以必须调用glVertexAttribPointer函数,将这些数据发送到显卡,然后GPU才能使用这些数据进行绘图。
void glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *ptr) void glVertexAttribIPointer(GLuint index, GLint size, GLenum type, GLsizei stride, const void *ptr)
-
index — 顶点属性的索引。值的范围:[0,MAX_VERTEX_ATTRIBS_NUM - 1],其中MAX_VERTEX_ATTRIBS_NUM为OpenGL所支持的顶点属性个数。
-
size — 数组中顶点属性数据的分量个数,可选的值为: 1~4
-
type — 数组中顶点属性数据的数据类型,可选的值有:
-
GL_BYTE
-
GL_UNSIGNED_BYTE
-
GL_SHORT
-
GL_UNSIGNED_SHORT
-
GL_INT
-
GL_UNSIGNED_INT
-
GL_FLOAT
-
GL_FIXED
-
normalized — 表示非浮点数据类型在转换为浮点类型时,是否进行规范化。
-
stride — 顶点数组中每一个顶点属性数据之间的跨距,单位:字节。 如果stride为0,那么每一个顶点属性数据将紧密排列在一起;如果stride大于0,则该值作为获取下一个顶点属性数据的跨距。
-
ptr — 如果使用顶点数组,则ptr为顶点数组的数据缓冲区指针; 如果使用顶点缓冲区对象,则ptr表示该缓冲区内的偏移量。
//2. 为顶点属性 指定 顶点数据 GL30::glVertexAttribPointer(m_posLoc, //顶点属性的索引 3, //顶点坐标有x,y,z三个分量 GL30::GL_FLOAT, //顶点坐标的数据类型为float GL_FALSE, //顶点数组为float类型,无需规范化 3*sizeof(float), //顶点之间的跨距为3个float大小 vVertices); //顶点数组的指针
四、使用顶点数组绘图
出于性能考虑,所有的顶点属性默认都是关闭的。即使三角形的顶点数据已经发送到了显卡,但对于着色程序来说,这些数据依然是不可见的。为了让着色程序能够使用这顶点数据,必须调用glEnableVertexAttribArray函数启用相应的顶点属性:
void glEnableVertexAttribArray(GLuint index);
-
index — 顶点属性的索引
启用了顶点属性之后,才可以调用glDrawArrays函数,使用之前定义的顶点数组绘制三角形
void glDrawArrays(GLenum mode, GLint first, GLsizei count);
-
mode — 绘制图元的方式,可选择的值有:
-
GL_POINTS GL_LINES
-
GL_LINE_STRIP
-
GL_LINE_LOOP
-
GL_TRIANGLES
-
GL_TRIANGLE_STRIP
-
GL_TRIANGLE_FAN
-
first — 顶点数组中起始顶点的索引
-
count — 要绘制的顶点数量
void MCOpenGLWindow::DrawGLScene() { //使用之前设置的颜色,清除OpenGL窗口的背景 GL30::glClear(GL30::GL_COLOR_BUFFER_BIT); // 定义三角形顶点 GLfloat vVertices[] = { 0.0f, 0.5f, 0.0f, -0.5f, -0.5f, 0.0f, 0.5f, -0.5f, 0.0f }; //1. 启用着色程序 m_program.UseProgram(); //2. 为顶点属性 指定 顶点数据 GL30::glVertexAttribPointer(m_posLoc, //顶点属性的索引 3, //顶点坐标有x,y,z三个分量 GL30::GL_FLOAT, //顶点坐标的数据类型为float GL_FALSE, //顶点数组为float类型,无需规范化 3*sizeof(float), //顶点之间的跨距为3个float大小 vVertices); //顶点数组的指针 //3. 启用顶点索引 GL30::glEnableVertexAttribArray (m_posLoc); //4. 使用顶点绘制三角形 GL30::glDrawArrays ( GL30::GL_TRIANGLES, 0, 3 ); }
五、运行结果
源码下载:FirstTriangle