[Unity Shader] 渲染管线流程

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[Unity Shader] 渲染管线流程相关的知识,希望对你有一定的参考价值。

参考技术A 顶点(模型空间)

顶点裁剪(视锥体裁剪)
背面剔除(Cull)
图元装配
光栅化为片段

模板测试(Stencil Test)
深度测试(Depth Test)
透明测试(Alpha Test)
混合模式(Blend Mode)
更新帧缓存中的颜色值

Unity渲染管线流程

渲染管线概述

Unity渲染管线流程:

  1. CPU打包数据,材质,法线
    a. 剔除物体(摄像机外的物体将被剔除)
    b. 渲染排序(安装给定顺序和深度对物体进行排序)
    c. 发送数据(将所有数据打包,发送给GPU)
    d. 调用Shader:SetPassCall,DrawCall
  2. GPU渲染管线
    a. 顶点着色器,将逐顶点执行。将模型空间转换到裁剪空间。可以处理顶点偏移和顶点光照。
    b. 顶点着色器后:光栅化阶段
    1. 裁剪片元: 将裁剪空间外的模型裁剪掉
    2. 转换为NDC(标准化设备坐标,使用透视除法)
    3. 剔除(背面剔除,正面剔除等),将特定方向的顶点进行剔除
    4. 视口转换(转换到屏幕坐标)
    5. 图元装配
    6. 光栅化(会产生锯齿)
      c. 片元着色器,逐像素执行。
      d. 片元着色器之后:输出合并阶段
    7. Alpha测试
    8. 模板测试
    9. 深度测试
    10. 颜色混合
      e. 帧缓冲区
      f. 后处理阶段(CPU调用GPU渲染管线)
      g. GPU渲染到屏幕

多相机渲染

  1. 每个摄像机都会渲染一遍该相机内的物体的顶点。

  2. 每个相机都会跑一次完整的渲染管线流程

  3. 默认会清除除天空盒之外的其他物体,通过ClearFlags和Depth设置清楚条件和渲染顺序。

  4. 如果不清楚其他相机的物体将会出现渲染叠加。

通过一个相机清除,一个相机保留好像可以实现3D视频的左右眼效果。(虽然需要两倍的渲染时间)

CPU渲染管线

剔除物体(摄像机外的物体将被剔除)
视锥体剔除

即:使用摄像机可视范围的视锥体构成的区域与物体进行碰撞检测。
优化:但是复杂的物体碰撞检测很耗性能,因此会使用一个简单的碰撞体Box Colider进行包裹,称为AABB包围盒。

层级剔除

通过摄像机进行特定物体的剔除。剔除特定层级的物体(未被勾选即被剔除物体)。
感觉这个可以实现物体的快速隐藏。(通过将物体设置到隐藏层来减少渲染,而又无需摧毁)。

遮挡剔除

通过判断物体位置和遮挡关系对被不透明物体完全遮挡的物体进行剔除。
渲染排序(安装给定顺序和深度对物体进行排序)
通过Render Queue数值进行排序,相等时使用深度进行排序。

  1. 不透明物体默认2000
  2. 透明物体默认3000
  3. 大于2500理解为半透明队列,将按深度进行从后向前排序
  4. 小于2500理解为不透明物体,按深度从前向后排序

不透明物体从前向后排序有助于优化,被遮挡物体将不会被渲染。

半透明物体从后向前渲染才能保证渲染颜色正确,半透明颜色混合顺序不同,结果颜色不同。
半透明物体内部不能保证完全从后向前进行渲染。导致的显示效果:1.向面片一样没有厚度,

发送数据(将所有数据打包,发送给GPU)

模型数据
格式:obj格式,fbx格式(推荐)

obj数据:

  1. 顶点坐标(三维)
  2. 法线信息(法线向量)
  3. UV坐标(二维数据,最后一位无用)
  4. 索引列表,一行数据表示一个三角面(通过索引找到每个顶点的顶点、法线、UV)。通过索引记录有助于压缩数据,

调用Shader:SetPassCall,DrawCall

GPU渲染管线

顶点着色器Shader
Unity中的顶点Shader

最重要的任务:将顶点坐标从模型空间变换到裁剪空间:
通过MVP矩阵对顶点的坐标进行变换。

  1. 模型空间变化到世界空间:
    参考系由物体自身变化到世界统一坐标
    模型空间时建模软件的坐标空间,就是模型生成的坐标。
  2. 世界空间变化到相机空间
    参考系由世界坐标变化到以相机为中心
  3. 相机空间变化到裁剪空间
    将相机的视锥体进行压扁标准化成2x2x1的(CVV矩阵)

图元装配及光栅化

执行裁剪

在裁剪坐标空间中裁剪多余的像素和图像。
超过CVV之外的形状将会被裁剪。

空间范围:[w, w, 0] ~ [-w, w, w]
NDC - 标准化设备坐标

为转换到屏幕坐标做准备
空间范围:[-1, -1, 1]~[1, 1, 0]
空间中的z值为深度值,将会在下面几个操作中保留,然后再深度剔除中排上用场。

裁剪空间转换到NDC

P.xyz / P.w = NDC
同时要注意不同的设备NDC不同
DX平台:左上角[0, 0], 右下角[1, 1]
OpenGL:左下角[0, 0],右上角[1, 1]

// 适应不同平台下的ndc坐标
o.screen_pos.y = o.screen_pos.y * _ProjectionParams.x;

计算NDC

我们能直接获得的最远只有裁剪空间下的坐标,想要获取NDC坐标需要自己计算。但是很简单。
裁剪空间下坐标空间为:[-w,-w,w] -> [w, w, 0]
NDC坐标为:[-1, -1,1] -> [1, 1, 0]
裁剪空间坐标除以w可以得到=>[-1, -1, 0] -> [1, 1, 0]

方法一:
// 顶点着色器

o.screen_pos = o.vertex;
// 适应不同平台下的ndc坐标
o.screen_pos.y = o.screen_pos.y * _ProjectionParams.x;

// 片段着色器
// 计算屏幕空间NDC坐标
// 透视除法 xyz范围 [-w,-w,w],[w,w,0] => [-1, -1,1]=>[1,1,0]
half2 screen_ndc = i.screen_pos.xy / (i.screen_pos.w + 0.000001);
half2 screen_uv = (screen_ndc + 1.) * .5; // [x, y]范围: [-1, 1] => [0, 1]

方法二:
// 顶点着色器
o.screen_pos = ComputeScreenPos(o.vertex);

// 片段着色器
// 计算屏幕空间NDC坐标
// [x, y]范围: [-1, 1] => [0, 1]
half2 screen_uv = i.screen_pos.xy / (i.screen_pos.w + 0.000001);

背面剔除

将背对着摄像机的物体进行剔除。

通过三角面的索引列表进行排列,如果排列顺序为顺时针则为正面,为逆时针则为背面。

屏幕坐标空间- 视口转换
将xy坐标转换到屏幕空间的宽高。
将NDC空间下转换到屏幕空间下的坐标

图元装配

在平面中将顶点连线,形成一个一个封闭的三角形。

光栅化

在平面中对每一个图元中每一个片元生成像素。

深度值Z
法线
顶点色
切线
位置
所有自定义数据

片元着色器Shader

纹理技术
纹理采样
在纹理坐标UV中进行采样[0, 0] ~ [1, 1]。通过UV映射到纹理的像素上,取出那个点的像素。
纹理过滤机制
小图像映射到大块区域:相当于图片放大显示。

  1. 四舍五入 - 会产生比较尖锐的边缘
  2. 双线性差值

大图像映射到小块区域引起的失真:
Mipmap,通过给定一组大小不同的图像进行映射。显示大区域时用大图像,小区域小图像。
Mipamp只会增加1/3的存储占用。每个图像大小依次递减1/2。

纹理寻址模式

当索引超过UV时应该怎么取值。

  1. Repeat重复模式 - 取小数(将形成循环取UV的效果)
  2. Clamp截取 - 超过将会取最大、最小值。

纹理压缩格式

RGBA 32bit
ASTC 4x4 block, ASTC 6x6 - 纹理细节保留较好,压缩明显
ETC2 8bits
PVRTC 4bits

光照计算

光照组成:

  1. 直接光照
  2. 间接光照
光照模型

BRDF基于物理的渲染模型,过于复杂。

经验光照模型

光照模型基本框架 = 直接光漫反射 + 直接光镜面反射 + 间接光漫反射 + 间接光镜面反射 + …(次表面散射,PDR等)

  1. Lambert漫反射光照模型
  2. Phong光照模型
    通过入射光方向和法向量计算反射光方向。知道了反射光就可以对比视线和反射光的夹角来得出其看到的光线多少。
  3. Blinn光照模型
    通过入射光方向和法向量夹角,计算出其半角向量。以半角方向对反射光方向进行近似(半角计算更简单)。
  4. Ground光照模型
  5. Flat光照模型
环境光

可以等同于间接光进行模拟。
间接光照实现:
Lightmap
Reflection Probe
Light Probe

输出合并

最重要任务:处理遮挡关系,处理半透明混合。
帧缓冲区FrameBuffer:

  1. 颜色缓冲区ColorBuffer
  2. 深度缓冲区DepthBuffer,Z-buffer
  3. 模板缓冲区StencilBuffer

Alpha测试

完全透明的,或者不满足给定透明度的片元将被丢弃。

模板测试

选定一个区域进行对比,不在区域中会被丢弃。

深度测试

使用深度进行测试,不通过将被丢弃。
通过测试的片元的深度将被存在Z-buffer中。(也可控制不写入)。
在Shader中可以通过ZWrite和ZTest进行控制。
ZWrite:是否写入Z-buffer
ZTest:深度测试规则

提前深度测试技术:
Early-Z,发生在顶点Shader之后,图元装配之前。
是一种提前优化技术,通过提前测试避免不必要的图元装配和片元计算。

混合Blending

对半透明像素进行颜色混合。

  1. 从后到前
  2. 一般情况下会关闭ZWrite

写在Shader中如:
Blend SrcAlpha OneMinusSrcAlpha
Blend SrcAlpha One

后处理

后处理可以在着色器工作完成后由CPU调用GPU对整体屏幕画面进行调整。


内容源:《一、渲染管线概述》

以上是关于[Unity Shader] 渲染管线流程的主要内容,如果未能解决你的问题,请参考以下文章

unityHDRP渲染管线 VolumeLight

unity渲染管线及升级URP

[Unity] Shader(着色器)之固定管线

Unity-默认渲染管线-刻晴卡渲shader

Unity Shaders学习笔记——渲染管线

Unity渲染流程(渲染管线)(渲染流水线)