strange vertex program behaviour

I was trying to get my Tangent space bump mapping to work, and my TBN matrix just didn’t seem to be working… when I knew for a fact It was equating to an identity matrix.

It turned out using the R registers I had copied the TBN vectors into, wasn’t working when I tried to do a DP3.

for instance this code works

//Copy TBN vectors into R registers.
“MOV R4,v[4];”
“MOV R5,v[5];”
“MOV R6,v[6];”

//Get object space light position.
“MOV R9,c[30];”

//Convert object space light into tangent space.
“DP3 R3.x,R9,v[4];
DP3 R3.y,R9,v[5];
DP3 R3.z,R9,v[6];”

But this code does not!

//Copy TBN vectors into R registers.
“MOV R4,v[4];”
“MOV R5,v[5];”
“MOV R6,v[6];”

//Get object space light position.
“MOV R9,c[30];”

//Convert object space light into tangent space.
“DP3 R3.x,R9,R4;
DP3 R3.y,R9,R5;
DP3 R3.z,R9,R6;”

The only difference is using the R registers, that have the contents of v[4] - v[6].

Using actual v[4] - v[6] works, using the registers does not!

Can anyone explain this?

I am using a GF3, and 23.11 reference drivers.


P.S. I know the code efficieny sucks, I was just testing stuff at the mo…

No GL errors? Does it work with emulated vertex programs (e.g. GF2)?

  • Matt

There were no GL errors.

I’haven’t tried on GF2, I will try that out today when I have a spare moment.


I’ve had similar problems with 23.11 on GeForce3. The following piece of shadow mapping code does not work properly with these drivers,

// ----------------
// Transform position
// ----------------

“DP4 o[HPOS].x, c[0], v[OPOS];”
“DP4 o[HPOS].y, c[1], v[OPOS];”
“DP4 o[HPOS].z, c[2], v[OPOS];”
“DP4 o[HPOS].w, c[3], v[OPOS];”

// ----------------
// Project texture
// ----------------

“DP4 o[TEX0].x, c[8], v[OPOS];”
“DP4 o[TEX0].y, c[9], v[OPOS];”
“DP4 o[TEX0].z, c[10], v[OPOS];”
“DP4 o[TEX0].w, c[11], v[OPOS];”

I just checked it with 21.81 and it works with those.

[This message has been edited by PH (edited 01-21-2002).]

One more thing ( to make it even more strange ) - I have a similar vertex program that includes the above code ( this time for ordinary projected textures ), distance attenuation, tangent space calculations and more. The only thing that doesn’t work here are the tangent space calculations ( and that used to work too ). Thanks Nutty, now I know why .

I have a tangent space bump mapping demo on my site now.


Looks like 23.11 drivers are a bit dodgy then!
Whered you get them newer ones from? I can’t find them anywhere…

Nutty should have them ( I use 21.85 on Win2000 right now and they appear to work ).

I tried 22.80 but those have the same problems as 23.11.

Send me the app and I can try this myself…

  • Matt

Will do Matt.

Just in case anyones interested, this bug/problem did not occur on GF1 board using VP emulation, using the same drivers as the problem appears on GF3.

Matt I’ve already sent you a test app…


Nutty, just wanted to say thank you for that tangent space bump mapping demo. Your comments at the top of the source file have taught me more in 30 lines than any of those bullet-point pdf presentations NVidia has available. You put things simply.
I now, finally understand how to do embm properly. Only trouble is that for my dynamic mesh, I’ll have to calculate a binormal and tangent vector every frame on top of the normal I’m already calculating!
Keep up the good work.

One question:-
I’m new to vertex programs.
I’ve found that glEnable(GL_NORMALIZE); is far faster than normalising vectors in my app - for dynamic meshes only (obviously).
Now, I assume that when you enable a vertex program, you have to normalise the vectors in the vertex program yourself - but how do you do this?

I had the same problem. It seems to be a bug in the vertex program compiler. It is caused by accessing R3 only by components, not as a whole.

If you first initialize R3 with “MOV R3, whatever”, it works as expected.

Hope that helps in fixing, Matt :wink:

The new 27.10 drivers work as expected. I doubt it was the compiler as Nutty said it worked on GF1 using VP emulation.
The new drivers have some new interesting extensions btw, still no render_texture though. Some sort of occlusion query extension is there and a new VP extension ( version 1.1 ? ).


// Length squared of vector v in R1
“DP3 R0.w, R1, R1;”

// w = 1 / sqrt( x^2 + y^2 + z^2 ) = 1 / Length
“RSQ R0.w, R0.w;”

// v’ = ( 1 / sqrt( x^2 + y^2 + z^2 ) * v ) = normalize( v )
“MUL, R0.w, R1;”

EDIT: This will normalize a vector in R1 and store the result in R0 - you could just replace R1 with R0 in the above if you don’t need the unnormalized vector for anything.

[This message has been edited by PH (edited 01-23-2002).]

I have a question about environment bump mapping.
Nutty, I followed your instructions about calculating the tangent and binormal vectors for every surface. If I draw the normal, binormal and tangent as lines a vertex I should get 3 lines all perpendicular to each other, shouldn’t I? Defining a coordinate frame? This is what I’m getting anyway.

I’m not using a vertex program for now.
Instead, I construct a surface-space matrix myself from the normal/binormal/tangent, multiply this matrix by the inverse-transpose-modelview matrix, and pass this matrix to the texture shader spreading it across 4 dimensional texture coordinates for units 2,3,and 4 (like it says to do in the nvidia demo).
Now, I’m getting some kind of nice bumpmapping affect - but it isn’t correct. If I supply a normal map that consists of nothing but positive-Z normals, I don’t get a correct view of the cube map - I should be seeing the cube rotating around me as I rotate the object around me…instead I’m getting the cube map sort of distorting over the mesh.

Here’s my texture shader:-

Here’s the code I use to construct and pass the surface-matrix (I’m normalising the viewpoint->vertex vector, purely out of desperation - it shouldn’t be normalised should it?):-

surfacematrix.m(0,0) = *(tangent+X);
surfacematrix.m(0,1) = *(tangent+Y);
surfacematrix.m(0,2) = *(tangent+Z);

		surfacematrix.m(1,0) = *(binorm+X);
		surfacematrix.m(1,1) = *(binorm+Y);
		surfacematrix.m(1,2) = *(binorm+Z);

		surfacematrix.m(2,0) = *(norm+X);
		surfacematrix.m(2,1) = *(norm+Y);
		surfacematrix.m(2,2) = *(norm+Z);

		// multiply the surface matrix by the (inverse transpose)modelview matrix

		viewpos[0] = *(vert+X) - vpos[X];
		viewpos[1] = *(vert+Y) - vpos[Y];
		viewpos[2] = *(vert+Z) - vpos[Z];


		// load the surface matrix and eye-to-vertex vector into
		// texture unit 1-3's texture coordinates
		*(tex2+0) = surfacematrix.m(0,0);	// tangent_x
		*(tex2+1) = surfacematrix.m(0,1);	// binormal_x
		*(tex2+2) = surfacematrix.m(0,2);	// normal_x
		*(tex2+3) = viewpos[0];

		*(tex3+0) = surfacematrix.m(1,0);	// tangent_y
		*(tex3+1) = surfacematrix.m(1,1);	// binormal_y
		*(tex3+2) = surfacematrix.m(1,2);	// normal_y
		*(tex3+3) = viewpos[1];

		*(tex4+0) = surfacematrix.m(2,0);	// tangent_z
		*(tex4+1) = surfacematrix.m(2,1);	// binormal_z
		*(tex4+2) = surfacematrix.m(2,2);	// normal_z
		*(tex4+3) = viewpos[2];

Can anyone shed any light on what I’m doing wrong?

Thanks for the nice words Knackered.

To normalize a vector in a vertex program, you find the square of the magnitude like this.

sq_mag = (v.xv.x) + (v.yv.y) + (v.z*v.z)

This is done with a DP3 (Dot product) command.

You then do a reciprocal square root.
which gives 1 / sqrt(sq_mag);

Then multiply the original vector by this value.
It takes 3 instructions to do.

I’m quite new to TBN and the whole tangent space malarky myself, so forgive me if I make some errors.

To make sure the surfaces are all perpendicular, you would have to use facet normals I believe. Averaged normals between two polygons, would not result in 90 degrees between it, and the tangent. If I’m wrong on this, someone tell me.

I’ve never looked at texture shaders yet, so I hope someone else can help you on that part.