Большинство толковых уроков по openGL сводится к рисованию цветного треугольничка. Максимум что рассказывают - как загрузить модель. Примеры таких вступительных уроков:
вот, вот ещё
урок на гугл-коде и
вот (последний - подробный вводный урок, увы, однако, не заходящий дальше всё того же треугольника).
Проблема заключается в том, что едва начинается написание рендерера реальной игровой сцены с более чем одним объектом - возникают проблемы и нормальные уроки по этому поводу мне, например, найти не удалось.
Лирика по поводу вопроса
Примечание: Дальше я буду называть объектом "экземпляр" модели. В случаи с играми, объектом будет то, что игрок видит как юнит. Ясно, что в сцене могут быть несколько юнитов одного типа (соответственно, это будет несколько объектов одной модели).
На текущем уровне понимания openGL я вижу следующие способы нарисовать сцену с более чем одним объектом модели:
1. Неэффективный по скорости. Вызывается draw одного и того же VAO с вершинами модели, но с разными матрицами. Вызов draw, как я понимаю, дорогая операция - требует явной синхронизации чего-то там и прочих неприятных вещей. Если для каждого объекта вызывать draw (которых в RTS, например, могут быть тысячи) - работать такой код может намного дольше, чем код, в который мы скопом передаём все вершины сцены.
Эта техника обсуждалась
тут,
тут и
тут... Везде, короче.
2. Неэффективный по памяти. Можно отправить на видеоустройство громадный VAO, хранящий все вершины мира - с дублированием вершин для каждого объекта модели. Таким образом треугольники всех объектов пойдут на отрисовку одновременно и этот процесс будет легче распараллелить. Но таким образом видеопамять будет забивать по самое не хочу бессмысленно дублируемыми данными. Такой метод, думаю, вообще нигде не используется.
3. Индексирование. В openGL есть возможность индексирования вершин. В случаи использования этого метода, вместо атрибутов, привязываемых через VAO, можно отсылать массив индексов вершин, по которым будут формироваться треугольники объектов. В этом случаи дублирование данных всё равно будет - дублирование элементов индексных массивов (см. картинку, индексы в массиве мировых моделей будут от 0 до N, где N - это количество вершин модели). Но места индексы будут занимать меньше места.
Ещё одна проблема - отсылка информации о матрицах преобразования для объектов (на картинке - строчка transforms). Ясно что передавать матрицу с каждой вершиной будет избыточно (для каждой вершины одного объекта модели используется одна и та же матрица преобразования). С другой стороны, если опять-таки сделать что-то вроде индексации. То есть отсылать матрицы в большом UBO и к индексам вершин привязывать индекс матрицы преобразования соответствующей модели (не уверен что знаю как это сделать). К тому же, так как объекты могут создаваться и исчезать достаточно активно в течение сцены, использование UBO может тормозить процесс рендера (насколько я понимаю, UBO грузить на видяху дольше чем VAO).
Сам вопрос... Точнее, вопросы.
1. Действительно ли команда draw настолько дорога? По логике должна быть дорога - ведь если мы рисуем сцену маленькими порциями по несколько сот треугольников вместо отрисовки сразу миллионов треугольников всей сцены - отрисовку сложнее запараллелить.
2. Если ответ на первый вопрос "Да", то есть ли команда рисования в openGL, принимающая на вход массив промежутков индексов (в формате "индекса начала - индекс конец") и вызывающая пайпалайн для этих промежутков так, как glDrawArrays вызывается для одного VAO, заданного подряд идущими, не разнесёнными в памяти вершинами?.. Блин, звучит, кажется, вообще непонятно... Я имею в виду что-то вроде такого:
Код
// В следующей строчка - код отсылки вершин моделей. Скорее всего, в едином VAO
// или UBO.
. . .
// Смещения вершин на отрисовку и количество вершин в каждом наборе.
int worldObjectRanges[][] = {{0, model1_VertexCount}, {0, model0_VertexCount },
{ model0_VertexCount, model1_VertexCount },
{ model0_VertexCount + model1_VertexCount, model2_VertexCount }};
matrix worldObjectTransforms[] = { obj0Trans, obj1Trans, obj2Trans, obj3Trans };
// В следующей строчке кода идёт отправка трансформаций моделей
// worldObjectTransforms (и, кстати, ещё какой-нибудь уникальной для моделей
// сопроводительной инфы) - предположительно через UBO.
. . .
// Тут делается магия. Пайплайн вызывается для всех вершин, находящихся
// в промежутках заданных через worldObjectRanges. При нужно чтобы в вершинный
// шейдер передавался индекс элемента worldObjectRanges, на основе которого шейдер
// был вызван для данной вершины (фактически, это будет индекс объекта); это нужно
// чтобы по индексу из UBO с трансформациями моделей получить трансформацию
// рисуемой в шейдере модели.
glDrawIndexedRanges(worldObjectRanges);
3. Ну, и если это все мои размышления полная фигня - где можно почитать про техники эффективной отрисовки реальных сцен с многими моделями с помощью openGL?
Лирика по поводу сверхзадачи
P.S.: Планирую поднять ещё несколько тем по openGL. В общем и целом, темы перечисленны в
этом вопросе. После того, как чуть разберусь, планирую написать учебную статью (возможно, цикл статей) на хабре. Делать это буду по схеме, при которой комментирование статьи знающими людьми будет приводить к модификации статьи (схему такой работы я описывал в этом вопросе
этом вопросе).
Помимо меркантильной пользы "самому разобраться", надеюсь заполнить циклом пробел, который имеется в учебных темах по openGL. Дело в том, что, как упоминалось в начале этого вопроса, есть простые статьи, есть сразу статьи очень сложные (чтобы понять такие нужно курить openGL не один год), а промежуточных нету. Это дело нужно исправлять.