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
- Lab 03 complete: a coloured triangle renders using a GLSL shader loaded from
res/shaders/triangle.shader. src/main.cppcontains theparseShaderandcreateProgramhelper functions from Lab 03.
Learning Objectives
By the end of this lab you will be able to:
- Explain why OpenGL requires all geometry to be expressed as triangles.
- Construct a quad from two triangles and identify which vertices are shared.
- Update an existing VAO and VBO to handle a larger vertex array without changing the shader.
- Describe the memory cost of duplicated vertices and explain why it motivates index buffers.
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.
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.
- Find the line:
glDrawArrays(GL_TRIANGLES, 0, 3); - Change the count from
3to6.
// 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.
- How many unique positions does the quad have?
- How many entries are stored in
vertices[]? -
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
- Bytes stored:
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.