Уроки Коммент.

Создание игр » DirectX 9, Featured » Vertex Declaration

Vertex Declaration

Vertex DeclarationVertex Declaration в Direct3D создаётся с помощью функции CreateVertexDeclaration, которой, в качестве аргументов, передаётся массив объектов типа D3DVERTEXELEMENT9, каждый из которых определяет отдельную запись (тип данных) в формате вершин модели – такие как позиция вершины, нормаль, текстурные координаты и так далее. Далее, при рендеринге объектов через Direct3D Device, до вызова отрисовки примитивов, созданный Vertex Declaration устанавливается с помощью функции SetVertexDeclaration и после этого графический конвейер сможет верно парсить данные нашей модели и передавать их на обработку в vertex shader, который, в свою очередь, после обработки, сможет их передавать в pixel shader.

Создание и использование Vertex Declaration

В примерах исходников к одному из предыдущих уроков, Vertex buffer, Index buffer практика, я уже использовал функцию создания Vertex Declaration и, там же, использовал созданную декларацию для рендера модели. Вот код делкарации из урока Per-pixel lighting:

D3DVERTEXELEMENT9 dwDeclMyVertex[] = 
{
	{0, 0,  D3DDECLTYPE_FLOAT3,   D3DDECLMETHOD_DEFAULT, 
	D3DDECLUSAGE_POSITION, 0}, // первая декларация
 
	{0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, 
	D3DDECLUSAGE_NORMAL, 0}, // вторая декларация
	{0, 24, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, 
	D3DDECLUSAGE_NORMAL, 1}, // третья декларация
	{0, 36, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, 
	D3DDECLUSAGE_NORMAL, 2}, // четвёртая декларация
 
	{0, 48, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, 
	D3DDECLUSAGE_TEXCOORD, 0}, // пятая декларация
 
	{0, 64, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, 
	D3DDECLUSAGE_COLOR, 0}, // шестая декларация
 
	D3DDECL_END() // седьмая декларация
};

Поля Vertex Declaration

Каждая запись (элемент массива) в Vertex Declaration является указанием DirectX о том, какие конкретно данные находятся в каком конкретно месте нашей структуры vertex’a. Когда мы устанавливаем поток данных (Vertex buffer) с помощью функции SetStreamSource, мы лишь указываем DirectX сам источник данных (т.е. наш Vertex Buffer, это второй параметр функции) и размер “данных вертекса” в байтах (третий параметр функции). Но этого мало для того, что бы DirectX мог работать с нашими данными – мы так же должны указать какие конкретно данные находятся в нашем вертексе и где конкретно (смещение от начала структуры) они лежат.

Каждая запись в массиве Vertex Declaration это структура D3DVERTEXELEMENT9, которая состоит из следующих частей:

  1. Stream – номер потока, из которого будут читаться данные. Номер потока, это первый аргумент функции установки потока данных SetStreamSource. По идее, мы можем установить сразу несколько потоков (скажем 3: №0, №1 и №2) и указать DirectX, что вертекс должен собираться по данным из нескольких потоков.
  2. Offset – смещение относительно начала текущего элемента данных (в байтах).
  3. Type – какой тип данных находится по указанному Offset (например, трёхкомпонентный вектор или четырёхкомпонентый вектор или DWORD)
  4. Method – используется для тесселятора. Мы его использовать не планируем (ибо в DirectX 9 он кривой, неудобный, тормозной и нормально не настраивается), потому всегда D3DDECLMETHOD_DEFAULT
  5. Usage – как будут использоваться данные. Например, мы можем указать, что этот элемент это текстурные координаты… или цвет… или позиция вертекса… или что-то ещё
  6. UsageIndex – индекс. В DirectX каждый вертекс может иметь данные (позиция, нормаль, цвет, текстурные координаты) в разных каналах. Например: TEXCOORD0, TEXCOORD1, TEXCOORD2. Вот это как раз индекс – 0, 1, 2 и т.д.

Что бы далее не объяснять абстрактно, давайте просто разберём приведённый выше пример декларации по строкам. Как Вы видите из комментариев, которые я добавил к коду, всего наш Vertex Declaration состоит из 7 записей-деклараций элементов.

  1. Первый элемент Vertex Declaration читается из 0’го потока (первый ноль) по смещению 0 (второй ноль), это тип данных трёхмерный вектор (D3DDECLTYPE_FLOAT3), который будет использоваться в шейдере как позиция вертекса (D3DDECLUSAGE_POSITION) с индексом 0, т.е. как POSITION0 (или просто POSITION)
  2. Второй элемент в Vertex Declaration (да и все остальные тоже) читается из потока №0, по смещению 12 (т.к. позиция это трёхмерный вектор, а трёхмерный вектор это 3 float’а, а каждый float занимает 4 байта, что в итоге даёт 3*4=12 байт), это тоже трёхмерный вектор (D3DDECLTYPE_FLOAT3), в шейдере он будет использоваться в качестве нормали (D3DDECLUSAGE_NORMAL) с индексом ноль
  3. Третий элемент Vertex Declaration, по смещению 24 (т.к. до него уже 2 трёхмерных вектора, 2*(3*4) = 24 байта), это трёхмерный вектор (D3DDECLTYPE_FLOAT3), который тоже будет использоваться как нормаль (D3DDECLUSAGE_NORMAL), но уже с индексом 1 (единица в конце)
  4. Далее аналогично, но это NORMAL2
  5. По смещению 48 байт от начала “данных вертекса” хранятся текстурные координаты (D3DDECLUSAGE_TEXCOORD), которые представлены четёрхмерным вектором (D3DDECLTYPE_FLOAT4, я запаковал 2 канала по 2 координаты в один 4хмерный вектор), индекс текстурных координат =0
  6. Последним в Vertex Declaration в идёт DWORD (D3DDECLTYPE_D3DCOLOR это DWORD), который в вертексе будет использоваться как цвет (D3DDECLUSAGE_COLOR) с индексом 0
  7. Завершающий D3DDECL_END() должен быть в конце каждого Vertex Declaration – не забывайте его указывать

Уфф… Кто-нибудь что-нибудь понял? ))) Надеюсь, что да )

Наверное, у Вас возник закономерный вопрос “а зачем иметь возможность использовать несколько разных Stream одновременно?”. Я пока не хочу сильно углубляться в этот вопрос. Приведу лишь один пример. Например, вы можете хранить в одном потоке (Stream) геометрию, а в другом – цвета вершин. При этом имея 1 геометрию и 2 потока с цветами, вы можете получить две разных модели (одна с одними цветами, а другая с другими). Когда у вас будет много моделей и каждая сможет иметь кучу цветов (например, один домик, но когда он стоит в лесу он обычный, когда он стоит на болоте у него фундамент становится более зелёным, а когда на воде – более тёмным) – это даст существенную экономию памяти (вместо того, что бы хранить 3 разных домика, мы будем хранить 1 доминк + 3 набора цветов к нему… это в сумме по затратам памяти где-то в 2-2.5 раза меньше, чем хранить 3 разных домика). Кроме того, потоки нужны для инстансинга, но geometry instancing это вообще отдельная тема и касаться я её сейчас не буду – по нему потом будет отдельный урок.

Вот Вам “домашнее задание” для закрепления материала: напишите Vertex Declaration для использования вот такой структуры в качестве данных вертекса:

struct HomeVertex
{
	D3DXVECTOR4 texCoord0;// текстурные координаты №0
	D3DXVECTOR4 pos;// позиция вертекса
	DWORD unused; // не используется в шейдере
	D3DXVECTOR4 data;// тоже не используется
	DWORD color0;// цвет №0
	DWORD color1;// цвет №1
	D3DXVECTOR2 texCoord1;// текстурные координаты №1
};

Исходников к этому уроку нет – они есть у Вас с прошлых уроков, Вы можете поэксперементировать с ними.

Ещё по этой теме:




Раздел: DirectX 9, Featured · Теги: Direct3D, Создание движка

5 комментариев на "Vertex Declaration"
  1. Antony пишет:

    Возможно дурацкий вопрос, но как же оно всё у нас работало и без указания деклараций (Vertex Declaration)?

    Спасибо за урок!

    1. Вячеслав пишет:

      Последние демки все были с использованием Vertex Declaration. А спрайты – используют FVF (SetFVF) – это устаревший, гораздо менее гибкий и менее удобный, но пока ещё работающий (только в ДХ9 и не выше) способ.

      »crosslinked«

  2. Antony пишет:

    Это я и хотел узнать – спасибо.

  3. Сергей пишет:

    Здравствуйте!
    Эту структуру вертекса нужно повторить во входной структуре к вертексному шейдеру, чтобы он ее правильно понимал я правильно понял? Тогда мне остается непонятным почему в VertexDeclaration цвет задает как DWORD, а на входе вертексного шейдера он уже как float4? Спрашиваю, потому что не получается из за чего-то передать цвет вертекса в шейдер.

    1. Вячеслав пишет:

      Граф. коневейер сам производит конвертацию из DWORD во флоаты. В декларации указано, что у нас цвет это DWORD, а на вход шейдера все данные приходят как float – потому просиходит автоматическая конвертация (она совершенно бесплатна).

Оставить комментарий

*

Вы можете использовать это HTMLтеги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>