BI-PGA Programování grafických aplikací
Jdi na navigaci předmětu

VR Avatar s využitím inverzní kinematiky

Úvod

V tomto tutoriálu společně vytvoříme ve virtuální realitě avatara, který bude realisticky reprezentovat hráčovo tělo v prostoru za pomoci inverzní kinematiky a procedurálně generovaných animací. Ve výsledku bude možné tento VR Avatar napojit na jakýkoliv VR Rig. Pro demonstraci funkčnosti nakonci využijeme VR brýle HTC Vive.

Prerekvizity

  • Unity Engine 3D verze 2019.4
  • Animation Rigging balíček - Stáhne se skrze Unity přes Window → Package Manager → Animation Rigging. Pokud ho v seznamu balíčků nemůžete najít, klikněte na Advanced → Show preview packages. Balíček nainstalujte kliknutím na tlačítko Install.
  • 3D model lidské postavy. Je celkem jedno jaký tento model je, je však důležité, aby byl takzvaně Rigged (aby se hierarchie modelu skladala z jednotlivých kostí). Pro účely tohoto tutoriálu použijeme volně dostupný model mužské postavy. Ke stažení ZDE (stahujeme .fbx soubor).

Připravení scény

Do scény umístíme plochu, aby měl náš avatar po čem chodit. Pravým tlačítkem klikneme do hierarchie, a zvolíme 3D Object → Plane. Následně naimportujeme stažený model mužské postavy do scény. Rozbalenou složku, kterou jsme předtím stáhli stačí přetáhnout to kořenové projektové složky, tím se model automaticky naimportuje. Po přetažení objektu do scény vidíme samotný bílý Mesh bez textur. Defaultně vytvořený materiál totiž správně nenamapoval textury, které jsme společně s modelem stáhli.

1

Bude nutné vytvořit nový materiál a aplikovat ho na model. Vytvoříme v projektové hierarchii novou složku Materials a pravým tlačítkem vytvořím nový materiál. V inspektorovém okně se nám otevře jeho detail. Do prázdných míst pro textury postupně přetahneme Albedo a Normal texturu ze složky tex, která je ve složce s modelem. Vytvořený materiál pak stačí opět přetáhnout na model ve scéně a lidská postava najednou vypadá reálně.

2

Momentálně máme připravený model a můžeme se vrhnout na implementaci inverzní kinematiky. Inverzní kinematiku použijeme kvůli tomu, že zápěstí modelu bude namapované k VR ovladači a bude kopírovat jeho polohu. Vzhledem k tomu, že zápěstí je součástí celé ruky, musí jeho pohyb nějak ovlivnit celý pohyb paže. To samé se týká pohybu hlavy a celého těla. Pro zobrazení jednotlivých kostí modelu podržíme tlačícko Alt a rozbalíme šipkou importovaný model v hierarchii.

Vrchní část těla - inverzní kinematika

Začneme tím, že na kořenový objekt připneme skript Bone Renderer. Tato komponenta nám lépe vizualizuje jednotlivé kosti v modelu. Označme všechny kosti modelu a přetáhnutím myši je umístíme do proměnné Transforms v Bone Renderer skriptu. Jak lze vidět na obrázku, jednotlivé kosti jsou zvýrazněné modrou barvou.

3

Pro vlastní implementaci inverzní kinematiky prvně přidáme další skript na kořenový objekt a to Rig Builder. Dále vytvoříme prázdný objekt s názvem VR Constraints, který bude synem našeho modelu. Pomocí inspektoru tomuto objektu připneme skript Rig a přetáhneme vytvořený prázdný objekt do volného okénka Rig Layers ve skriptu Rig Builder v kořenovém objektu. Toto propojí omezující body s kostrou hlavního modelu.

4

Nyní vytvoříme jednotlivé omezení pro IK. Začneme pravou rukou. Vytvořme opět prázdný objekt, který bude synem VR Constraints a připneme na něj skript Two Bone IK Constraint. Je možné si všimnout, že skript vyžaduje 5 proměných. První tři jsou reference na kosti v paži. Namapujeme následovně: upperarm_r → Root, lowerarm_r → Mid a hand_r → Tip (stačí opět přetahnout myší objekty z hierarchie modelu to volných okýnek). Po úspešném namapování zbývají prázdná dvě políčka. Vytvořme další dva prázdné objekty jako syny, s tím že je nazveme Target a Hint a obdobně přetáhneme myší do Two Bone IK Constraint skriptu. Teď už jenom stačí tyto nově vytvořené objekty korektně umístit. Využijme nástroj Animator Rigging pro jednoduché umístění. Označme v hierarchii objekt Target, podržme tlačícko Shift na klávesnici a myší klikněme na zápěstní kost vlevo ve scéně. Nadíle nahoře v menu klikněme na Animation Rigging → Align Transform a pozice objektu Target se perfektně srovná s danou kostí. To stejné uděláme i s objektem Hint, tenktokrát však jako kost vybereme loket. Hint totiž slouží jako pomocný bod, kam se ruka může při pohybu posouvat. Po srovnání pozice s kostí tedy posuenme Hint trochu dozadu. Pro kontrolu, vše by mělo vypadat stejně jako na obrázku níže.

5
6

Funkcionalitu námi odvedené práce hned můžeme vyzkoušet spuštěním aplikace a posunem objektu Target směrem k tělu za běhu aplikace v editoru. Paže se automaticky a přirozeně prohne dozadu a prakticky tak pohyb zápěstí ovlivňuje pohyb celé ruky.

7

Jediné co zbývá dodělat je to samé pro levou ruku. Postup je úplně stejný, akorát pro Two Bone IK Constraint skript použijeme kosti z druhé ruky. Výsledek bý měl vypadat takto. Můžete opět vyzkoušet spuštěním aplikace, že levá ruka funguje stejně dobře jako pravá.

8

Tímto máme vyřešenou inverzní kinematiku pro ruce. Jako poslední vytvoříme opět prázdný objekt, tentokrát však omezení pro hlavu. Prázdnému objektu připneme skript Multi-parent Constraint, který hýbe s omezeným objektem tak, jako kdyby byl synem zdrojového objektu. Do proměnné Constrained Object přetáhneme kost hlavy modelu a do Source Object vložíme tento nově vytvořený objekt Head IK. V neposlední řadě využijeme opět Align transform a srovnáme pozici Head IK s kostí. Po spuštění a posunu hlavy si můžeme však všimnout, že samotné tělo zatím nijak nereaguje na pohyb hlavy a zůstává na místě.

9

Tento problém spravíme tím, že každý snímek budeme updatovat pozici těla dle aktuální pozice hlavy. Pro to si vytvoříme nový skript, nazveme ho VRRig a připneme ho na náš kořenový objekt modelu lidské postavy. Díky tomu, že vytvoříme skript skrze Unity Editor, bude autmoaticky obsahovat dvě předpřipravené funkce. Funkce Start se volá pouze jednou a to hned po spuštění aplikace a funkce Update se volá každý snímek. Důležité zde je, abychom přepsali metodu Update na LateUpdate. LateUpdate je Unity volán až poté, co doběhnou všechny Update funkce napříč aplikací. Třídě VRRig přídáme dvě public proměnné a to headContraint a headBodyOffset. Na zacatku si ve funkci start uložíme původní odsazení hlavy od těla a poté v každém snímku dopočítáme novou pozici pro tělo v závislosti na pohybu hlavy. Dopočítáme samozřejmě i korektní rotaci těla vuči hlavě pomocí vektorové funkce ProjectOnPlane. Po přepnutí se zpět do editoru, nezapoměnme myší přetáhnout Head IK do proměnné Head Constraint. Níže uvedený kód si můžete zkopírovat do vašeho řešení.

Ukázka 1. VRRig.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class VRRig : MonoBehaviour
{
    public Transform headConstraint;
    private Vector3 headBodyOffset;

    // Start is called before the first frame update
    void Start()
    {
        headBodyOffset = transform.position - headConstraint.position;
    }

    // Update is called once per frame
    void LateUpdate()
    {
        transform.position = headConstraint.position + headBodyOffset;
        transform.forward = Vector3.ProjectOnPlane(headConstraint.up, Vector3.up).normalized;
    }
}

Poslední věc, kterou je potřeba udělat, je napojení VR hardwaru na náš Avatar a VRRig třídu. K zjednodušení práce si proto navrhneme pomocnou třídu VRConnector, který bude obsahovat dvě proměnné jako reference pro daný VR hardware, například ovladač, a objekt z modelu lidské postavy. Zároveň bude obsahovat jednoduchou funkci Synchronize, která po zavolání nastaví pozici a rotaci obou zdrojů na stejno. Tuto pomocnou třídu umístíme opět do samostatného skriptu, ať udržíme pořádek v kódu. Jednoduchý kód si můžete opět zkopírovat do vašeho řešení.

Ukázka 2. VRConnector.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[System.Serializable]
public class VRConnector
{
    public Transform vrTarget;
    public Transform modelTarget;
    public Vector3 positionOffset;
    public Vector3 rotationOffset;

    public void Synchronize()
    {
        modelTarget.position = vrTarget.TransformPoint(positionOffset);
        modelTarget.rotation = vrTarget.rotation * Quaternion.Euler(rotationOffset);
    }
}

Tuto pomocnou třídu můžeme následně využít ve skriptu VRRig, kde přidáme tři nové proměnné a to head, leftHand a rightHand. Tyto proměnné využijeme ve funkci LateUpdate, kde každý snímek zavoláme dříve implementovanou metodu Synchronize. Zajistíme tím korektní a synchronizovanou translaci VR ovladačů a daných kostí v modelu lidské postavy.

Ukázka 3. Finální verze skriptu VRRig.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class VRRig : MonoBehaviour
{
    public Transform headConstraint;
    public VRConnector head;
    public VRConnector leftHand;
    public VRConnector rightHand;
    private Vector3 headBodyOffset;


    // Start is called before the first frame update
    void Start()
    {
        headBodyOffset = transform.position - headConstraint.position;
    }

    // Update is called once per frame
    void LateUpdate()
    {
        transform.position = headConstraint.position + headBodyOffset;
        transform.forward = Vector3.ProjectOnPlane(headConstraint.up, Vector3.up).normalized;
        head.Synchronize();
        leftHand.Synchronize();
        rightHand.Synchronize();
    }
}

Po úspěšné programovací části by v inspektoru měl model lidské postavy vypadat takto.

10

Připojení na VR hardware

Ve stavu, ve kterém se nachází aktuálně náš projekt, je úplně jedno, jaký VR headset a ovladače použijeme. Pro zjednodušení zde použiji HTC Vive headset, ale vše funguje i s Oculus Rift a jinými headsety na trhu. Pro rychlý VR setup použijeme Steam VR plugin, který stáhneme přes Window → Asset Store → Search Steam VR → Download → Import. V průběhu instalace SteamVR pluginu na vás může vyskočit okno, jestli použít Legacy system nebo Unity XR. Zvolme novější Unity XR. Tento plugin abstrahuje práci s hardware zařízením a opravdu nám usnadňuje práci.

Po importu SteamVR pluginu, musíme nakonfigurovat nastavení ovládání obou ovladačů. Pokud na vás nevyskočí dialog s Inputem automaticky, nahoře stačí kliknout na Window → Steam VR Input a otevře se vám okno vyobrazené níže. Zde pouze klikneme vlevo dole na Save and generate. Nechceme upravovat žádné nastavení, pouze vyzkoušet VR Avatara. Poté okno zavřete.

11

Do projektové hierarchie přetáhneme prefab [CameraRig] ze složky Assest\SteamVR\Prefabs a samozřejmě nezapomeneme přetáhnout kameru, levou a pravou ruku do našeho skriptu VRRig, který je připnutý na modelu lidské postavy. Do zbývajících prázdných proměných přetáhneme prázdné objekty Target, které jsme vytvářeli na začátku v rámci VR Constraints.

Nyní mužeme spustit aplikaci a vidíme, že paže a hlava opravdu kopírují pohyb ovladačů a headsetu. Pokud máte nějaký problém a v konzoli jsou varování nebo chyby, zkontrolujte pořádně, jestli jsou všechny skriptové proměnné správně umístěné a jestli nejsou nějaké prázdné. Můžete také zkontrolovat XR Plugin management nastavení přes Edit → Project Settings → XR Plugin Management a ověřit, že OpenVR Loader políčko je zaškrnuté.

Jak je na první pohled vidět, ruce a hlava jsou špatně natočené. K tomu nám poslouží proměnná ve skriptu Position offset a Rotation offset, které můžeme za běhu dle potřeby upravit. Před vypnutím aplikace si hodnoty nezapoměnte zkopírovat a následně uložit napevno. Já jsem pro jednotlivé odsazení zvolil následující hodnoty:

Head - Position → X: 0, Y: -0.15, Z: -0.1 - Rotation → X: 0, Y: 90, Z: -45

Left Hand - Position → X: 0, Y: 0, Z: 0 - Rotation → X: 56, Y: 205, Z: 96

Right Hand - Position → X: 0, Y: 0, Z: 0 - Rotation → X: -128, Y: -189, Z: 78

Výsledná konfigurace mapování a odsazení jsou vidět na následujícím obrázku.

12

Závěr

V tomto krátkém tutoriálu jsme společně zvládnuli namapovat libovolný VR Rig na 3D model lidské postavy za pomoci inverzní kinematiky. Pokud nebylo něco jasné, v přiloženém adresáři je možné najít složku s celým projektem v Unity 2019.4 a je možné tak dohledat jakékoliv nesrovnalosti.

Demo

Projekt se scénou je možné najít v repozitáři. Celé skripty lze najít zde.