- •Введение
- •Глава 1. Подготовка к изучению книги
- •Установка DirectX SDK
- •Выбор отладочных или рабочих версий библиотек
- •Настройка вашего компилятора
- •Установка директорий DirectX SDK
- •Привязывание к библиотекам DirectX
- •Установка используемого по умолчанию состояния символа
- •Использование вспомогательного кода книги
- •Использование вспомогательных объектов
- •Проверка вспомогательных функций
- •Двигаясь дальше по книге
- •Глава 2. Синхронизация анимации и движения
- •Использование движения, синхронизированного по времени
- •Считывание времени в Windows
- •Анимирование с использованием временных меток
- •Перемещение, синхронизированное со временем
- •Движение вдоль траекторий
- •Создание анализатора маршрутов .X файла
- •Создание внутриигровых кинематографических последовательностей
- •Посмотрите демонстрационные программы
- •TimedAnim
- •TimedMovement
- •Route
- •Cinematic
- •Глава 3. Использование формата файла .X
- •Работа с .X шаблонами и объектами данных
- •Определение шаблонов
- •Работа со стандартными шаблонами DirectX
- •Открытие .X файла
- •Перечисление объектов данных
- •Получение данных объекта
- •Создание класса .X анализатора
- •Загрузка мешей с использованием D3DX
- •Загрузка мешей, используя анализатор .X
- •Загрузка скелетных мешей
- •Загрузка анимации из .X
- •Загрузка специализированных данных из .X
- •Посмотрите демонстрационные программы
- •ParseFrame
- •Глава 4. Работа со скелетной анимацией
- •Начало скелетной анимации
- •Использование структур скелетов и иерархий костей
- •Использование скелетной структуры и скелетного меша
- •Загрузка иерархий из .X
- •Изменение положения костей
- •Обновление иерархии
- •Работа со скелетными мешами
- •Загрузка скелетных мешей из .X
- •Создание контейнера вторичного меша
- •Сопоставление костей фреймам
- •Обновление скелетного меша
- •Визуализация скелетных мешей
- •Глава 5. Использование скелетной анимации, основанной на ключевых кадрах
- •Использование наборов скелетных анимаций, основанных на ключевых кадрах
- •Использование ключей при анимации
- •Работа с четырьмя типами ключей
- •Считывание данных анимации из .X файлов
- •Прикрепление анимации к костям
- •Обновление анимации
- •Посмотрите демонстрационные программы
- •Глава 6. Комбинирование скелетных анимаций
- •Комбинирование скелетных анимаций
- •Соединение преобразований
- •Улучшение объектов скелетной анимации
- •Посмотрите демонстрационные программы
- •Глава 7. Создание кукольной анимации
- •Работа с физикой твердого тела
- •Создание твердого тела
- •Расположение и ориентирование твердых тел
- •Обработка движения твердых тел
- •Использование сил для создания движения
- •Соединение твердых тел с помощью пружин
- •Обеспечение обнаружения столкновений и ответной реакции
- •Создание систем кукольной анимации
- •Определение состояния твердого тела
- •Хранение костей
- •Создание класса управления куклой
- •Создание данных костей
- •Вычисление ограничивающего параллелепипеда кости
- •Установка сил
- •Объединение костей
- •Обработка столкновений
- •Восстановление соединений костей
- •Перестроение иерархии
- •Посмотрите демонстрационные программы
- •Глава 8. Работа с морфирующей анимацией
- •Морфинг в действии
- •Определение исходного и целевого меша
- •Морфинг мешей
- •Создание морфированного меша при помощи обработки
- •Визуализация морфированных мешей
- •Расчленение наборов
- •Создание морфирующего вершинного шейдера
- •Посмотрите демонстрационные программы
- •Глава 9. Использование морфирующей анимации, основанной на ключевых кадрах
- •Использование наборов морфируемой анимации
- •Создание шаблонов .X для морфируемой анимации
- •Загрузка данных морфируемой анимации
- •Визуализации морфированного меша
- •Получение данных морфируемого меша из альтернативных источников
- •Посмотрите демонстрационные программы
- •Глава 10. Комбинирование морфированных анимаций
- •Комбинирование морфированных анимаций
- •Использование базового меша в комбинированных морфированных анимациях
- •Вычисление разностей
- •Комбинирование разностей
- •Создание вершинных шейдеров комбинированного морфирования
- •Использование вершинного шейдера морфируемого комбинирования
- •Посмотрите демонстрационные программы
- •Глава 11. Морфируемая лицевая анимация
- •Основы лицевой анимации
- •Использование комбинированного морфирования
- •Использования фонем для речи
- •Создание лицевых мешей
- •Создание базового меша
- •Создание выражений лица
- •Создание мешей визем
- •Создание анимационных последовательностей
- •Создание последовательностей фонем
- •Использование анализатора файлов .X для последовательностей
- •Проигрывание лицевых последовательностей со звуком
- •Использование DirectShow для звука
- •Синхронизация анимации со звуком
- •Зацикливание воспроизведения звуков
- •Посмотрите демонстрационные программы
- •Глава 12. Использование частиц в анимации
- •Работа с частицами
- •Основы
- •Рисование частиц с помощью квадратных полигонов
- •Работа с точечными спрайтами
- •Улучшения визуализации частиц при помощи вершинных шейдеров
- •Оживление частиц
- •Передвижение частиц при помощи скорости
- •Использование интеллекта при обработке
- •Создание и уничтожение частиц
- •Управление частицами с помощью класса
- •Использование излучателей в проектах
- •Создание движков частиц в вершинных шейдерах
- •Посмотрите демонстрационные программы
- •Глава 13. Имитирование одежды и анимация мешей мягких тел
- •Имитация одежды в ваших проектах
- •Получение данных одежды из мешей
- •Приложение сил для создания движения
- •Воссоздание и визуализация меша одежды
- •Восстановление исходного меша
- •Добавление дополнительных пружин
- •Загрузка данных масс и пружин из .X файла
- •Создание анализатора .X данных одежды
- •Работа с обнаружением столкновений и реакцией на них
- •Определение объектов столкновений
- •Обнаружение и реакция на столкновения
- •Создание класса меша одежды
- •Использование мешей мягких тел
- •Восстановление мешей мягких тел
- •Посмотрите демонстрационные программы
- •Глава 14. Использование анимированных текстур
- •Использование анимации текстур в ваших проектах
- •Работа с преобразованиями текстур
- •Создание преобразования текстур
- •Установка матриц преобразования текстуры
- •Использование преобразования текстур в проектах
- •Использование файлов видео в качестве текстур
- •Импорт видео при помощи DirectShow
- •Создание специализированного фильтра
- •Работа со специализированным фильтром
- •Создание менеджера анимированных текстур
- •Окончание современной анимации
- •Веб-сайты
- •Рекомендуемые книги
- •DirectX 9.0 SDK
- •GoldWave Demo
- •Paint Shop Pro Trial Version
- •TrueSpace Demo
- •Microsoft Agent and LISET
- •Предметный указатель
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; // перейти к следующим четырем вершинам
После копирования всех вершин в буфер вершин вы можете увеличить количество частиц, содержащихся в нем. Если их количество достигает максимального значения, которые может содержать буфер, тогда он разблокируется, визуализируется и блокируется заново, подготавливаясь таким образом принять новый набор частиц.