GameTech/URP 渲染实战
源码 / Ch05/Editor/Ch05Shots.cs
Ch05/Editor/Ch05Shots.cs
// 第 5 章配图:经典光照模型的参数扫描与对比
using Cookbook.Core;
using UnityEngine;

namespace Cookbook.Ch05
{
    static class Ch05Shots
    {
        static readonly Color Orange = new Color(0.85f, 0.5f, 0.25f);
        static readonly Color DarkBg = new Color(0.10f, 0.11f, 0.13f);

        // KeywordEnum:Unity 2022 新建材质时会按属性默认值自动打开一个关键字,
        // 所以切换时要同时设属性值、打开目标关键字、关掉同组其它关键字
        static void SetKeywordEnum(Material m, string prop, string[] options, int index)
        {
            m.SetFloat(prop, index);
            for (int i = 0; i < options.Length; i++)
            {
                string kw = (prop + "_" + options[i]).ToUpperInvariant();
                if (i == index) m.EnableKeyword(kw); else m.DisableKeyword(kw);
            }
        }

        // 低多边形 UV 球:用来放大逐顶点光照的问题
        static Mesh UvSphere(int lon, int lat)
        {
            var verts = new System.Collections.Generic.List<Vector3>();
            var tris = new System.Collections.Generic.List<int>();
            for (int y = 0; y <= lat; y++)
            {
                float v = (float)y / lat * Mathf.PI;
                for (int x = 0; x <= lon; x++)
                {
                    float u = (float)x / lon * Mathf.PI * 2f;
                    verts.Add(new Vector3(Mathf.Sin(v) * Mathf.Cos(u), Mathf.Cos(v), Mathf.Sin(v) * Mathf.Sin(u)) * 0.5f);
                }
            }
            for (int y = 0; y < lat; y++)
                for (int x = 0; x < lon; x++)
                {
                    int a = y * (lon + 1) + x, b = a + lon + 1;
                    tris.Add(a); tris.Add(a + 1); tris.Add(b);
                    tris.Add(b); tris.Add(a + 1); tris.Add(b + 1);
                }
            var m = new Mesh();
            m.SetVertices(verts);
            m.SetTriangles(tris, 0);
            var n = new Vector3[verts.Count];
            for (int i = 0; i < n.Length; i++) n[i] = verts[i].normalized;   // 光滑法线
            m.normals = n;
            m.RecalculateBounds();
            return m;
        }

        // 一排球用正交相机拍:每个球的观察角度完全相同,对比才公平
        static void RowCamera(Shot s, float y = 0.5f, float halfHeight = 0.97f)
        {
            s.Background(DarkBg);
            s.LookAt(new Vector3(0, y, -8f), new Vector3(0, y, 0));
            s.Cam.orthographic = true;
            s.Cam.orthographicSize = halfHeight;
        }

        [BookShot("ch05-lambert", W = 640, H = 480, Panels = 3)]
        static void Lambert(Shot s)
        {
            s.Floor();
            var m = s.Mat("Cookbook/Ch05/Lambert");
            m.SetColor("_BaseColor", s.Panel == 0 ? Color.white * 0.8f : Orange);
            m.SetFloat("_AmbientStrength", s.Panel == 2 ? 1f : 0f);
            s.Sphere(m, new Vector3(0, 0.8f, 0), 1.5f);
            s.SunDir(30, -95);
            s.LookAt(new Vector3(0, 1.4f, -4.2f), new Vector3(0, 0.75f, 0), 32);
        }

        [BookShot("ch05-lightcolor", W = 640, H = 480, Panels = 3)]
        static void LightColor(Shot s)
        {
            s.Floor();
            var m = s.Mat("Cookbook/Ch05/Lambert");
            m.SetColor("_BaseColor", new Color(0.8f, 0.8f, 0.8f));
            m.SetFloat("_AmbientStrength", 0.15f);
            s.Sphere(m, new Vector3(0, 0.8f, 0), 1.5f);
            s.SunDir(40, -60, 1f);
            if (s.Panel == 1) s.Sun.color = new Color(1f, 0.55f, 0.25f);
            if (s.Panel == 2) s.Sun.intensity = 3f;
            s.LookAt(new Vector3(0, 1.4f, -4.2f), new Vector3(0, 0.75f, 0), 32);
        }

        [BookShot("ch05-halflambert", W = 640, H = 480, Panels = 3)]
        static void HalfLambert(Shot s)
        {
            s.Floor();
            Material m;
            if (s.Panel == 0) m = s.Mat("Cookbook/Ch05/Lambert");
            else { m = s.Mat("Cookbook/Ch05/HalfLambert"); m.SetFloat("_Power", s.Panel == 1 ? 1f : 2f); }
            m.SetColor("_BaseColor", Orange);
            m.SetFloat("_AmbientStrength", 0f);
            s.Sphere(m, new Vector3(0, 0.8f, 0), 1.5f);
            s.SunDir(30, -95);
            s.LookAt(new Vector3(0, 1.4f, -4.2f), new Vector3(0, 0.75f, 0), 32);
        }

        [BookShot("ch05-wrap", W = 960, H = 420)]
        static void Wrap(Shot s)
        {
            RowCamera(s, 1.1f, 1.36f);
            float[] w = { 0f, 0.25f, 0.5f, 0.75f, 1f };
            for (int row = 0; row < 2; row++)
            {
                int r = row;
                s.SphereRow(5, i =>
                {
                    var m = s.Mat("Cookbook/Ch05/WrapLighting");
                    m.SetColor("_BaseColor", Orange);
                    m.SetFloat("_Wrap", w[i]);
                    m.SetFloat("_AmbientStrength", 0.15f);
                    if (r == 0) m.EnableKeyword("_WRAP_ENERGY_CONSERVING");
                    return m;
                }, y: row == 0 ? 0.5f : 1.7f, step: 1.15f);
            }
            s.SunDir(10, -90);
        }

        [BookShot("ch05-wrap-tint", W = 640, H = 480, Panels = 2)]
        static void WrapTint(Shot s)
        {
            s.Floor();
            var m = s.Mat("Cookbook/Ch05/WrapLighting");
            m.SetColor("_BaseColor", new Color(0.8f, 0.6f, 0.5f));
            m.SetFloat("_Wrap", s.Panel == 0 ? 0f : 0.5f);
            m.EnableKeyword("_WRAP_ENERGY_CONSERVING");
            m.SetColor("_TerminatorTint", s.Panel == 0 ? Color.black : new Color(0.7f, 0.12f, 0.05f));
            m.SetFloat("_AmbientStrength", 0.3f);
            s.Sphere(m, new Vector3(0, 0.8f, 0), 1.5f);
            s.SunDir(25, -95);
            s.LookAt(new Vector3(0, 1.4f, -4.2f), new Vector3(0, 0.75f, 0), 32);
        }

        [BookShot("ch05-phong-shininess", W = 960, H = 300)]
        static void PhongShininess(Shot s)
        {
            RowCamera(s);
            float[] n = { 2f, 8f, 32f, 128f, 512f };
            s.SphereRow(5, i =>
            {
                var m = s.Mat("Cookbook/Ch05/Phong");
                m.SetFloat("_Shininess", n[i]);
                m.SetFloat("_AmbientStrength", 0.4f);
                return m;
            }, step: 1.15f);
            s.SunDir(35, 25);
        }

        [BookShot("ch05-phong-vs-blinn", W = 640, H = 480, Panels = 3)]
        static void PhongVsBlinn(Shot s)
        {
            s.Background(DarkBg);
            Material m = s.Mat(s.Panel == 0 ? "Cookbook/Ch05/Phong" : "Cookbook/Ch05/BlinnPhong");
            m.SetColor("_BaseColor", new Color(0.12f, 0.16f, 0.22f));
            m.SetColor("_SpecColor", new Color(0.8f, 0.8f, 0.8f));
            m.SetFloat("_Shininess", s.Panel == 2 ? 128f : 32f);
            m.SetFloat("_AmbientStrength", 0.3f);
            s.Grid(m, new Vector3(0, 0, 15f), 40f, 16);
            // 逆光、低角度:光从远处贴着平面照向相机,这是两种模型差异最大的情形
            s.SunDir(12, 180, 1.2f);
            s.LookAt(new Vector3(0, 1.3f, -3f), new Vector3(0, 0f, 2.5f), 45);
        }

        [BookShot("ch05-normalized", W = 960, H = 420)]
        static void Normalized(Shot s)
        {
            RowCamera(s, 1.1f, 1.36f);
            float[] n = { 8f, 32f, 128f, 512f, 2048f };
            for (int row = 0; row < 2; row++)
            {
                int r = row;
                s.SphereRow(5, i =>
                {
                    var m = s.Mat("Cookbook/Ch05/SpecularNormalized");
                    m.SetFloat("_Shininess", n[i]);
                    SetKeywordEnum(m, "_Model", new[] { "BlinnPhong", "Phong" }, 0);
                    if (r == 0) m.EnableKeyword("_NORMALIZE_SPECULAR"); else m.DisableKeyword("_NORMALIZE_SPECULAR");
                    m.SetFloat("_AmbientStrength", 0.5f);
                    return m;
                }, y: row == 0 ? 0.5f : 1.7f, step: 1.15f);
            }
            s.SunDir(35, 25, 1.2f);
        }

        static void RoughRow(Shot s)
        {
            RowCamera(s);
            s.SphereRow(5, i =>
            {
                var m = s.Mat("Cookbook/Ch05/RoughDiffuse");
                SetKeywordEnum(m, "_Model", new[] { "Lambert", "OrenNayar", "Minnaert" }, i == 0 ? 0 : i == 4 ? 2 : 1);
                m.SetFloat("_Roughness", i == 1 ? 0.25f : i == 2 ? 0.5f : 1.0f);
                m.SetFloat("_MinnaertK", 0.7f);
                m.SetFloat("_AmbientStrength", 0.15f);
                return m;
            }, step: 1.15f);
        }

        [BookShot("ch05-roughdiffuse-front", W = 960, H = 300)]
        static void RoughFront(Shot s) { RoughRow(s); s.SunDir(8, 3, 1.3f); }      // 光源几乎在相机身后

        [BookShot("ch05-roughdiffuse-side", W = 960, H = 300)]
        static void RoughSide(Shot s) { RoughRow(s); s.SunDir(15, -80, 1.3f); }    // 侧光

        [BookShot("ch05-gouraud", W = 640, H = 480, Panels = 3)]
        static void Gouraud(Shot s)
        {
            s.Floor();
            var m = s.Mat("Cookbook/Ch05/Gouraud");
            if (s.Panel == 1) m.DisableKeyword("_PER_VERTEX"); else m.EnableKeyword("_PER_VERTEX");
            var pos = new Vector3(0, 0.8f, 0);
            if (s.Panel == 2) s.Sphere(m, pos, 1.5f);
            else s.MeshObj(UvSphere(14, 9), m, pos, 1.5f, "LowPolySphere");
            s.SunDir(40, 25);
            s.LookAt(new Vector3(0, 1.4f, -4.2f), new Vector3(0, 0.75f, 0), 32);
        }

        [BookShot("ch05-classiclab", W = 480, H = 420, Panels = 4)]
        static void ClassicLab(Shot s)
        {
            s.Floor();
            var m = s.Mat("Cookbook/Ch05/ClassicLab");
            int[] spec = { 1, 2, 2, 0 };   // Phong, BlinnPhong, BlinnPhong, None
            Color[] col = { new Color(0.2f, 0.35f, 0.65f), Orange, new Color(0.8f, 0.55f, 0.45f), new Color(0.7f, 0.68f, 0.62f) };
            SetKeywordEnum(m, "_Diffuse", new[] { "Lambert", "HalfLambert", "Wrap", "OrenNayar" }, s.Panel);
            SetKeywordEnum(m, "_Specular", new[] { "None", "Phong", "BlinnPhong" }, spec[s.Panel]);
            m.SetColor("_BaseColor", col[s.Panel]);
            m.SetFloat("_Smoothness", s.Panel == 2 ? 0.45f : 0.6f);
            m.SetFloat("_Wrap", 0.5f);
            m.SetFloat("_Roughness", 0.8f);
            m.SetFloat("_AmbientStrength", 0.6f);
            s.Knot(m, new Vector3(0, 1.0f, 0), 1.1f);
            s.SunDir(35, -60);
            s.LookAt(new Vector3(0, 1.7f, -4.3f), new Vector3(0, 0.9f, 0), 34);
        }
    }
}