录十六

持之以恒

绘制第一个三角形

前面,我们已经成功创建了一个可执行的着色程序。现在就可使用它来绘制一个最简单的三角形。

一、什么是顶点属性数据?

顶点属性数据也简称顶点数据。例如顶点的坐标,颜色,法线,以及顶点的纹理坐标等,都是顶点属性数据。

顶点属性数据,分为顶点数组常量顶点属性两种。

二、简单的着色程序

编写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 );
}

五、运行结果

无标题.jpg

源码下载:FirstTriangle


发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

Copyright © 1999-2019, lu16.com, All Rights Reserved