This is part of the Rendering Tutorial

Mapping 3D to 2D

In this section, I'll explain how the data of the house is projected on a 2D viewport.

All faces of the house can be kept in data structures, and they're mainly containers of 3D coordinates. I won't go into explaining how I did it, or how you could keep these 3 dimensional objects in a data structure in your rendering engine. The main goal of a renderer is to tranform and project the 3D coordinates of the objects to the 2D screen. Connecting the dots, and displaying faces and such, is up to you. I'll explain what can go wrong and what you should look after when displaying faces later. It's not as easy as simply connecting the dots, but we will assume everything will go right for now.

So the main thing we want to do is to transform the 3 dimensional coordinates to coordinates in a 2 dimensional plane. And oh boy, this is easy. At least, this is true if we make a very important assumption, an assumption I'll keep during this introduction in rendering. We'll keep the camera at a fixed position. the camera always looks up the Z direction, and always looks perpendicular to the XY plane. The camera is placed in such a position that it sees the axes on the XY plane similar to the X and Y axes on a computer screen. Namely, the X direction on a computer screen goes from left to right, where the Y direction goes from the top of your screen to the bottom of your screen. This latter is different from Y directions you've met in math classes, as those Y axes always point upwards. The camera thus perceives the XY plane similarly as we're looking at a computer screen. That makes things easy.

Now think along with me for a minute. As we're looking along the Z axis, all objects that should be rendered on our screen appear smaller as their coordinates have bigger Z values. Similarly, objects appear bigger as their Z values are smaller, since their coordinates are closer to the camera. In order to transform 3D coordinates to 2D coordinates, we can thus simply divide the X and Y values in the coordinates by the Z value in order to display them as X and Y coordinates in a 2 dimensional view.

In code, this becomes something like

x2d = x3d / z3d;
y2d = y3d / z3d;

Told you this was easy.

But there are some snappy details you should take care off, or your projection of the 3D data will look pretty skewed.

• First of all, always make sure that the center of your 3D objects is close to the origin of the 3D space. The camera looks straight to the origin of the space, as depicted by the axes in our world view. If all the objects would have coordinates with strictly positive X, Y and Z values, your objects would appear on the screen seen from the side, not aimed right to the object. It'll also makes things easy when rotating objects later on to view them from a good angle. Centering is everything.
• But this centering creates another problem. As we're simply dividing the X and Y values by the Z value, and all verteces are clustered around the origin of our space, some Z values will be negative, and dividing a X, Y coordinate by this negative Z value will not make it bigger (as these coordinates are supposed to be closer to the camera) but it will reverse the sign of our X and Y coordinates and we would be scaling these coordinates the wrong way. In order to overcome this problem, we add a number to the Z value so all Z values are strictly positive before we divide the X and Y values by this Z. This extra number is called a distance. It simulates how far the camera is from the origin. make the distance larger, and the X and Y coordinates will be divided by a larger number, making the object to appear further away from the camera. Easy as cake. Also make sure that all the Z values (plus the distance) are always strictly positive (meaning no zeroes), as you don't want to be dividing the values of your coordinates by 0. That would be plain dumb.
• After you've computed the 2 dimensional X and Y values of the projected coordinates, it might be a good idea to scale the new coordinates up or down to make sure that the objects fit your viewport. You don't want to be looking at a small cluster of pixels depicting your 3D object, or you don't want to be looking at a completely blown up projection of your objects that don't fit the screen. Depending on the size of the values of the 3 dimensional coordinates and the distance you're using to compute your 2D coordinates, you should be able to figure out a scaling factor of your 2D coordinates so it fits your screen.
• The last detail I'd like to point out is a last step you can make to make your objects appear on the screen as you want to. The origin of a computer screen sits at the top left of your screen or your viewport. If we would draw our house on the screen, it would appear around the top left corner of your screen, as we have put the 3D objects around the 3D origin. Simply add an X and Y offset to your computed 2D coordinates in order to put the projected objects in the middle of the screen. The X and Y values of the center of your screen, measured in pixels, is often the best offset.

Putting it all together, your resulting code for projecting a 3D coordinate to a 2D coordinate using a camera following our assumption will look like the following:

x2d = xOffset + scale * x3d / ( z3d + distance );
y2d = yOffset + scale * y3d / ( z3d + distance );