Я пытаюсь получить четкое представление о том, когда я должен использовать индексные массивы вершин OpenGL, оттянутых с глоссарием [Много] DrawElements и т.п., по сравнению с тем, когда я должен просто использовать непрерывные массивы вершин, оттянутых с глоссарием [Много] DrawArrays.
(Обновление: согласие в ответах, которые я получил, состоит в том, что нужно всегда использовать индексированные вершины.)
Я несколько раз шел назад и вперед по этой проблеме, таким образом, я собираюсь обрисовать в общих чертах свое текущее понимание, в надеждах кто-то может или сказать мне, что я теперь наконец более или менее корректен, или иначе указываю, где мои остающиеся недоразумения. А именно, у меня есть три заключения, полужирным. Исправьте их, если они неправы.
Один простой случай - то, если моя геометрия состоит из сеток для формирования кривых поверхностей. В этом случае вершины посреди сетки будут иметь идентичные атрибуты (положение, нормальное, цвет, текстурируют coord, и т.д.) для каждого треугольника, который использует вершину.
Это приводит меня приходить к заключению что:
1. Для геометрии с немногими швами индексные массивы являются большой победой.
Следуйте правилу 1 всегда, кроме:
Для геометрии, которая очень 'массивна', в котором каждый край представляет шов, преимущество индексных массивов менее очевидно. Для взятия простого куба в качестве примера, хотя каждая вершина используется в трех различных поверхностях, мы не можем совместно использовать вершины между ними, потому что для единственной вершины, поверхность normals (и возможные другие вещи, как цвет и текстурируют co-порядок) разойдется в каждой поверхности. Следовательно мы должны явно ввести избыточные положения вершины в наш массив, так, чтобы то же положение могло несколько раз использоваться с другим normals и т.д. Это означает, что индексные массивы имеют меньше применения.
например, При рендеринге единственной поверхности куба:
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, затем я никогда не должен использовать индексные массивы и должен вместо этого всегда использовать глоссарий [Много] DrawArrays.
(Обновление: Ответы указывают, что это заключение является неправильным. Даже при том, что индексы не позволяют нам уменьшать размер массивов здесь, они должны все еще использоваться из-за других выигрышей в производительности, как обсуждено в комментариях),
Единственное исключение к правилу 2:
При использовании GL_TRIANGLES (вместо полос или вентиляторов), затем половина вершин может все еще быть снова использована дважды, с идентичным normals и цветами, и т.д., потому что каждая поверхность куба представляется как два отдельных треугольника. Снова, для той же единственной поверхности куба:
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 используются дважды, но только представлены однажды в каждом из verts, normals и массивов цветов. Это походит на маленькую победу для использования индексов, даже в крайнем случае каждого края геометрии, являющегося швом.
Это приводит меня приходить к заключению что:
3. При использовании GL_TRIANGLES нужно всегда использовать индексные массивы, даже для геометрии, которая является всеми швами.
Исправьте мои заключения полужирным, если они неправы.
Из этого я делаю вывод, что при рендеринге геометрии, которая состоит из швов или в основном из швов, при использовании GL_TRIANGLE_STRIP или _FAN, я никогда не должен использовать индексированные массивы, а вместо этого всегда должен использовать gl[Multi]DrawArrays.
Нет, и причина довольно проста.
Ваш вывод основан на том, что вы проанализировали один квадрат, состоящий из двух треугольников. Эти два треугольника, нарисованные с помощью triangle fan/strip, не могут быть упрощены с помощью индексированных массивов.
Но попробуйте подумать о большой геометрии местности. Каждый блок местности рисуется как квадрат, используя примитив triangle fan/strip. Например:
Каждая треугольная полоса на рисунке имеет общие вершины с соседними треугольными полосами, и использование индексов позволяет сжать определение геометрии, вместо того, чтобы повторять вершины для каждой треугольной полосы.
В принципе, рисование примитивов (треугольников, вееров и полос) с использованием индексов полезно всякий раз, когда вы можете разделить большинство вершин одного примитива с другим.
Совместное использование информации позволяет экономить пропускную способность передачи информации, но это не единственное преимущество. Фактически индексированные массивы позволяют:
Индексированный массив нельзя использовать, как вы указали, когда вершина не может поделиться всей информацией, связанной с ней (цвет, координаты текстуры и т.д.) с другой совпадающей вершиной.
Просто для полноты картины, размер информации, необходимой для спецификации геометрии, не единственный фактор, определяющий оптимальную операцию рендеринга.
Фактически, еще одним основополагающим фактором примитивного рендеринга является локализация данных в кэше. Плохо заданные геометрические данные (не перемежающиеся буферные объекты, длинные треугольные полосы...) вызывают большое количество промахов кэша, снижая производительность графической карты.
Для оптимизации операции рендеринга спецификация вершин должна быть переупорядочена таким образом, чтобы с наибольшей вероятностью повторно использовать ранее указанные вершины. Таким образом, кэш-линия графической карты может повторно использовать ранее указанные вершины без извлечения их из памяти.