How to calculate 3D objects

2004-04-17
Written by PerErik Klarenfjord


Contents:

1. Setup and rotate a 3D object
2. Calculate 3D -> 2D (computer screen) with perspective
3. Example of rotating algorithms in C
4. Receive coordinates in 3D from a picture in 2D with perspective


1. Setup and rotate a 3D object

Coordinates of points in a 3D object can be described by using the X, Y and Z cartesian coordinates. In this document you may see the coordinates of a point as (X, Y, Z), i.e. (20, -30, 40) means X=20, Y=-30, Z=40. (0, 0, 0) is allways the coordinates of reference and can be located in the middle of the object.

Negative values of X is to the left of (0, 0, 0).
Negative values of Y is above (0, 0, 0).
Negative values of Z is deeper into the screen relative (0, 0, 0).

A 3D object contains several points. A cube has a point in every corner which give us 8 points. The points of the cube can have values like:

(20, 20, 20), (-20, 20, 20), (-20, -20, 20), (20, -20, 20), (20, 20, -20), (-20, 20, -20), (-20, -20, -20), (20, -20, -20)



The side of the cube has the length 2*20=40.

We are now going to derive the formulas needed for rotating a 3D object, i.e. calculate new coordinates for every point in the object. It's rather simple to use linear algebra and matrix multiplication to derive the formulas. If you are not familiar with this kind of mathematics you can still use the finnished formulas later in this section.


To rotate around the Y axis an angular A:

                | cos(A)  0  sin(A)| |x|   |Sx|
My * MX = Ms => |   0     1    0   | |y| = |Sy|
                |-sin(A)  0  cos(A)| |z|   |Sz|
x, y and z (MX) are the coordinates of a point before rotation.
Sx, Sy and Sz (Ms) are the new coordinates of a point after rotation.
My is the matrix of rotation around the Y axis.

To calculate the new Sx, Sy and Sz values you need to multiply the two matrices above, which give us:

Sx = x*cos(A) + z*sin(A)
Sy = y
Sz = -x*sin(A) + z*cos(A)


To rotate around the X axis an angular B:
                |1     0       0   | |x|   |Sx|
Mx * MX = Ms => |0   cos(B)  sin(B)| |y| = |Sy|
                |0  -sin(B)  cos(B)| |z|   |Sz|
Sx = x
Sy = y*cos(B) + z*sin(B)
Sz = -y*sin(B) + z*cos(B)


To rotate around the Z axis an angular C:
                | cos(C)  sin(C)  0| |x|   |Sx|
Mz * MX = Ms => |-sin(C)  cos(C)  0| |y| = |Sy|
                |   0       0     1| |z|   |Sz|
Sx = x*cos(C) + y*sin(C)
Sy = -x*sin(C) + y*cos(C)
Sz = z


You may now see a pattern on how rotating is performed. We are now going to multiply all the rotating matrices which let us rotate an object around every axis with one formula for X, Y and Z.

My * Mx * Mz * MX = Ms

   | cos(A)  0  sin(A)| |1     0       0   | | cos(C)  sin(C)  0| |x|   |Sx|
=> |   0     1    0   | |0   cos(B)  sin(B)| |-sin(C)  cos(C)  0| |y| = |Sy|
   |-sin(A)  0  cos(A)| |0  -sin(B)  cos(B)| |   0       0     1| |z|   |Sz|
Sx = x*(cos(A)*cos(C) + sin(A)*sin(B)*sin(C)) + y*(cos(A)*sin(C) - sin(A)*sin(B)*cos(C)) + z*(sin(A)*cos(B))
Sy = x*(-cos(B)*sin(C)) + y*(cos(B)*cos(C)) + z*(sin(B))
Sz = x*(-sin(A)*cos(C) + cos(A)*sin(B)*sin(C)) + y*(-sin(A)*sin(C) - cos(A)*sin(B)*cos(C)) + z*(cos(A)*cos(B))

The same result as above can be calculated by using a second algorithm which only needs half as many multiplications:

Ax = x*cos(A) + z*sin(A)
Ay = y
Az = -x*sin(A) + z*cos(A)

Bx = Ax
By = Ay*cos(B) + Az*sin(B)
Bz = -Ay*sin(B) + Az*cos(B)

Sx = Bx*cos(C) + By*sin(C)
Sy = -Bx*sin(C) + By*cos(C)
Sz = Bz


We now have the formulas for rotation of objects. For the object to be painted on a computer screen with a bit of realism you need to have it converted to 2D with perspective.


2. Calculate 3D -> 2D (computer screen) with perspective

If you paint a 3D object with only the X and Y coordinates on a computer screen the picture is lacking in depth and you have no perspective. To get rid of that you need to use the Z coordinate when calculating the X and Y coordinates of point to paint on screen.

H = 1 - Sz/D
Px = Sx/H
Py = Sy/H

D is a value of depth, shall be bigger than 1. Begin to set D to a high value (100-1000) and experiment until depth of picture looks good.
Px and Py is coordinates of point to paint on screen.




3. Example of rotating algorithms in C

It's allways easier to understand something when you have an example to look at. In this C example the algorithms to use is implemented in functions and a cube is initiated. The program has no main function and the painting routines are missing.


#include <math.h>


// Calculate coordinates after rotation
void calculate(const int fig[][3], int matrix[][2], double angle_a, double angle_b, double angle_c, int numb, int depth)
{
  int s;
  double H, Sx, Sy, Sz;
  double cos_a = cos(angle_a);
  double sin_a = sin(angle_a);
  double cos_b = cos(angle_b);
  double sin_b = sin(angle_b);
  double cos_c = cos(angle_c);
  double sin_c = sin(angle_c);

  for(s = 0; s < numb; s++)
  {
    // Algorithm 1
    Sx = fig[s][0]*(cos_a*cos_c + sin_a*sin_b*sin_c) + fig[s][1]*(cos_a*sin_c - sin_a*sin_b*cos_c) + fig[s][2]*(sin_a*cos_b);
    Sy = fig[s][0]*(-cos_b*sin_c) + fig[s][1]*(cos_b*cos_c) + fig[s][2]*(sin_b);
    Sz = fig[s][0]*(-sin_a*cos_c + cos_a*sin_b*sin_c) + fig[s][1]*(-sin_a*sin_c - cos_a*sin_b*cos_c) + fig[s][2]*(cos_a*cos_b);

    H = 1 - Sz/depth;

    matrix[s][0] = Sx/H;
    matrix[s][1] = Sy/H;
  }
}


// Rotate the object, angles in radians
void rotate(double angle_a, double angle_b, double angle_c)
{
  // A cube with side 40
  const int fig[8][3] = {{20,20,20},{-20,20,20},{-20,-20,20},{20,-20,20},{20,20,-20},{-20,20,-20},{-20,-20,-20},{20,-20,-20}};

  // The points to draw, changed inside the calculate function
  static int matrix[8][2];

  // First time boolean
  static bool bFirstTime = true;

  // Clear object on screen
  if(!bFirstTime)
    draw(matrix, backcolor);

  // Calculate new coordinates, depth = 300, 8 points
  calculate(fig, matrix, angle_a, angle_b, angle_c, 8, 300);

  // Draw object on screen
  draw(matrix, drawcolor);
  
  bFirstTime = false;
}

4. Receive coordinates in 3D from a picture in 2D with perspective

Let's imagine that you have a 3D object painted on a computer screen. The object has the same Z coordinate in every point and could be a planar rectangular object. Let's then imagine that you are pointing with the mouse on the object and want the coordinates of the mouse pointer inside the object.

The coordinates of the mouse pointer on the screen is only the drawing coordinates, Px and Py from chapter 2. The coordinates you want to know is x and y in the figure (you allready know z, it's the same everywhere). To know x and y we need to derive some new functions.

The only coordinates we have from the mouse pointer is Px and Py, which give us:

Sx = Px*H
Sy = Py*H
Sz = (1-H)*D

In chapter 1 we learned that the matrix equation My * Mx * Mz * MX = Ms gaved us three different formulas for calculating Sx, Sy and Sz. Now when we are going to have Sx, Sy and Sz (matrix Ms) we can calculate x, y and z (matrix MX):

MX = inv(Mz) * inv(Mx) * inv(My) * Ms

Be aware of the order of the matrices in the formula above. They must be in this order to reflect the rotation formula in chapter 1.

Because of the simplicity of the rotating matrices we can easy calculate the inverse matrices used in the formula.

          |cos(C)  -sin(C)  0|
inv(Mz) = |sin(C)   cos(C)  0|
          |  0        0     1|

          |1    0        0   |
inv(Mx) = |0  cos(B)  -sin(B)|
          |0  sin(B)   cos(B)|

          |cos(A)  0  -sin(A)|
inv(My) = |  0     1     0   |
          |sin(A)  0   cos(A)|
The matrix equation to solve is:
|cos(C)  -sin(C)  0| |1    0        0   | |cos(A)  0  -sin(A)| |Sx|   |x|
|sin(C)   cos(C)  0| |0  cos(B)  -sin(B)| |  0     1     0   | |Sy| = |y|
|  0        0     1| |0  sin(B)   cos(B)| |sin(A)  0   cos(A)| |Sz|   |z|
x = Sx*(cos(A)*cos(C) + sin(A)*sin(B)*sin(C)) + Sy*(-cos(B)*sin(C)) + Sz*(-sin(A)*cos(C) + cos(A)*sin(B)*sin(C))
y = Sx*(cos(A)*sin(C) - sin(A)*sin(B)*cos(C)) + Sy*(cos(B)*cos(C)) + Sz*(-sin(A)*sin(C) - cos(A)*sin(B)*cos(C))
z = Sx*(sin(A)*cos(B)) + Sy*(sin(B)) + Sz*(cos(A)*cos(B))

Now we can derive an equation for H that is only dependent of Px, Py, D, z and the angles A, B and C:

z = Px*H*(sin(A)*cos(B)) + Py*H*(sin(B)) + (1-H)*D*(cos(A)*cos(B)) = constant
=> H(Px*(sin(A)*cos(B)) + Py*(sin(B)) - D*(cos(A)*cos(B))) + D*(cos(A)*cos(B)) = z
=> H = (z - D(cos(A)*cos(B))) / (Px*(sin(A)*cos(B)) + Py*(sin(B)) - D*(cos(A)*cos(B)))

Now we can calculate H with Px and Py as the coordinates of the mouse pointer. When we have H we can easy calculate Sx, Sy and Sz and then receive x and y with the formulas above.