University of Wales Trinity Saint David
ACCA6021 - Graphics and Games Programming

Lab 04 - Two Triangles

Overview

Every lab so far has drawn a single triangle. Most real geometry is made of rectangles: UI panels, floor tiles, sprites, and faces of 3D meshes. This lab extends the vertex array to draw a quad by placing two triangles side by side along a shared diagonal. The code changes are small - a larger vertex array and an updated draw count - but the result exposes a real inefficiency in how vertex data is stored, which the Index Buffers and Uniforms lab (Lab 05) will fix.

Prerequisites

Learning Objectives

By the end of this lab you will be able to:

Concept

OpenGL's Core Profile renders only one primitive type at the geometry level: the triangle. There is no quad, rectangle, or polygon primitive. Every shape you draw must be decomposed into triangles before the GPU can process it. A rectangle has four corners, and no single triangle can cover all four, so two triangles are needed. They share a diagonal edge, and together they tile the rectangle exactly.

(-0.5, 0.5) *-----------* (0.5, 0.5) | T2 / | | / | | / | | / T1 | (-0.5,-0.5) *-----------* (0.5,-0.5)

With glDrawArrays, vertices are read from the buffer in order, three at a time. Triangle 1 consumes entries 0-2 and Triangle 2 consumes entries 3-5. Both triangles share the diagonal corners at (-0.5, -0.5) and (0.5, 0.5), but glDrawArrays has no way to reuse a position it has already read. Both shared corners must appear twice in the buffer. A quad therefore requires 6 entries to store 4 unique positions, wasting 33% of the buffer on duplication. For a mesh with thousands of triangles the waste grows far larger. The Index Buffers and Uniforms lab (Lab 05) solves this by letting the GPU reference positions by index rather than repeating them.

Tasks

Task 1 - Replace the Vertex Array

Open src/main.cpp and replace the 3-vertex triangle array with the 6-vertex quad array below. The comments mark which entries are duplicates of an earlier entry.

// file: src/main.cpp -- replace the existing vertices[] array
float vertices[] = {
    // Triangle 1 - bottom-right half of the quad
    -0.5f, -0.5f, 0.0f,   // 0: bottom-left
     0.5f, -0.5f, 0.0f,   // 1: bottom-right
     0.5f,  0.5f, 0.0f,   // 2: top-right

    // Triangle 2 - top-left half of the quad
     0.5f,  0.5f, 0.0f,   // 3: top-right    (same position as vertex 2)
    -0.5f,  0.5f, 0.0f,   // 4: top-left
    -0.5f, -0.5f, 0.0f    // 5: bottom-left  (same position as vertex 0)
};

The glBufferData call uses sizeof(vertices), so it will automatically upload all 6 entries without any other changes.

Task 2 - Update the Draw Call

Inside the render loop, update the vertex count passed to glDrawArrays. The GPU now needs to read 6 vertices (3 per triangle, 2 triangles) instead of 3.

  1. Find the line: glDrawArrays(GL_TRIANGLES, 0, 3);
  2. Change the count from 3 to 6.
// TODO: update the vertex count from 3 to 6
glDrawArrays(GL_TRIANGLES, 0, /* TODO */);

Task 3 - Count the Duplication

Before moving on, take a moment to measure the cost of the approach.

  1. How many unique positions does the quad have?
  2. How many entries are stored in vertices[]?
  3. Each entry is 3 floats at 4 bytes each. Calculate:
    • Bytes stored: 6 entries * 3 floats * 4 bytes = ?
    • Bytes needed: 4 entries * 3 floats * 4 bytes = ?
    • Bytes wasted on duplicates: ? bytes

For a quad the difference is small. A typical game character mesh has tens of thousands of triangles. Every vertex is shared by on average six triangles, so without reuse the buffer would be roughly six times larger than necessary. Lab 05 replaces this approach with an Index Buffer Object (IBO) that lets the GPU reference vertex 0 or vertex 2 by number, storing each unique position exactly once.

Expected Result

A solid coloured square appears in the centre of the window, the same colour as the triangle from Lab 03 (the shader is unchanged). The shape should fill a symmetrical region from -0.5 to +0.5 on both axes. If only one triangle appears, check that the draw count was changed to 6. If neither triangle appears, verify that glBufferData still uses sizeof(vertices) and has not been hard-coded to the old size.

Extension Challenge

Optional: Switch the renderer to wireframe mode to see the diagonal edge that separates the two triangles. Add the following line immediately before the draw call:

glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

The quad will render as two outlined triangles with the shared diagonal clearly visible. Restore solid rendering by changing GL_LINE to GL_FILL. For a harder challenge, try rearranging the vertex order to place the diagonal on the other axis (top-left to bottom-right instead of bottom-left to top-right) and confirm the quad still fills correctly.