Dot3 woes

Recommended Posts

I'm not sure whether this is the correct forum, but anyway. Okay, I've been ripping my hair out trying to get this working over the last few days. I'm trying to get a simple per pixel dot3 shader working for a point light (no attenuation at the moment), but I just can't get it to look right :-(. It SORT of works, as in I can see the diffuse component of the dot3 operation changing as the light moves around, but it doesn't look good at all (everything looks black as the light gets closer to the polygon, and the illumination angles seem all wrong). I have a feeling that its something to do with transforming the light direction vector into tangent space. Can someone have a look at my code and see if theres any obvious (or not so obvious) flaws? Oh yeah, I'm using DX8.1.... Here is the tangent vector generation code, taken from the NVSDK:
    // Calculate the tangent vector for each vertex
for (uint32 iTri = 0; iTri < nNumTris; iTri++)
{
Vector edge01, edge02, cp;
uint16 TriIndex[3];

TriIndex[0] = pIndices[iTri];
TriIndex[1] = pIndices[iTri+1];
TriIndex[2] = pIndices[iTri+2];

assert((TriIndex[0] < nNumVertices) && (TriIndex[1] < nNumVertices) && (TriIndex[2] < nNumVertices));

Dot3Vertex& v0 = pVertices[TriIndex[0]];
Dot3Vertex& v1 = pVertices[TriIndex[1]];
Dot3Vertex& v2 = pVertices[TriIndex[2]];

// x, s, t
edge01 = Vector( v1.m_vPos.x - v0.m_vPos.x, v1.m_fU - v0.m_fU, v1.m_fV - v0.m_fV );
edge02 = Vector( v2.m_vPos.x - v0.m_vPos.x, v2.m_fU - v0.m_fU, v2.m_fV - v0.m_fV );

cp = edge01.Cross(edge02);
if ( fabs(cp.x) > SMALL_FLOAT )
{
v0.m_vTangent.x += -cp.z / cp.x;
v1.m_vTangent.x += -cp.z / cp.x;
v2.m_vTangent.x += -cp.z / cp.x;
}

// y, s, t
edge01 = Vector( v1.m_vPos.y - v0.m_vPos.y, v1.m_fU - v0.m_fU, v1.m_fV - v0.m_fV );
edge02 = Vector( v2.m_vPos.y - v0.m_vPos.y, v2.m_fU - v0.m_fU, v2.m_fV - v0.m_fV );

cp = edge01.Cross(edge02);
if ( fabs(cp.x) > SMALL_FLOAT )
{
v0.m_vTangent.y += -cp.z / cp.x;
v1.m_vTangent.y += -cp.z / cp.x;
v2.m_vTangent.y += -cp.z / cp.x;
}

// z, s, t
edge01 = Vector( v1.m_vPos.z - v0.m_vPos.z, v1.m_fU - v0.m_fU, v1.m_fV - v0.m_fV );
edge02 = Vector( v2.m_vPos.z - v0.m_vPos.z, v2.m_fU - v0.m_fU, v2.m_fV - v0.m_fV );

cp = edge01.Cross(edge02);
if ( fabs(cp.x) > SMALL_FLOAT )
{
v0.m_vTangent.z += -cp.z / cp.x;
v1.m_vTangent.z += -cp.z / cp.x;
v2.m_vTangent.z += -cp.z / cp.x;
}
}

// Normalize all the tangents
for (uint32 iVert = 0; iVert < nNumVertices; iVert++)
{
Dot3Vertex& v = pVertices[iVert];
v.m_vTangent = v.m_vTangent.Normalize();
}

Here is my vertex shader:
vs.1.1

// c0-c3        - world*view*proj
// c8           - light position(In object space)
// c9           - 1 / lightRange
// c10			- view position

// v0    pos
// v1    normal
// v2    tangent
// v3    colour
// v4    uv

// r3 reserved for light dir
// r4 reserved for camera dir
// r5 reserved for half angle
// r6 reserved for binormal

def c30, 1,1,1,1
def c31, 0.5, 0.5, 0.5, 0.5
def c32, 0,0,0,1

// Get direction to the light and normalize
sub r3, c8, v0
dp3 r3.w, r3.xyz, r3.xyz
rsq r3.w, r3.w
mul r3.xyz, r3.xyz, r3.w

// Get the direction to the camera and normalize
sub r4, c10, v0
dp3 r4.w, r4.xyz, r4.xyz
rsq r4.w, r4.w
mul r4.xyz, r4.xyz, r4.w

// Generate the binormal S by cross product
mov r7, v2
mov r8, v1
mul r1,-r7.zxy,r8.yzx

// Transform the light vector
dp3 r10.x, r6, r3 // binormal
dp3 r10.y, v2, r3 // tangent
dp3 r10.z, v1, r3 // normal
mov r10.w, c30.w
mov r3, r10

// Multiply by a half to bias, then add half
//mad r3,  r3,  c31, c31

// Output everything
mov oT0, v4		  // texture coord
mov oT1, v4
m4x4 oPos, v0, c0 // Position
mov oD0, r3       // light dir

And finally the pixel shader (no base coolour texture yet, just outputting the diffuse):
ps.1.1

tex t0 // normal map

dp3	r0, t0_bx2, v0_bx2  // Dot(normal, light direction)

Any help to regain my sanity is muchly appreciated :).

Share on other sites
I've seen the algorithm you're using to compute your tangent space before, but I can't remember all the details. Inside all the "if" statements, you have something like this:

m_vTangent.x += -cp.z / cp.x;

Are you sure that it's always supposed to be -cp.z??? I thought I remembered that a few of those should be different. I could be mistaken though.

As far as your shaders are concerned, first make sure that your normal map is correct. If you really do have a tangent space normal map, then it should look mostly blue when you open it in some viewer. If you see a rainbow of colors, then your normal map is in object space and not tangent space.

Also, comments in your vertex shader say that the light is defined in object space. Are you sure about this? In your application, did you transform the lights position by the inverse of the world matrix for this particular model and then set that value as the constant to the vertex shader? I ask because it's possible to have missed that part.

The part in your vertex shader that transforms the light vector into tangent space; try swapping the tangent and binormal. What I mean is try this:
// Transform the light vectordp3 r10.x, r2, r3 // tangentdp3 r10.y, v6, r3 // binormaldp3 r10.z, v1, r3 // normalmov r10.w, c30.wmov r3, r10

It is also possible that one (or both) of either tangent or binormal are pointing in the opposite direction than they're supposed to. Make sure that that is not the case.

Hope this helps,
neneboricua

Share on other sites
Oh, crap. I was indexing the vertices wrong in the triangle loop :P.

ie.

TriIndex[0] = pIndices[iTri];
TriIndex[1] = pIndices[iTri+1];
TriIndex[2] = pIndices[iTri+2];

should be

TriIndex[0] = pIndices[iTri*3];
TriIndex[1] = pIndices[(iTri*3)+1];
TriIndex[2] = pIndices[(iTri*3)+2];

I'm testing on a horizontal flat quad, with the normal map UV'ed to fit exactly. The tangent vector is being computed to be (0,0,1) on each vertex, which seems to be correct (all surface normals are (0,1,0)). So as far as i can tell, the tangent should be fine. But it still don't look right :(.

Regarding the light being in object space, yes I'm pretty sure thats correct, because this is in my static geometry rendering code and all static geometry is pretransformed in world space :). (ie. the world matrix is being set to identity for all static geometry).

And yeah, the normal map is in tangent space (i'm borrowing a map from the NVSDK -_^).

I'll try your other suggestions now.....

Thanks.

Share on other sites
Whee! I got it working :). The biggie was that i was accidentally blatting out all the constants defined in the shader by setting them in the app -_^. Then i just had to reverse the order of the cross product, 'cause it was getting lit from the wrong direction...

Now, I just need to implement per pixel attenuation and specular.

Should I do attenuation in a separate pass, or in the same shader? I'd prefer to do it all in one pass. Any info on doing attenuation in the shader at PS1.x level would be great. :)

Thanks.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account