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: Unity's standard terrain shader (Stretched normals but they work perfectly) 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"
}
CGPROGRAM
// 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;
INTERNAL_DATA
};
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));
}
ENDCG
}
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.