GameTech/URP 渲染实战
源码 / Ch29/MipLevelViz.shader
Ch29/MipLevelViz.shader
// 案例 29.6:Mip 级别可视化。用屏幕空间导数估算 GPU 会选哪一级 Mip,并按级别着色。
// 级别越高 = 一个像素覆盖的纹素越多;如果近处也是高级别,说明贴图分辨率给多了(浪费带宽和内存)。
Shader "Cookbook/Ch29/MipLevelViz"
{
    Properties
    {
        [MainTexture] _BaseMap ("Base Map", 2D) = "white" {}
        _Blend ("Overlay Strength", Range(0, 1)) = 0.65
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" "RenderPipeline"="UniversalPipeline" "Queue"="Geometry" }
        Pass
        {
            Name "MipViz"
            Tags { "LightMode"="UniversalForward" }
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

            TEXTURE2D(_BaseMap); SAMPLER(sampler_BaseMap);
            float4 _BaseMap_TexelSize;   // (1/w, 1/h, w, h),由 Unity 自动设置
            CBUFFER_START(UnityPerMaterial)
                float4 _BaseMap_ST;
                half _Blend;
            CBUFFER_END

            struct Attributes { float4 positionOS : POSITION; float2 uv : TEXCOORD0; };
            struct Varyings { float4 positionCS : SV_POSITION; float2 uv : TEXCOORD0; };

            Varyings vert(Attributes input)
            {
                Varyings o;
                o.positionCS = TransformObjectToHClip(input.positionOS.xyz);
                o.uv = TRANSFORM_TEX(input.uv, _BaseMap);
                return o;
            }

            float ComputeMipLevel(float2 uv, float2 texSize)
            {
                // 相邻像素之间 uv 变化了多少个纹素
                float2 dx = ddx(uv * texSize);
                float2 dy = ddy(uv * texSize);
                float maxSq = max(dot(dx, dx), dot(dy, dy));
                return 0.5 * log2(maxSq);   // = log2(最大纹素跨度);<0 表示放大(一个纹素盖住多个像素)
            }

            half3 MipColor(float mip)
            {
                static const half3 c[7] = { half3(0.1, 0.35, 1), half3(0.15, 0.8, 0.3), half3(0.95, 0.9, 0.15),
                                            half3(1, 0.55, 0.1), half3(0.95, 0.15, 0.1), half3(0.8, 0.1, 0.7), half3(0.35, 0.1, 0.5) };
                // c[0] 给“放大”区(mip<0),c[1] 起依次是 mip 0,1,2...
                float m = clamp(mip + 1, 0, 6);
                int i = (int)floor(m);
                return lerp(c[i], c[min(i + 1, 6)], frac(m));
            }

            half4 frag(Varyings i) : SV_Target
            {
                half3 albedo = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, i.uv).rgb;
                float mip = ComputeMipLevel(i.uv, _BaseMap_TexelSize.zw);
                return half4(lerp(albedo, MipColor(mip), _Blend), 1);
            }
            ENDHLSL
        }
    }
}