Search Unity

How to make terrain triplanar shader always paint horizontally?

Discussion in 'Shaders' started by SomeHumbleOnion, Aug 25, 2021.

  1. SomeHumbleOnion

    SomeHumbleOnion

    Joined:
    Jan 18, 2021
    Posts:
    28
    Hello forum. I've been experimenting with the terrain triplanar shaders online lately and I've been able to implement it into my custom terrain shader. This shader automatically paints a cliff texture for the terrain depending on the dot product of the normal.

    However, both the bottom and top parts of a cliff are textured incorrectly and I haven't been able to figure out the cause of this (see screenshot). Instead of being textured horizontally, the y and z blends are being texture slightly vertically.

    s1.PNG

    How can I achieve a horizontal texturing straight across the terrain walls? I've pasted my terrain surface shader below to show how the triplanar mapping is done. Thanks!

    Code (CSharp):
    1.             void surf(Input IN, inout SurfaceOutput o)
    2.             {
    3.                 // Splat map controls.
    4.                 half4 splat_control;
    5.                 half weight;
    6.                 fixed4 mixedDiffuse;
    7.                 // Splat map mixer called in cginc.
    8.                 SplatmapMix(IN, splat_control, weight, mixedDiffuse, o.Normal);
    9.  
    10.                 // texture scape for cliff texture
    11.                 float3 scaledWorldPos = IN.localCoord / _MapScale;
    12.  
    13.                 // Get UVs for each axis based on local position of the fragment.
    14.                 half2 yUV = IN.localCoord.xz / _MapScale;
    15.                 half2 xUV = IN.localCoord.zy / _MapScale;
    16.                 half2 zUV = IN.localCoord.xy / _MapScale;
    17.  
    18.                 // Texture samples from diffuse map with each of the 3 UV sets.
    19.                 half3 yDiff = tex2D(_VerticalTex, yUV);
    20.                 half3 xDiff = tex2D(_VerticalTex, xUV);
    21.                 half3 zDiff = tex2D(_VerticalTex, zUV);
    22.  
    23.                 // Get the absolute value of the world normal.
    24.                 // Put the blend weights to the power of BlendSharpness to sharpen transition
    25.                 half3 blendWeights = pow(abs(IN.localNormal), _TriplanarBlendSharpness);
    26.  
    27.                 // Divide blend mask by the sum of it's components.
    28.                 blendWeights = blendWeights / (blendWeights.x + blendWeights.y + blendWeights.z);
    29.  
    30.                 // Sharpen blend weight completely by rounding (currently causing black spots between blends).
    31.                 blendWeights = round(blendWeights);
    32.  
    33.                 // Blend together all three samples based on the blend mask.
    34.                 half3 color = xDiff * blendWeights.x + yDiff * blendWeights.y + zDiff * blendWeights.z;
    35.  
    36.                 // Automatically texture terrain with cliff texture if dot product is greater than 0.8.
    37.                 if (dot(IN.localNormal, fixed3(0, 1, 0)) >= 0.8)
    38.                 {
    39.                     o.Albedo = mixedDiffuse.rgb;
    40.                 }
    41.                 else {
    42.                     o.Albedo = color.rgb;
    43.                 }
    44.                 o.Alpha = 1;
    45.             }
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    If you are only ever going to be using this for doing textures on cliffs or the like, then you don't need or even want to do a full triplanar shader. Just skip anything to with the Y axis, since you don't want it at all.
    Code (csharp):
    1. // max prevents a divide by 0 on floors where the x and z values would both be 0.0
    2. blendWeights = blendWeights / max(0.0001, blendWeights.x + blendWeights.z);
    3. half3 color = xDiff * blendWeights.x + zDiff * blendWeights.z;
     
    SomeHumbleOnion likes this.
  3. SomeHumbleOnion

    SomeHumbleOnion

    Joined:
    Jan 18, 2021
    Posts:
    28
    Thanks so much for the reply bgolus! That makes total sense, can't believe I didn't try that before. I made a small tweak to your code block which I've pasted below for anyone curious:

    Code (CSharp):
    1. blendWeights = blendWeights / (blendWeights.x + blendWeights.z);
    2. blendWeights = round(blendWeights);
    This helped fix some issues where the texture would overlap it's repeated self (on the Y axis I believe). Then I round it to make the repeated texturing blending pixel perfect and sharp. Thanks again for the help!