Skip to main content
gamedev.net
🔒 Locked

problem with normalmap!

Started by genesys Oct 14, 2005 at 11:02 AM 2 replies 750+ views
Original Post
genesys
genesys
Hi! I've got a strange problem... I'm working on a shader for a landscape and i'm getting problems with the lambert shading. see the following screenshots: on the upper left you can see the normals of my mesh. On the right there is a rendered normalmap i wan't to use for my pixelshader. on the bottom you see the shading (the red dot is my pointlight). With the meshnormals, there is no problem. But when i'm using the normalmap instead of the meshnormals, the shading gets dark in the right bottom corner and bright in the upper right corner! i really don't know how this can be because the both normalmaps look for me the same (at least the green value for example is the same) - the normalmap is just more complex than the mesh normals... So - can anyone guess what could go wrong? for the shading i just take shading=dot(normalize(lightvector),normals); Thx!
neneboricua19
neneboricua19
Normal maps store values in the range of [0..1]. When you fetch this value from the normal map, you have to adjust its range to [-1..1]. Also, make sure you normalize this new vector. You can do this as follows:

vNormal = normalize( 2.0f*tex2D( NormalMapSampler, In.vTexCoords ) - 1.0f );

It seems like your normal map is not stored in tangent-space. That is fine as long as you perform all lighting calculations in the same space. I think your normal map is probably stored in model space. If this normal map was created by your artist, then it probably is stored in model space.

So for your lighting to work correctly, both your normal vector and your light vector must be in the same space. The best way to do this is generally to transform your light vector into model space using the inverse of the world matrix of your mesh. You can do this in your main application and just pass the resulting vector to your pixel shader as a constant. Your shader would then just fetch the normal, adjust its range, normalize it, and perform a dot prodcut with the light vector you passed in as a constant.

Hope this helps,
neneboricua
genesys
genesys
what you say makes sense for me... but as I tried that I got the following result:





:(

What could be the problem?
neneboricua19
neneboricua19
Quote:
Original post by genesys
what you say makes sense for me... but as I tried that I got the following result:

What exactly did you try? Normalizing the data in the normal map, transforming to a different space? What?

First, you need to know what space your normal maps are created in. This is pretty easy to see if you can just load the normal map into some file viewer. If your normalmap is very blue, then it is in Tangent space. If your normal map is red, green, and blue, then it is in model space.

Once you know which space your normal maps are in, you can transform your light(s) into that space. If your normal maps are in model space, then you'll need to transform your lights by the inverse of the model's world matrix. You should do this in the main application. Once you've transformed the light into model space, you can set this position as a constant to your shader.

If your normal maps are in tangent space, then you'll need to transform your light(s) into tangent space as well. Since the tangent space defined at each vertex of a mesh is different, you'll need to transform your light in the vertex shader of your mesh. The tangent basis vectors stored in each vertex transform things from model space to tangent space. So that means that your application still has to transform the light from world space to model space and set it as a constant to your shader, just like as if your normal maps were in model space. Your vertex shader can then transform your light from model space to tangent space using the tangent basis vectors of each vertex.

When your pixel shader runs, it doesn't need to know what space things are in. As long as all data is in the same space, the pixel shader calculations will remain exactly the same.

Here is a very simple shader that will transform your data into tangent space and use the pixel shader to light things up:
VS_OUTPUT RenderSceneVS( float4 vPos : POSITION,                          float3 vNormal : NORMAL,                         float3 vBinormal : BINORMAL,                         float3 vTangent : TANGENT,                         float2 vTexCoord0 : TEXCOORD0 ){  VS_OUTPUT Out;  // Transform the position from object space to homogeneous projection space  Out.vPosition = mul(vPos, g_mWorldViewProjection);    // Just copy the texture coordinate through  Out.vTexCoord = vTexCoord0;      // Calculate vector from vertex to light source (done in model space)  float3 vLightDir = g_vLightPos - vPos.xyz;      // Transform light vector from model space to tangent space.  float3x3 mTangentSpace = float3x3( vBinormal, vTangent, vNormal );  Out.vLightDir = mul( mTangentSpace, vLightDir );      // Output tangent space normal.  Out.vNormal = mul( mTangentSpace, vNormal );         return Out;    }float4 RenderScenePS( VS_OUTPUT In ) : COLOR0{  // Fetch normal from normal map and adjust to proper range [0..1] -> [-1..1]  float3 vNormalMap = normalize( (2.0f * tex2D(MeshNormalSampler, In.vTexCoord)) - 1.0f );  float3 vInterpolatedNormal = normalize(In.vNormal);  float3 vNormal = normalize( vNormalMap + vInterpolatedNormal );  float3 vLightDir = normalize(In.vLightDir);		  return max(dot(vNormal, vLightDir), 0.0f) * tex2D(MeshTextureSampler, In.vTexCoord);}

Hope this helps,
neneboricua

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.