4. OpenGL Primitives – Square (Version 2.0)

Introduction

Drawing shapes is one of those crucial things we do in OpenGL, because if we don’t draw anything, we don’t see anything. This tutorial was originally very simple, we drew a simple square, but I am going to extend it further and actually show how a bunch of different primitive types work, but don’t worry, we will still get to draw our square.

Different Drawing Types

OpenGL provides us with several different types of shapes that we can draw in order to get the geometry we want on the screen. In brief, they are:

  • GL_LINES
  • GL_LINE_STRIP
  • GL_LINE_LOOP
  • GL_POINTS
  • GL_POLYGON
  • GL_QUADS
  • GL_QUAD_STRIP
  • GL_TRIANGLES
  • GL_TRIANGLE_FAN
  • GL_TRIANGLE_STRIP

Let me give you a brief rundown of these shapes.

GL_LINES

As the name implies, this draws a line from one point to another in 3D space.

GL_LINE_STRIP

A line strip is a series of lines, where each point in the line has a point before it and after it, except for the end points. This is useful if you want to draw a series of lines, maybe you want to draw a circle and a circle is a set of lines.

GL_LINE_LOOP

This is similar to the GL_LINE_STRIP, except the end points double back to each other. So the first point will automatically connect to the end point via one line.

GL_POINTS

Draws a series of points in 3D space, their size can also be changed.

GL_POLYGON

A polygon is a 3D shape with any number of vertices, it is a filled surface.

GL_QUADS

A quad is similar to a polygon, but it limits the number of vertices to 4. This allows OpenGL to break it up into two triangles quite simply for faster processing. In short, a quad is a shape with 4 sides.

GL_QUAD_STRIP

A quad strip is a line of quads, where each quad shares two vertices with the quad before, and after it.

GL_TRIANGLES

Triangles are the ultimate shape in 3D geometry, this is because you can quickly and efficiently calculate how to fill them. GL_TRIANGLES takes a series of 3 vertices; each 3 vertices describe the points for a triangle.

GL_TRIANGLE_FAN

A triangle fan has one central vertex, and all other vertices form triangles extending from this point. Very useful for drawing filled circles.

GL_TRIANGLE_STRIP

A triangle strip is similar to a quad strip and a line strip, but it uses triangles, where two of the three vertices are shared with a triangle before and after itself.

Coding

So let’s stop babbling and get on to drawing a square. I am going to implement a new method, in order to keep our code nice and tidy. This method will be called renderPrimitive and will take no parameters.

void renderPrimitive (void) {

}

Now inside of our display method, we are going to want to call this, so before our glFlush line, make a call to render Primitive.

void display (void) {
…
renderPrimitive(); // Render the primitive
glFlush(); // Flush the OpenGL buffers to the window
}

Ok, good, we have a method we can fill to draw our shape, and we are calling it in our render loop. Next up, we aren’t going to go straight to drawing our shape, but we are going to add a line above our renderPrimitive call, and I’ll explain why. The default position for the camera in OpenGL is at the world origin, which is (0, 0, 0) in 3D space. Because we have a near plane at 1.0, anything drawn between the values of 0.0 and 1.0 on the z axis will be culled automatically. In other words, it won’t be drawn. In order to make sure we can see our shape, instead of positioning further along the z-axis when I set the vertices, I am going to move the entire scene back 5 units, so anything we draw at (0, 0, 0) will actually be drawn at (0, 0, 5). This may sound a little complicated, but trust me on this, and it will be explained a little more in the camera tutorial as the camera tutorial consists of 4 calls we do, before any of our rendering, to make sure we can see what we want.

Now, you can go ahead and add a line before our call to renderPrimitive which will translate (move) our scene 5 units in front of us. This is done with a call to glTranslate which takes three parameters, each one relating to how much we want to move everything on the x, y and z axis.

void display (void) {
…
glTranslatef(0.0f, 0.0f, -5.0f); // Push eveything 5 units back into the scene, otherwise we won't see the primitive
renderPrimitive(); // Render the primitive
glFlush(); // Flush the OpenGL buffers to the window
}

And finally everything is now set up for us to actually draw our square. When we want to tell OpenGL we are drawing some geometry, we need to make two calls, one at the start to tell OpenGL we now want to draw a shape, and one at the end, telling OpenGL we have finished drawing our shape.

These calls are glBegin and glEnd, relating to the beginning and the ending of our shape drawing, and glBegin takes one of the geometry types I mentioned earlier as a parameter. Because we are drawing a square, I am going to settle for GL_QUADS, although it would be more efficient to go for a GL_TRIANGLE_STRIP and supply 5 vertices to OpenGL to get the performance of triangles, I am going for simplicity and using only 4 vertices for a quad.

void renderPrimitive (void) {
glBegin(GL_QUADS); // Start drawing a quad primitive
glEnd();
}

At the moment, OpenGL will happily accept this code, but we won’t see anything on the screen. This is because we need to supply the vertex data for each corner of the quad to OpenGL. To make a call to create a vertex we use glVertex. glVertex can take either two or three parameters, and the types for the parameters can change, depending on the type of glVertex call. For example, if we want to draw a 2D vertex using float values, we would call glVertex2f. If we want to create a 3D vertex using double values, we would call glVertex3d. The same also works for integer values, and even arrays of data.

To be clear, the centre of the window, because I have only moved the camera back, is at the location (0, 0, 0). To draw our quad, we are going to create 4 vertices that revolve around this point. I am first going to declare the bottom left corner, followed by the top left, top right and bottom right. This is because it makes it simple to follow how texture coordinates will be assigned later on, as they go in this order.

In between our glBegin and glEnd calls, create the vertex calls like so:

glVertex3f(-1.0f, -1.0f, 0.0f); // The bottom left corner
glVertex3f(-1.0f, 1.0f, 0.0f); // The top left corner
glVertex3f(1.0f, 1.0f, 0.0f); // The top right corner
glVertex3f(1.0f, -1.0f, 0.0f); // The bottom right corner

You should now be able to compile and run this, and have it draw a white square in the middle of your window.

Go ahead and experiment with the other geometry types. You should be able to create the square, both filled and outlined, using any of the geometry types above.

If you have any questions, please email me at swiftless@gmail.com

Here are some examples of other geometry types:

Points

void renderPrimitive (void) {
glPointSize(20.0f);
glBegin(GL_POINTS); // Start drawing a point primitive
glVertex3f(-1.0f, -1.0f, 0.0f); // The bottom left corner
glVertex3f(-1.0f, 1.0f, 0.0f); // The top left corner
glVertex3f(1.0f, 1.0f, 0.0f); // The top right corner
glVertex3f(1.0f, -1.0f, 0.0f); // The bottom right corner
glEnd();
}

Line Loop

void renderPrimitive (void) {
glBegin(GL_LINE_LOOP); // Start drawing a line primitive
glVertex3f(-1.0f, -1.0f, 0.0f); // The bottom left corner
glVertex3f(-1.0f, 1.0f, 0.0f); // The top left corner
glVertex3f(1.0f, 1.0f, 0.0f); // The top right corner
glVertex3f(1.0f, -1.0f, 0.0f); // The bottom right corner
glEnd();
}

Triangle Strips

void renderPrimitive (void) {
glBegin(GL_TRIANGLE_STRIP); // Start drawing a triangle strip primitive
// The first triangle
glVertex3f(-1.0f, -1.0f, 0.0f); // The bottom left corner
glVertex3f(-1.0f, 1.0f, 0.0f); // The top left corner
glVertex3f(1.0f, 1.0f, 0.0f); // The top right corner
// The end of the second triangle
glVertex3f(1.0f, -1.0f, 0.0f); // The bottom right corner
glVertex3f(-1.0f, -1.0f, 0.0f); // The bottom left corner
glEnd();
}

Tutorial Code

#include <GL/glew.h> // Include the GLEW header file
#include <GL/glut.h> // Include the GLUT header file

bool* keyStates = new bool[256]; // Create an array of boolean values of length 256 (0-255)

void keyOperations (void) {
if (keyStates[GLUT_KEY_LEFT]) { // If the left arrow key has been pressed
// Perform left arrow key operations
}
}

void renderPrimitive (void) {
glBegin(GL_QUADS); // Start drawing a quad primitive

glVertex3f(-1.0f, -1.0f, 0.0f); // The bottom left corner
glVertex3f(-1.0f, 1.0f, 0.0f); // The top left corner
glVertex3f(1.0f, 1.0f, 0.0f); // The top right corner
glVertex3f(1.0f, -1.0f, 0.0f); // The bottom right corner

glEnd();
}

void display (void) {
keyOperations();

glClearColor(1.0f, 0.0f, 0.0f, 1.0f); // Clear the background of our window to red
glClear(GL_COLOR_BUFFER_BIT); //Clear the colour buffer (more buffers later on)
glLoadIdentity(); // Load the Identity Matrix to reset our drawing locations

glTranslatef(0.0f, 0.0f, -5.0f); // Push eveything 5 units back into the scene, otherwise we won't see the primitive

renderPrimitive(); // Render the primitive

glFlush(); // Flush the OpenGL buffers to the window
}

void reshape (int width, int height) {
glViewport(0, 0, (GLsizei)width, (GLsizei)height); // Set our viewport to the size of our window
glMatrixMode(GL_PROJECTION); // Switch to the projection matrix so that we can manipulate how our scene is viewed
glLoadIdentity(); // Reset the projection matrix to the identity matrix so that we don't get any artifacts (cleaning up)
gluPerspective(60, (GLfloat)width / (GLfloat)height, 1.0, 100.0); // Set the Field of view angle (in degrees), the aspect ratio of our window, and the new and far planes
glMatrixMode(GL_MODELVIEW); // Switch back to the model view matrix, so that we can start drawing shapes correctly
}

void keyPressed (unsigned char key, int x, int y) {
keyStates[key] = true; // Set the state of the current key to pressed
}

void keyUp (unsigned char key, int x, int y) {
keyStates[key] = false; // Set the state of the current key to not pressed
}


int main (int argc, char **argv) {
glutInit(&argc, argv); // Initialize GLUT
glutInitDisplayMode (GLUT_SINGLE); // Set up a basic display buffer (only single buffered for now)
glutInitWindowSize (500, 500); // Set the width and height of the window
glutInitWindowPosition (100, 100); // Set the position of the window
glutCreateWindow ("Your first OpenGL Window"); // Set the title for the window

glutDisplayFunc(display); // Tell GLUT to use the method "display" for rendering

glutReshapeFunc(reshape); // Tell GLUT to use the method "reshape" for reshaping

glutKeyboardFunc(keyPressed); // Tell GLUT to use the method "keyPressed" for key presses
glutKeyboardUpFunc(keyUp); // Tell GLUT to use the method "keyUp" for key up events

glutMainLoop(); // Enter GLUT's main loop
}
  • March 25, 2010
  • 24