Jump to content


Transforming normals propertly


23 replies to this topic

#1 Mihail121

    Senior Member

  • Members
  • PipPipPipPip
  • 1052 posts

Posted 23 September 2005 - 08: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!

#2 Reedbeta

    DevMaster Staff

  • Administrators
  • 4979 posts
  • LocationBellevue, WA

Posted 23 September 2005 - 10: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.
reedbeta.com - developer blog, OpenGL demos, and other projects

#3 Mihail121

    Senior Member

  • Members
  • PipPipPipPip
  • 1052 posts

Posted 24 September 2005 - 07: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.

#4 Reedbeta

    DevMaster Staff

  • Administrators
  • 4979 posts
  • LocationBellevue, WA

Posted 24 September 2005 - 09: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.
reedbeta.com - developer blog, OpenGL demos, and other projects

#5 geon

    Senior Member

  • Members
  • PipPipPipPip
  • 893 posts

Posted 25 September 2005 - 08: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.

#6 Reedbeta

    DevMaster Staff

  • Administrators
  • 4979 posts
  • LocationBellevue, WA

Posted 26 September 2005 - 01:36 AM

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?
reedbeta.com - developer blog, OpenGL demos, and other projects

#7 Mihail121

    Senior Member

  • Members
  • PipPipPipPip
  • 1052 posts

Posted 26 September 2005 - 07:03 AM

Exactly! :)

#8 Alex

    Valued Member

  • Members
  • PipPipPip
  • 152 posts

Posted 26 September 2005 - 08: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

#9 SigKILL

    Valued Member

  • Members
  • PipPipPip
  • 200 posts

Posted 26 September 2005 - 09: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...

#10 Alex

    Valued Member

  • Members
  • PipPipPip
  • 152 posts

Posted 26 September 2005 - 10:39 AM

A good reason not to use non uniform scales :)

#11 Reedbeta

    DevMaster Staff

  • Administrators
  • 4979 posts
  • LocationBellevue, WA

Posted 26 September 2005 - 04:17 PM

SigKILL said:

- 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.
reedbeta.com - developer blog, OpenGL demos, and other projects

#12 kusma

    Valued Member

  • Members
  • PipPipPip
  • 163 posts

Posted 27 September 2005 - 12:14 AM

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.

#13 XORcist

    Member

  • Members
  • PipPip
  • 39 posts

Posted 27 September 2005 - 08: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...acroix_01.shtml

#14 Nyad

    Member

  • Members
  • PipPip
  • 44 posts

Posted 17 March 2008 - 07: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

#15 JarkkoL

    Senior Member

  • Members
  • PipPipPipPip
  • 467 posts

Posted 17 March 2008 - 07: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.

#16 Goz

    Senior Member

  • Members
  • PipPipPipPip
  • 574 posts

Posted 17 March 2008 - 09:45 AM

XORcist said:

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...acroix_01.shtml

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

#17 Reedbeta

    DevMaster Staff

  • Administrators
  • 4979 posts
  • LocationBellevue, WA

Posted 17 March 2008 - 09:48 AM

warning: old thread.
reedbeta.com - developer blog, OpenGL demos, and other projects

#18 Goz

    Senior Member

  • Members
  • PipPipPipPip
  • 574 posts

Posted 17 March 2008 - 03:51 PM

:whistle:

#19 Nyad

    Member

  • Members
  • PipPip
  • 44 posts

Posted 19 March 2008 - 10:38 AM

JarkkoL said:

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?

#20 Wernaeh

    Senior Member

  • Members
  • PipPipPipPip
  • 368 posts

Posted 19 March 2008 - 11:16 AM

Quote

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
Some call me mathematician, some just call me computer guy. Yet, I prefer the term professional weirdo :)





1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users