-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- INTRODUCTION TO 3D SERIES #1 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
Writer: Matthew River Knight
Have you ever played Quake and wished you could make a similar game in Qbasic? Have you ever tried to learn how to code a 3d program in Qbasic, only to find that there are no good tutorials out there? Yep? Well then this tutorial is just for you! ^_^
I remember when I wanted to learn how to do 3d, it was a total nightmare. I went to every shop I knew of in search of a book - they had books on the subject, but they were all aimed at C programmers...there was nothing for Qbasic coders such as myself. Next I decided to search around the internet for a decent tutorial. I found several tutorials, but to my alarm, they were all so complicated! I'm sure that some of you have, at some stage, downloaded a certain 3d tutorial by Matt Bross. What a mistake that was eh?! It is a pathetic excuse for a tutorial. It consists only of extremely complicated and difficult to read code, then followed by a very brief explanation of what it does, and an even briefer explanation of how it goes about achieving this.
In order to fill the obvious void in the QB world for a decent 3d programming tutorial, I have decided to do a series about it right here in QBCM! We are going to take things step-by-step, progressing from the very simplest aspects of 3d programming, right up to the more complicated, but extremely fun stuff! ^_^
Don't worry if you aren't good at maths, or aren't a programming guru. I am going to explain things in such a way that they are going to be very easy to understand, regardless of your ability! I do however recommend that you have at least an intermediate knowledge of Qbasic. As far as the maths is concerned, there is a large misconception amongst the programming circles that in order to do 3d, you have to be a genius with mathematics. Not so. You will need to have a good knowledge of Algebra, Geometry, and Analytical Algebra. That's it. It does help to know Trigonometry, but it is not a necessity.
Okay, I have rambled on long enough! Let's start learning how to program in 3d!!! To begin with, we need to know how to define a point in a 3d world. You already know how to define a point in 2d - it's simple...you have your X coordinate to describe the point/pixel's location across the screen, and you have your Y coordinate to describe the point/pixel's location down the screen. In 3d it works exactly the same way, however, we have to add another coordinate: Z. The Z coordinate describes the depth, or in other words, how far the point/pixel is away from the viewer. In a 3d world, the exact center of the world is X,Y,Z = (0,0,0). So, a point that is 100 pixels to the left of the center of the 3d world would be at (-100,0,0). A point that is 50 pixels to the right of the center of the 3d world, 100 pixels higher than the center of the 3d world, and 10 pixels closer to the viewer than the center of the 3d world, would be at (-50,-100,10). All of the points in your 3d world are collectively called the world coordinates.
Using the above method, it is easy to create a 3d world. If, for example, you wanted a quadrilateral (such as a cube) in your 3d world, you would compose it by defining points which are the vertices of the object. Later on, when actually drawing the object on the screen, we connect all of the points/pixels together by using the LINE statement. This gives us what is called a wireframe representation of the object. But, before we can draw our 3d objects on the screen there's some stuff you have to know - we'll cover that at a later stage.
Once we have defined all of our world coordinates, we have to place the camera in the 3d world. The camera (also called the eye or the viewer) is what actually looks at the world. If you place it high above the 3d world, then you see the world in much the same way that it would be seen from a high tree, or a building. If you place the camera much lower down, then you will see the 3d world from an ants point of view! The camera's position is defined in exactly the same way as the world coordinates: you describe the camera's position in the 3d world with X, Y, and Z coordinates. It is important to note that the camera will ALWAYS 'look' to the center of the 3d world (0,0,0).
Now that we have defined our world coordinates, and have defined our camera position, we have to find a way to plot the 3d world on the screen, relative to the camera's point of view. Now the most obvious question is "how on Earth do I plot a 3d object on a 2d surface like the screen??!!" Well, let's take this one step at a time.
Okay, let's say we have defined a single point in a 3d world where:
x_world = 100 y_world = 20 z_world = 10
The first step in getting our 3d world onto the screen is changing these world coordinates into what is called the camera coordinates. The camera coordinates describe the world coordinates as they are seen from the camera's point of view.
Now, in order to change the world coordinates into camera coordinates, we have to use a set of mathematical formulae. "Nooooo!" I hear you say. Heheh, don't worry, it isn't difficult at all...and I am going to explain how it works. ^_^
Now let's think about this logically. Suppose we are in a 3d car racing game. Our camera (which is basically the same as the person inside our car in the game) is at (100,10,10) and is looking at the center of the 3d world (0,0,0) (you will recall that the camera in a 3d world will always look to the center of this world). With the camera at this position, the center of the world is at (-100,-10,-10). From this information, it seems that:
x_camera = x_world - camerapos_x y_camera = y_world - camerapos_y z_camera = z_world - camerapos_z
x_camera = x coordinate of a point (camera coordinate system). Ditto with y_camera, and z_camera. x_world = x coordinate of a point (world coordinate system). Ditto with y_world, and z_world. camerapos_x = x coordinate, describing the position of the camera in the 3d world. Ditto with camerapos_y, and camerapos_z.
And guess what...those equations are actually the equations we use to convert world coordinates into camera coordinates!!! ^_^
It is important to note that you can place the camera anywhere in the 3d world, except for one place - you CANNOT place the camera in the center of the 3d world (0,0,0). You can put it anywhere but there. Why? Well, since the camera will always look to the center of the world, if the camera was in the center of the world it would be looking at itself. Can you look at your eyes??!! Nope, of course not (not without a mirror anyway.) So you can't put the camera at (0,0,0). If you do put the camera in the center of the world, then some of the mathematical formulae which we are about to describe will not work, and the program will crash.
Okay, so now we have converted our world coordinates into camera coordinates. But we still can't draw our 3d world/object(s) on the screen yet. We still have points which are described by X, Y, and Z, but the PSET and LINE statements can only draw with points defined by X and Y (which is 2d) - this is logical really...the screen is, after all, a 2d surface.
In order to draw in 3d on the screen using PSET or LINE, we have to convert our camera coordinates (which describe the 3d world coordinates relative to the camera's point of view) into 2d screen coordinates. How? Simple. We use a set of mathematical formulae!!! (Do I hear groaning?! ^_^)
Once again, we have to think about the problem at hand with a bit of logic and common sense. In real life, objects that are further away from us look smaller than they do when you are right next to them. The principle is the same with 3d computer graphics. For example, imagine that in our 3d world we have two points/pixels that are 10 pixels length apart. If their Z coordinates are both decreased by 1, then the two points are each going to move one pixel length closer to the other. This information demonstrates the golden wisdom of every 3d programmer: "The screen coordinates could be calculated by dividing the X and Y position through the depth (z coord- inate)."
This shown as a formula is:
x_screen = x_camera / z_camera y_screen = y_camera / z_camera
x_screen = x coordinate of the pixel on the screen. y_screen = y coordinate of the pixel on the screen. x_camera, y_camera, z_camera = (see above).
However, we have several different screen modes at our disposal, and (0,0) on the screen is actually the top left hand side of the screen, so we have to change the formulae a little if we want the 3d object to be centered on the screen and if we want the program to work in all screen modes. The final set of formulae for conversion of camera coordinates to screen coordinates is as follows:
x_screen = (x_camera / z_camera) * srx + srx / 2 y_screen = (y_camera / z_camera) * sry + sry / 2
srx and sry describe the screen resolution. In SCREEN 13, srx would be equal to 320, and sry would be equal to 200.
Another very important thing that I must mention is that if any of our points in the 3d world go behind the camera, or in line with the camera, then the math for projection of the camera coordinates onto the screen goes all haywire. This is logical really. In real life, if an object is behind you, or in line with your eyes on the side, then you cannot see it. This is the same with 3d computer graphics, but the problem is that the computer doesn't yet know that we must not be able to see these points, so it attempts to use the math on them anyway, and it goes nuts!!! ;)
We'll say that points in our world coordinates that are in line with or behind the camera are "out of range". In order to fix this problem, it is necessary that we do a test on all of the points in our 3d world (the world coordinates) to see if they are "out of range" and if they are, then we have to temporarily eliminate them. When they are back in range again, then we can put them back and have them plotted on the screen. Let's explain this another way: if the value of the world Z coordinate of a point is equal to camerapos_z or greater than camerapos_z then it is "out of range" and we must eliminate it (ie. we don't draw it!) Simple eh?! Here's some code for this:
IF x_world >= camerapos_z THEN 'Don't PSET this point!
Please note that when you are doing a 3d engine you should NEVER use PSET do draw everything in the world. That would be terribly slow. LINE is a much faster alternative, however, I used PSET here in order to make the explanation easier to understand.
Well, this concludes the first entry into the INTRODUCTION TO 3D SERIES. I hope you enjoyed it! Next issue we'll be covering 3d rotation, how to code a 3d program in which you can 'walk' about the world, and hidden surface removal. We'll also add some source code to help ya out! ^_^
Cya next month!