basic normal map shader


for a good looking terrain I decided I decided to go with texture splatting. Since I wanted more textures as could be implemented by the technique shown in this example I had to write my own shader. Miraculously I got that working (at least on a basic stage so far). Now my next problem was normal mapping, for each texture I add to the terrain I want a normal map that is ‘splatted’ on top. To start of simple I tried writing a shader that only uses one color and one normal map:

//Cg profile arbvp1 arbfp1 

void vshader( in float4 vtx_position : POSITION, 
              in float2 vtx_texcoord0 : TEXCOORD0, 
              in uniform float4x4 mat_modelproj,

              out float2 l_texcoord0 : TEXCOORD0, 
              out float4 l_position : POSITION) 

void fshader( in uniform sampler2D tex_0 : TEXUNIT0,
	      in uniform sampler2D tex_1 : TEXUNIT1,
              in float2 l_texcoord0 : TEXCOORD0, 
	      in float4 l_position : POSITION,
	      in uniform float3 k_lightvec, 

              out float4 o_color : COLOR )
	float3 N = tex2D( tex_1, l_texcoord0 ) * 2.0 - 1.0 ;
	N = normalize(N);

	float3 L = normalize(k_lightvec);
	L.x *= -1;

	float diffuse = saturate( dot( N,L ) )*.5+.5;

	o_color = tex2D( tex_0, l_texcoord0 );
	o_color *= diffuse; 
	o_color.a = 1;

It is working decently (not giving the same result as the built in bump mapping shader but I can live with that) yet I am having two problems:

The first one is pretty basic, why do I have to multiply the x component of the light vector by -1? If I do not do this the light seems to light up the wrong ‘side’ of the texture.

The second problem probably has got to do with coordinate spaces. The code now is independent of the angle of the triangles; even if the triangles are parallel to the light vector, texture is lit up as if the triangle was lying horizontally. How can I modify this?

My python code:

import direct.directbase.DirectStart
from pandac.PandaModules import *

terrain = loader.loadModel('terrain')




Thanks in advance!

How does it look if you just use the regular normal for the lighting calculation, instead of the normal map? Try that (without the -x, also) first. If that looks ok, make sure your actual normal map is constructed correctly.

thanks for the reply,

Your suspicion seems to be right: when using the autoshader without normal map the light comes from the correct direction, when I add the normal map the light is flipped x-wise. I am using the color and normal maps from the bump-mapping sample (layingrock-n.jpg). The strange thing is that the normal maps seems to be working correctly in the bump-mapping sample, also when a directional light is added.

can anyone point me in the direction of the normal map shader used by the shader generator? I tried understanding the C++ shader generator class but I can’t make heads or tails out of it…

You can try to set

dump-generated-shaders #t

in Config.prc to dump generated shaders on run and look at dumped files.

That was exactly the direction I meant, thanks :smiley:. Got it working now!

//Cg profile arbvp1 arbfp1 

void vshader(
		in float2 vtx_texcoord0 : TEXCOORD0, 
		in float4 vtx_position : POSITION,
		in float4 vtx_normal : NORMAL,
		in float4 vtx_tangent1,
		in float4 vtx_binormal1,
		uniform float4x4 mat_modelproj,
		uniform float4x4 trans_world_to_model,

		in uniform float3 k_dlightvec, 

		out float2 l_texcoord0 : TEXCOORD0, 
		out float4 l_position : POSITION,
		out float3 l_normal,
		out float3 l_tangent,
		out float3 l_binormal,
		out float3 l_dlightvec
) { 

	l_normal =;
	l_tangent =;
	l_binormal =;

	l_dlightvec = normalize(mul((float3x3)trans_world_to_model, -k_dlightvec));

void fshader(
		uniform sampler2D tex_0 : TEXUNIT0,
		uniform sampler2D tex_1 : TEXUNIT1,

		in float2 l_texcoord0 : TEXCOORD0, 
		in float4 l_position : POSITION,
		in float3 l_normal,
		in float3 l_tangent,
		in float3 l_binormal,
		in float3 l_dlightvec,

		in uniform float4 k_dlightcolor, 
		in uniform float4 k_alightcolor,

		out float4 o_color : COLOR
) {
	// Getting the correct normals
	float3 TN = tex2D( tex_1, l_texcoord0 ) * 2.0 - 1.0 ; //Texture normal
	float3 CN = l_normal; //Combined normal
	CN *= TN.z;
	CN += l_tangent * TN.x;
	CN += l_binormal * TN.y;
	CN = normalize(CN);

	// Getting the correct light for this fragment
	float4 lightcolor = k_alightcolor;
	lightcolor += k_dlightcolor*saturate(dot(CN,l_dlightvec));
	lightcolor = saturate(lightcolor);

	// Final fragment color
	o_color = tex2D( tex_0, l_texcoord0 );
	o_color *= lightcolor;

Even though the shader is working I still have some questions:

  1. Am I correct to think that the tangent is a vector in the direction of where the x-direction of the texture is going on the model? (So the direction (u,v)->(u+.0001,v) mapped on the model) and the binormal the y-direction ((u,v)->(u,v+.0001)
  2. Why does the binormal have to be multiplied by -1 (i think because the y direction on a uv map is opposite to the y-direction in model space)
  3. why does the light vector have to be multiplied by -1

I am already very pleased with the results but it is much appreciated if someone could confirm my first two ideas!