3.1. Drawing a triangle

This section describes how to draw a 2D triangle:

  1. Select the tutorial project 06 - Drawing a Triangle the Hard Way and open the main.cpp file.

  2. Example 3.1 shows placing the processing code, including the initialization code for the context and keyboard, in to a try block:

    Example 3.1. Creating the system objects

    #include <mde/mde.h>
    using namespace MDE;
    int main(int argc, char** argv)
            Managed<System> system = create_system_backend();    
            Managed<Context> context = system->createContext(320, 240);
            Managed<Timer> timer = system->createTimer();
            #ifdef MDE_OS_PC_LINUX
                Managed<Keyboard> keyboard = system->createKeyboard(context);
                Managed<Keyboard> keyboard = system->createKeyboard();

    Use the Context interface to:

    • create a context to draw with

    • create GL objects that are linked to a context such as:

      • textures

      • buffers

      • shader programs.

  3. Because this example actually draws a shape to the context, there must be a vertex and fragment shader. Example 3.2 shows how to define simple identity shaders:

    Example 3.2. Identity shaders

            * Define the shader sources. The following GLSL shaders define a simple identity-transform vertex 
            * shader and a fragment shader which outputs the color value calculated in the vertex shader.
            const char* vertexsource =
                "attribute vec4 POSITION; \
                attribute vec4 COLOR;\
                varying vec4 col;\
                void main(void)\
                col = COLOR;\
                gl_Position = POSITION;\
            const char* fragmentsource =
               "precision mediump float; \
               varying vec4 col;\
               void main(void)\
               gl_FragColor = col;\

  4. The shaders in vertexsource and fragmentsource are text strings that contain the source. Example 3.3 shows how to associate the shaders with the context:

    1. compile the shaders with createShader()

    2. use createProgram() to create a new program that uses the compiled shaders

    3. activate the program for the context with setProgram().

    Example 3.3. Building the identity shaders

            * Create Shader and Program objects. This is done using the createShader() and createProgram() functions
            * on the context object. We then set our newly created program active using setProgram().
            Managed<Shader> vertexshader = context->createShader(GL_VERTEX_SHADER, vertexsource);
            Managed<Shader> fragmentshader = context->createShader(GL_FRAGMENT_SHADER, fragmentsource);
            Managed<Program> program = context->createProgram(vertexshader, fragmentshader);

    The vertexshader, fragmentshader, and program all use the Managed<> template format to automatically manage object destruction.

  5. Specify the triangle as shown in Example 3.4. There are two parts to the specification:

    • Because this is a 2D triangle, there are three coordinates and each coordinate is specified with two numbers that locate the vertex in the XY plane.

    • This example uses vertex coloring, but all of the colors for the triangle vertices are set to red. Colors are specified with four numbers that indicate the red, blue, green, and alpha channel values.

    Example 3.4. Triangle coordinates

            GLfloat vertexData[] = 
                -1.0, -1.0, 
                 1.0, -1.0, 
                 0.0,  1.0 
            GLubyte colorData[] =
                255, 0, 0, 255,
                255, 0, 0, 255,
                255, 0, 0, 255

  6. The coordinate and color information are associated with new buffers as shown in Example 3.5:

    Example 3.5. Creating vertex and color buffers for the triangle

            Managed<Buffer> vertexBuffer = context->createBuffer(GL_ARRAY_BUFFER, sizeof(vertexData),
            vertexBuffer->setData(0, sizeof(vertexData), vertexData);
            Managed<Buffer> colorBuffer = context->createBuffer(GL_ARRAY_BUFFER, sizeof(colorData),
            colorBuffer->setData(0, sizeof(colorData), colorData);
            VertexElement elements[2];
            elements[0].components = 2;                    
            elements[0].offset = 0;                        
            elements[0].semantic = POSITION;            
            elements[0].stream = 0;                        
            elements[0].type = GL_FLOAT;                
            elements[0].normalize = false;                
            elements[1].components = 4;                    
            elements[1].offset = 0;                        
            elements[1].semantic = COLOR;                
            elements[1].stream = 1;                        
            elements[1].type = GL_UNSIGNED_BYTE;        
            elements[1].normalize = true;                
            Managed<VertexDeclaration> vertexDeclaration = context->createVertexDeclaration(elements, 2);

    There are several buffers created in Example 3.5:

    1. createBuffer() creates vertexBuffer and colorBuffer objects from vertexData and colorData

    2. A VertexElement array named elements is created. The array size is two because there are position and color attributes for each vertex.

    3. Each VertexElement object describes one vertex attribute. The values of the each of the elements[] attributes is initialized:

      • The first element has two components because each vertex specifies a position in the XY plane. The type is GL_FLOAT because floating-point numbers specify the position.

      • The second element has four components because it specifies a color. The type is GL_UNSIGNED_BYTE because, in this example, 8-bit numbers specify the color and alpha channel values.

      For more information on attributes, see Passing data as uniforms.

    4. A vertexDeclaration object is created from elements. There are two objects in the elements array.

    5. setVertexDeclaration() associates the new vertexDeclaration object with the context.

  7. The triangle has been specified, so it can be drawn as shown in Example 3.6.

    For each iteration of the loop:

    1. The first element of the vertex buffer that is associated with context is set to the vertexBuffer object defined previously.

    2. The second element of the vertex buffer that is associated with context is set to the colorBuffer object defined previously.

    3. drawArrays() uses the vertex buffer arrays to draw the first object:

      • The object to draw is GL_TRIANGLES so three vertices are used.

      • The second parameter is 0, so the vertices start from the beginning of the buffer.

      • The third parameter specifies the primitiveCount. There is only one primitive, a triangle, so the value is 1.

    4. A test for the escape key provides a way to end the display.

    5. If the context window is closed, update() returns false and the do loop ends.

    Example 3.6. Using the vertex information to draw the triangle

                context->setVertexBuffer(0, vertexBuffer);
                context->setVertexBuffer(1, colorBuffer);
                /* Draw indexed triangles using the first 3 vertices of the buffers */
                context->drawArrays(GL_TRIANGLES, 0, 1);
                if(keyboard->getSpecialKeyState(Keyboard::KEY_ESCAPE)) break;
            } while( context->update() );

  8. All of the execution code was enclosed in one try catch block which ends with the code in Example 3.7. If there is an exception, a message is displayed in the system window.

    Example 3.7. Trapping an exception in the triangle example

        catch(Exception& e)
            printf("MDE \n%s\n\n", e.getMessage().getCharString());

  9. Figure 3.1 shows the triangle:

    Figure 3.1. The 2D red triangle

    The 2D red triangle

Copyright © 2010 ARM. All rights reserved.ARM DUI 0527A‑02a
Non-Confidential - Draft - BetaID070710