# Normal mapping in deferred and forward rendering

This topic is 744 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

I have implemented normal mapping in forward and deferred rendering paths but the results are slightly different (see the attached images and maybe download to compare them better). In forward rendering I'm doing the calculations in tangent space and in deferred rendering I'm doing the calculations in world space. I guess the difference comes from interpolating values between vertex and fragment shader. Let's see the most interesting parts of the shader code (I have narrowed the problem down to the value of "float diff"):

Forward

N = normalize(mat3(M) * normal); // M = model to world space matrix
T = normalize(mat3(M) * tangent);
B = cross(T, N);
mat3 TBN = transpose(mat3(T, B, N)); // Transpose to get transform from world to tangent space
lightPos_Tan = TBN * lightPos_World; // Output to fragment shader
vertexPos_Tan = TBN * vertexPos_World; // Output to fragment shader


vec3 normal_Tan = normalize(texture(normalTexture, texCoord).rgb * 2.0 - vec3(1.0));
vec3 lightDir_Tan = normalize(lightPos_Tan - vertexPos_Tan); // Inputs from vertex shader
float diff = clamp(dot(normal_Tan, lightDir_Tan), 0, 1);


Deferred

vec3 N = normalize(mat3(M) * normal); // M = model to world space matrix
vec3 T = normalize(mat3(M) * tangent);
vec3 B = cross(T, N);
TBN = mat3(T, B, N); // Output to fragment shader

vec3 normal = normalize(texture(normalTexture, texCoord).rgb * 2.0 - vec3(1.0));
outNormal = normalize(TBN * normal); // TBN = Input from vertex shader


vec3 normal = texture(normalTexture, texCoord).rgb; // Normal in world space written in gbuffer fragment shader
vec3 lightDir = normalize(lightPos - vertexPos); // Positions in world space
float diff = clamp(dot(normal, lightDir), 0, 1);


Do the shaders look OK? My guess is that the difference comes from interpolation between vertex and fragment shader: in forward rendering there are two vec3s and in deferred rendering there is a mat3. If in forward rendering the TBN matrix is created on fragment shader the results seem nearly identical (but this would be inefficient). Hence my questions are:

1. What could explain the small difference in the attached images?

2. How is mat3 interpolated between vertex and fragment shader?

3. What should be done to make results identical between forward and deferred rendering?

##### Share on other sites

What gbuffer format are you using? Have you tried verifying the gbuffer contents to ensure no precision/srgb/etc shenanigans are interfering?

##### Share on other sites
I would implement world-space shading in forward too, and then work to make your TS and WS forward shaders look the same. This will be simpler by removing the other differences of forward vs deferred.

The issue is likely that, while WS is well defined and consistent for the whole world, TS is defined per vertex, and we then interpolate it to get a new TS definition per pixel.

When interpolating a direction vector, you should normalize it in the pixel shader to avoid any unintended scaling. When interpolating three basis vectors, you should reorthonormalize them in the pixel shader (use cross to make sure they're all at right angles) to avoid unintended skewing.

##### Share on other sites
I treat the TBN matrix as 3 (2 + 1 extrapolated) orthonormal vectors that are interpolated by the fragment shader.

// attributes
attribute vec3 vtx_t;
attribute vec3 vtx_n;

//variables
varying vec3 t_interp, n_interp;

t_interp = M * vec4( vtx_t, 0.0 );
n_interp = M * vec4( vtx_n, 0.0 );

vec3 t, b, n;
t = normalize( t_interp );
b = normalize( cross( n_interp, t_interp ) );
n = cross( t, b );

vec3 norm = normalize( 2.0 * texture2D( tex, uv ).rgb - vec3(1.0) );

norm = norm.x * t + norm.y * b + norm.z * n;

The gotcha here is that like the vertex normal, the tangent and bitangent vectors are usually interpolated across the fragment shader, and unless they are the same across all 3 vertices, they will be skewed.

I tend to interpolate only two and extract the third during the re-orthonormalization process.

• ### What is your GameDev Story?

In 2019 we are celebrating 20 years of GameDev.net! Share your GameDev Story with us.

• 15
• 14
• 46
• 22
• 27
• ### Forum Statistics

• Total Topics
634046
• Total Posts
3015219
×