Frequently Asked Questions (FAQ) - vquanghuy/learn-opengl GitHub Wiki

This article addresses some common questions and design considerations that arose during the development of this Learn OpenGL project.

Q: Why is the main render loop kept in main.cpp instead of being placed inside the GLWindow class?

A: The main render loop is kept in main.cpp to maintain main as the central orchestrator of the application. This design keeps the GLWindow class focused solely on window and context management. This separation allows for greater flexibility in integrating other systems (like physics, UI, or multiple rendering passes) that drive the loop, which are typically managed outside the window class.

Q: What was the issue with the Xcode debugger on macOS, and how was it addressed?

A: A specific issue was encountered where the Xcode debugger on macOS could cause instability (such as spawning multiple windows) on immediate re-runs of the application. This was addressed by implementing a small sleep delay at the start of the application using a static helper method debuggerSleepWorkaround() within the GLWindow class. This workaround helps ensure the previous process fully terminates before a new debugging session begins.

The details is discussed in Xcode launches the program twice

Q: How are asset file paths managed when running the executable from Xcode's build directory?

A: Properly managing asset file paths (like shader and texture files) when running an executable from Xcode's build directory required using the "Copy Files" build phase. This phase is configured to copy assets relative to the executable, ensuring the program can find its resources regardless of the working directory from which it is launched.

Q: What were the common issues encountered with linking external libraries like GLFW and GLAD, and how were they resolved?

A: Discussions around linking external libraries like GLFW and GLAD highlighted common issues such as "Undefined symbols" errors during the linking phase. These were resolved by ensuring that the correct static libraries (.a) were linked and that all necessary macOS system frameworks (like IOKit, Cocoa, OpenGL, Metal, CoreFoundation, CoreGraphics) that the external libraries depend on were also included in the linking phase for the target.

Q: Why are individual Model, View, and Projection matrices passed to the vertex shader instead of a single combined MVP matrix?

A: The decision was made to pass individual Model, View, and Projection matrices to the vertex shader and perform the multiplication on the GPU. This approach provides greater flexibility for per-vertex calculations (like lighting) by making intermediate coordinate spaces (world space, view space) easily accessible within the shader. While there is a slight performance overhead from the extra multiplications, it is generally negligible on modern GPUs compared to the benefits gained in shader flexibility.

Q: Which libraries were considered for loading 3D models and images?

A: For loading 3D models, tinygltf (for glTF models) was discussed as a suitable, minimal option. For image decoding (needed for textures), stb_image.h was used. These libraries were chosen for their compatibility with C++ and OpenGL and their relative simplicity compared to larger alternatives like Assimp.

Q: What naming convention is used for shader variables?

A: A consistent naming convention is adopted for shader variables to improve readability. This involves using prefixes: a for vertex attributes (in variables in the vertex shader), u for uniforms (data set from the CPU), and v for varyings (data passed and interpolated between shader stages).

Refer Shader Naming Conventions: A Quick Guide

Q: Why is the class that manages the linked shader program named Shader instead of ShaderProgram?

A: While OpenGL technically distinguishes between individual shaders (vertex, fragment) and a linked shader program, the term "Shader" is commonly used in practice within the OpenGL community and many libraries as a shorthand or general term to refer to the complete, usable program object that is bound and used for rendering. Therefore, naming the class Shader aligns with this common convention.

Q: What is the difference between a Mesh and a Model?

A: In 3D graphics, a Mesh refers to the raw geometric data (vertices, edges, faces, and associated per-vertex attributes like normals and texture coordinates) that defines the shape of a single part of an object. A Model, on the other hand, is a higher-level representation of a complete object, often composed of one or more Meshes, along with material properties, textures, transformation data, and potentially a scene hierarchy or animation. The Mesh class in this project encapsulates the geometric data and its OpenGL rendering setup, while a Model would typically contain one or more Mesh objects and their associated appearance data.

Q: What is the difference between a Vertex Array Object (VAO) and Vertex Buffer Objects (VBO/EBO)?

A: Vertex Buffer Objects (VBOs) and Element Buffer Objects (EBOs) are OpenGL buffer objects that store the raw vertex data (like positions, normals, texture coordinates) and index data in GPU memory. A Vertex Array Object (VAO) is a state object that stores the configuration of how to interpret the data within one or more VBOs and an EBO. It remembers the bindings and configurations made by functions like glBindBuffer, glVertexAttribPointer, and glEnableVertexAttribArray, allowing you to restore the entire vertex state with a single bind call (glBindVertexArray). In essence, VBOs/EBOs hold the data, while a VAO holds the configuration of how to use that data.