US3S5L2——在Shader中判断光源类型

知识回顾

  1. 预处理指令:Shader中也存在预处理指令,它的使用和C#的预处理器指令类似

  2. 前向渲染路径在哪里进行光照计算

    • Base Pass(基础渲染通道):主要用于处理影响该物体的一个高质量光源(平行光)、所有中(逐顶点处理)低质量(SH处理)光源 等
    • Additional Pass(附加渲染通道):主要用于处理影响该物体的除平行光以外的其它高质量光源(每个高质量光源都会调用)

本章代码关键字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Unity 内置的判断光源的宏
_DIRECTIONAL_LIGHT //平行光
_POINT_LIGHT //点光源
_SPOT_LIGHT //聚光灯
// 使用宏定义进行条件编译
#if defined()
#elif defined()
#else
#endif
// 更准确的判断光源类型的宏
AutoLight.cginc //提供宏的cginc文件,需要#include
USING_DIRECTIONAL_LIGHT //平行光
POINT //点光源
SPOT //聚光灯

前向渲染路径主要关注的处理内容

两个 Pass​:

  1. Base Pass(基础渲染通道,每个片元只会计算一次):

    只需要处理一个逐像素平行光源(一般场景中最亮会自动赋值对应变量),其他的中(逐顶点)、低质量(SH)光源,Unity 会帮助我们处理

  2. Additional Pass(附加渲染通道):

    除了最亮的平行光、其他高质量的光源(可能是平行光、点光源、聚光灯)都会调用一次该 Pass​ 进行计算

因此我们一般需要在 Additional Pass 中判断光源类型来分别处理部分逻辑

如何在 Shader 中判断光源类型

Unity中提供了三个重要的宏,分别是:

  • _DIRECTIONAL_LIGHT​:平行光
  • _POINT_LIGHT​:点光源
  • _SPOT_LIGHT​:聚光灯

宏在这里的作用:

可以用于在编译时根据条件判断来包含或排除不同的代码块,实现条件编译
我们可以使用这些宏配合 Unity Shader 中的条件编译预处理指令,用于在编译时根据一定的条件选择性地包含或排除代码块

1
2
3
4
5
6
7
8
9
#if defined(_DIRECTIONAL_LIGHT)
平行光逻辑
#elif defined(_POINT_LIGHT)
点光源逻辑
#elif defined(_SPOT_LIGHT)
聚光灯逻辑
#else
其他逻辑
#endif

不过,上面的宏判断,可能会出现无法准确判断光源类型的问题,导致探照灯在平面上显示时会出现问题,
因此我们采用 AutoLight.cginc​ 内置文件中的宏来进行判断,它为我们提供了如下宏去做判断:

  • USING_DIRECTIONAL_LIGHT​:平行光
  • POINT​:点光源
  • SPOT​:聚光灯

因此判断代码修改为:

首先需要引用 AutoLight.cginc

1
#include "AutoLight.cginc"

然后,根据 USING_DIRECTIONAL_LIGHT​ 是否被定义来判断光源是否为平行光,若不是,则通过 POINT​ 和 SPOT​ 来判断是点光源还是聚光灯

1
2
3
4
5
6
7
8
9
10
11
#ifdef ​USING_DIRECTIONAL_LIGHT​
// 平行光逻辑
#else
#if defined(POINT)
// 点光源逻辑
#elif defined(SPOT)
// 聚光灯逻辑
#else
// 其他逻辑
#endif
#endif

其中 #ifdef​ 用来判断一个宏是否被定义

Unity 底层会根据该条件编译指令生成多个 Shader Variants(着色器变体)
这些 Variants 变体共享相同的核心代码,但根据条件编译的选择会包含不同的代码块

Shader variants 的基本概念是在编写 shader 时,通过条件编译指令(if​, #elif​,#else​,#endif​)
根据不同的配置选项生成多个版本的 shader。这些不同版本的 shader 称为 shader variants。