Mixing two textures using lerp function doesn't work as it should

I want to get smooth transitions between texture layers, e.g. sand texture smoothly passes into grass texture. I tried to use lerp function for this, but it doesn’t work as it should. Here is simple shader that mixes 2 textures

Shader "Custom/LerpDemo"
{
    Properties
    {
        _Sand("_Sand", 2D) = "black" {}
        _TerrainFloor("_TerrainFloor", 2D) = "black" {}
        _Blend("_Blend", Range(0,1) ) = 0.5
    }
   
    SubShader
    {
        Tags
        {
            "Queue"="Geometry"
            "IgnoreProjector"="False"
            "RenderType"="Opaque"
        }
 
        Cull Back
        ZWrite On
        ZTest LEqual
        ColorMask RGBA
       
        CGPROGRAM
        #pragma surface surf BlinnPhongEditor  
        #pragma target 2.0
       
        sampler2D _TerrainFloor;
        sampler2D _Sand;
        float _Blend;
 
        struct EditorSurfaceOutput {
            half3 Albedo;
            half3 Normal;
            half3 Emission;
            half3 Gloss;
            half Specular;
            half Alpha;
            half4 Custom;
        };
       
        inline half4 LightingBlinnPhongEditor_PrePass (EditorSurfaceOutput s, half4 light)
        {
            half3 spec = light.a * s.Gloss;
            half4 c;
            c.rgb = (s.Albedo * light.rgb + light.rgb * spec);
            c.a = s.Alpha;
            return c;
        }
 
            inline half4 LightingBlinnPhongEditor (EditorSurfaceOutput s, half3 lightDir, half3 viewDir, half atten)
            {
                half3 h = normalize (lightDir + viewDir);
               
                half diff = max (0, dot ( lightDir, s.Normal ));
               
                float nh = max (0, dot (s.Normal, h));
                float spec = pow (nh, s.Specular*128.0);
               
                half4 res;
                res.rgb = _LightColor0.rgb * diff;
                res.w = spec * Luminance (_LightColor0.rgb);
                res *= atten * 2.0;
 
                return LightingBlinnPhongEditor_PrePass( s, res );
            }
           
            struct Input {
                float2 uv_Sand;
                float2 uv_TerrainFloor;
            };
 
            void surf (Input IN, inout EditorSurfaceOutput o)
            {
                o.Normal = float3(0.0,0.0,1.0);
                o.Alpha = 1.0;
                o.Albedo = 0.0;
                o.Emission = 0.0;
                o.Gloss = 0.0;
                o.Specular = 0.0;
                o.Custom = 0.0;
               
                float4 Sampled2D1=tex2D(_Sand,IN.uv_Sand.xy);
                float4 Sampled2D0=tex2D(_TerrainFloor,IN.uv_TerrainFloor.xy);
                float4 Lerp0=lerp(Sampled2D1,_Blend.xxxx,Sampled2D0);
               
                o.Albedo = Lerp0;
 
                o.Normal = normalize(o.Normal);
            }
        ENDCG
    }
    Fallback "Diffuse"
}

So in theory if _Blend equals 0 I should get the first texture and if it is 1 then I should get the second one.
But it doesn’t work that way - I am getting much darker texture than original and in case if one texture is lighter then one can see the darker texture underneath.

  1. Why it doesn’t work?

2.If someone knows how to get smooth transitions between texture layers, e.g. sand texture is used on terrain up to 1 meter height , grass texture is used on terrain from 1 to 10 meters height etc., please let me know. I can’t use Unity Terrain Toolkit because I generate terrain on the fly, I don’t use Unity Terrain objects.

I suspect that the lerp parameters are in the wrong order - at least according to the CG NVidia tutorial, the order should be:

  float4 Lerp0=lerp(Sampled2D1,Sampled2D0,_Blend.xxxx);

Another thing: maybe you should use _Blend.x instead of _Blend.xxxx.

About the terrain layers, the Unity Terrain Toolkit allows you to texture in function of the height, like you want.

Lerp is (color1, color2, 0to1 blend value). You’ve got the order flipped. Use: o.Albedo=lerp(Sampled2D0, Sampled2D1, _Blend);

To blend over a range, you just need to scale it into a 0-1. Suppose you have height in H and you want it to blend as it goes from 1-10. Subtract the start and divide by the range: lerpVal = (H-1)/9; (check: (10-1)/9 is 1.) Then you have to stop if from going out of 0-1, so use: lerpVal=clamp( (H-1)/9, 0, 1);