|  
                               General: 
                                Matrix Maths 
                                Author: 
                                Jack Hoxley 
                                Written: 12th July 2001 
                                Contact: [EMail] 
                               
                              Contents 
                                of this lesson 
                                1. Introduction 
                                2. 2D Transformations 
                                3. 3D Transformations 
                               
                              1. 
                                Introduction 
                              I've 
                                had so many emails in recent months about the 
                                transformation of 2D and 3D geometry that I'm 
                                getting bored with it - so I thought, to answer 
                                the question for the final time, I'll write an 
                                article to explain the whole thing. This is that 
                                article... 
                              Transformation 
                                consists of 3 things - rotating (spinning things 
                                around), scaling (making things bigger/smaller) 
                                and translation (moving things around). With 3D 
                                graphics Direct3D will do 99% of the work for 
                                you, however, along my travels I've found a way 
                                of going faster - if you do the matrix maths calculations 
                                yourself you can go just that little bit faster 
                                (2.5x faster), we're talking the difference between 
                                0.0001ms and 0.00005ms here, but if you're rendering 
                                500+ objects a frame (several 1000 a second), 
                                it could be worth the extra effort. It also looks 
                                much cleverer! (if you open-source your code). 
                              Onwards 
                                and upwards... 
                               
                              2. 
                                2D Transformations 
                              This 
                                is the most useful part, using either DirectDraw 
                                (fully 2D) or Direct3D (quasi 2D) no transformations 
                                are done for you - you want to rotate, translate 
                                or scale something you're gonna have to do it 
                                yourself. The actual mathematical proof behind 
                                all of this is a little complicated, and to be 
                                honest - it really makes much difference why it 
                                works, all we want to know is how to make it work 
                                for us. Much of the pure maths is glossed over 
                                here, if you're interested in proofs or further 
                                explanations dig out your old maths books or go 
                                searching the web - much of the pure maths is 
                                too lengthy to explain here. 
                              We're 
                                going to be using a mathematical technique known 
                                as matrix maths - the ideas behind matrices (plural 
                                of matrix) isn't too important, heck! I dont even 
                                know exactly how to use them - I just learnt what 
                                I need to know about them. Matrix maths allows 
                                us to do the transformations relatively quickly, 
                                and relatively simply as well - just what I like. 
                              The 
                                basic structure is like this [X,Y] ---} [M] ---} 
                                [X',Y'] (one set of coordinates goes in, something 
                                happens, and a new set of coordinates come out). 
                                [M] is the transformation matrix, it may scale 
                                the coordinates, rotate them, translate them or 
                                some combination of the 3. The best part is that 
                                you can combine all 3 transformations into one 
                                matrix (more on that later). 
                              Translation 
                                 This is probably the easiest of the 3, and 
                                thus we'll start here. A 2D translation matrix 
                                for what we want is a 3x3 grid of numbers (known 
                                as a 3x3 matrix). for a translation it looks like 
                                this: 
                                
                              a 
                                bit weird really, isn't it. in order to translate 
                                a point by this matrix we must multiply the point 
                                by the matrix. Now this isn't as simple as it 
                                sounds, it's not quite like normal multiplication, 
                                and it's an awful lot more complicated. if we 
                                refer to all of the elements in row-column notation 
                                (ie, yx) and the rows are i and columns j, to 
                                get the FINAL value for element <i,j> we 
                                multiply each element in the row i (of the source 
                                matrix) with each element in the column j (of 
                                the destination matrix), we then add all of these 
                                values together and that is our final value. scared? 
                                look at this following example and see if you 
                                can understand what happened: 
                                
                              it 
                                aint too scary really, is it? the final results 
                                aren't particularly amazing - I'm pretty sure 
                                that without all this extra work you could of 
                                told me how to translate the original coordinates 
                                (the x' = x + dx part)... It'll come into it's 
                                own a little later on when we're combining rotation, 
                                scaling and translation into one big equation. 
                              Rotation 
                                 Rotation is the big one - this is what everyone 
                                likes emailing me about. Rotating your sprite 
                                around is a very useful trick in games - and is 
                                often quite heavily used; therefore it obviously 
                                helps being able to do it! The following diagram 
                                shows you what the 2D rotation matrix should look 
                                like: 
                                
                              A 
                                little more scary this time - trig functions. 
                                The presence of these trig functions will bring 
                                the processing time for a rotation transformation 
                                up considerably - trig functions are slow. If 
                                you can optimise away any of the trig functions 
                                then do so - the only realistic optimisation to 
                                be done here is to pre-process CosX and SinX, 
                                as that will 1/2 the number of calls to Cos( ) 
                                or Sin( ). More on this later (when we do a bit 
                                of code). 
                              I've 
                                already explained the basics of matrix multiplication 
                                in the translation section, so it should 
                                make sense this time around - if not, the derived 
                                equations are perfectly usable without any knowledge 
                                of matrix mathematics. Here's how we rotate our 
                                point [x,y] by X radians to retrieve [x',y'] : 
                                
                              Again, 
                                not too simple if you can see your way through 
                                the matrix multiplication algorithm. Be careful 
                                to differentiate between the X and the x, the 
                                X is the angle (usually denoted as theta, q), 
                                and x is the coordinate.  
                              Scaling 
                                 This isn't used as often, but it's pretty 
                                simple - so I may as well explain it, and we can 
                                incorporate it into the big matrix later on. You 
                                may have noticed that all of the matrices so far 
                                have had the r=c values equal to 1 (unless replaced 
                                by another value), this is because the r=c values 
                                are the scaling values - in previous matrices 
                                they are set to 1 so that they dont intefere with 
                                the resultant values. a matrix where all the values 
                                are 0 except the r=c values which are 1 is called 
                                the "identity matrix", which is a useful 
                                type of matrix, but not really relevant here. 
                                
                              Not 
                                at all complicated, and neither is the resulting 
                                equations - you can probably guess them now! 
                                
                              Tell 
                                me you could see that coming? it's pretty obvious... 
                              Combining 
                                Transformations 
                                Up till this point, the use of matrices has been 
                                a little pointless - the derived equations are 
                                enough to rotate, translate and scale. you could 
                                quite easily apply each transformation individually 
                                like this: 
                              x' 
                                = x + dx 
                                y' = y + dy 
                                 
                                x'' = x'Cosq - y'Sinq 
                                y'' = x'Sinq + y'Cosq 
                                 
                                x''' = x'' * sx 
                                y''' = y'' * sy 
                              the 
                                above code would translate the point, rotate it 
                                around the origin and then scale it by a given 
                                factor - not necessarily what you want (rotation 
                                before translation is more common), but you can 
                                juggle the lines around. BUT, using matrices we 
                                can combine all 3 transformations together, then 
                                split them out into two lines - generating x',y' 
                                from x,y instead of going all the way to x''',y''' 
                                - is that not better? 
                              The 
                                way we do this is by creating a master matrix 
                                - we multiply (using matrix multiplication) the 
                                translation, rotation and scaling matrices together 
                                into one matrix, then multiply the point x,y by 
                                this master matrix. A prior note on multiplication 
                                order - as with the 6 equations just listed it 
                                matter what order they go in, rotation-translation 
                                is very different from translation-rotation (one 
                                will create an orbiting body, the other will create 
                                a spinning object). Normally you would scale the 
                                points, rotate them and then translate them - 
                                but you'll need to decide which is best for your 
                                application. here is the complete proof for the 
                                "master matrix": 
                              [M] 
                                is the Master Matrix 
                                [S] is the scaling Matrix 
                                [R] is the rotation Matrix 
                                [T] is the translation Matrix 
                              [M] 
                                = [S][R][T] 
                                = ( [S]*[R] ) * [T] 
                                
                              [M] 
                                = [SR][T] 
                                
                              there 
                                we have the final "Master Matrix". How 
                                amazing, if we now multiply a point [x,y,1] by 
                                this matrix we should be left with an equation 
                                to transform x and an equation to transform y 
                                - which will result in a rotation, translation 
                                and scaling. Arguably, through substitution, you 
                                could of combined the original 3 equations, but 
                                this way is open to much more powerful calculations 
                                - there are quite a few other types of transformation 
                                that can be done using matrices, and a few shortcuts 
                                can be found along the way as well. The following 
                                illustration indicates how the final all-in-one 
                                formula works: 
                                
                              So 
                                the final equation to transform the point (x,y) 
                                - rotating through q radians, scaling by (sx,sy) 
                                and translating by (dx,dy) units - is shown above. 
                                we can prove that this combined equation is actually 
                                correct by substituting in the previous set of 
                                equations - shown below. 
                              Scale: 
                                x' = x * sx 
                                y' = y * sy 
                              Rotate: 
                                x'' = x'Cosq - y'Sinq 
                                y'' = x'Sinq + y'Cosq 
                              Translate: 
                                x''' = x'' + dx 
                                y''' = y'' + dy 
                              Combined: 
                                x' = ((x * sx)Cosq 
                                - (y * sy)Sinq) + dx 
                                y' = ((x * sx)Sinq 
                                + (y * sy)Cosq) + dy 
                                 
                              Simplified: 
                                x' = xSxCosq - ySySinq 
                                + dx 
                                y' = xSxSinq + ySyCosq 
                                + dy 
                              and 
                                there, as if by magic - we've gotten the same 
                                formula back. The method you choose - by straight 
                                algebra or by matrix algebra - is your choice 
                                entirely; I personally prefer the matrix method 
                                as it allows for many 1000's of combinations; 
                                for example - scale, rotate, translate, rotate 
                                - will (given a flying saucer object) spin it 
                                around it's center and then spin it around the 
                                origin (like orbiting a planet) - you try doing 
                                that with those plain equations, it's still possible 
                                - but a little more complicated methinks. To finish 
                                things off, I've written a simple transformation 
                                function incorporating the overall equation. 
                               
                                
                                  
                                     
                                     
                                       
                                        
                                           
                                           
                                             
                                              
                                                 
                                                 
                                                   
                                                    
                                                       
                                                       
                                                         
                                                          Private Function Transform2DPoint(tX As Single, tY As Single, sX As Single, sY As Single, Theta As Single, SrcPt As Pt) As Pt     '(tX,tY) describes the translation     '(sX,sY) describes the scale     'Theta describes the rotation     'SrcPt is the point to be transformed     '[RETURN] is the transformed point          '   The general formulas:     '   X' = xSxCosq - ySySinq + dx     '   Y' = xSxSinq + ySyCosq + dy          Dim Cosq As Single     Dim Sinq As Single     Cosq = Cos(Theta)     Sinq = Sin(Theta)          Transform2DPoint.X = (SrcPt.X * sX * Cosq) - (SrcPt.Y * sY * Sinq) + tX     Transform2DPoint.Y = (SrcPt.X * sX * Sinq) + (SrcPt.Y * sY * Cosq) + tY End Function 
                                                         | 
                                                       
                                                       
                                                     
                                                   | 
                                                 
                                                 
                                               
                                             | 
                                           
                                           
                                         
                                       | 
                                     
                                     
                                   
                                
                               
                                
                               
                              3. 
                                3D Transformations 
                              I'm 
                                not going to say much on this topic - 90% of what 
                                was in the previous section is still relevant 
                                in this section. More importantly, however, is 
                                that Direct3D (or any other 3D API) will do matrix 
                                transformations for you - only the specialist/elite 
                                will need to play around with the transformation 
                                matrices manually. 
                              The 
                                advantage of having D3D do the actual transformation 
                                is that all you need to do is present the overall 
                                matrix and it'll work out what needs to happen 
                                - and in some cases the hardware will actually 
                                do the mathematics on the geometry (which is going 
                                to be 10000x faster than any software implementation). 
                                As visitors to the VoodooVB message board will 
                                be aware, I actually worked this out a while back 
                                and posted a generalised matrix formula for a 
                                3D transformation. During my tests on this it 
                                gave exactly the same results as using the built 
                                in D3DX functions, yet was 1.6-2.6x faster than 
                                them. The only trade off is actually working out 
                                the generalised matrix in the first place - this 
                                usually only ever has to be done once. 
                              The 
                                only two differences between the 2D matrices and 
                                the 3D matrices is their size (now 4x4 instead 
                                of 3x3) and there are 3 rotation matrices (x,y,z) 
                                - 2D rotation (on a plane) only requires you to 
                                have 1 rotation axis, 3D has 3 main rotation axis's... 
                              The 
                                5 matrices are shown below - this is for reference, 
                                given the information about 2D transformations 
                                you should quite easily be able to do some clever 
                                things with them... 
                                
                                -   
                                -   
                                
                                -   
                              Finally, 
                                As I already mentioned, I calculated the generalised 
                                matrix for this a while back - and it's shown 
                                below. 
                                
                              Pretty 
                                isn't it! Here's the same transformation matrix 
                                but in code: 
                               
                                
                                  
                                     
                                     
                                       
                                        
                                           
                                           
                                             
                                              
                                                 
                                                 
                                                   
                                                    
                                                       
                                                       
                                                        |  
                                                           Private 
                                                            Function CreateMatrix(Rx 
                                                            As Single, Ry As Single, 
                                                            Rz As Single, Sx As 
                                                            Single, _  
                                                            Sy As Single, Sz As 
                                                            Single, Tx As Single, 
                                                            Ty As Single, Tz As 
                                                            Single) As D3DMATRIX 
                                                          Dim 
                                                            CosRx As Single, CosRy 
                                                            As Single, CosRz As 
                                                            Single 
                                                            Dim SinRx As Single, 
                                                            SinRy As Single, SinRz 
                                                            As Single 
                                                          CosRx 
                                                            = Cos(Rx) 'Used 6x 
                                                            CosRy = Cos(Ry) 'Used 
                                                            4x 
                                                            CosRz = Cos(Rz) 'Used 
                                                            4x 
                                                            SinRx = Sin(Rx) 'Used 
                                                            5x 
                                                            SinRy = Sin(Ry) 'Used 
                                                            5x 
                                                            SinRz = Sin(Rz) 'Used 
                                                            5x 
                                                            'total of 29 trig 
                                                            functions 
                                                            '23 trig functions 
                                                            cancelled out by 
                                                            'this optimisation; 
                                                            hence the 2.6x speed 
                                                            increase. 
                                                          With 
                                                            CreateMatrix 
                                                            .m11 = (Sx * CosRy 
                                                            * CosRz) 
                                                            .m12 = (Sx * CosRy 
                                                            * SinRz) 
                                                            .m13 = -(Sx * SinRy) 
                                                             
                                                            .m21 = -(Sy * CosRx 
                                                            * SinRz) + (Sy * SinRx 
                                                            * SinRy * CosRz) 
                                                            .m22 = (Sy * CosRx 
                                                            * CosRz) + (Sy * SinRx 
                                                            * SinRy * SinRz) 
                                                            .m23 = (Sy * SinRx 
                                                            * CosRy) 
                                                             
                                                            .m31 = (Sz * SinRx 
                                                            * SinRz) + (Sz * CosRx 
                                                            * SinRy * CosRz) 
                                                            .m32 = -(Sz * SinRx 
                                                            * CosRx) + (Sz * CosRx 
                                                            * SinRy * SinRz) 
                                                            .m33 = (Sz * CosRx 
                                                            * CosRy) 
                                                             
                                                            .m41 = Tx 
                                                            .m42 = Ty 
                                                            .m43 = Tz 
                                                            .m44 = 1# 
                                                            End With 
                                                            End Function 
                                                         | 
                                                       
                                                       
                                                     
                                                   | 
                                                 
                                                 
                                               
                                             | 
                                           
                                           
                                         
                                       | 
                                     
                                     
                                   
                                
                               
                               
                               
                               
                              Conclusion 
                              Wow 
                                - that's a lot of maths, I sure hope someone out 
                                there finds all this useful!! either way, I'm 
                                sure the 2D-Transformation parts will be interesting 
                                for a lot of people. Enjoy! 
                               |