VRでしかできないことがやりたい

AR,MRといった類似技術があるなかVRでしかできないことがやりたいのです。

Quaternionで回転する(ソースコードつき)

応用するとしっぽみたいなのがプログラムだけで作れます。

HMDは軸がフリーの回転をする

数学を好んでいようとそうでなかろうとHMDを使ってVRをやるには3次元空間が必要である。

そして3次元空間の計算には座標移動と回転がもれなく必要である。
特にVRではどの回転軸も固定されない。
よくある三人称視点のゲームを考えてみるとY軸(プレイヤーからみて上方向)以外の方向で回転することはあまりない。

なぜQuaternionか?

Unreal EngineにはFRotatorというオイラー角でジンバルロックのことをあまり考えずに回転ができる便利な構造体が用意されている。

ただUnityにはそういうものはないのでオイラー角で回転させようとすると色々考える必要がある。*1

UnityであろうとUnreal Engineであろうと内部ではQuaternionを使って回転している。 表示したときに直感的にわかりやすいのはオイラー角であるが、オイラー角だけで回転させようとすると諸々の考慮が必要である。

例えば角度を出したときに値がどういう範囲(0°~360°か?や-180°~180°)で返されるか?であったり、あるオブジェクトを回転させるときに2つの軸で90°回転させた場合はジンバルロックについての考慮が必要である。

しかし、Quaternionを使えばこのような考慮をせずとも回転させることができる。 きみもQuaternionを使って雑に回転させよう!

※Quaternionについての数学的な細かい解説はしません。
※数学を専門的に学んだわけじゃないので数学的に間違ってたりしたらごめんね。

Quaternionって何?

話をかんたんにするために平面で考えてみよう。

例えば 画像のy軸と原点と点Bを通る直線がなす角Qと 画像のx軸と原点と点Aを通る直線がなす角Rとの差分を求めたいときを考える。 x軸y軸は直交する。

オイラー角の場合

オイラー角の計算で∠Dを求める

オイラー角の場合はY軸とX軸は直行するので

∠D = 90° - (90°-∠Q) - (90°-∠R)
= ∠Q + ∠R - 90°
となる。

Quaternionの場合

Quaternionで∠Dを求める

さて、Quaternionで求めてみる。

Quaternionの値には回転の角度ではなくどちら回りに回転させるかの向きも表している。(重要)

向きがあるってどういうことだ?

∠Dの角度を出したいときはQ と R の掛け算になる。なぜ掛け算になるかは数学の話になるので割愛する。 Q, R, DはQuaternionで∠Q, ∠R, ∠Dの角度と向きを表すとする。

ただし Quaternionには向きが存在するために掛け算には順番が存在する (掛け算の交換法則が成り立たない)。 つまりQ * R と R * Qの値が異なる。

Q * R と R * Q

Q * RはDだが
R * QはD'になる。 掛け算をする操作が矢印の方向をたどってみることに対応する。 最終的にどちらを向いているかを考えるとよい。 あるいは引き算で引かれる数と引く数を逆にすると絶対値は同じだが符号が逆になってしまうことに近いかもしれない。

実際にUnityで計算してみる - Quatenionで他のオブジェクトに回転を複製してみる

一番左のCubeが回転の複製元である。複製元には回転軸とは別の軸で回転を入れておく。
Z軸を中心に回転させる。

ほかの複製先のCube(RotateObject)は補間の係数を変えて複製元(SourceObject)から回転させている。 複製先にもそれぞれ回転軸とは別の軸で回転が入っている。

複製元を回転させるソースコード

クリックすると展開されます

public class RotationTest_Source : MonoBehaviour
{
    [SerializeField]
    protected GameObject RotateObject;


    [SerializeField]
    protected float RotateDegreeX = 0.0f;

    [SerializeField]
    protected float RotateDegreeY = 0.0f;

    [SerializeField]
    protected float RotateDegreeZ = 0.0f;


    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        if(RotateObject==null)
        {
            return;
        }

        Vector3 rotateEular =  new Vector3 (  RotateDegreeX ,RotateDegreeY , RotateDegreeZ );

        RotateObject.transform.Rotate(rotateEular,Space.Self);
    }
}

回転を複製するソースコード

    //Rotate source
    [SerializeField]
    public GameObject SourceObject;

    [SerializeField]
    public GameObject RotateObject;

    [SerializeField]
    protected float RotationRate = 1.0f;

    Quaternion PrevFrameSourceRotation = Quaternion.identity;

    // Start is called before the first frame update
    void Start()
    {
        PrevFrameSourceRotation = SourceObject.transform.rotation;
    }

    // Update is called once per frame
    void Update()
    {
        if(SourceObject == null || RotateObject == null)
        {
            return;
        }

        Quaternion currentSrcRotation = SourceObject.transform.rotation;
        Quaternion deltaSrcRotation = Quaternion.Inverse(PrevFrameSourceRotation) * currentSrcRotation ;

        Quaternion addRotation = Quaternion.Lerp(Quaternion.identity, deltaSrcRotation ,RotationRate);

        RotateObject.transform.rotation *= addRotation;
        PrevFrameSourceRotation = currentSrcRotation;
    }

1つ前のフレームの回転と現在の回転の差分をとっているところが

Quaternion deltaSrcRotation = Quaternion.Inverse(PrevFrameSourceRotation) * currentSrcRotation ;

にあたる。 Rの向きが上記の図でQの逆を向いていたがこれにはQuaternion.Inverse(PrevFrameSourceRotation)が対応する。 この差分を割合でUpdateで現在の回転に加えている。

RotateObject.transform.rotation *= addRotation;

で現在の回転に掛けることが回転を加えることを表す。

かける順番が正しいケース

かける順番が正しいケース

Quaternion deltaSrcRotation = Quaternion.Inverse(PrevFrameSourceRotation) * currentSrcRotation ;

複製元(SourceObject)と同じように回転する。

かける順番が正しくないケース

かける順番が正しくないケース

Quaternion deltaSrcRotation = currentSrcRotation * Quaternion.Inverse(PrevFrameSourceRotation) ;

最初と最後はピッタリ合う。ただし途中の軌跡が異なる。 複製元と複製先に入っている回転と、回転なしの状態(つまりX軸, Y軸, Z軸の向き)の中間地点あたりの方向を軸にした回転となっている。

*1:LookAt関数みたいな用途がハッキリしているものはある。

docs.unity3d.com

酒呑童子になる-機械学習編

さて、今回がわざわざ酒呑童子になるまでの過程を記事にする動機になったところです。

 

環境はWindows 10,NVIDIA Geforce1060でやっています。

 

 

基本的には以下の記事のソースコードをまんま使っています。

 

massoumen.hatenablog.jp

 

aidiary.hatenablog.com

 

 

おそらくですがみくにゃんの記事を書いたまっそ氏、そして氏が参考にした記事の著者aidiary氏もWindowsを使っていないと思われる(SPTKのインストールに関する記述が少ないため)ので結構苦労しました。

 

そのため以下の記事を参考にSPTKをWIndows環境にインストールしました。

 

SPTKをWindows+MSVC環境にインストールする方法.md · GitHub

 

SPTKのバージョンが3.9以降だと外部参照のエラーが出て

ビルドできなかったため3.9でやっていました。

 

 

学習をすることに成功し、

 

重みなどのデータも保存し、その学習結果を元にメルケプストラムの変換まではすることができました。

 

しかしながら、メルケプストラムからwavデータに戻す際に

SPTKのコマンドがないというエラーが出ました。

f:id:danbokig:20180829214542p:plain

 

SPTK3.9のリファレンスを読んでみると確かにsoxというコマンドはなさげですが

clip -yというコマンドオプションはある模様。

 

 

誰か助けてほしい。

 

酒呑童子になる-動きをとる編

前回、前々回とNuitrackで動きをとってみようと試みてましたが、

モデルをAポーズで作っているとうまく動かないことが分かったので、

いっそのこと割り切ってNuitrackで動かすのは下半身だけにして上半身はコントローラーで動かそうと考えました。

 

ソースコードです。

 

 

処理は

1.HandTargetをアバターの位置に生成し、コントローラのCamera(eye)からの相対的な位置をHandTargetに送ってOnAnimatorのIKで手を動かす。

2.HeadTargetを生成し、Camera(eye)の子にしてその回転を首のボーンに送る

の2つです。

 

HeadTargetの回転は(たぶん)アバターのモデルのHipsの回転をとってくれば

首の回転にそのまま送れるという適当な憶測の元決めているので不具合があれば教えてください。

 

ただ、元のモデルを足を開いた状態で作ってしまったのでがに股で動いてしまってます。(多分モデルを作り直すことになる)

 

Nuitrackでの肘のSkeletonをIKヒントとして使ってやろうと思ったけれども

座標変換が必要っぽいのでとりあえずここまで。

酒呑童子になる-Nuitrack編②

前回の記事の続きです。

danbokig.hatenablog.com

 

NuitrackSDKのテストシーンがうまく動いたという前提で進めていきます。

 

Unity 3Dのパッケージを展開するとTutorialsというフォルダがあり、その中にRigged Avatorというシーンがあります。

f:id:danbokig:20180827083103p:plain

 

このシーンを参考にして用意した3DモデルにNuitrackのスクリプトを追加します。

 

基本的には公式のマニュアルがあるのでそこを参考に。

Nuitrack: Animating the Avatar using Skeleton

 

RiggedAvarorというスクリプトを適当なゲームオブジェクトにアタッチし、

jointsに適切なボーンを割り振ります。

 

フレデリックオドループのダンスをしてみました。

 

Unityちゃんでやった場合(サンプルシーン:RiggedAvator)

 

酒呑童子でやった場合

 

 

で、気づいたんですがTポーズでアバター作んないと適切に動かないみたいです。

具体的な処理としてトラッキングしたスケルトンのTポーズの時におそらくボーンの角度に0を入れていると思われるので、

 

Tポーズをしたときに酒呑童子のほうだと手が下がってしまっています。

ついでに言えば足を開いた状態で作ってしまったのでがに股になってしまった。

 

とりあえずNuitrack編おしまい。

酒呑童子になる-Nuitrack編①

前回の記事の続きです。

 

 

酒呑童子になるべくUnityで全身トラッキングをしようと思ったわけです。

使っているデバイスがOculusなのでViveTrackerを6つとか使ってやる方針ではなく、

深度カメラを使ってやろうと考えました。

 

 

Intel D415です。

 

深度カメラでUnityとなるとNuitrack一択なのかなという感じなのでNuitrackのマニュアルに準じます。

 

Windowsが32ビットの場合はNuitrack-32.zipを、64ビットの場合はNuitrack-64.zipをダウンロードします。

 

f:id:danbokig:20180826204018p:plain

zipにOpen NIが入っているので実行し、インストールします。

任意の場所に"nuitrack"フォルダを移動し、

環境変数のPATHに<nuitrackを置いたフォルダ>\nuitrack\binを追加し、

環境変数に新たにNUITRACK_HOMEを追加し、<nuitrackを置いたフォルダ>\nuitrack

を通します。

 

 

そしてNuitrackのホームページからNuitrackのSDKをダウンロードします。(要メールアドレス)

nuitrack.com

 

 

任意のプロジェクトにNuitrackSDKの中のUnity3Dのパッケージを追加します。

 

とりあえず今日はここまで。

酒呑童子になる-モデル編

こんにちは、danbokigです。

 

突然ですが、僕はFGOに出てくる酒呑童子が好きです。

 

f:id:danbokig:20180826130812j:plain

 

 

なのでVRを使って何者かに変身してみたかった僕はこのキャラクターになってみることにしました。

 

 

モデリング

 ここではモデリングについては説明を割きません。

僕の専門ではないし、それほどうまくできたとは思えないためです。

 

ただ、学生の方にはMayaをおすすめします。

学生証をAutodeskに送るだけで4年間は無償版が使えるためです。

 

基本的にはこの一連の記事を参考にしています。

area.autodesk.jp

 

Mayaで作っていたのでblenderのことはわかりませんが、

Blender講座で散見されるポリゴンを下絵に合わせてなぞっていく方法よりも

最初にプリミティブで大まかな形を作って細部を分割していく方法のほうが早くできるかと思います。

 

 

また、Unityで使うためにモデリングをしたので第5回のリギングのリグコントローラの部分は飛ばしてしまって結構です。

area.autodesk.jp

 

 

そうしてできたモデルはこのような感じです。

 

 続きますよ

VRの制作物1 - VR双眼鏡

f:id:danbokig:20170416025556j:plain

f:id:danbokig:20170416025604j:plain

Unity、AndroidBluetoothマウスを使い、初めてVRにを出した作品です。

3年の前期、後半に制作したものでしょうか。

上記画像だけではさっぱりわからないと思うので補足説明をします。

(きちんとどうなっているか分かる画像を残しておくべきですね)

 

 

 

f:id:danbokig:20170416030533j:plain

 

 まず、シーンはこんな感じになっています。マウスクリックでカメラがUnityちゃんに近づいていきます。

 

 

 

f:id:danbokig:20170416031559j:plain

体験状況はこうです。

 

VRゴーグルをのぞく状況が双眼鏡をのぞくのと似ているのでは無いかと考えました。

前にある模型を、双眼鏡でみてズームをするとUnityちゃんが居て、ポーズを取ってくれる…という感じでした。

 

 

ARをつかってカメラ画像と合成したほうがよかったんだろうか。

もしくは、体験者の周りの状況を作り込んで双眼鏡を使っているという感じを出すべきだったんだろう。

先生にも言われたが、このままでは模型の意味があまり感じられない。