Search Unity

Snow shader with perlin noise

Discussion in 'Shaders' started by spiritworld, Aug 19, 2019.

  1. spiritworld

    spiritworld

    Joined:
    Nov 26, 2014
    Posts:
    2
    I added snow effect feature into a toon shader I'm using (MKToon Free). I utilized basically this: https://forum.unity.com/threads/snow-up-vector-shader.177606/

    MKToon's v2f struct:
    Code (CSharp):
    1. struct VertexOutputForward {
    2.     float4 pos : SV_POSITION;
    3.     float2 uv_Main : TEXCOORD0;
    4.  
    5.     float4 posWorld : TEXCOORD1;
    6.     half3 normalWorld : TEXCOORD2;
    7.     half3 tangentWorld : TEXCOORD3;
    8.     half3 binormalWorld : TEXCOORD4;
    9. ...
    10. };
    11.  
    Inside MKToon I modified SurfaceColor function.
    Code (CSharp):
    1.    
    2. //get surface color based on blendmode and color source
    3. void SurfaceColor(out fixed3 albedo, out fixed alpha, float2 uv, half3 normal) {
    4.    fixed4 c = tex2D(_MainTex, uv) * _Color;  
    5.    float difference = dot(normal, _SnowDirection.xyz);
    6.    if(difference >= _SnowLevel) {
    7.       albedo = _SnowColor.rgb;
    8.    }
    9.    else {
    10.       albedo = c.rgb;
    11.    }
    12.  
    13.    alpha = c.a;
    14. }
    Here's outcome when it's only partly snowy:
    upload_2019-8-19_14-20-19.png

    It looks fine on small surfaces but e.g. on large grounds it's more on/off effect (no snow/full snow). What I really want is in the first example here using perlin noise: https://www.alanzucconi.com/2018/08/18/shader-showcase-saturday-6/

    "The snowy transition seen in Overland can be replicated using a grayscale, smooth noise texture. The UV coordinates of the ground mesh are used to sample a colour from that texture."

    I tinkered with this shader months ago and didn't get the sampling working, mostly because I'm not very adept with shader language. I added _NoiseTexture for perlin noise texture and tried to take samples

    Code (CSharp):
    1. float surfaceNoiseSample = tex2D(_NoiseTex, uv).r;
    2. if(difference >= _SnowLevel) {
    3.     albedo = surfaceNoiseSample > 0.75 ? _SnowColor.rgb : c.rgb;
    4. }
    5. else ...
    but this has no effect, instead snow doesn't appear at all. Should I use tex2D(_NoiseTex, UvOfNoiseTex) like in here: https://github.com/IronWarrior/ToonWaterShader/blob/master/Assets/Shaders/ToonWater.shader sample
     
  2. brownboot67

    brownboot67

    Joined:
    Jan 5, 2013
    Posts:
    365
    You don't need these branches. Generally, don't do branches in shaders until you really know what you're doing. Just do something like:

    Code (csharp):
    1.  
    2. float snowMask = saturate(difference * surfaceNoiseSample * 10.0);
    3. albedo = lerp(c.rgb, _SnowColor.rgb, snowMask);
    4.  
     
  3. spiritworld

    spiritworld

    Joined:
    Nov 26, 2014
    Posts:
    2
    Ok thanks. I'll try to refactor my code better because I just solved it by... putting noiseSample comparison in same IF :D

    Code (CSharp):
    1. if(difference >= _SnowLevel && surfaceNoiseSample >= _SnowLevel)
    2. {
    3.      albedo = _SnowColor.rgb;
    4. }
    5. else ...
    6.  
    upload_2019-8-19_21-15-40.png