OpenGLES-03 使用索引绘制矩形

2018-05-07 16:01:02 浏览数 (1)

这篇文章我们同样借助上篇文章《OpenGLES-02 绘制基本图元(点、线、三角形)》的代码,使用另外一种画法来绘制一个矩形。

修改render方法如下:

代码语言:javascript复制
-(void)render
{
    //设置清屏颜色,默认是黑色,如果你的运行结果是黑色,问题就可能在这儿
    glClearColor(0.3, 0.5, 0.8, 1.0);
    /*
    glClear指定清除的buffer
    共可设置三个选项GL_COLOR_BUFFER_BIT,GL_DEPTH_BUFFER_BIT和GL_STENCIL_BUFFER_BIT
    也可组合如:glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    这里我们只用了color buffer,所以只需清除GL_COLOR_BUFFER_BIT
     */
    glClear(GL_COLOR_BUFFER_BIT);
    
    // Setup viewport
    glViewport(0, 0, self.frame.size.width, self.frame.size.height);
    
    GLfloat vertices[] = {
        // 第一个三角形
        0.5f, 0.5f, 0.0f,   // 右上角
        0.5f, -0.5f, 0.0f,  // 右下角
        -0.5f, 0.5f, 0.0f,  // 左上角
        // 第二个三角形
        0.5f, -0.5f, 0.0f,  // 右下角
        -0.5f, -0.5f, 0.0f, // 左下角
        -0.5f, 0.5f, 0.0f   // 左上角
    };
    
    glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE, 0, vertices);
    glEnableVertexAttribArray(_positionSlot);
    
    glDrawArrays(GL_TRIANGLES, 0, 6);
    [_context presentRenderbuffer:_renderBuffer];
}

运行结果.png

如上代码所示,我们绘制的矩形是由2个三角形组成的,一个三角形3个顶点,共使用了6个顶点,其中第2个顶点与第4个顶点相同(0.5,-0.5,0.0),第3个顶点与第6个顶点相同(-0.5,0.5,0.0)。 其实对于矩形来说,它只有4个而不是6个顶点,绘制这个矩形,我们指定了右下角和左上角两次,这样就产生了50%的额外开销。还好我们这会儿只要画一个矩形,当我们要画成千上万个矩形或者别的多边形的时候,这样的绘制方法产生的额外消耗会更多从而产生一大堆浪费。 更好的解决方案是只储存不同的顶点,并设定绘制这些顶点的顺序。这样子我们只要储存4个顶点就能绘制矩形了,之后只要指定绘制的顺序就行了。还好OpenGL有这样的方式:

代码语言:javascript复制
glDrawElements();

glDrawElements 函数的原型为:glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices);

第一个参数 mode 为描绘图元的模式,其有效值为:GL_POINTS, GL_LINES, GL_LINE_STRIP,  GL_LINE_LOOP,  GL_TRIANGLES,  GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN。这些模式具体含义下面有介绍。

第二个参数 count 为顶点索引的个数也就是,type 是指顶点索引的数据类型,因为索引始终是正值,索引这里必须是无符号型的非浮点类型,因此只能是 GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT, GL_UNSIGNED_INT 之一,为了减少内存的消耗,尽量使用最小规格的类型如 GL_UNSIGNED_BYTE。

第三个参数 indices 是存放顶点索引的数组。(indices 是 index 的复数形式,3D 里面很多单词的复数都挺特别的。)

索引缓冲对象(简称EBO)的工作方式正是这样的。和顶点缓冲对象一样,EBO也是一个缓冲,它专门储存索引,OpenGL调用这些顶点的索引来决定该绘制哪个顶点。所谓的索引绘制(Indexed Drawing)正是我们问题的解决方案。首先,我们先要定义(独一无二的)顶点,和绘制出矩形所需的索引:

代码语言:javascript复制
GLfloat vertices[] = {
    0.5f, 0.5f, 0.0f,   // 右上角
    0.5f, -0.5f, 0.0f,  // 右下角
    -0.5f, -0.5f, 0.0f, // 左下角
    -0.5f, 0.5f, 0.0f   // 左上角
};

GLubyte indices[] = { // 注意索引从0开始! 
    0, 1, 3, // 第一个三角形
    1, 2, 3  // 第二个三角形
};

然后整个render函数的代码该是这样的:

代码语言:javascript复制
-(void)render
{
    //设置清屏颜色,默认是黑色,如果你的运行结果是黑色,问题就可能在这儿
    glClearColor(0.3, 0.5, 0.8, 1.0);
    /*
    glClear指定清除的buffer
    共可设置三个选项GL_COLOR_BUFFER_BIT,GL_DEPTH_BUFFER_BIT和GL_STENCIL_BUFFER_BIT
    也可组合如:glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    这里我们只用了color buffer,所以只需清除GL_COLOR_BUFFER_BIT
     */
    glClear(GL_COLOR_BUFFER_BIT);
    
    // Setup viewport
    glViewport(0, 0, self.frame.size.width, self.frame.size.height);
 
    GLfloat vertices[] = {
        0.5f, 0.5f, 0.0f,   // 右上角
        0.5f, -0.5f, 0.0f,  // 右下角
        -0.5f, -0.5f, 0.0f, // 左下角
        -0.5f, 0.5f, 0.0f   // 左上角
    };
    
    GLubyte indices[] = { // 注意索引从0开始!
        0, 1, 3, // 第一个三角形
        1, 2, 3  // 第二个三角形
    };
    
    glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE, 0, vertices);
    glEnableVertexAttribArray(_positionSlot);
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, indices);  //6表示有6个索引数据,可以使用sizeof(indices)/sizeof(GLubyte)来确定
    
    [_context presentRenderbuffer:_renderBuffer];
}

两种方式的运行结果是一样的,我们可以发现,运行图中的矩形是个长方形,而我们给的坐标,照理来讲应该是正方形的,这是因为屏幕的宽高比不同,对应的openGL坐标x、y坐标也不一样,后面我们会讲到投影矩阵,投影矩阵能修复这个问题。

所有教程代码在此 : https://github.com/qingmomo/iOS-OpenGLES-

0 人点赞