🔒 Locked
Matrix Math
Advertisement
Original Post
I've decided to work for the community. Seeing the high amount of matrix- and transform-related questions lately in this forum I've taken up my quill pen (well, notepad, actually) to try and explain some fundamentals about matrix math and easy ways to use this extremely powerful tool. These are the methods I use to keep my life (and code) simple. These solutions use no trigonometry or weird matrix-component tricks. They use only Matrix Inverse, Matrix Multiplication, Rotation(around vector) Matrix, Translation Matrix and Scaling Matrix functions, that should hopefully be available to you in your API (they exist in Direct3D and GL as well).
A transform is any modification of the position, orientation, or size of an object. This includes translations, rotations, scaling, and all combinations thereof.
It all starts with
A Matrix == A Transform
M*X == T(X)
Simple as that! Now, everything we can do with transforms, we can do with matrices.
Matrix multiplication, inverse
M*X : When you apply a matrix with a vector, you apply the equivalent transformation to that vector.
M*N : When you multiply two matrices together, you compose their transformations. To apply the matrix (M*N) is to apply the matrix N, then the matrix M.
M^(-1) : The inverse of a matrix is also the inverse of a transform. If you apply a transform, and then its inverse, you will be back to the initial situation.
How do I get the up/right/direction/position vector of an object from its matrix?
The "initial" up vector of an object is (0,1,0,0).
The "initial" right vector of an object is (1,0,0,0).
The "initial" direction vector of an object is (0,0,1,0).
The "initial" position vector of an object is (0,0,0,1).
The above are 4d (x,y,z,w) vectors. APIs provide functions to convert vectors between 3d and 4d format .
When you multiply an object's vertices by a matrix, you apply this transform to all its vectors, including the up/right/direction vector. So if M is the matrix of an object, M * (0,1,0) is its new up vector, M * (1,0,0) is its new right vector, M * (0,0,1) is its new direction vector, and M*(0,0,0) is its new position vector.
How should I store an object's state?
The fast answer is : store its matrix, because a matrix contains all the necessary information : scale, orientation and position.
A better answer is to store three matrices : S for scaling, T for translation, R for rotation. The real transform matrix of an object is : T*R*S (apply the scaling, then the rotation, then the translation), and can as such be computed easily from the three storage matrices.
When you want to translate an object, multiply T by the applied translation matrix T' : T <- T'*T
When you want to rotate an object, multiply R by the applied rotation matrix R' : R <- R'*R
When you want to scale an object, multiply S by the applied scaling matrix S' : S <- S'*S
When I have the camera's position, how do I get the view matrix?
You have a matrix M, which defines the camera's position. This means M takes the camera from (0,0,0), moves it and reorients it to its new position.
The view matrix V is meant to take the camera from its current position, and move it to (0,0,0) while reorienting it back.
Which means V is the inverse of M.
View = Camera^(-1)
How do I use sin/cos/LookAt to move the camera?
Don't. You're hopefully keeping a whole load of information (T, R and S) about the camera, and can do whatever you want with it without ever calling a single trigonometric or LookAt function. What do you want to do (to your camera) today?
Turn it left/right? Extract its "up" vector from its R matrix, create a matrix R' to rotate around this axis by the needed angle, and apply the steps seen two paragraphs above.
Turn it up/down? Extract its "right" vector from its R matrix and repeat.
Roll it around? Use the "direction" vector this time.
Move it forward/backwards/sideways/vertically? Extract the correct vector from the R matrix, create a translation by the vector (multiplied by the wanted distance, which can of course be negative) and apply it to T.
Teleport it somewhere? just erase T and set it to the new translation matrix.
Then, once you have your camera matrix by multiplying T*R*S, inverse it and voilà your view matrix.
How do I use sin/cos/LookAt to move an object?
See above, minus the "inverse the matrix" part.
How do I attach an object to another?
Say, you want object B to be attached to object A. What you first do is keep A at (0,0,0) and move B around it to its rightful relative position using a first matrix M. Then, you move A to its rightful place using a matrix N. But since B is attached to A, it must move as well, so you must apply N to B as well.
This means the "real" matrix of B will be N*M (you aplpied M then N). Now, if A was attached to an object Z using a matrix L, A's matrix would be L*N instead of N, so B's matrix would be L*N*M.
To attach an object B to an object A, store its relative position using the standard (T,R,S) method, then, remember its "true" matrix is equal to
("true" matrix of B)=("true" matrix of A)*T*R*S
Which means you have to compute the true matrix of A beforehand. You can make this easier by creating a "T,R,S" class that can be "attached" to another class of its type, and has a function that returns its true matrix on demand. This would give you the graph :
0 -(Ta,Ra,Sa)-> A -(Tb,Rb,Sb)-> B
How do I do a third person camera?
Attach the camera to the object. The (T,R,S) of the camera should be set so that it is "behind" the object. Remember the object is initially at (0,0,0) facing (0,0,1)! Then, each frame, get the "true" matrix of the camera using the paragraph above. The graph is :
Object -(Tcam, Rcam, Scam)-> Camera
To get the view matrix, inverse the camera matrix.
Now, you don't have to change the camera (T,R,S) anymore (unless you want to), only the position of the object, and when you update the view matrix, the camera will follow around (and rotate as well).
How do I make the Camera orbit around an object?
Well, we've seen above that if you attach a camera to an object, the camera will move around this object. What we want to do is have the camera rotate around an object when the object does NOT rotate. A good way to do this is to add another object, that is invisible, and is attached to the initial object, and then attach the camera to the new object. Like this :
Object -(0, Rorbit, Sorbit)-> NewObject -(Tcam, Rcam, Scam)-> Camera
Rorbit and Sorbit are the rotation and scaling that allow you to orbit with the camera, and bring it closer/further to the object. Tcam, Rcam and Scam are the same as above.
How do I "shake" the screen when the player is hit?
You can create an imaginary object "Shake" and attach the camera to it, like this :
Object or 0 -(Tshake,Rshake,0)-> Shaker -(0,Rcam,Scam)-> Camera
Remember, the position of the Shaker/Camera object is Tshake. Its rotation is Rcam. So to move the camera, use Rcam vectors applied to Tshake, and to rotate the camera apply Rcam vectors on Rcam itself.
To shake, change Rshake to a random rotation matrix every frame until the shake ends.
What would the sun, earth, and moon look like using your RST system?
0 -(Tsun,0,0)-> SunCenter -(0,Rsun,Ssun)-> SunObject
which is rendered with Tsun*Rsun*Ssun
0 -(Tsun,0,0)-> SunCenter -(0,Reorbit,0)-> EarthOrbit -(Tearthc,0,0)-> EarthCenter -(0,Rearth,Searth)-> EarthObject
which is rendered with Tsun*Reorbit*Tearthc*Rearth*Searth
0 -(Tsun,0,0)-> SunCenter -(0,Reorbit,0)-> EarthOrbit -(Tearthc,0,0)-> EarthCenter -(0,Rmorbit,0)-> MoonOrbit -(Tmoon,Rmoon,Smoon)-> MoonObject
which is rendered with Tsun*Reorbit*Tearthc*Rmorbit*Tmoon*Rmoon*Smoon
THE END
I hope somebody'll find this useful, and please post any comments you see fit.
[edited by - ToohrVyk on January 14, 2004 3:44:58 AM]
Can you include some tips on projective shadow calculations?
Projecting a 3D model''s vertices onto a plane?
Thanx for your hard work, lets evolve this with more n00b questions and doubts :D
Projecting a 3D model''s vertices onto a plane?
Thanx for your hard work, lets evolve this with more n00b questions and doubts :D
[Hugo Ferreira][Positronic Dreams][Colibri 3D Engine][Entropy HL2 MOD][Yann L.][Enginuity]
The most irrefutable evidence that there is intelligent life in the Universe is that they haven''t contacted us!
I don''t know much about projective shadow calculations. However, you have functions that create matrices to project triangles on planes... You want to apply these matrices to fully transformed objects, so I guess all you have to do is :
- compute the plane equation
- plug the equation in the matrix creating function
- compute the matrix of the object
- apply the object matrix, then the shadow matrix...
or : Object -(shadow projection)-> Shadow
But I''m not sure about this...
- compute the plane equation
- plug the equation in the matrix creating function
- compute the matrix of the object
- apply the object matrix, then the shadow matrix...
or : Object -(shadow projection)-> Shadow
But I''m not sure about this...
Very nice. Why not turn this into a little article and submit it to the staff so that people who don''t visit this forum can benefit from it as well?
The Artist Formerly Known as CmndrM
http://chaos.webhop.org
The Artist Formerly Known as CmndrM
http://chaos.webhop.org
Good job, I think it''s a good article, you obviously know what you are doing.
Since this is in FAQ format, why not put it in the FAQ?
You have to remember that you''re unique, just like everybody else.
You have to remember that you''re unique, just like everybody else.
Sorry if it seems like I'm being pedantic...
This isn't entirely accurate. You should go into the differences between row and column major matrices. Also, the position vector part will only work if you are working with homogenous matrices, in which case your vectors need to be 4 dimensional, not 3, otherwise the position will always come out as 0 ( non-homogenous 3x3 transformation matrices cannot hold translational information ).
A matrix doesn't equal a transformation. Matrices can be used to perform transformations. Firstly, you should should define that:
M*X = T(X)
Where M is a matrix, X is a vector, and T(X) is a transformation function that operates on the vector X to produce another vector ( X' for instance ).
That should eliminate any ambiguity.
This *highly* depends on what you're doing. You could for instance store one matrix, which is the current frame for the object. Other parts of your FAQ come back to this, at least propose other systems that can work just aswell ( and even work better in some circumstances ).
You have to remember that you're unique, just like everybody else.
[edited by - python_regious on January 10, 2004 2:26:12 PM]
[edited by - python_regious on January 10, 2004 2:26:40 PM]
quote:
How do I get the up/right/direction/position vector of an object from its matrix?
The "initial" up vector of an object is (0,1,0).
The "initial" right vector of an object is (1,0,0).
The "initial" direction vector of an object is (0,0,1).
The "initial" position vector of an object is (0,0,0).
When you multiply an object's vertices by a matrix, you apply this transform to all its vectors, including the up/right/direction vector. So if M is the matrix of an object, M * (0,1,0) is its new up vector, M * (1,0,0) is its new right vector, M * (0,0,1) is its new direction vector, and M*(0,0,0) is its new position vector.
This isn't entirely accurate. You should go into the differences between row and column major matrices. Also, the position vector part will only work if you are working with homogenous matrices, in which case your vectors need to be 4 dimensional, not 3, otherwise the position will always come out as 0 ( non-homogenous 3x3 transformation matrices cannot hold translational information ).
quote:
It all starts with
A Matrix == A Transform
M*X == T(X)
Simple as that! Now, everything we can do with transforms, we can do with matrices.
A matrix doesn't equal a transformation. Matrices can be used to perform transformations. Firstly, you should should define that:
M*X = T(X)
Where M is a matrix, X is a vector, and T(X) is a transformation function that operates on the vector X to produce another vector ( X' for instance ).
That should eliminate any ambiguity.
quote:
How should I store an object's state?
The fast answer is : store its matrix, because a matrix contains all the necessary information : scale, orientation and position.
A better answer is to store three matrices : S for scaling, T for translation, R for rotation. The real transform matrix of an object is : T*R*S (apply the scaling, then the rotation, then the translation), and can as such be computed easily from the three storage matrices.
When you want to translate an object, multiply T by the applied translation matrix T' : T <- T'*T
When you want to rotate an object, multiply R by the applied rotation matrix R' : R <- R'*R
When you want to scale an object, multiply S by the applied scaling matrix S' : S <- S'*S
This *highly* depends on what you're doing. You could for instance store one matrix, which is the current frame for the object. Other parts of your FAQ come back to this, at least propose other systems that can work just aswell ( and even work better in some circumstances ).
You have to remember that you're unique, just like everybody else.
[edited by - python_regious on January 10, 2004 2:26:12 PM]
[edited by - python_regious on January 10, 2004 2:26:40 PM]
quote:
Original post by python_regious
Sorry if it seems like I''m being pedantic...
It doesn''t.
quote:
This isn''t entirely accurate. You should go into the differences between row and column major matrices. Also, the position vector part will only work if you are working with homogenous matrices, in which case your vectors need to be 4 dimensional, not 3, otherwise the position will always come out as 0 ( non-homogenous 3x3 transformation matrices cannot hold translational information ).
You are right. However in 3D graphics (and even more so in the gaming part of it) 3x3 matrices are not used, because as you said they hold no translational information, so I assumed for my post that all matrices are 4x4. I''ll also change the vectors to 4-coordinate ones to make it more consistent.
About the part of row and column major matrices, I feel this would go against the overall idea of my post, which is to explain how to do basic stuff without going into the contents of matrices at all. The message I try to get across is : you don''t need to know how matrices work on the inside to do these things, all you have to do is use functions that operate on them.
quote:
A matrix doesn''t equal a transformation. Matrices can be used to perform transformations. Firstly, you should should define that:
M*X = T(X)
Where M is a matrix, X is a vector, and T(X) is a transformation function that operates on the vector X to produce another vector ( X'' for instance ).
That should eliminate any ambiguity.
From a mathematical point of view you are right, however I use as a convention that they are completely equivalent for the purposes of my post.
quote:
This *highly* depends on what you''re doing. You could for instance store one matrix, which is the current frame for the object. Other parts of your FAQ come back to this, at least propose other systems that can work just aswell ( and even work better in some circumstances ).
My post was not actually meant to be an FAQ but rather some explanations about the way I do these operations. There are of course many other systems, into the depth of which I had neither the time nor the will to dive.
Concerning the part about "one matrix, which is the current frame", I have found that this method is in many cases too restrictive (you can''t rotate such an object easily: you have to translate it back to origin first, then translate it back to its position), although in others it can be quite fitting.
I am considering more and more the idea of an article containinig source code and more detailed explanations, but I''ll keep updating this post with corrections until I get some free time on my hands.
Thank you for your positive criticism.
One post before I go to bed ( I''ll probably expand and say some other stuff tomorrow, but I''m tired ).
I disagree, primarily because Direct3D and OpenGL both handle their matrices differently. D3D uses row major, whereas OGL uses column major.
You have to remember that you''re unique, just like everybody else.
quote:
About the part of row and column major matrices, I feel this would go against the overall idea of my post, which is to explain how to do basic stuff without going into the contents of matrices at all. The message I try to get across is : you don''t need to know how matrices work on the inside to do these things, all you have to do is use functions that operate on them.
I disagree, primarily because Direct3D and OpenGL both handle their matrices differently. D3D uses row major, whereas OGL uses column major.
You have to remember that you''re unique, just like everybody else.
first, i think the three direction vectors should have w=0. for one to make the identity matrix make sense and second because as a vague rule of thumb directions should have w=0 and positions should have w=1. after thinking about that one a little its even making perfect sense, that opengl uses the positions w components to decide between point ("positional") and direction light ,-)
second, dont waste too much energy on that row/column major stuff. it has nothing to do with the matrix itself and even directx is storing them "vectorwise", though with that dozen options to access the single values that doesnt exactly mean too much and its just the way they are stored in memory because thats the order you usually need them.
example:
D3DXVECTOR3 tmp=Matrix.m[3];
after that you have the position in tmp, so the index is indexing columns which would make column major just like opengl. also, look at the docs, its 11,12,13,14, 21,22,... with the first number being the row. in memory and treated as float[16] d3d is looking exactly the same. but as i said, d3d has about a dozen ways to access matrix elements pretty much hiding how they really look like in memory.
also, if you really need that w-component depends on what youre doing with it. to transform a vector by a matrix it seems to make more sense to forget about w instead of doing a lot of calculations reducing themselves to 0 (but make sure the vector gets its w=1, except its supposed to be a direction rather than a position).
storing scaling, rotation and translation seperate isnt a bad idea, but i really wouldnt store each of them as a matrix with a whole lot of zeros. its just tempting you to do everything with matrix multiplications. say we want to translate x to the right. should we really build a whole 4x4 matrix for that and multiply it with our current one, when we could simply update the translation part with +=x*right ? moving along all three axes would be 3 scaled vector additions which still seems like less work than a full blown matrix mult. maybe im just touchy because it seemed horribly wasteful in dx to build a translations matrix and multiply it another matrix with a lot of the calculations being pointless. a 3x3 matrix and 2 3d vectors for scaling and translations should be enough. if possible id completely stay away from scaling because its just making inverting the matrix more complicated. as position and orientation are strictly "seperated" inside the matrix just storing a 4x4 matrix would only waste 4 unused values but can be quite comfortable.
as mentioned, for simple transformations the w components in the matrix are quite useless, especially if you imagine the vector/matrix mult as 3 dot products between the vector and and all x,y,z components of the matrix. the w in the vector we want to transform is quite important though, as this is the deciding factor if we add the translation or not. you could as well multiply the vector with only the 3x3 orientation part and then add the translation part to the result (or not, for directional vectors like normals, etc.)
random things:
"Turn it left/right? Extract its "up" vector from its R matrix, create a matrix R' to rotate around this axis by the needed angle, and apply the steps seen two paragraphs above."
except you dont have to extract its up vector, as rotating around (0,1,0) will automatically rotate us around the objects "up". the beauty about it: multiply this rotation matrix from the other side and this rotation will happen before the other transformations, meaning: you rotate around the global "up". the difference between applying a transformation in world or local coordinates is just a question of left or right side.
[edited by - Trienco on January 13, 2004 12:37:50 PM]
second, dont waste too much energy on that row/column major stuff. it has nothing to do with the matrix itself and even directx is storing them "vectorwise", though with that dozen options to access the single values that doesnt exactly mean too much and its just the way they are stored in memory because thats the order you usually need them.
example:
D3DXVECTOR3 tmp=Matrix.m[3];
after that you have the position in tmp, so the index is indexing columns which would make column major just like opengl. also, look at the docs, its 11,12,13,14, 21,22,... with the first number being the row. in memory and treated as float[16] d3d is looking exactly the same. but as i said, d3d has about a dozen ways to access matrix elements pretty much hiding how they really look like in memory.
also, if you really need that w-component depends on what youre doing with it. to transform a vector by a matrix it seems to make more sense to forget about w instead of doing a lot of calculations reducing themselves to 0 (but make sure the vector gets its w=1, except its supposed to be a direction rather than a position).
storing scaling, rotation and translation seperate isnt a bad idea, but i really wouldnt store each of them as a matrix with a whole lot of zeros. its just tempting you to do everything with matrix multiplications. say we want to translate x to the right. should we really build a whole 4x4 matrix for that and multiply it with our current one, when we could simply update the translation part with +=x*right ? moving along all three axes would be 3 scaled vector additions which still seems like less work than a full blown matrix mult. maybe im just touchy because it seemed horribly wasteful in dx to build a translations matrix and multiply it another matrix with a lot of the calculations being pointless. a 3x3 matrix and 2 3d vectors for scaling and translations should be enough. if possible id completely stay away from scaling because its just making inverting the matrix more complicated. as position and orientation are strictly "seperated" inside the matrix just storing a 4x4 matrix would only waste 4 unused values but can be quite comfortable.
as mentioned, for simple transformations the w components in the matrix are quite useless, especially if you imagine the vector/matrix mult as 3 dot products between the vector and and all x,y,z components of the matrix. the w in the vector we want to transform is quite important though, as this is the deciding factor if we add the translation or not. you could as well multiply the vector with only the 3x3 orientation part and then add the translation part to the result (or not, for directional vectors like normals, etc.)
random things:
"Turn it left/right? Extract its "up" vector from its R matrix, create a matrix R' to rotate around this axis by the needed angle, and apply the steps seen two paragraphs above."
except you dont have to extract its up vector, as rotating around (0,1,0) will automatically rotate us around the objects "up". the beauty about it: multiply this rotation matrix from the other side and this rotation will happen before the other transformations, meaning: you rotate around the global "up". the difference between applying a transformation in world or local coordinates is just a question of left or right side.
[edited by - Trienco on January 13, 2004 12:37:50 PM]
Topic Locked
This topic has been locked by a moderator. New replies are not allowed.
Sign in to reply to this topic.
Advertisement