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

Lab 01 - Window and Context

Overview

In the Environment Setup lab (Lab 00) you installed the toolchain and verified the project compiles. This lab focuses on what happens at runtime: initialising GLFW to create an OS window, establishing an OpenGL 4.3 Core Profile context, and loading OpenGL function pointers with GLEW. You will then write the main render loop that drives every frame for the rest of the module.

All subsequent labs build directly on the skeleton you produce here. Getting this foundation right, including correct error handling and context version targeting, prevents hard-to-debug failures later.

Prerequisites

Learning Objectives

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

Concept

OpenGL does not manage windows or input. It is purely a rendering API. Without a window there is nowhere to draw, and without a context the GPU driver does not know which application's state to modify. GLFW solves the first problem: it provides a cross-platform API for creating OS windows and an attached OpenGL context. Once a window exists, calling glfwMakeContextCurrent binds that context to the current thread so every subsequent OpenGL call targets it.

OpenGL function addresses (pointers like glClear, glGenBuffers, etc.) are not statically linked. They are resolved at runtime from the GPU driver. GLEW queries the driver for every function the application might use and stores the addresses in global function pointers. Setting glewExperimental = GL_TRUE before calling glewInit forces GLEW to load all extensions even in a Core Profile context, which would otherwise cause some pointers to remain null.

The main loop runs once per frame. Each iteration clears the previous frame's pixels with glClear, renders the scene, then calls glfwSwapBuffers to flip the back buffer to the screen (double-buffering). Finally, glfwPollEvents processes any queued OS messages such as keyboard input or window resize. Calling glfwSwapInterval(1) before the loop enables V-Sync, which ties the swap to the display refresh rate and prevents screen tearing.

Tasks

Task 1 - Initialise GLFW and Set Version Hints

Open src/main.cpp and add the following at the top of main().

  1. Call glfwInit() and check its return value. Print an error message and return -1 if it fails.
  2. Call glfwWindowHint three times to target OpenGL 4.3 Core Profile:
    • GLFW_CONTEXT_VERSION_MAJOR set to 4
    • GLFW_CONTEXT_VERSION_MINOR set to 3
    • GLFW_OPENGL_PROFILE set to GLFW_OPENGL_CORE_PROFILE
// file: src/main.cpp
int main() {
    // TODO: Call glfwInit() and return -1 with an error message if it fails.

    // TODO: Set GLFW_CONTEXT_VERSION_MAJOR to 4.
    // TODO: Set GLFW_CONTEXT_VERSION_MINOR to 3.
    // TODO: Set GLFW_OPENGL_PROFILE to GLFW_OPENGL_CORE_PROFILE.
}

Task 2 - Create the Window and Make the Context Current

Immediately after the hints, create the window and bind its context.

  1. Call glfwCreateWindow(800, 600, "OpenGL Window", NULL, NULL) and store the result in a GLFWwindow* named window.
  2. Check that window is not nullptr. If it is, call glfwTerminate() and return -1.
  3. Call glfwMakeContextCurrent(window) to bind the context to this thread.
// file: src/main.cpp  (after the window hints)

    // TODO: Create an 800x600 window titled "OpenGL Window".
    //       Store the result in GLFWwindow* window.

    // TODO: If window is nullptr, terminate GLFW and return -1.

    // TODO: Make the window's context current on this thread.

Task 3 - Initialise GLEW

After the context is current, load OpenGL function pointers.

  1. Set glewExperimental = GL_TRUE before calling glewInit.
  2. Call glewInit(). If it does not return GLEW_OK, print an error, call glfwTerminate(), and return -1.
  3. Print the loaded OpenGL version to confirm everything is working:
    std::cout << "OpenGL version: " << glGetString(GL_VERSION) << std::endl;
  4. Call glfwSwapInterval(1) to enable V-Sync.
// file: src/main.cpp  (after glfwMakeContextCurrent)

    // TODO: Set glewExperimental = GL_TRUE.

    // TODO: Call glewInit(). If it fails, terminate GLFW and return -1.

    // TODO: Print the OpenGL version string using glGetString(GL_VERSION).

    // TODO: Call glfwSwapInterval(1) to cap to the monitor refresh rate.

Task 4 - Write the Main Render Loop

Add the loop that runs every frame until the window is closed.

  1. Write a while loop that continues as long as !glfwWindowShouldClose(window) is true.
  2. Inside the loop, call glClearColor(0.1f, 0.1f, 0.15f, 1.0f) to set a dark blue-grey clear colour.
  3. Call glClear(GL_COLOR_BUFFER_BIT) to clear the colour buffer.
  4. Call glfwSwapBuffers(window) to present the back buffer.
  5. Call glfwPollEvents() to process queued OS events.
// file: src/main.cpp  (after glfwSwapInterval)

    // TODO: Loop while the window should not close.
    while (!glfwWindowShouldClose(window)) {

        // TODO: Set the clear colour to a dark blue-grey (r=0.1, g=0.1, b=0.15, a=1.0).

        // TODO: Clear the colour buffer.

        // TODO: Swap front and back buffers.

        // TODO: Poll for OS events (keyboard, mouse, window close).
    }

Task 5 - Shutdown and Cleanup

After the loop exits, release all GLFW resources. The code below is provided as a reference - add it after the closing brace of your render loop.

// file: src/main.cpp  (after the render loop) -- provided for reference
    glfwDestroyWindow(window);
    glfwTerminate();
    return 0;
  1. Build the project with Ctrl+Shift+B and check the Output window for errors.
  2. Run with F5 (Debug) or Ctrl+F5 (without debugger).
  3. Verify the console prints the OpenGL version string before the window opens.

Expected Result

An 800x600 window titled "OpenGL Window" opens. The interior is filled with a solid dark blue-grey colour (no flickering). The console prints a line similar to:

OpenGL version: 4.3.0 NVIDIA 560.94

Clicking the window's close button exits the application cleanly with no crash or assertion failure. The frame rate is capped to the monitor's refresh rate (typically 60 fps) due to V-Sync.

Extension Challenge

Optional: Make the clear colour pulse smoothly over time. Use glfwGetTime() inside the loop to get the elapsed seconds as a float, then pass sin(time) * 0.5f + 0.5f as the red channel of glClearColor. The window should cycle from dark to a warm reddish tint and back roughly once per second. Try mapping different channels to sine waves of different frequencies to produce a colour cycling effect.