Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
DirectX. Продвинутая Анимация (2004) [rus].pdf
Скачиваний:
335
Добавлен:
16.08.2013
Размер:
8.39 Mб
Скачать

360 Глава 12

Создание и уничтожение частиц

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

Например, попадание в стену из лазерной винтовки может породить группу частиц, представляющих летящие осколки. Эти осколки быстро отлетают и рассеиваются. Это типично для большинства используемых частиц - они существуют только в течение короткого интервала времени, а потом исчезают. Конечно есть исключения, когда частицы существуют всю игру. Например, возьмите биллбордные деревья или знаки, которые наполняют ландшафт. Эти частицы никогда не двигаются и не исчезают (если только их не растопчет монстр). Лучшим способом управления этими долго существующими объектами является рисование только тех, которые находятся в пределах определенного расстояния от смотрящего, таким образом уменьшая количество частиц, рисуемых за каждый кадр. Для демонстрационных программ, содержащихся на компакт-диске, я использовал частицы деревьев и людей, которые не уничтожаются. Также я использовал частицы клубов дыма - эти частицы уничтожаются после непродолжительного времени.

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

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

class cParticle

{

public:

// Предыдущие данные частицы, такие как тип, положение и т.д. cParticle *m_Prev; // Предыдущая частица в связанном списке cParticle *m_Next; // Следующая частица в связанном списке

public:

// Конструктор и деструктор для очистки и освобождения данных cParticle () { m_Prev = NULL; m_Next = NULL; }

~cParticle () { delete m_Next; m_Next=NULL; m_Prev=NULL; }

};

Использованиечастицванимации

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

вкачестве следующей (используя указатели m_Prev и m_Next).

//pRoot = текущая корневая частица списка

//Создать новый экземпляр используемого класса частиц cParticle *pParticle = new cParticle();

//Связать новую частицу в качестве корня и привязать к старому корню

pParticle->m_Prev = NULL; // Очистить указатель на предыдущий объект pParticle->m_Next = pRoot; // Привязать новую частицу к старой

корневой

pRoot->m_Prev = pParticle; // Привязать корневую частицу к новой pRoot = pParticle; // Переприсвоить корневую частицу новой

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

//pParticle = указатель удаляемой частицы

//pRoot = корневая частица

//Привязать предыдущую частицу к следующей if(pParticle->m_Prev) {

pParticle->m_Prev->m_Next = pParticle->m_Next;

}else {

//Если предыдущей частицы нет, значит текущая частица является корневой.

//Теперь необходимо сделать корневой следующую частицу

pRoot = pParticle->m_Next;

}

//Привязать следующую частицу к предыдущей if(pParticle->m_Next) pParticle->m_Next->m_Prev = pParticle->m_Prev;

//Очистить указатели частицы и освободить ресурсы pParticle->m_Prev = pParticle->m_Next = NULL; delete pParticle;

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

362

Глава 12

Я оставляю вам самостоятельно посмотреть исходный код демонстрационной программы Particles, чтобы увидеть, как использовать связанный список для хранения большого числа частиц. А пока, читайте, как использовать связанный список частиц для их визуализации.

Рисованиечастиц

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

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

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

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

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

//pRoot = корневой объект cParticle, содержащий все визуализируемые

//частицы

//matView, matProj = преобразования вида и проекции

//pTexture = объект текстуры, используемый при визуализации частиц

//pShader, pDecl = объекты вершинного шейдера и объявления

//pVB, pIB = объекты буферов вершин и индексов

//NumParticles = количество частиц, которые могут находится

вбуфере вершин

Использованиечастицванимации

// Установить вершинный шейдер, объявления и источники потоков pDevice->SetFVF(NULL);

pDevice->SetVertexShader(pShader); pDevice->SetVertexDeclaration(pDecl);

pDevice->SetStreamSource(0, pVB, 0, sizeof (sShaderVertex)); pDevice->SetIndices(pIB);

// Включить альфа-тестирование

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->SetTexture(0, pTexture);

//Сохранить транспонированную матрицу вид*проекция в константах D3DXMATRIX matViewProj = (*matView) * (*matProj); D3DXMatrixTranspose(&matViewProj, &matViewProj); pDevice->SetVertexSnaderConstantF(0, (float*)&matViewProj, 4);

//Получить нормальные векторы вверх и вправо из преобразования вида D3DXVECTOR4 vecRight, vecUp;

//Вектор вправо явяляется первым столбцом D3DXVec4Normalize(svecRight, \

&D3DXVECTOR4(matView._11, \ matView._21, \ matView._31, 0.0f));

//Вектор вверх является вторым столбцом

D3DXVec4Normalize(&vecUp, \ &D3DXVECTOR4(matView._12, \

matView._22, \ matView._32, 0.0f));

// Сохранить векторы в константах pDevice->SetVertexShaderConstantF(4, (float*)&vecRight, 1); pDevice->SetVertexShaderConstantF(5, (float*)&vecUp, 1);

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

364

Глава12

Замечание. Т. к. здесь буфер вершин постоянно блокируется, изменяется иразблокируется, возможно вы захотите использовать флаг D3DUSAGE_DYNAMIC при вызове IDirect3DDevice9::CreateVertexBuffer.ЭтопозволитсообщитьDirect3D,чтосбуфером будет проводиться многоработы, чтобы разместить его в легко доступной и эффективноиспользуемойпамяти.

//Начать с первой частицы в списке cParticle *Particle = pRoot;

//Установить количество для сброса буфера вершин DWORD Num = 0;

//Заблокировать буфер вершин

sShaderVertex *Ptr;

pVB->Lock(0, 0, (void**)&Ptr, D3DLOCK_DISCARD);

// Просмотреть все частицы while(Particle != NULL) {

После начала цикла вы можете скопировать данные текущей вершины в буфер вершин

// Скопировать данные частиц в буфер вершин float HalfSize = Particle->m_Size / 2.0f;

Ptr[0].vecPos = Particle->m_vecPos;

Ptr[0].vecOffset = D3DXVECTOR2(-HalfSize, HalfSize);

Ptr[0].Diffuse = Particle->m_Color;

Ptr[0].u = 0.0f;

Ptr[0].v = 0.0f;

Ptr[1].vecPos = Particle->m_vecPos;

Ptr[1].vecOffset = D3DXVECTOR2(HalfSize, HalfSize);

Ptr[1].Diffuse = Particle->m_Color;

Ptr[1].u = 1.0f;

Ptr[1].v = 0.0f;

Ptr[2].vecPos = Particle->m_vecPos;

Ptr[2].vecOffset = D3DXVECTOR2(-HalfSize, -HalfSize);

Ptr[2].Diffuse = Particle->m_Color;

Ptr[2].u = 0.0f;

Ptr[2].v = 1.0f;

Ptr[3].vecPos = Particle->m__vecPos;

Ptr[3].vecOffset = D3DXVECTOR2(HalfSize, -HalfSize);

Ptr[3].Diffuse = Particle->m_Color;

Ptr[3].u = 1.0f;

Ptr[3].v = 1.0f;

Ptr+=4; // перейти к следующим четырем вершинам

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

Соседние файлы в предмете Программирование на C++