Здравствуйте. Несколько недель назад я начинал серию переводов статей по изучению OpenGL. Но на 4 статье один хабровчанин заметил, что мои переводы могут нарушать лицензию, по которой распространяются учебные материалы, предоставленные в исходной статье. И действительно, мои переводы нарушали лицензию. Для разрешения этой проблемы я обратился к авторам того набора уроков, но так и не смог добиться нормального ответа. По этой причине я связался с автором другого, не менее (а возможно даже и более) крутого, набора уроков по OpenGL: Joey de Vries. И он дал полное разрешение на перевод его набора уроков. Его уроки гораздо более обширные, чем прошлый набор, поэтому эти переводы растянутся на долго. И я обещаю, будет интересно. Заинтересовавшихся прошу под кат.

Также я встал на распутье: либо я опишу все основы вроде создания окна и контекста в одной статье, чтобы не плодить статьи, но в таком случае такую огромную статью не всякий осилит; либо я также как и раньше буду переводить, опираясь на иерархию оригинала. Я решил выбрать второй вариант.

На счет уроков по Vulkan: к сожалению мне тяжело сейчас написать уроки по данному API по причине скудной видеокарты на данный момент, которая просто не поддерживает Vulkan API, поэтому уроки по данному API будут только после обновления видеокарты.

Меню


1. Начинаем
  1. OpenGL
  2. Создание окна


Часть 1.1 — OpenGL


Вступление


Прежде чем мы начнем наше путешествие нам стоило бы разобраться что такое OpenGL. В основном под OpenGL понимают API (Интерфейс Программирования Приложений), который предоставляет большой набор функций, которые мы можем использовать для управления графикой и изображениями. Но на самом деле OpenGL это скорее спецификация, разработанная и поддерживаемая Khronos Group.

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

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

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

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

Core-profile и Immediate mode (Мгновенный режим)


Раньше, использование OpenGL предполагало разработку в Immediate mode (также известен как фиксированный конвейер (fixed function pipeline)), которая была проста в использовании для рисования графики. Большинство функционала OpenGL было скрыто в библиотеках и у разработчиков не было свободы в понимании вычислений, производимых OpenGL.

Разработчики требовали большей гибкости в разработке и позже спецификация стала более гибкой, а разработчики получили больше контроля над процессом отрисовки их графики. Immediate mode был прост в использовании и понимании, но он был крайне неэффективным. По этой причине спецификация указала Immediate mode как устаревший, и начиная с версии 3.2 начала мотивировать программистов использовать Core-profile режим, который исключал весь устаревший функционал.

При использовании core-profile, OpenGL заставляет нас пользоваться современными практиками. Когда мы пытаемся использовать устаревшие функции, OpenGL выбрасывает ошибку и прекращает отрисовку. Преимущества использования современных практик — это гибкость и эффективность, но к сожалению бОльшая сложность в изучении. Immediate mode является бОльшей абстракцией и он скрывает большое количество реальной работы, выполняемой OpenGL и поэтому его было легко изучать, но трудно разобраться, как OpenGL на самом деле работает. Современный подход требует от разработчика полного понимания OpenGL и графического программирования в целом и хоть это немного сложнее, такая схема позволяет добиться большей гибкости, эффективности.

Это причина, почему наши уроки основаны на Core-Profile OpenGL версии 3.3.
Хоть он немного и сложнее, но это того стоит.

Сейчас уже вышли гораздо более новые версии OpenGL (на момент написания 4.5) и вы можете спросить: зачем мы должны изучать OpenGL 3.3, когда уже вышел 4.5? Ответ довольно прост. Все старшие версии OpenGL, начиная от версии 3.3 не добавляют различные полезные возможности без изменения основной механики. Новые версии просто предоставляют немного более эффективные или более удобные способы выполнения одних и тех же операций. В результате все концепты и техники, применимые к OpenGL 3.3 можно применить к новым версиям OpenGL.

Использование новейших версий OpenGL сопряжено с одной проблемой. Исполнять новейшие API смогут только современные видеокарты.

Расширения


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

Разработчику надо лишь проверить доступность расширения (или использовать библиотеку расширения). Такой подход позволяет программисту выполнять действия более эффективно, основываясь на имеющихся у него расширениях:

if(GL_ARB_extension_name)
{
    // Можно использовать новый функционал. Он поддерживается железом
}
else
{
    // Расширение не поддерживается: делаем по-старинке.
}

C OpenGL 3.3 нам редко будут нужны расширения, но когда будут нужны, необходимые инструкции будут предоставлены.

Конечный автомат


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

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

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

Объекты


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

Объект в OpenGL — это набор опций, которые представляют подмножество состояний OpenGL. К примеру мы можем создать объект, описывающий конфигурацию отрисовки окна; мы можем задать размер, количество цветов и так далее. Такой объект можно представить C-подобной структурой:


struct object_name {
    GLfloat  option1;
    GLuint   option2;
    GLchar[] name;
};

Примитивные типы
Заметьте, что при использовании OpenGL рекомендуется использовать примитивы, заданные OpenGL. Вместо использования float записывать его с приставной GL. Тоже самое для int, uint char, bool и так далее. OpenGL определяет разметку памяти для его GL примитивов для обеспечения кроссплатформенности, поскольку некоторые операционные системы могут иметь иную разметку. Использования OpenGL примитивов позволяет добиться полной кроссплатформенности вашего приложения.

Каждый раз, когда мы хотим использовать объекты в основном мы запишем это как-то так:

// OpenGL состояние
struct OpenGL_Context {
  	...
  	object* object_Window_Target;
  	...  	
};

// Создание объекта
GLuint objectId = 0;
glGenObject(1, &objectId);
// Привязка объекта к контексту
glBindObject(GL_WINDOW_TARGET, objectId);
// Установка значений опции объекта, привязанного к GL_WINDOW_TARGET
glSetObjectOption(GL_WINDOW_TARGET, GL_OPTION_WINDOW_WIDTH, 800);
glSetObjectOption(GL_WINDOW_TARGET, GL_OPTION_WINDOW_HEIGHT, 600);
// Установка цели контекста в значение по умолчанию
glBindObject(GL_WINDOW_TARGET, 0);

Этот небольшой участок кода — то, что вы будете часто встречать во время работы с OpenGL. В начале мы создаем объект и сохраняем ссылку на него в виде идентификационного номера (id). (Реальные данные объекта спрятаны в реализации). Затем мы привязываем объект к требуемой части контекста (Расположение целевого объекта окна из примера задано, как `GL_WINDOW_TARGET`). Затем мы устанавливаем значения опций окна и, в конце концов, отвязываем объект, установив id в 0. Значения, установленные нами продолжают храниться в объекте, доступ к которому мы можем получить через objectId и восстановить их снова привязав объект к GL_WINDOW_TARGET.
Данный код лишь показывает пример того, как работает OpenGL. В последствии будут представлены реальные примеры.

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

Давайте начнем


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

Дополнительные ресурсы


  • opengl.org: оффициальный сайт OpenGL;
  • OpenGL registry: хранилище спецификаций OpenGL и всех его расширений для всех версий OpenGL.
Поделиться с друзьями
-->

Комментарии (8)


  1. A-Stahl
    24.09.2016 08:17
    +3

    >Vulkan: к сожалению мне тяжело сейчас написать уроки по данному API
    Будем ждать.


  1. NirumVS
    24.09.2016 13:55

    подскажите: что лучше сейчас изучать OpenGL или Vulkan?


    1. Megaxela
      24.09.2016 13:58
      +2

      И то и другое. Vulkan гораздо более низкоуровневый. Если проводить аналогии с языками программирования — то Vulkan это ассемблер, а OpenGL — C. Я не думаю, что разрабатывая арканоид вы захотите связываться с распределением полезной нагрузки на очереди видеокарты. Это как разрабатывать графический калькулятор на ASM.


      1. Torvald3d
        24.09.2016 16:16
        +1

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


        1. Gorthauer87
          24.09.2016 16:46
          +2

          Для виртуальных машин вот тоже есть байткод и его представление в виде ассемблерных инструкций.


        1. creker
          24.09.2016 21:10
          +1

          Даже более единый, чем OpenGL

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


    1. creker
      24.09.2016 21:06

      Начинайте с OpenGL. В любом случае Vulkan еще слишком молод и в ближайшее время найдет основное применение только в совсем топовых разработках. Тоже самое с DX12. Эти вещи не созданы упразднить прошлые версии, просто потому что DX12 и Vulkan слишком сложны для простых задач и неопытных программистов. Первые проекты на них явно страдают от отсутствия большого опыта. В некоторых проектах есть подозрения на утечки памяти из-за ресурсов, которыми раньше управляла библиотека.

      Vulkan и OpenGL это как C и высокоуровневые языки. Вторые делают намного больше той работы, которую придется делать вручную в первом случае. Их API намного больше заточены под специфику. Это ровно то, в чем различаются эти API. Вам совершенно не нужен весь этот контроль, когда вы делаете простой проект. Собственно, почему в этих случаях вообще не смотрят на эти OpenGL и DX и выбирают вообще готовых движок. И уж тем более не лезут в дебри новых API.


  1. RussDragon
    25.09.2016 00:36

    Хороший перевод отличного учебника. В своё время изучал OGL именно по нему.
    Правда, он немного великоват, если нужно изучить технологию быстро. Где-то были ссылки на более короткие, но не менее информативные гайды, да вот только не под рукой ;(
    В любом случае, за переводы Вам огромный плюс.