6
votes

I just want to be sure I understand TBN matrix calculation correctly

In vertex shader we usually use:

vec3 n = normalize(gl_NormalMatrix * gl_Normal);
vec3 t = normalize(gl_NormalMatrix * Tangent.xyz);
vec3 b = normalize(gl_NormalMatrix * Bitangent.xyz);
mat3 tbn = mat3(t, b, n);

As I understand this tbn matrix transforms a vector from Tangent space to Eye space. Actually we want the reverse - transform a vector from eye space to Tangent space. Thus we need to invert the tbn matrix:

tbn = transpose(tbn); // transpose should be OK here for doing matrix inversion

note: tbn - should contain only rotations, for such case we can use transpose to inverse the matrix.

And we can transform our vectors:

vec3 lightT  = tbn * light_vector;
...          = tbn * ...

In several tutorials, source codes I've found that authors used something like this:

light.x = dot(light, t);
light.y = dot(light, b);
light.z = dot(light, n);

The above code does the same as multiplying by the transposed(tbn) matrix.

The question:

Should we use transposed tbn matrix just as I explained above? Or maybe I am missing something?

Note by that solution we have vectors (light_vector) transformed into TBN in the vertex shader, then in fragment shader we only have to get normal from the normal map. Other option is to create TBN matrix that transform from TBN space into eye space and then in the fragment shader transform each read normal from the normal map.

1
Define "correct"? You took the cross-product of your normal and tangent vectors to get your bitangent vector. This is, generally speaking, not correct for most surfaces. It may be correct for yours, but most texture mapping don't use perfectly orthogonal mappings to the surface.Nicol Bolas
Right, I've updated the question. I've changed bitangent calculation as well.fen

1 Answers

0
votes

transpose is not inversion of matrix !!!

my TBN matrixes in vertex shader look like this:

uniform mat4x4 tm_l2g_dir;
layout(location=3) in vec3 tan;
layout(location=4) in vec3 bin;
layout(location=5) in vec3 nor;
out smooth mat3 pixel_TBN;

void main()
{
    vec4 p;
    //...
    p.xyz=tan.xyz; p.w=1.0; pixel_TBN[0]=normalize((tm_l2g_dir*p).xyz);
    p.xyz=bin.xyz; p.w=1.0; pixel_TBN[1]=normalize((tm_l2g_dir*p).xyz);
    p.xyz=nor.xyz; p.w=1.0; pixel_TBN[2]=normalize((tm_l2g_dir*p).xyz);
    //...
}

where:

  • tm_l2g_dir is transformation matrix from local model space to global scene space whitout any movement (changes only direction) for your code it is your Normal matrix
  • tan,bin,nor are vectors for TBN matrix (as part of my models)

nor - is normal vector to surface from actual Vertex position (can calculate as vector multiplication of two vertices from this vertex)

tan,bin are perpendicular vectors usually parallel to texture mapping axises or teselation of your model. If you choose wrong tan/bin vectors than sometimes some lighting artifacts can occur. for example if you have cylinder than bin is its rotation axis and tan is perpendicular to it along the circle (tangent)

tan,bin,nor should be perpendicular to each other

you can calculate TBN also automatic but that will cause some artifacts. to do it you simply chose as tan vector one vertice used for normal computation and bin=nor x bin

in fragment shader i use pixel_TBN with normal map to compute the real normal for fragment

//------------------------------------------------------------------
#version 420 core
//------------------------------------------------------------------
in smooth vec2 pixel_txr;
in smooth mat3 pixel_TBN;
uniform sampler2D   txr_normal;
out layout(location=0) vec4 frag_col;
const vec4 v05=vec4(0.5,0.5,0.5,0.5);
//------------------------------------------------------------------
void main(void)
    {
    vec4 col;
    vec3 normal;
    col=(texture2D(txr_normal,pixel_txr.st)-v05)*2.0;       // normal/bump maping
    normal=pixel_TBN*col.xyz;
    // col=... other stuff to compute col

    frag_col=col;
    }
//------------------------------------------------------------------