As a programmer, I often find myself trying to write things that already exist. It gives some satisfaction to say, "I did this on my own." even if it is not as good as what is already out there. Nothing is more impressive than your own work, even if it is not as good. It's an accomplishment. I often times find myself making things like this to settle the curious question, "How does this thing work?!".
This tutorial is targeted at the beginner wanting to get into 3D rendering without using pre-made 3D engines. This is not by any means the only or best way to do 3D graphics.
Section 1: Vertices, Vertexes, what are they and how are they defined?
A vertice is the location of a point on the X axis, Y axis, and Z axis. These are !!NOT!! screen coordinates. the X, Y, and Z coordinates are defined by the distance away from the center. Lets map a cube, which is 8 vertices. In the art below, C represents the center, which is 0,0,0. The numbers show where each vertice is located on the cube.
| | C | |
Vertice 1 = -50 -50 -50
Vertice 2 = -50 50 -50
Vertice 3 = 50 50 -50
Vertice 4 = 50 -50 -50
Vertice 5 = -50 -50 50
Vertice 6 = -50 50 50
Vertice 7 = 50 50 50
Vertice 8 = 50 -50 50
Vertexes are a map to a screen coordinate, this is in X,Y. Let's assume our drawing screen is 640x480. We want to know where Vertice 1 is on the screen. Well to do this we need to do a Perspective Transform (which will be elaborated in the next section) to convert a 3d point to a 2d point on our screen. both 500's are a z scale, to avoid the nasty complication of z = 0.
xp = -50 * 500 / (-50 + 500)
yp = -50 * 500 / (-50 + 500)
xp = -55.555556
yp = -55.555556
Now, you might ask, why did we assume our screen size was 640x480? Simple, we want to center our object on the screen, so we need 640/2=320 and 480/2=240
Vertex 1.x = 320 + -55.555556
Vertex 1.y = 240 + -55.555556
Vertex 1.x = 264.444444
Vertex 1.y = 184.444444
we now know where at the 3d point is at relative to our view space which is only 2d. We have a 2d Coordinate. Obviously you need to round the x,y since a screen coordinate cant be in decimals, its always an integer.
Section 2: Perspective Transforms (Projections)
All current 3D cards lack what is commonly referred to as a "Geometry Engine". What this basically means is that they do not understand the concept of true 3D graphics. It's up to you, the programmer, to use this component to alter the x & y values to give the illusion of 3D on a 2D screen.
This trickery is done by using a perspective transform to alter the x & y values according to the z value. Basically, the further objects are from the viewer, the smaller they become. This can be approximated by the following equation:
xp = x / z
yp = y / z
As you can see, as z gets larger, x & y get smaller (ie closer together). It's fairly obvious that if z=0 then this equation will fail in a nasty manner. This situation can be partially avoided by the following:
xp = x * scale / (z + scale)
yp = y * scale / (z + scale)
Section 3: 3D Rotational Matrices
There is not much to describe in this section. Below are our rotational matrices. The next section will elaborate more on the use of these matrices, so just bounce between this section and the next so you grasp the concept of what I am illustrating to you.
| Rotational X Matrix |
1 0 0 0
0 Cos(Theta) -Sin(Theta) 0
0 Sin(Theta) Cos(Theta) 0
0 0 0 1
| Rotational Y Matrix |
Cos(Theta) 0 Sin(Theta) 0
0 1 0 0
-Sin(Theta) 0 Cos(Theta) 0
0 0 0 1
| Rotational Z Matrix |
Cos(Theta) -Sin(Theta) 0 0
Sin(Theta) Cos(Theta) 0 0
0 0 1 0
0 0 0 1
Theta is a degree, which is a number between 0 and 359. Why not 360 you ask? Simple. 360 is a complete rotation, so 0 and 360 are the same thing.
Theta needs to be in radians. A simple method of converting degrees to radians is:
(Deg * PI) / 180
Example: (1 * 3.14159) / 180 = 0.017453
Some programming languages do not have PI built in, in this case, PI can easily be calculated by:
PI = ATAN(1) * 4, or you could just set a constant, 3.14159
Section 4: Multiplying a Matrix to a Vertice
Ok, I am going to define a matrix call by "m". m1-1 will represent line 1, position 1. Here is a basic Chart, so you can follow along if you dont understand.
m1-1 m1-2 m1-3 m1-4
m2-1 m2-2 m2-3 m2-4
m3-1 m3-2 m3-3 m3-4
m4-1 m4-2 m4-3 m4-4
x = (x * m1-1) + (y * m1-2) + (z * m1-3) + m1-4
y = (x * m2-1) + (y * m2-2) + (z * m2-3) + m2-4
z = (x * m3-1) + (y * m3-2) + (z * m3-3) + m3-4
w = (x * m4-1) + (y * m4-2) + (z * m4-3) + m4-4
Lets do an example: We will use a Vertice, and a X Matrix which we passed one degree to:
x = -50
y = -50
z = -50
1 0 0 0
0 0.999848 -0.017452 0
0 0.017452 0.999848 0
0 0 0 1
so, using our multiplication definition above:
x = (-50 * 1) + (-50 * 0) + (-50 * 0) + 0
y = (-50 * 0) + (-50 * 0.99984 + (-50 * -0.017452) + 0
z = (-50 * 0) + (-50 * 0.017452) + (-50 * 0.99984 + 0
w = (-50 * 0) + (-50 * 0) + (-50 * 0) + 1
so you should get this result:
x = -50
y = -49.1198
z = -50.865
w = 1
Because of our pre-defined rotational matrices, w will ALWAYS be 1, so this is not important to calculate.
Section 5: A Basic step by step description of how your program should work
Ok, Lets assume you have all cube vertices stored, the first step in rotating our cube is to multiply ALL the vertices by the rotational matrices. lets rotate on the X, Y, and Z axis by one degree. Heres the steps of what your program should do.
Step 1: Calculate X Matrix by our degree (which needs to be converted to radians)
Step 2: Calculate Y Matrix by our degree (which needs to be converted to radians)
Step 3: Calculate Z Matrix by our degree (which needs to be converted to radians)
Step 4: Multiply our Vertice by the X matrix.
Step 5: Take the results, (new x,y,z) and multiply those by the Y matrix.
Step 6: Take the results, (new x,y,z) and multiply those by the Z matrix.
Step 7: Take the results, (new x,y,z) and do a perspective transformation.
Step 8: draw the Vertex on the screen. (our perspective transformation)
Step 9: Repeat step 4-8 for the other 7 vertices
Later, you should also save the vertexes, so you don't need to re-calc them when you want to draw faces, which is in the next section.
Section 6: Defining faces of our 3D object
Ok, so we got our vertices, and we know how to project them on to our 2D screen. Now we need to play connect the dots, and draw our faces. Lets go back to our cube diagram so we can map our faces. as you know, a cube has 6 faces.
| | C | |
Face 1 = 1 2 3 4 1 = Back
Face 2 = 2 6 7 3 2 = Bottom
Face 3 = 3 7 8 4 3 = Right
Face 4 = 1 5 6 2 1 = Left
Face 5 = 1 4 8 5 1 = Top
Face 6 = 8 7 6 5 8 = Front
Lets take Face 1, and define what these numbers mean! I will use "P" to define a point. basically, we draw a line from P1 to P2, from P2 to P3, from P3 to P4, and from P4 to P1 and we have one side!
The point is the vertex of a vertice, which is a perspective transformation.