渲染管线总流程
应用程序阶段 => 顶点数据 => 顶点着色器 => 曲面细分着色器 => 几何着色器 => 图元装配 => 裁剪&剔除 => 光栅化 => 片元着色器 => 测试&混合
应用程序阶段
剔除 Culling
渲染对象层面的剔除,多使用AABB包围盒进行,包括视锥体剔除/遮挡剔除/LayerMask剔除
排序 Sorting
确定渲染对象的渲染顺序,通过RenderQueue确定,先画不透明再画半透明
当RenderQueue相同时:
不透明物体(RenderQueue <= 2500)从前到后排序(优化性能,节省DrawCall)
半透明物体(RenderQueue > 2500)从后到前排序(确保AlphaBlend结果正确)
打包数据 Batch
打包模型信息和材质参数
绘制调用 DrawCall
SetPass Call 和 Draw Call
顶点着色器 & 几何着色器
这几个阶段最重要的是需要将顶点的位置从模型空间转换到裁剪空间(只要确保数据传入图元装配及光栅化阶段之前变换到裁剪空间即可,不限VS or GS)
顶点着色器
最简单的顶点着色器示例:
struct appdata |
曲面细分着色器
由曲面细分控制着色器(Hull Shader)、镶嵌器(Tessellator)和曲面细分求解着色器(Domain Shader)三个阶段构成
几何着色器
最简单的几何着色器示例:
[maxvertexcount(4)] |
[maxvertexcount(n)]
是一个属性,用来指定几何着色器可能会输出的最大顶点数。
这个属性是必需的,你必须在几何着色器函数前声明它。编译器和图形硬件会使用这个信息来预先分配足够的内存空间来存储几何着色器输出的顶点。
point
是一个图元类型,表示一个点。在几何着色器中,你可以指定输入图元的类型,它可以是 point
、line
或 triangle
。
TriangleStream
是一个几何着色器的输出类型,用于从几何着色器中输出三角形。几何着色器的输出类型决定了输出图元的种类,可以是点(PointStream
)、线(LineStream
)或者三角形(TriangleStream
)。
图元装配 & 光栅化
因为由硬件控制,又称“硬件操作阶段“
上图中图元装配位置错误,应该位于裁剪操作之前
所以最终顺序为:
图元装配 => 裁剪&剔除 => 光栅化
图元装配
在图元装配阶段,渲染管线会根据索引缓冲(IndexBuffer)把顶点数据组合成指定的图元(Primitive)。
(如果曲面细分着色器或几何着色器存在,会有一个有限形式的预图元装配阶段在这些着色器之前进行,以传给他们需要的数据)
裁剪 Clipping
裁剪操作发生在裁剪空间中,在图元装配阶段之后,透视除法之前。
任何完全在视椎体内的图元会被保留下来;完全在视椎体外的图元会被完全丢弃;部分在视椎体内部分在视椎体外的图元则会被裁剪,只保留在视椎体内的部分,可能会在裁剪处生成新的顶点。
背面剔除 Back-face Culling
背面剔除发生在透视除法之后,光栅化之前。
与之前CPU中对象层面的剔除不同,这是图元层面上的剔除。
通过检查三角形的顶点顺序(winding order),可以判断三角形是正面朝向(逆时针)还是背面朝向(顺时针)。如果一个三角形是背面朝向,那么它就会被剔除。
从上图可以看到,我们可以使用行列式(determinant)来确定投影后的2D三角形到底是CW还是CCW顺序。
光栅化
片元着色器
纹理技术
注意:Mipmap三线性插值总共采样$2^3$一共8次
光照计算
测试&混合
Alpha Test
根据透明度的Threshold来丢弃一些像素
Stencil Test
根据一个叫做模板缓冲(Stencil Buffer)的缓冲区来控制哪些像素应该被绘制,哪些像素应该被丢弃。你可以在模板缓冲中为每个像素定义一个值,然后在模板测试中设置条件,只有满足这个条件的像素才会被渲染。
Depth Test
深度测试也叫Z-Test,根据像素在场景中的深度值来确定哪些像素是可见的,哪些是被其他物体遮挡的。
深度测试会将每个新像素的深度值与深度缓冲区中对应位置的值进行比较,只有深度值小于z-buffer中的值时,才会更新颜色缓冲区。
Alpha Blend
虽然之前CPU中排序可以确定对象间的渲染顺序,但是Alpha Blend此时无法确定片元的前后顺序,所以无法保证从后到前混合,可能会引入错误。
如果需要保证从后向前混合,则需要引入顺序无关的透明(OIT)技术,如深度剥离和逐像素链表。
参考文献:
https://www.khronos.org/opengl/wiki/Rendering_Pipeline_Overview