Similar to 2D transformations, which used 3x3 matrices, 3D transformations use 4X4 matrices (X, Y, Z, W)
3D Translation: point (X,Y,Z) is to be translated by amount Dx, Dy and Dz to location (X',Y',Z')
X' = Dx + X
Y' = Dy + Y
Z' = Dz + Z
or P' = T * P where
_ _ P' = | X' | | Y' | | Z' | | 1 | - - _ _ T = | 1 0 0 Dx | = T(Dx,Dy,Dz) | 0 1 0 Dy | | 0 0 1 Dz | | 0 0 0 1 | - - _ _ P = | X | | Y | | Z | | 1 | - -
_ _ P' = | X' | | Y' | | Z' | | 1 | - - _ _ S = | Sx 0 0 0 | = S(Sx,Sy,Sz) | 0 Sy 0 0 | | 0 0 Sz 0 | | 0 0 0 1 | - - _ _ P = | X | | Y | | Z | | 1 | - -
For 3D rotation we need to pick an axis to rotate about. The most common choices are the X-axis, the Y-axis, and the Z-axis
_ _ P' = | X' | | Y' | | Z' | | 1 | - - _ _ Rz = | cos(theta) -sin(theta) 0 0 | = Rz(theta) | sin(theta) cos(theta) 0 0 | | 0 0 1 0 | | 0 0 0 1 | - - _ _ Rx = | 1 0 0 0 | = Rx(theta) | 0 cos(theta) -sin(theta) 0 | | 0 sin(theta) cos(theta) 0 | | 0 0 0 1 | - - _ _ Ry = | cos(theta) 0 sin(theta) 0 | = Ry(theta) | 0 1 0 0 | | -sin(theta) 0 cos(theta) 0 | | 0 0 0 1 | - - _ _ P = | X | | Y | | Z | | 1 | - -
Now what if rotations are to occur other than around one of the cartesian axes? There are two ways of looking at this:
Composition is handled in a similar way to the 2D case, multiplying the transformation matrices from right to left.
In OpenGL translation, rotation, and scaling are performed using commands such as:
glTranslate{fd}(X,Y,Z) - glTranslatef(1.0, 2.5, 3.0)
glRotate{df}(Angle, X, Y, Z) - glRotatef(60.0, 0.0, 0.0, 1.0)
glScale{df}(X, Y, Z) - glScalef(1.0, 1.5, 2.0)
What these commands do in practice is to generate the corresponding transformation matrix for the operation that was requested, multiply it by whatever matrix is currently on top of the currently active matrix stack, and replace the matrix on the top of the stack with the result. If you want to calculate and save the result of a complex series of transformations, one approach is to push an identity matrix onto the stack, perform each of the operations in turn, and then save a copy of the result from the top of the stack into a global or static variable, ( possibly popping it off the stack if it won't be needed there immediately. )