Introduction:
In this blog I will be writing about how to solve the perspective problem for a 3D video game. I will be showing how I came to my solutions and explaining what I did, this will not be a code along tutorial on how to solve the problem. I will be first explaining how I solved the perspective problem. In my solutions I have only used C++, SDL2 and modern OpenGL so I had to write the maths myself.
Finally, there is maths involved but it’s only in concept and you wont be required to do any maths to understand what I am talking about. However a little understanding of Linear Algebra will help. In the final section I talk about the code I used, a little understanding of Object Orientated Programing will help you there to.
Understanding Perspective for Video Games:
What is perspective in a video games? When rendering 3D objects in space you need to be able to judge depth and this is what perspective allows you to do in real life and in video games. Effectively you need to see objects in the distance to be smaller and objects closer to the player to be bigger. Without perspective everything looks the same size no matter how far they are, which is very bad.
The two solutions:
There are normally multiple ways of solving problems in maths and especially in computing, but some are better than others. One solution would be to scale everything depending on the distance it is from the camera. Sounds great right? No, this is a horrible idea doing this for millions of object would cause massive strain on your game and you would be rewriting the same code over and over again, which is always bad. The best way to solve the problem is using the View Frustum and matrix multiplication this allows us to apply linear system of equations very easily and this comes with the added benefit of 3D object no longer have to care about scaling.
The Algebraic View of the View Frustum:
Now I have completed the introduction and I can talk about the View Frustum properly. We need to first define the View Frustum which is a space were everything in the scene is rendered and also applies perspective. Also each graphical object needs to have its own coordinate system called local space and its own transform for movement. So now because every object has its own transform we can apply the View Frustum to that object before drawing it. The creators of OpenGLU has online a matrix view of its function gluPerspective so we can look up the maths for a View Frustum:
and the function g defines the matrix:
For more information and source link: https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/gluPerspective.xml
The Geometric View of the View Frustum:
Now to understand what to do next we need to be able to define the variables used in the function. I will go through each of the labels which are the variables in the function. The geometric view looks like this:
The first variable I want to talk about is the variable C this is just the camera position which is at coordinate point (0, 0, 0). Next is the flovy variable which is the angle in degree from the top and the bottom of the View Frustum. The aspect variable found in the function g is width divided by the height of the View Frustum which is normally just the width and height of the window. Now the zFar plain is the draw distance of the game world, so if any object is beyond this plain it wont be drawn on screen. The zNear is the opposite so if an object approaches and goes behind the zNear plain it wont be drawn either. Note objects can be sliced in half if they are half in and out of these plains.
In my video game I defined my View Frustum flovy at 70 degrees (45 degrees also works well) if this is too small then then things look squished. The View Frustum zNear to be 0.1 away from the camera position and the zFar to be at 1000. So that means everything between 0.1 and 1000 will be drawn between an angle of 70 degrees.
The Implementation of the View Frustum:
Now there’s a lot of code goes into creating a video game so I will only be focusing on the stuff important to the View Frustum. Firstly you need to understand that every object, even objects that don’t move has its own transform which will be multiplied by the View Frustum in a file called the Vertex Shader. So I counted the objects and wrote a for loop for the amount of objects I have in the game. So lets see it in action:
The Camera instance called cam creates the View Frustum before the loop begins with function getViewProjection(). After that we take the data and pass it into the Vertex Shader. Then each object gets a new temporary empty 4 by 4 matrix to be filled with the objects transform, this is then filled with the objects transform with the function getGLTransform(), that is all taken to the Vertex Shader.
As we are drawing our scene we do a matrix multiplication with all the objects in the vertex shader which looks like this:
The gl_Position is OpenGL speak for final position and vec4(position, 1.0) is just to turn the matrix into a vector for positioning. That’s not important though as you can see the camera which is the View Frustum in this example is multiplied by the model which creates the perspective we needed!
So the finally result is this:
Please be aware that this isn’t the full code for an application and my project is still a work in progress. An improvement would be checking to see if the object is inside the View Frustum and only loop through them rather than everything.
Thanks for reading!