OpenGL:Tutorials:Tutorial Framework:Adding Depth and Color

From GPWiki
Jump to: navigation, search

An illusion of depth is required in our application to provide the effect of a 3D world on a 2D screen. If we just rendered our polygons to the screen, they would overlap in draw order and look pretty naff.


Setting up the Z-Buffer

The use of a Z-Buffer ensures that polygons overlap correctly. To enable the Z-Buffer within OpenGL we do two things:

glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);

Enabling the depth test makes every drawing operation check the Z-buffer before placing pixels onto the screen.
Enabling the depth mask makes every successful drawing operation place it's depth values into the Z-buffer.


Another technique that aids the illusion is backface culling. This works best with convex objects (cubes, spheres, etc). The method uses the normal vector of a polygon to determine its 'direction', only polygons that face the screen are drawn. In a cube, the three faces on the far side are not rendered, so they can't interfere with illusion of a soild object. Backface culling also reduces the on screen polycount by around a half, so it is a valuable optimisation.

Enabling backface culling in OpenGL is simple:

glEnable(GL_CULL_FACE);

We can change the behaviour of the culling with glCullFace() if required. Depending on the order in which your vertices are specified you can pass either GL_FRONT or GL_BACK:

glCullFace(GL_FRONT);
// or
glCullFace(GL_BACK);

Adding Color

In this example we're going to render a nice spinning cube, we'll color the faces to demonstrate OpenGL's color functions.

  glBegin(GL_QUADS);
   glColor3f(1.0f,0.0f,0.0f); // Red
   glVertex3f(-1.0f, 1.0f, 1.0f);
   glVertex3f( 1.0f, 1.0f, 1.0f);
   glVertex3f( 1.0f, 1.0f,-1.0f);
   glVertex3f(-1.0f, 1.0f,-1.0f);
 
   glColor3f(0.0f,0.0f,1.0f); // Blue
   glVertex3f(-1.0f,-1.0f, 1.0f);
   glVertex3f(-1.0f,-1.0f,-1.0f);
   glVertex3f( 1.0f,-1.0f,-1.0f);
   glVertex3f( 1.0f,-1.0f, 1.0f);
  glEnd();

glColor() comes in a few flavours, the one used here specifies three float values in the range 0.0 - 1.0 for red, green and blue. Other versions of the function take integer or double arguments and specify alpha values too. The color specified is used for all drawing operations after the function call.

Having drawn the top and bottom faces for our cube, we now move onto the sides.

     // Draw the side faces
     glBegin(GL_TRIANGLE_STRIP);
      glColor3f(1.0f,1.0f,1.0f); // White
      glVertex3f(-1.0f, 1.0f, 1.0f);
      glVertex3f(-1.0f,-1.0f, 1.0f);
      glVertex3f( 1.0f, 1.0f, 1.0f);
      glColor3f(0.0f,1.0f,0.0f); // Green
      glVertex3f( 1.0f,-1.0f, 1.0f);
      glColor3f(1.0f,1.0f,0.0f); // Yellow
      glVertex3f( 1.0f, 1.0f,-1.0f);
      glColor3f(0.0f,1.0f,1.0f); // Aqua?
      glVertex3f( 1.0f,-1.0f,-1.0f);
      glColor3f(0.6f,0.6f,0.6f); // Gray
      glVertex3f(-1.0f, 1.0f,-1.0f);
      glColor3f(0.1f,0.1f,0.1f); // Dark Gray
      glVertex3f(-1.0f,-1.0f,-1.0f);
      glColor3f(0.0f,0.0f,1.0f); // Blue
      glVertex3f(-1.0f, 1.0f, 1.0f);
      glColor3f(1.0f,0.0f,1.0f); // Red
      glVertex3f(-1.0f,-1.0f, 1.0f);
     glEnd();

The triangle strip drawing method treats the first three vertices as a triangle, subsequent vertices produce a new triangle using the previous two points.
We specify a new color for each vertex, the result is a smooth transition between the colors rather than the flat color produced on the quads.

Pressing the 'W' key during the program displays a wireframe view to illustrate how the triangle strip is created.

GLTut2Cube.jpg GLTut2WireCube.jpg

Try commenting out the Z-Buffer and backface culling commands to see how the illusion is destroyed when the polygons are not displayed correctly.

Source Code

The source to Render.cpp, compile this demo using the OpenGL Tutorial Framework

#include "Framework.h"
 
void Render(void)
 {
  float Rotate=0.0f; // A rotation value to be used to spin our polygon
 
  glClearColor(0,0,0,0);
 
  // Setup our screen
  glViewport(0,0,800,600);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(45.0f,800.0f/600.0f,1.0f,500.0f);
  glMatrixMode(GL_MODELVIEW);
 
  // Ensure correct display of polygons
  glEnable(GL_CULL_FACE);
  glEnable(GL_DEPTH_TEST);
  glDepthMask(GL_TRUE);
 
  // This loop will run until Esc is pressed
   while(RunLevel)
    {
      if(Keys[VK_ESCAPE]) // Esc Key
       RunLevel=0;
 
     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear screen and z-buffer
     glLoadIdentity(); // Reset current matrix (Modelview)
 
     // Press 'w' to show wireframe view
      if(Keys['W'])
       glPolygonMode(GL_FRONT,GL_LINE);
      else
       glPolygonMode(GL_FRONT,GL_FILL);
 
 
     // Do our rotations
     glTranslatef(0.0f,0.0f,-5.0f);
     glRotatef(Rotate,0.0f,0.0f,1.0f);
     glRotatef(Rotate,1.0f,0.6f,0.0f);
 
     // Draw the top and bottom faces
     glBegin(GL_QUADS);
      glColor3f(1.0f,0.0f,0.0f); // Red
      glVertex3f(-1.0f, 1.0f, 1.0f);
      glVertex3f( 1.0f, 1.0f, 1.0f);
      glVertex3f( 1.0f, 1.0f,-1.0f);
      glVertex3f(-1.0f, 1.0f,-1.0f);
 
      glColor3f(0.0f,0.0f,1.0f); // Blue
      glVertex3f(-1.0f,-1.0f, 1.0f);
      glVertex3f(-1.0f,-1.0f,-1.0f);
      glVertex3f( 1.0f,-1.0f,-1.0f);
      glVertex3f( 1.0f,-1.0f, 1.0f);
     glEnd();
 
     // Draw the side faces
     glBegin(GL_TRIANGLE_STRIP);
      glColor3f(1.0f,1.0f,1.0f); // White
      glVertex3f(-1.0f, 1.0f, 1.0f);
      glVertex3f(-1.0f,-1.0f, 1.0f);
      glVertex3f( 1.0f, 1.0f, 1.0f);
      glColor3f(0.0f,1.0f,0.0f); // Green
      glVertex3f( 1.0f,-1.0f, 1.0f);
      glColor3f(1.0f,1.0f,0.0f); // Yellow
      glVertex3f( 1.0f, 1.0f,-1.0f);
      glColor3f(0.0f,1.0f,1.0f); // Aqua?
      glVertex3f( 1.0f,-1.0f,-1.0f);
      glColor3f(0.6f,0.6f,0.6f); // Gray
      glVertex3f(-1.0f, 1.0f,-1.0f);
      glColor3f(0.1f,0.1f,0.1f); // Dark Gray
      glVertex3f(-1.0f,-1.0f,-1.0f);
      glColor3f(0.0f,0.0f,1.0f); // Blue
      glVertex3f(-1.0f, 1.0f, 1.0f);
      glColor3f(1.0f,0.0f,1.0f); // Red
      glVertex3f(-1.0f,-1.0f, 1.0f);
     glEnd();
 
     // Add to the rotation for next frame 
     Rotate+=0.05f;
 
     // Show our cube
     FlipBuffers();
    }
 
 }