【URP】Unity[视差贴图]模拟[风格化地形]实践

URP HLSL完整实现代码

关键特性说明

  • 动态层数优化‌:通过lerp(_MaxLayers, _MinLayers, saturate(dot(float3(0,0,1), viewDirTS)))实现平视视角自动增加采样精度

  • 抗失真处理‌:_LayerBias参数修正陡峭表面的UV偏移计算,避免采样断裂

  • 风格化增强‌:ramp贴图控制光影过渡,边缘光强化轮廓立体感

  • StylizedRockParallax.shader

    
    
    Shader "Universal Render Pipeline/StylizedRockParallax"
    {
    Properties
    {
    [Header(Base Textures)]
    _MainTex("Albedo (RGB)", 2D) = "white" {}
    _NormalMap("Normal Map", 2D) = "bump" {}
    _HeightMap("Height Map", 2D) = "white" {}
    _RampTex("Stylized Ramp", 2D) = "white" {}
    [Header(Parallax Settings)]
    _ParallaxScale("Depth Scale", Range(0, 0.15)) = 0.08
    _LayerBias("Layer Bias", Range(0.1, 0.5)) = 0.3
    _MinLayers("Min Layers", Int) = 5
    _MaxLayers("Max Layers", Int) = 12
    [Header(Stylized Lighting)]
    _RimPower("Rim Power", Range(1, 10)) = 3
    _ShadowTint("Shadow Tint", Color) = (0.3,0.3,0.4,1)
    }
    SubShader
    {
    Tags { "RenderType"="Opaque" "RenderPipeline"="UniversalPipeline" }
    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);
    TEXTURE2D(_RampTex); SAMPLER(sampler_RampTex);
    float _ParallaxScale;
    float _LayerBias;
    int _MinLayers, _MaxLayers;
    float _RimPower;
    float4 _ShadowTint;
    // 陡峭视差映射核心算法
    float2 SteepParallaxMapping(float3 viewDirTS, float2 uv)
    {
    // 动态层数计算(平视视角增加层数)
    int numLayers = (int)lerp(_MaxLayers, _MinLayers, saturate(dot(float3(0,0,1), viewDirTS)));
    float layerHeight = 1.0 / numLayers;
    float2 deltaUV = _ParallaxScale * viewDirTS.xy / (viewDirTS.z + _LayerBias) / numLayers;
    // 光线步进初始化
    float currentLayerHeight = 0;
    float2 currentUV = uv;
    float currentDepth = 1 - SAMPLE_TEXTURE2D(_HeightMap, sampler_HeightMap, currentUV).r;
    // 分层深度检测
    [loop]
    for (int i = 0; i < _MaxLayers; ++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 = pow((currentLayerHeight - currentDepth) / (prevDepth - currentDepth + 0.001), 2);
    return lerp(currentUV, prevUV, saturate(weight * 1.5));
    }
    // 风格化光照计算
    half3 StylizedShading(float3 normalWS, float3 viewDirWS, float NdotL)
    {
    float rim = pow(1 - saturate(dot(normalWS, viewDirWS)), _RimPower);
    float2 rampUV = float2(NdotL * 0.5 + 0.5, 0.5);
    half3 rampColor = SAMPLE_TEXTURE2D(_RampTex, sampler_RampTex, rampUV).rgb;
    return lerp(rampColor * _ShadowTint.rgb, rampColor, saturate(NdotL + rim));
    }
    ENDHLSL
    Pass
    {
    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 normalWS : TEXCOORD2;
    float3 viewDirWS : TEXCOORD3;
    float4 shadowCoord : TEXCOORD4;
    };
    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.normalWS = normInput.normalWS;
    OUT.viewDirWS = viewDirWS;
    OUT.shadowCoord = GetShadowCoord(posInput);
    OUT.uv = IN.uv;
    return OUT;
    }
    half4 frag(Varyings IN) : SV_Target
    {
    // 计算陡峭视差UV
    float2 parallaxUV = SteepParallaxMapping(normalize(IN.viewDirTS), IN.uv);
    // 采样纹理
    half4 albedo = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, parallaxUV);
    half3 normalTS = UnpackNormal(SAMPLE_TEXTURE2D(_NormalMap, sampler_NormalMap, parallaxUV));
    // 转换法线到世界空间
    float3x3 TBN = float3x3(
    normalize(cross(IN.normalWS, IN.viewDirWS)),
    normalize(IN.normalWS),
    normalize(IN.viewDirWS)
    );
    float3 normalWS = mul(TBN, normalTS);
    // 光照计算
    Light mainLight = GetMainLight(IN.shadowCoord);
    float NdotL = saturate(dot(normalWS, mainLight.direction));
    half3 lighting = StylizedShading(normalWS, normalize(IN.viewDirWS), NdotL);
    return half4(albedo.rgb * lighting * mainLight.color, 1);
    }
    ENDHLSL
    }
    }
    }
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值