Tutorial3: Rendering 3D Objects (C /SDL)

From OpenGL.org
Jump to: navigation, search

Overview

This tutorial is designed to help explain the process of rendering 3D objects using the OpenGL 3.2 core profile. The requirements to compile and run this tutorial are the same as the previous tutorials.

Rendering an object in a three dimensional space makes use of three different matrices to determine where rendering should occur. These are the model, view, and projection matrices. The model matrix is used to describe where an object exists in the world. The view matrix is used to describe the vantage point of our view. The view matrix can be thought of as the position and angle of a camera used to take a picture. The projection matrix is used to give our view perspective such as making close objects appear larger than distant objects. The projection matrix also provides a field of view which can be thought of as a camera lens; you can decide to use a wide-angle lens or a telephoto lens. Multiplying these three matrices together and then with a object's vertex positions will provide us with a three dimensional view of the object.

If you have used the old OpenGL API, then you are probably familiar with some of the functions used to modify these matrices. These functions have been deprecated and are not available in the OpenGL 3.2 core profile. This means that we are required to perform these matrix operations using our own methods. In this tutorial I have included utils.c and utils.h files which will allow us to perform some of the matrix operations we will need. If you are programming in C++, there is an excellent library called glm which will perform the necessary functionality as well.

Changes to the window setup

In this tutorial we make a couple of changes to our window setup. The first change we make is to enable multisampling. Multisampling is a form of anti-aliasing used to make object edges less jagged. We enable 4x mutisampling using the follow code:

    /* Enable multisampling for a nice antialiased effect */
    SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
    SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4);

The previous code is executed before the window and OpenGL context are created. The other change we make is to enable depth testing which occurs after the window and OpenGL context are created. Enabling depth testing causes OpenGL to keep track of where rendered primitives exist in the world. We also tell OpenGL that we want to use the GL_LESS depth comparison function to determine which objects should be shown on top. The result of this is that objects closer to the viewpoint will obscure object further away from the viewpoint. If we didn't enable the depth testing, the last drawn polygon would always appear to be closest to the viewpoint. We perform these operations with the following code:

    /* Enable Z depth testing so objects closest to the viewpoint are in front of objects further away */
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LESS);

Changes to drawing the scene

The first changes we make are declaring our projection and model matrices. You may notice that we don't declare a view matrix in this tutorial. This is because we are using the default view, which consists of what is known as the right-handled coordinate system. The right-handed coordinate system consists of the 0,0,0 coordinates being positioned in the center of the view with the negative Z axis extending in front of us. The next change we make is making vertex points describing a tetrahedron, a simple three dimensional object consisting of four triangles. Most of the code from tutorial2 is reused to setup our VAO, VBOs, shaders, and program. The next changes consist of creating a perspective for our projection matrix, loading the identity matrix for the model matrix, then rotating and translating our model matrix. We then multiply our model and projection matrices together and bind the resultant matrix as a uniform for use in our vertex shader. A uniform is simply a value or values, in our case a matrix, which does not change value during the execution of a shader. Here's the code snippet performing these operations:

    /* Create our projection matrix with a 45 degree field of view
     * a width to height ratio of 1.0 and view from .1 to 100 infront of us */
    perspective(projectionmatrix, 45.0, 1.0, 0.1, 100.0);
 
    /* Loop our display rotating our model more each time. */
    for (i=0; i < 360; i++)
    {
        /* Load the identity matrix into modelmatrix. rotate the model, and move it back 5 */
        memcpy(modelmatrix, identitymatrix, sizeof(GLfloat) * 16);
        rotate(modelmatrix, (GLfloat)i * -1.0, X_AXIS);
        rotate(modelmatrix, (GLfloat)i * 1.0, Y_AXIS);
        rotate(modelmatrix, (GLfloat)i * 0.5, Z_AXIS);
        translate(modelmatrix, 0, 0, -5.0);
 
        /* multiply our modelmatrix and our projectionmatrix. Results are stored in modelmatrix */
        multiply4x4(modelmatrix, projectionmatrix);
 
        /* Bind our modelmatrix variable to be a uniform called mvpmatrix in our shaderprogram */
        glUniformMatrix4fv(glGetUniformLocation(shaderprogram, "mvpmatrix"), 1, GL_FALSE, modelmatrix);

Changes in the vertex shader

The vertex shader is almost the same as the previous tutorial. The only differences are the declaration of the uniform model-view-projection matrix (mvpmatrix) we now have as well as multiplying the matrix to our vector.

// mvpmatrix is the result of multiplying the model, view, and projection matrices */
uniform mat4 mvpmatrix;
void main(void) {
// Multiply the mvp matrix by the vertex to obtain our final vertex position
    gl_Position = mvpmatrix * vec4(in_Position, 1.0);

Full source code

Full source code is available in a zip file here (Note: broken link).

Full source code of a variation of tutorial3 using a single VBO is available in a zip file here

Compilation

On linux:

gcc utils.c tutorial3.c -o tutorial3 -lGL $(sdl-config --cflags --libs)

If you have SDL-1.2 and SDL-1.3 both installed, be sure to run the 1.3 version of sdl-config. For example if you installed SDL-1.3 in /usr/local:

gcc utils.c tutorial3.c -o tutorial3 -lGL $(/usr/local/bin/sdl-config --cflags --libs)

Execution

./tutorial3

The result should be a 512x512 window centered on your display showing a rotating tetrahedron.