QB CULT MAGAZINE
Issue #5 - July-October 2000

Faster 3D Rotation

By Eclipzer <eclipzer@aol.com>

As we all know, the greatest part about doing 3D graphics is being able to view your models from any angle. If you know anything about 3D rotation at all, then you'll know that it is nothing more than three 2D rotations, one rotation for each axis. You simply supply the number of degrees to rotate about each axis, and calculate the rotated position of each point based on that. For this document we shall use the following terms for the number of degrees to rotate about each axis:

  a = z-angle 'degrees to rotate about z-axis
  b = y-angle 'degrees to rotate about y-axis
  c = x-angle 'degrees to rotate about x-axis

The key to doing faster rotation is to eliminate as much math as possible within the point rotation routine. Before we can do this we must first take a look at the standard method of point rotation. Our initial point will be (x,y,z) and our final point will be (x'',y'',z'') [read "x double prime, y double prime, z double prime"]:

  x'  = x*cos(a) - y*sin(a)   'rotate about the 
  y'  = y*cos(a) + x*sin(a)   'z-axis

  x'' = x'*cos(b) - z *sin(b) 'rotate about the
  z'  = z *cos(b) + x'*sin(b) 'y-axis

  y'' = y'*cos(c) - z'*sin(c) 'rotate about the
  z'' = z'*cos(c) + y'*sin(c) 'x-axis

Notice that once we have rotated two points about an axis we must save their values to be used in the next axis rotation. This prevents the axes from rotating independently of each other. You should also notice that to get from the inital point (x,y,z) to the final point (x'',y'',z'') it requires 12 multiplications. So for every 3D point that you wish to rotate you must perform 12 multiplications. Ultimately our goal is to reduce the number of multiplications performed per point, thereby increasing speed. Our solution? Simplify! To do this, first we will substitute the actual calculations for x',y',z' into our equations for x'',y'',z'':

x'' = x'*cos(b) - z*sin(b)                                         'standard x'' calculation
    = [x*cos(a) - y*sin(a)]*cos(b) - z*sin(b)                      'substitute for x'

y'' = y'*cos(c) - z'*sin(c)                                        'standard y'' calculation 
    = [y*cos(a) + x*sin(a)]*cos(c) - [z*cos(b) + x'*sin(b)]*sin(c) 'substitute for y' and z'

z'' = z'*cos(c) + y'*sin(c)                                        'standard z'' calculation
    = [z*cos(b) + x'*sin(b)]*cos(c) + [y*cos(a) + x*sin(a)]*sin(c) 'substitute for z' and y'

Alright, we've done the first substitution and it seems our equations are even uglier! We've still got x' in both the y'' and z'' equations. It's going to get worse before it gets better. Our next step is to use the distributive property to expand our equations into addition and subtraction of products. From there we will substitute for x' where it exists and then use the distributive property once again:

x'' = [x*cos(a) - y*sin(a)]*cos(b) - z*sin(b)                      'substitute for x'
    = x*cos(a)cos(b) - y*sin(a)cos(b) - z*sin(b)                   'distribute cos(b)

y'' = [y*cos(a) + x*sin(a)]*cos(c) - [z*cos(b) + x'*sin(b)]*sin(c) 'substitute for y' and z'
    = y*cos(a)cos(c) + x*sin(a)cos(c) -                            'distribute cos(c)
      z*cos(b)sin(c) - x'*sin(b)sin(c)                             'distribute -sin(c)
    = y*cos(a)cos(c) + x*sin(a)cos(c) - z*cos(b)sin(c) -
      [x*cos(a) - y*sin(a)]*sin(b)sin(c)                           'substitute for x' 
    = y*cos(a)cos(c) + x*sin(a)cos(c) - z*cos(b)sin(c) -
      x*cos(a)sin(b)sin(c) + y*sin(a)sin(b)sin(c)                  'distribute -sin(b)sin(c)

z'' = [z*cos(b) + x'*sin(b)]*cos(c) + [y*cos(a) + x*sin(a)]*sin(c) 'substitute for z' and y'
    = y*cos(a)sin(c) + x*sin(a)sin(c) +                            'distribute sin(c)    
      z*cos(b)cos(c) + x'*sin(b)cos(c)                             'distribute cos(c)
    = y*cos(a)sin(c) + x*sin(a)sin(c) + z*cos(b)cos(c) +
      [x*cos(a) - y*sin(a)]*sin(b)cos(c)                           'substitute for x'
    = y*cos(a)sin(c) + x*sin(a)sin(c) + z*cos(b)cos(c) +
      x*cos(a)sin(b)cos(c) - y*sin(a)sin(b)cos(c)                  'distribute sin(b)cos(c)

Now all our equations are represented by the addition and subtraction of products. Also we have totally eliminated x',y',z' from the equations meaning x'',y'',z'' are only in terms of x,y,z. The next step is to factor out any multiple x,y,z terms. Note that x'' has no terms to be factored:

x'' = x*cos(a)cos(b) - y*sin(a)cos(b) - z*sin(b)                   'no factoring needed

y'' = y*cos(a)cos(c) + x*sin(a)cos(c) - z*cos(b)sin(c) -
      x*cos(a)sin(b)sin(c) + y*sin(a)sin(b)sin(c)                  'distribute -sin(b)sin(c)
    = x*[sin(a)cos(c) - cos(a)sin(b)sin(c)] + 
      y*[cos(a)cos(c) + sin(a)sin(b)sin(c)] - z*cos(b)sin(c)       'factor out x and y terms

z'' = y*cos(a)sin(c) + x*sin(a)sin(c) + z*cos(b)cos(c) +
      x*cos(a)sin(b)cos(c) - y*sin(a)sin(b)cos(c)                  'distribute sin(b)cos(c)
    = x*[sin(a)sin(c) + cos(a)sin(b)cos(c)] +
      y*[cos(a)sin(c) - sin(a)sin(b)cos(c)] + z*cos(b)cos(c)       'factor out x and y terms

Well now our equations look much cleaner, but it seems as if we have more math going on now then before! However, you should notice that the x,y,z terms in each equation are only multiplied by sines and cosines. These sin/cos values are constants and can therefore be precalculated. Not only that but many of the sin/cos products are repeated in each equation so we have even less to do less. For now we will simply calculate our constants and rewrite our equations:

  xx = cos(a)cos(b)                      'calc xx constant
  xy = -sin(a)cos(b)                     'calc xy constant
  xz = -sin(b)                           'calc xz constant 

  yx = sin(a)cos(c) - cos(a)sin(b)sin(c) 'calc yx constant
  yy = cos(a)cos(c) + sin(a)sin(b)sin(c) 'calc yy constant
  yz = -cos(b)sin(c)                     'calc yz constant 

  zx = sin(a)sin(c) + cos(a)sin(b)cos(c) 'calc zx constant
  zy = cos(a)sin(c) - sin(a)sin(b)cos(c) 'calc zy constant
  zz = cos(b)cos(c)                      'calc zz constant

  x'' = x*xx + y*xy + z*xz               'final x'' equation
  y'' = x*yx + y*yy + z*yz               'final y'' equation 
  z'' = x*zx + y*zy + z*zz               'final z'' equation

As you can see our final equations look a whole lot nicer now, and we have reduced the number of multiplications per rotation from 12 to 9. This may not seem like a whole lot but with our original method we were doing three times the number of points more multiplications each time we rotated. Food for thought. Also, we only need to calculate the constants once per rotation. So any time we change the angles a,b,c we must recalculate the constants. This is truly a trivial price to pay for the increase in speed. Well that's all there is to it. Enjoy!

Note to reader:
If you find any of the code or methods within this tutorial useful, or include them in your own code, then all I ask is that you give credit where credit is due.
Contact:
If you have any questions or comments concerning the material presented within this tutorial, please feel free to e-mail me at eclipzer@aol.com. Please head your message as "Tutorial Questions/Comments", as I tend to delete mail from people I do not know.
Special Thanks:
Special thanks goes out to Avatar for these methods. Happy Coding!

-Eclipzer

Return to Menu