1. 渲染路径
渲染路径是针对光照的设置,指的是以什么方式来进行光照计算,不同渲染路径主要影响的内容包括:
- 渲染的过程,比如不同渲染路径分别需要哪些Pass,每个Pass的作用是什么,以及是否会生成中间数据(G-Buffer)等
- Shader中可以使用的内置光照变量(如我们之前常用的_LightColor0等),Unity会根据配置的渲染路径给对应的光照变量进行赋值,因此渲染路径跟光照变量需要搭配使用,否则就会产生错误的渲染结果
- 对于多光源的处理,其实也是渲染过程中的一部分
如上图所示,可以在摄像机的 Rendering Path 属性下选择当前摄像机的渲染路径,虽然选项看起来很多,但是现在我们只需要关心两个:
- Forward:前向渲染路径
- Deferred:延迟渲染路径
除此之外,我们在 Shader 中实际实现光照计算时,会用到 _LightColor0 这些内置光照变量,要保证计算结果的正确,就需要在 Shader 执行时这些光照变量已经被正确赋值。因此,还需要在 Shader 中指明当前 Pass 用于哪种渲染路径,这样 Unity 才知道要给哪些变量赋值以及赋什么值。
在【Unity Shader入门精要 第3章】Unity Shader基础中曾经提到过,Pass 中也可以设置 Tags 标签,其中最常用的 Pass 级标签 “LightMode” 的主要作用就是负责通知引擎当前 Pass 对应的是哪种渲染路径,以及在当前渲染路径中所处的身份。
LightMode 标签支持的设置选项如下(顶点照明路径和老的延迟渲染路径选项这里省略了):
标签 | 描述 |
---|---|
Always | 无论使用哪种渲染路径,该Pass都会被渲染,但不会计算任何光照 |
ForwardBase | 前向渲染路径时,该Pass会被渲染,且该Pass身份为前向渲染路径的Base Pass |
ForwardAdd | 前向渲染路径时,该Pass会被渲染,且该Pass身份为前向渲染路径的Additional Pass |
Deferred | 延迟渲染路径时,该Pass会被渲染 |
ShadowCaster | 一个特殊功能类型的Pass,会将物体的深度信息写入到阴影映射纹理,用于对阴影的渲染,另外,在指定了摄像机需要生成深度纹理时,也会利用该类型的Pass进行深度纹理的生成 |
2. 前向渲染路径
2.1 光源处理类型
在前向渲染中,会对光源进行重要性排序,并按照排序对照射到当前物体上的光源分成三类,依次为:逐像素光源、逐顶点光源、SH(球谐)光源。
重要性排序的依据主要为光源对当前物体的影响,比如距离、强度等。另外,在光源 Inspector 面板的 Render Mode 选项中也可以指定当前光源的重要性(如下图)。
分类的大概过程为:
- 场景内最亮的平行光一定是逐像素光源,当场景内没有平行光时,会默认为一个黑色的平行光
- 在Project Settings 的 Quality 页签内可以设置场景内逐像素光源的最大数量 Pixel Light Count
- Render Mode 设置为 Important 的光源会被分配为逐像素光源
- 如果没有达到设定的逐像素光源最大数量,按照重要性排序依次将重要性较高的光源分配为逐像素光源
- 逐像素光源分配完成后,如果还有剩余的光源,则按照重要性排序依次分配为逐顶点光源,最多四个
- 如果还有多余的光源,全部分配为SH光源
2.2 Pass类型
在前向渲染中包含两种类型的Pass,分别为Base Pass 和 Additional Pass。
2.2.1 Base Pass
通过在 Pass 的 Tags 中设置标签 “LightMode” = “ForwardBase”,可以指定当前Pass为前向渲染中的 Base Pass
添加 #pragma multi_compile_fwdbase ,通知引擎为 Pass 编译不同 keyword 的变体,如果不加这一行,Unity只会按照一个默认的组合条件进行编译,在该情况以外的环境中,光照就会出错
Base Pass会处理一个逐像素光源和所有的逐顶点光源、所有的SH光源
Base Pass处理的逐像素光源必须是平行光,如果当前场景中没有平行光,则会按照一个黑色的平行光进行处理。假如我们在场景中不放置平行光,只放置一个点光源,此时依然可以照亮物体,是因为该点光源按照逐顶点光源的方式对物体进行了光照
Base Pass的渲染结果包含了一个光照效果里的所有部分内容,比如光源带来的漫反射等、自发光、环境光、光照纹理影响、阴影效果等,或者说,Base Pass 负责完成一次基础且完整的光照效果渲染
2.2.2 Additional Pass
通过在 Pass 的 Tags 中设置标签 “LightMode” = “ForwardAdd”,可以指定当前Pass为前向渲染中的 Additional Pass
添加 #pragma multi_compile_fwdadd ,通知引擎为 Pass 编译不同 keyword 的变体,此时,该 Pass 不会进行阴影的渲染
也可以通过 #pragma multi_compile_fwdadd_fullshadows 来对 Additional Pass 进行变体编译,此时该 Pass 可以支持阴影渲染,但效率更低
Additional Pass 会依次处理剩余的所有逐像素光源,如果物体身上有 M 个 Additional Pass,且受到 N 个逐像素光源的影响(Base Pass 处理的逐像素光源除外),则会执行 M * N 次渲染
Additional Pass 中需要设置混合模式,通常可以设置为 Blend One One,否则会将已有的渲染结果覆盖掉
Additional Pass 只处理光源自身的光照影响,不计算自发光、环境光等固定部分的光照效果,因为这部分效果应该只生效一次,不应该随光源数量而叠加
2.2.3 总结
由上可知:
- 前向渲染的原理为,先通过 Base Pass 渲染出基础的光照效果,然后对额外的逐像素光源依次执行 Additional Pass 计算出每个光源带来的附加效果,并叠加到基础效果上,得到最终的渲染结果
- 每个 Base Pass 只会渲染一次,通常也只会有一个 Base Pass
- 每个Addition Pass针对每个额外光源渲染一次
由于一个 Pass 就是一个完整的渲染过程,因此,在前向渲染中,渲染流水线和光源数量的关系可以大概理解为下图的样子
2.3 内置变量
在前向渲染中可以使用的内置变量如下:
变量 | 类型 | 描述 |
---|---|---|
_LightColor0 | float4 | 该Pass 处理的逐像素光源的颜色 |
_WorldSpaceLightPos0 | float4 | xyz表示逐像素光源的世界位置(平行光时为光源方向),w为0时表示当前处理的逐像素光源为平行光,否则为非平行光 |
_LightMatrix0 | float4x4 | 世界空间到光源空间的变换矩阵 |
unity_4LightPosX0/unity_4LightPosY0/unity_4LightPosZ0 | float4 | 仅可用于BasePass,前4个非重要点光源的世界空间位置的X值、Y值、Z值 |
unity_4LightAtten0 | float4 | 仅可用于BasePass,前4个非重要点光源的衰减因子 |
unity_4LightColor | float4[4] | 仅可用于BasePass,前4个非重要点光源的颜色 |
3. 延迟渲染路径
3.1 原理
在前向渲染中,每个逐像素光源都会至少执行一次对应的Pass,而每执行一次Pass都需要走一遍完整的渲染流水线,这其中需要进行很多的空间变换、可见性测试等计算。
但在这一帧中,摄像机和物体的空间关系并没有发生变化,也就是物体的可见性并不会发生改变。换句话讲,屏幕空间下,哪些像素需要渲染哪些像素不需要渲染是确定的,只需要进行一次计算即可。
延迟渲染的思路就是先计算出哪些像素需要显示,并记录下它们在进行渲染时所需的各种信息,然后只针对这些需要显示的像素挨个光源进行光照计算。
为此,延迟渲染通常需要两个 Pass 来完成:
- 第一个 Pass 不进行光照计算,只进行可见性计算,获取到需要显示的像素,同时,将这些像素在光照计算中需要的各种信息存储到一个特殊的缓冲区——G-Buffer——中,供第二个Pass使用
- 在第二个Pass中,针对那些需要显示的像素,从 G-Buffer 中获取该像素的相关 数据,对每个照射到当前像素的光源都进行一次光照计算,得到最终的光照结果(相当于每个光源都是逐像素的)。
因此,在延迟渲染中,渲染流水线和光源数量的关系可以大概理解为下图的样子:
3.2 G-Buffer
G-Buffer包含一系列的 RenderTexture 和缓冲,默认如下:
名字 | 描述 |
---|---|
RT0 | ARGB32,RGB通道存储漫反射颜色,A通道没有用到 |
RT1 | ARGB32,RGB通道存储高光反射颜色,A通道没有用到 |
RT2 | ARGB2101010,RGB通道存储法线,A通道没有用到 |
RT3 | ARGB32,存储自发光 + LightMap + 反射探针 |
深度缓冲 | |
模板缓冲 |
由于在延迟渲染中,片元着色器计算光照的各种数据基本都要在 G-Buffer 中进行存储,而默认的四张纹理几乎已经都被占满了,所以当片元着色器计算比较复杂使用参数较多时,很可能就需要扩展纹理,从而带来较大的显存压力。
另外,由于延迟渲染是在片元着色器中对所有光源进行光照计算,因此所有光源都是使用的同一个光照模型,而且由于延迟渲染的目标是可见像素,也就是离摄像机最近的像素,因此对于半透的渲染支持较差。
另一方面,由于启用了 G-Buffer,在诸如生成深度纹理等操作时,可以直接从G-Buffer中读取所需信息,使得延迟渲染对于某些屏幕后处理的支持更好。
3.3 内置变量
延迟渲染中可用的内置光照变量如下:
变量 | 类型 | 描述 |
---|---|---|
_LightColor | float4 | 当前正在处理的光源的颜色 |
_LightMatrix | float4x4 | 世界空间到当前光源空间的变换矩阵 |