BlaXuan 发表于 2022-5-5 20:44

【Unity Shader】积雪效果

一、简介

在Unity中要完成雪地场景的制作,积雪是不可或缺的。要在项目中满足整个场景的积雪,我这主要实现三种方式来混合使用。

[*]顶点色控制,适用于小型和模型摆放较固定的物体。
[*]根据法线朝向的积雪效果,适用于如大型石块,模型复用率较高的物体。
[*]基于高度贴图的模型积雪笔刷,适用于入地板会存在一些地缝细节的物体上。


本文主要讲的是基于法线朝向的积雪效果实现。
二、实现

我主要使用的是自定义的Lit Shader。
由于积雪是覆盖在模型表面,我们可以根据模型的法线方向和世界向上的法线点乘的值来判断模型世界空间下的上方向,点乘的值越大就是越趋于上方向,由此可以得到一个积雪的范围区域。
half blendPower = 0.001 +_LayerThreshold *0.999;
half blendAlpha = saturate(pow(dot(saturate(layerNormal.xyz),float3(0,1,0)) + _LayerPower, blendPower));

但是单用法线方向得到区域范围受模型结构的影响,很容易出现断裂的积雪层,且积雪区域会受模型顶点的影响。


由此我们需要修正模型的法线。

[*]首先,我们需要去掉模型的硬边结构,即统一模型的法线,硬边结构只通过模型的法线贴图来控制。
[*]使用积雪的法线贴图来影响模型自身的法线。
[*]由于贴图的平铺是基于世界空间的,要保证在不同方向上贴图的采样uv能够平铺于原来的模型表面。
法线贴图的采样方式如下:
   inline float3 TriplanarSampling(Varyings input,TEXTURE2D(topTexMap),SAMPLER(sampler_topTexMap) , float falloff, float2 tiling, float3 normalScale,inout float3 NormTS)
    {
      float3 projNormal = ( pow( abs( input.normalWS ), falloff ) );
        projNormal /= ( projNormal.x + projNormal.y + projNormal.z ) + 0.00001;
        float3 nsign = sign( input.normalWS );
        half4 xNorm; half4 yNorm; half4 zNorm;
        xNorm = SAMPLE_TEXTURE2D( topTexMap,sampler_topTexMap, tiling * input.positionWS.zy * float2(nsign.x, 1.0 ) );
        yNorm = SAMPLE_TEXTURE2D( topTexMap,sampler_topTexMap, tiling * input.positionWS.xz * float2(nsign.y, 1.0 ) );
        zNorm = SAMPLE_TEXTURE2D( topTexMap,sampler_topTexMap, tiling * input.positionWS.xy * float2( -nsign.z, 1.0 ) );
        xNorm.xyz= half3( UnpackNormalScale( xNorm, normalScale.y ).xy * float2(nsign.x, 1.0 ) + input.normalWS.zy, input.normalWS.x ).zyx;
        yNorm.xyz= half3( UnpackNormalScale( yNorm, normalScale.x ).xy * float2(nsign.y, 1.0 ) + input.normalWS.xz, input.normalWS.y ).xzy;
        zNorm.xyz= half3( UnpackNormalScale( zNorm, normalScale.y ).xy * float2( -nsign.z, 1.0 ) + input.normalWS.xy, input.normalWS.z ).xyz;
        NormTS = normalize( xNorm.xyz * projNormal.x + yNorm.xyz * projNormal.y + zNorm.xyz * projNormal.z );
      float3x3 tbn = float3x3(input.tangentWS.xyz, input.bitangetWS.xyz, input.normalWS.xyz);
      NormTS= normalize(mul(tbn,NormTS));
               
        float3 tanToWorld0 = float3( input.tangentWS.x, input.bitangetWS.x,input.normalWS.x );
        float3 tanToWorld1 = float3( input.tangentWS.y, input.bitangetWS.y,input.normalWS.y );
        float3 tanToWorld2 = float3( input.tangentWS.z, input.bitangetWS.z,input.normalWS.z );
        float3 NormalWS = float3(dot(tanToWorld0,NormTS), dot(tanToWorld1,NormTS), dot(tanToWorld2,NormTS));
      return NormalWS;
    }同时削弱积雪表面模型法线贴图的影响。
    outSurfaceData.normalTS =lerp(normalTS,0.5*(normalTS+layerNormalTS),blendAlpha); 积雪演示:

zifa2003293 发表于 2022-5-5 20:54

算边缘的时候把阔边儿多算出来一层。然后颜色加深。模拟视觉上的厚度[捂脸]
页: [1]
查看完整版本: 【Unity Shader】积雪效果