Подтвердить что ты не робот

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

Я пытаюсь понять, когда я должен использовать индексированные массивы вершин OpenGL, нарисованные с помощью gl [Multi] DrawElements и тому подобное, в отличие от того, когда я должен просто использовать непрерывные массивы вершин, нарисованные с помощью gl [ Мульти] 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 поплавки, а цвет часто составляет 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 всегда следует использовать индексированные массивы, даже для геометрии, которые являются всеми швами.

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

4b9b3361

Ответ 1

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

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

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

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

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


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

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

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

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


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

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

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