Pull to refresh

OpenGL ES 2.0 для начинающих

Одна из самых мощный сторон современных мобильных устройств Apple- это графическая подсистема, позволяющая писать качественные 2d и 3d игры с отличной производительностью и детальной графикой. О ней я и хочу рассказать, так как в интернете для новичков информации про OpenGL ES 2.0 на русском языке очень мало.

Итак, что же такое OpenGL? Это Открытая графическая библиотека (Open Graphics Library), которая имеет свой api, свои переменные, и является самой ближайшей точкой взаимодействия между процессором и графическим чипом (GPU).


Как работает OpenGL

В устройствах на iOS есть центральный процессор (CPU) и графический процессор (GPU). GPU призван разгрузить центральный процессор от обработки графических данных перед выводом на экран.
Другими словами, OpenGL позволяет рассчитывать все детали конечного изображения на графическом чипе (Графический чип в разы быстрее в расчетах чисел с плавающей точкой), а центральный процессор оставить для других расчетов, например, игровой логики.
Так же OpenGL предоставляет множество возможностей для хранения информации, данных и изображений в оптимальном для графического чипа формате для более быстрой обработки. Эти данные будут обрабатываться напрямую непосредственно графическим чипом.

Логика OpenGL

Логика OpenGL до безобразия проста, и ее можно объяснить тремя вещами:
  • примитивы
  • буферы
  • растеризация

Весь OpenGL работает вокруг этих понятий. Рассмотрим их поподробнее.

Примитивы

Примитивы в OpenGL состоят из трех объектов:
  • точка в пространстве (x,y,z)
  • линия в пространстве (состоит из 2х точек в пространстве)
  • треугольник в пространстве (состоит из 3х точек в пространстве)

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

Буферы

Буферы в OpenGL это временное хранилище. Они делятся на 3 типа:
  • frame buffer (кадровый буфер)
  • render buffer (визуализирующий буфер)
  • buffer object (буфер объектов)

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

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

буфер визуализации цвета хранит итоговое цветное изображение, созданное рендером OpenGL. Это цветное (RGB) изображение.
буфер визуализации глубины хранит координату глубины (точка z) объектов в пространстве.
буфер визуализации шаблона хранит видимую часть объекта (как маску видимой части). В буфере хранится черно-белое изображение.

Буфер объектов хранит информацию о структурах и индексах, причем эта информация в отличие от других буферов может храниться на протяжении всего цикла выполнения приложения.
Структурой является массив, который описывает 3d-модель как массив вершин, массив координат текстур или массив нужных вам данных. Индексы более специфичны. Массив индексов используется для указания, как стороны вашего полигона будут построены на основе массива структур.
Давайте представим куб в пространстве. Куб состоит из 6 сторон и 8 вершин.
image
Каждая из сторон это квадрат, но OpenGL умеет работать только с треугольниками. Таким образом нам нужно преобразовать каждый квадрат в треугольники, что бы отобразить его. Когда мы это сделаем, то получится, что 6 сторон преобразовались в 12.
Треугольники в OpenGL представляют собой комбинацию из 3х вершин. Таким образом, что бы «объяснить» OpenGL что мы хотим изобразить куб, нам потребуется нарисовать 2 треугольника со следующими вершинами: {vertex1,vertex2,vertex3}, {vertex1,vertex3,vertex4}.
Что бы построить куб в OpenGL, нам потребуется создать массив с информацией о 8 вершинах: {vertex 1, vertex 2, vertex 3, vertex 4, ...}, но вместо того, что бы каждый раз не перезаписывать эту информацию для каждой стороны куба, можно построить массив индексов вида {0,1,2,0,2,3,2,6,3,2,5,6...}, где каждая комбинация из 3х элементов (0,1,2 — 0,2,3 — 6,3,2) представляет собой сторону треугольника. Эта возможность позволяет один раз записать информацию о вершинах в массив индексов и многократно ее использовать.
Таким образом, буфер объектов хранит массивы структур {vertex1,vertex2,vertex3,...} и массив индексов {0,1,2,0,2,3,2,6,3...}.
Огромным преимуществом буфера объектов является то, что объекты оптимизированы для обработки на графическом чипе и вам не нужно хранить массив в приложении после создания объекта в этом буфере.

Растеризация

Растеризация это процесс, когда OpenGL собирает всю информацию о 3d объектах (координаты, вершины и т. д. ) и создает 2d изображение, как правило для отображения на экране устройства.
В реализации OpenGL Apple вы не можете вывести изображение сразу на экран, а должны обязательно его поместить в кадровый буфер, а уже оттуда, средствами EAGL (Extended Apple GL) вывести его на экран устройства.

Конвейеры OpenGL

В OpenGL ES 2.0 используется программируемый конвейер. Это означает что вся ответственность за камеры, освещение, эффекты лежит полностью на разработчиках. Делается все это шейдерами. Таким образом, когда слышите о программируемом конвейере, думайте о шейдерах :)
Шейдеры это такие маленькие кусочки кода, маленькие программки, которые выполняются непосредственно на графическом чипе для совершения сложных расчетов.
Фиксированный конвейер это полная противоположность программируемому. Фиксированный конвейер предоставляет api для управления камерой, освещением, эффектами, материалами.

Для создания шейдеров в OpenGL ES используется язык, весьма похожий на C, называемый OpenGL ES Shader Language (GLSL ES или ESSL).
Как же работают шейдеры? Вы создаете их либо в отдельных файлах, либо прямо в коде, главное что бы строка, содержащая код шейдера, была отправлена в ядро OpenGL и скомпилирована там для использования.
Шейдеры работают в паре — вершинные шейдеры и фрагментные. Что бы понять что каждый из них означает, вернемся к примеру с кубом.


Вершинный шейдер

Вершинные шейдеры (vertex shader), так же известные как VS или VSH, это маленькие программы, которые выполняются для каждой вершины. Если посмотреть на куб на рисунке выше, то у куба получается 8 вершин (5 вершина невидимая). Соответственно вершинный шейдер обработает этот куб 8 раз на графическом процессоре.
Вершинный шейдер задаст конечные позиции вершин с учетом положения камеры, а так же подготовит и выведет некоторые переменные, требуемые для фрагментного шейдера. В OpenGL мы не можем задавать переменные напрямую для фрагментного шейдера, только через вершинный. Почему нельзя обращаться к фрагментному шейдеру напрямую? Давайте рассмотрим.

Фрагментный фейдер (fragment shader, FSH)

Давайте посмотрим вновь на куб.
5 вершина невидимая по причине местоположения и поворота куба в пространстве, значит мы можем видеть только 3 стороны куба, и они составляют 7 вершин.
Это то, что делает фрагментный шейдер. Он обрабатывает каждую видимую часть конечного изображения. Можно преставить каждую часть как пиксель, но это не совсем так, потому что пиксеть в рендеринге OpenGL и в итоговом изображение, которое вы видите на экране, может различаться по размеру. Таким образом фрагмент может быть больше или меньше нежели реальный пиксель, в зависимости от конфигурации устройства и параметров рендеринга. В кубе, приведенном выше, фрагментный шейдер обработает каждый пиксеть на всех 3х сторонах куба, сформированных с помощью 7 вершин.
Внутри фрагментного шейдера мы будем работать со всем, что связано с поверхностью- освещение, тени, отражения, текстуры и любые эффекты, которые вы захотите. Результат работы фрагментного шейдера это цвет пикселя в формате RGBA (красный, зеленый, синий и альфа-канал).
Вершинный и фрагментный шейдеры работают вместе. Один вершинный шейдер и один фрагментный, не больше и не меньше. Что бы гарантировать, что м ы не наделаем ошибок, OpenGL всегда компилирует пару VSH и FSH.

Ошибки в OpenGL

Так как OpenGL это отдельный api и выполняется он в графическом чипе, вы не имеете прямого доступа к процессам, происходящим внутри. Поэтому если возникает ошибка внутри, то с вашим приложением ничего не случится.
Но как узнать, что если в одном из шейдеров есть ошибка или буфер рендеринга настроен неправильно? Для таких случаев в OpenGL есть специальный Error api. Он весьма прост и состоит из нескольких функций, одна из которых это простая проверка успешность завершения каких либо операций, возвращающая yes или no. Таким образом очень просто быстро проверить есть ли ошибки, и, если таковые имею место быть, то вы получите сообщение с ошибкой. Обычно проверки размещаются в критических точках приложения, например при компиляции шейдеров или при создании буферов.

Если тема будет интересна, продолжу уже с реальными примерами для iOS.

Текст оригинальной статьи здесь.
Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.