# Normal mapping erroneous results for diffuse lighting

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

## Recommended Posts

I have been trying to implement normal mapping with a degree of success but it does not seem to work entirely as it should. I tried verifying that it works correctly by binding a normal map filled with values (0, 0, 1) and comparing the results of this draw call with one where I did not bind a normal map and instead just used the normals of the mesh for lighting calculations (simplified here as just diffuse lighting calculation). As I understand it, this is an 'identity normal map' since value (0, 0, 1) in the texture represents an unperturbed normal. But the results are not identical.

I've made some comparison pics. The direction of the camera matches the light direction in the first two, and it is exactly opposite on the second two:

It seems like the normals are inverted 'inside out' in the picture 1b.

I don't know why it looks like light hitting the tree in picture 2b.

Since the code calculating the tangents is just a copy of Lengyel I think it's not the source of the problem. I suspect the vertex shader(particularly the calculation of the tangent space light direction) is responsible. I make sure that all of the vectors(view, light, normal, tangent, bitangent) are in the same space (world/model space) and then transform the view and light vectors into the tangent space, constructed from the normal, tangent and bitangent vectors.

Then in the pixel shader depending on whether the normal map is bound or not I use the original mesh's normal and world space light direction for diffuse lighting or I sample the normal map and use the tangent space light direction. I would expect the results to be the same.

Am I making an unsound assumption at any point here? Thank you.

Code (all of it is based on Eric Lengyel's work at: http://www.terathon.com/code/tangent.html)

Calculating tangents (almost a direct copy of Lengyel)

// Receives 3 vertices of a triangle and their corresponding normal
Vector4 CalculateTangent(Vertex vertex1, Vertex vertex2, Vertex vertex3, Vector3 normal)
{
Vector3 v1 = vertex1.position;
Vector3 v2 = vertex2.position;
Vector3 v3 = vertex3.position;
Vector2 uv1 = vertex1.texCoord;
Vector2 uv2 = vertex2.texCoord;
Vector2 uv3 = vertex3.texCoord;

// Edge v2 - v1
float x1 = v2.x - v1.x;
float y1 = v2.y - v1.y;
float z1 = v2.z - v1.z;
float s1 = uv2.x - uv1.x;
float t1 = uv2.y - uv1.y;

// Edge v3 - v1
float x2 = v3.x - v1.x;
float y2 = v3.y - v1.y;
float z2 = v3.z - v1.z;
float s2 = uv3.x - uv1.x;
float t2 = uv3.y - uv1.y;

float r = 1.0f / (s1 * t2 - s2 * t1);

Vector3 t = Vector3(t2 * x1 - t1 * x2,
t2 * y1 - t1 * y2,
t2 * z1 - t1 * z2) * r;

Vector3 b = Vector3(s1 * x2 - s2 * x1,
s1 * y2 - s2 * y1,
s1 * z2 - s2 * z1) * r;

// Gram-Schmidt orthogonalize.
Vector3 tangent = Math::Normalize((t - normal * Math::Dot(normal, t)));
// Calculate handedness.
float handedness = (Math::Dot(Math::CrossProduct(normal, t), b) < 0.0f) ? -1.0f : 1.0f;

return Vector4(tangent, handedness);
}


cbuffer LightBuffer: register(b3)
{
float4 lightDir;		// in model(world) space
float4 cameraPosition;		// in model(world) space
};

{
float3 pos : POSITION;
float3 normal : NORMAL;
float4 tangent : TANGENT;
float2 texCoord: TEXCOORD;
float4 params: PARAMETERS;
float3 instance_pos : INSTANCE_POSITION;
};

{

float4 pos = float4(input.pos, 1.0f);

pos = mul(model, pos);
pos.xyz += input.instance_pos;
output.worldPos = pos;
output.viewDir = cameraPosition.xyz - output.worldPos.xyz;
pos = mul(view, pos);
pos = mul(projection, pos);
output.pos = pos;

float4 normal = float4(input.normal, 0.0f);	// model/world space
normal = mul(model, normal);
output.normal = normal.xyz;
normal = normalize(normal);

float4 tangent = float4(input.tangent.xyz, 0);
tangent = mul(model, tangent);	// model/world space
tangent = normalize(tangent);

float3 bitangent = cross(input.normal, input.tangent.xyz) * input.tangent.w; // tangent.w == handedness
bitangent = mul(model, float4(bitangent, 0)).xyz;	// model/world space
bitangent = normalize(bitangent);

float3 viewDir = output.viewDir;	// in model/world space

output.tangentSpaceViewDir = float3(dot(normal.xyz, viewDir),
dot(tangent.xyz, viewDir),
dot(bitangent, viewDir));

output.tangentSpaceLightDir = float3(dot(normal.xyz, lightDir.xyz),
dot(tangent.xyz, lightDir.xyz),
dot(bitangent, lightDir.xyz));
output.worldSpaceLightDir = lightDir.xyz;
...
}


struct PixelShaderInput
{
float4 pos : SV_POSITION;
float2 texCoord : TEXCOORD;
float4 worldPos: WORLDPOS;
float3 normal : NORMAL;
float3 lightDir : LIGHT;
float4 diffuseReflect : DIFFUSE;
float4 ambientReflect : AMBIENT;
float4 specularReflect: SPECULAR;
float3 viewDir : VIEWDIR;
float3 tangentSpaceViewDir : TANGENT_SPACE_VIEWDIR;
float3 tangentSpaceLightDir : TANGENT_SPACE_LIGHTDIR;
float transparency : TRANSP;
bool useTexture : USETEX;
};

// Only the barest lighting calculation for simplicity's sake. View direction is not used.
{
float3 normal;
float3 surfaceToLight;
float3 viewDir;

// Brightness(either with normal map + tangent space light and view vectors)
// or with mesh normals + world space light and view vectors
if (input.useTexture)	// Normal map
{
surfaceToLight = -normalize(input.tangentSpaceLightDir);
viewDir = input.tangentSpaceViewDir;
normal = colorTexture.Sample(texSampler, input.texCoord).rgb;
normal = normalize(2.0f * normal - 1.0f);
}
else
{
viewDir = normalize(input.viewDir);
surfaceToLight = -normalize(input.lightDir);
normal = normalize(input.normal);
}

float toLightDot = dot(normal, surfaceToLight);
float brightness = max(0.1f, saturate(toLightDot));

float4 result = input.ambientReflect + input.diffuseReflect * brightness;
result.a = 1;
return result;
}


##### Share on other sites

Just a quick thought while I'm passing by, but:

output.tangentSpaceViewDir = float3(dot(normal.xyz, viewDir),
dot(tangent.xyz, viewDir),
dot(bitangent, viewDir));

output.tangentSpaceLightDir = float3(dot(normal.xyz, lightDir.xyz),
dot(tangent.xyz, lightDir.xyz),
dot(bitangent, lightDir.xyz));

Are you sure this is the correct order? I'm wondering if maybe instead of moving from WS to tangent space, you're doing the opposite (WS to whatever space). Maybe you need to transpose this matrix?

##### Share on other sites

No it is not the correct order :). Thank you Styves.

The correct order should look like this (it's not that it should be transposed, I just messed up the order of the vectors):

	output.tangentSpaceViewDir = float3(dot(tangent.xyz, viewDir),
dot(bitangent, viewDir),
dot(normal.xyz, viewDir));

output.tangentSpaceLightDir = float3(dot(tangent.xyz, lightDir.xyz),
dot(bitangent, lightDir.xyz),
dot(normal.xyz, lightDir.xyz));


The lighting looks correct now.

##### Share on other sites

Awesome, glad you got it working.

1. 1
2. 2
Rutin
22
3. 3
4. 4
frob
17
5. 5

• 9
• 33
• 13
• 12
• 10
• ### Forum Statistics

• Total Topics
632577
• Total Posts
3007157

×