找回密码
 立即注册
首页 业界区 业界 【URP】Unity[视差贴图]模拟[风格化地形]实践 ...

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

涅牵 17 小时前
【从UnityURP开始探索游戏渲染】专栏-直达
陡峭视差贴图(Steep Parallax Mapping)实现原理

陡峭视差贴图通过‌分层深度比较‌和‌动态UV偏移‌技术增强岩石表面立体感.
视角自适应分层采样


  • 根据视线与表面法线的夹角动态分配采样层数(平视视角增加至12层,俯视视角减少至5层),解决标准视差贴图在平视角度下的失真问题
深度图梯度修正


  • 引入_LayerBias参数(推荐值0.2-0.4)调整UV偏移量计算公式,避免陡峭区域出现采样断裂:
$\Delta UV=\frac{ParallaxScale \cdot ViewDir_{xy}}{(ViewDir_z+LayerBias) \cdot LayerCount}$
风格化深度增强


  • 在最终插值阶段使用pow(weight,2)强化轮廓对比度,配合ramp贴图实现卡通化光影过渡效果
URP HLSL完整实现代码

关键特性说明


  • 动态层数优化‌:通过lerp(_MaxLayers, _MinLayers, saturate(dot(float3(0,0,1), viewDirTS)))实现平视视角自动增加采样精度
  • 抗失真处理‌:_LayerBias参数修正陡峭表面的UV偏移计算,避免采样断裂
  • 风格化增强‌:ramp贴图控制光影过渡,边缘光强化轮廓立体感
  • StylizedRockParallax.shader
    1. Shader "Universal Render Pipeline/StylizedRockParallax"
    2. {
    3.     Properties
    4.     {
    5.         [Header(Base Textures)]
    6.         _MainTex("Albedo (RGB)", 2D) = "white" {}
    7.         _NormalMap("Normal Map", 2D) = "bump" {}
    8.         _HeightMap("Height Map", 2D) = "white" {}
    9.         _RampTex("Stylized Ramp", 2D) = "white" {}
    10.         [Header(Parallax Settings)]
    11.         _ParallaxScale("Depth Scale", Range(0, 0.15)) = 0.08
    12.         _LayerBias("Layer Bias", Range(0.1, 0.5)) = 0.3
    13.         _MinLayers("Min Layers", Int) = 5
    14.         _MaxLayers("Max Layers", Int) = 12
    15.         [Header(Stylized Lighting)]
    16.         _RimPower("Rim Power", Range(1, 10)) = 3
    17.         _ShadowTint("Shadow Tint", Color) = (0.3,0.3,0.4,1)
    18.     }
    19.     SubShader
    20.     {
    21.         Tags { "RenderType"="Opaque" "RenderPipeline"="UniversalPipeline" }
    22.         HLSLINCLUDE
    23.         #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
    24.         #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
    25.         TEXTURE2D(_MainTex);    SAMPLER(sampler_MainTex);
    26.         TEXTURE2D(_NormalMap);  SAMPLER(sampler_NormalMap);
    27.         TEXTURE2D(_HeightMap);  SAMPLER(sampler_HeightMap);
    28.         TEXTURE2D(_RampTex);    SAMPLER(sampler_RampTex);
    29.         float _ParallaxScale;
    30.         float _LayerBias;
    31.         int _MinLayers, _MaxLayers;
    32.         float _RimPower;
    33.         float4 _ShadowTint;
    34.         // 陡峭视差映射核心算法
    35.         float2 SteepParallaxMapping(float3 viewDirTS, float2 uv)
    36.         {
    37.             // 动态层数计算(平视视角增加层数)
    38.             int numLayers = (int)lerp(_MaxLayers, _MinLayers, saturate(dot(float3(0,0,1), viewDirTS)));
    39.             float layerHeight = 1.0 / numLayers;
    40.             float2 deltaUV = _ParallaxScale * viewDirTS.xy / (viewDirTS.z + _LayerBias) / numLayers;
    41.             // 光线步进初始化
    42.             float currentLayerHeight = 0;
    43.             float2 currentUV = uv;
    44.             float currentDepth = 1 - SAMPLE_TEXTURE2D(_HeightMap, sampler_HeightMap, currentUV).r;
    45.             // 分层深度检测
    46.             [loop]
    47.             for (int i = 0; i < _MaxLayers; ++i) {
    48.                 if (currentLayerHeight >= currentDepth) break;
    49.                 currentUV -= deltaUV;
    50.                 currentDepth = 1 - SAMPLE_TEXTURE2D(_HeightMap, sampler_HeightMap, currentUV).r;
    51.                 currentLayerHeight += layerHeight;
    52.             }
    53.             // 风格化插值修正
    54.             float2 prevUV = currentUV + deltaUV;
    55.             float prevDepth = currentDepth - layerHeight;
    56.             float weight = pow((currentLayerHeight - currentDepth) / (prevDepth - currentDepth + 0.001), 2);
    57.             return lerp(currentUV, prevUV, saturate(weight * 1.5));
    58.         }
    59.         // 风格化光照计算
    60.         half3 StylizedShading(float3 normalWS, float3 viewDirWS, float NdotL)
    61.         {
    62.             float rim = pow(1 - saturate(dot(normalWS, viewDirWS)), _RimPower);
    63.             float2 rampUV = float2(NdotL * 0.5 + 0.5, 0.5);
    64.             half3 rampColor = SAMPLE_TEXTURE2D(_RampTex, sampler_RampTex, rampUV).rgb;
    65.             return lerp(rampColor * _ShadowTint.rgb, rampColor, saturate(NdotL + rim));
    66.         }
    67.         ENDHLSL
    68.         Pass
    69.         {
    70.             HLSLPROGRAM
    71.             #pragma vertex vert
    72.             #pragma fragment frag
    73.             struct Attributes
    74.             {
    75.                 float4 positionOS : POSITION;
    76.                 float2 uv : TEXCOORD0;
    77.                 float3 normalOS : NORMAL;
    78.                 float4 tangentOS : TANGENT;
    79.             };
    80.             struct Varyings
    81.             {
    82.                 float4 positionCS : SV_POSITION;
    83.                 float2 uv : TEXCOORD0;
    84.                 float3 viewDirTS : TEXCOORD1;
    85.                 float3 normalWS : TEXCOORD2;
    86.                 float3 viewDirWS : TEXCOORD3;
    87.                 float4 shadowCoord : TEXCOORD4;
    88.             };
    89.             Varyings vert(Attributes IN)
    90.             {
    91.                 Varyings OUT;
    92.                 VertexPositionInputs posInput = GetVertexPositionInputs(IN.positionOS.xyz);
    93.                 OUT.positionCS = posInput.positionCS;
    94.                 VertexNormalInputs normInput = GetVertexNormalInputs(IN.normalOS, IN.tangentOS);
    95.                 float3 viewDirWS = GetWorldSpaceViewDir(posInput.positionWS);
    96.                 OUT.viewDirTS = TransformWorldToTangent(viewDirWS,
    97.                     normInput.tangentWS, normInput.bitangentWS, normInput.normalWS);
    98.                 OUT.normalWS = normInput.normalWS;
    99.                 OUT.viewDirWS = viewDirWS;
    100.                 OUT.shadowCoord = GetShadowCoord(posInput);
    101.                 OUT.uv = IN.uv;
    102.                 return OUT;
    103.             }
    104.             half4 frag(Varyings IN) : SV_Target
    105.             {
    106.                 // 计算陡峭视差UV
    107.                 float2 parallaxUV = SteepParallaxMapping(normalize(IN.viewDirTS), IN.uv);
    108.                 // 采样纹理
    109.                 half4 albedo = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, parallaxUV);
    110.                 half3 normalTS = UnpackNormal(SAMPLE_TEXTURE2D(_NormalMap, sampler_NormalMap, parallaxUV));
    111.                 // 转换法线到世界空间
    112.                 float3x3 TBN = float3x3(
    113.                     normalize(cross(IN.normalWS, IN.viewDirWS)),
    114.                     normalize(IN.normalWS),
    115.                     normalize(IN.viewDirWS)
    116.                 );
    117.                 float3 normalWS = mul(TBN, normalTS);
    118.                 // 光照计算
    119.                 Light mainLight = GetMainLight(IN.shadowCoord);
    120.                 float NdotL = saturate(dot(normalWS, mainLight.direction));
    121.                 half3 lighting = StylizedShading(normalWS, normalize(IN.viewDirWS), NdotL);
    122.                 return half4(albedo.rgb * lighting * mainLight.color, 1);
    123.             }
    124.             ENDHLSL
    125.         }
    126.     }
    127. }
    复制代码
材质配置

参数组合风格化效果_ParallaxScale=0.05 + _RimPower=5轻度凹凸+柔和边缘光_ParallaxScale=0.1 + _LayerBias=0.4强烈凹凸+抗失真处理_ShadowTint=(0.4,0.2,0.6)紫色调阴影增强风格化表现<blockquote>
【从UnityURP开始探索游戏渲染】专栏-直达
(欢迎点赞留言探讨,更多人加入进来能更加完善这个探索的过程,
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册