Back to home

Normal mapping

Just some thoughts for myself as a reminder on how to work with normalmapping and shaders

  • When you use varying variables in your shader (i.e. normals: binormal, tangent, normal) make sure to normalize them in your fragment shader as varyings are interpolated between vertex and fragment shader which mess up normalization done in the vertex shader.
  • I found it easiest to debug/test my shaders by not using a diffuse texture but just using a very dark color for the diffuse part. I.e. 0.01 for RGB. 
  • Also, use a normal map which does not have very fine details but use a coarse one
  • I was using the shaders below though I still have some questions like: do I need to normalize the tangent and bitangent for the tangent matrix; it doesnt seem to matter though. Also I had to put the lights at a negative z position (-50), when using positive z values I didn't see any lighting. This can be due to the light direction vector... this was exactly the case; I had to transform the light position into tangent space as well.
  • Also use at least two lights which makes testing the normalmap a lot clearer
  • Use a hight specularity value so it has a sharp fallof (50-128)
  • Use a moving light so you can better see if shading works; sometimes it's difficult to notice shading changes because they are minimal

Vertex shader

#define LIGHT_COUNT 2
 
attribute vec4 pos; 
attribute vec2 tex;
attribute vec3 norm;
attribute vec4 tan;
 
uniform mat4 modelview;
uniform mat4 projection;
uniform mat4 modelview_projection;
 
struct Light {
	vec3 position;
	vec4 diffuse_color;
	vec4 specular_color;
};
 
uniform Light lights[LIGHT_COUNT];
 
varying vec2 texcoord;
varying vec3 normal;
varying vec3 tangent;
varying vec3 binormal; 
varying vec3 light_directions[LIGHT_COUNT];
varying vec3 eye_position;
varying vec3 eye_normal; 
 
void main() {
	gl_Position = modelview_projection * pos;
	eye_position = vec3(modelview * pos);
	texcoord = tex;
	normal = norm;
	eye_normal = normalize(vec3(modelview * vec4(normal,0.0)));
	tangent = normalize(tan.xyz);
	binormal = cross(eye_normal, tangent) * tan.w;
	binormal = normalize(binormal);
	mat3 tangent_space = mat3(tangent, binormal, eye_normal); 
 
	for(int i = 0; i < LIGHT_COUNT; ++i) { 
		vec3 lp = (vec3(modelview * vec4(lights[i].position, 0.0)));
		light_directions[i] = normalize(tangent_space * (lp - eye_position)); 
	}
}

Fragment shader

#define LIGHT_COUNT 2
 
uniform sampler2D diffuse_texture; 
uniform sampler2D normal_texture; 
 
struct Light {
	vec3 position;
	vec4 diffuse_color;
	vec4 specular_color;
};
uniform Light lights[LIGHT_COUNT];
 
varying vec3 eye_position;
varying vec3 normal;
varying vec3 eye_normal;
varying vec2 texcoord;
varying vec3 tangent;
varying vec3 binormal;
varying vec3 light_directions[LIGHT_COUNT];
 
void main() {
	vec4 texel_color = vec4(0.0, 0.0, 0.0, 1.0);
	vec3 final_normal = normalize(eye_normal);
	vec3 normal_color = texture2D(normal_texture, texcoord).xyz * 2.0 - 1.0;
	normal_color = normalize(normal_color);
	mat3 tangent_matrix = mat3(tangent, binormal, final_normal);
	final_normal = normalize(tangent_matrix * normal_color);	vec4 diffuse_color = texture2D(diffuse_texture, texcoord);
	texel_color += diffuse_color;
	for(int i = 0; i < LIGHT_COUNT; ++i) { 
		float n_dot_l = max(dot(normalize(final_normal), light_directions[i]), 0.0); 
		if(n_dot_l > 0.0) {
			texel_color += ( 0.4*(n_dot_l * lights[i].diffuse_color));
			vec3 reflection = normalize(reflect(-normalize(light_directions[i]), final_normal));
			float spec = max(0.0, dot(final_normal, reflection)); 
			float fspec = pow(spec, 352.0);
			texel_color += (fspec * lights[i].specular_color)  ;
		}
	}
	gl_FragColor = texel_color;
}