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.
The most basic shapes that can be drawn in OpenGL are known as primitives. The primitives that can be drawn by OpenGL include:
That's it, folks! Everything known to exist can be created from these four primitives. Extrapolating into 3D, there are these 3D primitives:
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.
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:
glClear
function clears the buffer so that we can start drawing to it.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).glRotatef
function. Recall that the cube in the previous chapter slowly rotated.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.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
.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.
There is only one kind of point primitive.
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.
There are three kinds of line primitives, based on different interpretations the vertex list.
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.
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.
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.
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.
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();
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:
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:
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).
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();
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:
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.
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(); }
18.222.116.233