12 марта 2016 г.

Заметка о 3D-анимации в вебе с использованием three.js

Короткий журнал о том, что я узнал в процессе изучения three.js и попытки запустить в нём анимацию мешей. Версия three.js r74 и r74-dev, версия blender 2.76

 Почти все загрузчики разных форматов находятся в examples/js/loaders. Встроенным является ObjectLoader (загрузчик одного меша) и JSONLoader (загрузчик сцены).

Формат OBJ для мешей сопровождается файлом с расширением mtl, в котором описаны параметры материалов. Хорошая статья о параметрах материалов в mtl есть на англоязычной вике о obj.

Three.js не парсит значения всех текстурных карт из mtl: apha-map, displacement-map и ещё какие-то. Зато легко можно дописать это в 5 строк на каждый материал в исходники MTLLoader.

Формат OBJ не поддерживает анимацию




FBXLoader не умеет парсить анимацию из fbx.

Анимация полностью переписана в последнем релизе three.js. Очень мало гайдов о новом THREE.AnimationMixer и AnimationClip. Один из хороших гайдов по ссылке.

Обязательно нужно пройтись по всем материалам меша и указать им, что они skinning. Если материал один, то просто ему указать material.skinning = true.

Skinned-анимация передаётся в json-формате и хранится в geometry.animations. Чтобы запустить такую анимацию, нужно создать экземпляр проигрывателя THREE.AnimationMixer, аргументом ему указать меш, который будет анимироваться. После загрузки json создать экземепляр AnimationClip вызвав метод у миксера:
var action = mixer.clipAction( object.children[0].geometry.animations[ 0 ] );
action.play();
А перед рендером на каждом фрейме вызывать mixer.update( delta ). Об этом в гайде по ссылке выше.

Плагин json-экспорта из блендера имеет много галочек. Но одна из них влияет на то, как загружать этот json в three.js. Галочка "Scene", если её поставить, то в json будет сцена, которую нужно загружать через JsonLoader. Если её не ставить, то будет выгружен только меш, который нужно загружать через ObjectLoader. В последнем случае, чтобы выгрузить и материалы, нужно ставить галочку Face Materials. Ах да, если выгружаете один меш, то его геометрию и нужно выделить в списке объектов в blender перед экспортом.

Если нужно выгрузить анимацию, то обязательно нужны галочки Bones, Skinning, Embed Animation.

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

Если вы в blender импортировали меш из другого формата, скорее всего, ваш меш будет в Rest-позе. Первый экспорт пройдет нормально, но после него поза будет сброшена на другую. При попытке экспортировать из такой позы, в three.js будет каша, а не анимация. Сбросить позу можно выделив Pose в дереве объектов (или перейти в режим Pose у меша), выделить все кости одним нажатием на "A", нажать пробел и в поиске ввести и выбрать "Clear Pose Transforms" (можно и вручную сбрасывать через alt+G, alt+R, alt+S).

Рекомендую экспериментировать с выгрузкой загрузкой на проверенном маленьком меше, например, monster.dae из папки с примерами three.js.

Если вам кажется, что все проблемы в three.js, то я пробовал babylon.js. Экспортировал в его формат из blender меш с анимацией, запустил и получил ту же кашу из анимаций.

Если вам кажется, что все проблемы в blender, то я пробовал выгрузку из 3DS MAX 2016, анимация выгружалась больше 10 минут из того же простого импортированного меша, а потом ругнулась на то, что что-то не так с мешем. Гугл помог, оказалось, там нужно много всего настроить, например, добавить Edit Mesh модификатор конкретно для моей ошибки. Это я сделал. Анимация выгружалась полтора часа, а потом ругнулась на какой-то не такой индекс в каком-то массиве uv. Эксперименты довольно увлекательные, но слишком длительные.

Анимация из blender более-менее удачно экспортируется, если вы экспортируете без галочки Scene, то есть один меш (при этом из правильной позы). Если вы экспортируете несколько мешей (поставили галочку Scene), то, скорее всего, получите кашу из анимаций. Этот вопрос я пока изучаю. Выяснил, что всё дело в координатах выгружаемых вершин геометрии. Они значительно больше в числах, когда выгружается сцена. Я пробовал в json-файле скопировать просто вершины одично выгруженного меша в координаты вершин выгруженной сцены, и анимация заработала как надо.

Вместо выгрузки всей сцены, можно объединить меши в один и выгрузить как один. В этом случае всё работает. Перед объединением мешей в blender нужно переименовать у каждого меша UV-развертку в одно и то же имя, тогда она после слияния тоже будет объединена, а выгруженный меш будет с мультиматериалом. После переименования UV-развёрток, нужно выделить все меши и нажать CTRL+J. Теперь можно выделить один получившийся меш и выгружать его без галочки "Scene". Файл будет гораздо меньше, чем при выгрузке всей сцены, потому что анимация не дублируется для каждой геометрии.
Немного про тени. Чтобы включить динамические тени, нужно прописать
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap; //есть и другие варианты
Источникам света, например, DirectionalLight указать castShadows = true, мешу recieveShadows = true. Но, как выяснилось, даже один источник света с тенью спускает производительность на мобильном Note 4 с 58 fps до 25 fps. Поэтому с этим нужно быть осторожным.


Комментариев нет:

Отправить комментарий