Качественный риггинг руки
Меня зовут Загоруйко Александр. Мне 22 года, я работаю в области 3D-графики в течение 5 лет. Сейчас я главный сетапер и разработчик скриптов в студии Artoon в Воронеже. Студия занимается созданием полнометражных мультфильмов. Риггингом я занимаюсь на протяжении последних 3-ех лет и нахожу это занятие весьма интересным и увлекательным. Собственно в этой области я и хочу расти дальше.
Помимо сетапа мне очень нравится программирование в разном виде:
C/C++,Assembler, Pascal, Python, веб-программирование (php, html, js, css ),Java ME, Lisp.
Другие мои интересы: путешествия,психология и музыка ( играю на скрипке, флейте и на гитаре )
В данный момент работаю над собственным проектом: скриптовой риг человекоподобных персонажей. Проект практически доделан и я скоро буду заниматься его распространением :-)
Основное внимание в этом уроке будет уделено таким темам:
1. Три режима работы руки: IK,FK, ElbowFix
2. Стретч для каждого режима
3. Общее скалирование всей системы
4. Безшвовое переключение режимов IK-FK
Итак, в данном уроке нам предстоит разобрать в деталях как именно делается грамотный риг руки (плеча, предплечья) с необходимым набором возможностей и функций.
Урок рассчитан на достаточно опытных пользователей системы Autodesk Maya , впрочем, все детально объяснено и практически любой начинающий пользователь сможет повторить описанную ниже процедуру.
Начнем по-порядку. Делаем систему из трех костей в начале координат по оси X ( для наглядности мы пока опустим кисть )
Так как кости мы построили строго по оси X, для корректной работы IK-решателя нам необходимо задать "угол-предпочтения" (Preferred angle).
Повернем joint2 по оси Y на -45 градусов и жмем Ctrl+правая-кнопка-мыши. В меню выбираем "Set preferred angle"
Угол предпочтения необходим для того, чтобы IK-решатель знал как именно поворачивать цепочку костей при позиционировании IK-Handle.
Далее, на данном этапе нам нужно сделать 3 копии получившейся системы.
Первая - для FK, вторая - для IK, третья - для ElbowFix ( фиксация локтя )
Выделяем joint1 и жмем Ctrl+d 3 раза. Итого у нас получается 4 системы костей.
А теперь самое время навести марафет. По секрету вам скажу, что порядок в сцене - это 80% хорошего, грамотно-сделанного
сетапа. Такой риг легко отлаживать, заскриптовывать и поддерживать на протяжении всего проекта.
Первую систему ( joint1 ) именуем соответственно arm1_joint, arm2_joint, arm3_joint
Вторую ( joint4 ): armFK1_joint, armFK2_joint, armFK3_joint
Третью ( joint 5 ): armIK1_joint, armIK2_joint, armIK3_joint
Четвертую ( joint 6 ): armElbowFix1_joint, armElbowFix2_joint, armElbowFix3_joint
Создадим несколько "папочек" для организации сетапа в сцене.
Делаем главную родительскую группу arm и помещаем в нее еще одну группу skeleton ( в этой группе мы будем хранить все что связано
с цепочками костей и кинематиками ). Перемещаем в группу skeleton созданные цепочки костей (arm1_joint, armFK1_joint, armIK1_joint, armElbowFix1_joint)
Итого у нас должно получится вот что:
Наведем еще кое-какой порядок в сцене: скроем все атрибуты для группы arm. Запомните, всегда оставляйте в сцене только то, что реально нужно аниматору.
В противном случае вам придется расплачиваться "глюками" под конец проекта :-)
Следующий наш шаг - это настроить каждую систему в отдельности. Начнем с FK.
Давайте скроем все, кроме системы armFK1_joint. Сделаем элементы управления системой FK в виде NURBS-кружков.
Убедитесь, что кружки сориентированы верно, имеют 0 в каналах translate и rotate и 1 в канале scale, а также в истории отсутствует какая-либо информация.
Называем данные контролы соответственно armFK1, armFK2
Помещаем armFK2 в armFK1, тем самым создав FK иерархию для элементов управления.
Далее, соединим контролы и кости: выделяем armFK1_joint и armFK1 и выбираем Constrain->Point->[] ( опции ). Жмем Edit->Reset, а затем Apply.
Также назначим pointConstraint для armFK2_joint и armFK2. В результате у нас каждая кость системы FK управляет по translate соответствующим
элементом управления.
Теперь свяжем вращение контролов и костей. Для этого выделяем последовательно armFK1, armFK1_joint и
выбираем Constrain->Orient->[] ( опции ). Жмем Edit->Reset, ставим галочку Maintain offset, щелкаем Apply.
Аналогично для armFK2, armFK2_joint.
На данном этапе мы имеем кости, управляющие элементами управления по translate, и элементы управления, управляющие костями по rotate.
Наводим порядок: помещаем созданные элементы управления в новую "папку" armFK_controls, которая в свою очередь находится в новой группе arm|controls.
Скрываем лишние атрибуты: у armFK1 оставляем только каналы rotate, у armFK2 только rotateY.
Базовую настройку системы FK мы завершили.
Приступим к IK-системе.
Как и в предыдущий раз скройте все лишнее, оставив только цепочку костей armIK1_joint.
Назначим IK-решатель системе, выбрав Skeleton -> IK Handle Tool -> []. Нажмите Reset. В списке Current solver у вас должно стоять ikRPsolver.
Щелкаем по первой кости (armIK1_joint) в окне проекции,а затем по последней (armIK3_joint).
В результате будет создан решатель инверсной кинематики для цепочек armIK1_joint,armIK2_joint и armIK3_joint.
Назовите полученный ikHandle1 как armIK_IKHandle и поместите его в группу arm|skeleton.
Давайте создадим элементы управления для IK-системы. Сделаем их также из NURBS-кривых.
Создайте кружок и поместите его в район последнего сустава (armIK3_joint). Назовите данный объект armIK.
Для локтя мы сделаем элемент из трех шейпов. Создайте круг в начале координат,а затем еще два. Поверните их соответствующим образом
чтобы получалась сферка.
Далее выделите полученные кривые и запустите следующий скрипт:
{ // Данный скрипт переносит шейпы выделенных объектов под трансформ последнего выделенного объекта
string $ls[] = `ls -sl`; // получить список выделенных объектов
if (`size $ls`<2) error "Select at least 2 object to make parenting"; // сообщить об ошибке, если их меньше двух
$parent = $ls[`size $ls`-1]; // последний выделенный объект - родитель для остальных
for ($obj in $ls){ // пройтись по объектам
if ($obj == $parent) continue; // пропустить, если текущий объект - родитель
parent $obj $parent; // иначе припарентить к родителю текущий объект
string $objShape[] = `listRelatives -f -s $obj`; // получить список шейпов для текущего объекта
makeIdentity -apply true -t 1 -r 1 -s 1 -n 0 $obj; // зафризить их
for ($s in $objShape) parent -r -shape $s $parent; // и припарентить к новому трансформу
delete $obj; // удалить трансформ
}
select -r $parent;
}
После выполнения скрипта у вас останется всего один объект, но с тремя шейпами.
Переименуйте полученный объект в elbowIK.
Поместите эти два объекта в группу arm|controls|armIK_controls.
Скройте лишние атрибуты: у elbowIK оставьте только translate, а у armIK оставьте translate и rotate (для ориентации кисти).
Не назначая констрейнтов, перейдем к системе "фиксированного локтя".
Оставьте в сцене только armElbowFix1_joint. Назначьте инверсную кинематику с решателем ikSCsolver от кости armElbowFix1_joint к кости
armElbowFix2_joint. (Skeleton -> IK Handle Tool ->[], Current Solver = ikSCsolver )
И еще один такой же IK-решатель назначьте от кости armElbowFix2_joint к кости armElbowFix3_joint.
Переименуйте IKHandle1 и IKHandle2 соответственно в armElbowFix1_IKHandle и armElbowFix2_IKHandle и поместите эти объекты в arm|skeleton.
Теперь пришла пора связей.
Выделите elbowIK и armElbowFix1_IKHandle и выберите Constrain->Point ( с базовыми параметрами ).
По аналогии armIK и armElbowFix2_IKHandle.
Свяжите локоть и "хэндл руки" полевекторным ограничителем, выделив elbowIK и armIK_IKHandle и выбрав Constrain->Pole Vector.
Выделите последнюю кость системы ElbowFix ( armElbowFix3_joint ) и armIK, затем выделив armIK_IKHandle и выбрав Constrain->Point ( с базовыми параметрами ).
В последнем случае у нас два объекта управляют одним по translate. Рассчет итоговой трансформации определяется по весам в атрибутах pointConstraint ноды.
Давайте добавим атрибут, управляющий режимом "фиксированный локоть".
Запустите следующую команду:
// добавить атрибут "elbowFix" типа double, изменяющийся в пределах от 0 до 1 с начальным значением 0
addAttr -ln "elbowFix" -at double -min 0 -max 1 -dv 0 -k true armIK;
Если значение этого атрибута равно 0,то используется обычный режим IK, если же 1 - используется ElbowFix.
Сделаем это через управляемые ключи ( Driven keys ). Найдите ноду (узел) armIK_IKHandle_pointConstraint1 ( с двумя управляющими объектами ).
Откройте окошко Set Driven Key, нажав Animate -> Set Driven Key -> Set ...
C помощью Load Driver, Load Driven добейтесь соответствия с изображением. Нам необходимо, чтобы атрибут elbowFix управлял двумя атрибутами у pointConstraint.
Нажимаем Key при таких значениях атрибутов: elbowFix = 0, armElbowFix3_jointW0 = 0, armIKW1 = 1
И еще раз при таких: elbowFix = 1, armElbowFix3_jointW0 = 1, armIKW1 = 0
Настройка режима IK завершена.
Далее у нас по плану связать системы FK и IK с основной, но прежде нужно сделать еще один элемент управления.
Данный контрол нужен для управления рукой в целом (скажем, для переключения кинематик, для принудительного скалирования отдельных суставов и т.д.).
Сделаем флажок, назовем его arm_control и поместим его в arm|controls
Добавим атрибут для переключения кинематики и скроем все лишнее:
// добавить атрибут
addAttr -ln "kinematic" -at double -min 0 -max 1 -dv 0 -k true arm_control;
Свяжем системы IK и FK, запустив скрипт:
{
int $num = 2; // число костей
string $basic = "arm"; // название базовой системы
string $basicFK = "armFK"; // FK система
string $basicIK = "armIK"; // IK система
string $end = "_joint"; // оставшаяся часть в названии без цифр
$attr = "arm_control.kinematic"; // атрибут, управляющий переключением кинематики
for ($i=1;$i<=$num;$i++){
select -r ($basicFK+$i+$end); // сперва выделяем FK кость
select -add ($basicIK+$i+$end); // потом IK
select -add ($basic+$i+$end); // а затем основную
string $oc[] = `orientConstraint`; // в итоге у нас две кости будут влиять на последнюю по rotate
setAttr ($oc[0]+".interpType") 2; // shortness
setDrivenKeyframe -at ($basicFK+$i+$end+"W0") -v 1 -dv 0 -cd $attr $oc[0]; // установка управляющих ключей
setDrivenKeyframe -at ($basicFK+$i+$end+"W0") -v 0 -dv 1 -cd $attr $oc[0];
setDrivenKeyframe -at ($basicIK+$i+$end+"W1") -v 0 -dv 0 -cd $attr $oc[0];
setDrivenKeyframe -at ($basicIK+$i+$end+"W1") -v 1 -dv 1 -cd $attr $oc[0]; }
}
После выполнения скрипта атрибут arm_control.kinematic управляет какая именно система влияет на основные кости (0 - FK, 1 - IK)
Скрипт аналогичен серии команд Set Driven Keys с нужными параметрами.
Скройте все, оставив только систему arm1_joint и arm_control.
Теперь свяжем видимость нужных элементов управления, чтобы,скажем, в режиме FK аниматору были доступны только элементы FK.
Выделите armFK_controls и arm1_joint_orientConstraint1 ( нод, который появился после выполнения скрипта выше ) и
откройте Connection Editor (Windows->General->Connection Editor)
Соедините arm1_joint_orientConstraint1.armFK1_jointW0 с armFK_controls.visibility.
Аналогично соедините arm1_joint_orientConstraint1.armIK1_jointW1 с armIK_controls.visibility.
Теперь у нас отображаются только те элементы, которые нужны для данной кинематики.
Все готово, но у нас еще нет кисти. Давайте создадим ее.
Постройте 3 системы как показано на рисунке, переименуйте их соответствующим образом и поместите в arm|skeleton.
Система handFK1_joint предназначения для управления кистью в режиме FK, а handIK1_joint - для IK.
Выделите armFK3_joint и handFK1_joint и выберите Constrain->Point ( с базовыми параметрами ).
Аналогично armIK3_joint и handIK1_joint и arm3_joint и hand1_joint.
Для режима FK нам понадобиться создать контрол на кисть ( NURBS-круг ). Назовите этот элемент armFK3 и припарентите к armFK2
Сделайте, чтобы кость armFK3_joint управляла armFK3 по translate ( через pointConstraint )
Выделите armFK3 и handFK1_joint и выберите Constrain->Orient ( с Maintain offset ).
Аналогично armIK и handIK1_joint.
У armFK3 оставьте только каналы rotate.
Далее с помощью вышенаписанного скрипта настройте переключение IK-FK для кисти.
{ // Этот код настраивает работу кисти для разных кинематик
int $num = 1; // число костей
string $basic = "hand"; // название базовой системы
string $basicFK = "handFK"; // FK система
string $basicIK = "handIK"; // IK система
string $end = "_joint"; // оставшаяся часть в названии без цифр
$attr = "arm_control.kinematic"; // атрибут, управляющий переключением кинематики
for ($i=1;$i<=$num;$i++){
select -r ($basicFK+$i+$end);
select -add ($basicIK+$i+$end);
select -add ($basic+$i+$end);
string $oc[] = `orientConstraint`;
setAttr ($oc[0]+".interpType") 2; // shortness
setDrivenKeyframe -at ($basicFK+$i+$end+"W0") -v 1 -dv 0 -cd $attr $oc[0];
setDrivenKeyframe -at ($basicFK+$i+$end+"W0") -v 0 -dv 1 -cd $attr $oc[0];
setDrivenKeyframe -at ($basicIK+$i+$end+"W1") -v 0 -dv 0 -cd $attr $oc[0];
setDrivenKeyframe -at ($basicIK+$i+$end+"W1") -v 1 -dv 1 -cd $attr $oc[0];
}
}