+--------------------------+
|.------------------------.|
|| kee_reel@blog:~$ cd    ||
|| си python терминал     ||
|| opengl sql             ||
||                        ||
||                обо_мне ||
|.------------------------.|
+-::--------------------::-+
.--------------------------.
 // /ooooooooooooooooooooo\\ \\ 
 // /ooooooooooooooooooooooo\\ \\ 
//------------------------------\\
\\------------------------------//

OpenGL/C++. Первое окно

В этой статье я расскажу как происходит инициализация OpenGL и создаётся окно в котором мы будем всё отрисовывать.

В прошлой статье я рассказал как настроить окружение и запустить самый простой проект – продолжу с того, на чём остановился.

В левой части экрана ты можешь увидеть список всех проектов – найди там проект “tutorial01_first_window” и открой файл “tutorial01.cpp”.

Список проектов в QtCreator

Там ты увидишь код, который инициализирует все библиотеки и создаёт окно – давай разберём по порядку.

Подключаем стандартные заголовки

// Include standard headers
#include <stdio.h>
#include <stdlib.h>

Тут никаких откровений – стандартные библиотеки C++ для вывода логов через всякие printf.

Подключаем заголовочный файл GLEW

// Include GLEW
#include <GL/glew.h>

GLEW это обёртка над OpenGL, которая подключает самые свежие функции OpenGL, поддерживаемые твоей системой.

У них на сайте написано: “OpenGL Extension Wrangler Library (GLEW) это кросс-платформенная C++ библиотека с открытым исходным кодом для загрузки расширений. GLEW предоставляет эффективный механизм, который в реальном времени определяет какие расширения OpenGL поддержаны на целевой платформе. Вся базовая функциональность OpenGL и его расширений предоставляется в едином заголовочном файле.

Почему в OpenGL сразу не включены все функции, зачем какие-то расширения?

OpenGL сообщает, что базовая функциональность OpenGL работает везде, а вот работоспособность расширений уже зависит от конкретного железа и операционной системы. Поэтому и приходится сначала смотреть что у тебя за система, а потом подключать функции.

Подключаем заголовочный файл GLFW и создаём указатель на окно

// Include GLFW
#include <GLFW/glfw3.h>
GLFWwindow* window;

GLFW это библиотека, в которой спрятана вся платформенно-зависимая логика создания окон и управления вводом. Без неё пришлось бы для разработки под Windows разбираться с Win API, под Linux – с Linux API, а под MacOS – Cocoa API.

У них на сайте написано: “GLFW это кросс-платформенная библиотека с открытым исходным кодом для разработки с применением OpenGL, OpenGL ES и Vulkan. GLFW предоставляет простой API для создания окон, контекстов и поверхностей, а также получения ввода и системных событий.”

Кроме подключения заголовочника, мы ещё создаём указатель на объект окна, который в дальнейшем создадим.

Подключаем заголовочный файл GLM

// Include GLM
#include <glm/glm.hpp>
using namespace glm;

GLM это библиотека, которая реализует множество математических конструкций и операций над ними – они нам пригодятся в дальнейшем.

У них в гитхабе написано: OpenGL Mathematics (GLM) это математическая библиотека из заготовочных файлов, основанная на спецификации к OpenGL Shading Language (GLSL).

Ну вроде всё, с библиотеками закончили, теперь к самому коду!

Инициализация GLFW

// Initialise GLFW
if( !glfwInit() )
{
    fprintf( stderr, "Failed to initialize GLFW\n" );
    getchar();
    return -1;
}

glfwWindowHint(GLFW_SAMPLES, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // To make MacOS happy; should not be needed
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

Первыми делом в main() мы инициализируем библиотеку GLFW, чтобы приступить к созданию окна.

После этого мы выставляем ряд параметров для окна GLFW:

  • GLFW_SAMPLES – выбираем 4-х кратный экранный буфер для сглаживания MSAA. Если эту опцию не ставить, то без сглаживания все линии будут рисоваться лесенкой.
  • GLFW_CONTEXT_VERSION_MAJOR и GLFW_CONTEXT_VERSION_MINOR – задают версию OpenGL, которую будет использовать приложение
  • GLFW_OPENGL_FORWARD_COMPAT – опция для выставления совместимости в экзотических случаях: “можно использовать более новую версию OpenGL, если необходимо”
  • GLFW_OPENGL_PROFILE – выбор подмножества функций OpenGL

Подробное описание этих и кучи других параметров можно найти здесь.

Создаём окно

// Open a window and create its OpenGL context
window = glfwCreateWindow( 1024, 768, "Tutorial 01", NULL, NULL);
if( window == NULL ){
    fprintf( stderr, "Failed to open GLFW window. If you have an Intel GPU, they are not 3.3 compatible. Try the 2.1 version of the tutorials.\n" );
    getchar();
    glfwTerminate();
    return -1;
}
glfwMakeContextCurrent(window);

Вызываем функцию библиотеки GLFW для создания окна, и указываем там его ширину, высоту и заголовок. После этого делаем контекст окна активным.

Контекст – это объект из OpenGL, который в себе хранит всё-всё-всё относящееся к OpenGL (текстуры, полигоны и т.д.). GLFW заботливо заворачивает внутрь окна контекст OpenGL – мы можем создать несколько окон с разными контекстами (несколько окон с разными играми), а можем создать несколько окон с одним и тем же контекстом (одна игра на несколько окон).

Инициализация GLEW

// Initialize GLEW
if (glewInit() != GLEW_OK) {
    fprintf(stderr, "Failed to initialize GLEW\n");
    getchar();
    glfwTerminate();
    return -1;
}

Инициализируем GLEW, а вместе с ней и сам OpenGL.

Включаем отслеживание клавиш для текущего окна

// Ensure we can capture the escape key being pressed below
glfwSetInputMode(window, GLFW_STICKY_KEYS, GL_TRUE);

Выставляем цвет очистки окна

// Dark blue background
glClearColor(0.0f, 0.0f, 0.4f, 0.0f);

Выставляем цвет очистки окна в формате RGB (последний аргумент это альфа-канал, про него потом).

В OpenGL каждый кадр открисовывается заново. Представь как художники рисуют анимации – они рисуют сразу все кадры на отдельных холстах, а потом их меняют. OpenGL – это художник, у которого есть только два “холста”:

  • OpenGL показывает пользователю первый “холст”
  • В это время весь второй “холст” заливается сплошным цветом (в нашем случае синим)
  • Поверх этого цвета рисуются наши 3D-модели
  • OpenGL меняет местами первый и второй “холст” – теперь второй показывается пользователю, а первый перерисовывается

“Холст” называется кадром, и этот процесс происходит очень быстро. Например 60 FPS, это 60 кадров в секунду – каждые 16 миллисекунд OpenGL заканчивает рисовать кадр и меняет его с предыдущим.

Такой механизм, в котором есть два кадра – “в перерисовке” и “готовый” – называется двойной буфферизацией.

Зачищаем окно и слушаем ввод с клавиатуры

do{
    // Clear the screen. It's not mentioned before Tutorial 02, but it can cause flickering, so it's there nonetheless.
    glClear( GL_COLOR_BUFFER_BIT );

    // Swap buffers
    glfwSwapBuffers(window);
    glfwPollEvents();

} // Check if the ESC key was pressed or the window was closed
while( glfwGetKey(window, GLFW_KEY_ESCAPE ) != GLFW_PRESS &&
        glfwWindowShouldClose(window) == 0 );

Вызываем функцию OpenGL glClear(), которая очищает экран указанным в glClearColor() цветом. GL_COLOR_BUFFER_BIT говорит о том, что нужно почистить только цвет (да, в OpenGL на экране хранится не только цвет, но об этом тоже потом).

Функция glfwSwapBuffers() делает то, о чём я говорил – меняет кадры “в перерисовке” и “готовый” местами.

Фунцкия glfwPollEvents() запрашивает у системы новые события – это нужно чтобы отслеживать нажатие клавиш и прочее.

В условии цикла while:

  • Через функцию glfwGetKey() мы забираем текущее состояние кнопки Escape и сравниваем с состоянием GLFW_PRESS. Без этой проверки окно не закроется при нажатии Escape.
  • Вызыввем функцию чтобы получить текущее состояние окна – если пользователь нажал крестик на окне или Alt+F4, то вернётся 1. Без этой проверки окно не закроется при нажатии на крестик.

Высвобождаем ресурсы

// Close OpenGL window and terminate GLFW
glfwTerminate();

Говорим GLFW сворачиваться, а он за нас зачистит и объект окна window, и сам OpenGL – просто сказка, а не библиотека!

Задание на закрепление

Задания на закрепления всегда очень простые и преследуют цель вовлечь тебя в процесс экспериментирования (ведь это интересно).

  • Замени цвет очистки окна на свой любимый цвет. Вот мой любимый цвет:
glClearColor(0.f, 227.f/255.f, 253.f/255.f, 0.0f);
  • Плавная смена цвета:
    • Перенеси функцию glClearColor() внутрь цикла while()
    • создай вне цикла float переменную i
    • Добавь внутрь цикла изменение i на 0.0001
    • Подставь i вместо одной из компонент цвета в функцию glClearColor()

Заключение

Итого, мы изучили:

  • Что такое библиотеки:
    • GLEW (управляет расширениями OpenGL)
    • OpenGL (базовая функциональность + расширения)
    • GLFW (кросс-платформенная библиотека для создания и управления окнами)
    • GLM (библиотека для математики)
  • Двойная буфферизация (два холста)

В следующей статье ты нарисуешь самый настоящий треугольник! Это будет поворотным моментом в твоём обучениии, ведь даже самые крутые и сложные 3D-модели будут состоять из тех же самых треугольников.