Hi, it is again me. Every game you’ve ever played, every simulation you’ve ever seen is just a bunch of this primitives. In this tutorial i will try to explain how to draw those simple primitives in opengl.

#include <GLTools.h>   // OpenGL toolkit
#include <GLMatrixStack.h>
#include <GLFrame.h>
#include <GLFrustum.h>
#include <GLBatch.h>
#include <GLGeometryTransform.h>

#include <math.h>
#ifdef __APPLE__
#include <glut/glut.h>
#include <GL/glut.h>

we declare an instance of GLMatrixStack for the modelview matrix and the projection matrix. We use the GLFrustum class to construct our projection matrix, and finally an instance of the GLGeometryTransform class to manage our matrix stacks.

// An assortment of needed classes
GLShaderManager shaderManager;
GLMatrixStack       modelViewMatrix;
GLMatrixStack       projectionMatrix;
GLFrame         cameraFrame;
GLFrame             objectFrame;
GLFrustum           viewFrustum;

The GLTools library contains a simple container class called GBatch. This class can contain a single batch of any of the seven primitives such as line,point,triangle etc., and it knows how to render the primitives when using any of the stock shaders supported by the GLShaderManager. Using the GLBatch class is simple. First initialize the batch, telling the class the type of primitive it represents, the number of vertices it will contain, and optionally, one or two sets of texture coordinates: void GLBatch::Begin(GLenum primitive, GLuint nVerts, GLuint nTextureUnits = 0); Then, at a minimum, copy in an array of three component (x, y, z) vertices. void GLBatch::CopyVertexData3f(GLfloat *vVerts); Optionally, you can also copy in surface normals, colors, and texture coordinates as well: void GLBatch::CopyNormalDataf(GLfloat *vNorms); void GLBatch::CopyColorData4f(GLfloat *vColors); void GLBatch::CopyTexCoordData2f(GLfloat *vTexCoords, GLuint uiTextureLayer); When you are finished, you can call End to signify you are done copying in data, and the internal flags will be set so the class knows which attributes it contains. void GLBatch::End(void); The GLBatch class, however, is simply a convenience class, much like using GLUT is convenient so you don’t have to worry about OS specifics until you are ready.

GLBatch    pointBatch;
GLBatch lineBatch;
GLBatch lineStripBatch;
GLBatch lineLoopBatch;
GLBatch triangleBatch;
GLBatch   triangleStripBatch;
GLBatch   triangleFanBatch;

You can probably guess that having a matrix stack for both the modelview matrix and the projection matrix carries a lot of advantages. Very often you will also need to retrieve both of these matrices and multiply them to get the modelview projection matrix. Another useful matrix is the normal matrix, which is used for lighting computations and is derived from the modelview matrix. Another utility class, GLGeometryTransform keeps track of these two matrix stacks for you and quickly retrieves the top of either matrix stack, the modelview projection matrix, or the normal matrix.

GLGeometryTransform    transformPipeline;
M3DMatrix44f        shadowMatrix;

GLfloat vGreen[] = { 0.0f, 1.0f, 0.0f, 1.0f };
GLfloat vBlack[] = { 0.0f, 0.0f, 0.0f, 1.0f };

// Keep track of effects step
int nStep = 0;

// This function does any needed initialization on the rendering context. 
// This is the first opportunity to do any OpenGL related tasks.
void SetupRC()
    // gray background
    glClearColor(0.7f, 0.7f, 0.7f, 1.0f );


Depth testing is another effective technique for hidden surface removal. The concept is simple: When a pixel is drawn, it is assigned a value (called the z value) that denotes its distance from the viewer’s perspective. Later, when another pixel needs to be drawn to that screen location, the new pixel’s z value is compared to that of the pixel that is already stored there. If the new pixel’s z value is higher, it is closer to the viewer and thus in front of the previous pixel, so the previous pixel is obscured by the new pixel. If the new pixel’s z value is lower, it must be behind the existing pixel and thus is not obscured. This maneuver is accomplished internally by a depth buffer with storage for a depth value for every pixel on the screen. Almost all the samples in this book use depth testing. You should request a depth buf fer when you set up your OpenGL window wi th GLUT. For example, you can request a color and a depth buffer like this:


To enable depth testing, simply call:


    transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);

    // Some points, more or less in the shape of Florida
    GLfloat vCoast[24][3] = {{2.80, 1.20, 0.0 }, {2.0,  1.20, 0.0 },
                            {2.0,  1.08, 0.0 },  {2.0,  1.08, 0.0 },
                            {0.0,  0.80, 0.0 },  {-.32, 0.40, 0.0 },
                            {-.48, 0.2, 0.0 },   {-.40, 0.0, 0.0 },
                            {-.60, -.40, 0.0 },  {-.80, -.80, 0.0 },
                            {-.80, -1.4, 0.0 },  {-.40, -1.60, 0.0 },
                            {0.0, -1.20, 0.0 },  { .2, -.80, 0.0 },
                            {.48, -.40, 0.0 },   {.52, -.20, 0.0 },
                            {.48,  .20, 0.0 },   {.80,  .40, 0.0 },
                            {1.20, .80, 0.0 },   {1.60, .60, 0.0 },
                            {2.0, .60, 0.0 },    {2.2, .80, 0.0 },
                            {2.40, 1.0, 0.0 },   {2.80, 1.0, 0.0 }};
    // Load point batch
    pointBatch.Begin(GL_POINTS, 24);

A line segment is drawn between two vertices, so a batch of lines should consist of an even number of vertices, one for each end of the line segment.

    // Load as a bunch of line segments
    lineBatch.Begin(GL_LINES, 24);

For a true connect-the-dots parallel, line strips draw line segments from one vertex to the next continually. To make a continuous line around the state of Florida with separated lines, each of the connecting vertices would have to be specified twice. Once as the end of one line segment, and then sent down again as the beginning of the next line segment. Moving all this extra data and transforming the same point twice is waste of bandwidth and clock cycles on the GPU.

    // Load as a single line segment
    lineStripBatch.Begin(GL_LINE_STRIP, 24);

A simple extension to line strips, a line loop draws an extra line segment from the end of the batch back to the beginning. This provides a net savings of only one vertex but is convenient when you are trying to close a loop or line-based figure. With just these primitives, you could easily draw any shape possible in three dimensions. You could, for example, draw six squares and arrange them so they form the sides of a cube.

// Single line, connect first and last points
    lineLoopBatch.Begin(GL_LINE_LOOP, 24);
    // For Triangles, we'll make a Pyramid
    GLfloat vPyramid[12][3] = { -2.0f, 0.0f, -2.0f, 
                                2.0f, 0.0f, -2.0f, 
                                0.0f, 4.0f, 0.0f,
                                2.0f, 0.0f, -2.0f,
                                2.0f, 0.0f, 2.0f,
                                0.0f, 4.0f, 0.0f,
                                2.0f, 0.0f, 2.0f,
                                -2.0f, 0.0f, 2.0f,
                                0.0f, 4.0f, 0.0f,
                                -2.0f, 0.0f, 2.0f,
                                -2.0f, 0.0f, -2.0f,
                                 0.0f, 4.0f, 0.0f};
    triangleBatch.Begin(GL_TRIANGLES, 12);

    // For a Triangle fan, just a 6 sided hex. Raise the center up a bit
    GLfloat vPoints[100][3];    // Scratch array, more than we need
    int nVerts = 0;
    GLfloat r = 3.0f;
    vPoints[nVerts][0] = 0.0f;
    vPoints[nVerts][1] = 0.0f;
    vPoints[nVerts][2] = 0.0f;

    for(GLfloat angle = 0; angle < M3D_2PI; angle += M3D_2PI / 6.0f) {
        vPoints[nVerts][0] = float(cos(angle)) * r;
        vPoints[nVerts][1] = float(sin(angle)) * r;
        vPoints[nVerts][2] = -0.5f;
    // Close the fan
    vPoints[nVerts][0] = r;
    vPoints[nVerts][1] = 0;
    vPoints[nVerts][2] = 0.0f;

you can use GL_TRIANGLE_FAN to produce a group of connected triangles that fan around a central point.A fan of three triangles produced by specifying four vertices. The first vertex, V0, forms the origin of the fan. After the first three vertices are used to draw the initial triangle, all subsequent vertices are used with the origin (V0) and the vertex immediately preceding it (Vn–1) to form the next triangle.

// Load it up
    triangleFanBatch.Begin(GL_TRIANGLE_FAN, 8);
    // For triangle strips, a little ring or cylinder segment
    int iCounter = 0;
    GLfloat radius = 3.0f;
    for(GLfloat angle = 0.0f; angle <= (2.0f*M3D_PI); angle += 0.3f)
        GLfloat x = radius * sin(angle);
        GLfloat y = radius * cos(angle);
        // Specify the point and move the Z value up a little   
        vPoints[iCounter][0] = x;
        vPoints[iCounter][1] = y;
        vPoints[iCounter][2] = -0.5;

        vPoints[iCounter][0] = x;
        vPoints[iCounter][1] = y;
        vPoints[iCounter][2] = 0.5;

    // Close up the loop
    vPoints[iCounter][0] = vPoints[0][0];
    vPoints[iCounter][1] = vPoints[0][1];
    vPoints[iCounter][2] = -0.5;
    vPoints[iCounter][0] = vPoints[1][0];
    vPoints[iCounter][1] = vPoints[1][1];
    vPoints[iCounter][2] = 0.5;

For many surfaces and shapes, you need to draw several connected triangles. You can save a lot of time by drawing a strip of connected triangles with the GL_TRIANGLE_STRIP primi- tive. For the progression of a strip of three triangles specified by a set of five vertices numbered V0 through V4. Here, you see that the vertices are not necessarily traversed in the same order in which they were specified. The reason for this is to preserve the winding (counterclockwise) of each triangle. The pattern is V0, V1, V2; then V2, V1, V3; then V2, V3, V4; and so on. There are two advantages to using a strip of triangles instead of specifying each triangle separately. First, after specifying the first three vertices for the initial triangle, you need to specify only a single point for each additional triangle. This saves a lot of program or data storage space when you have many triangles to draw. The second advantage is mathemati- cal performance and bandwidth savings. Fewer vertices means a faster transfer from your computer’s memory to your graphics card and fewer times your vertex shader must be executed.

    // Load the triangle strip
    triangleStripBatch.Begin(GL_TRIANGLE_STRIP, iCounter);

This renders a green batch of primitives and then draws a black wireframe version over it. Note that we used thicker, antialiased lines for the outlines for a better appearance. We talk more about antialiasing in the upcoming section on blending.

void DrawWireFramedBatch(GLBatch* pBatch)
    // Draw the batch solid green
                     transformPipeline.GetModelViewProjectionMatrix(), vGreen);

While the depth buffer can have positive visual and performance effects, sometimes it just gets a little in the way, and you might need to fib to it just a little bit. This happens when ever you intentionally want to draw two pieces of geometry in the same place. This might sound odd, but consider two cases. At times, you may want to draw a large plane and then draw a smaller shape over the plane but in the same physical location. This is called decal- ing, and you might for example draw a star shape over a flat surface to make a design. In this case, the depth values of the star will be the same or nearly the same as the values in the depth buffer from drawing the original plane. This causes fragments to pass or fail the depth test unpredictably and can leave nasty visual artifacts from what is commonly called z-fighting.Another case (and easier to demonstrate with our current examples), is when you want to draw solid geometry but want to highlight the edges. In the example program Primitives, presented earlier, triangles, triangle fans, and triangle strips were all drawn in green but with black lines showing the individual triangles. This is not the default behavior, and we had to take special care to make this happen. To see the triangle edges, we would need to draw the strip using glPolygonMode as shown in the previous section.The glPolygonOffset function shown here allows you to tweak the depth values of fragments, thus offsetting the depth values but not the actual physical location in 3D space. void glPolygonOffset(GLfloat factor, GLfloat units); The total offset applied to fragments is given by this equation: Depth Offset = (DZ X factor) + (r X units) DZ is the change in depth values (the z) relative to the screen area of the polygon, and r is the smallest value that produces a change in depth buffer values.

    // Draw black outline
    glPolygonOffset(-1.0f, -1.0f);      // Shift depth values

to using glPolygonOffset to set up your offset values, you must enable polygon offset separately for filled geometry (GL_POLYGON_OFFSET_FILL), lines (GL_POLYGON_OFFSET_LINE), and points (GL_POLYGON_OFFSET_POINT)


Turning on antialiasing is simple. First, you must enable blending and set the blending function to be the same as you used in the preceding section for transparency:


You also need to make sure the blend equat ion is set to GL_ADD, but because this is the default and most common blending equation, we don’t show it here. After blending is enabled and the proper blending function and equation are selected, you can choose to antialias points, lines, and/or polygons (any solid primitive) by calling glEnable:

glEnable(GL_POINT_SMOOTH);    // Smooth out points 
glEnable(GL_LINE_SMOOTH);     // Smooth out lines 
glEnable(GL_POLYGON_SMOOTH);  // Smooth out polygon edges

You should use GL_POLYGON_SMOOTH with care. You might expect to smooth out edges on solid geometry, but there are other tedious rules to making this work. For example, geome- try that overlaps requires a different blending mode, and you may need to sort your scene from front to back.

    // Draw lines antialiased

You already learned that OpenGL rendering places color values in the color buffer under normal circumstances. You also learned that depth values for each fragment are also placed in the depth buffer. When depth testing is turned off (disabled), new color values simply overwrite any other values already present in the color buffer. When depth testing is turned on (enabled), new color fragments replace an existing fragment only if they are deemed closer to the near clipping plane than the values already there. Under normal circumstances then, any drawing operation is either discarded entirely, or just completely overwrites any old color values, depending on the result of the depth test. This oblitera- tion of the underlying color values no longer happens the moment you turn on OpenGL blending: glEnable(GL_BLEND); When blending is enabled, the incoming color is combined with the color value already present in the color buffer. How these colors are combined leads to a great many and varied special effects. How the source and destination colors are combined when blending is enabled is controlled by the blending equation. By default, the blending equation looks like this: Cf = (Cs * S) + (Cd * D) Here, Cf is the final computed color, Cs is the source color, Cd is the destination color, and S and D are the source and destination blending factors. These blending factors are set with the following function:

glBlendFunc(GLenum S, GLenum D);

As you can see, S and D are enumerants and not physical values that you specify directly.


Polygons (triangles) don’t have to be solid. By default, polygons are drawn solid, but you can change this behavior by specifying that polygons are to be drawn as outlines or just points (only the vertices are plotted). The function glPolygonMode allows polygons to be rendered as filled solids, as outlines, or as points only. In addition, you can apply this rendering mode to both sides of the polygons or only to the front or back. the face parameter can be GL_FRONT, GL_BACK, or GL_FRONT_AND_BACK. The mode parameter can be GL_FILL (the default), GL_LINE, or GL_POINT.

// Draw black wireframe version of geometry
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
                     transformPipeline.GetModelViewProjectionMatrix(), vBlack);
    // Put everything back the way we found it
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

// Called to draw scene
void RenderScene(void)
    // Clear the window with current clearing color

    M3DMatrix44f mCamera;

      M3DMatrix44f mObjectFrame;

      shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);

        switch(nStep) {
            case 0:

You can change the default point size by calling glPointSize. void glPointSize(GLfloat size); Not all point sizes are supported, however, and you should make sure the point size you specify is available. Use the following code to get the range of point sizes and the smallest interval between them:

GLfloat sizes[2];     // Store supported point size range 
GLfloat step;         // Store supported point size increments

// Get supported point size range and step size 

the shader built-in variable is gl_PointSize, and in your shader source code, you’d simply set it like this:

gl_PointSize = 5.0;

Another way to set the point size is to enable program point size mode.

            case 1:

Lines are by default one pixel in width. The only way to change a line’s width is with the function glLineWidth. void glLineWidth(GLfloat width);

            case 2:
            case 3:
            case 4:
            case 5:
            case 6:

    // Flush drawing commands

// Respond to arrow keys by moving the camera frame of reference
void SpecialKeys(int key, int x, int y)
    if(key == GLUT_KEY_UP)
        objectFrame.RotateWorld(m3dDegToRad(-5.0f), 1.0f, 0.0f, 0.0f);
    if(key == GLUT_KEY_DOWN)
        objectFrame.RotateWorld(m3dDegToRad(5.0f), 1.0f, 0.0f, 0.0f);
    if(key == GLUT_KEY_LEFT)
        objectFrame.RotateWorld(m3dDegToRad(-5.0f), 0.0f, 1.0f, 0.0f);
    if(key == GLUT_KEY_RIGHT)
        objectFrame.RotateWorld(m3dDegToRad(5.0f), 0.0f, 1.0f, 0.0f);

// A normal ASCII key has been pressed.
// In this case, advance the scene when the space bar is pressed
void KeyPressFunc(unsigned char key, int x, int y)
    if(key == 32)

        if(nStep > 6)
            nStep = 0;
        case 0: 
        case 1:
        case 2:
        case 3:
        case 4:
        case 5:
        case 6:
// Window has changed size, or has just been created. In either case, we need
// to use the window dimensions to set the viewport and the projection matrix.
void ChangeSize(int w, int h)
    glViewport(0, 0, w, h);
    viewFrustum.SetPerspective(35.0f, float(w) / float(h), 1.0f, 500.0f);

// Main entry point for GLUT based programs
int main(int argc, char* argv[])
    glutInit(&argc, argv);

You should request a depth buf fer when you set up your OpenGL window wi th GLUT. For example, you can request a color and a depth buffer like this:

    glutInitWindowSize(800, 600);

    GLenum err = glewInit();
    if (GLEW_OK != err) {
        fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
        return 1;

    return 0;

Another way to improve rendering performance is to update only the portion of the screen that has changed. You may also need to restrict OpenGL rendering to a smaller rectangular region inside the window. OpenGL allows you to specify a scissor rectanglewithin your window where rendering can take place. By default, the scissor rectangle is the size of the window, and no scissor test takes place. You turn on the scissor test with the ubiquitous glEnable function:


You can, of course, turn of f the scissor test again wi th the corresponding glDisable function call. The rectangle within the window where rendering is performed, called the scissor box, is specified in window coordinates (pixels) with the following function:

void glScissor(GLint x, GLint y, GLsizei width, GLsizei height);

The x and y parameters specify the lower-left corner of the scissor box, with width and height being the corresponding dimensions of the scissor box.

Now you can draw images below:

Here you can download the complete code given above:


Bir Cevap Yazın

Aşağıya bilgilerinizi girin veya oturum açmak için bir simgeye tıklayın:

WordPress.com Logosu

WordPress.com hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Google+ fotoğrafı

Google+ hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Twitter resmi

Twitter hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Facebook fotoğrafı

Facebook hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )


Connecting to %s