user1796942
user1796942

Reputation: 3518

Why transform normals with the transpose of the inverse of the modelview matrix?

I am working on some shaders, and I need to transform normals.

I read in few tutorials the way you transform normals is you multiply them with the transpose of the inverse of the modelview matrix. But I can't find explanation of why is that so, and what is the logic behind that?

Upvotes: 69

Views: 49139

Answers (6)

rain-n-thunder
rain-n-thunder

Reputation: 1

TLDR Refer to the other answers for the original question. I'm just adding that if modelview is RST-transforms-only then you can skip the ^-1^T.

I'd just like to add that if you're certain your transformation is composed of only RST's (rotations, uniform scaling along all axes, and/or translations), then you can skip the invert-then-transpose because it's guaranteed the top-left 3-by-3 is isolated from the rest of the matrix and is itself a scalar multiple of a 3-by-3 orthogonal matrix, i.e. in pseudocode,

LET A = mat3(modelview);
EXISTS float c, mat3 Q.
  A == c * Q AND c != 0 AND Q == Q^-T

from which we can derive

EXISTS float c.
  A == c^2 * A^-T AND c != 0

and if you're gonna normalize the viewspace normal anyways then there's no point in getting the scale right, so passing the matrix along (trimmed to 3-by-3) will work just the same.

// when modelview is RST
mat3 A = mat3(modelview);
// otherwise
mat3 A = transpose(inverse(modelview));
// finally
vspace_normal = A * mspace_normal;

Upvotes: 0

wcochran
wcochran

Reputation: 10896

My favorite proof is below where N is the normal and V is a tangent vector. Since they are perpendicular their dot product is zero. M is any 3x3 invertible transformation (M-1 * M = I). N' and V' are the vectors transformed by M.

enter image description here

To get some intuition, consider the shear transformation below.

enter image description here

Note that this does not apply to tangent vectors.

Upvotes: 51

eric
eric

Reputation: 71

If the model matrix is made of translation, rotation and scale, you don't need to do inverse transpose to calculate normal matrix. Simply divide the normal by squared scale and multiply by model matrix and we are done. You can extend that to any matrix with perpendicular axes, just calculate squared scale for each axes of the matrix you are using instead.

I wrote the details in my blog: https://lxjk.github.io/2017/10/01/Stop-Using-Normal-Matrix.html

Upvotes: 4

Invalid
Invalid

Reputation: 1880

Take a look at this tutorial:

https://paroj.github.io/gltut/Illumination/Tut09%20Normal%20Transformation.html

You can imagine that when the surface of a sphere stretches (so the sphere is scaled along one axis or something similar) the normals of that surface will all 'bend' towards each other. It turns out you need to invert the scale applied to the normals to achieve this. This is the same as transforming with the Inverse Transpose Matrix. The link above shows how to derive the inverse transpose matrix from this.

Also note that when the scale is uniform, you can simply pass the original matrix as normal matrix. Imagine the same sphere being scaled uniformly along all axes, the surface will not stretch or bend, nor will the normals.

Upvotes: 42

Cyber176
Cyber176

Reputation: 1

Don't understand why you just don't zero out the 4th element of the direction vector before multiplying with the model matrix. No inverse or transpose needed. Think of the direction vector as the difference between two points. Move the two points with the rest of the model - they are still in the same relative position to the model. Take the difference between the two points to get the new direction, and the 4th element, cancels out to zero. Lot cheaper.

Upvotes: -3

Tommy
Tommy

Reputation: 100622

It flows from the definition of a normal.

Suppose you have the normal, N, and a vector, V, a tangent vector at the same position on the object as the normal. Then by definition N·V = 0.

Tangent vectors run in the same direction as the surface of an object. So if your surface is planar then the tangent is the difference between two identifiable points on the object. So if V = Q - R where Q and R are points on the surface then if you transform the object by B:

V' = BQ - BR
   = B(Q - R)
   = BV

The same logic applies for non-planar surfaces by considering limits.

In this case suppose you intend to transform the model by the matrix B. So B will be applied to the geometry. Then to figure out what to do to the normals you need to solve for the matrix, A so that:

(AN)·(BV) = 0

Turning that into a row versus column thing to eliminate the explicit dot product:

[tranpose(AN)](BV) = 0

Pull the transpose outside, eliminate the brackets:

transpose(N)*transpose(A)*B*V = 0

So that's "the transpose of the normal" [product with] "the transpose of the known transformation matrix" [product with] "the transformation we're solving for" [product with] "the vector on the surface of the model" = 0

But we started by stating that transpose(N)*V = 0, since that's the same as saying that N·V = 0. So to satisfy our constraints we need the middle part of the expression — transpose(A)*B — to go away.

Hence we can conclude that:

 transpose(A)*B = identity
 => transpose(A) = identity*inverse(B)
 => transpose(A) = inverse(B)
 => A = transpose(inverse(B))

Upvotes: 67

Related Questions