Как вы можете видеть, частицы являются важной частью игр — многие игры используют их для создания разнообразных спецэффектов. Корпорация Microsoft прекрасно об этом знала и добавила возможность визуализации биллбордных квадратных полигонов в Direct3D при использовании точечных спрайтов. Точечный спрайт является простым биллдным квадратным полигоном, содержащим минимальный набор данных. Каждая частица представлена одной трехмерной точкой (соответствующей ее центру) и размером (как он вводился в предыдущей части, за исключением того, что он одинаков для обоих осей х и у). Это позволяет сэкономить огромное количество памяти по сравнению с использованием квадратов.
Помните, ранее в этой главе упоминалось, что квадратная частица требует четырех вершин. В предыдущей структуре sVertex использовалось двадцать вещественных значений и четыре значения DWORD на одну частицу. Итого: 96 байт данных для визуализации одной частицы! А как насчет точечных спрайтов в DirectX
Точечные спрайты используют следующую структуру вершин:
typedef struct {
D3DXVECTOR3 vecPos; // Координаты центра частицы
float Size; // Размер частицы
D3DCOLOR Diffuse; // Рассеянный цвет } sPointSprite;
Выглядит очень похоже, разве нет?
Большим отличием является то, что для одной частицы необходима только одна структура sPointSprite. Так и есть, структура sPointSprite использует только 20 байт данных, что меньше чем sVertex на 76 байт. Какая экономия!
Так в чем же хитрость точечных спрайтов? Вы же знаете, что должны быть какие-нибудь недостатки, не так ли? Недостатком точечных спрайтов является ограниченность их размера. При использовании квадратов вы можете создавать частицы любого размера, точечные спрайты ограничены в максимальном размере, определяемом переменной D3DCAPS9::MaxPointSize. Это означает, что максимальный размер точечных спрайтов зависит от видео карты конечного пользователя.
Другим недостатком является то, что драйвер видео карты должен поддерживать точечные спрайты. Часто я встречал плохие драйверы, при использовании которых точечные спрайты при визуализации моргали либо имели неправильный размер. Вам остается только надеяться, что конечный пользователь обновит свои драйверы, что гарантирует корректное использование точечных спрайтов.
Замечание. Для того чтобы убедиться, что вы можете аппаратно визуализировать точечные спрайты, необходимо проверить возможности аппаратного драйвера при помощи функции IDirect3D9::GetDeviceCaps. Если значение D3DCAPS9::MaxPoin-tSize, полученное после вызова этой функции, установлено в 1.0, то тогда точечные спрайты не поддерживаются аппаратно.
Не будем брать во внимание недостатки, - точечные спрайты сохраняют огромное количество памяти; для тех видео карт, которые могут их использовать, это является превосходным решением для рисования частиц. Как вы видели в структуре вершин sPointSprite, точечные спрайты используют только четыре вещественных значения и одно DWORD для хранения координат, размера и рассеянного цвета частицы соответственно.
Замечание. Точечные спрайты используют всю поверхность текстуры, на которую рисуются. Это означает, что вам необходима одна текстура на каждую используемую картинку частицы. Это также означает, что вам не надо задавать текстурные координаты в структуре вершин точечного спрайта.
Точечные спрайты используют следующее объявление FVF:
#define POINTSPRITEFVF (D3DFVF_XYZ|D3DFVF_PSIZE|D3DFVF_DIFFUSE)
{mospagebreak}При вызове функции CreateVertexBuffer необходимо указать флаг D3DUSAGE_POINTS, как показано в следующем кусочке кода:
pDevice->CreateVertexBuffer(8 * sizeof(sPointSprite), \ D3DUSAGE_POINTS | D3DUSAGE_WRITEONLY, \ POINTSPRITEFVF, D3DPOOL_DEFAULT, \ &pBuffer, 0);
После того как вы создали буфер вершин (не забыв указать количество содержащихся в буфере вершин), вы можете начать заполнять его данными частиц, которые вы хотите визуализировать. Предположим, вы хотите визуализировать восемь частиц, каждая из которых имеет размер 10 единиц (занимая по 5 единиц в направлениях осей х и у) и использует белый рассеянный цвет. Используя только что созданный буфер вершин, вы можете заблокировать, заполнить и разблокировать его, используя следующий код:
float Size = 10.0f; // Сделать размер частиц равным 10 единицам
sPointSprite PointSprites[8] = { // Частица #0 { D3DXVECTOR3(0.0f,0.0f,0.0f), Size,
D3DCOLOR_RGBA(255,255,255,255) }, // Частица #1 { D3DXVECTOR3(10.0f,0.0f,0.0f), Size,
D3DCOLOR_RGBA(255,255,255,255) }, // Частица #2 { D3DXVECTOR3(22.0f,0.0f,0.0f), Size,
D3DCOLOR_RGBA(255,255,255,255) }, // Частица #3 { D3DXVECTOR3(30.0f,0.0f,0.0f), Size,
D3DCOLOR_RGBA(255,255,255,255) }, // Частица #4 { D3DXVECTOR3(-10.0f,0.0f,0.0f), Size,
D3DCOLOR_RGBA(255,255,255,255) }, // Частица #5 { D3DXVECTOR3(-20.0f,0.0f,0.0f), Size,
D3DCOLOR_RGBA(255,255,255,255) }, // Частица #6 { D3DXVECTOR3(-30.0f,0.0f,0.0f), Size,
D3DCOLOR_RGBA(255,255,255,255) },
// Частица #7
{ D3DXVECTOR3(-40.0f,0.0f,0.0f), Size, D3DCOLOR_RGBA(255,255,255,255) }, };
// Заблокировать буфер вершин sPointSprite *Ptr; pBuffer->Lock(0,0,(void**)&Ptr,0);
// Скопировать данные вершин в буфер memcpy(Ptr, PointSprites, sizeof(PointSprites));
// Разблокировать буфер вершин pBuffer->Unlock();
После того как вы создали и заполнили буфер вершин данными точечного спрайта, можно визуализировать его. Подождите! Я забыл упомянуть об установке важных состояний визуализации. Direct3D должно знать, что вы используете точечные спрайты, расположенные в трехмерном пространстве (в противоположность экранному пространству). Для этого вы должны установить соответствующие состояния визуализации, как показано тут:
// Использовать всю текстуру для визуализации точечного спрайта pDevice->SetRenderState(D3DRS_POINTSPRITEENABLE, TRUE);
// Масштабировать в пространстве камеры pDevice->SetRenderState(D3DRS_POINTSCALEENABLE, TRUE);
Также необходимо установить минимальный размер точечного спрайта и насколько большим его делать, если в объявлении вершин отсутствует размер. На данный момент я установлю, чтобы точечные спрайты использовали размер 1, если в объявлении вершин отсутствуют необходимые данные, и чтобы минимальный размер был 0.
// Установить минимальный и определяемый по умолчанию размер точечного спрайта
pDevice->SetRenderState(D3DRS_POINTSIZE, FLOAT2DWORD(1.0f)); pDevice->SetRenderState(D3DRS_POINTSIZE_MIN, FLOAT2DWORD(0.0f));
{mospagebreak}Наконец необходимо установить несколько масштабируемых коэффициентов затухания, основанных на расстоянии. Direct3D сможет изменять размер частиц в зависимости от их удаленности от смотрящего. Здесь не нужно ничего необычного, так что значения, установленные по умолчанию (заданные в документации DirectX SDK) подойдут. Эти коэффициенты (которые фактически являются состояниями визуализации) устанавливаются при помощи следующего кода:
// Определить функцию, преобразующую float в DWORD
inline DWORD FLOAT2DWORD(FLOAT f) { return *((DWORD*)&f); }
// Установить значения затухания для масштабирования pDevice->SetRenderState(D3DRS_POINTSCALE_A, FLOAT2DWORD(1.0f)); pDevice->SetRenderState(D3DRS_POINTSCALE_B, FLOAT2DWORD(0.0f)); pDevice->SetRenderState(D3DRS_POINTSCALE_C, FLOAT2DWORD(0.0f));
Вы заметите кое-что забавное в последнем кусочке кода- использование функции FLOAT2DWORD. Как вы знаете, функция SetRenderState принимает только DWORD в качестве второго параметра. Значения коэффициентов затухания являются вещественными, так что приходится приводить их к типу DWORD. Вот здесь то и появляется FLOAT2DWORD. Используя эту функцию, вы можете указывать любое вещественное значения в качестве параметра функции SetRenderState и быть уверенным, что оно правильно преобразуется в DWORD.
Наконец можно визуализировать точечные спрайты! Помните, точечные спрайты являются обычными буферами вершин, использующими примитив типа точечный спрайт, указываемый флагом D3DPT_POINTLIST при вызове функции DrawPrimitive. Не отвлекаясь, вот код, позволяющий включить альфа-тестирование и смешивание, установить FVF и источники потоков и вызвать функцию DrawPrimitive.
// Включить альфа-тестирование
pDevice->SetRenderState(D3DRS_ALPHATESTENABLE, TRUE); pDevice->SetRenderState(D3DRS_ALPHAREF, 0x08); pDevice->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL);
// Включить альфа-комбинирование (простого добавочного типа) pDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); pDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCCOLOR); pDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_DESTCOLOR);
// Установить вершинный шейдер и источник потока
pDevice->SetVertexShader(NULL);
pDevice->SetFVF(POINTSPRITEFVF);
pDevice->SetStreamSource(0, pBuffer, 0, sizeof(sPointSprite));
// Визуализировать точечный спрайт (восемь спрайтов) pDevice->DrawPrimitive(D3DPT_POINTLIST, 0, 8);
Как вы можете видеть, работать с точечными спрайтами очень легко. По крайней мере намного проще, чем иметь дело с биллбордными квадратными полигонами, в спрайтах используется меньше данных. Единственной проблемой является то, что точечные спрайты ограничены в размерах и не полностью поддерживаются всеми видео-картами. Было бы очень хорошо, если бы была возможность использовать большие частицы и скорость визуализации точечных спрайтов, не правда ли? Замечательные новости - вы можете получить и то и другое при использовании вершинных шейдеров!
12 июля 2010