(七)图元装配和光栅化
图元
图元由一组表示顶点位置的顶点描述。其他如颜色、纹理坐标和几何法线等信息也作为通用属性与每个顶点关联。
OpenGL ES 3.0可以绘制如下图元:三角形、直线和点精灵。
三角形
三角形是最常用的图元。OpenGL ES 3.0支持的三角形图元GL_TRIANGLES、GL_TRIANGLE_STRIP和GL_TRIANGLE_FAN。
三角形图元类型.png
GL_TRIANGLES用于绘制单独三角形,可以绘制n/3个。
GL_TRIANGLE_STRIP和GL_TRIANGLE_FAN用于绘制一系列相连的三角形,可以绘制n-2个。
上述n代表glDraw** API中Count指定的索引。
直线
OpenGL ES 3.0支持的直线图元GL_LINES、GL_LINE_STRIP和
GL_LINE_LOOP。
直线图元类型.png
GL_LINES用于绘制一系列不相连的线段,可以绘制n/2条。
GL_LINE_STRIP用于绘制相连的线段,可以绘制n-1条。
GL_LINE_LOOP用于绘制收尾相连的线段,可以绘制n条。
上述n代表glDraw** API中Count指定的索引。
点精灵
OpenGL ES 3.0支持的点精灵图元GL_POINTS。
gl_PointSize是可用于在顶点着色器中输出点半径的内建变量。
gl_PointCoord是只能在渲染图元为点精灵时用于片元着色器内部的内建变量。
绘制图元
OpenGL ES中有5个绘制图元的API调用:glDrawArrays、
glDrawElements、glDrawRangeElements、glDrawArraysInstanced和glDrawElementsInstanced。
void glDrawArrays( GLenum mode, // 图元类型,共7种
GLint first, // 指定启用的顶点数组的起始顶点索引
GLsizei count) // 指定要绘制的顶点数量
// ===============================
void glDrawElements( GLenum mode, // 图元类型,共7种
GLsizei count, // 指定要绘制的顶点数量
GLenum type, // 指定indices中保存的元素索引类型
const GLvoid *indices) // 指定元素索引存储位置的指针
// ===============================
void glDrawRangeElements(GLenum mode, // 图元类型,共7种
GLuint start, // 指定indices中的最小数组索引
GLuint end, // 指定indices中的最大数组索引
GLsizei count, // 指定要绘制的顶点数量
GLenum type, // 指定indices中保存的元素索引类型
const GLvoid *indices) // 指定元素索引存储位置的指针
使用哪一个函数绘制应当考虑内存占用和内存带宽需求来决定。
图元重启
使用图元重启可以在一次绘图调用中渲染多个不相连的图元。
代码启动和禁用图元重启:
glEnable ( GL_PRIMITIVE_RESTART_FIXED_INDEX );
// Draw primitives
…
glDisable ( GL_PRIMITIVE_RESTART_FIXED_INDEX );
驱动顶点
对于给定的图元实例,这个驱动顶点确定使用顶点着色器的哪一个顶点输出,因为只能使用一个顶点:
驱动顶点选择.png
几何形状实例化
几何形状实例化很高效,可以用一次API调用多次渲染具有不同属性的一个对象。这一功能在渲染大量类似对象时很有用,例如对人群的渲染。几何图形实例化降低了向OpenGL ES引擎发送许多API调用的CPU处理开销。要使用实例化绘图调用渲染,可以使用:
void glDrawArraysIns tanced(GLenum mode,
GLint first,
GLsizei count,
GLsizei instanceCount)
void glDrawElementsI nstanced (GLenum mode,
GLsizei count,
GLenum type,
const GLvoid *indices,
GLsizei instanceCount)
可以使用如下命令读取实例
void glVertexAttribDivisor(GLuint index,
GLuint divisor)
性能提示
应用程序应该确保使用尽可能大的图元尺寸调用glDrawElements和glDrawElementsInstanced。
在确定如何安排图元元素索引时考虑变换后顶点缓存的大小也是值得研究的技术。大部分GPU采用一个变换后顶点缓存。使用变换后缓存的大小来确定元素索引的创建方式应该有助于提升总体性能,因为这将减少顶点着色器执行重用顶点的次数。
图元装配
通过glDraw**提供的顶点由顶点着色器执行,顶点着色器变换的每个顶点包括描述顶点(x,y,z,w)值的顶点位置。对于每个单独图元及其对应顶点,图元装配将如下图过程执行:裁剪、透视分割和视口变换。
图元装配.png
坐标系统
坐标系统.png
为了避免在可视景体之外处理图元,图元将被裁剪到裁剪空间。
透视分割
透视分割取得裁剪坐标(Xc, Yc, Zc, Wc)指定的点,并将其投影到屏幕或者视口上。这个投影通过将(Xc, Yc, Zc)除以Wc来执行。
视口变换
视口是一个二维矩形窗口区域,是所有OpenGL ES渲染操作最终显示的地方。
// 视口变换调用
void glViewport(GLint x, GLint y, GLsizei w, GLsizei h)
// 确定深度范围
void glDepthRangef(GLclampf n, GLclampf f)
光栅化
光栅化是将图元转换为一组二维片段的过程,这些片元由片元着色器处理,代表可以在屏幕上绘制的像素。
下图时光栅化管线。在顶点变换和图元裁剪之后,光栅化管线取到单独图元,并为该图元生成对应得片段。每个片段由屏幕空间中得整数位置(x, y)标识。片段代表了屏幕空间中(x, y)指定得像素位置和由片段着色器处理而生成片段颜色得附加片段数据。
光栅化.png
剔除
在光栅化之前,需要确定它们时正面还是背面。剔除操作将抛弃背向观看者的三角形。
// 指定正面三角形方向,输入参数可以时GL_CW或者GL_CCW
void glFrontFace(GLenum dir)
// 指定剔除的三角形的面
void glCullFace(GLenum mode)
顺逆时针.png
剔除应该始终启用,以避免GPU浪费时间去光栅化不可见的三角形。
多边形偏移
当在绘制两个相互重叠的多边形时,很可能产生伪像,这些伪像被称为深度冲突伪像。
为了避免伪像,我们需要在执行深度测试和深度值写入深度缓冲区之前,在计算出来的深度值上面添加一个偏移量。
遮挡查询
遮挡查询用查询对象来跟踪通过深度测试得任何片段或者样本。
设置一个遮挡查询对象和查询结果:
// 开始遮拦查询
glBeginQuery ( GL_ANY_SAMPLES_PASSED, queryObject );
// draw primitives here
…
// 结束遮拦查询
glEndQuery ( GL_ANY_SAMPLES_PASSED );
…
// after several frames have elapsed, query the number of
// samples that passed the depth test
glGetQueryObjectuiv( queryObject, GL_QUERY_RESULT, &numSamples );
小结
本章主要讲解如何用顶点数据绘制图元。