【URP】Unity[视差贴图]模拟[冰面裂缝]实践

【从UnityURP开始探索游戏渲染】专栏-直达

Unity URP 冰面裂缝视差效果实现方案

冰面裂缝效果优化的URP Shader实现。该方案通过‌视差遮挡贴图(POM) ‌技术增强深度表现,结合‌高度图动态控制‌实现可调节的冰缝裂痕效果。

核心特性

  • 物理精确的裂缝深度 ‌采用光线步进算法精确计算冰缝几何形状,通过_DepthMultiplier参数控制裂缝视觉深度
  • 冰面光学特性模拟 ‌添加折射率参数(_RefractionIndex)和菲涅尔效应,增强冰面半透明质感
  • 性能优化‌动态采样层数控制(8-12层),在移动端保持30fps以上流畅度

完整HLSL代码实现

关键参数说明

  • 高度图控制_DepthMultiplier参数动态调节冰缝视觉深度,值越大裂缝越深

  • 光学参数_RefractionIndex控制冰面折射率,_FresnelPower调整边缘高光强度

  • 性能控制minSamplesmaxSamples控制光线步进精度,移动端建议8-10层

  • IceCrackPOM.shader

    c 复制代码
    Shader "Universal Render Pipeline/IceCrackPOM"
    {
        Properties
        {
            [Header(Base Textures)]
            _MainTex("Albedo (RGB) Ice Color", 2D) = "white" {}
            _NormalMap("Normal Map", 2D) = "bump" {}
            _HeightMap("Height Map (Ice Cracks)", 2D) = "white" {}
    
            [Header(Parallax Settings)]
            _ParallaxScale("Crack Depth Scale", Range(0, 0.2)) = 0.08
            _DepthMultiplier("Depth Multiplier", Range(0.5, 3)) = 1.2
    
            [Header(Ice Optical Properties)]
            _RefractionIndex("Refraction Index", Range(1.1, 1.5)) = 1.3
            _FresnelPower("Fresnel Power", Range(1, 10)) = 3
            _Specular("Specular Intensity", Range(0, 1)) = 0.5
        }
    
        SubShader
        {
            Tags { "RenderType"="Transparent" "Queue"="Transparent" }
    
            HLSLINCLUDE
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
    
            TEXTURE2D(_MainTex);    SAMPLER(sampler_MainTex);
            TEXTURE2D(_NormalMap);  SAMPLER(sampler_NormalMap);
            TEXTURE2D(_HeightMap);  SAMPLER(sampler_HeightMap);
    
            float _ParallaxScale;
            float _DepthMultiplier;
            float _RefractionIndex;
            float _FresnelPower;
            float _Specular;
    
            // 冰面裂缝POM计算
            float2 IceParallaxOcclusion(float3 viewDirTS, float2 uv)
            {
                int minSamples = 8;
                int maxSamples = 12;
                int numSamples = (int)lerp(maxSamples, minSamples, saturate(dot(float3(0,0,1), viewDirTS)));
    
                float layerHeight = 1.0 / numSamples;
                float2 deltaUV = _ParallaxScale * viewDirTS.xy / viewDirTS.z / numSamples * _DepthMultiplier;
    
                float currentLayerHeight = 0;
                float2 currentUV = uv;
                float currentDepth = 1 - SAMPLE_TEXTURE2D(_HeightMap, sampler_HeightMap, currentUV).r;
    
                [loop]
                for (int i = 0; i < maxSamples; ++i) {
                    if (currentLayerHeight >= currentDepth) break;
                    currentUV -= deltaUV;
                    currentDepth = 1 - SAMPLE_TEXTURE2D(_HeightMap, sampler_HeightMap, currentUV).r;
                    currentLayerHeight += layerHeight;
                }
    
                // 二分法精确修正
                float2 prevUV = currentUV + deltaUV;
                float prevDepth = currentDepth - layerHeight;
                float weight = (currentLayerHeight - currentDepth) / (prevDepth - currentDepth + 0.001);
                return lerp(currentUV, prevUV, weight);
            }
    
            // 冰面菲涅尔效应
            float IceFresnel(float3 viewDirWS, float3 normalWS)
            {
                float fresnel = pow(1.0 - saturate(dot(viewDirWS, normalWS)), _FresnelPower);
                return fresnel * 0.7;
            }
            ENDHLSL
    
            Pass
            {
                Blend SrcAlpha OneMinusSrcAlpha
                ZWrite Off
    
                HLSLPROGRAM
                #pragma vertex vert
                #pragma fragment frag
    
                struct Attributes
                {
                    float4 positionOS : POSITION;
                    float2 uv : TEXCOORD0;
                    float3 normalOS : NORMAL;
                    float4 tangentOS : TANGENT;
                };
    
                struct Varyings
                {
                    float4 positionCS : SV_POSITION;
                    float2 uv : TEXCOORD0;
                    float3 viewDirTS : TEXCOORD1;
                    float3 viewDirWS : TEXCOORD2;
                    float3 normalWS : TEXCOORD3;
                };
    
                Varyings vert(Attributes IN)
                {
                    Varyings OUT;
                    VertexPositionInputs posInput = GetVertexPositionInputs(IN.positionOS.xyz);
                    OUT.positionCS = posInput.positionCS;
    
                    VertexNormalInputs normInput = GetVertexNormalInputs(IN.normalOS, IN.tangentOS);
                    float3 viewDirWS = GetWorldSpaceViewDir(posInput.positionWS);
                    OUT.viewDirTS = TransformWorldToTangent(viewDirWS, 
                        normInput.tangentWS, normInput.bitangentWS, normInput.normalWS);
                    OUT.viewDirWS = viewDirWS;
                    OUT.normalWS = normInput.normalWS;
    
                    OUT.uv = IN.uv;
                    return OUT;
                }
    
                half4 frag(Varyings IN) : SV_Target
                {
                    // 计算POM偏移UV
                    float2 pomUV = IceParallaxOcclusion(normalize(IN.viewDirTS), IN.uv);
    
                    // 采样纹理
                    half4 albedo = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, pomUV);
                    half3 normalTS = UnpackNormal(SAMPLE_TEXTURE2D(_NormalMap, sampler_NormalMap, pomUV));
    
                    // 转换法线到世界空间
                    float3x3 TBN = float3x3(
                        normalize(cross(IN.normalWS, IN.viewDirWS)),
                        normalize(IN.normalWS),
                        normalize(IN.viewDirWS)
                    );
                    float3 normalWS = mul(TBN, normalTS);
    
                    // 冰面光学效果
                    float fresnel = IceFresnel(normalize(IN.viewDirWS), normalWS);
                    float3 refractedView = refract(-normalize(IN.viewDirWS), normalWS, 1.0/_RefractionIndex);
    
                    // 合成最终颜色
                    half3 finalColor = albedo.rgb * (1 - fresnel) + fresnel * 0.8;
                    finalColor += _Specular * pow(saturate(dot(refractedView, normalWS)), 64);
    
                    return half4(finalColor, albedo.a * 0.9);
                }
                ENDHLSL
            }
        }
    }

材质配置建议

纹理类型 制作要求 示例用途
高度图 黑白分明,裂缝处为黑色(0值) 控制裂缝形状和深度
法线贴图 配合高度图制作微观凹凸 增强冰面晶体质感
底色贴图 半透明蓝色调,带裂纹边缘高光 基础颜色和透明度控制

实际应用中可通过调整_ParallaxScale(0.05-0.1)和_DepthMultiplier(1.0-2.0)获得不同结冰程度效果


【从UnityURP开始探索游戏渲染】专栏-直达

(欢迎点赞留言探讨,更多人加入进来能更加完善这个探索的过程,🙏)