OpenGL — 3D-графика на C++
Что такое OpenGL
OpenGL — кроссплатформенный графический API: набор функций для описания геометрии, текстур, шейдеров и состояния GPU. Драйвер видеокарты переводит вызовы OpenGL в команды железа.
Важно разделять роли:
- OpenGL — только рендер (что рисовать на GPU);
- GLFW / SDL / SFML — окно, контекст, swap buffers, ввод;
- GLAD / GLEW — загрузка указателей на функции OpenGL 3.3+ (на Windows
OpenGL32.dllзнает лишь устаревшие версии).
OpenGL не создаёт игру и не загружает PNG — это ваш C++ код и stb_image.
Теория конвейера и GLSL — OpenGL и шейдеры. Здесь — практический C++-стек и место OpenGL среди SFML, SDL, Raylib, DirectX и Vulkan.
Ключевые понятия
| Термин | Определение |
|---|---|
| Core profile | Режим OpenGL 3.3+ без устаревшего fixed-function |
| VBO | Vertex Buffer Object — массив вершин в памяти GPU |
| VAO | Vertex Array Object — привязка атрибутов вершин к шейдеру |
| EBO / IBO | Индексный буфер — переиспользование вершин в mesh |
| Shader program | Скомпилированная пара vertex + fragment shader |
| Uniform | Глобальная переменная шейдера (MVP, цвет, текстура) |
| MVP | Model × View × Projection — цепочка 3D-трансформаций |
| Framebuffer | Целевой буфер кадра (цвет + depth) |
Математика 3D — компьютерная графика.
Стек для учёбы на C++
| Компонент | Роль |
|---|---|
| GLFW | Окно, контекст OpenGL, glfwSwapBuffers, ввод |
| GLAD или GLEW | Загрузка glGen*, glDraw* и т.д. |
| GLM | mat4, vec3, lookAt, perspective |
| stb_image | PNG/JPG → пиксели для glTexImage2D |
| CMake | Сборка (1006) |
Окно через SDL: SDL_GL_CreateContext. Через SFML: window.setActive(true) + GLAD.
OpenGL, Vulkan и DirectX
| Критерий | OpenGL 3.3+ | Vulkan | DirectX 12 |
|---|---|---|---|
| Платформы | Win, Linux (macOS — deprecated) | Win, Linux, Android; macOS via MoltenVK | Windows, Xbox |
| Управление | Среднее | Полное | Полное |
| Первый треугольник | Быстрее | Дольше | Средне (D3D11 проще) |
| CPU overhead | Выше | Ниже | Низкий |
| Шейдеры | GLSL | SPIR-V | HLSL → DXIL |
Правило выбора: OpenGL — учёба 3D на Windows/Linux. Vulkan — свой движок (29). DirectX — Windows/Xbox (2746). 2D без шейдеров — Raylib или SFML.
Core profile и конвейер
С OpenGL 3.3 fixed-function (glBegin/glEnd) убран. Минимальная цепочка:
CPU: данные в VBO → привязка VAO → glDrawElements
GPU: Vertex Shader → Rasterization → Fragment Shader → Framebuffer
Этапы GPU (подробнее — 18):
- Vertex shader — позиция каждой вершины (
gl_Position), передача UV и нормалей; - Rasterization — треугольник → фрагменты (будущие пиксели);
- Fragment shader — цвет пикселя, текстуры, освещение;
- Depth test — Z-buffer отсекает скрытые поверхности.
Минимальный каркас на C++ с GLFW и GLAD
#include <glad/glad.h>
#include <GLFW/glfw3.h>
static void framebuffer_size_callback(GLFWwindow*, int w, int h) {
glViewport(0, 0, w, h);
}
int main() {
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
GLFWwindow* window = glfwCreateWindow(800, 600, "OpenGL", nullptr, nullptr);
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
glEnable(GL_DEPTH_TEST);
while (!glfwWindowShouldClose(window)) {
glClearColor(0.12f, 0.12f, 0.16f, 1.f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// drawScene(mvp);
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwTerminate();
return 0;
}
Разбор построчно
glfwWindowHint(..., CORE_PROFILE)— запрос modern OpenGL; без hints macOS может дать 2.1.glfwMakeContextCurrent— OpenGL привязан к потоку; вызывать GL из другого потока нельзя без sharing.gladLoadGLLoader(glfwGetProcAddress)— загрузка функций; до этогоglClearможет упасть.glEnable(GL_DEPTH_TEST)— без depth 3D-объекты "просвечивают" друг через друга.glClear— очистка color + depth буферов каждый кадр.glfwSwapBuffers— double buffering; показ готового кадра.glfwPollEvents— обработка закрытия окна и ввода.
Шейдеры подключают отдельно: читают .vert/.frag, glCompileShader, glLinkProgram, лог через glGetProgramInfoLog.
MVP и камера через GLM
glm::mat4 model = glm::mat4(1.f);
glm::mat4 view = glm::lookAt(
glm::vec3(0.f, 0.f, 3.f), glm::vec3(0.f), glm::vec3(0.f, 1.f, 0.f));
glm::mat4 proj = glm::perspective(
glm::radians(45.f), aspect, 0.1f, 100.f);
glm::mat4 mvp = proj * view * model;
glUniformMatrix4fv(locMvp, 1, GL_FALSE, &mvp[0][0]);
- Model — положение объекта в мире.
- View — положение камеры (обратная трансформация мира).
- Projection — перспектива или ортография.
- В vertex shader:
gl_Position = uMVP * vec4(aPos, 1.0).
Текстуры
Типичный порядок:
stbi_load→ пиксели на CPU;glGenTextures,glTexImage2D,glGenerateMipmap;- Fragment shader:
uniform sampler2D uTex;+texture(uTex, vUV).
OpenGL считает начало текстуры снизу; для stb часто нужен stbi_set_flip_vertically_on_load(true).
CMake-фрагмент
find_package(glfw3 REQUIRED)
find_package(glad REQUIRED)
find_package(glm CONFIG REQUIRED)
add_executable(gl_app main.cpp)
target_link_libraries(gl_app PRIVATE glfw glad glm::glm)
Шейдеры копируют в build dir: file(COPY shaders DESTINATION ${CMAKE_BINARY_DIR}).
OpenGL внутри других фреймворков
| Фреймворк | Как получить OpenGL |
|---|---|
| SFML | window.setActive(true) + GLAD |
| SDL | SDL_GL_CreateContext |
| Qt | QOpenGLWidget, Qt Quick Scene Graph |
| Raylib | GL скрыт; raw GL — отдельное окно GLFW/SDL |
Desktop UI на GL — Qt. Только Windows — DirectX.
Когда OpenGL уместен
| Сценарий | OpenGL |
|---|---|
| Учебный курс 3D, Linux/Windows | Да |
| Новый macOS-only продукт | Metal / MoltenVK |
| Максимум CPU→GPU throughput | Vulkan / D3D12 |
| 2D без шейдеров | Raylib, SFML |
Частые ошибки
| Симптом | Причина | Решение |
|---|---|---|
Segfault на glDraw* | GLAD не загружен | loader после context |
| Пустой экран | viewport 0×0 | callback resize |
| Розовые текстуры | format / mipmap | internalFormat, mipmap |
| Z-fighting | near plane слишком близко | поднять near, log depth |