Saturday, March 30, 2013

Table of Contents

For those new to the site, here is an introduction.

This post is a table of all posts created so far and will be updated and reposted with every new post.

I also had some realizations about my past examples, which are covered in this note.

Here is the table:
Programming with C, 1989 Standard
    Preparation
    Basics
    Variables
    Operators
    Functions
    Logic and Conditions
    Loops
    Structures
    Pointers
    Strings
    Enumeration and Flags
Basics of SDL
    Setup
    Drawing
    Keyboard
    Mouse
    BMP Images
    Other Images
Using OpenGL 2.1
    Setup
    Drawing
    Textures 
    More Textures
    Transformations
    3D Transformations
    Depth Testing
    Lighting (in progress)

Also, here are the concepts still planned to be covered:
Adding GLSL to OpenGL with GLEW
Using OpenAL 1.1
Multimedia file formats

OpenGL Depth Testing

    Like SDL, OpenGL will draw shapes in the order you specify them.  As seen in the post http://multimediaprogram.blogspot.com/2012/08/drawing-in-sdl.html, shapes drawn later overwrite previously drawn shapes.  However, when dealing with 3D scenes, this can lead to weird visual effects.  Luckily, OpenGL provides an easy solution called depth testing.

Draw Order
    As said before, OpenGL will by default overwrite previously drawn shapes any time a new shape is drawn.  In 3D, we want shapes close to the camera to be the most visible, overwriting any shapes behind them.  As a demonstration, here is what happens if a distant shape is drawn last:
#include <SDL/SDL.h>
#include <SDL/SDL_opengl.h>
#include <stdio.h>

SDL_Surface* screen;
SDL_Event input;
int loop = 1;

/*z positions*/
float tealZ = -3;
float colorZ = -5;

int main(int argc, char** argv) {
    SDL_Init(SDL_INIT_EVERYTHING); /*initialize SDL*/
    screen = SDL_SetVideoMode(640, 480, 32, SDL_OPENGL);

    /*print depths of drawn shapes*/
    printf("depth of teal square = %g\n", tealZ);
    printf("depth of multi-colored square = %g\n", colorZ);

    /*set clear color to golden yellow*/
    glClearColor(0.8, 0.8, 0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);

    /*create 3D camera*/
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(70, 1.333, 1, 100);

    /*camera set at origin*/
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    while (loop) {
       glClear(GL_COLOR_BUFFER_BIT); /*clear screen*/

       while (SDL_PollEvent(&input)) { /*check exit button*/
          if (input.type == SDL_QUIT) loop = 0;
       }

       /*draw quadrilaterals*/
       glBegin(GL_QUADS);

       /*light teal square*/
       glColor3ub(0, 255, 255);
       glVertex3f(-2, 1, tealZ);
       glVertex3f(-2, -1, tealZ);
       glVertex3f(0, -1, tealZ);
       glVertex3f(0, 1, tealZ);

       /*multi-colored square*/
       glColor3f(1, 1, 0); glVertex3f(-1, 1, colorZ);
       glColor3f(1, 0, 0); glVertex3f(-1, -1, colorZ);
       glColor3f(0, 1, 0); glVertex3f(1, -1, colorZ);
       glColor3f(0, 0, 1); glVertex3f(1, 1, colorZ);

       glEnd();

       SDL_GL_SwapBuffers();
       SDL_Delay(20); /*wait 20ms*/
    }

    /*perform final commands then exit*/
    SDL_Quit(); /*close SDL*/
    fflush(stdout); /*update stdout*/
    return 0;
}

prompt/stdout:
depth of teal square = -3
depth of multi-colored square = -5

output inside window:
Distant Square Drawn Last


    Even though the multi-colored square is farther from the camera, it appears to be in front of the left square.  One solution would be to draw the light teal rectangle last so that it overwrites the multi-colored square.  However, if the camera were to move beyond the multi-colored square and look toward the teal square, we would have a similar problem.

    We could therefore switch the order shapes are drawn based on distance from the camera.  In fact, switching the order of shapes drawn used to be the best solution to 3D visibility.  This is known as the Painter's algorithm and can be read about at http://en.wikipedia.org/wiki/Painter%27s_algorithm.

Depth Testing (and Buffering)
    A more general solution to 3D visibility is Z-buffering, which can be read about at http://en.wikipedia.org/wiki/Z-buffering.  Basically, OpenGL can track the depth of each drawn pixel, save it to a buffer, and test against it when drawing new shapes.  Also, OpenGL makes it incredibly easy to use this feature:
#include <SDL/SDL.h>
#include <SDL/SDL_opengl.h>
#include <stdio.h>

SDL_Surface* screen;
SDL_Event input;
int loop = 1;

/*z positions*/
float tealZ = -3;
float colorZ = -5;

int main(int argc, char** argv) {
    SDL_Init(SDL_INIT_EVERYTHING); /*initialize SDL*/
    screen = SDL_SetVideoMode(640, 480, 32, SDL_OPENGL);

    /*print depths of drawn shapes*/
    printf("depth of teal square = %g\n", tealZ);
    printf("depth of multi-colored square = %g\n", colorZ);

    /*set clear color to golden yellow*/
    glClearColor(0.8, 0.8, 0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);

    /*setup depth buffering*/
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LESS); /*lesser depth*/
    glClearDepth(1); /*clear to maximum depth*/

    /*create 3D camera*/
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(70, 1.333, 1, 100);

    /*camera set at origin*/
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    while (loop) {
       /*clear the color and depth of each pixel*/
       glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

       while (SDL_PollEvent(&input)) { /*check exit button*/
          if (input.type == SDL_QUIT) loop = 0;
       }

       /*draw quadrilaterals*/
       glBegin(GL_QUADS);

       /*light teal square*/
       glColor3ub(0, 255, 255);
       glVertex3f(-2, 1, tealZ);
       glVertex3f(-2, -1, tealZ);
       glVertex3f(0, -1, tealZ);
       glVertex3f(0, 1, tealZ);

       /*multi-colored square*/
       glColor3f(1, 1, 0); glVertex3f(-1, 1, colorZ);
       glColor3f(1, 0, 0); glVertex3f(-1, -1, colorZ);
       glColor3f(0, 1, 0); glVertex3f(1, -1, colorZ);
       glColor3f(0, 0, 1); glVertex3f(1, 1, colorZ);

       glEnd();

       SDL_GL_SwapBuffers();
       SDL_Delay(20); /*wait 20ms*/
    }

    /*perform final commands then exit*/
    SDL_Quit(); /*close SDL*/
    fflush(stdout); /*update stdout*/
    return 0;
}

prompt/stdout:
depth of teal square = -3
depth of multi-colored square = -5

output inside window:
Squares with Depth Buffering


    The first necessary command is "glEnable(GL_DEPTH_TEST);".  Like textures, the depth testing must first be enabled to be used.  The following 2 commands could be called in any order, but glClearDepth works similar to glClearColor.  For glClearDepth, the default depth is set for each pixel when glClear is called.  1 refers to the maximum depth, so 0 would refer to the closest distance.

    The next command, glDepthFunc, will setup how depth testing is performed.  In this case, GL_LESS means pixels with a lesser depth are drawn.  For this example, the left square is drawn first and each of its pixels are set to a small depth value.  When the multi-colored square is drawn, half of the pixels do not have a lesser depth value than what has been recorded, so the left half is not drawn.

    In order to account for a moving camera, it is best to clear the depth buffer like we have done with the color buffer so far.  That is why the command "glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);" is used in this example.  Note that each pixel's color becomes the most recent input to glClearColor while each pixel's depth becomes the most recent input to glClearDepth.

Depth Function
    Enabling depth testing simply means the depth of individual pixels will be saved in a depth buffer.  Setting the depth function will allow us to specify which pixels are drawn.  For 3D scenes, GL_LESS or GL_LEQUAL (less than or equal) will ensure the closest shape is drawn.  However, there are other inputs to glDepthFunc which perform different tests.

    The documentation for glDepthFunc, http://www.opengl.org/sdk/docs/man/xhtml/glDepthFunc.xml, shows that other tests include GL_GREATER, GL_GEQUAL (greater/equal), GL_NOTEQUAL, and GL_ALWAYS or GL_NEVER.  For GL_GREATER, this means farther pixels will pass the depth test and get drawn.  GL_ALWAYS means everything passes the test and is similar to the regular draw order of OpenGL, except that pixel depths are recorded.  Here is an example of the GL_GREATER test in action:
#include <SDL/SDL.h>
#include <SDL/SDL_opengl.h>
#include <stdio.h>

SDL_Surface* screen;
SDL_Event input;
int loop = 1;

/*z positions*/
float redZ = -3;
float greenZ = -5;
float blueZ = -4;

int main(int argc, char** argv) {
    SDL_Init(SDL_INIT_EVERYTHING); /*initialize SDL*/
    screen = SDL_SetVideoMode(640, 480, 32, SDL_OPENGL);

    /*print depths of drawn shapes*/
    printf("depth of red square = %g\n", redZ);
    printf("depth of green square = %g\n", greenZ);
    printf("depth of blue square = %g\n", blueZ);

    /*set clear color to golden yellow*/
    glClearColor(0.8, 0.8, 0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);

    /*setup depth buffering*/
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_GREATER); /*greater depth*/
    glClearDepth(0); /*clear to minimum depth*/

    /*create 3D camera*/
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(70, 1.333, 1, 100);

    /*camera set at origin*/
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    while (loop) {
       /*clear the color and depth of each pixel*/
       glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

       while (SDL_PollEvent(&input)) { /*check exit button*/
          if (input.type == SDL_QUIT) loop = 0;
       }

       /*draw quadrilaterals*/
       glBegin(GL_QUADS);

       /*red square*/
       glColor3ub(255, 0, 0);
       glVertex3f(-2, 1, redZ);
       glVertex3f(-2, -1, redZ);
       glVertex3f(0, -1, redZ);
       glVertex3f(0, 1, redZ);

       /*green square*/
       glColor3ub(0, 255, 0);
       glVertex3f(-1, 1, greenZ);
       glVertex3f(-1, -1, greenZ);
       glVertex3f(1, -1, greenZ);
       glVertex3f(1, 1, greenZ);

       /*blue square*/
       glColor3ub(0, 0, 255);
       glVertex3f(-1, 0, blueZ);
       glVertex3f(-1, -2, blueZ);
       glVertex3f(1, -2, blueZ);
       glVertex3f(1, 0, blueZ);

       glEnd();

       SDL_GL_SwapBuffers();
       SDL_Delay(20); /*wait 20ms*/
    }

    /*perform final commands then exit*/
    SDL_Quit(); /*close SDL*/
    fflush(stdout); /*update stdout*/
    return 0;
}

prompt/stdout:
depth of red square = -3
depth of green square = -5
depth of blue square = -4

output inside window:
Same-Sized Squares with GL_GREATER Depth Test


    Here, three squares are drawn with the GL_GREATER depth test.  The red square is drawn first, followed by the green, and finally the blue.  Since the green square has the greatest depth, all of its pixels are drawn.  Even though the blue is drawn last, some of its pixels fail the depth test and are not drawn.

    The clear depth (glClearDepth) was set to 0 for this example.  Had it remained 1, nothing would pass the depth test since 1 is the maximum pixel depth OpenGL will record.  Even though the depth of these squares is -3, -4, and -5, the depth will be saved in the depth buffer as a number between 0 and 1.  The way it is translated to this fraction is determined by the near and far values used in gluPerspective.

    That is it for this post.  The next post should discuss OpenGL lighting.  Thank you for reading!

P.S.  tohtml.com is currently fixing bugs so the code will not be highlighted as a result.  This should be remedied soon though.