Drawing your weapons

It's time for us to learn how to draw things in OpenGL. Whether you are drawing your weapons, an alien spacecraft, or a blade of grass, it all starts by with very simple shapes that are combined to make more complex shapes.

Getting primitive

The most basic shapes that can be drawn in OpenGL are known as primitives. The primitives that can be drawn by OpenGL include:

  • Points: As the name suggests, a point renders a single point and is defined by a single vertex.
  • Lines: A line is rendered as a line drawn between two vertices.
  • Triangles: A triangle is defined by three vertices and the three lines that pass from one vertex to the other.
  • Quads: A quad is defined by four vertices and the four lines that pass from one vertex to the other. Technically, a quad is actually two triangles that have been joined together at the hypotenuse.

That's it, folks! Everything known to exist can be created from these four primitives. Extrapolating into 3D, there are these 3D primitives:

  • A plane is a 2D extrusion of a line (okay, I know that a plane isn't really 3D!)
  • A pyramid is a 3D representation of a quad and four triangles
  • A cube is the 3D extrusion of a quad
  • A sphere is a 3D construct based on a circle, which is created by lines (yes, lines, and the shorter each line, the more convincing the circle)
  • A cylinder is a 3D extrusion of a circle

The objects in the preceding list aren't actually defined as OpenGL primitives. However, many 3D modeling programs refer to them as primitives because they are the simplest 3D objects to create.

Drawing primitives

In the previous chapter, we created a cube using the following code:

void DrawCube()
{
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  glTranslatef(0.0f, 0.0f, -7.0f);
  glRotatef(fRotate, 1.0f, 1.0f, 1.0f);
  glBegin(GL_QUADS);
  glColor3f(0.0f, 1.0f, 0.0f);
  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(1.0f, 0.5f, 0.0f);
  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(1.0f, 0.0f, 0.0f);
  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(1.0f, 1.0f, 0.0f);
  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);
  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(1.0f, 0.0f, 1.0f);
  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();
  fRotate -= 0.05f;
  
}

Now, let's learn about how this code actually works:

  1. Any time that we want to draw something in OpenGL, we first start by clearing the render buffer. In other words, every frame is drawn from scratch. The glClear function clears the buffer so that we can start drawing to it.
  2. Before we start drawing objects, we want to tell OpenGL where to draw them. The glTranslatef command moves us to a certain point in 3D space from which we will start our drawing (actually, glTranslatef moves the camera, but the effect is the same).
  3. If we want to rotate our object, then we provide that information with the glRotatef function. Recall that the cube in the previous chapter slowly rotated.
  4. Just before we provide vertices to OpenGL, we need to tell OpenGL how to interpret these vertices. Are they single points? Lines? Triangles? In our case, we defined vertices for the six squares that will make the faces of our cube, so we specify glBegin(GL_QUADS) to let OpenGL know that we are going to be providing the vertices for each quad. There are several other possibilities that we will describe next.
  5. In OpenGL, you specify the properties for each vertex just before you define the vertex. For example, we use the glColor3f function to define the color for the next set of vertices that we define. Each succeeding vertex will be drawn in this specified color until we change the color with another call to glColor3f.
  6. Finally, we define each vertex for the quad. As a quad requires four vertices, the next four glVertex3f calls will define one quad. If you look closely at the code, you will notice that there are six groups of four vertex definitions (each preceded by a color definition), which all work together to create the six faces of our cube.

Now that you understand how OpenGL draws quads, let's expand your knowledge by covering the other types of primitives.

Making your point

There is only one kind of point primitive.

Gl_Points

The glBegin(GL_POINTS) function call tells OpenGL that each following vertex is to be rendered as a single point. Points can even have texture mapped onto them, and these are known as point sprites.

Points are actually generated as squares of pixels based on the size defined by the GL_PROGRAM_POINT_SIZE parameter of the glEnable function. The size defines the number of pixels that each side of the point takes up. The point's position is defined as the center of that square.

The point size must be greater than zero, or else an undefined behavior results. There is an implementation-defined range for point sizes, and the size given by either method is clamped to that range. Two additional OpenGL properties determine how points are rendered: GL_POINT_SIZE_RANGE (returns 2 floats), and GL_POINT_SIZE_GRANULARITY. This particular OpenGL implementation will clamp sizes to the nearest multiple of the granularity.

Getting in line

There are three kinds of line primitives, based on different interpretations the vertex list.

Gl_Lines

When you call glBegin(GL_LINES), every pair of vertices is interpreted as a single line. Vertices 1 and 2 are considered one line. Vertices 3 and 4 are considered another line. If the user specifies an odd number of vertices, then the extra vertex is ignored.

Gl_Line_Strip

When you call glBegin(GL_LINES), the first vertex defines the start of the first line. Each vertex thereafter defines the end of the previous line and the start of the next line. This has the effect of chaining the lines together up to the last vertex in the list. Thus, if you pass n vertices, you will get n-1 lines. If the user only specifies only one vertex, the drawing command is ignored.

Gl_Line_Strip

Gl_Line_Loop

The call glBegin(GL_LINE_LOOP) works almost exactly like line strips, except that the first and last vertices are joined as a line. Thus, you get n lines for n input vertices. If the user only specifies one vertex, the drawing command is ignored. The line between the first and last vertices happens after all of the previous lines in the sequence.

Gl_Line_Loop

Triangulation

A triangle is a primitive formed by three vertices. There are three kinds of triangle primitives, based again on different interpretations of the vertex stream.

Gl_Triangles

When you call glBegin(GL_TRIANGLES), every three vertices define a triangle. Vertices 1, 2, and 3 form one triangle. Vertices 4, 5, and 6 form another triangle. If there are fewer than three vertices at the end of the list, they are ignored:

glBegin(GL_TRIANGLES);
  glVertex3f( 0.0f, 1.0f, 0.0f);
  glVertex3f(-1.0f,-1.0f, 0.0f);
  glVertex3f( 1.0f,-1.0f, 0.0f);
glEnd();

Gl_Triangle_Strip

When you call glBegin(GL_TRIANGLE_STRIP), the first three vertices create the first triangle. Thereafter, the next two vertices create the next triangle, creating a group of adjacent triangles. A vertex stream of n length will generate n-2 triangles:

Gl_Triangle_Strip

Gl_Triangle_Fan

When you call glBegin(GL_TRIANGLE_FAN), the first vertex defines the point from which all other triangles are defined. Thereafter, each group of two vertices define a new triangle with the same apex as the first one, forming a fan. A vertex stream of n length will generate n-2 triangles. Any leftover vertices will be ignored:

Gl_Triangle_Fan

Being square

A quad is a quadrilateral, having four sides. Don't get confused and think that all quads are either squares or rectangles. Any shape with four sides is a quad. The four vertices are expected to be in the same plane and failure to do so can lead to undefined results. A quad is typically constructed as a pair of triangles, which can lead to artifacts (unanticipated glitches in the image).

Gl_Quads

When you call glBegin(GL_QUADS), each set of four vertices defines a quad. Vertices 1 to 4 form one quad, while vertices 5 to 8 form another. The vertex list must be a number of vertices divisible by 4 to work:

glBegin(GL_QUADS); 
  glVertex3f(-1.0f, 1.0f, 0.0f);
  glVertex3f( 1.0f, 1.0f, 0.0f);
  glVertex3f( 1.0f,-1.0f, 0.0f);
  glVertex3f(-1.0f,-1.0f, 0.0f);
glEnd();

Gl_Quad_Strip

Similarly to triangle strips, a quad strip uses adjacent edges to form the next quad. In the case of quads, the third and fourth vertices of one quad are used as the edge of the next quad. So, vertices 1 to 4 define the first quad, while 5 to 6 extend the next quad. A vertex list of n length will generate (n - 2)/2 quads:

Gl_Quad_Strip

Saving face

All of the primitives that we discussed are created by creating multiple shapes that are glued together, more or less. OpenGL needs to know which face of a shape is facing the camera, and this is determined by the winding order. As you can't see both the front and back of a primitive, OpenGL uses facing to decide which side must be rendered.

In general, OpenGL takes care of the winding order so that all of the shapes in a particular list have consistent facing. If you, as a coder, try to take care of facing manually, you are actually second-guessing OpenGL.

Back to Egypt

As we have already demonstrated the code to draw a cube, let's try something even more interesting: a pyramid. A pyramid is constructed by four triangles with a square on the bottom. So, the simplest way to create a pyramid is to create four GL_TRIANGLE primitives and one GL_QUAD primitive:

int DrawGlPyramid(GLvoid)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glTranslatef(-1.5f,0.0f,-6.0f);
glBegin(GL_TRIANGLES); 
glColor3f(1.0f,0.0f,0.0f); 
glVertex3f( 0.0f, 1.0f, 0.0f);
glColor3f(0.0f,1.0f,0.0f); 
glVertex3f(-1.0f,-1.0f, 1.0f);
glColor3f(0.0f,0.0f,1.0f);
glVertex3f( 1.0f,-1.0f, 1.0f); 
glColor3f(1.0f,0.0f,0.0f); 
glVertex3f( 0.0f, 1.0f, 0.0f);
glColor3f(0.0f,0.0f,1.0f);
glVertex3f( 1.0f,-1.0f, 1.0f);
glColor3f(0.0f,1.0f,0.0f);
glVertex3f( 1.0f,-1.0f, -1.0f); 
glColor3f(1.0f,0.0f,0.0f);
glVertex3f( 0.0f, 1.0f, 0.0f);
glColor3f(0.0f,1.0f,0.0f);
glVertex3f( 1.0f,-1.0f, -1.0f);
glColor3f(0.0f,0.0f,1.0f);
glVertex3f(-1.0f,-1.0f, -1.0f); 
glColor3f(1.0f,0.0f,0.0f);
glVertex3f( 0.0f, 1.0f, 0.0f);
glColor3f(0.0f,0.0f,1.0f);
glVertex3f(-1.0f,-1.0f,-1.0f);
glColor3f(0.0f,1.0f,0.0f);
glVertex3f(-1.0f,-1.0f, 1.0f);
glEnd();
}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.222.116.233