PDA

View Full Version : Transforming normals propertly


Mihail121
09-23-2005, 01:56 PM
Hi, I'm currently coding the new tutorial system but iIm blocked with the following question: what is the correct way to transform surface normals?

I'm doing lighting in world space so I need the world transformation matrix of an object do transform the normals. I also know that I have to take the transpose of the inverse world matrix and multiply the normals by it. After that i have to renormalize them. But the question is: is there anything i'm forgetting?

Thank you!

Reedbeta
09-23-2005, 03:44 PM
You've got it right. I would just point out that if the transformation is only translations and rotations (no scales), then neither the inverse transpose nor the renormalization is necessary. In this case the inverse transpose equals the original matrix. This is a good way to speed things up since inverses are kinda costly, and the transformations most people use are just rotations and translations.

Mihail121
09-24-2005, 12:53 AM
Yeah, but the problem is, that the world matrix used to place the object in the gaming world CAN include scaling too so I definetely need to renormalize them. To speed the inverse operation a little bit, instead of using 4X4 matrix, I get the linear 3X3 partion of the world matrix, inverse and transpose that and use it for calculations.

Reedbeta
09-24-2005, 02:40 PM
Yeah, definitely do that. I guess when composing your world matrix you could keep a flag that indicates if there are any scalings involved. But that adds more complexity, so it's up to you.

geon
09-25-2005, 01:18 PM
In ogl you can skip renormalizing normals. Just set glEnable() with GL_NORMALIZE.

And you can supply the transformation matrix per-object anyway, so I had no need to transform my normals at all.

Reedbeta
09-25-2005, 06:36 PM
That just makes OpenGL renormalize them itself :-)

OpenGL transforms normals by the inverse transpose automatically, so it's true that when you specify a modelview matrix, you don't need to worry about any of this yourself. However, I think Mihail's article is intended to explain what is going on "under the hood" with regard to transformations. Correct?

Mihail121
09-26-2005, 12:03 AM
Exactly! :)

Alex
09-26-2005, 01:04 AM
Just curious...
why don't you light in model space? Do you use more lights per object than your object has normals? Also keeping an unscaled version of the modelview matrix along with a possibly scaled one would save you inverting and renormalization completely.

Alex

SigKILL
09-26-2005, 02:15 AM
- In this case the inverse transpose equals the original matrix ..

No, it isn't equal. However, you shouldn't translate your normals so in practice there is no difference.

-Also keeping an unscaled version of the modelview matrix along with a possibly scaled one would save you inverting and renormalization completely.

This would break if the scale was non-uniform...

Alex
09-26-2005, 03:39 AM
A good reason not to use non uniform scales :)

Reedbeta
09-26-2005, 09:17 AM
- In this case the inverse transpose equals the original matrix ..

No, it isn't equal. However, you shouldn't translate your normals so in practice there is no difference.

I should have made it clear that the inverse transpose part applies only to the upper-left 3x3 submatrix, in the case that we are using 4x4 matrices on real projective space for our transformations. In this case, the upper 3x3 submatrix contains only rotations, so it is orthogonal and its inverse equals its transpose; hence the inverse transpose equals the original matrix.

kusma
09-26-2005, 05:14 PM
it also contains scale (as mentioned before), but that is easily fixed by calculating the scale-factor (the reciprocal square root of m31^2 + m32^2 + m33^2)) and multiplying modelview with that scale. as long as the scaling is uniform, that is.

XORcist
09-27-2005, 01:28 AM
Check out this link. But, I recommend this only on decent D3D9 cards ( 9800 & above, GF6800, etc )

A Unified Lighting Technique for a New Generation of Games
URL: http://www.gamasutra.com/features/20050729/lacroix_01.shtml

Nyad
03-17-2008, 12:01 AM
say I only want to transform my normals for collision detection purposes.
would I just multiply them by the modelview matrix like I would for an ordinary vertex? or are there other opperations?
This is for plane-plane intersections

JarkkoL
03-17-2008, 12:38 AM
I don't think you want to transform your normals to the view space but to the world space, right? Anyway, correct way to transform normals is by using inverse transpose of the matrix used to transform the vertices. E.g. if you transform vertices from object to world space with matrix M, then you transform normals from object to world space by using transpose(M^-1). Note that if M is orthonormal, transpose(M^-1) = M. Also note that W component of vertices is 1, while for normals (or any vectors) it's 0.

Goz
03-17-2008, 02:45 AM
Check out this link. But, I recommend this only on decent D3D9 cards ( 9800 & above, GF6800, etc )

A Unified Lighting Technique for a New Generation of Games
URL: http://www.gamasutra.com/features/20050729/lacroix_01.shtml

Errr ... ok ... sounds like the way everyone i've ever come across handles per-pixel lighting ...

Reedbeta
03-17-2008, 02:48 AM
warning: old thread.

Goz
03-17-2008, 08:51 AM
:whistle:

Nyad
03-19-2008, 03:38 AM
Note that if M is orthonormal, transpose(M^-1) = M. Also note that W component of vertices is 1, while for normals (or any vectors) it's 0.

So to keep my modelview matrix orthonormal I must only apply scaling and rotations to it and no translations?

Wernaeh
03-19-2008, 04:16 AM
So to keep my modelview matrix orthonormal I must only apply scaling and rotations to it and no translations?


Nope.

You must not apply any scaling (except for 1 and -1, that is).

This is best proven by an example:

Consider an unit matrix with ones as diagonal elements, and zeroes everywhere else. This matrix obviously is orthonormal (each row or column vector has a magnitude of one, and each two different row or column vectors are orthogonal to each other).

Now, scaling involves multiplying this matrix with a scaling matrix, with scale factors s_1 to s_n on the diagonal and zeroes everywhere else. The new matrix U * S is the scale matrix itself. Since you at least assume some kind of scaling, one of the s_i is not 1 or -1, thus there now is a row vector that is not normalized anymore (i.e. has a length of s_i), thus the result is not orthonormal.

Translations may be applied to your orthonormal matrix without affecting normal transformations.

Be a little bit careful here: Generally, a 3x3 matrix fully specifies a coordinate frame (aka rotation) in 3D. However, we use 4x4 matrices so that we can handle transformations in a uniform way (i.e. with the same multiplication also used for rotations). For this to work out correctly, we need to augment each 3D vector with a fourth coordinate ( we can't multiply a 4x4 matrix with a 3-element vector). The way we choose the fourth coordinate influences how the vector is transformed by the matrix.

A fourth coordinate of 0 essentially (work this out on paper) removes the influence of the added line and row in our 4x4 transformation matrix, the result is the same as if we multiplied a 3-element vector with the 3x3 rotational part only. As a consequence, we use a fourth coordinate of 0 when we wish to transform directional vectors (i.e. normals, for example-> these represent a direction, rather than a point in space)

A fourth component of 1 includes all translations, and is used for actual points within space (i.e. vertices of a model).

Hope this lights things up a bit,

Cheers,
- Wernaeh

Nyad
03-19-2008, 06:56 AM
so I must augment my modelview matrix by removing the bottom row and the rightmost column?
and I multiply by the normal, but I must not do any scaling, all other operations are ok, but it seems translation is going to be wiped away when I remove the rightmost column.
So I only apply rotations?

Reedbeta
03-19-2008, 08:58 AM
When you transform positions, you put the translation part in. When you transform normals, you leave it out. However, you don't need to modify the matrix. You can make this happen "automagically" by representing positions as 4-vectors with a final component of 1, and normals as 4-vectors with a final component of 0. Then normals will automatically not be subject to translations.

And removing the bottom row doesn't affect anything because that row should always be [0, 0, 0, 1] for affine transforms. You can save some memory by not storing it, if you like.

If you want to keep the 3x3 part of the modelview matrix orthonormal (which means that you can use the same matrix for normals and positions) then you must use only rotations and reflections (and translations), but not scalings. However if you are willing to compute the inverse transpose and use that for normals, then you may use any transformation you like, scalings included.

Nyad
03-19-2008, 09:05 AM
Thanks that cleared it all up.

rouncer
03-20-2008, 06:33 PM
Wow, you taught me something. I always had 2 matrices, one with the scale
and translation and one just for rotation.

So your actually allowed translations just not scales. amazing.