1. 前言

之前已经把纹理的渲染给弄出来了,但是又遇到一个新的问题,那就是图元装配,比如说我已经把图片给显示出来了,但是呢,并没有做到让它显示到具体的位置,而跟这个位置相关的则需要靠图元装配。

图元装配发生在顶点着色器处理图元顶点之后,在这一阶段,执行裁剪、透视分割和视口变换操作。而光栅化是将图元转化为一组二维片段的过程,这些片段由片段着色器处理,代表可以在屏幕上绘制的像素,发生在图元装配之后。

2. 图元

可以绘制图元的包括下面的函数:

  • glDrawArrays
  • glDrawElements
  • glDrawRangeElements
  • glDrawArraysInstanced
  • glDrawElementsInstanced

其中这些函数可以绘制三角形、直线和点精灵。

2.1 三角形

  1. GL_TRIANGLES:绘制一系列单独的三角形;
  2. GL_TRIANGLE_STRIP:绘制一系列相互连接的三角形;
  3. GL_TRIANGLE_FAN:绘制一系列相连的三角形;

2.2 直线

  1. GL_LINES:绘制一系列不相连的线段;
  2. GL_LINE_STRIP:绘制一系列相连的线段;
  3. GL_LINE_LOOP:同上,类似;

2.3 点精灵

支持的图元是GL_POINTS,点精灵对指定的每个顶点绘制,点精灵通常用于将粒子效果当作点而非正方形绘制,从而实现高效渲染。点精灵是指定位置和半径的屏幕对齐的正方形,位置描述正方形的中心,半径用于计算描述点精灵的正方形的4个坐标,。

2.4 绘制图元

OpenGL ES中有5个绘制图元的API调用,分别是文章开头谈到那5个,这里需要注意的是,前面3个是常规的非实例化绘图,后面两个才是实例化绘图。

2.4.1 glDrawArrays

glDrawArrays用元素索引为first到first+count-1的元素指定的顶点绘制mode指定的图元。需要注意的是,这里的count并不意味着越大绘制的就越多,这里仅仅是指顶点的个数,还需要看mode,是单独的还是相连的。

2.4.2 区别

如果你有一个由一系列顺序元素索引描述的图元,且几何形状的顶点不共享,则glDrawArrays很好用,但是,游戏或者其他3D应用程序使用的典型对象由多个三角形网格组成,其中的元素索引可能不一定按照顺序,顶点通畅在网格的三角形之间共享,则使用glDrawElements会比较好,这有很多原因,例如说,由于顶点重用,顶点属性数据的尺寸也较小,这也导致较小的内存占用和内存带宽需求。

比如画一个由2个3角形组成的正方形,左上角坐标是l,t,右下角坐标是r,b
使用glDrawArrays绘制时,画2个三角形,需要这样传:
(l,t),(r,t),(l,b)
(r,t),(r,b),(l,b)
而用glDrawElements画的话可以这样
float coord[4][2]={{l,t},{r,t},{r,b},{l,b}};
绘制时:
0,1,3
1,2,3

glDrawArrays传输或指定的数据是最终的真实数据,在绘制时效能更好
而glDrawElements指定的是真实数据的调用索引,在内存/显存占用上更节省。

2.4.3 几何形状实例化

几何形状实例化很高效,可以用一次API调用多次渲染具有不同属性(例如不同的变换矩阵、颜色或者大小)的一个对象。这一功能在渲染大量类似对象时很有用,例如对人群的渲染,这将会降低了向OpenGL ES引擎发送许多API调用的CPU开销处理

glDrawArraysInstanced

glDrawElementsInstanced

那如果我们想访问其中的实例对象时怎么办呢?有两种方法,分别是使用函数glVertexAttribDivisor,还有就是使用内建输入变量gl_InstanceID作为顶点着色器中的缓冲区索引,以访问每个实例的数据,不过前提是你使用的是几何形状实例化API调用,比如你使用的是非实例化绘图调用,那么gl_InstanceID将不会保存当前图元实例的索引。

2.5 图元重启

使用图元重启后,可以在一次绘图调用中渲染多个不相连的图元,之前我们绘制不相连的也就是使用glDrawArrays,这对于降低绘图API调用的开销是不利的。

我们可以通过在索引列表中插入一个特殊索引来重启一个用于索引绘图调用(如glDrawElements、glDrawElementsInstanced 或 glDrawRangeElements)的图元,这个特殊索引是该索引类型的最大可能索引,(例如,索引类型为GL_UNSIGNED_BYTE为255,而GL_UNSIGNED_SHORT则为65535)。

注意,可以使用代码去启用或者图元重启。

glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX);

glDisable(GL_PRIMITIVE_RESTART_FIXED_INDEX);

3. 图元装配

前面只是讲到图元的相关知识,从这里开始便进行到真正的图元装配。

在图元装配阶段,顶点着色器输出会依次经历:裁剪,透视分割,视口变换,才会到光栅化阶段。

(不得不说,还是OpenGL 编程指南讲得更详细一些。。。)

04-14 13:40