Мобильная версия сайта | |||||||||
[Главная] | [Новости] | [Статьи] |
[Проекты] |
[Ссылки] |
[Автор] |
||||
[Архив новостей] | |||||||||
[Форум] | на форуме можно задать вопрос, посмотреть ответы на часто задаваемые вопросы | ||||||||
Здравствуйте! Вы попали на информационно-образовательный сайт посвященный информатике, информационным технологиям и компьютерным играм. Подробнее о целях и задачах сайта в разделе Главная. [English version of this page here...] | |||||||||
[Базовые уроки по DirectX] [Основы DirectMusic на Delphi] [Основы DirectInput8 на Delphi] [Основы DirectSound8 на Delphi] | |||||||||
[Разработка компьютерной игры] [Пример игры Donuts3D] [Delphi DirectX] | |||||||||
Основы 3D-программирования DirectX8.1 в Delphi 6-7: теоретические и практические основы создания игр. | |||||||||
Анимация | |||||||||
10.01.2008 Эти заметки написаны с целью лучшего уяснения работы с шейдерами, а также для внедрения использования анимации в имеющийся код. |
|||||||||
[Все уроки] | |||||||||
Вывод моделей с использованием шейдеров | |||||||||
За основу взят пример SkinnedMesh. | |||||||||
Для начала нужно объявить все необходимые типы, константы и создать вспомогательные классы: | |||||||||
type METHOD = ( D3DNONINDEXED, D3DINDEXED, SOFTWARE, D3DINDEXEDVS, |
|||||||||
Данный тип будет определять метод выполнения анимации: неиндексированный, индексированный, программный, индексированный через вершинный шейдер. | |||||||||
SMeshContainer //этот класс исходя из названия будет служить контейнером меша модели | |||||||||
структуры PSRotateKeyXFile //поле определяющее трансформацию вращения в текущем кадре внутри XFile'а (кватернион) PSScaleKeyXFile //поле определяющее трансформацию масштабирования (один вектор, определяющий масштабные коэффициенты по всем трем осям) PSPositionKeyXFile //поле определяющее трансформацию смещения (вектор) PSMatrixKeyXFile //поле определяющее матрицу трансформации и аналогичные, но без суффикса XFile Исходя из имеющихся структур мы видим, что анимация может быть задана как отдельными трансформациями, так и матрицами, определяющими результирующие трансформации всех трех типов. |
|||||||||
класс SFrame //этот класс исходя из названия будет служить контейнером кадра | |||||||||
класс SDrawElement //этот класс служит для отрисовки цепочки кадров | |||||||||
В классе приложения объявляются следующие поля: CMyD3DApplication = class(CD3DApplication) {...} m_method: METHOD; //используемый метод анимации m_dwFVF: DWord; //формат вершин анимированной модели m_ArcBall: CD3DArcBall; //инструмент для манипулирования моделью - вращения, перемещения, масштабирования
m_pmcSelectedMesh: SMeshContainer; //текущий
меш
m_szPath: array [0..MAX_PATH-1] of Char; //имя
файла модели
m_dwIndexedVertexShader: array[0..3] of DWord; //вершинный
шейдер {...} |
|||||||||
В методе создания приложения создаем или обнуляем все необходимые объекты и поля. constructor
CMyD3DApplication.Create;
m_dwFVF := D3DFVF_XYZ or D3DFVF_DIFFUSE or D3DFVF_NORMAL or D3DFVF_TEX1; //это можно записать как const AnimMeshFVF = D3DFVF_XYZ or D3DFVF_DIFFUSE or D3DFVF_NORMAL or D3DFVF_TEX1; //в секции interface модуля m_dwFVF := AnimMeshFVF; //и данное вместо написанного выше m_method := D3DNONINDEXED;
m_pBoneMatrices:= nil;
m_szPath:= #0; |
|||||||||
Загружаем анимированный меш, устанавливаем параметры инструмента ArcBall function
CMyD3DApplication.InitDeviceObjects; LoadMeshHierarchy; //всё самое сложное, касающееся загрузки анимации находится внутри данной процедуры
if (m_pdeHead <> nil) then
Result:= S_OK; |
|||||||||
function CMyD3DApplication.RestoreDeviceObjects; begin //внутри этого метода происходит m_pd3dDevice.SetRenderState(D3DRS_DITHERENABLE, iTRUE); //установка необходимых состояний ... //
создание контейнера для вершинного шейдера // ... //
загрузка и ассемблирование кода шейдера из внешнего текстового файла {...} end; |
|||||||||
function CMyD3DApplication.InvalidateDeviceObjects; begin //
осуществляется освобождение зависимых от графического устройства мешей
|
|||||||||
function CMyD3DApplication.DeleteDeviceObjects; begin //
удаление из памяти FreeAndNil(m_pdeHead);
|
|||||||||
В приложении SkinnedMesh анимация может воспроизводиться 4-мя способами (см. метод function CMyD3DApplication.MsgProc): ID_OPTIONS_D3DNONINDEXED Значения данных констант обозначающих идентификаторы пунктов главного меню находятся в файле Resource.pas |
|||||||||
От метода анимации, устанавливаемого в поле m_method, будет зависеть как реализуется анимация внутри метода Render: | |||||||||
function
CMyD3DApplication.Render; //для
корневого объекта кадра (pframeRoot) у текущего элемента отрисовки (DrawElement
- pdeCur)
// Clear the viewport
if (m_pdeHead = nil) then //
Begin the scene
Result:= m_pd3dDevice.SetTransform(D3DTS_VIEW, m_mView); //полученная
трансформация устанавливается в качестве трансформации вида (View)
pdeCur:= m_pdeHead;
Result:= UpdateFrames(pdeCur.pframeRoot, mCur); //
здесь устанавливается эта матрица для всех элементов отрисовки
pdeCur:= pdeCur.pdeNext; {...}
// End the scene.
Result:= S_OK; |
|||||||||
В первом приближении всё вроде бы и не так сложно. В методе FrameMove происходит просто приращение текущего времени для всех элементов отрисовки начиная с корневого. | |||||||||
function
CMyD3DApplication.FrameMove; var pdeCur: SDrawElement; pframeCur: SFrame;
pframeCur:= pdeCur.pframeAnimHead;
pdeCur:= pdeCur.pdeNext;
Result:= S_OK; |
|||||||||
Теперь нам нужен ответ на вопросы: 1) Каким образом внедрить в код примера другие модели (например, земную поверхность)? 2) Как использовать в коде примера камеру ? 3) И наконец, как перемещать и вращать анимированную модель в 3D-сцене, например чтобы анимированный персонаж перемещался по земной поверхности ? |
|||||||||
Ответом на последний вопрос является строка кода (которая была приведена выше): D3DXMatrixIdentity(mCur); //поскольку наша анимированная модель движется на месте, то данная строка кода просто устанавливает матрицу идентичности |
|||||||||
Если взять за основу код из примера игры Donuts3D для земной поверхности с небольшими изменениями, то это можно легко воплотить. В коде данного примера это уже сделано. | |||||||||
Перейдем к главному. Т.к. в модифицированном примеме SkinnedMesh анимированная модель использует свои матрицы Мировую, Видовую и Проекционную и они используются примером глобально, то приходится для модели земной поверхности создать свои матрицы и использовать их. |
|||||||||
В код метода function CMyD3DApplication.Render; между строками : BeginScene и EndScene добавим //m_pd3dDevice.SetTransform(D3DTS_WORLD,
PlanetMeshWorldMatrix); |
|||||||||
Откомпилируем и запустим. Как видим модель земной поверхности ориентирована и перемещается вместе с анимированной моделью. Это и понятно - она использует те же матрицы, что и анимированная модель. | |||||||||
Теперь раскомментировав сначала первую строку и снова откомпилировав и запустив приложение мы увидим очевидный результат. Модель земной поверхности стала располагаться самостоятельно. Раскомментировав вторую строчку мы получим положение модели ориентированное к виду, а вот раскомментировав третью строку мы увидим, что анимированная модель исчезает из виду. | |||||||||
Что же делать? Это связано с тем, что в примере SkinnedMesh установка Проекционной матрицы происходит внутри шейдера. Другими словами трансформация вершин Проекционной матрицей производится в коде шейдера, а не строкой m_pd3dDevice.SetTransform(D3DTS_PROJECTION, ...); Как изменить код? |
|||||||||
Сначала при помощи автоматизированного поиска найдем в коде все места, где производится установка Проекционной матрицы. Нужно выполнять поиск с начала модуля (Ctrl+Home) по фразе m_pd3dDevice.SetTransform(D3DTS_PROJECTION | |||||||||
Обнаруживаем, что это имеет место только внутри function CMyD3DApplication.SetProjectionMatrix; | |||||||||
Подобным же способом ищем откуда вызывается данная функция - только внутри LoadMeshHierarchy, а данный метод вызывается только из InitDeviceObjects, т.е. только на стадии загрузки моделей. | |||||||||
Отсюда получается, что Проекционная матрица вообще не устанавливается приложением на стадии отрисовки командой m_pd3dDevice.SetTransform(D3DTS_PROJECTION, ...); | |||||||||
Это происходит по другому (см. уже упоминавшийся метод SetProjectionMatrix); | |||||||||
Итак, получается, что в качестве проекционной матрицы в приложении используется 2-ая константа АЛУ Обработки вершинного шейдера (Vertex Shader Pipeline ALU). | |||||||||
Поиск по слову SetVertexShaderConstant(2 не приведет Вас ни к чему новому. Это потому что код использования Проекционной матрицы вынесен в шейдерный! Чтобы убедиться в этом загляните внутрь любого из файлов в папке Media\Shaders из той папки куда Вы распаковали архив данного примера. |
|||||||||
Следовательно вывод модели земной поверхности должен происходить с учетом этих фактов. Возникает резонный вопрос: А как происходит вывод в случае анимированной модели? |
|||||||||
Теперь нам хочешь не хочешь, а придется "залезть" в код метода Result:= DrawFrames(pdeCur.pframeRoot, cTriangles); | |||||||||
Опорными точками для понимания сути данного метода могут служить строки m_pd3dDevice.SetStreamSource(0,
m_pVB, sizeof(CUSTOMVERTEX)); взятые из метода отрисовки Render примера Подлодка и Подводный мир. Нечто подобное надо искать и внутри метода DrawFrames Тогда станет ясно где и как это происходит. |
|||||||||
[далее - разберем подробнее код внутри DrawFrames] | |||||||||
Обновления
и новости о развитии Delphi DirectX проекта http://www.megainformaticsite.pochta.ru http://www.megainformatic.boom.ru http://www.megainformatic.narod.ru
|
|||||||||
по всем вопросам пишите на megainformatic@mail.ru или оставьте сообщение на форуме |
|||||||||
Обмен ссылками | |||||||||
|
|||||||||
(с) МЕГА ИНФОРМАТИК 2006-2009 | |||||||||
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |