I've been developing a custom terrain shader in Unity. And I've been having problems with my normal maps. Even after hours of fiddling I still haven't fixed it yet. There are 2 noticeable issues: 1. Normal maps don't appear on vertices whose normal points towards the X axis 2. Normals just don't appear "correct", as in they're faint, and don't look as good as standard terrain normals.

The following pictures should help illustrate my point: Standard Shader Unity's standard terrain shader (Stretched normals but they work perfectly) My Shader My shader (Washed out normals, and normals on the x axis are virtually non-existent)

I have stripped my code down to the bare essentials and I still get this result.

My code:

     // Set by terrain engine
        [HideInInspector] _Control ("Control (RGB)", 2D) = "red" {}
        //[HideInInspector] _Splat3 ("Layer 3 (A)", 2D) = "white" {}
        [HideInInspector] _Splat2 ("Layer 2 (B)", 2D) = "white" {}
        [HideInInspector] _Splat1 ("Layer 1 (G)", 2D) = "white" {}
        [HideInInspector] _Splat0 ("Layer 0 (R)", 2D) = "white" {}
        //[HideInInspector] _Normal3 ("Normal 3 (A)", 2D) = "bump" {}
        [HideInInspector] _Normal2 ("Normal 2 (B)", 2D) = "bump" {}
        [HideInInspector] _Normal1 ("Normal 1 (G)", 2D) = "bump" {}
        [HideInInspector] _Normal0 ("Normal 0 (R)", 2D) = "bump" {}
        [HideInInspector] [Gamma] _Metallic0 ("Metallic 0", Range(0.0, 1.0)) = 0.0  
        [HideInInspector] [Gamma] _Metallic1 ("Metallic 1", Range(0.0, 1.0)) = 0.0  
        [HideInInspector] [Gamma] _Metallic2 ("Metallic 2", Range(0.0, 1.0)) = 0.0  
        //[HideInInspector] [Gamma] _Metallic3 ("Metallic 3", Range(0.0, 1.0)) = 0.0
        [HideInInspector] _Smoothness0 ("Smoothness 0", Range(0.0, 1.0)) = 1.0  
        [HideInInspector] _Smoothness1 ("Smoothness 1", Range(0.0, 1.0)) = 1.0  
        [HideInInspector] _Smoothness2 ("Smoothness 2", Range(0.0, 1.0)) = 1.0  
        //[HideInInspector] _Smoothness3 ("Smoothness 3", Range(0.0, 1.0)) = 1.0

        // used in fallback on old cards & base map
        [HideInInspector] _MainTex ("BaseMap (RGB)", 2D) = "white" {}
        [HideInInspector] _Color ("Main Color", Color) = (1,1,1,1)

    SubShader {
        Tags {
            "SplatCount" = "3"
            "Queue" = "Geometry-100"
            "RenderType" = "Opaque"

        // Physically based Standard lighting model, and enable shadows on all light types
        #pragma surface surf Standard fullforwardshadows
        // Use shader model 3.0 target, to get nicer looking lighting
        #pragma target 3.0
        #pragma multi_compile_fog
        #pragma exclude_renderers gles
        #include "UnityPBSLighting.cginc"
        #include "Tom's Terrain-Helper.cginc"

        // Access the Shaderlab properties
        half _depthThresh;

        uniform sampler2D _Control;

        sampler2D _Splat0,_Splat1,_Splat2;
        //float4 _Splat0_ST;
        //float4 _Splat1_ST;
        //float4 _Splat2_ST;
        sampler2D _Normal0,_Normal1,_Normal2;

        half _Metallic0;
        half _Metallic1;
        half _Metallic2;
        //half _Metallic3;

        half _Smoothness0;
        half _Smoothness1;
        half _Smoothness2;
        //half _Smoothness3;

        half _Height0;
        half _Height1;
        half _Height2;

        float _TextureScale;
        half _NormalStrengh;
        float _TriplanarNormals;

        struct Input {
            float3 worldPos;
            float3 worldNormal;
            float2 uv_MainTex : TEXCOORD0;
            float2 uv_Control : TEXCOORD1;
            float2 uv_Splat0 : TEXCOORD2;
            float2 uv_Splat1 : TEXCOORD3;
            float2 uv_Splat2 : TEXCOORD4;
            //float2 uv_Splat3 : TEXCOORD5;

            //float2 uv_Normal0 : TEXCOORD6;
            //float2 uv_Normal1 : TEXCOORD7;
            //float2 uv_Normal2 : TEXCOORD8;
            //float2 uv_Normal3 : TEXCOORD9;

        void SplatmapVert(inout appdata_full v, out Input data)
            UNITY_INITIALIZE_OUTPUT(Input, data);
            float4 pos = mul (UNITY_MATRIX_MVP, v.vertex);
            //UNITY_TRANSFER_FOG(data, pos);

            v.tangent.xyz = cross(v.normal, float3(0,0,1));
            v.tangent.w = -1;

        half _Glossiness;
        half _Metallic;
        fixed4 _Color;

        void surf (Input IN, inout SurfaceOutputStandard o) {
            fixed4 splatControl = tex2D(_Control, IN.uv_Control);

            fixed4 nrm = 0.0f;
            //nrm += splatControl.r * normal0;
            //nrm += splatControl.g * normal1;
            //nrm += splatControl.b * normal2;
            nrm += splatControl.r * tex2D(_Normal0, IN.uv_Splat0);
            nrm += splatControl.g * tex2D(_Normal1, IN.uv_Splat1);
            nrm += splatControl.b * tex2D(_Normal2, IN.uv_Splat2);
            //nrm /= b1 + b2 + b3;

            nrm.g = nrm.r;

            fixed3 finalNormal = UnpackNormal(nrm);

            //Final output
            o.Normal = finalNormal;
            o.Albedo = 1;
            o.Alpha = 1;
            o.Smoothness =dot(tex2D(_Control, IN.uv_Control), half3(_Smoothness0, _Smoothness1, _Smoothness2));
            o.Metallic = dot(tex2D(_Control, IN.uv_Control), half3(_Metallic0, _Metallic1, _Metallic2));
    Dependency "AddPassShader" = "Hidden/TerrainEngine/Splatmap/Standard-AddPass"
    Dependency "BaseMapShader" = "Hidden/TerrainEngine/Splatmap/Standard-Base"

    Fallback "Nature/Terrain/Diffuse"

You might notice that a lot of this code is very similar to unity's code. And even though this code is so similar it still behaves differently.

So my question is; what am I missing?

Thanks in advance.

Some other images to help understand my problem: Standard shader My Shader


1 Answer 1


It turns out that I had forgotten to declare my vertex shader in the pragma.

This line:

 #pragma surface surf Standard fullforwardshadows

Becomes, this line:

#pragma surface surf Standard vertex:SplatmapVert fullforwardshadows

Note the addition of:


You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .