We finally go 3D! All the prep I did with the mesh class last time pays off big time. But going from 2D to 3D is not as straightforward as adding a z coordinate; I had to consider the presence of a camera and a whole bunch of transformations between going from local space to the screen.
I started by adding the z coordinate to the mesh representation and making the appropriate changes so that this is propagated to the vertex shader. Following this I created a struct representing the camera in the scene. The struct consists of a rigid body that contains information about the camera’s position and orientation, the field of view, the aspect ratio, the near plane position and the far plane position.
Now with the fundamental structures prepared, the next step is to do all the transformations. Let’s get some terminology out of the way first: the space in which the vertices exist is called the model space, the vertices are all relative to the local origin which exists in this space; the model itself exists in a larger world and we call this world space; the space that is visible to the camera is called the camera space; the 2D space onto which the 3D meshes are projected onto is called the projected space or screen space.
All this while we’ve been submitting the sprites and mesh vertices in model space. In order to take the mesh and put it on the screen as seen through the camera, we need to do some transformations. The first transformation is from local to world space, which will transform the vertices from their local space into their position in the world. The second transformation is from world space to camera space and the third and final transformation is from camera space to the projection. The projection transformation takes into consideration, the camera’s field of view, aspect ratio and the positions of the near and far planes. All these transformations involve a lot of calculations and are faster if executed by the graphics card, so we do them in our vertex shader before we output the final vertex location. HLSL and GLSL have different functions that do the actual multiplication of matrices that result in these transformations, and since we have platform independent code in our shaders I had to implement a macro that would hide the platform specific implementations.
Now that we have the object on screen we need to enable depth buffering and also clear the depth buffer each frame. This allows us models to overlap and pass through each other instead of just being drawn on top of each other. The final result can be seen below.
If you want to play around with moving the cube you can use the arrow keys. You can also move the camera, the controls are:
Home: Move forward in Z
End: Move backwards in Z
Page Down: Move Right
Delete: Move Left