从零开始学习OpenGL(4) 顶点数组

突然写OpenGL的教程了,但其实都是接以前的文章的,只不过没有写一个合集。

本人写系列教程都是断断续续的,无论是JavaFX和OpenGL,虽然都有系列教程,但并不是经常性的更新。有需要的朋友可以选择性的看一下。由于在公司的工作早已转向3D开发,所以以后写OpenGL和u3d相关的内容会偏多一点,感谢大家支持。

从这个周末开始就要休婚假了…18天婚假前面接一个周末后面接一个五一,所以一直到5月4日才开始上班,表示从工作后第一次休这么长时间的假了…


对于OpenGL绘制几何图元,需要调用大量的函数。首先调用一次glBegin(),然后为每个顶点调用一次函数,最后调用一次glEnd(),有时候还会有颜色法线等额外信息,这样一来,每个顶点上还要增加函数调用。这可能会成倍的增加渲染几何体所需要的含数调用数量,在某些时候,可能会影响应用程序的性能。

另外,按照标准方法来绘制3D物体,比如说立方体,会有一些共享顶点,如果按照标准方法来绘制每个顶点必须指定3次,这样一共指定了24个顶点,然而实际上只需要处理8个顶点就够了,这样会产生冗余。

在OpenGL中要解决这个问题,只需要我们使用顶点数组对几何图形进行渲染就行了。

1.启用数组

第一个步骤是调用glEnableClientState(GLenum array)函数激活选择的数组。目前OpenGL中可激活的数组有8个,详情可自行查阅文档。我这节只需要使用GL_VERTEX_ARRAY和GL_COLOR_ARRAY,顶点数组和颜色数组。

 

2.指定数组的数据

我们需要将数据放入数组中,然后通过内存地址进行访问。

指定顶点数组数据:void glVertexPointer(GLint size, GLenum type, GLsizei stride, const GLvoid* pointer) size是每个顶点的坐标数量,必须是2 3和4,type指定了数组中数据的类型,比如GL_SHORT,GL_INT, GL_FLOAT或GL_DOUBLE。stride是连续顶点的字节偏移量,如果顶点紧密相联,则为0;pointer是数组中包含第一个顶点的第一个坐标的内存地址。

指定颜色数组数据:void glColorPointer(GLint size,GLenum type,GLsizei stride, const GLvoid* pointer) 同上。

其他的自行查阅文档。

 

3.渲染

数组中的内容需要发送到图形处理管线进行渲染,在这里有多种方式进行解引用。

void glArrayElement(GLint ith) 获取当前所有已启用数组的第ith个顶点的数据,通常在glBegin()和glEnd()中调用。

示例:

GLfloat colors[] = {
         1.0,0.0,0.0,
         0.0,1.0,0.0,
         0.0,0.0,1.0,
         1.0,1.0,0.0,
         1.0,0.0,1.0,
         0.0,1.0,1.0,
         1.0,0.0,0.0,
         0.0,1.0,0.0
};

GLfloat vertices[] = {
         -1.0,0.0,0.0,
         1.0,0.0,0.0,
         0.0,1.0,0.0,
         0.0,-1.0,0.0,
         -1.0,1.0,0.0,
         -1.0,-1.0,0.0,
         1.0,1.0,0.0,
         1.0,-1.0,0.0
};
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glColorPointer(3, GL_FLOAT, 0, colors);
glVertexPointer(3, GL_FLOAT, 0, vertices);
         
glBegin(GL_QUADS);
glArrayElement(4); // vertices中第五个顶点
glArrayElement(6); // vertices中第七个顶点
glArrayElement(7); // vertices中第八个顶点
glArrayElement(5); // vertices中第六个顶点
glEnd();

上面就是通过glColorPointer()和glVertexPointer()指定顶点数组的数据,然后通过glArrayElement()组合4个顶点用来绘制一个四边形。

运行效果如下:

void glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices) 使用count个元素定义一个几何图元序列,他们的索引值保存在indices数组中。

glDrawElements()的效果差不多相当如下面的代码:

glBegin(mode);
for(int i = 0; i < count; i++)
 glArrayElement(indices[i]);
glEnd();

在上面的例子中,我们可以进行如下修改,达到同样的效果:

GLubyte indices[] = {4,6,7,5}; //定义索引的序列
/* 注释掉
glBegin(GL_QUADS);
glArrayElement(4);
glArrayElement(6);
glArrayElement(7);
glArrayElement(5);
glEnd();*/

glDrawElements(GL_QUADS, 4, GL_UNSIGNED_BYTE, indices);

事实上,这样运行的效果是相同的,但是在代码量上会少很多。当然,很多公司KPI希望代码量越多越好,也会有很多人尽量往多的代码写- -!

 

void glMultiDrawElements(GLenum mode,GLsizei* count,GLenum type,const GLvoid** indices,GLsizei primcount) 将几个DrawElements合并到一个中调用。 count是一个数组,例如GLsizei count[] = {7, 6},表示第一个数组元素列表中有7个顶点。第二个数组元素列表中有6个顶点;mode和type与DrawElements中的参数相同;primcount表示DrawElements函数调用的次数。

相当于如下代码:

for(int i = 0;i < primcount; i++){ if(count[i] > 0)
    glDrawElements(mode, count[i], type, indices[i]);
}

那么,这一节的OpenGL就讲解到这里了。主要是顶点数组的使用,当然,这些函数说到底都是为了减少函数的调用和顶点的冗余,在实际开发中灵活运用还是很有必要的。

发表评论

电子邮件地址不会被公开。