渲染流水线简单梳理
目录概述1 应用阶段可操控优化1.1 粗粒度剔除1.2 设置渲染顺序1.3 把数据加载到显存1.4 设置渲染状态SetPassCall告诉GPU使用哪个Shader设置BlendCullZWrite等和DrawCall使用哪些模型数据和贴图渲染2 几何阶段2.1 顶点着色器完全可编程2.2 裁剪GPU硬件固定操作可通自定义裁剪操作来控制2.3 屏幕映射GPU完成2.4 补充一下3 光栅化阶段3.1 三角形设置3.2三角形遍历3.3 Early-Z技术把深度测试提前做4 像素处理阶段4.1 片元着色器完全可编程4.2 逐片元操作输出合并阶段可配置5 结尾概述整体上流水线分为四个阶段1应用阶段2几何阶段3光栅化阶段4像素处理阶段当然也可以分成三个阶段把3和4统一叫光栅化阶段。这不重要重要是理解每个阶段的细节处理。1 应用阶段可操控优化此阶段在CPU上运行的完全可控。主要分为4个阶段1.1 粗粒度剔除包括视锥体剔除AABB包围盒和视锥体碰撞检测遮挡剔除Occlusion Culling层级规则剔除Culling Mask1.2 设置渲染顺序根据摄像机距离和渲染队列决定顺序队列小于2500不透按照由近到远渲染大于2500透明按照由远到近渲染1.3 把数据加载到显存根据设置好的顺序依次提交打包数据Batch数据包括模型信息顶点坐标法线UV顶点色切线索引表等变换矩阵世界转换矩阵根据摄像机位置的VP矩阵FOV参数等灯光材质灯光信息材质Shader等。1.4 设置渲染状态SetPassCall告诉GPU使用哪个Shader设置BlendCullZWrite等和DrawCall使用哪些模型数据和贴图渲染2 几何阶段2.1 顶点着色器完全可编程主要任务是坐标变换通过MVP矩阵把模型空间顶点坐标转换到齐次裁剪空间。当然还有其他任务顶点着色例如逐顶点光照颜色的计算为光栅化阶段输出其他数据等。转换过程如下图其中红色框中为顶点着色器的处理过程齐次裁剪空间要重点说明一下如图所示左面是观察空间坐标系右面是其次裁剪空间此时顶点坐标做了齐次处理增加了w1分量主要原因是透视空间中数值是无法正常做线性插值的需要增加维度方便透视投影和裁剪通过升维的方式简化裁剪过程想到这个方法的真是天才我怀疑他是高纬度的生物经过裁剪矩阵投影矩阵转换后w有了新的意义。可通过w判断顶点是否在视锥体内。顶点着色的输出数据为齐次裁剪空间下的坐标主要目的就是为了后面的裁剪和屏幕映射做准备。这个计算公式可以看到xyz做了不同的缩放w分量编程了Pview的-z也就是观察空间下的z轴距离后面再说SV_POSITION的时候还会提到。曲面细分着色器可选着色器主要用于细分图元。几何着色器也是可选的用于逐图元操作或产生更多图元这里不是重点暂且不谈。2.2 裁剪GPU硬件固定操作可通自定义裁剪操作来控制通过透视投影矩阵转换到其次裁剪空间如果该顶点在视锥体中必须满足-wx,y,zw然后做透视除法齐次除法可以得到归一化设备坐标NDCNDC空间下的在dx中-1x,y10z1在OpenGL中-1x,y,z1从而判断是否是在可视范围内完全在NDC外的剔除部分在NDC内的生成新的图元。2.3 屏幕映射GPU完成在NDC的基础上进一步处理把顶点坐标转换到屏幕坐标系。此时图元坐标还是3维的xyz范围是[-1,1]所以映射到[0,1]乘以屏幕分辨率就是屏幕坐标了在OpenGL中屏幕的原点在左下角DX在左上角以GL为例转换公式为结合了其次除法转换屏幕坐标公式如下screen.x clip.x * pixeWidth / 2*clip.w pixeWidth/2screen.y clip.y * pixeHeight / 2*clip.w pixeHeight/2此时的z和w分量作什么呢通常做法会把clip.z/clip.w的值储存在zbuffer里看硬件驱动w分量可用在透视校正插值。2.4 补充一下整个几何阶段输出的核心数据SV_POSITION也不再是齐次裁剪空间的坐标了因为做了齐次除法和屏幕映射。这里我很少见人提到过所以还是重点提一下吧。pos.x clip.x * pixeWidth / 2*clip.w pixeWidth/2pos.y clip.y * pixeHeight / 2*clip.w pixeHeight/2pos.z clip.z/clip.wdx中是[0,1]gl中[-1,1]pos.w 1 ? 这里不对1没有意义啊SV_POSITION.w的值就是经过插值后的ClipSpace的 w 值。在Unity中这个值等价观察空间下的 z 坐标值也就是世界空间中到摄影机的z周距离。这个距离值可以非常方便地实现一些雾效的效果或者用于在Cluster based Forward中计算 z 轴上的采样位置。OpenGL中在VertexShader下类似于 SV_POSTION 的语义为 gl_POSITION。在Fragement Shader下类似于SV_POSTION 为 gl_FragCoord。gl_FragCoord.xyz 值和 SV_POSTION.xyz 的语义是一样的。而 gl_FragCoord.w的值等于 ClipSpace 的 w 值的倒数这点和 SV_POSTION.w 是不同的。不过在Unity中为我们做了自动的转换如果我们在 PixelShader中使用 SV_POSTION.w 值的时候在 OpenGL下会自动转换成 1/gl_FragCoord.w还是非常方便的。此段为引用别人写的原文链接所以此在片元着色器里可以直接用SV_POSITION语义的变量除以屏幕分辨率获得屏幕UV。3 光栅化阶段光栅化啥意思简单点说就是根据三角形的三个顶点计算出覆盖的像素。主要分为两个子阶段三角形设置Triangle Setup三角形遍历Triangle Traversal。3.1 三角形设置由固定硬件完成主要任务是计算三角形网格信息如根据上一步得到的顶点信息获得三角形顶点坐标和边界线用数学表达式。3.2三角形遍历检查每一个采样点被三角形覆盖的像素并使用三角形的三个顶点对覆盖的像素进行插值为覆盖的像素生成片元fragment片元的信息包括屏幕坐标、深度信息、顶点着色器输出的顶点信息如法线uv等。3.3 Early-Z技术把深度测试提前做正常的深度测试是在像素阶段的逐片元操作里目的是把不需要处理的片元提前舍弃掉提升性能目前很多硬件都支持以下情况会使其失效1.开启Alpha Test由于Alpha Test需要在像素着色器后面的Alpha Test阶段比较所以无法在像素着色器之前就决定该像素是否被剔除可以用Alpha Blend代替或者双Pass优化一个Pass做discard/clip深度读写一个Pass做颜色处理不做深度写入2.关闭了深度测试深度测试是Early-Z的前提。3.开启了Alpha Blend 启用了Alpha混合的像素很多需要与frame buffer做混合无法执行深度测试也就无法利用Early-Z技术但并不影响其他Pass使用Early-Z。4.开启Tex Kill即在shader代码中有像素摒弃指令DX的discardOpenGL的clip1中的AlphaTest也是使用了clip函数。4 像素处理阶段4.1 片元着色器完全可编程输入的从顶点信息插值的结果通过对这些信息的处理为这个片元计算颜色常用的技术是纹理采样通过顶点插值来的uv采样纹理的颜色。4.2 逐片元操作输出合并阶段可配置主要任务1.通过一些列测试决定们每个片元的可见性如透明度测试-模板测试-深度测试2.把通过测试的片元的颜色和颜色缓冲区的颜色进行混合混合后写入缓冲区。和颜色缓冲区一样还有模板缓冲区当前模板之和缓冲区值比较决定片元是否舍弃和写入深度缓冲区和模板缓冲区类似系统中所有的缓冲区组成了帧缓冲区frame buffer至此屏幕就显示了帧缓冲区里的颜色了为了使画面连续使用双重缓冲技术。4.3 拿到帧缓冲之后其实还有一个很重要的操作就是做屏幕后处理例如BloomDOF等5 结尾本文大量篇幅在几何阶段这个阶段相对难理解的也是最为重要的他们整个流水线做了基础的准备工作有了这些准备后面的流程才能方便的计算后续还有其他细节想起来再补充吧。