Когда следует использовать индексированные массивы вершин OpenGL?

Я пытаюсь понять, когда мне следует использовать индексированные массивы вершин OpenGL, нарисованные с помощью gl [Multi] DrawElements и т.п., по сравнению с тем, когда я должен просто использовать непрерывные массивы вершин, нарисованные с помощью gl [Multi] DrawArrays .

(Обновление: все ответы, которые я получил, сводятся к тому, что всегда следует использовать индексированные вершины.)

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

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

Это подводит меня к выводу, что:

1. Для геометрии с небольшим количеством швов большие преимущества имеют индексированные массивы.

Всегда соблюдайте правило 1, за исключением:

Для очень «блочной» геометрии, в которой каждое ребро представляет собой шов, преимущество индексированных массивов менее очевидно. Чтобы взять простой куб в качестве примера, хотя каждая вершина используется в трех разных гранях, мы не можем разделять вершины между ними, потому что для одной вершины нормали поверхности (и возможные другие вещи, такие как цвет и координаты текстуры ) будет отличаться на каждой грани. Следовательно, нам необходимо явно ввести избыточные позиции вершин в наш массив, чтобы одна и та же позиция могла использоваться несколько раз с разными нормалями и т. Д. Это означает, что индексированные массивы менее полезны.

например При рендеринге одной грани куба:

 0     1
  o---o
  |\  |
  | \ |
  |  \|
  o---o
 3     2

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

при рендеринге с использованием GL_TRIANGLE_FAN (или _STRIP) каждая грань куба может быть визуализирована следующим образом:

verts  = [v0, v1, v2, v3]
colors = [c0, c0, c0, c0]
normal = [n0, n0, n0, n0]

Добавление индексов не позволяет упростить это.

Из этого я делаю вывод, что:

2. При рендеринге геометрии, которая представляет собой все швы или в основном швы, при использовании GL_TRIANGLE_STRIP или _FAN я никогда не должен использовать индексированные массивы, а вместо этого всегда должен использовать gl [Multi] DrawArrays.

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

Единственное исключение из правила 2:

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

 0     1
  o---o
  |\  |
  | \ |
  |  \|
  o---o
 3     2

Без индексов, используя GL_TRIANGLES, массивы будут примерно такими:

verts =   [v0, v1, v2,  v2, v3, v0]
normals = [n0, n0, n0,  n0, n0, n0]
colors =  [c0, c0, c0,  c0, c0, c0]

Поскольку вершина и нормаль часто состоят из трех чисел с плавающей запятой, а цвет часто составляет 3 байта, это дает для каждой грани куба примерно:

verts   = 6 * 3 floats = 18 floats
normals = 6 * 3 floats = 18 floats
colors  = 6 * 3 bytes  = 18 bytes

= 36 floats and 18 bytes per cube face.

(Я понимаю, что количество байтов может измениться, если используются разные типы, точные цифры приведены только для иллюстрации.)

С помощью индексов мы можем немного упростить это, дав:

verts   = [v0, v1, v2, v3]     (4 * 3 = 12 floats)
normals = [n0, n0, n0, n0]     (4 * 3 = 12 floats)
colors  = [c0, c0, c0, c0]     (4 * 3 = 12 bytes)
indices = [0, 1, 2,  2, 3, 0]  (6 shorts)

= 24 floats + 12 bytes, and maybe 6 shorts, per cube face.

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

Это подводит меня к выводу, что:

3. При использовании GL_TRIANGLES всегда следует использовать индексированные массивы, даже для геометрии, состоящей из швов.

Пожалуйста, исправьте мои выводы жирным шрифтом, если они ошибочны.


person Jonathan Hartley    schedule 02.06.2010    source источник
comment
Мне очень нравится формат этого вопроса.   -  person SimpleVar    schedule 21.08.2014


Ответы (1)


Из этого я прихожу к выводу, что при визуализации геометрии, состоящей только из швов или в основном из швов, при использовании GL_TRIANGLE_STRIP или _FAN я никогда не должен использовать индексированные массивы, а вместо этого всегда должен использовать gl [Multi] DrawArrays.

Нет, и причина довольно проста.

Ваш вывод основан на том факте, что вы проанализировали один четырехугольник, состоящий из двух треугольников. Эти два треугольника, нарисованные с использованием треугольника веером / полосой, нельзя упростить с помощью индексированных массивов.

Но попробуйте подумать о большой геометрии местности. Каждый блок ландшафта рисуется в виде квадрата с использованием примитива веера / полосы треугольника. Например:

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


По сути, рисование примитивов (треугольников, вееров и полос) с использованием индексов полезно тогда, когда вы можете разделить большую часть вершин одного примитива с другим.

Совместное использование информации позволяет сэкономить пропускную способность передачи информации, но это не единственное преимущество. Фактически индексированные массивы позволяют:

  • Избегайте синхронизации информации, принадлежащей одной и той же «концептуальной» вершине, заданной много раз.
  • Разрешить выполнять одну и ту же операцию шейдера на одной вершине вместо многократного выполнения, по одному для каждого дублирования вершины.
  • Кроме того, сочетание использования треугольных полос / вееров и индексов позволяет приложению сжимать буфер индексов, поскольку спецификация полоски / веера требует меньше индексов (треугольник всегда требует 3 индекса для каждой грани).

Индексированный массив нельзя использовать, как вы указали, если вершина не может совместно использовать всю связанную с ней информацию (цвет, координаты текстуры и т. Д.) С другой совпадающей вершиной.


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

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

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

person Luca    schedule 02.06.2010
comment
+1, но я бы добавил, что при использовании индексов в игру вступает кеш вершин. Пока вершины, общие для разных треугольников, используются близко друг к другу, тогда видеокарта уже знает, каков результат преобразования индекса i, и, следовательно, будет использовать кешированный ответ, а не пересчитывать его. Это ОЧЕНЬ полезно, если у вас есть сложные программы для вершин ... Это также означает, что объем работы по преобразованию вершин между индексированными, не разделенными и неиндексированными разделенными, одинаковыми. Неразрезанный путь также более оптимизирован, поэтому вы получаете лучшую производительность, чем раздетый. - person Goz; 02.06.2010
comment
Все это имеет смысл, большое спасибо за ваш вклад. Однако в выводе (2), который вы цитируете, конкретно говорится, что он применяется только при рендеринге геометрии, состоящей только из швов или в основном из швов. Мое намерение состояло в том, чтобы изображение большой сетки, которую вы показываете, попадало бы под заключение (1): для геометрии с мало швов, индексированные массивы - большая победа. Приношу свои извинения мне не очень понятно. Имея это в виду, вы все равно не согласны с выводом 2? Большое спасибо. - person Jonathan Hartley; 02.06.2010
comment
IMHO, использование примитива полосы / вентилятора (с ot без швов) не означает плохого использования индексированной геометрии, как указано в цитате. - person Luca; 03.06.2010
comment
Справедливо. Большое спасибо Люку и Гозу, все это было очень познавательно и очень ценно. - person Jonathan Hartley; 03.06.2010