I am tired of misleading and insufficient articles making me more confused each time I read, I need a clarification that will solve my TBN matrix problem forever.

Each article I read informs me differently about from which space TBN matrix converts to tangent space. One article says that it converts from eye space, other one says that it converts from world space to tangent space.

As far as I know, world space is ModelMatrix * vertex, eye space is ViewMatrix * ModelMatrix * vertex. Another article says that transpose of TBN Matrix actually does it.

Can you please explain me how to use TBN matrix, does it (or transposed tbn) convert from eye or world to tangent ? Should I convert my lighting vectors from eye space to world and then apply TBN on them ?

  • \$\begingroup\$ there is no tangent space or world space or eye space. In final, you always multiply by only one matrix, which can be product of 3, as well as 30000 matrices, since matrix multiplication is associative. You don't have to give each matrix the name. You should know some basic linear algebra and low-level shader programming, then you will understand everything. \$\endgroup\$ Jun 15, 2013 at 16:22
  • 1
    \$\begingroup\$ @IvanKuckir:... That doesn't even begin to make sense. Shaders can do whatever they want. And you do multiply by multiple matrices, since you can't do lighting in post-projection clip-space. \$\endgroup\$ Jun 15, 2013 at 16:35
  • \$\begingroup\$ @deniz you are welcome :) I just want you to close the browser and open some Math book :) \$\endgroup\$ Jun 15, 2013 at 19:26

1 Answer 1


Since you didn't provide links to these tutorials that confused you, I am going to assume that they weren't written by idiots and that the code in them is correct (where possible).

They're all correct (well, except transpose guy; I'll get to him). Your problem (likely stemming from the writers not being clear) is that you're not seeing the other differences in the examples.

A matrix converts vectors from space A to space B. The columns of this vector are the basis vectors of space A, expressed in the space of B. It is what space A looks like to objects in space B.

The TBN matrix is composed from 3 normals which are all in some space. That's your space A, the source space for the transform. The destination is of course tangent space.

Therefore, if you construct a matrix from these 3 normals, using them as the columns of the matrix, you will have created a matrix that goes from whatever space those normals were in to tangent space. What space those normals are in is entirely up to the code in question. I'm sure some example code transforms the normals to camera space before putting them in the matrix. I'm sure other example code transforms them to world space.

Again, it's all about what the code is doing; conceptually, it all does the same thing. This (note: it is likely that the transform of the normals is in the vertex shader, while the multiplication is in the fragment shader):

mat3 worldToTangent_tbn = mat3(
  normalModelToWorld * tangent,
  normalModelToWorld * bitangent,
  normalModelToWorld * normal)
vec3 tangent_space_light_direction = worldToTangent_tbn * world_space_light_direction;
//do lighting in tangent space.

Is fundamentally no different from this:

mat3 cameraToTangent_tbn = mat3(
  normalModelToCamera * tangent,
  normalModelToCamera * bitangent,
  normalModelToCamera * normal)
vec3 tangent_space_light_direction = cameraToTangent_tbn * camera_space_light_direction;
//do lighting in tangent space.

They both get the light direction into tangent space. You can even do things directly from model space (which is what I suggest):

mat3 modelToTangent_tbn = mat3(
vec3 tangent_space_light_direction = modelToTangent_tbn * normalCameraToModel * camera_space_light_direction;
//do lighting in tangent space.

That requires transforming the camera-space light direction into model space, so you need a matrix to do that. Or you could do that transformation on the CPU and save time, so you'd be passing the model-space light position/direction.

So there isn't just one possible TBN matrix; there are many forms. It's all about what space you want to transform from and to.

And this is just my personal opinion, but if you see a tutorial that suggests you do lighting in world space, stop reading it. Read something written by someone who's not an idiot.

As for transpose guy, he is simply wrong. The transpose of a pure rotation matrix is its inverse, so what he's doing is take a matrix that transforms from normal space (the space the normals are in, which as shown above is dealer's choice) to tangent space and doing it backwards. The goal of that is to transform the normal fetched from the tangent space bump map into the space of the normals, so that you can do your lighting computations in a space other than tangent space.

His erronous assumption is that the TBN matrix is a rotation matrix; it isn't. TBN matrices will almost never be orthonormal; even if you compute the bitangent by cross-product, the normal and the tangent do not have to perpendicular, and they often will not be. Indeed, they should only be perpendicular if the surface is flat. The tangent and bitangent point along the flow of the surface in the S and T components of the UV. That's almost never in the plane of the normal.

  • \$\begingroup\$ ahh so if i calculate TBN matrix using gl_NormalMatrix, it will convert eye spaces to tangent space since NormalMatrix is in eye space. Or If I use another space matrix, it will convert that space to tangent. I think that transpose guy was trying to calculate everything in fragment shader instead of vertex, which is more expensive and I see no reason. \$\endgroup\$
    – deniz
    Jun 15, 2013 at 16:49
  • 1
    \$\begingroup\$ "Therefore, if you construct a matrix from these 3 normals, using them as the columns of the matrix, you will have created a matrix that goes from whatever space those normals were in to tangent space." I think you've got it backward - that matrix goes from tangent space to the source space. Consider if you multiply that matrix by (1, 0, 0) - you get the original tangent vector, in the source space. Multiplying by (0, 1, 0) gives you the bitangent and (0, 0, 1) the normal. So it's transforming from tangent space to the original space the vectors were in. \$\endgroup\$ Jun 15, 2013 at 19:04
  • \$\begingroup\$ "if you see a tutorial that suggests you do lighting in world space, stop reading it. Read something written by someone who's not an idiot." A little more nuance, please. Pure world space can give you precision problems when far from the origin, but camera-centered world space is fine and quite useful, e.g. for sampling cubemaps or SH lighting that are defined relative to world space. \$\endgroup\$ Jun 15, 2013 at 19:08
  • \$\begingroup\$ @NathanReed: "camera-centered world space" is functionally no different from "camera space". \$\endgroup\$ Jun 15, 2013 at 19:27
  • \$\begingroup\$ @NicolBolas Camera space as I understand the term is oriented with the camera, which makes it more difficult to sample world-aligned cubemaps, as you need an extra transformation step. \$\endgroup\$ Jun 15, 2013 at 19:31

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .