# 复杂光照

在之前的学习中,我们使用的都是单一光照,实际在游戏中可能存在多个光源,不同的光源会互相影响构成真正的光照系统,在这一节我们将实现一个真正可用的光照模型。

# 渲染路径 Rendering Path

📖 决定了光照是如何应用到 Unity Shader 中的。

我们只有为 Shader 正确地选择和设置了需要的渲染路径,该 Shader 的光照计算才能被正确执行。

🏷渲染路径的分类

  • 前向渲染路径(Forward Rendering Path)
  • 延迟渲染路径(Deferred Rendering Path)
  • 顶点照明渲染路径(Vertex Lit Rendering Path)—— 已经被弃用

目前 unity 提供了新的渲染路径代替了原来的渲染路径。

默认使用 Graphics 中的设置,可以在摄像机中对设置进行覆盖。

image-20220902112709918

在 Shader Pass 中来指定该 Pass 使用的渲染路径:

Pass  { Tags  {  "LightMode"  =  "ForwardBase"  } }

LightMode 标签可供使用的渲染路径:

image-20220902112959760

💭为 Pass 指定正确的渲染路径对于 Unity 光照变量正确赋值具有重要意义。

Unity 默认使用前向渲染光照路径.

# 前向渲染路径

前向渲染是传统渲染方式,也是我们常用的一种渲染路径。

📖 原理:每进行一次完整的前向渲染,我们需要渲染该对象的渲染图元,并计算两个缓冲区的信息:颜色缓冲区和深度缓冲区。

前向渲染伪代码

image-20220902140220407

每个光照下的物体都需要进行一次逐像素的 Pass,也就是在一个场景中,n 个物体 m 个光源的下,渲染场景需要 n*m 次 Pass。

# Unity 中的前向渲染

Unity 中照亮物体有 3 种方式:

  1. 逐顶点处理 Spherical
  2. 逐像素处理 Harmonics
  3. 球谐函数 SH

光源使用哪一种处理方式取决于它的类型和渲染模式。

image-20220902141112298

将渲染类型设置为 Important(重要的)来告诉 Unity 这个光源是重要的,把它当成一个逐像素光源来处理。

通常,Unity 会自动根据远近、强度等参数对光源的重要性进行排序。

  • 场景中最亮的平行光总是按逐像素处理的。

  • 渲染模式被设置成 Not Important 的光源,会按逐顶点或者 SH 处理。・渲染模式被设置成 Important 的光源,会按逐像素处理。

  • 如果根据以上规则得到的逐像素光源数量小于 Quality Setting 中的逐像素光源数量 (Pixel Light Count),会有更多的光源以逐像素的方式进行渲染。

# 内置变量函数

image-20220902142017381

image-20220902142034183

# 相关参考

Unity 渲染路径 - 知乎 (zhihu.com)

# 顶点照明渲染路径

顶点照明渲染路径是对硬件配置要求最少、运算性能最高,但同时也是得到的效果最差的一种类型,它不支持那些逐像素才能得到的效果,例如阴影、法线映射、高精度的高光反射等。

📖同理,使用逐顶点的方式进行光照计算。—— 是前向渲染的一个子集。

# 延迟渲染路径

当场景中包含大量实时光源时,前向选人的性能会急速下降。

除了前向渲染中使用的颜色缓冲和深度缓冲之外,延迟渲染还需要利用额外的缓冲 —— 这些缓冲被统称为 GBuffer。

# 原理

image-20220902143817442

在光照计算 Pass 中直接存储法线,表面情况等,从 Gbuff 取到数据后直接在光照 Pass 中计算,故而延迟渲染和场景复杂度不挂钩,通常就只使用两个 Pass 完成故而效率较高。

# 缺点

  • 不支持真正的抗锯齿(anti-aliasing)功能。
  • 不能处理半透明物体。
  • 对显卡有一定要求。如果要使用延迟渲染的话,显卡必须支持 MRT(Multiple Render Targets)、Shader Mode 3.0 及以上、深度渲染纹理以及双面的模板缓冲。

通常我们使用前向渲染路径类型:)

# Unity 光源类型

# 阴影

# 不透明物体的阴影

image-20220913113734111

通过MeshRender可以设置阴影

将 CastShaows 设置为 Two Sided 可以双面渲染。

Unity 选择使用一个额外的 Pass 来专门更新光源的阴影映射纹理,这个 Pass 就是 LightMode 标签被设置为 ShadowCasterPass

  • 接收阴影:对阴影纹理进行采样,最终把阴影纹理和光照进行相乘。
  • 投射阴影:加入到光源的阴影纹理计算中,通过指定的 Pass 来实现,如果使用了屏幕空间投射,还会生成一张摄像机的深度图。

# 接收阴影

  1. 在 BasePass 中包含内置文件:
#include "AutoLight.cginc"
  1. 在顶点着色器的输出结构体中添加 SHADOW_COORDS

SHADOW_COORDS(2)

  1. 在顶点着色器返回之前添加一个内置宏 TRANSFER_SHADOW

TRANSFER_SHADOW(o)

  1. 在片元着色器中计算阴影值,使用 SHADOW_ATTENUATION

fixed shadow=SHADOW_ATTENUATION(i)

📖PS: 由于这些宏中会使用上下文变量来进行相关计算,例如 TRANSFER_SHADOW 会使用 v.vertex 或 a.pos 来计算坐标,因此为了能够让这些宏正确工作,我们需要保证自定义的变量名和这些宏中使用的变量名相匹配。我们需要保证:a2f 结构体中的顶点坐标变量名必须是 vertex,顶点着色器的输出结构体 v2f 必须命名为 v,且 v2f 中的顶点位置变量必须命名为 pos。

# 透明物体的阴影