Dhpoware Demos Normal Mapping unexpected result

Recommended Posts

I'm sure a lot of beginners who do their research eventually find the Dhpoware demos: http://www.dhpoware.com/demos/index.html

I learned to do Blinn–Phong and normal mapping from there a long while ago. Normal mapping in particular:

http://www.dhpoware.com/demos/d3d9NormalMapping.html

http://www.dhpoware.com/demos/xnaNormalMapping.html

Yet I have always had a major problem with the point light shader: it would give very strange results as the object rotates. Sometimes the direction or the intensity would change direction as the objects rotates, or if I kick over an object and move so far away that I'm outside the light radius, the sides of the object, the parts that are almost parallel with my view direction would flare up, becoming very bright.

It is hard to screenshot because it requires rotating an object through physics interaction, but I got a sample of the sides of the object flaring up in "Untiled.png".

Untitled2 and Untitled3 show another common artifact. This time I disabled everything except for point lighting on the barrel, and the cap of barrel changes illumination intensity as it rotates. The two screenshots were taken about one second apart. As the barrel rolls, the cap will flare up and dim out completely, while the sides remain relatively constantly lit.

I kind of isolated the bug to the attenuation, but I'm not sure if that's the problem and what it is.

Here is the vertex shader for reference:

VS_OUTPUT_POINT VS_PointLighting(VS_INPUT IN)
{
VS_OUTPUT_POINT OUT;

float3 worldPos = mul(float4(IN.position, 1.0f), worldMatrix).xyz;
float3 viewDir = cameraPos - worldPos;
float3 lightDir = (light.pos - worldPos) / light.radius;

float3 n = mul(IN.normal, (float3x3)worldInverseTransposeMatrix);
float3 t = mul(IN.tangent.xyz, (float3x3)worldInverseTransposeMatrix);
float3 b = cross(n, t) * IN.tangent.w;
float3x3 tbnMatrix = float3x3(t.x, b.x, n.x,
t.y, b.y, n.y,
t.z, b.z, n.z);

OUT.position = mul(float4(IN.position, 1.0f), worldViewProjectionMatrix);
OUT.texCoord = IN.texCoord;
OUT.viewDir = mul(viewDir, tbnMatrix);
OUT.lightDir = mul(lightDir, tbnMatrix);
OUT.diffuse = material.diffuse * light.diffuse;
OUT.specular = material.specular * light.specular;

return OUT;
}


float4 PS_PointLighting(VS_OUTPUT_POINT IN) : COLOR
{
float atten = saturate(1.0f - dot(IN.lightDir, IN.lightDir));

float3 n = normalize(tex2D(normalMap, IN.texCoord).rgb * 2.0f - 1.0f);
float3 l = normalize(IN.lightDir);
float3 v = normalize(IN.viewDir);
float3 h = normalize(l + v);

float nDotL = saturate(dot(n, l));
float nDotH = saturate(dot(n, h));
float power = (nDotL == 0.0f) ? 0.0f : pow(nDotH, material.shininess);

float4 color = (material.ambient * (globalAmbient + (atten * light.ambient))) +
(IN.diffuse * nDotL * atten) + (IN.specular * power * atten);

return color * tex2D(colorMap, IN.texCoord);
}


The way I temporary fixed this to make the shader usable was by adding a second lightDir output to the vertex shader, one that maintains the original light direction before TBN matrix transformation:

float3 lightDir = (light2.pos - worldPos) / light2.radius;

...

OUT.lightDir = mul(lightDir, tbnMatrix);
OUT.lightDir2 = lightDir;


Then, in the pixel shader I base my attenuation computation on this new value:

float atten = saturate(1.0f - dot(IN.lightDir2, IN.lightDir2));


This works, but as I'm now trying to cram as many point lights into the shaders and creating permutations for all the number of lights, I am running out of intrinsic because the extra field that the shader outputs.

Can someone tell me if they see something wrong with the shader? My models get their world matrix assigned by some very complex computations, but they do wind up having translation, non-uniform scale and rotation. Or if the shader looks fine, can someone point me to some working point light with normal mapping shader? I Googled it and couldn't really find some good samples, only point lights without normal mapping or with weird attenuation computations.

PS: Dhpoware also includes some Parallax Normal Mapping demos, but I have never been able to get parallax to not have very ugly swirly artifacts on anything that is not a flat surface and doesn't use some repeating patterns, like a brick wall or something. Can parallax be made to look good, or does one generally stick to plain normal mapping.

Share on other sites

So nobody noticed anything wrong with the shaders...

I'll try and create a very short demo with shaders and the model attached that can be run.

Share on other sites

One mistake i noticed is viewDir. AB vector is B-A so view vector should be cam to wPos so

float3 viewDir = worldPos-cameraPos; same with lightDir,

float3 lightDir = (worldPos-light.pos) / light.radius;

Share on other sites

One mistake i noticed is viewDir. AB vector is B-A so view vector should be cam to wPos so

float3 viewDir = worldPos-cameraPos; same with lightDir,

float3 lightDir = (worldPos-light.pos) / light.radius;

Thanks for the reply! Unfortunately, swapping them around causes the light to no longer be rendered at all.

Share on other sites

I created a video showing of the attenuation bug I am experiencing. First I show the bug with a barrel pinned to the camera direction with two separate rotations, then I show the same two angles with my correction to the shader:

Share on other sites

Non-uniform scales generally makes lighting more expensive and complicated. What I believe that shader you have is doing is assuming that there is no non-uniform scale, and therefore the scale can't affect the direction of the normals.

There's a good (if somewhat old) explanation of lighting, and how the maths works at http://its.lnpu.edu.ua/edocs1/new_doc/en/mathematicsofperpixellighting.pdf which should help you fix your problem. Just remember that it's OpenGL so some of the conventions are slightly different.

Alternatively avoid non-uniform scaling

Share on other sites

Good you postet that video, i had similar bug with same effect.

float power = (nDotL == 0.0f) ? 0.0f : pow(nDotH, material.shininess); should be

it should be (nDotL < 0.0f)

EDIT: nvm i didn't notice that saturate

Edited by -Tau-

Share on other sites

Non-uniform scales generally makes lighting more expensive and complicated. What I believe that shader you have is doing is assuming that there is no non-uniform scale, and therefore the scale can't affect the direction of the normals.

There's a good (if somewhat old) explanation of lighting, and how the maths works at http://its.lnpu.edu.ua/edocs1/new_doc/en/mathematicsofperpixellighting.pdf which should help you fix your problem. Just remember that it's OpenGL so some of the conventions are slightly different.

Alternatively avoid non-uniform scaling

While normally I am using non-uniform scaling to fit a mesh into a physics shape, in this video scaling is uniform. And anyway, you should be able to fix non-uniform scaling by using the inverse transpose matrix.

The only change between the wrong shading and the correct one is the attenuation. I think attenuation gets converted to tangent space or something similar.

Share on other sites

Good you postet that video, i had similar bug with same effect.

float power = (nDotL == 0.0f) ? 0.0f : pow(nDotH, material.shininess); is wrong

it should be (nDotL < 0.0f)

Thanks! That makes sense for the specular component.  It won't fix my problem though, because the diffuse component of the lighting is wrong. Everything that is multiplied by the attenuation is wrong.

Edit: should have posted a video just with the diffuse component.

Edited by DwarvesH

Share on other sites

Edit: should have posted a video just with the diffuse component.

Actually, let me post a video that visualizes only the attenuation value. The spot light is at camera position and has a range of 15m, that's why I'm showing the barrel at 3 different distances from the camera in the "corrected" implementation:

In the wrong scenario, the attenuation value is clearly rotating out of sync with the object.

Share on other sites

I'm not exactly sure what's going on here but even the ad-hoc correction looks a bit wrong to me.  It's better but still seems off.  I've seen similar anomalies that were the result of messed up tangents and normals; you might want to look into how those are being computed.

One thing that jumps out at me is that you are doing your lighting in tangent space.  When you have just one light that's fine but for each light you have to increase the amount of data that gets interpolated for use by the pixel shader.  It's better to pass the world space tangent and normal vector to the pixel shader then construct the tbn matrix there and transform the per-pixel normal into world space.  That way your light data can exist in world space and simply be stored in constant buffers with the added benefit of your reflection model and attenuation calculations living in world space.  This scales much better, is good for your sanity, and keeps your interpolator count down.

Create an account

Register a new account

• Forum Statistics

• Total Topics
627701
• Total Posts
2978711

• 21
• 14
• 12
• 10
• 12