PDA

View Full Version : Calculate Tangent and BiNormal for Normal Mapping



technomage
18-02-2007, 10:02 PM
Hi people. this is not competition related. :)

I'm using Opengl to play with Normal mapping for the Alert figher project. I'm having problems calculating the Tanget and BinNormal vectors from the data have. Here is the code I came up with, can anyone see any problems (it's converted from C++, as always). Or even better has some code that works already :wink:.



function ROUNDOFF(d: Single): Single;
begin
//ROUNDOFF here is a macro that sets a value to 0.0f if the value is a very small
// value, such as > -0.001f and <0> -0.001) and (Result < 0.001) then Result := 0.0;
end;

procedure FindInvTBN(v0,v1,v2: TVector;t0,t1,t2:TTextureCoordinate;
var InvNormal: TVector; var InvBinormal: TVector; var InvTangent: TVector);

var v2v1, v3v1: TVector;
c2c1_T, c2c1_B: Single;
c3c1_T, c3c1_B: Single;
fDenominator, fScale1, fScale2: Single;
T,B,N: TVector;

begin
//Calculate the vectors from the current vertex
//to the two other vertices in the triangle
v2v1 := VectorSub(v0,v2);
v3v1 := VectorSub(v1,v0);
//Calculate the “direction” of the triangle based on texture coordinates.
c2c1_T := t0.u - t2.u;
c2c1_B := t0.v - t2.v;
c3c1_T := t1.u - t2.u;
c3c1_B := t1.v - t2.v;
//Look at the references for more explanation for this one.
fDenominator := c2c1_T * c3c1_B - c3c1_T * c2c1_B;
if ROUNDOFF(fDenominator) = 0.0 then
begin
//We won't risk a divide by zero, so set the tangent matrix to the
//identity matrix
InvTangent := NewVector(1.0, 0.0, 0.0);
InvBinormal := NewVector(0.0, 1.0, 0.0);
InvNormal := NewVector(0.0, 0.0, 1.0);
end
else
begin
fScale1 := 1.0 / fDenominator;
T := NewVector((c3c1_B * v2v1.x - c2c1_B * v3v1.x) * fscale1,
(c3c1_B * v2v1.y - c2c1_B * v3v1.y) * fScale1,
(c3c1_B * v2v1.z - c2c1_B * v3v1.z) * fScale1);
B := NewVector((-c3c1_T * v2v1.x + c2c1_T * v3v1.x) * fScale1,
(c3c1_T * v2v1.y + c2c1_T * v3v1.y) * fScale1,
(-c3c1_T * v2v1.z + c2c1_T * v3v1.z) * fScale1);
N := CrossProduct(T, B);
//This is where programmers should break up the function to smooth the tangent, binormal and
// normal values. */

//Look at “Derivation of the Tangent Space Matrix” for more information.
fScale2 := 1.0 / ((T.x * B.y * N.z - T.z * B.y * N.x) +
(B.x * N.y * T.z - B.z * N.y * T.x) +
(N.x * T.y * B.z - N.z * T.y * B.x));

InvTangent := NewVector(CrossProduct(B,N).x * fScale2,
CrossProduct((VectorScale(N, -1)),T).x * fScale2,
CrossProduct(T,B).x * fScale2 );
InvTangent := VectorNormalize(InvTangent);

InvBinormal := NewVector(CrossProduct(VectorScale(B, -1), N).y * fScale2,
CrossProduct(N,T).y * fScale2,
CrossProduct(VectorScale(T, -1), B).y * fScale2);
InvBinormal := VectorNormalize(InvBinormal);

InvNormal := NewVector(CrossProduct(B,N).z * fScale2,
CrossProduct(VectorScale(N, -1), T).z * fScale2,
CrossProduct(T,B).z * fScale2);
InvNormal := VectorNormalize(InvNormal);
end;

end;

JSoftware
19-02-2007, 07:39 AM
I use the translated code from Terathon (http://www.terathon.com/code/tangent.php)

I could post the translated code but I use my own vector lib which is using operator overloading

Edit: Ah wait.. for some reason the function has been erased from the lib... I can maybe find it later today

Edit2: Actually I found it again without operator overloaded vectors:
function calculateTangentSpaceVector(position1, position2, position3: tvertex): normals;
var side0, side1, normal, tangent, binormal, tangentCross: tvector4f;
deltaV0, deltaV1, deltaU0, deltaU1: real;
begin
side0 := sub(position2.pos, position1.pos);
side1 := sub(position3.pos, position1.pos);
normal := normalize(cross(side0, side1));

deltaV0 := position1.texcoord.y - position2.texcoord.y;
deltaV1 := position3.texcoord.y - position1.texcoord.y;
tangent := normalize(sub(multiply(side0, deltaV1), multiply(side1, deltaV0)));

deltaU0 := position1.texcoord.x - position2.texcoord.x;
deltaU1 := position3.texcoord.x - position1.texcoord.x;
binormal := normalize(sub(multiply(side0, deltau1), multiply(side1, deltau0)));
tangentCross := normalize(cross(tangent, binormal));
if (dot(tangentCross,normal) < 0.0) then
begin
tangent := sub(tangent,tangent);
binormal := sub(binormal,binormal);
end;
result.tangent := tangent;
result.binormal := binormal;
end;

technomage
20-02-2007, 11:02 PM
Thanks JSoftware. You code looks allot simpler than the stuff I have.

I got it all working anyway. Here are some sample screen shots. :D

http://www.infinitespace-online.net/images/screenshots/ViperMk21.png
http://www.infinitespace-online.net/images/screenshots/ViperMk22.png
http://www.infinitespace-online.net/images/screenshots/ViperMk23.png

http://www.infinitespace-online.net/images/screenshots/ViperMk71.png
http://www.infinitespace-online.net/images/screenshots/ViperMk72.png

the models are not mine (I wish they were) but they are some rather cool low poly ones from a Star Trek Bridge Commander mod. I did generate the normal maps however and convert them to my own format. :)

JSoftware
20-02-2007, 11:08 PM
I looks nice but isn't the light a bit of? It looks to me like it's only "local" light, like if you're calculating light in tangent space instead of transforming it to world or light space?

Edit: or maybe the light comes from the camera in all the shots?

technomage
20-02-2007, 11:19 PM
The light is a directional one coming from the direction of the camera. I still need to experiment a bit more, but this is allot better than it was, I had a blank screen for a few days :oops: