Краткая информация по OpenGL ES 2.0

Всем привет! Решил систематизировать свои знания. Просьба относится к тексту ниже, как к вольному сочинению на тему «OpenGLES». Спасибо за понимание!

Вершинные координаты

В OpenGL используется правосторонняя система координат. Обычно направления координат X, Y и Z проверяются на пальцах правой руки. Большой палец — это X, указательный — Y, а средний — Z. По умолчанию X направлен вправо, Y вверх, а Z смотрит на нас.
В 2D-играх обычно координата X направлена вправо, Y — вниз, а Z игнорируют.

OpenGL: мировые координаты

Экранные координаты

Координаты экрана представляют собой две оси: горизонтальная и вертикальная (X и Y соответственно).
Горизонтальная ось направлена вправо, вертикальная — вверх. Центр экрана — это начало координат.
Координаты углов экрана: левый верхний (-1.0, 1.0), левый нижний (-1.0, -1.0), правый нижний (1.0, -1.0), правый верхний (1.0, 1.0).

OpenGL: экранные координаты

Текстурные координаты

Текстурные координаты задаются от левого нижнего угла. Оси текстурных координат называются по-разному: (U, V) или (S, T).
Горизонтальная ось U или S направлена слева направо. Вертикальная ось V или T направлена вверх.
Для наложения текстуры один в один, координаты должны быть в пределах от нуля до единицы. Если задать координаты больше единицы, то в OpenGL можно задать несколько вариантов обработать такую ситуацию: повторение текстуры, зеркалирование, повторение крайних пикселей.

OpenGL: текстурные координаты

Немного о треугольниках

По умолчанию рисование треугольников идет против часовой стрелки. Существуют три способа задать вершины:

  • GL_TRIANGLES — по три вершины на треугольник
  • GL_TRIANGLE_STRIP — три вершины на треугольник, затем каждая следующая с двумя предыдущими
  • GL_TRIANGLE_FAN — представляет собой веер, где первая вершина является центральной точкой веера, а остальные добавляют треугольники к вееру, подробнее в википедии

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

OpenGL:  координаты для рисования квадрата с помощью GL_TRIANGLE_STRIP

Шейдеры

Шейдер состоит из двух частей: вершинный шейдер и пиксельный шейдер.
На входе шейдер получает данные текущей вершины (например, позиция точки, цвет пикселя или текстурные координаты) и параметры общие для всего шейдера (матрица транформации, общая прозрачность объекта и так далее).
На выходе вершинный шейдер выдает координаты точки (с помощью gl_Position), а пиксельный — цвет точки (gl_FragColor).

OpenGL: работа шейдера
источник картинки

Данные текущей вершины называются аттрибутами и задаются с помощью ключевого слова "attribute" в самом шейдере, а передаются в него процедурой glVertexAttribPointer().

Общие параметры для всего шейдера задаются с помощью ключевого слова "uniform" и передача их идет процедурой glUniform().

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

Для установки параметров типа uniform используется такой код:

// Получаем ссылку на параметр "uniformName" в шейдере
GLuint uniformId = glGetUniformLocation(shader, "uniformName");
// Устанавливаем значение параметра
glUniform(uniformId, uniformValue);

Поскольку параметр типа uniform может представлять собой матрицу или массив 2-4 элементов, функция glUniform() имеет множество модификаций (glUniform1i, glUniform4f и так далее).

Параметры типа attribute задаются для каждой вершины. У каждого аттрибута необходимо знать две вещи: количество элементов в аттрибуте и тип элемента. Например, позиция вершины в 3D координатах XYZ имеет три элемента тип GL_FLOAT, а текстурные координаты UV имеют соответственно два элемента и тип GL_FLOAT.

Аттрибуты для вершин можно представить двумя способами.

В первом случае это просто массив для каждого типа аттрибутов:

// Позиция каждой вершины
float [] position = 
{ 
    x0, y0, z0, 
    x1, y1, z1, 
    ..., 
    xN, yN, zN 
};
// Текстурные координаты каждый вершины
float [] texCoord = 
{ 
    u0, v0, 
    u1, v1, 
    ..., 
    uN, vN 
};

Передача аттрибутов в шейдерв таком виде элементарна и не представляет сложности:

// Получаем ссылку на аттрибут "position" в шейдере
positionId = glGetAttribLocation(shader, "position");
// Передаем массив аттрибутов с тремя элементами типа float для каждой вершины
glVertexAttribPointer(positionId, 3, GL_FLOAT, GL_FALSE, 0, position);
// Включаем аттрибут
glEnableVertexAttribArray(positionId);
 
// Получаем ссылку на аттрибут "texCoord" в шейдере
texCoordId = glGetAttribLocation(shader, "texCoord");
// Передаем массив аттрибутов с двумя элементами типа float для каждой вершины
glVertexAttribPointer(texCoordId, 2, GL_FLOAT, GL_FALSE, 0, texCoord);
// Включаем аттрибут
glEnableVertexAttribArray(texCoordId);

Во втором случае все аттрибуты задаются в одном массиве и называется такой буфер interleaved:

// Данные для вершин
float [] vertices = 
{ 
    x0, y0, z0, u0, v0,
    x1, y1, z1, u1, v1,
    ...,
    xN, yN, zN, uN, vN
};

Код для передачи в шейдер похож на первый случай:

// Размер аттрибутов для одной вершины в байтах (5 элементов типа float)
#define VERTEX_SIZE (5 * sizeof(float))
// Начало 3D координат в буфере (координаты идут с самого начала буфера)
#define POS_OFFSET 0
// Начало текстурных координат в буфере (текстурные 
// координаты начинаются сразу после трех 3D координат
#define TEX_OFFSET 3
 
// Получаем ссылку на аттрибут "position" в шейдере
positionId = glGetAttribLocation(shader, "position");
 
// Передаем массив аттрибутов с тремя элементами типа float для каждой вершины
glVertexAttribPointer(
    positionId, 
    3, 
    GL_FLOAT, 
    GL_FALSE, 
    VERTEX_SIZE, 
    vertices + POS_OFFSET);
 
// Включаем аттрибут
glEnableVertexAttribArray(positionId);
 
// Получаем ссылку на аттрибут "texCoord" в шейдере
texCoordId = glGetAttribLocation(shader, "texCoord");
 
// Передаем массив аттрибутов с двумя элементами типа float для каждой вершины
glVertexAttribPointer(
    texCoordId, 
    2, 
    GL_FLOAT, 
    GL_FALSE, 
    VERTEX_SIZE, 
    vertices + UV_OFFSET);
 
// Включаем аттрибут
glEnableVertexAttribArray(texCoordId);

Как нетрудно заметить, во втором случае ссылка на данные фактически передается два раза. Умные люди придумали выход из этой ситуации и называется он VertexBufferObject. Вкратце идея такая: создаем один раз буфер, а затем в glVertexAttribPointer() передаем только смещения от начала.

Ходят слухи, что отрисовка во втором случае с использованием VertexBufferObject гораздо быстрее, обычной, но мои эксперименты особого выигрыша не выявили.

Комментариев нет

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

Ваш e-mail не будет опубликован.