Возможно ли использовать объекты индексного буфера (IBO) с функцией ‘glMultiDrawElements’?

#opengl #glsl

#opengl #glsl

Вопрос:

Я разрабатываю небольшой 3D-движок, используя OpenGL и GLSL.

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

В настоящее время я использую функцию ‘glMultiDrawElements’ для рендеринга определенного пакета данных (таким образом, один вызов draw). Так, например, если у меня в пакете 3 сетки, у меня также есть 3 массива индексов (по одному для каждой сетки), организованных в массив ‘GLvoid **’ (двойной массив). Итак, чтобы отобразить мои 3 сетки, у меня есть уникальный вызов glMultiDrawElements, но я должен передать непосредственно функции двойной массив элементов в параметре.

Но мне интересно (по вопросу производительности), возможно ли сохранить все элементы (двойной массив элементов) в объекте индексного буфера (IBO) (как это можно сделать с помощью glDrawElements -> здесь простой массив элементов) и связать его перед вызовом glMultiDrawElements… Я так не думаю, потому что это двойной массив, а не простой массив, но, возможно (и я надеюсь на это) Я ошибаюсь.

Вот пример использования glDrawElements (псевдокод):

 [...]

//Setup data

[...]

#define OFFSET_BUFFER(offset) ((char*)NULL   offset)

foreach (pMeshGeometry from meshes) //iterates for each mesh of the scene
{
    pMeshGeometry->GetIndexBuffer().Lock(); //Bind IBO
    {
         glDrawElements(pMeshGeometry->GetPrimitiveType(),       
             pMeshGeometry->GetIndexBufferSize(), pMeshGeometry->GetIndexBuffer().GetType(), OFFSET_BUFFER(0)); //Render a specific mesh eccording to indices
    }
}
  

На данный момент я использую glMultiDrawElement таким образом:

 glMultiDrawElements(GL_TRIANGLES, amp;this->m_CountElementArray[0], GL_UNSIGNED_INT,
                    (const GLvoid **)amp;this->m_IndexAttribArray[0], this->m_CountElementArray.size()); //I enter the array of pointer directly in parameter
  

Итак, возможно, следующий пример должен быть возможен:

 #define OFFSET_BUFFER(offset) ((char**)NULL   offset) //Something like this

glMultiDrawElements(GL_TRIANGLES, amp;this->m_CountElementArray[0], GL_UNSIGNED_INT,
                    OFFSET_BUFFER(0), this->m_CountElementArray.size()); //Something like this
  

Итак, если это невозможно сделать, я подумал о функции ‘glDrawRangeElements’. Для моего примера из 3 сеток в уникальном VBO мне просто нужно будет привязать IBO перед каждым вызовом glDrawRangeElements (здесь 3 вызова рисования для каждой сетки -> so цикл glDrawRangeElements). Итак, здесь явно можно использовать IBO.

Этот метод наверняка сработает, но я не думаю, что он лучший! Я думаю, что это можно сделать, используя glMultiDrawElements с IBO, но я не знаю, как это сделать.

Или, может быть, это действительно невозможно. Возможно, тот факт, что ввод directy в параметр массива индексов выполняется быстрее, чем метод, использующий glDrawRangeElements и его IBO, и поэтому использование IBO в этом случае, возможно, устарело и поэтому не адаптировано.

Что вы об этом думаете?

Комментарии:

1. Пожалуйста , используйте некоторые разрывы строк. Этот гигантский абзац практически невозможно прочитать.

2. Что именно вы здесь спрашиваете? Даже с разрывами строк я не уверен.

3. Извините, я думал, это ясно. Подводя итог, мне интересно, можно ли использовать IBO с glMultiDrawElements вместо прямого использования двойного массива индексов в параметре.

4. @user1364743: Нет «двойного массива». Существует массив указателей / смещений. Есть ARB_multi_draw_indirect . Но я не уверен, что это то, о чем вы просите.

5. Извините, я хотел сказать массив указателей, а не двойной массив. Я обновил свой вопрос, добавив пример исходного кода (псевдокода) с помощью метода ‘glDrawElements’. Как вы можете видеть перед основным циклом, я настраиваю все данные вершин в VBO, а все данные индексов — в IBO. После этого в основном цикле я использую макрос OFFSET_BUFFER. Это работает. Возможно ли использовать такой макрос для функции ‘glMultiDrawElements’ (и поэтому использовать IBO вместо прямого использования массива указателей)?

Ответ №1:

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

Чтобы увидеть, как это работает, нам нужно посмотреть, что означают аргументы glMultiDrawElements() . Вызов — это, по сути, просто ярлык для нескольких glDrawElements() вызовов. Подпись:

 void glMultiDrawElements(GLenum mode, const GLsizei* count, GLenum type,  
                         const GLvoid** indices, GLsizei primcount); 
  

Помимо некоторых деталей проверки ошибок, этот вызов эквивалентен:

 for (int i = 0; i < primcount;   i) {
    glDrawElements(mode, count[i], type, indices[i]);
}
  

Теперь помните, что если буфер индекса привязан, последним аргументом glDrawElements() является относительное смещение в буфер. Таким образом, соответствующий 4-й элемент glMultiDrawElements() представляет собой массив смещений в буфер. 2-й аргумент — это соответствующий массив значений.

Люди часто используют макрос, чтобы скрыть громоздкое преобразование типа последнего аргумента glDrawElements() . Используя это:

 #define BUFFER_OFFSET(offset) (static_cast<char*>(0)   (offset))
  

В качестве примера, допустим, у нас есть привязка к индексному буферу, и мы хотим нарисовать 3 поддиапазона индексного массива одним вызовом:

  • 20 индексов, начинающихся с индекса 10 в буфере.
  • 30 индексов, начиная с индекса 40 в буфере.
  • 10 индексов, начинающихся с индекса 90 в буфере.

Я буду использовать unsigned shorts (GLushort) для типа индекса. Таким образом, индексный буфер был бы заполнен данными из GLushort indexA[100] в какой-то момент в прошлом. Тогда вызов setup и draw выглядит следующим образом:

 GLsizei count[3] = {20, 30, 10};
GLvoid* indices[3] = {
    BUFFER_OFFSET(10 * sizeof(GLushort)),
    BUFFER_OFFSET(40 * sizeof(GLushort)),
    BUFFER_OFFSET(90 * sizeof(GLushort)),
};
glMultiDrawElements(GL_TRIANGLE_STRIP, count, GL_UNSIGNED_SHORT, indices, 3);
  

Комментарии:

1. Большое вам спасибо за этот полный ответ. Я уверен, что ваше решение является лучшим, и оно очень логично. Я собираюсь интегрировать ее в свою программу. Хорошего дня. Пока!