Разница в glDrawArrays и glDrawElements

12

Освежая свой разум на OpenGL ES, я наткнулся glDrawArraysи на glDrawElements.

Я понимаю, как они используются, и понимаю, почему они разные.

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

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

Мне нужно было бы иметь набор из 6 вершин, использующих glDrawArraysпри использовании glDrawElements, мне нужно было бы только 4 в дополнение к массиву индекса, который имеет 6 элементов.

Учитывая вышесказанное, вот что я не понимаю:

  1. как glDrawElementsсохранить вызовы отрисовки, если ему все еще нужно использовать массив индексов (6 индексов в случае квадрата) для индексации в моем массиве вершин, в котором было 4 элемента (6 раз)? Другими словами, значит ли это, glDrawElementsчто все равно должно быть в общей сложности 6 вызовов на розыгрыш glDrawArrays?
  2. Как бы glDrawElementsсэкономить место, если бы все еще нужно было иметь 2 массива, а именно: один для вершин и один для индексов?
  3. В случае рисования квадрата из 2 треугольников, для простоты, сколько вызовов рисования glDrawElements(4 элемента в массиве вершин и 6 элементов в массиве индексов) и glDrawArrays(6 элементов только в массиве вершин) по отдельности?

Спасибо.

Unheilig
источник

Ответы:

16

Каждый glDraw * является вызовом отрисовки.

1 glDrawArrays - это 1 вызов на ничью.
1 glDrawElements - это 1 вызов ничьей.

Неважно (что касается количества вызовов отрисовки), сколько вершин или индексов вы используете, 1 glDraw * - это 1 вызов отрисовки.

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

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

glDrawArrays (GL_TRIANGLE_FAN, .....);
glDrawArrays (GL_TRIANGLE_STRIP, .....);
glDrawArrays (GL_TRIANGLE_STRIP, .....);
glDrawArrays (GL_TRIANGLE_FAN, .....);
glDrawArrays (GL_TRIANGLE_STRIP, .....);
glDrawArrays (GL_TRIANGLE_FAN, .....);

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

glDrawElements (GL_TRIANGLES, .....);

Это один розыгрыш для всей модели.


Преимущества glDrawElements по сравнению с glDrawArrays заключаются не только в экономии памяти, что является наивным способом измерения. Существует потенциал для экономии памяти, когда вершины можно использовать повторно, потому что индекс обычно меньше вершины (поэтому, даже если у вас много индексов в балансе, они будут меньше), но другие преимущества включают в себя:

  • Уменьшено количество вызовов отрисовки. Каждый вызов отрисовки влечет за собой некоторые накладные расходы от проверки состояния, настройки и т. Д., И большая часть этих накладных расходов происходит на стороне ЦП. Сокращая количество вызовов отрисовки, мы избегаем значительной части этих накладных расходов.

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


Итак, чтобы ответить на ваши конкретные вопросы:

  1. Как glDrawElements может сохранить вызовы отрисовки ...? В примере, который вы используете, это не так. У каждого есть один колл-дро. Как я уже говорил выше, этот пример очень плох для сравнения.

  2. Как с помощью glDrawElements сэкономить место ...? Потому что индексы меньше, чем вершины. 16-битный индекс составляет два байта, вершина позиции / цвета / текстовой координаты составляет 24 байта. 6 вершин - 144 байта, 4 вершины плюс 6 индексов - 108 байтов.

  3. В случае рисования квадрата из 2-х треугольников ...? Один и один. Вызов glDraw * - это один вызов отрисовки, количество используемых вершин или индексов не имеет значения для этого измерения. Но я должен еще раз подчеркнуть, что это очень плохой пример для сравнения двух.


И, наконец, если немного усложнить ситуацию, в то время как glDrawElements с треугольниками - это оптимальный путь для настольных графических процессоров, для мобильных устройств он может сильно отличаться. Некоторые мобильные графические процессоры могут предпочесть glDrawArrays с GL_TRIANGLE_STRIP (в этом случае вы бы добавили вырожденные треугольники для объединения примитивов), и я даже не затрагивал более сложные темы, такие как перезапуск примитивов или мульти-рисование.

Максимус Минимус
источник
Большое спасибо за ваш отзыв; Я просто уделяю немного времени чтению того, чем вы поделились. Просто хотел сообщить, что я признал твой ответ. Еще раз спасибо.
Unheilig
В вашем примере преобразования 6 вызовов DrawTriangleFans и DrawTriangleStrips в 1 отдельный вызов DrawTriangles. Это действительно улучшает производительность? Насколько я понимаю, рисование треугольной полосы, состоящей из 100 треугольников, гораздо эффективнее, чем рисование 100 треугольников по отдельности, и выгода легко компенсирует любой штраф, полученный при использовании 6 вызовов функций, а не 1.
Самуэль Ли
@SamuelLi 6-к-1 не будет огромным улучшением, он просто призван проиллюстрировать принцип. Кроме того, как я уже говорил, полоски обычно не выигрывают в производительности по сравнению с индексированными списками на настольном оборудовании после 1998 года. Проверьте дату на любом документе, рекомендующем использовать полоски.
Максимус
Понял, спасибо за разъяснения! @MaximusMinimus
Самуэль Ли