渲染管线的作用 : 通过一系列的处理,将一些3D元素转换成屏幕上的2D图像(常用的分法是将逐片元操作归入光栅化中)
整体流程(每个阶段的输出就会成为下一个阶段的输入):
CPU :
GPU : GPU具有并行性比较好的特点,一般将几何阶段,光栅化阶段,逐片元操作交给GPU处理,如一个物体上同一个物体可能与很多顶点都需要做光照、着色计算,而且这些顶点只是数据不同,但是光照着色模型,以及几何运算方式都是一样的,就可以将他们分配到GPU的不同工作单元上面去同时执行,光栅化和逐片元操作也是同理
一 、应用阶段 Application:
准备基本场景数据 → 加速算法 粗粒度剔除 → 设置渲染状态 准备渲染参数 → 调用DrawCall 输出渲染图元到显存
1,准备基本数据 : 场景、对象的基本数据,比如场景里的物体,他们的位置朝向、大小及物体对应的模型里每个顶点的位置、法线、切线等;或是场景光源的位置、朝向和一些基本属性,摄像机的位置、朝向,以及其他的全局数据等
①场景物体数据 :物体变换数据:位置、旋转、缩放等;物体网格数据:顶点位置、UV贴图等
②光源信息 : 光源类型:方向光、点光、聚光等;位置、方向、角度等其他参数
设置光源:方向光 : 颜色、方向等; 点光源 : 颜色、位置、范围等; 聚光源 : 颜色、位置、方向、内外圆锥角等
设置阴影 : 是否需要阴影 : 判断该光源可见范围内是否有可投射阴影的物体;阴影参数 : 对应光源序号、阴影强度、级联参数、深度偏移、近平面偏移等
逐光源绘制阴影贴图 : 进平面偏移; 逐级联计算当前光源 + 级联对应的观察矩阵、投影矩阵、以及对应到的阴影贴图里的视口区域; 绘制到阴影贴图
③摄像机参数 : 位置、方向、远近裁剪平面;正交/透视;视口比例/尺寸等
2,粗粒度剔除 :在加载模型、贴图等资源的时候,首先进行粗粒度剔除,如果一个物体被挡到另一个物体的后面,那么被挡住的物体就不需要显示,也不需要提交到管线后面的阶段去处理
①碰撞检测
②加速算法 : 可见光裁剪; 可见场景裁剪 : 八叉树、BSP树、K-D树、BVH树
③遮挡剔除 : 对于摄像机视锥体中之外的物体可以进行剔除或裁剪不予渲染
④其他算法
3,进行渲染设置 : 如渲染UI和渲染场景,他们的渲染参数和模式可能是不一样的。
①绘制设置 :如 对于不同的物体使用不同的着色器,如对于不同的渲染对象使用一些合批方式,如GPU instance或者动态批处理,
②绘制顺序 : 如对于场景物体,可能是由远到近渲染,或是先渲染不透明的,在渲染半透明的
相对与摄像机的距离;使用材质RenderQuere;使用UICanvas的顺序等
③渲染目标 : 以及渲染完成后输出的渲染目标,是RenderTexture还是FrameBuffer(帧缓存)?也有可能同时输出到多个渲染目标,通过一些特定的方式来合并到一起
④渲染模式 :如前向渲染(Unity的轻量管线和通用管线URP)和延迟渲染(UE4、Unity的高清渲染管线HDRP)
4,输出到几何阶段 : 调用DrawCall 输出渲染图元到显存,交给GPU处理
①顶点数据 : 位置、颜色、法线、纹理UV坐标、其他顶点数据
②其他数据 : MVP变换矩阵、纹理贴图、其他数据
二、几何阶段 Geometry Processing:
顶点着色Vertex Shading → 可选顶点处理 → 投影Projection → 裁剪Clipping → 屏幕映射Screen Mapping
1,顶点着色器 : 计算顶点光照的需要知道光源的位置和朝向,以及摄像机的位置和朝向,以及当前顶点的世界位置,计算当前顶点的世界位置需要知道顶点再模型空间的位置,以及模型本身的位置、旋转、缩放等
①视图变换 : 模型变换、视图变换、投影变换,对应的就是MVP矩阵
②顶点着色 :
2,可选顶点处理:
①曲面细分 : 通过现有的顶点生成更多的顶点,需要知道顶点再模型里的位置信息
②几何着色器 : 通过现有的图元做一些几何方面的操作,生成更多的顶点和图元,如对现有顶点生成法线,需要知道现有图元的位置
3,投影:
①正交 :xyz正常,w始终为1,所以所有的物体无论远近都不会变大或变小
②透视 : xyz正常,w的值越远越小,越近越大,
4,顶点裁剪 : 去除屏幕以外看不到的顶点
①CVV
②正面或背面剔除(可配置):在Shader中可以通过一些代码控制开关
5,屏幕映射 : 把顶点位置从3D坐标空间转换到2D坐标空间
①从连续到离散
②坐标系差异(OpenGl/D3D)屏幕原点的位置:OpenGL在左下方,D3D在左上方
三、光栅化阶段 :
三角形设置Triangle Setup → 三角形遍历Triangle Traversal
1,三角形(点/线)设置 : 把映射到2D空间的顶点位置计算边界信息组成三角形
2,三角形(点/线)遍历 : 寻找三角形包括哪些2D空间的像素点。这里得到的片元并不等于像素,因为屏幕的同一种像素位置,有可能对应于多个三角形的不同片元,他们重叠在一起,各自着色后可能还需要再决定是否保留,保留哪些
3,片段着色器 : 对这些点使用它们包含的数据进行着色(仅顶点着色),为后面的逐片元着色准备数据
抗锯齿(MSAA):
SSAA : 渲染到一个分辨率放大到n倍的buffer; 对放大n倍的buffer下采样
MSAA :在光栅化阶段 ; 设置多个子采样点,进行覆盖测试和遮挡测试,覆盖测试是看这个子采样点是不是在三角形以内,遮挡测试是看对这个子采样点的深度和也即是深度缓存里面的数值进行比较,看是否能够通过。如果能够通过这两个测试,说明该子采样点属于三角形,最终就能得到该像素被三角形覆盖的信息,并且被保存起来在后续的逐片元操作中用于着色混合。
FXAA / TXAA :后处理技术
四、逐片元操作(片元可以理解为屏幕上的某一个像素点 ) :
片元着色Fragment Shader → 颜色混合Color Blending → 目标缓冲区
1,片元着色
2,颜色混合
①裁剪测试
②透明度测试
③深度测试 : 测试片元深度值,与缓冲区的深度值进行比较,通过测试就会被留下来
④模板测试 : 如对以下两个立方体进行描边,但又想绘制的描边不会覆盖在另一个立方体上面,那么首先将两个立方体的顶点放大一些,并且把它们绘制成描边的颜色,同时写入模板值,绘制完后得到两个绿色区域的立方体,然后在进行真实的立方体绘制的时候,吧立方体的模板值和之前描边颜色的模板值进行比较,只要模板值是描边模板值,我们就把他覆盖掉,这样就可以实现立方体永远在描边之上
⑤混合 : 把通过测试的片元进行一个混合操作,从而得到像素点最终输出的颜色
五、后处理:图像处理,如模糊、景深、高光等