Использование данных Motion Capture

Использование данных Motion Capture

Получение данных Motion Capture (СКАЧАТЬ ZIP + ПРИМЕРЫ)

Наверняка по телевизору в передачах про компьютерные игры вы видели следующую картину: человек, увешанный датчиками и проводами, совершает различные движения, взмахи руками, ногами, а на экране монитора трёхмерная модель какого-нибудь мифического персонажа точь в точь повторяет за ним эти движения. Это ни что иное, как Motion Capture - процесс, в котором данные с датчиков поступают в компьютер и преобразуются в движения виртуального скелета.


skelgen.pdf: Skeletal Parameter Estimation from Optical Motion Capture Data
Adam Kirk, James F. O’Brien, David A. Forsyth, University of California, Berkeley

Полученный таким образом анимированный скелет в большинстве случаев выглядит лучше, чем иискуственно созданые аниматороми движения, и выглядят более естественно. С помощью Motion Capture можно получать не только данные о движении скелета, но и мимики человеческого лица. С помощью специальных программ данные с датчиков или видеокамер в результате обработки преобразуются в математическое описание двигательных процессов. Для наших с вами экспериментов мы можем взять уже готовые файлы с даннми Motion Capture, записанные с профессиональных актёров. Существуют различные форматы файлов, в этой статье я покажу как работать с файлами типа BVH. Такие файлы можно скачать в интернете (http://mocapdata.com, http://truebones.com и т.д.). Скачав парочку таких BVH-файлов, мы волне готовы к тому, чтобы начать нашу работу с данными Motion Capture.

Просмотр записей Motion Capture

Как известно, Motion Capture широко используется не только в играх, но и при создание фильмов с компьютерными эффектами. Программу Blender часто используют в кинематографии, мультипликации, при создании рекламных заставок, логотипов и прочих шедевров компьютерной графики. За тот год, что я пробыл в армии, Blender стал ещё круче! Поэтому и не удивительно, что "фришный" пакет 3D моделирования Blender (http://www.blender.org) позволяет загружать записи в формате BVH.

После запуска Blender нажмите клавишу DEL и потом ENTER, чтобы удалить кубик-заготовку, который ставится в центр экрана при создании новой сцены. При этом вы увидите сообщение "OK? Erase selected Object(s)". В меню FILE выбирете FILE > IMPORT > MOTION CAPTURE (BVH). Удостоверимся, что в появившемся окне для загрузки выбран режим "As armature" и нажмём кнопку OK.

После этого в основном экране просмотра 3D сцены вы увидите скелет. Чтобы проиграть Motion Capture данные, нажмите ALT+A. Если вы загрузили какую-нибудь ядреный BVH с большой длительностью, то в окошке SCENE (F10) / RENDER BUTTONS скорректируйте значения STA и END на вкладке ANIM (например, поставьте END = 1000).

Вдоволь наигравшись с данными Motion Capture вам наверняка захочется попробовать прикрепить к этому скелету модель какого-нибудь персонажа. Этот вопрос уже достаточно хорошо освещён на форумах по Blender. Поэтому вместо этого я покажу другой интересный способ - создание модели на основе скелета.

Создание модели на основе скелета Motion Capture

Для создания модели на основе скелета мы воспользуемя достаточно интересной штукой - metaballs (меташарики). Не вдаваясь в глубины математики и формул, стоящих за понятием metaballs, дам простое и понятное объяснение. Шарики metaballs ничем не отличаются от обычных сфер до тех пор, пока вы не сблизите их на "опасное" расстояние, тут то и начинается самое интересное: они начинают слипаться. И чем ближе вы их друг к другу придвините, тем ярче будет наблюдаться этот эффект. Что-то наподобие огромных капель воды в космосе. Нанизывая эти шарики на кости скелета мы получим объёмную модель.

Процесс размещения шариков metaballs вручную займёт какое-то время: надо будет размещать вдоль каждой кости по несколько шариков, чтобы они слипаясь формировали ноги, руки, туловище и голову нашего персонажа. Чтобы облегчить себе работу, воспользуемся одной из потрясающих возможностей Blender - поддержкой скриптов на языке Python. Мне не пришлось изобретать велосипед, потому что в документации Blender Python API Reference я нашёл уже готовое решение - пример маленького скрипта, который размещает шарики metaballs вдоль костей с учетом их длины bone.length и радиусов "головки" bone.headRadius и "хвостика" bone.tailRadius каждой кости. Положение самих костей определяются векторами начала bone.head['ARMATURESPACE'] и конца bone.tail['ARMATURESPACE'] в системе координат скелета (ARMATURESPACE).
После небольшой доработки был получен скрипт FREEHUMAN.PY, для установки которого достаточно скопировать его в директорию C:\Program Files\Blender Foundation\Blender\.blender\scripts\ и перезапустить Blender. После повторного запуска Blender опять удалим кубик по клавишам DEL, ENTER. Загрузим данные Motion Capture через меню Import. Теперь выберем скелет правой кнопкой мыши, после чего он будет подсвечен цветом по контуру. Наведём курсор на границу любых окон Blender и нажмём среднюю кнопку мыши, чтобы разделить это окно (split Area) на два маленьких. Сменить полосу разделения с горизонтальной на вертикальную и обратно можно повторным нажатием средней кнопки мыши. Выбрав линию разделения, завершим процесс нажатием левой кнопки мыши. После этого получится два одинаковых окна. В одном из этих окон в левом нижнем углу на вкладке типа окна нажмите SCRIPTS WINDOW. В появившемся окне выбираем SCRIPTS > WIZARDS > FREE HUMAN v2008.1.9, потом два раза жмём OK, не меняя параметры metaballs (разрешения res и толщины fat).

После этого в окне 3D View появится модель из metaballs-ов. Осмотрите её и при необходимости подкорректируйте (выберите metaballs-модель правой кнопкой мыши, войдите в режим редактирования клавишей TAB, подправьте положение metaballs-ов, и выйдете из этого режима повторным нажатием клавиши TAB).

Так как за понятием metaballs в наших масштабах лежит достаточно "тяжелая" математика, выгодно перейти в более привычное представление модели с помощью полигонов. Для этого выберите нашего человечка из metaballs-ов и нажмите ALT+C. В появившемся диалоге "Convert Metaball to" выберите "Mesh (Delete Original)". После этого в вашем распоряжении окажется уже полигональная модель. Если в полигональной модели есть треугольные дырки, то их можно залатать в режиме редактирования (по клавише TAB попадаем в Edit Mode, нажимаем кнопочку Edge Select Mode, выделяем все Edge клавишей A, затем нажимаем F и выбираем Make Face > Auto). Если наж движок не поддерживает четырёхугольники, сконвертируем модель в треугольные полигона с помощью CTRL+T в режиме "Edit Mode".

На этом закончим этап моделирования. Впоследствии вы можете её довести до ума, используя различные функции моделирования, тот же самый Sculpt Mode.

Чтобы текстурировать полученную модель, сменим тип окна со "Script Window" на "UV / Image Editor", далее в этом окне выбираем IMAGE > OPEN и загружаем текстуру.

В окне с изображением модели ("3D VIEW") выбираем режим "UV Face Select", выбираем все полигоны модели с помощью клавиши A. Затем в окне "UV / Image Editor" выбираем из списка текстуру и вновь поместив курсор мыши над окном "3D VIEW" нажимаем клавишу U и выбираем устраивающий нас метод создания текстурных координат.

Присоединение 3D модели к скелету Motion Capture

Для присоединения модели (mesh) к скелету (armature) в Blender существует удобная функция автоматического расчёта весов костей. Итак, в режиме просмотра объектов "Object Mode" выбираем сначала модель правой кнопкой мыши, затем скелет armature клавишей SHIFT + правой кнопкой мыши. Нажимаем Ctrl + P, в появившемся вопросе выбираем "Make Parent To" > ARMATURE, затем выбираем автоматическое прикрепление модели к костям "Create Vertex Groups?" > Create From Closest Bones.

Всё! Теперь наша модель полностью управляется скелетом. Вы можете немедленно проверить результат нажав ALT+A, и увидеть как будет анимироваться модель на основе положения костей скелета. Разумеется, влияние костей на различные части модели можно подправить вручную в режиме "Weight Paint", выбирая на вкладке EDITING > LINKS AND MATERIALS нужные VERTEX GROUPS, соответствующие той или иной кости. Полученная таким образом модель полностью готова к использованию во встроенном игровом движке Blender, который отлично подходит для создания простых 3D игр и интерактивных презентаций!

Экспорт модели во внешние игровые движки.

В настоящее время среди игроделов большой популярностью пользуется язык Delphi (http://www.turboexplorer.com, http://www.pascalgamedevelopment.com). Это связано с наличием сформировавшегося комьюнити разработчиков игр, использующих этот язык, всевозможных форумов и большой базы примеров с использованием DirectX и OpenGL, возможности использования современной технологии шейдеров, несомненно определяющее позитивный романтический настрой времён создания игрушек на Turbo Pascal и доступа к мощи возможностей современного железа на Delphi, чем безусловно определяется успех любых начинаний в этой области.

Выше я уже упомянул о возможности использования в редакторе Blender скриптов на языке Python. С помощью функций Blender Python API вы имеете полный доступ ко всем внутренним данным Blender, чтению и записи файлов, и можете создавать не только новые плагины, но и программы для экспорта моделей, анимации, материалов, камер, источников света, взаимосвязей и прочих данный, записывать их в свой формат. После поверхностного знакомства с Blender Python API, чтения ответов на часто возникающие вопросы по скриптингу Python на форумах по Blender-у и просмотра уже готовых Python-скриптов по экспорту моделей из Blender в форматы CAL3D, Half-Life 2 smd, X-Files и GTA я разработал свою программу NFT_EXPORT.PY на языке Python, экспортирующую данные в придуманный мною текстовый формат NFT (NashFormaT). Для экспорта модели достаточно скопировать скрипт NFT_EXPORT.PY в директорию C:\Program Files\Blender Foundation\Blender\.blender\scripts\ и вызвать его из меню FILE > EXPORT > Nash Format (.nft).

Для загрузки файлов NFT я создал программу NFT LOADER, в которой для инициализации OpenGL окна и загрузки текстур используется свежая версия удобных юнитов eXgine (http://xproger.mirgames.ru). Если вы уже знакомы с OpenGL, eXgine будет отличным образцом по написанию своих DLL в Delphi. Первым делом нам потребуется вызвать функцию loadnft('model.nft',mdl,skanim,myts):

  • 'model.nft' - путь и имя файла с моделью
  • mdl:TMeshes - массив из mesh-ей
  • skanim:TSkeletonAnimation - данные скелетной анимации
  • myts:TTextures - массив с текстурами

Так как в модель может быть составной, и состоять из нескольких mesh-ей, для всех элементов массива mdl[i] выполняются подготовительные манипуляции со скелетом и вершинами: BuildSkeleton(mdl[i].verts, mdl[i].restverts, mdl[i].vertsParents, skanim.reference, skanim.skeleton):

  • mdl[i].verts - массив вершин i-того mesh
  • mdl[i].restverts - массив вершин i-того mesh в исходном состоянии (rest position)
  • mdl[i].vertsParents - массив, описывающий связи вершин i-того mesh-а с костями
  • skanim.reference - исходное состояние костей скелета (rest position)
  • skanim.skeleton - описание иерархических взаимосвязей в скелете

В главной процедуре отрисовки Render мы каждый кадр определяем текущее время с помощью вызова ex.GetTime, и анимируем скелет вызовом Anim(skanim.actions, xstate, k, ex.GetTime - oldTime):

  • skanim.actions - массив с действиями скелетной анимации (бег, ходьба и т.п.)
  • xstate - переменная с текущим состоянием скелета
  • k - задаём номер действия, который будет взят из массива skanim.actions
  • ex.GetTime - oldTime - расчёт разницы по времени для интерполяции

Затем на основе обновлённых данных xstate мы производим генерацию матриц трансформации иерархического скелета для текущей позы скелета вызовом процедуры BuildFrame(xState.currentPose,skanim.skeleton):

  • xState.currentPose - переменная с описанием текущей позы
  • skanim.skeleton - описание иерархических связей в скелете

Полученные матрицы трансформации применяются ко всем вершинам mesh данной модели вызовом Transform(mdl[i].verts, mdl[i].restverts, mdl[i].vertsParents, xState.currentPose, skanim.reference, skanim.skeleton):

  • mdl[i].verts - массив вершин i-того mesh
  • mdl[i].restverts - массив вершин i-того mesh в исходном состоянии (rest position)
  • mdl[i].vertsParents - массив, описывающий связи вершин i-того mesh-а с костями
  • xState.currentPose - переменная с описанием текущей интерполированной позы
  • skanim.reference - исходное состояние костей скелета (rest position)
  • skanim.skeleton - описание иерархических взаимосвязей в скелете

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

Сопоставив исходники скрипта экспорта моделей из Blender и исходников на Delphi вы сможете найти некоторые взаимосвязи, облегчающие понимание иерархической взаимосвязей между матрицами трансформации в скелете: например, строчка if realBone.hasParent(): ... boneMat = boneMat * parentMat в Python-скрипте должна у вас по анологии найти отражение в исходнике на Delphi: if skel[i].parent<>-1 then pose[i].absmat:=ConcatMatrix(pose[skel[i].parent].absmat, pose[i].relmat). В обоих случаях если у i-той кости есть "родительская" кость (parent bone), то её относительная матрица умножается на абсолютную матрицу "родителя". Желаю вам успехов в освоении 3D графики! Эту статью можно свободно распространять и размещать на других сайтах.

Георгий Мошкин

e-mail: tmtlib@narod.ru