思路


要画多边形,最终可以归纳为画四边形,从三角形到四边形的转变,是一个根本性的转变,一旦会画四边形,其他多边形可以举一反三。

考察接口

由于 OpenGL ES 设计的简洁性,断断是没有 DrawRect 这类接口可用。所有接口都可以在 手册 中查阅。

通过查阅得知,绘制函数只有两个:

void glDrawArrays(GLenum mode,
                GLint first,
                GLsizei count);

glDrawArrays — render primitives from array data

void glDrawElements(GLenum mode,
                    GLsizei count,
                    GLenum type,
                    const GLvoid * indices);

glDrawElements — render primitives from array data

这两个接口的描述是一样的。区别在于,glDrawArrays 的顶点数据是连续存储的,glDrawElements 的顶点数据是可以分离存储,因为它的参数是索引。

目前我们只使用 glDrawArrays。

考察第一个参数,它表示了三种「图元」(Primitive):点、线、三角形。其值有:

1. 点

  • GL_POINTS

2. 线

  • GL_LINES
  • GL_LINE_LOOP
  • GL_LINE_STRIP

3. 三角形

  • GL_TRIANGLES
  • GL_TRIANGLE_STRIP
  • GL_TRIANGLE_FAN

没错,这就是指定你要绘制的是哪种「图元」(Primitive),OpenGL 中所有其他图形都是由这三种组成的。

第二个参数表示从第几个顶点开始,第三个参数表示要绘制的顶点个数。

小结

由于我们要绘制的是四边形,因此排除了点、线两种,考虑到多边形都是由多个三角形组成,所以最终我们选择的参数只在三角形「图元」中。

版本一


首先考虑到的是,添加一个顶点,然后 glDrawArrays 时,第三个参数传入顶点个数改成4。

修改后的代码为:

void Draw ( ESContext *esContext )
{
    UserData *userData = esContext->userData;
    GLfloat vVertices[] = {  0.0f,  0.5f, 0.0f, 
                       -0.5f, -0.5f, 0.0f,
                        0.5f, -0.5f, 0.0f,
                        0.5f, 0.0f, 0.0f};

    // Set the viewport
    glViewport ( 0, 0, esContext->width, esContext->height );

    // Clear the color buffer
    glClear ( GL_COLOR_BUFFER_BIT );

    // Use the program object
    glUseProgram ( userData->programObject );

    // Load the vertex data
    glVertexAttribPointer ( 0, 3, GL_FLOAT, GL_FALSE, 0, vVertices );
    glEnableVertexAttribArray ( 0 );

    glDrawArrays ( GL_TRIANGLES, 0, 4 );
}

运行结果:

结论

可以看到,画出来的仍然是原来的三角形,由此可见,参数 GL_TRIANGLES 只会绘制前三个顶点。

版本二


于是,我们把 GL_TRIANGLES 改成 GL_TRIANGLE_STRIP 试试:

glDrawArrays (GL_TRIANGLE_STRIP, 0, 4);

运行结果:

Shit! 这特么是在逗我?怎么会画出这么个玩意?原因我们后面解释。

版本三


继续修改参数为 GL_TRIANGLE_FAN:

glDrawArrays (GL_TRIANGLE_FAN, 0, 4);

运行结果:

这回好了,这是我们期待的结果。

为什么?


GL_TRIANGLES 只会绘制前三个顶点,这个业已知悉。现在的问题是 GL_TRIANGLE_STRIP 和 GL_TRIANGLE_FAN 的区别。

GL_TRIANGLE_STRIP

考察 GL_TRIANGLE_STRIP 绘制出来的形状,不难得知,它的绘制工序是这样的:

假设我们上面的四个顶点分别是 V~1~、V~2~、V~3~、V~4~。

首先,取 V~1~、V~2~、V~3~ 绘制第一个三角形 T~123~。

其次,取 V3、V2、V~4~ 绘制第二个三角形 T~324~。(注意顺序)

因为已经到达最后一个顶点 V~4~,因此绘制完毕。结果形状就如版本二所示。

归纳一下,假设有顶点 V~1~、V~2~、V~3~、…… 、V~N~,

总计绘制的三角形个数为 N-2。

绘制的顺序,根据顶点的奇偶性来决定:

偶数环绕法则:

T = [n-1 n-2 n]

奇数环绕法则:

T = [n-2 n-1 n]

【2015/06/25更新】

 

GL_TRIANGLE_FAN

假设我们上面的四个顶点分别是 V~1~、V~2~、V~3~、V~4~。

首先,取 V~1~、V~2~、V~3~ 绘制第一个三角形 T~123~。

其次,取 V~1~、V~3~、V~4~ 绘制第二个三角形 T~134~。

因为已经到达最后一个顶点 V~4~,因此绘制完毕。结果形状就如版本三所示。

归纳一下,假设有顶点 V~1~、V~2~、V~3~、…… 、V~N~,其绘制的三角形将是:

T~123~、T~134~、T~145~、……、T~1\ N-1\ N~。

总计绘制的三角形个数为 N-2。

我们可以从 GL_TRIANGLE_FAN 的名字上了解,FAN 应该是 fan-shaped 扇形的缩写,因为这个绘制的流程就像在画扇形。

小结


到此,绘制多边形已经不在话下。其核心是:

  • 添加顶点
  • 使用 GL_TRIANGLE_FAN

但有个小问题产生:

  • 如果上面的示例改成用线「图元」参数,会发生什么?

相信对比三角形的三个参数,线的三个参数也容易理解了。这里不再赘述。