Chapter 23. Working with OpenGL

This chapter reveals how to go beyond the familiar 2D and gain full control over the graphics pipeline. Here you’ll discover why OpenGL remains an industry standard, learn the secret of its seamless integration with Qt, and unlock how to achieve high rendering speed without sacrificing application architecture.

Without unnecessary theory and abstractions, it shows how to use QOpenGLWidget, manage rendering contexts, work with projection and modeling matrices, and apply antialiasing, primitives, and display lists. Just a few key rules—and your code starts working noticeably faster and more stable even in fullscreen mode.

This chapter is the transition point from “drawing” to “controlling graphics.” Skip it, and you’ll easily get stuck at the primitives level and miss out on the potential of hardware acceleration.

This chapter includes ready-to-use code examples.

Chapter Self-Check

What is an OpenGL context and who is responsible for creating it in Qt applications?Answer
Correct answer: An OpenGL context is a set of state variables for working with graphics. Each QOpenGLWidget class object creates it automatically.
Why does OpenGL use suffixes in function names (e.g., glColor3f)?Answer
Correct answer: Suffixes indicate the number and type of parameters passed: the digit indicates the number of arguments, the letter indicates their type (f — float, i — int, etc.).
What does a viewport represent in OpenGL?Answer
Correct answer: A viewport is a rectangular area within the widget window (“window within a window”) where rendering occurs. It’s set using the glViewport() function.
What is the unit of information in OpenGL and how are objects built from them?Answer
Correct answer: A vertex is the unit of information in OpenGL. Complex objects are built from vertices, with the glBegin() function defining how they connect (points, lines, triangles, quads).
Why does using OpenGL in Qt ensure complete platform independence?Answer
Correct answer: Qt automatically binds the rendering context to the windowing system of a specific platform, eliminating the need for developers to modify code for different operating systems.
Why is it necessary to clear the depth buffer (GL_DEPTH_BUFFER_BIT) before drawing a new frame?Answer
Correct answer: The depth buffer is used to remove hidden surfaces in 3D scenes. Clearing it is necessary for correctly determining object visibility in a new frame.
What is the fundamental difference between the modeling matrix and the projection matrix?Answer
Correct answer: The modeling matrix (GL_MODELVIEW) sets the position and orientation of an object in space, while the projection matrix (GL_PROJECTION) defines the method of projecting a 3D scene onto a 2D screen (orthogonal or perspective).
Why is the resizeGL() method the most convenient place to set the viewport?Answer
Correct answer: This method is automatically called when the widget size changes, receiving current dimensions in parameters, which allows proper adaptation of the viewport and projection proportions.
Which three methods must be overridden in a class inheriting from QOpenGLWidget, and why these specifically?Answer
Correct answer: initializeGL() for OpenGL setup when creating the context, resizeGL() for adapting to window size changes, paintGL() for actual scene rendering.
Why is glShadeModel(GL_FLAT) called in the pyramid example?Answer
Correct answer: To disable color smoothing mode (enabled by default) and get clear boundaries between different colored pyramid faces, otherwise the faces would have rainbow gradient coloring.
How can you make an OpenGL application fullscreen with one line of code?Answer
Correct answer: Replace the show() method call with showFullScreen(). This allows debugging the program in a window and simply changing one method for release.
What primitive type should be used to create a closed contour from line segments?Answer
Correct answer: GL_LINE_LOOP — creates a polyline, automatically connecting the last point to the first, unlike GL_LINE_STRIP, which requires repeating the first vertex at the end.
How do you create a quad with gradient fill from red to blue in OpenGL?Answer
Correct answer: Assign each vertex of the quad (GL_QUADS) its own color via glColor*() before glVertex*(). OpenGL will automatically perform color interpolation when GL_SMOOTH mode is enabled.
When is it more efficient to use GL_TRIANGLE_FAN instead of GL_TRIANGLES?Answer
Correct answer: When creating objects with a common central vertex (circles, cones, pyramids). GL_TRIANGLE_FAN requires fewer vertices since it reuses the first vertex for all triangles.

Practical Exercises

Easy Level

Rainbow Triangle
Create a Qt application with an OpenGL widget displaying an equilateral triangle in the center of the window. Each vertex should have its own color (red, green, blue), and thanks to smoothing, the interior should show smooth color transitions.
Hints: Inherit the class from QOpenGLWidget. In initializeGL(), set a black background. In resizeGL(), configure glOrtho() for coordinates from -1 to 1. In paintGL(), use GL_TRIANGLES, call glColor3f() before glVertex2f() for each vertex. Coordinates for an equilateral triangle: (0, 0.8), (-0.7, -0.4), (0.7, -0.4).

Medium Level

Interactive Rotating Star
Develop an OpenGL application displaying a five-pointed star. Implement the ability to rotate the star around its axis using arrow keys: left/right — rotation around the Z-axis, up/down — changing rotation speed. Add automatic rotation using a timer.
Hints: Create a method to generate star vertices (alternate outer and inner points). Use GL_LINE_LOOP or GL_TRIANGLE_FAN. Store rotation angle in a class member variable. Override keyPressEvent() to handle keys. Use QTimer with 16ms interval (≈60 FPS), in its slot increment the angle and call update(). In paintGL(), apply glRotatef() before drawing.

Hard Level

3D Cube with Textured Faces and Lighting
Create a full-fledged 3D application displaying a rotating cube with different textures on each face and basic lighting. Implement mouse camera control (as in the pyramid example), ability to switch between wireframe and filled rendering modes (W key), and rotation pause (spacebar). Add a light source that can be moved with WASD keys.
Hints: Create a method to build a cube through a display list, using GL_QUADS for six faces. Set normals (glNormal3f) for each face for correct lighting. In initializeGL(), enable lighting: glEnable(GL_LIGHTING) and glEnable(GL_LIGHT0), configure light parameters via glLightfv(). For textures, use QImage and glBindTexture(). Store light position in separate variables. For mode switching, use glPolygonMode(GL_FRONT_AND_BACK, GL_LINE/GL_FILL). Implement camera control by changing angles in mouseMoveEvent() and applying glRotatef() in paintGL().

🎨✨

Join the Discussion!

Immersed in the world of 3D graphics with OpenGL? Have questions about display lists or transformation matrices?

Share your experiments with graphics primitives, discuss rendering optimization, or help other readers understand the nuances of OpenGL integration with Qt!

Your experience can be the key to understanding for other developers 🚀

Leave a Reply

Your email address will not be published. Required fields are marked *