Семеричный куб в перспективной проекции

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

(далее…)

Освещение 2D спрайта средствами WebGL

Не так давно в сети появился стандарт WebGL, о котором и пойдет речь ниже. Этот стандарт описывает каким образом можно использовать аппаратное ускорение графики в вашем браузере и является практически полным аналогом OpenGL ES 2.0, который есть сейчас в девяти смартфонах из десяти. Так что красивые, зрелищные игры уровня Assassin’s Creed не за горами.

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

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

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

OpenGL: освещение спрайта

Эффект объема у плоского объекта достигается техникой Bump mapping. Живой пример и исходники под катом.

(далее…)

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

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

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

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

Теория «шести рукопожатий» на практике

Есть такая малоизвестная среди старшего поколения «теория шести рукопожатий». Она гласит, что через цепочку знакомых длиной максимум в шесть человек, вы имеете связь с любым человеком на земле. Например, вы знаете Васю из третьего подъезда, он знает начальника ЖЭК, начальник ЖЭК знает главу района, глава района знает губернатора, а губернатор знает самого Путина. Вот вам и связь в пять-шесть рукопожатий до президента самой крупной страны в мире. Каково, а?

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

Фотоаппарат

И вы не поверите, через 5 дней, 30 декабря, фотоаппарат вернулся к своим счастливым владельцам. Насколько я понимаю, потребовалось всего около 20 перепостов этой записи.

Вот такое новогоднее чудо.

Блог Евгения Жирнова