СКЕЛЕТНАЯ АНИМАЦИЯ ОПТОМ И В РОЗНИЦУ

(Играем и обсуждаем игры)

Модератор: Buxyr

Аватара пользователя
Buxyr

Коммандо Активист Друг форума Кот Леопольд
Рейнджер
Сообщения: 451
Рег. Вс май 02, 2010 10:07 am
Награды: 4
Репутация: 429
Вклад в проект: 27
Откуда: --
Поблагодарили: 6 раз

СКЕЛЕТНАЯ АНИМАЦИЯ ОПТОМ И В РОЗНИЦУ

Сообщение Buxyr » Пт июн 18, 2010 1:02 pm

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

Моделирование
Все начинается с создания персонажа. С помощью программы 3D-моделирования мы должны создать 3D-модель человека/монстра, подлежащего анимированию. Я привык к классике — 3D Studio Max (в народе 3DS). Эта классика тяжеловесная и стоит дорого, но очень удобна и люба моему сердцу еще со времен MS DOS-варианта.

Сама программа 3D Studio Max позволяет моделировать сетку персонажа, но тут возможности не заканчиваются. Далее должны быть созданы кости, к ним должны быть привязаны вершины сетки — когда двигается какая-нибудь кость, все связанные с ней вершины также должны перемещаться и изгибаться. Для создания скелета в 3DS есть модуль Character Studio, который еще недавно был отдельной программой. Сейчас он встроен непосредственно в оболочку в меню Character. Чтобы создать реалистичный персонаж, будет маловато знаний одних программных пакетов, потребуются еще и хорошие знания в области анатомии и физики, иначе никто не поверит в реалистичность движений твоего персонажа.

Мы не будем изучать процесс создания моделей и скелетов, так как подобному вопросу должна быть посвящена отдельная статья или даже книга. Если хочешь разобраться в этой теме поглубже, рекомендую прочитать книгу «3DS MAX 6 и Character Studio 4. Анимация персонажей» (авторы Ю. Кулагин и Б. Морозов;). Отличная книга! Но чтобы понять ее, ты должен иметь начальные знания по самой программе 3DS. Авторы описывают только то, что касается анимации персонажей, и опускают базовые понятия моделирования.

Зачем же я завел разговор о моделировании, если не собираюсь рассматривать его? Создание персонажа — это отдельная тема, а сейчас нам нужно понять, что должно быть создано и, соответственно, чем мы будем оперировать в собственной программе.

Скелет
Посмотрим, из чего состоит скелет. Для иллюстрации и примера я взял один из файлов книги «3DS MAX 6 и Character Studio 4. Анимация персонажей». Найди иллюстрацию в этой статье с дамочкой и стоящим рядом скелетом. Дама — это то, что слева, а скелет — справа. По крайней мере, мне так кажется :). Скелет 3D-модели похож на настоящий скелет человека и состоит из костей, связанных между собой. Если повернуть плечо, то повернется вся рука вплоть до кончиков пальцев. Когда поворачивается кисть руки, поворачиваются и все ее пальцы.

Когда вращаются кости, движутся и связанные точки сетки персонажа. Одна точка сетки может быть привязана к нескольким костям. В этом случае точка сетки смещается в соответствии с указанным весом точки. Если вершина принадлежит двум костям, то по умолчанию вес в обоих случаях будет равен 0,5, то есть при вращении кости на 90 градусов вершина изменит положение только на 45. Так происходит только по умолчанию. В идеале мы можем настраивать вес самостоятельно (если позволит используемая программа 3D-графики). Если вершина принадлежит одной кости, то вес ее преобразований равен 1, то есть она будет трансформироваться на все 100%.

Сейчас отвлекусь от темы, чтобы дать один совет: никогда не двигай кости для создания анимации. Все кости только вращаются относительно начала (точки крепления с главной или вышестоящей по иерархии костью). Например, кость руки крепится к плечу, и чтобы рука была протянута вперед, происходит вращение кости относительно плеча. Притом все остальные части руки (локоть, кисть, пальцы) движутся автоматически — перемещать их не нужно, если только ты не хочешь, чтобы от туловища оторвалась рука или нога.

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

Экспорт сетки
Создав сетку персонажа, скелет и анимацию, сохрани их для последующей загрузки из собственной программы. Вот тут возникает проблема выбора: как сохранить? Формат .max позволяет хранить всю необходимую информацию, но он слишком сложный для использования в собственных программах. Можно использовать старый формат .3ds, но возникает проблема с сохранением информации о костях и самой скелетной анимации. Эти проблемы можно обойти, но тогда натолкнешься на большие проблемы. Придется писать собственные анализаторы и загрузки данных, потом создавать классы или функции для поддержки всего загруженного в программе.

Зачем выдумывать проблемы себе, когда можно использовать формат файла .x, который разработан Microsoft и является открытым, универсальным и легко расширяемым? Расширять возможности файла нам не понадобится, потому что все необходимое для хранения сетки и костей в нем уже есть. Сама 3DS Max не может сохранять или экспортировать данные в формат .x, но можно установить специальный plug-in, разработанный самой Microsoft. Этот plug-in можно найти в составе DX SDK, причем в исходных кодах. Нам остается только скомпилировать его и установить.

.x-файл обладает еще одним преимуществом: в составе функций Direct3D есть возможность загрузить сетку с помощью одной-единственной функции и с помощью еще одной — скелет. Корпорация Microsoft здесь очень хорошо позаботилась о нас как о программистах. Однако о функциях читай чуть позже. Сейчас еще немного мысленно пощупаем скелет.

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

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

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

Загрузка скелета
Для загрузки информации из .x-файла с учетом скелета можно воспользоваться функцией D3DXLoadMeshHierarchyFromX, в общем виде она выглядит следующим образом:

HRESULT D3DXLoadMeshHierarchyFromX(
LPCTSTR Filename,
DWORD MeshOptions,
LPDIRECT3DDEVICE9 pDevice,
LPD3DXALLOCATEHIERARCHY pAlloc,
LPD3DXLOADUSERDATA pUserDataLoader,
LPD3DXFRAME* ppFrameHeirarchy,
LPD3DXANIMATIONCONTROLLER* ppAnimController
);

Коротко пробежимся по параметрам этой функции.
Filename — что-то подсказывает мне, что это путь к загружаемому .x-файлу.
MeshOptions — опции загрузки. Чаще всего будет нужна опция D3DXMESH_MANAGED, чтобы можно было управлять загруженными данными вершинного буфера. Если управление не нужно, то можно вообще ничего не указывать, чтобы использовать значения по умолчанию.
pDevice — указатель на интерфейс IDirect3DDevice. Я думаю, тут не нужно пояснять, что это такое и зачем.
pAlloc — указатель на интерфейс ID3DXAllocateHierarchy, методы которого вызываются во время загрузки информации из .x-файла. Этот интерфейс необходим для выделения и освобождения фреймов и контейнера объектов.
pUserDataLoader — указатель на интерфейс ID3DXLoadUserData для загрузки пользовательских данных. Да, .x-файл является свободным, гибким, расширяемым, и поэтому может содержать и пользовательские данные, которые можно обработать указав здесь собственный экземпляр интерфейса ID3DXLoadUserData.
ppFrameHeirarchy — возвращает указатель на иерархию загруженных фреймов в виде массива структур D3DXFRAME.
ppAnimController — указатель на интерфейс ID3DXAnimationController, в котором будет размещена информация об анимации. Да, в .x-файле может оказаться еще и заранее подготовленная анимация. Следовать ей необязательно, но иногда очень удобно.
Фреймы
Остановимся подробнее на шестом параметре функции D3DXLoadMeshHierarchyFromX, где мы получаем указатель на массив структур D3DXFRAME. Что это такое? На самом деле каждая такая структура описывает определенную кость в скелете и выглядит следующим образом:

typedef struct _D3DXFRAME {
LPTSTR Name;
D3DXMATRIX TransformationMatrix;
LPD3DXMESHCONTAINER pMeshContainer;
struct _D3DXFRAME *pFrameSibling;
struct _D3DXFRAME *pFrameFirstChild;
} D3DXFRAME, *LPD3DXFRAME;

Что же есть в этой структуре и для чего «оно» предназначено?
Name — имя элемента скелета. Каждая кость может иметь свое имя, по которому достаточно удобно находить именно ту часть скелета, которая требует вращения. Зная имя кости, ты легко найдешь ее структуру, перебрав весь скелет. Указатели на наиболее часто используемые структуры лучше сохранить в глобальных переменных, и тогда ты не будешь каждый раз перебирать весь скелет, следовательно, повысишь скорость анимации.
TransformationMatrix — матрица преобразований. Определяет положение данной косточки.
pMeshContainer — указатель на контейнер сетки.
pFrameSibling — указатель на следующую структуру D3DXFRAME, которая находится на том же уровне. Например, все пальцы находятся на одном и том же уровне иерархии скелета, через этот указатель можно последовательно получать доступ к ним.
pFrameFirstChild — указатель на первую кость, которая находится на уровень ниже.
В движении
Перейдем к алгоритму, который позволит двигать скелет. Во время движения чаще всего необходимо изменять матрицу преобразования не только определенной косточки (например предплечья), но и всех дочерних (всех костей, которые входят в состав руки). Приходится создавать функцию, которая должна выполнять два действия: для начала должно изменяться положение текущей кости, а после этого — вызываться рекурсивная функция. Внутри рекурсивной функции нужно изменять матрицы положения не только дочерних, но и родственных (находящихся на одном уровне) костей. Например, когда движется кисть, должны двигаться и все ее пять пальцев, которые находятся на одном уровне, а не один, первый попавшийся палец. Функция может выглядеть примерно так:

Рекурсивная функция
void Преобразовать(D3DXFRAME фрейм, D3DXMATRIX матрица)
{
// Перемножаем матрицы, т.е. объединяем
фрейм.TransformationMatrix *= матрица;

// Если есть родственники, то преобразовать их фреймы
if (фрейм.pFrameSibling)
Преобразовать(фрейм.pFrameSibling, матрица);

// Если есть дочерние, то преобразовать их фреймы
if (фрейм.pFrameFirstChild)
Преобразовать(фрейм. pFrameFirstChild, фрейм.TransformationMatrix);
}

Внимание! Во время вызова функции обновления родственных костей в качестве второго параметра передается неизмененная матрица, а при обновлении дочерних костей — уже трансформированный вариант.

Советы
Реальный пример не рассматриваю, потому что экономлю место в журнале и боюсь сделать его слишком толстым. Если заинтересовался скелетной анимацией, обратись к примерам DX SDK, там посмотри примеры анимации для C++ и C# (с использованием Managed DirectX). Вся необходимая база для понимания исходного кода примера у тебя уже есть.

И напоследок — несколько советов по использованию скелетной анимации.
После модификации матрицы преобразований одной кости у тебя могут возникнуть проблемы с получением первоначального состояния — мой первый совет призван избавить тебя от этой напасти. Дело в том, что в любом случае существует какая-то погрешность, которая со временем начинает влиять на качество сцены. Например, ноги человека неожиданно откажутся выравниваться в тот момент, когда ты попытаешься остановить своего персонажа и поставить его ровно. Ликвидировать такой эффект легко: можно создать свой вариант структуры D3DXFRAME, то есть расширить ее, добавив еще одно поле типа D3DXMATRIX. В этом поле можно хранить матрицу преобразований кости на этапе начала анимации и при каждом отображении сцены плясать именно от этого значения.

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

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

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

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

Напоследок загляни на сайт — увидишь танцующего мальчика в памперсе. Это рисованная анимация, но есть и демка, в которой тот же самый мальчик танцует под хорошую музыку, причем персонаж реализован в виде сетки mesh и скелета. Я видел эту демку примерно три года назад, а сейчас что-то не смог найти. Жаль, хотелось выложить ее для тебя на диск

ОТЛИЧНЫЙ ПРИМЕР СКЕЛЕТНОЙ АНИМАЦИИ ИДЕТ В СОСТАВЕ DX SDK, И ЕГО МОЖНО УВИДЕТЬ В ДИРЕКТОРИИ DXSDK\SAMPLES\C++\DIRECT3D\MESHES\SKINNEDMESH

СКЕЛЕТНАЯ АНИМАЦИЯ ХОРОШО ПОДХОДИТ ДЛЯ СОЗДАНИЯ ДВИЖЕНИЯ ПЕРСОНАЖЕЙ В ИГРАХ. ЗАГРУЖАЯ ГОТОВЫЕ ТРАЕКТОРИИ ДВИЖЕНИЯ И ВРАЩЕНИЯ КОСТЕЙ ИЗ .X-ФАЙЛА, МЫ ЭКОНОМИМ ПРОЦЕССОРНЫЕ РЕСУРСЫ

НЕ ПЫТАЙСЯ СДЕЛАТЬ МИМИКУ С ПОМОЩЬЮ СКЕЛЕТА. ТУТ НЕОБХОДИМО ДВИЖЕНИЕ ГУБ, А В НИХ НЕТ КОСТЕЙ. НЕ НАДО СОВАТЬ КОСТИ В ГУБЫ! ЗДЕСЬ НЕОБХОДИМ УЖЕ СОВСЕМ ДРУГОЙ МЕТОД — НО ЭТО «УЖЕ СОВСЕМ ДРУГАЯ» ИСТОРИЯ

НЕ ЗАБУДЬ, ЧТО НА ЛЮДЯХ ДОЛЖНЫ РАСТИ ВОЛОСЫ, А ВОЛОСЫ ОБЯЗАНЫ КАК-ТО ШЕВЕЛИТЬСЯ. ЧТОБЫ УПРОСТИТЬ СЕБЕ ЖИЗНЬ, НАСЕЛИ СВОЙ МИР ТОЛЬКО КОТОВСКИМИ И ГОШАМИ КУЦЕНКО. ИЛИ НАДЕНЬ НА ВСЕХ КАСКИ


ФЛЕНОВ МИХАИЛ AKA HORRIFIC


Искусство это жизнь

Вернуться в «Игромир»

 

 

cron