- Home /
World Space Normal -> Tangent Space Normal is surface shader
I use a shader that calculates a normal vector trough a noise function, but i want to transform that world normal to a tangent normal so it will work properly with deferred lighting and other image effects; I tried passing a TANGENT_SPACE_ROTATION matrix to the surface function but it gave me an error:
"too many interpolators used"
Deferred Rendering with World Normals
Forward Rendering with World Normals and world light direction
Here is my shader:
Shader "LostInMind/Planet/Ground/Bumped Specular" {
Properties {
_Tess ("Tessellation", Range(1,32)) = 4
_MainTex ("Texture", 2D) = "white" {}
_TerrainHeight("Terrain Height",Float) = 128
_TerrainBump("Terrain Bump",Float) = 2
_TerrainBumpSample("Terrain Bump Sample",Float) = 1
_Frequency("Frequency",Float) = 0.01
}
SubShader {
Tags { "RenderType" = "Opaque" }
CGPROGRAM
#pragma surface surf SimpleLambert vertex:vert
#include "ImprovedPerlinNoise3D.cginc"
#pragma glsl
#pragma target 5.0
struct SurfaceOutputCustom {
fixed3 Albedo;
fixed3 Normal;
fixed3 Emission;
half Specular;
fixed Gloss;
fixed Alpha;
fixed3 LightDir;
};
half4 LightingSimpleLambert (SurfaceOutputCustom s, half3 lightDir, half atten) {
half NdotL = dot (s.Normal, s.LightDir);
half4 c;
c.rgb = s.Albedo * _LightColor0.rgb * (NdotL * atten * 2);
c.a = s.Alpha;
return c;
}
half4 LightingSimpleLambert_PrePass (SurfaceOutputCustom s, half4 light) {
half4 c;
c.rgb = s.Albedo + light.rgb;
c.a = s.Alpha;
return c;
}
float _TerrainHeight;
float3 _LightDir;
float _TerrainBump;
float _TerrainBumpSample;
struct Input {
float2 uv_MainTex;
float3 Pos;
float3 Normal;
};
float Noise(float3 P,int o)
{
return 1 - turbulence(P,o);
}
float3 GetNormal(float3 N,float3 P)
{
float e = _TerrainBumpSample;
int o = 4;
float F = Noise(float3(P.x,P.y,P.z),o) * _TerrainBump;
float Fx = Noise(float3(P.x+e,P.y,P.z),o) * _TerrainBump;
float Fy = Noise(float3(P.x,P.y+e,P.z),o) * _TerrainBump;
float Fz = Noise(float3(P.x,P.y,P.z+e),o) * _TerrainBump;
float3 dF = float3((Fx-F)/e, (Fy-F)/e, (Fz-F)/e);
return normalize(N-dF);
}
void vert (inout appdata_full v,out Input o)
{
UNITY_INITIALIZE_OUTPUT(Input,o);
TANGENT_SPACE_ROTATION;
o.Normal = mul(_Object2World, normalize(v.vertex));
o.Pos = normalize(mul(_Object2World, v.vertex)).xyz*12720;
}
sampler2D _MainTex;
void surf (Input IN, inout SurfaceOutputCustom o) {
o.LightDir = normalize(_WorldSpaceLightPos0).xyz;
o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
float3 n = GetNormal(IN.Normal,IN.Pos);
o.Normal = n;
}
ENDCG
}
Fallback "Diffuse"
}
Answer by spalmer · Apr 20, 2015 at 06:29 AM
I'm in the same pickle. I'm sure you wouldn't be satisfied with doing it the easy way in the vertex shader -- I'm not. You need to write o.Normal in the surface shader in tangent space. So flat (relative to interpolated triangle normal) is (0,0,1) just like bump map. So result of GetNormal must be transformed to tangent space. I noticed you're not even using the local matrix called 'rotation' that TANGENT_SPACE_ROTATION declared for you. You must provide the tangent space basis to the surface shader somehow by additional interpolators. Yeah you only have so many to work with; You're running out somehow. Anyway figure out your present interpolator problem then try passing enough of rotation to then use in the fragment portion. I advise don't bother computing a normal in the vertex shader (leave it flat) and then replace it completely in fragment portion since you may not know the tessellation to properly cancel out the interpolation.