GameTech/URP 渲染实战

案例 9.8同一个金球,两种工作流入门

目标:用 URP 自带的 Lit 验证两种工作流在“合法”参数下完全等价,并展示镜面工作流独有的自由度。

Assets/Cookbook/Ch09/Editor/Ch09Shots.cs(节选:workflow)
[BookShot("ch09-workflow", W = 420, H = 320, Panels = 3)]
static void Workflow(Shot s)
{
    s.Floor();
    Material m;
    if (s.Panel == 0)
    {
        m = s.Lit(new Color(1f, 0.78f, 0.34f), 1f, 0.75f);          // 金属度工作流:金属 = 1,底色即 F0
    }
    else
    {
        m = s.Lit(Color.black, 0f, 0.75f);
        m.SetFloat("_WorkflowMode", 0);
        m.EnableKeyword("_SPECULAR_SETUP");                        // 脚本建的材质不会跑 ShaderGUI,关键字要自己开
        if (s.Panel == 1)
            m.SetColor("_SpecColor", new Color(1f, 0.78f, 0.34f)); // 镜面工作流:漫反射黑、镜面色 = 同样的金色
        else
        {
            m.SetColor("_BaseColor", new Color(0.7f, 0.08f, 0.06f));
            m.SetColor("_SpecColor", new Color(0.25f, 0.45f, 0.75f)); // 红色漫反射 + 蓝色高光:金属度工作流表达不了
        }
    }
    s.Sphere(m, new Vector3(0, 0.75f, 0), 1.5f);
    s.LookAt(new Vector3(0, 1.35f, -4.0f), new Vector3(0, 0.7f, 0), 32);
}
图 9-22左:金属度工作流,BaseColor 为金色、Metallic = 1;中:镜面工作流,Albedo 为黑、Specular 为同样的金色,两者逐像素一致;右:镜面工作流的“非物理”组合——红色漫反射加蓝色高光,金属度工作流无法表达。
常见坑用脚本 new Material(Shader.Find("Universal Render Pipeline/Lit")) 创建材质时,材质面板(ShaderGUI)不会运行,所以面板上切换工作流、勾选法线贴图时自动设置的关键字都不会被设置。要切到镜面工作流,必须自己 EnableKeyword("_SPECULAR_SETUP");用了法线贴图要开 _NORMALMAP。忘了这一步,参数填了也没用。

粗糙度:Smoothness、感知粗糙度与 α

同一个“粗糙度”在不同地方有三种写法,读源码时一定要分清:

α=r2=(1−s)2

为什么要平方?Burley 在 Disney 报告里指出,如果直接把滑条线性映射到 α,滑条的前半段(α 从 0 到 0.5)高光就已经很糊了,后半段几乎看不出变化。平方之后,滑条每走一步,人眼感受到的变化大致均匀,所以叫“感知”粗糙度。

0 0.25 0.5 0.75 1 0 0.2 0.4 0.6 0.8 1 感知粗糙度 perceptualRoughness = 1 − smoothness 数值 线性(参照) α = perceptualRoughness² α² (GGX 公式里真正出现的量)
图 9-23感知粗糙度到 α 与 α² 的映射。感知粗糙度 0.5 时,α 只有 0.25,α² 只有 0.0625——GGX 的峰值因此仍然很高。

在 half 精度下,α 太小会导致 α2 下溢成 0,GGX 变成除零。URP 在 InitializeBRDFData 里用 HALF_MIN_SQRT(2−7)和 HALF_MIN(2−14)分别给 α 和 α2 设了下限,自己写 Shader 时也要做类似的钳制。