Найти в Дзене
ZDG

OpenGL: Приступим, помолясь

Наконец голова и руки дошли до написания первой программы с использованием OpenGL. Сразу скажу, что тема довольно нудная и с непривычки сложная (хотя по факту всё не так ужасно). Нужно немного терпения и усидчивости. Если вы не в курсе про принципы работы OpenGL, рекомендую сначала ознакомиться: У меня всё работает на ноутбуке с системой Windows 10. Я поставил на него компилятор языка C g++ в составе пакета mingw64. Там же есть графическая библиотека SDL2. Как всё это скачать, установить и настроить, подробно описано здесь: Редактирую в редакторе FAR, а компилирую из командной строки. Если у вас стоит какая-то IDE, типа Visual Studio, то вам нужно будет самостоятельно подключить заголовочные файлы и библиотеки к проекту. Какие именно – будет рассказано по ходу пьесы. 1. Загрузчик OpenGL-функций Наша программа должна вызывать различные OpenGL-функции. Но проблема в том, что мы не можем скомпилировать программу с этими функциями – указателей на них просто не существует. Функции содержат
Оглавление

Наконец голова и руки дошли до написания первой программы с использованием OpenGL. Сразу скажу, что тема довольно нудная и с непривычки сложная (хотя по факту всё не так ужасно). Нужно немного терпения и усидчивости.

Если вы не в курсе про принципы работы OpenGL, рекомендую сначала ознакомиться:

У меня всё работает на ноутбуке с системой Windows 10. Я поставил на него компилятор языка C g++ в составе пакета mingw64. Там же есть графическая библиотека SDL2.

Как всё это скачать, установить и настроить, подробно описано здесь:

Редактирую в редакторе FAR, а компилирую из командной строки. Если у вас стоит какая-то IDE, типа Visual Studio, то вам нужно будет самостоятельно подключить заголовочные файлы и библиотеки к проекту. Какие именно – будет рассказано по ходу пьесы.

1. Загрузчик OpenGL-функций

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

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

Это чудовищно нудная часть, но к счастью существуют загрузчики, которые берут всю эту канитель на себя.

Загрузчик это обычно два файла: заголовочный и библиотека. Нужно просто добавить их в проект. Сейчас модно использовать генерируемые загрузчики.

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

Генераторы работают или на Python, или на Lua, так что может потребоваться поставить на машину дополнительный язык программирования. Но проще всего воспользоваться онлайн-генератором, например GALOGEN:

Полный список библиотек и генераторов вы можете посмотреть здесь:

https://www.khronos.org/opengl/wiki/OpenGL_Loading_Library

Генератор выдаст вам два файла: gl.h и gl.c.

Эти файлы мы добавляем в папку своего проекта. И пора начинать писать программу:

-2
  • define SDL_MAIN_HANDLED – заморочка SDL для работы в Windows.
  • include <SDL2/SDL.h> – подключаем заголовочный файл библиотеки SDL.
  • include "gl.h" – подключаем заголовочный файл загрузчика OpenGL, сгенерированный с помощью GALOGEN. Этот файл находится в одном каталоге с нашей программой, поэтому используем кавычки.
  • include <SDL2/SDL_opengl.h> – подключаем OpenGL-интерфейс для SDL.

2. Настройка контекста

Начинаем писать функцию main():

-3

Инициализируем видео с помощью SDL_Init(SDL_INIT_VIDEO) и переходим к настройкам OpenGL.

Функция SDL_GL_SetAttribute() устанавливает одно из свойств (атрибутов) OpenGL в нужное значение. Как свойства, так и значения можно задать с помощью констант, уже объявленных в заголовочных файлах.

SDL_GL_DOUBLEBUFFER это константа-атрибут для включения режима двойной буферизации (техника, используемая для анимации, которую мы обсудим дальше).

SDL_GL_ACCELERATED_VISUAL – атрибут, который включает аппаратное ускорение (что нам и нужно)

SDL_GL_RED_SIZE – атрибут, который задаёт размер красного компонента цвета. В нашем случае это 8 бит, или 1 байт. Аналогичные атрибуты задают размер зелёного, синего и альфа-компонентов.

Наконец, мы задаём свойства контекста OpenGL. Это актуальная версия 4.3 (SDL_GL_CONTEXT_MAJOR_VERSION = 4 и SDL_GL_CONTEXT_MINOR_VERSION = 3). И профиль SDL_GL_CONTEXT_PROFILE_MASK, который мы устанавливаем в значение CORE. Это значит, что мы не будем поддерживать совместимость с предыдущими версиями OpenGL.

Обратите внимание, что при генерации загрузчика в GALOGEN мы тоже указали версию 4.3.

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

-4

Создание окна – стандартное, но здесь мы передаём дополнительный флаг SDL_WINDOW_OPENGL.

Затем мы получаем указатель на контекст OpenGL, связанный с этим окном:

SDL_GLContext context = SDL_GL_CreateContext(window);

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

И вот что мы делаем:

glClearColor(0.5, 0.0, 0.0. 0.0);

Мы задали цвет, которым очищается экран. Это 4 числа, которые соответствуют компонентам R, G, B и альфа (прозрачность). В отличие от обычных графических библиотек, компоненты это не целые числа от 0 до 255, а вещественные числа от 0 до 1.

Таким образом, мы задали половину интенсивности красного цвета (0.5) и нулевые интенсивности всего остального. Следовательно, цвет должен получиться тёмно-красным.

Далее мы задаём область просмотра (viewport) OpenGL. Это прямоугольник, на котором происходит рендеринг. Его размеры совпадают с размерами нашего окна (что необязательно, но логично):

glViewport(0, 0, width, height);

Задавать размеры области просмотра необходимо, так как в OpenGL используются нормализованные координаты 0..1, и нужно преобразовывать их в экранные координаты. А для этого нужно знать размеры области просмотра.

3. Программа

Далее мы организуем цикл опроса событий. Он не имеет отношения к OpenGL, а является стандартной частью любой интерактивной программы. Мы с ним знакомились в материалах про Сапёра:

-5

В цикле мы используем функции OpenGL:

glClear(GL_COLOR_BUFFER_BIT);

Очищаем экран заданным ранее цветом (тёмно-красным). Флаг GL_COLOR_BUFFER_BIT, передаваемый в функцию, указывает на то, что мы хотим очистить цветовой буфер. В OpenGL также есть буферы глубины (depth) и трафарета (stencil), но мы их пока не трогаем, и следовательно не очищаем.

После очистки буфера мы меняем два буфера местами:

SDL_GL_SwapWindow(window);

Как это работает? Ранее упоминался механизм двойной буферизации. Один буфер (то есть область памяти с нарисованным в ней изображением) мы видим на экране, а рисование происходит в другом буфере, который мы не видим. После того как рисование закончено, буфера меняются местами. Теперь на экране мы видим тот буфер, в котором рисовали, а рисуем в том, который видели.

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

Но у нас пока нет никакой анимации. Мы просто закрашиваем буфер красным цветом, и повторяем это бессмысленное действие в цикле:

-6

Но сам факт работы программы говорит о том, что всё в порядке.

После получения события SDL_QUIT мы выходим из цикла и освобождаем контекст OpenGL и прочие ресурсы:

-7

Вот и вся программа, наиболее простой и короткий вариант. Конечно, на этом нельзя останавливаться. Наша цель – нарисовать два треугольника, имеющие разные цвета вершин. Выглядеть это будет так:

-8

Предстоит задать вершины, цвета, и написать шейдеры. Это большой объём материалов, поэтому в одном выпуске я про это не расскажу. Но очень скоро, буквально на днях, будут новые выпуски.

Текущий код проекта лежит на github в ветке init.

Читайте дальше: Готовим данные и рисуем простой треугольник