# 复杂光照
在之前的学习中,我们使用的都是单一光照,实际在游戏中可能存在多个光源,不同的光源会互相影响构成真正的光照系统,在这一节我们将实现一个真正可用的光照模型。
# 渲染路径 Rendering Path
📖 决定了光照是如何应用到 Unity Shader 中的。
我们只有为 Shader 正确地选择和设置了需要的渲染路径,该 Shader 的光照计算才能被正确执行。
🏷渲染路径的分类
- 前向渲染路径(Forward Rendering Path)
- 延迟渲染路径(Deferred Rendering Path)
- 顶点照明渲染路径(Vertex Lit Rendering Path)—— 已经被弃用
目前 unity 提供了新的渲染路径代替了原来的渲染路径。
默认使用 Graphics 中的设置,可以在摄像机中对设置进行覆盖。
在 Shader Pass 中来指定该 Pass 使用的渲染路径:
Pass { Tags { "LightMode" = "ForwardBase" } } |
LightMode 标签可供使用的渲染路径:
💭为 Pass 指定正确的渲染路径对于 Unity 光照变量正确赋值具有重要意义。
Unity 默认使用前向渲染光照路径.
# 前向渲染路径
前向渲染是传统渲染方式,也是我们常用的一种渲染路径。
📖 原理:每进行一次完整的前向渲染,我们需要渲染该对象的渲染图元,并计算两个缓冲区的信息:颜色缓冲区和深度缓冲区。
每个光照下的物体都需要进行一次逐像素的 Pass,也就是在一个场景中,n 个物体 m 个光源的下,渲染场景需要 n*m 次 Pass。
# Unity 中的前向渲染
Unity 中照亮物体有 3 种方式:
- 逐顶点处理 Spherical
- 逐像素处理 Harmonics
- 球谐函数 SH
光源使用哪一种处理方式取决于它的类型和渲染模式。
将渲染类型设置为 Important(重要的)来告诉 Unity 这个光源是重要的,把它当成一个逐像素光源来处理。
通常,Unity 会自动根据远近、强度等参数对光源的重要性进行排序。
场景中最亮的平行光总是按逐像素处理的。
渲染模式被设置成 Not Important 的光源,会按逐顶点或者 SH 处理。・渲染模式被设置成 Important 的光源,会按逐像素处理。
如果根据以上规则得到的逐像素光源数量小于 Quality Setting 中的逐像素光源数量 (Pixel Light Count),会有更多的光源以逐像素的方式进行渲染。
# 内置变量函数
# 相关参考
Unity 渲染路径 - 知乎 (zhihu.com)
# 顶点照明渲染路径
顶点照明渲染路径是对硬件配置要求最少、运算性能最高,但同时也是得到的效果最差的一种类型,它不支持那些逐像素才能得到的效果,例如阴影、法线映射、高精度的高光反射等。
📖同理,使用逐顶点的方式进行光照计算。—— 是前向渲染的一个子集。
# 延迟渲染路径
当场景中包含大量实时光源时,前向选人的性能会急速下降。
除了前向渲染中使用的颜色缓冲和深度缓冲之外,延迟渲染还需要利用额外的缓冲 —— 这些缓冲被统称为 GBuffer。
# 原理
在光照计算 Pass 中直接存储法线,表面情况等,从 Gbuff 取到数据后直接在光照 Pass 中计算,故而延迟渲染和场景复杂度不挂钩,通常就只使用两个 Pass 完成故而效率较高。
# 缺点
- 不支持真正的抗锯齿(anti-aliasing)功能。
- 不能处理半透明物体。
- 对显卡有一定要求。如果要使用延迟渲染的话,显卡必须支持 MRT(Multiple Render Targets)、Shader Mode 3.0 及以上、深度渲染纹理以及双面的模板缓冲。
通常我们使用前向渲染路径类型:)
# Unity 光源类型
# 阴影
# 不透明物体的阴影
将 CastShaows 设置为 Two Sided
可以双面渲染。
Unity 选择使用一个额外的 Pass 来专门更新光源的阴影映射纹理,这个 Pass 就是
LightMode
标签被设置为ShadowCaster
的Pass
。
- 接收阴影:对阴影纹理进行采样,最终把阴影纹理和光照进行相乘。
- 投射阴影:加入到光源的阴影纹理计算中,通过指定的 Pass 来实现,如果使用了屏幕空间投射,还会生成一张摄像机的深度图。
# 接收阴影
- 在 BasePass 中包含内置文件:
#include "AutoLight.cginc" |
- 在顶点着色器的输出结构体中添加
SHADOW_COORDS
SHADOW_COORDS(2)
- 在顶点着色器返回之前添加一个内置宏
TRANSFER_SHADOW
TRANSFER_SHADOW(o)
- 在片元着色器中计算阴影值,使用
SHADOW_ATTENUATION
fixed shadow=SHADOW_ATTENUATION(i)
📖PS: 由于这些宏中会使用上下文变量来进行相关计算,例如 TRANSFER_SHADOW 会使用 v.vertex 或 a.pos 来计算坐标,因此为了能够让这些宏正确工作,我们需要保证自定义的变量名和这些宏中使用的变量名相匹配。我们需要保证:a2f 结构体中的顶点坐标变量名必须是 vertex,顶点着色器的输出结构体 v2f 必须命名为 v,且 v2f 中的顶点位置变量必须命名为 pos。