06.17.09
Module : Introduction to 3D Graphics – Part 2
The Camera
The next stage required me to implement a virtual camera into my project so that I could view objects in my renderd scenes. For the time being however, I was just concerned with getting my camera’s methods and data fields implemented. Just to point out, these tasks were being assigned on a weekly basis meaning that there was little time to really take in what was being learned making this project took up a lot of time outside of class time. The camera implementation was one of the most difficult concepts for me to grasp and implement correctly. I fell behind during this stage as I tried various ways of trying to get a working camera but learned some important lessons on how to approach writing code and methodologies used to make coding easier.
Usually I would just come up with a bit of pseudo code on paper and start whacking in all sorts of variables and parameters without really thinking about how code communicates with each other. What I started to do at this point was to use a system that would draw out how classes and object instances would communicate to each other (what would later be taught as case statements and UML diagrams). Although people may look at it as a way of wasting time by writing out code on paper, I find it helpful to be able to see how objects and methods should link together on paper. I really like being able to carry on thinking about how to solve a problem whilst being able to take a break away from starring at a computer screen and now use this approach whenever I’m writing code even if its only a small amount. Burn out is a killer when trying to meet deadlines and bang code out in the shortest amount of time and most of the time I end up re – writting naff code and not using the hundreds of floats I declared at the start of the night.
So for writing the camera class I had to think about what sort of functionality and data I would be working with and ways of implementing them. I had the tools already implemented as I had full Matrix and Vector functionality from the classes I had written and tested in the previous 2 weeks and so it was just a case now of getting the camera to make use of them. I started with the header file and set up a default constructor and destructor and then thought about the matrices I would need. I needed matrices to represent the screen, perspective and camera transforms which would be used in the local methods for creating each one. I would also need rotation methods for the X, Y, Z axis methods and later added a GetCamPos method which returned a Vector representing the position of the camera.
Camera transforms, rotations and references
When setting up a virtual camera there are a various important transformations that are required to correctly render images on screen. These transformations are based around moving from different co – ordinate spaces each being a different representation of my vertices data. The main four spaces I made use of were Modelling, World, View and Screen space and considered these when writing my renderer and constructing its pipeline. Modelling space was simply the co – ordinate system used local to the model and relative to the centre of the model i.e. the X, Y and Z at the centre of the model is 0, 0, 0. World space is responsible for translations scaling and rotations and allows different models with the same Model space co – ordinates to be set up in a larger space. View space is what I was really interested in at this point as it represents where my camera will be and how models in world space will be relative to it. Finally Screen space is simply pixel space on the screen and the transformation from 3D to 2D also known as projection. These four spaces are controlled by 3 matrices the World, View and Projection matrix. These are the rights of passage from space to space and are responsible for controlling the vertices data for the models.
So the first method I set up was to build my perspective transform which contained a matrix that took a float value and measured a field of view based on what was passed into it. This value was then passed along each axis to correctly set up the camera’s view in relation to the objects on screen. I then created a build screen transform method that simply created a screen based on an X and Y value passed in. The last transform method I needed was to build a camera transformwhich created me a view matrix. This was one of the hardest things for me to understand an get working correctly as I kept messing up the translation of the camera position and the order in which rotations were multiplied. It was also at this stage I realised I had a mistake in my matrix class that was responsible for screwing up my entire renders. In my Matrix multiplication method, I had copy and pasted lines of code and accidently changed a “x” to a “+” giving me some weird results and so learned another important lesson to avoid copy and paste when coding late at night!
The parameters for the rotation methods were simple a float amount and a matrix reference. The float amount was simply the value passed into the matrix column to rotate by. The matrix reference simply stores the results that are then used later. Another new technique I learned at this point to was how to make use of references when referring to values or data types.
A reference provides another name for a variable. Whatever is done to the reference is done to the variable it refers to. The best description I was given was from a fellow class mate who suggested thinking about references as a nickname for a variable i.e. another name that a variable goes by. So in my parameters to my methods, I pass a reference to a matrix which I then define and save the working calculations in. Make sense? check out the code below -
void Camera ::RotateX(float rotX, Matrix & resultMat)
{
// Create Matrix for X rotation a.k.a -roll
resultMat = Matrix( 1, 0, 0, 0
0, cos(rotX), -sin(rotX), 0
0, sin(rotX), cos(rotX), 0
0, 0, 0, 1 );
}
The variable reference resultMat then has the values saved into it and is recognised as a matrix. This makes things very easy and convenient when saving data and variables as well as not needing to copy large data types. References can be used in a variety of ways and have many advantages when deciding how to construct arguments. I had used References before but not in a way in which they were passed by parameter or as arguments. This gave me a nice introduction to using them and taught me an important lesson for passing data chunks.