Создайте свой собственный ИК в Unity

Иногда IK, встроенного в Unity, недостаточно. Я собираюсь показать вам, как создать собственный сценарий IK для Unity (или любой другой системы). Вы можете нанести этот ИК на любое сочлененное тело - пальцы, руки или ноги. IK, который я собираюсь рассмотреть, в настоящее время используется Unity, Unreal, Panda и некоторыми другими игровыми движками. Он называется ФАБРИК.

ФАБРИК - это итеративный метод. Итерационный метод - это метод, который не сразу дает решение. Итерационный метод становится все ближе и ближе к правильному решению с увеличением количества итераций метода - другими словами, итераций цикла.

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

Каждая итерация FABRIK состоит из двух основных частей.

void FABRIK() {
    while( abs(endEffectorPosition — goalPosition) > EPS ) {
        FinalToRoot(); // PartOne
        RootToFinal(); // PartTwo
    }
}

Первая часть проходит от последней конечности до корневой конечности. Для последней конечности вам нужно изменить угол / поворот конечности, чтобы она указывала на позицию цели (удерживая внутреннее положение закрепленным и позволяя внешнему положению перемещаться за счет изменения угла). Затем вы перемещаете последнюю конечность по обновленному углу к положению цели, пока внешнее положение конечности не станет равным положению цели (сохраняя угол, но позволяя внутреннему положению конечности измениться). Это почти все, что касается последней конечности, за исключением того, что теперь вам нужно обновить текущую позицию цели. Текущее положение цели теперь соответствует обновленному внутреннему положению последней конечности.

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

Повторяйте эти операции, пока не завершите эти операции на корневой конечности. После этого первая часть завершена.

/* Part One */
void FinalToRoot() {
    currentGoal = goalPosition;
    currentLimb = finalLimb;
    while (currentLimb != NULL) {
        currentLimb.rotation = RotFromTo(Vector.UP,
            currentGoal — currentLimb.inboardPosition);
        currentLimb.outboardPosition = currentGoal;
        currentGoal = currentLimb.inboardPosition;
        currentLimb = currentLimb->inboardLimb;
    }
}

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

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

Повторяйте эти операции, пока не завершите эти операции на последней конечности. После этого вторая часть завершена.

/* Part Two */
void RootToFinal() {
    currentInboardPosition = rootLimb.inboardPosition;
    currentLimb = rootLimb;
    while (currentLimb != NULL) {
        currentLimb.inboardPosition = currentInboardPosition;
        currentInboardPosition = currentLimb.outboardPosition;
        currentLimb = currentLimb->outboardLimb;
    }
}

После завершения первой и второй частей вы завершили первую итерацию метода FABRIK. Продолжайте повторять части первую и вторую, пока конечный эффектор не будет настолько близок, насколько вам нужно, к положению цели, определяемому небольшим значением EPS (эпсилон).

Вот и все! Это метод ФАБРИК. Часть первая повторяется в обратном направлении от последней конечности. Часть вторая повторяется вперед от корневой конечности. Метод FABRIK повторяет части первую и вторую, пока конечный эффектор не окажется достаточно близко к положению цели.

Если какой-либо жаргон IK не имеет смысла, просмотрите мой Обзор обратной кинематики.

Если вам понравилась эта статья, пожалуйста, , чтобы помочь другим найти ее!

Я хотел бы поблагодарить Ивана Биндоффа и Пэртель Ланг за их ценные отзывы в этой статье.