Отрисовка из буферов вершин и индексов (Direct3D 9)

Direct3D поддерживает как индексированные, так и неиндексированные методы рисования. Индексированные методы используют единый набор индексов для всех компонентов вершин. Данные вершин хранятся в буферах вершин, а данные индекса — в буферах индексов. Ниже приведено несколько распространенных сценариев рисования примитивов с помощью буферов вершин и индексов.

Эти примеры сравнивают использование IDirect3DDevice9::D rawPrimitive и IDirect3DDevice9::D rawIndexedPrimitive

Сценарий 1. Рисование двух треугольников без индексирования

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

Изображение квадрата, состоящего из двух треугольников

Если для отрисовки двух треугольников используется примитивный тип "Список треугольников", каждый треугольник будет храниться в виде трех отдельных вершин, что приведет к тому, что буфер вершин похож на следующий рисунок.

Схема буфера вершин, определяющего три вершины для двух треугольников

Вызов рисования очень прост; Начиная с расположения 0 в буфере вершин, нарисуйте два треугольника. Если отбрасывание включено, важен порядок вершин. В этом примере предполагается состояние отбраковки по умолчанию против часовой стрелки, поэтому видимые треугольники должны быть нарисованы в порядке часовой стрелки. Тип-примитив "Список треугольников" просто считывает три вершины в линейном порядке из буфера для каждого треугольника, поэтому этот вызов рисует треугольники (0, 1, 2) и (3, 4, 5):

DrawPrimitive( D3DPT_TRIANGLELIST, // PrimitiveType
               0,                  // StartVertex
               2 );                // PrimitiveCount

Сценарий 2. Рисование двух треугольников с помощью индексирования

Как вы заметите, буфер вершин содержит повторяющиеся данные в расположениях 0 и 4, 2 и 5. Это имеет смысл, так как два треугольника имеют две общие вершины. Эти дублирующиеся данные являются расточительными, и буфер вершин можно сжать с помощью буфера индекса. Меньший буфер вершин уменьшает объем данных вершин, которые необходимо отправить в графический адаптер. Что еще более важно, использование буфера индекса позволяет адаптеру хранить вершины в кэше вершин; Если рисуемый примитив содержит недавно использованную вершину, эту вершину можно получить из кэша, а не считывать из буфера вершин, что приводит к значительному увеличению производительности.

Буфер индекса "индексирует" в буфере вершин, поэтому каждая уникальная вершина должна храниться только один раз в буфере вершин. На следующей схеме показан индексный подход к предыдущему сценарию рисования.

Схема буфера индекса для предыдущего буфера вершин

В буфере индекса хранятся значения индекса VB, которые ссылаются на определенную вершину в буфере вершин. Буфер вершин можно рассматривать как массив вершин, поэтому индекс VB — это просто индекс в буфере вершин для целевой вершины. Аналогичным образом индекс IB является индексом в буфере индекса. Это может очень быстро запутаться, если вы не будете осторожны, поэтому убедитесь, что вы четко знаете используемый словарь: индекс значений индекса VB в буфер вершин, индекс значений индекса IB в буфер индекса, а сам буфер индекса хранит значения индекса VB.

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

   
DrawIndexedPrimitive( D3DPT_TRIANGLELIST, // PrimitiveType
                    0,                  // BaseVertexIndex
                    0,                  // MinIndex
                    4,                  // NumVertices
                    0,                  // StartIndex
                    2 );                // PrimitiveCount

Сценарий 3. Рисование одного треугольника с индексированием

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

Схема буфера индекса и буфера вершин для второго треугольника

Для этого вызова рисования первый используемый индекс IB — 3; это значение называется StartIndex. Наименьший используемый индекс VB — 0; это значение называется MinIndex. Несмотря на то, что для рисования треугольника требуется только три вершины, эти три вершины распределены по четырем смежным местам в буфере вершин; Число расположений в непрерывном блоке буфера вершин, необходимых для вызова рисования, называется NumVertices и в этом вызове будет равно 4. Значения MinIndex и NumVertices — это просто подсказки, помогающие Direct3D оптимизировать доступ к памяти во время обработки вершин программного обеспечения. Их можно просто задать, чтобы включить весь буфер вершин по цене производительности.

Ниже приведен вызов рисунка для регистра с одним треугольником; Значение аргумента BaseVertexIndex будет объяснено далее:

   
DrawIndexedPrimitive( D3DPT_TRIANGLELIST, // PrimitiveType
                    0,                  // BaseVertexIndex
                    0,                  // MinIndex
                    4,                  // NumVertices
                    3,                  // StartIndex
                    1 );                // PrimitiveCount

Сценарий 4. Рисование одного треугольника с индексированием смещения

BaseVertexIndex — это значение, которое фактически добавляется к каждому индексу VB, хранящейся в буфере индекса. Например, если бы мы передали значение 50 для BaseVertexIndex во время предыдущего вызова, это было бы функционально то же самое, что и при использовании буфера индекса на следующей схеме на протяжении всего вызова DrawIndexedPrimitive:

Схема буфера индекса со значением 50 для basevertexindex

Для этого значения редко устанавливается значение, отличное от 0, но оно может быть полезным, если требуется отделить буфер индекса от буфера вершин. Если при заполнении буфера индекса для определенной сетки расположение сетки в буфере вершин еще неизвестно, можно просто сделать вид, что вершины сетки будут расположены в начале буфера вершин. Когда придет время выполнить вызов draw, просто передайте фактическое начальную точку в качестве BaseVertexIndex.

Этот метод также можно использовать при рисовании нескольких экземпляров сетки с помощью одного буфера индекса; Например, если буфер вершин содержал две сетки с одинаковым порядком рисования, но немного разными вершинами (возможно, разные диффузные цвета или координаты текстуры), обе сетки можно было бы нарисовать с помощью разных значений для BaseVertexIndex. Используя эту концепцию еще на один шаг, можно использовать один буфер индекса для рисования нескольких экземпляров сетки, каждый из которых содержится в другом буфере вершин, просто циклизируя активный буфер вершин и корректируя BaseVertexIndex по мере необходимости. Обратите внимание, что значение BaseVertexIndex также автоматически добавляется к аргументу MinIndex, что имеет смысл, когда вы видите, как он используется:

Сделайте вид, что мы снова хотим нарисовать только второй треугольник четырехугольника, используя тот же буфер индексов, что и раньше; однако используется другой буфер вершин, в котором четырехугольник находится по индексу VB 50. Относительный порядок четырех вершин остается неизменным, отличается только исходное расположение в буфере вершин. Буфер индекса и буфер вершин будут выглядеть так, как показано на следующей схеме.

Схема буфера индекса и буфера вершин с индексом vb 50

Ниже приведен соответствующий вызов draw; Обратите внимание, что BaseVertexIndex — это единственное значение, которое изменилось по сравнению с предыдущим сценарием:

   
DrawIndexedPrimitive( D3DPT_TRIANGLELIST, // PrimitiveType
                    50,                 // BaseVertexIndex
                    0,                  // MinIndex
                    4,                  // NumVertices
                    3,                  // StartIndex
                    1 );                // PrimitiveCount

Отрисовка примитивов