返回目录
大家好,我是阿赵。
渲染管线网上很多人都介绍过,我这个基本上是写给自己的看的一个笔记,各位不用介意。
一、渲染流水线的简单说明:
如果把渲染流水线列出来,大概有这些过程:
CPU模型数据-顶点控制程序 - 曲面细分 - 几何着色器 - 裁剪 - NDC空间 - 屏幕空间 - 光栅 - 片元处理程序 - 帧缓存 - 后处理
其中:
1.CPU处理模型数据阶段,一般游戏引擎都是固定好格式来解析,不需要用户手动处理,只需要用户根据给定的API去组装好数据。
2.曲面细分和几何着色器是可选项,一般都没有。
3.从裁剪一直到光栅化的过程,一般都是底层处理,我们基本不需要操作
4.所以一般对于使用现成3D引擎的人来说,可以干涉的过程就只有顶点控制程序和片元处理程序。这两个过程我们可以通过编写shader来实现,具体能做到的事情就是改变物体的形状和颜色了。
二、每个步骤的具体实现:
如果只是使用3D引擎,其实下面的过程完全可以不看。不过基于了解一下原理会对使用层面也有帮助,或者某一天需要自己写引擎的时候,这些过程的知识还是需要了解一下的。阿赵我在差不多十年前就尝试过自己写引擎,由于能力有限,写出来的引擎只能达到渲染3D模型和播放骨骼动画的程度,不过整个过程应该还是完整的。
下面来逐个步骤说一下:
1、CPU阶段
1.物体剔除
目的:把场景内摄像机不应该看到的东西剔除掉,减少渲染的物体数量
(1)视锥剔除(视锥与物体作碰撞检测)
(2)层级(Layer)剔除
(3)遮挡剔除
这一步都纯CPU的东西,在推入GPU渲染之前,先通过某些条件来过滤掉一部分物体,减轻GPU的渲染压力。不过事情都是相对的,做完这些步骤之后,GPU压力是降低了,但会消耗CPU的性能。所以我感觉做优化的时候要根据自己的情况来控制,如果做得不好,可能就会变成负优化了。
如果能用Layer剔除,这个应该是消耗最小的
然后是视锥的剔除,简单的做一个顶点碰撞检测。先排除掉所有顶点都不在视锥范围内的物体。
最后是遮挡剔除,我个人感觉这个是用CPU和内存性能换GPU性能的一个手段。比如Unity的遮挡剔除功能,它需要把物体先标记为static,然后烘焙,生成遮挡数据,才能根据摄像机看到的范围,把没有看到的物体隐藏掉。
2.渲染排序
目的:确定物体渲染的顺序
半透明物体的渲染一直都是3D引擎里面比较值得研究的问题。可以通过渲染顺序来规定不同的渲染规则。
Unity引擎的规则
(1)渲染顺序<2500为不透明物体
排序顺序:按照离摄像机距离:从前到后排序
(2)渲染顺序>=2500 为半透明物体
排序顺序:按照离摄像机距离:从后到前
3.将信息提交到GPU
通过命令:SetPassCall、DrawCall提交
提交的信息包括:
(1)模型信息
(2)变换矩阵
(3)光源信息
(4)使用的shader
(5)材质球上的参数
到这一步位置,CPU的工作就结束了,剩下的是GPU的工作。
2、顶点处理
作用:改变物体的形状
1.从物体本地坐标(Local)转换到世界坐标(World)
2.从世界坐标转换到摄像机坐标(View)
3.从摄像机坐标转换到裁剪空间坐标(Clip)
这个过程简单的实现就是MVP矩阵和vertexPos相乘,最终把模型的顶点坐标从局部坐标转换到裁剪空间的坐标。
不过在实际编写shader的时候,我们不一定会规规矩矩的就只想得到一个顶点的裁剪空间坐标。我们还可以做很多事情,比如得到法线、切线等数据,比如处理顶点颜色、扭曲顶点位置等,这些我们都可以通过在自定义的数据结构里面加字段,并在顶点程序里面计算好结果,然后把结果保存下来,以便片段着色器使用。
3、光栅化操作阶段
这个阶段只是一个统称,其实在光栅化操作之前,还需要对模型信息进行处理,并转换他们的坐标系,齐次裁剪空间(CVV) - 归一化的设备坐标空间(NDC)- 屏幕坐标(Screen),我们才能得到实际在屏幕上显示的内容
(1)裁剪
把和视锥裁剪空间相交的三角形,把边缘裁剪掉,生成新的三角形
在裁剪空间只需要判断点的坐标是否超出裁剪范围(cvv空间),比较容易。生成新三角形这一步,从理论上是需要的,但从实际渲染的角度,究竟是根据裁剪边缘重新生成三角形快,还是把只要有一个顶点在裁剪范围内的三角形都渲染出来快,这个我也不是很确定。
(2)从CVV空间转换成NDC
NDC空间(归一化的设备坐标空间),这是在裁剪空间和屏幕坐标空间中间的一个空间,是把裁剪空间的坐标xyz除以w分量得到,它的三个轴的坐标取值范围都是是-1到1。
(3)背面剔除
目的:如果开启了背面剔除,就需要把处于背面的三角形面部分剔除掉,让它不参加渲染。
判断的方法:
通过三角形3个顶点在NDC坐标里面的关系判断:顺时针是背面,逆时针是正面。属于背面(背对着摄像机)的三角形就可以剔除掉
(4)把NDC坐标转换成屏幕坐标
把NDC的-1到1的取值范围的坐标,转换成在屏幕上面的实际坐标。
计算方式:
ScreenX = NDC.x * pixelWidth/2 + pixelWidth/2;
ScreenY = NDC.y * pixelWidth/2 + pixelWidth/2;
(5)图元装配
之前步骤操作的都是孤立的顶点,从这一步开始,将点通过索引,组装成三角形图形(图元)
(6)光栅化
将之前的三角形图形按照一定单位(可以理解成是像素)分解,可以认为得到的每个片元(片段),就是一个像素。所以可以笼统的理解,我们在片元着色器里面,就是改变某个像素的颜色。
4、片元处理
作用:改变像素颜色
1.光照着色
通过光照模型改变颜色
之前专门分享过关于光照模型的文章,一般的光照模型组成:
环境光+漫反射颜色+高光颜色
2.纹理着色
通过UV坐标去采样纹理贴图,并获得对应的颜色
5、输出合并
作用:决定当前像素点是否应该输出某些内容。
1.Alpha测试
2.模板测试
3.深度测试
4.颜色混合
6、帧缓冲
某一帧的显示输出内容保存在帧缓冲器,获得后可以进行再次处理。
7、后处理
对已经保存的帧缓冲内容,进行后期加工处理