Рубрики
3D мир Программирование

OpenGL 1: начало работы в VS Code

В данной заметке производится настройка скриптов сборки для VS Code, установка библиотек для работы OpenGL на ОС Windows: glfw3 и glad

Введение

Целью данного курса заметок по 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”

Большое СПАСИБО !
Более полного и ясного изложения не встречал !
Всех благ !!!

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.

Управление cookie