Splatmap shader not working properly

Hi everyone! I have this Splatmap shader that i’m using for my game, HOWEVER, the normal mapping, base map and main color don’t show up at all. I’m sure i’m missing something but i’m not sure what, i’ve been reading the ShaderLab documentation to no avail, does anyone have a solution? i would really appreciate some help with this.

(not sure if it’s worth mentioning but i want to use this for mobile devices, seems to work fine but is it ok?)

Here’s the code for the shader:

Shader "Custom/Terrain/Specular" {
    Properties {
        _SpecColor ("Specular Color", Color) = (0.5, 0.5, 0.5, 1)
        _Shininess ("Shininess", Range (0.03, 1)) = 0.078125

        // set by terrain engine
        _Control ("Control (RGBA)", 2D) = "red" {}
        _Splat3 ("Layer 3 (A)", 2D) = "white" {}
        _Splat2 ("Layer 2 (B)", 2D) = "white" {}
        _Splat1 ("Layer 1 (G)", 2D) = "white" {}
        _Splat0 ("Layer 0 (R)", 2D) = "white" {}
        _Normal3 ("Normal 3 (A)", 2D) = "bump" {}
        _Normal2 ("Normal 2 (B)", 2D) = "bump" {}
        _Normal1 ("Normal 1 (G)", 2D) = "bump" {}
        _Normal0 ("Normal 0 (R)", 2D) = "bump" {}
        // used in fallback on old cards & base map
        _MainTex ("BaseMap (RGB)", 2D) = "white" {}
        _Color ("Main Color", Color) = (1,1,1,1)
    }

    SubShader {
        Tags {
            "Queue" = "Geometry-100"
            "RenderType" = "Opaque"
        }

        CGPROGRAM
        #pragma surface surf BlinnPhong vertex:SplatmapVert finalcolor:SplatmapFinalColor finalprepass:SplatmapFinalPrepass finalgbuffer:SplatmapFinalGBuffer
        #pragma multi_compile_fog
        #pragma multi_compile __ _TERRAIN_NORMAL_MAP
        #pragma target 3.0
        // needs more than 8 texcoords
        #pragma exclude_renderers gles

        #include "TerrainSplatmapCommon.cginc"

        half _Shininess;

        void surf(Input IN, inout SurfaceOutput o)
        {
            half4 splat_control;
            half weight;
            fixed4 mixedDiffuse;
            SplatmapMix(IN, splat_control, weight, mixedDiffuse, o.Normal);
            o.Albedo = mixedDiffuse.rgb;
            o.Alpha = weight;
            o.Gloss = mixedDiffuse.a;
            o.Specular = _Shininess;
        }
        ENDCG
    }

    Fallback "Nature/Terrain/Diffuse"
}

Here’s the definition of it’s include:

#ifndef TERRAIN_SPLATMAP_COMMON_CGINC_INCLUDED
#define TERRAIN_SPLATMAP_COMMON_CGINC_INCLUDED

struct Input
{
   float2 uv_Splat0 : TEXCOORD0;
   float2 uv_Splat1 : TEXCOORD1;
   float2 uv_Splat2 : TEXCOORD2;
   float2 uv_Splat3 : TEXCOORD3;
   float2 tc_Control : TEXCOORD4;   // Not prefixing '_Contorl' with 'uv' allows a tighter packing of interpolators, which is necessary to support directional lightmap.
   UNITY_FOG_COORDS(5)
};

sampler2D _Control;
float4 _Control_ST;
sampler2D _Splat0,_Splat1,_Splat2,_Splat3;

#ifdef _TERRAIN_NORMAL_MAP
   sampler2D _Normal0, _Normal1, _Normal2, _Normal3;
#endif

void SplatmapVert(inout appdata_full v, out Input data)
{
   UNITY_INITIALIZE_OUTPUT(Input, data);
   data.tc_Control = TRANSFORM_TEX(v.texcoord, _Control);   // Need to manually transform uv here, as we choose not to use 'uv' prefix for this texcoord.
   float4 pos = mul (UNITY_MATRIX_MVP, v.vertex);
   UNITY_TRANSFER_FOG(data, pos);

#ifdef _TERRAIN_NORMAL_MAP
   v.tangent.xyz = cross(v.normal, float3(0,0,1));
   v.tangent.w = -1;
#endif
}

#ifdef TERRAIN_STANDARD_SHADER
void SplatmapMix(Input IN, half4 defaultAlpha, out half4 splat_control, out half weight, out fixed4 mixedDiffuse, inout fixed3 mixedNormal)
#else
void SplatmapMix(Input IN, out half4 splat_control, out half weight, out fixed4 mixedDiffuse, inout fixed3 mixedNormal)
#endif
{
   splat_control = tex2D(_Control, IN.tc_Control);
   weight = dot(splat_control, half4(1,1,1,1));

   #if !defined(SHADER_API_MOBILE) && defined(TERRAIN_SPLAT_ADDPASS)
     clip(weight - 0.0039 /*1/255*/);
   #endif

   // Normalize weights before lighting and restore weights in final modifier functions so that the overal
   // lighting result can be correctly weighted.
   splat_control /= (weight + 1e-3f);

   mixedDiffuse = 0.0f;
   #ifdef TERRAIN_STANDARD_SHADER
     mixedDiffuse += splat_control.r * tex2D(_Splat0, IN.uv_Splat0) * half4(1.0, 1.0, 1.0, defaultAlpha.r);
     mixedDiffuse += splat_control.g * tex2D(_Splat1, IN.uv_Splat1) * half4(1.0, 1.0, 1.0, defaultAlpha.g);
     mixedDiffuse += splat_control.b * tex2D(_Splat2, IN.uv_Splat2) * half4(1.0, 1.0, 1.0, defaultAlpha.b);
     mixedDiffuse += splat_control.a * tex2D(_Splat3, IN.uv_Splat3) * half4(1.0, 1.0, 1.0, defaultAlpha.a);
   #else
     mixedDiffuse += splat_control.r * tex2D(_Splat0, IN.uv_Splat0);
     mixedDiffuse += splat_control.g * tex2D(_Splat1, IN.uv_Splat1);
     mixedDiffuse += splat_control.b * tex2D(_Splat2, IN.uv_Splat2);
     mixedDiffuse += splat_control.a * tex2D(_Splat3, IN.uv_Splat3);
   #endif

   #ifdef _TERRAIN_NORMAL_MAP
     fixed4 nrm = 0.0f;
     nrm += splat_control.r * tex2D(_Normal0, IN.uv_Splat0);
     nrm += splat_control.g * tex2D(_Normal1, IN.uv_Splat1);
     nrm += splat_control.b * tex2D(_Normal2, IN.uv_Splat2);
     nrm += splat_control.a * tex2D(_Normal3, IN.uv_Splat3);
     mixedNormal = UnpackNormal(nrm);
   #endif
}

#ifndef TERRAIN_SURFACE_OUTPUT
   #define TERRAIN_SURFACE_OUTPUT SurfaceOutput
#endif

void SplatmapFinalColor(Input IN, TERRAIN_SURFACE_OUTPUT o, inout fixed4 color)
{
   color *= o.Alpha;
   #ifdef TERRAIN_SPLAT_ADDPASS
     UNITY_APPLY_FOG_COLOR(IN.fogCoord, color, fixed4(0,0,0,0));
   #else
     UNITY_APPLY_FOG(IN.fogCoord, color);
   #endif
}

void SplatmapFinalPrepass(Input IN, TERRAIN_SURFACE_OUTPUT o, inout fixed4 normalSpec)
{
   normalSpec *= o.Alpha;
}

void SplatmapFinalGBuffer(Input IN, TERRAIN_SURFACE_OUTPUT o, inout half4 diffuse, inout half4 specSmoothness, inout half4 normal, inout half4 emission)
{
   diffuse.rgb *= o.Alpha;
   specSmoothness *= o.Alpha;
   normal.rgb *= o.Alpha;
   emission *= o.Alpha;
}

#endif // TERRAIN_SPLATMAP_COMMON_CGINC_INCLUDED

I don´t like bumping my own threads but i really need some help here, i’d really appreciate it…

Could also post the definition of SplatmapMix.

If you want to more performance on mobile, it’s advised to not used surface shader instead user vertex/fragment shader.

1 Like

I will have to check the definition of SplatmapMix, i didn’t make this shader (since i never got into ShaderLab before so i’m kinda struggling there) i got this from the original terrain shaders unity uses, some further explanation on this would really help me out since i’m kinda lost with this.

And i was told before of not using surface shader too, however, idk how to change that either :frowning: what kind of shader would be best then? and how can i make an Splatmap shader out of that?

EDIT: The definition include was added to the OP, hope that can help.

There is a lot going on in this shader.

I’m not sure what your objective is but if you are not comfortable with editing shaders, I suggest you should start with simpler example.

Maybe you can find your way through the examples in the documention:

About surface shader: Unity - Manual: Surface Shader lighting examples
About vertex-and-fragment shader, check: Unity - Manual: Custom shader fundamentals

1 Like

Thanks, and yeah i’m already working on it, no success so far but i’ll try to get it done

All i want to do is make at least the normal mapping work since it doesn’t show up, idk if you or anyone else can help me out a little…

Is hard to help you debugging complex shader without almost any clue what is going wrong.

Is _TERRAIN_NORMAL_MAP defined?