自底向上的SRP教程 III

Directional Lights

对 Catlike Coding 教程的解析与补充

点击传送——>Directional Lights

1.设置多个Pass

1
2
private static ShaderTagId litShaderTagId = new ShaderTagId("CustomLit");
drawingSettings.SetShaderPassName(1, litShaderTagId);

DrawRendererSettings.SetShaderPassName(index,shaderPassName), index是Pass索引,用于控制绘制传递的顺序,shaderPassName为Pass名称

2.计算入射光照

1
2
3
4
float3 IncomingLight(Surface surface, Light light)
{
return saturate(dot(surface.normal, light.direction)) * light.color;
}
  • 表面法线与光照方向点积后乘灯光颜色得到入射光照
  • saturate(v) 将v夹取到[0,1]区间(表面朝向光源时才是对的)

3.向GPU发送灯光数据

1
2
3
4
5
6
private void SetupDirectionLight()
{
Light light = RenderSettings.sun;
buffer.SetGlobalVector(dirLightColorId, light.color.linear * light.intensity);
buffer.SetGlobalVector(dirLightDirectionId, -light.transform.forward);
}
  • RenderSettings.sun 访问场景主光源(可以在 Window -> Rendering -> LightingSetting 设置)
  • color.linear 关于为什么用线性值:
    物理世界中光的强度和亮度成正比,也就是线性(Linear)关系,但光电转换(显示器)却不是线性的,亮度增加量等于电压增加量的2.2次幂,这个值称为显示器的Gamma值(当然不一定都是这个值),也就是说当电压线性变化时,对比线性空间,Gamma空间的亮度在暗处变化慢占据数据范围更广,颜色会整体偏暗。当然计算机在处理的时候肯定是直接使用线性空间(对计算机而言都是数据)
    • Gamma校正:因为显示器显示的颜色空间为非线性,当我们输入一张线性空间的图片,输出时颜色就会失真,这时我们需要校正其色彩。实际上只需要进行一个Gamma值的逆运算$c_0=c_{i}^{\frac{1}{Gamma}}$,即可让图片回归线性空间,输出的图片就和物理世界一样。
    • sRGB:上边说了Gamma校正的原理,既然大家都需要做Gamma校正,不妨直接开发一个标准,大家都遵循这个标准,对物理空间的颜色预先做一次处理,然后再进过显示器输出得到物理世界的真实色彩。这个标准就是sRGB,在输出图片时直接对图片进行一次$\frac{1}{Gamma}$的处理(这个幂次一般是1÷2.2≈0.45)
  • -light.transform.forward 这里取负是因为需要光的来源方向而不是照射方向

4.发送多个光源数据

1
2
3
4
private void SetupLights()
{
NativeArray<VisibleLight> visibleLights = cullingResults.visibleLights;
}
  • 不用List用NativeArray的原因是:不需要GC,生成销毁代价低,适合作为临时数据结构
  • cullingResults.visibleLights 获取可见光(Unity在剔除阶段会找到哪些光源会影响相机的可见空间)

5.支持多个方向光

1
2
3
4
5
private void SetupDirectionLight(int index, ref VisibleLight visibleLight)
{
dirLightColors[index] = visibleLight.finalColor;
dirLightDirections[index] = -visibleLight.localToWorldMatrix.GetColumn(2);
}
  • visibleLight.finalColor 灯光的颜色乘以光强得到最终的颜色值
  • visibleLight.localToWorldMatrix 获取光源矩阵
  • GetColumn(2) 至于为什么矩阵第三列为光源方向:光源空间下光源正方形为z轴
    $$
    \begin{bmatrix}
    m_{00} & m_{01} & m_{02} & m_{03} \
    m_{10} & m_{11} & m_{12} & m_{13} \
    m_{20} & m_{21} & m_{22} & m_{23} \
    m_{30} & m_{31} & m_{32} & m_{33} \
    \end{bmatrix}
    \begin{bmatrix}
    0 \
    0 \
    1 \
    0 \
    \end{bmatrix}

    \begin{bmatrix}
    m_{02} \
    m_{12} \
    m_{22} \
    m_{32} \
    \end{bmatrix}
    $$

6. ShaderGUI

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class CustomShaderGUI : ShaderGUI
{
MaterialEditor editor;
Object[] materials;
MaterialProperty[] properties;

public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] properties)
{
base.OnGUI(materialEditor, properties);
this.editor = materialEditor;
materials = editor.targets;
this.properties = properties;
}
}

继承ShaderGUI,在OnGUI方法中实现

  • editor.targets获取所有正在检视的material对象
  • properties获取shader属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
bool SetProperty(string name, float value)
{
MaterialProperty property = FindProperty(name, properties, false);
if(property != null)
{
property.floatValue = value;
return true;
}
return false;
}
void SetProperty(string name, string keyword, bool enable)
{
if(SetProperty(name, enable ? 1f : 0f))
{
SetKeyword(keyword, enable);
}
}
void SetKeyword(string keyword, bool enable)
{
if(enable)
{
foreach(Material m in materials)
{
m.EnableKeyword(keyword);
}
}
else
{
foreach(Material m in materials)
{
m.DisableKeyword(keyword);
}
}
}
  • FindProperty(string propertyName, MaterialProperty[] properties, bool propertyIsMandatory) 查找材质属性
    • propertyName属性名
    • properties属性数组
    • propertyIsMandatory如果传入true,没有找到属性时会抛出异常
(下接 自底向上的SRP教程IV)

自底向上的SRP教程 III
https://baifabaiquan.cn/2023/04/18/自底向上的SRP教程III/
作者
白发败犬
发布于
2023年4月18日
许可协议