| |
|
|
||||
![]() |
||||||
| |
|
|||||
|
Complex Matrix Transformations Interpreting Concatenated Matrix Transforms Transforms are described in steps made up of translations, scales, and rotations. There is sometimes confusion though about which step is first. The problem is that there are two valid ways of interpreting a transform. You can think of a transform as progressing from right to left with a point, P, being transformed from distant reference frames towards the zero frame. One might describe the following matrix transform as "P4 is rotated by R2, translated by T2, rotated by R1 and then translated by T1."
One can also describe the transform as a series of changes applied from left to right. Each change is applied to a reference frame. It would then be described as, "Starting with the zero frame, the axes are translated by T1, rotated by R1, translated by T2 and then finally rotated by R2." The former description mentions a rotation by R2 as the first step. The latter description mentions a translation by T1 as coming first so it can be confusing. The right to left interpretation is obviously valid because you just start at the right and multiply your column vector by the right most matrix. At each step you get a column vector in another reference frame. The other interpretation is valid because you can imagine combining matrices from left to right. After each multiplication, you have a product matrix that can be partitioned into axis vectors and a translation, just like in Figure 2. If you run
into a discrepancy with someone about the way to read a matrix, write
it out and discuss the pieces of the transform. The matrix math is the
same regardless of the way it is read. You might each be talking about
the same matrix but in two different ways. C++ has been so widely accepted by game developers that by now everyone that wants a matrix class already has one. Chances are your thoughts on whether row or column matrices are better are irrelevant because the company's (or team's) matrix class already exists and you have to use it. The task now is to make sure that you learn the company's matrix conventions. This includes the way the matrix elements are stored, and the decision to form row or column matrices. You could ask another developer or you could take a look at the way a matrix is multiplied with a vector in the matrix class implementation. Look at the dot product performed to reach the first element in the matrix product. If the vector is dotted with the top row of the matrix, the vector is a column. If the vector is dotted with the left most column, then the vector is a row. Next do a sanity check with some other functions. For instance, if there is a function that converts a quaternion to a matrix, check that it is following the same convention. Look up the conversion in a reference and check that the reference author agrees with the author of your class. After you are sure of the class conventions you won't ever have to question what they are again. Debugging Matrix Concatenations There is a bad but accepted method of creating matrix transforms amongst many game programmers that goes like this. Make an initial guess of what the transform expression might be and type it in. Try it out and see if it works. If it doesn't work, transpose and swap matrices in the expression until it works. This is exactly what not to do. Instead, you should write out the expression for your matrix transform and know that it is right. You know it is right because you know your matrix conventions, and you used the above matrix naming scheme to create the expression. Of course there will be times when you have the correct expression but it doesn't work when you try it in code. When that happens you have to check that the matrices you created actually match their names and you have to check the matrices that were passed in from other sources as well. It can still be difficult but at least you will be progressing towards the right answer by isolating the problem. The reason it is so important not to mechanically transpose or swap your matrices is that it is easy to get lost in all the possible transposes. We've seen that the difference between row and column matrices is a transpose. Unscaled rotation matrices have the property that their inverse is their transpose. So if you blindly invert a matrix you can be introducing a transpose. With enough swapping and transposing, you can get back to where you started because of the matrix identity:
It is easy to get lost in all the transposes after only a few hacks. Another difficulty is that two transposes undo each other.
The iterative hacking of the matrix expression is supposed to stop when the result looks right but you may have two errors. This is why mysterious transposes live on in some code bases. After a while it would require a time consuming rigorous audit of too much code to fix. The best way to avoid those situations is to make the matrices correctly the first time. Conclusion We've covered several helpful ways to make creating transforms easier. Name vectors and points with the reference frame they are in. Name matrices by the reference frames that they transform between. Use the matrix names to guide the way they can be combined. Use the simplified 2x2 versions of the transforms to visualize and plan out your desired transform. And lastly, don't ever hack your transforms by swapping matrices or transposing them. If you follow these rules and get your fellow programmers to follow these rules, working with transforms becomes much easier. References and Further Reading The naming schemes, matrix concatenation, and the 2x2 transform notation was all covered in Prof. Lynn Conway's undergraduate Robotics course at the University of Michigan. Our course text had good coverage in a more rigorous manner: Robotics
for Engineers, Yoram Koren, McGraw-Hill, 1985., pp. 88-101. Unfortunately
this book is out of print. Amazon occasionally has a used one.
|
||||||||||||||
|
|