实时渲染(一)——图形渲染管线
在渲染管线中是最慢的阶段决定整个渲染的速度。
我们一般使用吞吐量(throughput)来描述一个阶段的处理速度,而不是帧率。因为帧率会受到设备更新的限制而导致实际速度比帧率所标示的更慢。
一个例子:
假设一个设备为60赫兹,这意味着这个设备16.666666ms刷新一次,这时恰好有一管线阶段花费了62.5ms执行完成,由于63大于16.666666*3,小于16.666666*4,所以他实际上想要最终完成工作必须等待下一次设备刷新。所以真实的执行时间折算下来其实不止62.5ms而是趋近于66ms。当然,如果关闭了垂直同步就另当别论了。
一个渲染管线可以粗略的按照执行顺序分成三大阶段:
- 应用阶段
- 几何阶段
- 光栅阶段
这些阶段又可以细分为更多的子阶段,注意这些都是功能性的分配,在实现上为了效率等因素往往会合并一些阶段或者拆分一些阶段等等。
下面是一张图:
一、应用阶段
这个阶段的目标就是为几何阶段产生合适的图元数据,一般来说,很多工作都在这个阶段完成,诸如碰撞检测、动画、输入、当然还有一些加速管线的算法,比如层次视椎剔除(hierarchical view frustum culling)等等。
二、几何阶段
几何阶段集中于逐多边形操作和逐顶点操作。
- 模型-视图转换:模型坐标-->世界坐标-->相机坐标
- 顶点着色:计算着色方程根据材质数据(位置、法线、颜色、其他着色方程需要的数据等等)决定光照效果,结果可以是(颜色、向量、纹理坐标或者其他种类的着色数据)。通常在世界空间中发生、有时候会把相关实体转换到其他空间,并在此空间进行计算。
- 投影:略
- 裁剪:在单位立方体之外的基元被舍弃,而完全在单位立方体之内的基元被保留;相交的基元则进行剪切,生成新的顶点,而老的被丢弃。
- 屏幕映射:只有已裁剪的部分才会被传递给屏幕映射,坐标仍然是3维的。将x/y转换成平面坐标。转换后的x/y叫做屏幕坐标。有一个注意事项是:如何把浮点数映射到像素以及某些系统之间屏幕做坐标原点的问题。
三、光栅阶段
- 建立三角形:计算三角形表面差值等数据,这个数据将会用于下一阶段的扫描转换,以及几何阶段生成的各种着色数据的插值。
- 遍历三角形:检查每个被三角形覆盖了中心的像素,并为何三角形重叠的部分生成片段。查找那些在三角形之内的像素或者采样的过程叫做扫描转换。生成的每个在三角形内的片段都是根据三角形顶点插值出来的,这些片段的数据包括深度以及各种来自几何阶段的着色数据。
- 像素着色:插值完毕的着色数据输入其中,执行所有的逐像素着色操作,并将一个或者多个颜色传递给下一个阶段。纹理技术也在此阶段实现。
- 合并:每个像素的信息被存储在颜色缓冲中,它是颜色的矩形数组(每个颜色拥有红、绿、蓝三部分)。合并阶段负责将着色阶段生成的片段颜色和当前缓冲中存储的颜色进行组合。并且包含可见性检测、模板操作等等。
最后要梳理一下各种shader通常都做什么:
vertex shader:模型-视图转换,顶点着色,投影
geometry shader:操作图元顶点(primitive概念看下方)——图元着色、销毁/创建图元
pixel(fragment) shader:像素着色
几何primitive: points、lines、triangles