QBXL Technical Articles

3D Tutorial #2

By Xerol

Instead of going over how to make your own "objects" today, I've decided to take a step back and detail what's needed in choosing the environment variables(EVs for short).

In order for the frames of the movie to display correctly, the camera has to be in the right place at the right time, and looking at whatever's going on. So, a certain amount of planning needs to go into this before you even put down your first piece of code.

Starting today, and continuing through the other tutorials, I will be using a sample 'movie' to demonstrate what is needed to create your own.
In this sample, there will be several things:

1) A generic room, with walls, ceiling, and floor. The ceiling will be 'shaded' to represent a lightsource in the center of the room, the walls will have a 'castle' texture to them, and the floor will be made up of alternating white & black tiles.
2) Inside this room there will be several objects:
    a) A cube, which has 6 different colored sides and slowly rotates about the z-axis.
    b) A sphere, which will 'bounce' around the walls, and perhaps the cube(or it might just go through the cube).
3) In addition to the walls, there will be several doors around the room, the actual lightsource in the middle of the ceiling(represented as a chandilier), windows, and 'raised' lettering on the walls.

Now, a quick note before we continue: The 'objects' that are created don't really exist as such, instead they are plotted point-by-point on the screen, every time there is an iteration. The cube would be represented by 3 sets of nested For-loops, as will be detailed in tomorrow's exercise.

OK, so we have to figure out where we're looking into the room. In order to do this, we have to find out where the 'room' actually IS. So, we go to a little diagram:

                          x
 -10                      0                      +10
 +----+----+----+----+----+----+----+----+----+----+ +10
 |                        |                        |
 |                        |                        |
 +                        +                        +
 |                        |                        |
 |                        |                        |
 +                        +                        +
 |                        |                        |
 |                        |                        |
 +                        +                        +
 |                        |                        |
 |                        |                        |
 +                        +                        +
 |                        |                        |
 |                        |                        |
y+----+----+----+----+----+----+----+----+----+----+ 0  y
 |                        |                        |
 |                        |                        |
 +                        +                        +
 |                        |                        |
 |                        |                        |
 +                        +                        +
 |                        |                        |
 |                        |                        |
 +                        +                        +
 |                        |                        |
 |                        |                        |
 +                        +                        +
 |                        |                        |
 |                        |                        |
 +----+----+----+----+----+----+----+----+----+----+-10

Now, no matter what your movie is about, the units you use are completely up to you. We'll use 20x20 as the room size, centered at the 'universe origin', or (0,0,0). This diagram represents an overhead view of the room. Every + on the walls represents 2 units.

To a beginner, putting the camera smack in the center of the room, and rotating the camera 360 degrees throughout the progress of the movie sounds like a good idea. But, (and this is the voice of experience) you almost NEVER want the camera in the middle of the action. The reason for this is a 'flaw' in the program(mostly just laziness on my part, and partly because I couldn't find a poly-filler that would meet my requirements for the z-buffer) that EVERY OBJECT IN THE WORLD HAS TO BE PUT DOWN POINT-BY-POINT! (I will get more into this tomorrow, but a small explanation is needed.)  Because of this, if the camera is in the middle of the action, even with a high S-value, the points near the 'base' of the camera will be sporadic and have lots of holes, or perhaps even be just a few pixels among a lot of black.

This means that the camera should be 'safely out of the way'. (You don't want the camera to be hit by that bouncing sphere anyway. Do you have any idea how expensive those things are?) So, we have several choices as to where to put the camera:

1) Outside any of the 'sides' of the room.
2) Outside any of the 'corners' of the room.

Choice one sounds appealing, and choosing a side comes down to a certain decision: What will show the most action in the room? In order to have the camera be 'outside', we will have to skip over rendering one side of the room, which is a good thing because that means less render time. It's also a good choice because there will be nothing 'behind' the camera to NOT see. (With the camera in the middle of the room, half of the room is always behind you, meaning a lot of wasted render time.)

Choice two is also fairly easy to implement, with an added bonus: Only two walls need to be rendered, cutting time almost in half for the wall rendering. But, as there are going to be objects on all four walls(i'll place those in the next diagram) half of the 'details' will be missed-out on.

And we have one more thing to consider: The camera doesn't have to be fixed.

So, we go into brainstorming mode and come up with an idea: Let's make the camera move! The camera will start out at around (-20, 0) and move diagonally, across the lower-left corner of the room, and finally end up at (0, -20). At the same time, the horizontal camera rotation will go from +90 degrees(facing 'right') to 0 degrees(facing 'up'). The camera's path is shown in the following diagram, as well as a few of the wall objects:

                           x
 -20                       0                      +20
 +----+----+--|--+----+----+----+----+--|--+----+----+ +20
 |                         |                         |
 |                         |                         |
 +                         +                         +
 |                         |                         |
 |                         |                         |
 +                         +                         +
 |                         |                         |
 -            +---B---B---B+--B---B---B-|            -
 |            |            |            |            |
 +            |            +            |            +
 |            |            |            |            |
 |            C            |            |            |
 +            C            +            |            +
 |            C            |            |            |
 |            |            |           A|            |
y+1---+----+--|--+----+----0----+----+-A|--+----+----+ 0  y
 | \          |            |           A|            |
 |  \         |            |            |            |
 +   -\       |            +            |            +
 |     \      |            |            |            |
 |      -\    |            |            |            |
 +        \   |            +            |            +
 |         \  |            |            |            |
 -          -\+------DDDDDD+------------|            -
 |            \            |                         |
 +             \           +                         +
 |              -\         |                         |
 |                \        |                         |
 +                 \       +                         +
 |                  -\     |                         |
 |                    \    |                         |
 +----+----+--|--+----+----2----+----+--|--+----+----+-20


Note: This is an ascii diagram for crying out loud, did you expect it to look good?

Also note that I 'zoomed out' a little for this diagram, putting the room in the middle at half-size. The backslashes show the path of the camera, which should end up where the number '2' is after starting out at number '1'. Other things on this diagram:
A: This is a door.
B: Windows.
C: Another door.
D: Raised text on the wall.
0: Lightsource in the middle of the room.


Now, to have the camera move along this path, we need to generate an equation:

Y = -X-20

Except as we go through the movie, we have the 'iter' variable(or basically a "t" as in parametric equations). Before we create the equations, we have to figure out how many frames we will be doing in this movie. Assuming 25 FPS, we'll do 200 frames for 8 seconds of video. So, we get these equations:

ViewX = -20+Iter/10
ViewY = -Iter/10

Before we run this, make sure to test your variables in a chart to make sure everything lines up correctly:
  Iter  |   X     |   Y
--------+---------+-------
   0    |  -20    |   0
   1    | -19.9   |  -.1
   2    | -19.8   |  -.2
   3    | -19.7   |  -.3
   4    | -19.6   |  -.4
   5    | -19.5   |  -.5
  ...   |  ...    |  ...
  199   |  -.1    |  -19.9
  200   |   0     |  -20

Success! Note that most of the time you won't get these right on the first try.

For ViewZ, the room is 10 units high, so we want to center the camera about that:

ViewZ = 5

Now, to the camera rotation. We already said we wanted the camera to rotate from 90 to 0 over 200 frames. So, here's what I got:

HorCamRot = (90-(Iter / 200 * 90)) * (3.141592/180)

Basically, here's how that works:
1) Iter / 200 breaks it down into a 'percentage of frames done'.
2) Multiplying by 90 makes it range from 0 to 90 instead of 0 to 1.
3) Subtract that number from 90 to 'invert' the range(90 to 0 instead of 0 to 90).
4) Convert to radians.

Vertical camera rotation is unneccessary here.

VerCamRot = 0 * (3.141592/180)

The last important thing is the Zoom. This is what probably takes the most trial-and-error. Basically, with Zoom of 1, a 'wall' of points one unit away from the camera will show up as being one pixel apart on the screen(like normal Pset if the camera is in 'standard position' (0,0,0 with no rotation)). So, we have to figure out how far away the room is.

On the first iteration(iteration 0) the closest point of the room is 10 units away, and the farthest is 30. (Well, the farthest are the far corners, but i'm simplifying things here a little). In order for the nearest points to be one pixel apart, a zoom of 10 would be needed UNDER NORMAL STEPPING! But this would make the room only 20 pixels wide on the screen. In order for it to take up all of the screen, the zoom should be 32 times that, or 320.

Well, that's fine for the first and last frames, but what about those in the middle? Note that you get closer to the room in the middle frames, getting closest at frame 100:

  Iter   |   X     |   Y
---------+---------+-------
   99    |  -10.1  |  -9.9
  100    |  -10    |  -10
  101    |  -9.9   |  -10.1

The camera is actually ON the lower-left corner of the room. With a horizontal rotation of 45 degrees, the apparent width of the room is the distance from one corner to the opposite. Using the pythagorean theorem gives us:

Sqr(20^2 + 20^2) or Sqr(800) which is 20*sqr(2) = 20*1.414 so about 28 units.

Assume 32 units because the screen is 640 wide, and we don't need to take up the WHOLE screen all the time. But remember that that 'line' isn't at the camera, but instead
sqr(10^2 + 10^2) or 10 * sqr(2) or about 14 units away. So zoom needs to be about 20 * 14 or 280.

Once again, the zoom doesn't need to be fixed but if you want it to be simple use a zoom of 300 for the whole thing:

ZoomX = 300
ZoomY = 300

But, if you want to get technical, so will I. You basically need to create equations for these, where the zoom goes from 320, down to 280, and back up to 320 again. This sounds like a job for...ABSOLUTE VALUE! This equation gets a little technical in places but I think you can figure it out:

ZoomX = Abs(iter - 100) / 5 * 2 + 280
ZoomY = Abs(iter - 100) / 5 * 2 + 280

And we're done. Except for one thing: the S value. But, as we haven't actually put any code in to use it yet, I'll leave that explanation until tomorrow.


Until then, happy programming,

--Xerol Is a good man. A good man who is going to kill me for using this old tutorial!

       
  Next issue: Your first objects.

Basically, we'll put the walls, ceiling, and floor down, as well as
doors and windows.