   HLSL strange normal mapping problem
  I've been working on my first HLSL shaders and everythings working pretty well except for for one problem that's been driving me crazy, I've spent hours tinkering with the code and stepping through disassembly in PIX. The normal map looks fine when the object is at zero rotation, but as the object rotates the depth gets shallower and eventually inverts. A sample image of this effect is here:On the left is 0 rotation and is the way it's supposed to look, on the right is about 180 degrees rotation. You can see that the grout on the right image is actually looking higher than the bricks.http://www.twilightfair.com/otherstuff/images/NormalExamples.jpgThis would seem to be a simple problem, a missing transition or an out of order matrix multiplication, but I've had no luck finding it. I need some experienced eyes to take a look at the code and point out my fallacy. Here is the HLSL shader with all the extras pulled out (spectral mapping, spectral highlights, environment mapping, etc. etc.) It still has the same problem.Relevant C# code:   Matrix marbleWorld = Matrix.CreateFromYawPitchRoll(ballRotation.Y, ballRotation.X, ballRotation.Z);   effect.Parameters["WorldTransform"].SetValue(marbleWorld);   effect.Parameters["CameraTransform"].SetValue(marbleWorld * camera.View * camera.Projection);   effect.Parameters["DirectionalLightDirection"].SetValue(new Vector3(1.0f, -1.0f, 0.5f));   effect.Parameters["DirectionalLightColor"].SetValue(new Vector4(0.5f, 0.5f, 0.5f, 1.0f));HLSL codefloat4x4 WorldTransform;float4x4 CameraTransform;float3 DirectionalLightDirection;float4 DirectionalLightColor;////////////////////////////////////////////////////  Normal debugging shader//////////////////////////////////////////////////struct VertexIn{    float4 position  : POSITION0;    float2 texCoords : TEXCOORD0;    float3 normal    : NORMAL0;   float3 tangent   : TANGENT0;    float3 binormal  : BINORMAL0;};struct VertexToPixelBuggedNormal{float4 position : POSITION;    float2 texCoords: TEXCOORD0;float3 viewVector    : TEXCOORD1;float3 normal   : TEXCOORD2;    float3x3 tangentToWorld : TEXCOORD3;};struct VertexToPixelBuggedNormal{float4 position : POSITION;    float2 texCoords: TEXCOORD0;float3 viewVector    : TEXCOORD1;float3 normal   : TEXCOORD2;    float3x3 tangentToWorld : TEXCOORD3;};VertexToPixelBuggedNormal BuggedNormalVertexShader(VertexIn input){VertexToPixelBuggedNormal output = (VertexToPixelBuggedNormal)0;output.position = mul(input.position, CameraTransform);output.texCoords = input.texCoords;output.normal = normalize(mul(float4(input.normal,1.0f), WorldTransform));output.viewVector = -(DirectionalLightDirection.xyz*50) - mul(input.position, WorldTransform);output.tangentToWorld[0] = mul(float4(input.tangent,1.0f),WorldTransform).xyz;output.tangentToWorld[1] = mul(float4(input.binormal,1.0f),WorldTransform).xyz;output.tangentToWorld[2] = mul(float4(input.normal,1.0f), WorldTransform).xyz;return output;}PixelToFrame BuggedNormalPixelShader(VertexToPixelBuggedNormal input){    PixelToFrame output = (PixelToFrame)0;  // clean up our inputs a bit    input.viewVector = normalize(input.viewVector);    // bump map    float3 bumpNorm = (2 * (tex2D(BumpSampler, input.texCoords).rgb-0.5f));    bumpNorm = normalize(mul(bumpNorm,input.tangentToWorld));  // directional lightfloat4 DirectionalLighting = (dot(bumpNorm, input.viewVector)) * DirectionalLightColor;// base color   float4 baseColor = tex2D(TextureSampler, input.texCoords);    // adjust for lighting    baseColor.xyz *= saturate(DirectionalLighting + AmbientColor).xyz;   output.Color = baseColor;    return output; }technique BuggedNormal{    pass Pass0    {    VertexShader = compile vs_2_0 BuggedNormalVertexShader();    PixelShader = compile ps_2_0 BuggedNormalPixelShader();    }}
  Hi,I see that you're passing the rotation matrix to your shader. Why don't you multiply that matrix with your model's original world matrix?//You're using C# but I'll write in C++ cuz I don't know the other :)D3DXMATRIX marbleWorld;D3DXMatrixIdentity (&marbleWorld, &marbleWorld); //this makes your sphere located on the origin and zero-rotated.D3DXMATRIX marbleRotation;D3DXMatrixRotationY (&marbleRotation, timeGetTime() / 1000.0f); //slowly and time-based turning around y-axismarbleWorld *= marbleRotation; //final transformationEffect->SetMatrix ("WorldTransform", &marbleWorld);Hope this helps.Rohat.   ;Quote:Original post by programci_84Hi,I see that you're passing the rotation matrix to your shader. Why don't you multiply that matrix with your model's original world matrix?*** Source Snippet Removed ***Hope this helps.Rohat.Thanks for you're reply, but the rotation matrix is the models world matrix. All it can do is rotate in this simple sample application.True, to be absolutely correct it should be multiplied by an identity matrix which represents the sphere in it's initial state, but this ultimately has no impact on the result. I did try it to be sure.I discovered a potentially important clue though. By placing the camera above the sphere I could see that the normal map effects were not changing with the rotation as I had thought, but that the map is effecting the sphere differently around it's circumference. That is, in the 'front' of the sphere the normals look correct, at the 'sides' the normal map is almost canceled out and in the 'back' the maps effects seem inverted. This is constant as the sphere rotates, so with the camera fixed in front of the sphere it looks like normals are changing but they are not. This leads me to think something's wrong with    output.tangentToWorld[0] = mul(input.tangent, WorldTransform);    output.tangentToWorld[1] = mul(input.binormal, WorldTransform);    output.tangentToWorld[2] = mul(input.normal, WorldTransform);Or perhaps there's something not quite right with the tangent and binormal I'm getting? Hmmm.   ;  Hi,Perhaps your mesh's vertex data doesn't contain any TBN basis. You may need to clone your mesh with TBN basis vertex declaration:D3DVERTEXELEMENT9 TBNElems[] = {   {0, 12, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},   {0, 24, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0},   {0, 36, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TANGENT, 0},   {0, 48, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BINORMAL, 0},   {0, 60, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},   D3DDECL_END};LPD3DXMESH cloneMesh = NULL;YourSphereMesh->CloneMesh (YourSphereMesh->GetOptions(), TBNElems, YourD3DDevice, &cloneMesh);//...LPD3DXMESH cloneTBNMesh = NULL;D3DXComputeTangentFrameEx (cloneMesh, D3DDECLUSAGE_TEXCOORD, 0,D3DDECLUSAGE_BINORMAL, 0,D3DDECLUSAGE_TANGENT, 0,D3DDECLUSAGE_NORMAL, 0,cloneMesh->GetOptions() | D3DXTANGENT_GENERATE_IN_PLACE,NULL, .01f, .25f, .01f, &cloneTBNMesh, NULL););After these computations, your mesh is ready to be used for normal mapping.Hope this helps.   ;Quote:Original post by programci_84Hi,Perhaps your mesh's vertex data doesn't contain any TBN basis. You may need to clone your mesh with TBN basis vertex declaration:*** Source Snippet Removed ***After these computations, your mesh is ready to be used for normal mapping.Hope this helps.Yes, when I got your post I was in the middle of implementing the XNA equivalent of what you suggested (you have to use a Content Processor at build time, there is no handy Mesh->CloneMesh in XNA) suspecting that very thing. I had tried a couple of different models, but it turns out NONE of them had TBN data.So this was, indeed, the problem. The relief I feel for finding the issue and realizing my shaders have been correct all along is completely overwhelmed by my disgust at myself for not having checked the validity of my starting data days ago!Arg!At least I learned A LOT about shaders while trying to solve a problem that wasn't in the shaders.Thanks for the help, you did identify the problem I was having.   