Введение
Целью данного курса заметок по OpenGL является изучение возможностей данной библиотеки и разработка приложения для рисования трехмерной графики.
OpenGL — спецификация API, конкретные функции которой реализуются драйвером (или программной реализацией). Для использования функций OpenGL — их необходимо инициализировать, например с помощью библиотеки glad.
Для создания окна будет использоваться библиотека glfw3, которая обладает простым интерфейсом, в качестве альтернативы можно использовать SDL, который дает позволяет детальнее контролировать созданное окно.
В качестве IDE для разработки будет использоваться VS Code ввиду её скорости работы и простоты использования.
В проекте будет использоваться система сборки CMake. Заметки разрабатываются под GCC, но являются совместимыми с компиляторами, поддерживающими стандарт C++17.
Примеры по заметкам доступны в репозитории.
Правила сборки CMake
Репозиторий с заметками имеет следующую иерархию:
OpenGL_notes/
├── 01
│ ├── CMakeLists.txt - правила сборки заметки
│ └── src - исходные тексты программы
├──02
│ ├── CMakeLists.txt - правила сборки заметки
│ ├── include - заголовочные файлы
│ ├── shaders - шейдеры
│ └── src - исходные тексты программы
├── ...
├── build
│ ├── 01 - результаты сборки заметки
│ ├── 02 - результаты сборки заметки
│ ...
│ ├── bin - исполняемые файлы заметок
│ ...
├── lib - результат сборки библиотек
│ ...
│
├── CMakeLists.txt - правила сборки CMake для репозитория
├── dependencies - директория с библиотеками
│ ├── CMakeLists.txt - правила сборки библиотек
│ ├── glad - окна, контекст OpenGL и ввод
│ ├── glfw - загрузчик функций OpenGL
│ ├── glm - векторы, матрицы и математические операции
│ ├── tinygltf - загрузка моделей glTF
│ └── tinyobjloader - загрузка моделей OBJ
├── ...
└── README.md - инструкции
Важное замечание: для библиотеки glad требуется python-библиотека jinja2.
Команды для сборки примеров необходимо вызывать из корневой директории репозитория:
cmake -S . -B build
cmake --build build --parallel
Переменная CMAKE_BUILD_TYPE может задавать конфигурацию сборки:
Debug— сборка с отладочной информацией для GDB;Release— сборка с оптимизациями;RelWithDebInfo— сборка с отладочной информацией и включенными оптимизациями;MinSizeRel— минимизация размера приложения.
Настройка VS Code
Для работы с примерами рекомендуется использовать следующие расширения:
- C/C++ — группа расширений для работы с языком;
- CMake Tools — расширение для интеграции параметров из CMake.
Для настройки плагина CMake Tools в директории «.vscode» создан файл «settings.json» со следующим содержимым:
{
"cmake.buildDirectory": "${workspaceFolder}/build",
"cmake.configureOnOpen": true,
"C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools"
}
В данном файле можно изменить настройки расширения:
cmake.buildDirectory— директория сборки проекта;cmake.configureOnOpen— автоматическое выполнение конфигурации CMake при открытии проетка;C_Cpp.default.configurationProvider— источник информации о настройках, компиляции и путях для IntelliSense.
Создание окна
Создадим новый файл 01/src/main.cpp.
В начале файла подключим заголовочные файлы библиотек, а так же iostream для отладочного вывода и stdexcept для исключений:
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <stdexcept>
#include <iostream>
Далее необходимо определить константы, которые будут использоваться при создании окна:
#define WINDOW_WIDTH 800
#define WINDOW_HEIGHT 600
#define WINDOW_CAPTION "OPENGL notes on k-re.ru"
В определении функции main инициализируется glfw вызовом функции glfwInit. В случае ошибки — выдается исключение:
int main(void)
{
// Инициализация GLFW3
if (!glfwInit())
throw std::runtime_error("Ошибка инициализации GLFW");
С помощью функции glfwWindowHint задаются параметры для создаваемого окна, такие как: мажорная и минорная версии OpenGL и содержание контекста (поддержка расширений). Пример вызова функций glfwWindowHint:
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); // Мажорная версия спецификаций OpenGL
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6); // Минорная версия спецификаций OpenGL
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Контекст OpenGL, который поддерживает только основные функции
Для создания окна используется функция glfwCreateWindow, которая принимает в качестве аргументов ширину и высоту окна в пикселях, текст заголовка окна, а так же контекст монитора и контекст окна для обмена ресурсами. Последние два параметра не используются и заместо них передается NULL. В случае ошибки при создании окна выдается исключение и завершается работа приложения:
// Создание окна GLFW3 с заданными шириной, высотой и заголовком окна
GLFWwindow* window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_CAPTION, NULL, NULL);
if (!window)
throw std::runtime_error("Ошибка создания окна GLFW");
Так как библиотека glfw позволяет работать с несколькими контекстами OpenGL в разных окнах — имеется механизм переключения используемого контекста внутри вызывающего потока с помощью функции glfwMakeContextCurrent:
// Делает контекст OpenGL, связанный с окном, текущим для рендера в этом потоке
glfwMakeContextCurrent(window);
В OpenGL нормализованные координаты устройства (NDC) задаются числом с плавающей точкой (float) в диапазоне [-1;1] как по осям X, Y, Z. Для задания соответствия размеров окна к координатам окна используется функция glViewport, которая задает положение и размеры области вывода в пикселях. Если не вызывать данную функцию при изменении размеров окна — содержимое окна будет занимать лишь часть окна.
Добавим функцию window_resize_callback, которая будет обрабатывать изменение размеров окна:
// Функция-callback для обработки изменения размеров поверхности окна
void window_resize_callback(GLFWwindow* window, int width, int height)
{
std::cout << "Изменены размеры окна, w:" << width << " h: " << height << "\n";
glViewport(0, 0, width, height); // Задание области просмотра
}
Теперь необходимо связать окно и функцию-обработчик, для этого в функции main необходимо добавить вызов функции glfwSetFramebufferSizeCallback:
// Установка callback-функции для обработки изменения размеров
glfwSetFramebufferSizeCallback(window, window_resize_callback);
Для ограничения частоты кадров будет использоваться вертикальная синхронизация, подробнее об этом можно прочитать в соответствующей заметке: ограничение частоты кадров. Для включения вертикальной синхронизации используется функция glfwSwapInterval с параметром, равным 1:
// Вертикальная синхронизация для ограничения частоты кадров
glfwSwapInterval(1);
Как упоминалось во введении: OpenGL — спецификация API, конкретные функции которой реализуются драйвером (или программной реализацией). Для инициализации функций необходимо вызвать gladLoadGL с адресом функции glfwGetProcAddress:
// Загрузка функций OpenGL с помощью GLAD
if (!gladLoadGL(glfwGetProcAddress))
throw std::runtime_error("Ошибка GLAD при инициализации функций OpenGL");
Содержимое буфера кадра может быть перерисовано после каждого вывода на экран. Для очистки конкретного буфера используется функция glClear. Для задания цвета, которым будет выполнена очистка буфера используется функция glClearColor:
// Установка цвета очистки буфера цвета
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
Приложение будет выполнять цикл отрисовки на экран, пока библиотека glfw не сообщит о необходимости закрыть окно:
// Пока не произойдет событие запроса закрытия окна
while(!glfwWindowShouldClose(window))
{
В начале каждого кадра очистим содержимое буфера цвета с помощью glClear:
// Очистка буфера цвета
glClear(GL_COLOR_BUFFER_BIT);
Важное замечание: в компьютерных играх в целях снижения нагрузку на видеокарту избегают использования этой функции, так как сцена перерисовывается целиком благодаря скайбоксу, который затирает предыдущий кадр. Например такое поведение можно увидеть в Counter-Strike 1.6, если вылететь за пределы карты.
После очистки буфера цвета можно приступать к рисованию кадра, которое будет рассмотрено далее.
По завершению отрисовки необходимо вызывать glfwSwapBuffers — функцию переноса буфера кадра на поверхность окна. Так же требуется вызвать функцию glfwPollEvents, которая обработает события окна и вызовет callback-функции.
// Представление содержимого буфера цепочки показа на окно
glfwSwapBuffers(window);
// Обработка системных событий
glfwPollEvents();
}
Перед завершением работы программы необходимо уничтожить окно и завершить работу с библиотекой:
glfwDestroyWindow(window); // Уничтожим окно
glfwTerminate(); // Закончим работу с библиотекой
Итоговый текст программы:
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
#define WINDOW_WIDTH 800
#define WINDOW_HEIGHT 600
#define WINDOW_CAPTION "OPENGL notes on rekovalev.site"
// Функция-callback для изменения размеров буфера кадра в случае изменения размеров поверхности окна
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}
int main(void)
{
GLFWwindow* window; // Указатель на окно GLFW3
// Инициализация GLFW3
if (!glfwInit())
{
std::cout << "GLFW init error\n";
return -1;
}
// Завершение работы с GLFW3 перед выходом
atexit(glfwTerminate);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); // Мажорная версия спецификаций OpenGL
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6); // Минорная версия спецификаций OpenGL
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Контекст OpenGL, который поддерживает только основные функции
// Создание окна GLFW3 с заданными шириной, высотой и заголовком окна
window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_CAPTION, NULL, NULL);
if (!window)
{
std::cout << "GLFW create window error\n";
return -1;
}
// Установка основного контекста окна
glfwMakeContextCurrent(window);
// Установка callback-функции для изменения размеров окна и буфера кадра
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
// Загрузка функций OpenGL с помощью GLAD
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "GLAD load GL error\n";
return -1;
}
// Установка цвета очистки буфера цвета
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
// Пока не произойдет событие запроса закрытия окна
while(!glfwWindowShouldClose(window))
{
// Очистка буфера цвета
glClear(GL_COLOR_BUFFER_BIT);
// Тут производится рендер
// ...
// Представление содержимого буфера цепочки показа на окно
glfwSwapBuffers(window);
// Обработка системных событий
glfwPollEvents();
}
return 0;
}
Заключение
В рамках данной заметки была произведена настройка VS code для работы с примерами заметок и сборка библиотек и программы, создающей окно для дальнейшей работы.
Проект доступен в репозитории: OpenGL_notes/01
2 ответа к “OpenGL 1: начало работы в VS Code”
Большое СПАСИБО !
Более полного и ясного изложения не встречал !
Всех благ !!!
Полезный материал, сам бы долго разбирался