VTuber風のコンテンツを深度センサーXtion2とUnityを使って実際に作ってみました。3DCGのキャラクターを動かしながら、VTuberのように音声を加えて動画に仕立ててみました。

チャレンジ&ナレッジ最終更新日: 20190321

VTuber風コンテンツをXtion2とUnityで作る

  • このエントリーをはてなブックマークに追加

ASUSの深度センサー「Xtion2」を使うと、身体にセンサー等のデバイスを装着することなく、人間の動きを捉えることが可能になります。
今回はXtion2で実際に3DCGのキャラクターを動かしながら、VTuberのように音声を加えて動画に仕立ててみました!

Xtion2でUnity上の3Dキャラクターを動かす仕組み

ASUSの深度センサー「Xtion2」と、画像認識プログラムを組み合わせて人間を撮影することで、人の形を認識させるとともに、人の形から推測される骨格の位置を割り出す(=骨格検出)ことができます。

この骨格の位置を3DCGモデルの骨格と紐付けることで、人の動きと3DCGモデルの動きを連動させることが可能になります。

※Xtion2を用いた骨格検出については下記の記事も参考にしてください。

“深度センサー Xtion2 を使って画面上の3Dモデルを動かす”
https://www.pc-koubou.jp/magazine/15974

OpenNI/Nuitrack/Unityの準備

事前にASUSのサイトから「OpenNI」、3DiViのサイトから「Nuitrack」をインストールしてください。

“Xtion 2 Driver & Tools | 3Dセンサー | ASUS 日本”.ASUS.
https://www.asus.com/jp/3D-Sensor/Xtion-2/HelpDesk_Download/

“Nuitrack Full Body Skeletal Tracking Software”.3DiVi Inc.
https://nuitrack.com/

また、今回3DCGキャラクターは上記のとおりUnity上で動かしますので、下記サイトからUnityをインストールし、「Asset Store」より「Nuitrack Skelton Tracking」をインポートした上で、サンプルプログラム「BasicSkeleton.unity」を起動しておいてください。

“Unity”.2019.Unity Technologies.
https://unity3d.com/jp

※セットアップの詳細は下記記事の内容も参考にしてください
“深度センサー Xtion2 を使って画面上の3Dモデルを動かす”
https://www.pc-koubou.jp/magazine/15974

Unity側の設定

3Dキャラクター「ユニティちゃん」の準備

今回動かす3DキャラクターはUnityの公式キャラクター「ユニティちゃん」を使用します。

“ABOUT | UNITY-CHAN! OFFICIAL WEBSITE”
http://unity-chan.com/contents/about/

Unityの画面左下「Assets→NuitrackSDK→Unitychan→Models」を選択し、下部中央に出てきた「unitychan」を、左上の「BasicSkeleton」にドラッグアンドドロップします。

Assets→NuitrackSDK→Unitychan→Models→unitychan の階層Assets→NuitrackSDK→Unitychan→Models→unitychan の階層

「BasicSkeleton」にドラッグアンドドロップする「BasicSkeleton」にドラッグアンドドロップする

ドラッグしたあとの画面内のスクリーンショットドラッグしたあとの画面内のスクリーンショット

NuitrackとUnityの連携設定

ユニティちゃんの準備ができたら、Nuitrackで認識した動きがUnity上のユニティちゃんの動きに反映されるように設定を行います。

今回はチュートリアルの中のプログラムをベースに動かしてみたいと思います。

まず「Assets→NuitrackSDK→Tutorials→First Project」の「NativeAvatar.cs」をダブルクリックで開いてソースコードを変更します。

NativeAvatarを開いてファイルを編集する。NativeAvatarを開いてファイルを編集する。

「NativeAvatar.cs」をダブルクリックすると、ファイルに紐づけられたアプリケーションでコードを編集する画面(テキストエディタ等)が開きますので、以下のソースコードをコピーして「NativeAvatar.cs」に貼り付けます。

もともと「NativeAvatar.cs」に書かれていたコードはすべて削除し、下記のソースコードと入れ替え、上書き保存します。

            #region Description

            // The script performs a direct translation of the skeleton using only the position data of the joints.
            // Objects in the skeleton will be created when the scene starts.
            
            #endregion
            
            
            using UnityEngine;
            using System.Collections.Generic;
            
            [AddComponentMenu("Nuitrack/Example/TranslationAvatar")]
            public class NativeAvatar : MonoBehaviour
            {
                string message = "";
            
                public nuitrack.JointType[] typeJoint;
                GameObject[] CreatedJoint;
                public GameObject PrefabJoint;
                //↓追加
                public Quaternion[] navi_Quatenion = new Quaternion[16];
            
                void Start()
                {
                    CreatedJoint = new GameObject[typeJoint.Length];
                    for (int q = 0; q < typeJoint.Length; q++)
                    {
                        CreatedJoint[q] = Instantiate(PrefabJoint);
                        CreatedJoint[q].name = typeJoint[q].ToString();  //名前の変更に一行追加
                        CreatedJoint[q].transform.SetParent(transform);
                    }
                    message = "Skeleton created";
                }
            
                void Update()
                {
                    if (CurrentUserTracker.CurrentUser != 0)
                    {
                        nuitrack.Skeleton skeleton = CurrentUserTracker.CurrentSkeleton;
                        message = "Skeleton found";
            
                        for (int q = 0; q < typeJoint.Length; q++)
                        {
                            nuitrack.Joint joint = skeleton.GetJoint(typeJoint[q]);
                            Vector3 newPosition = 0.001f * joint.ToVector3();
                            CreatedJoint[q].transform.localPosition = newPosition;
                            //↓追加
                            navi_Quatenion[q] = joint.ToQuaternion();
                        }
                    }
                    else
                    {
                        message = "Skeleton not found";
                    }
                }
            
                // Display the message on the screen
                void OnGUI()
                {
                    GUI.color = Color.red;
                    GUI.skin.label.fontSize = 50;
                    GUILayout.Label(message);
                }
            }
            
    

「NativeAvatar.cs」が保存できたら、編集していたテキストエディタ等を閉じ、Unityに戻ります。

ユニティちゃんを動かすための設定

Nuitrackで捉えた動きをUnity上のキャラクターの動きに反映するための設定ができたら、先ほど読み込んだユニティちゃんの3Dモデルが実際に動くように設定を行います。

Unityの上部メニュー「GameObject」内の「Create Empty」をクリックします。

GameObejectが追加された画面GameObejectが追加された画面

作成された「GameObject」をクリックして、右側インスペクタの「Add Component」をクリックし、「New Script」をクリックして、任意の名前を入力して「Create and Add」をクリックします。

リストボックスから「New Script」を選択するリストボックスから「New Script」を選択する

任意の名前を入力して「Create and Add」をクリック今回は「NewBehaviourScript」と命名する任意の名前を入力して「Create and Add」をクリック
今回は「NewBehaviourScript」と命名する

作成したスクリプト「NewBehaviourScript」が「Assets」(NuitrackSDKの上)内にできていますので、 「NewBehaviourScript」をダブルクリックで開き、先ほどと同様にテキストエディタ等でソースコードを変更します。

「Assets」内の階層にある、先ほど作成したスクリプトファイルを開いて編集する「Assets」内の階層にある、先ほど作成したスクリプトファイルを開いて編集する

「NewBehaviourScript 」が開いたら、下記「NewBehaviourScript.cs」のソースコードをコピーして貼り付ける。

こちらも元のソースコードをすべて下記に入れ替え、上書き保存します。

            using System.Collections;
            using System.Collections.Generic;
            using UnityEngine;
            
            public class NewBehaviourScript : MonoBehaviour {
            
            
                public GameObject _UnityChan;
                //ユニティちゃんのボーン
                public GameObject Ref;
                public GameObject Hips;
                public GameObject LeftUpLeg;
                public GameObject LeftLeg;
                public GameObject RightUpLeg;
                public GameObject RightLeg;
                public GameObject Spine1;
                public GameObject Spine2;
                public GameObject LeftShoulder;
                public GameObject LeftArm;
                public GameObject LeftForeArm;
                public GameObject LeftHand;
                public GameObject RightShoulder;
                public GameObject RightArm;
                public GameObject RightForeArm;
                public GameObject RightHand;
                public GameObject Neck;
                public GameObject Head;
            
                public GameObject navi;
                public GameObject navi_Waist;
            
                Component component;
            
                Quaternion[] quaternion = new Quaternion[16];
            
                // Use this for initialization
                void Start () {
            
                    navi = GameObject.Find ("NativeAvatar").gameObject;
            
                    //ユニティちゃんの各部位のオブジェクトの取得
                    _UnityChan =  GameObject.Find ("unitychan").gameObject;
            
                    Ref = _UnityChan.transform.FindChild("Character1_Reference").gameObject;
                    Hips =
                        Ref.gameObject.transform.FindChild("Character1_Hips").gameObject;
                    LeftUpLeg =
                        Hips.transform.FindChild("Character1_LeftUpLeg").gameObject;
                    LeftLeg =
                        LeftUpLeg.transform.FindChild("Character1_LeftLeg").gameObject;
                    RightUpLeg =
                        Hips.transform.FindChild("Character1_RightUpLeg").gameObject;
                    RightLeg =
                        RightUpLeg.transform.FindChild("Character1_RightLeg").gameObject;
                    Spine1 =
                        Hips.transform.FindChild("Character1_Spine").gameObject.
                        transform.FindChild("Character1_Spine1").gameObject;
                    Spine2 =
                        Spine1.transform.FindChild("Character1_Spine2").gameObject;
                    LeftShoulder =
                        Spine2.transform.FindChild("Character1_LeftShoulder").gameObject;
                    LeftArm =
                        LeftShoulder.transform.FindChild("Character1_LeftArm").gameObject;
                    LeftForeArm =
                        LeftArm.transform.FindChild("Character1_LeftForeArm").gameObject;
                    LeftHand =
                        LeftForeArm.transform.FindChild("Character1_LeftHand").gameObject;
                    RightShoulder =
                        Spine2.transform.FindChild("Character1_RightShoulder").gameObject;
                    RightArm =
                        RightShoulder.transform.FindChild("Character1_RightArm").gameObject;
                    RightForeArm =
                        RightArm.transform.FindChild("Character1_RightForeArm").gameObject;
                    RightHand =
                        RightForeArm.transform.FindChild("Character1_RightHand").gameObject;
                    Neck =
                        Spine2.transform.FindChild("Character1_Neck").gameObject;
                    Head =
                        Neck.transform.FindChild("Character1_Head").gameObject;
            
            
                    navi_Waist = navi.transform.FindChild("Waist").gameObject;
                }
            
                // Update is called once per frame
                void Update () {
                    for (int i = 0;i < 16;i++) {
                        quaternion[i] = navi.GetComponent().navi_Quatenion[i];
                    }
                    Hips.transform.position = navi_Waist.transform.position;
                    Hips.transform.rotation = quaternion[3];
                    Hips.transform.Rotate(0,-270,-90);
            
            
                    Spine1.transform.rotation = quaternion[2];
                    Spine1.transform.Rotate(0,90,-90);
            
                    LeftArm.transform.rotation =  quaternion[4];
                    LeftArm.transform.Rotate(0,180,0);
                    LeftForeArm.transform.rotation = (quaternion[6]);
                    LeftForeArm.transform.Rotate(0, 180, 0);
                    LeftHand.transform.rotation = (quaternion[8]);
                    LeftHand.transform.Rotate(0, 180, 0);
            
                    RightArm.transform.rotation = (quaternion[5]);
                    RightForeArm.transform.rotation = (quaternion[7]);
                    RightHand.transform.rotation = (quaternion[9]);
            
                    RightUpLeg.transform.rotation = quaternion[11];
                    RightUpLeg.transform.Rotate(0,-90,90);
                    RightLeg.transform.rotation = quaternion[13];
                    RightLeg.transform.Rotate(0,-90,90);
            
                    LeftUpLeg.transform.rotation = quaternion[10];
                    LeftUpLeg.transform.Rotate(0, -90, 90);
                    LeftLeg.transform.rotation = quaternion[12];
                    LeftLeg.transform.Rotate(0,-90,90);
            
                    Neck.transform.rotation = (quaternion[1]);
                    Neck.transform.Rotate(0, 70, -100);
            
            
                }
            }
            
    

上記を「NewBehaviourScript.cs」にペーストしたら、「NewBehaviourScript.cs」を保存してテキストエディタ等を閉じ、再度Unityに戻ります。

Unityに戻ったら「BasicSkeleton」内の「NativeAvatar」をリストの一番下にドラッグ&ドロップします。

一番上に配置されている「NativeAvatar」一番上に配置されている「NativeAvatar」

「Native Avatar」が一番下になるようにドラッグする「Native Avatar」が一番下になるようにドラッグする

現状では実際に動かした際、関節を示す赤い丸表示が表示されてしまうため、これを非表示にするために「Assets→NuitrackSDK→Tutorials→First Project→JointSphere」をクリックし、右側インスペクタの「Scale」のXYZを0にします

「JointSphere」を選択してInsepectar画面に「JointSphere」を選択してInsepectar画面に

「Inspector」内の「Scale」のパラメーターx.y.zを0に変更する「Inspector」内の「Scale」のパラメーターx.y.zを0に変更する

パラメーター0に変更したところパラメーター0に変更したところ

ユニティちゃんをXtion2で動かしてみる

上記の準備ができたら、unity画面上部の実行ボタンをクリックして動作確認してみます。

Unityの画面の真ん中の再生ボタンをクリックして実行しますUnityの画面の真ん中の再生ボタンをクリックして実行します

このように、人の動きと連動して画面内のユニティちゃんが動いてくれます。

エラーが出る場合

環境等によりエラーが表示される場合は、下記設定を行います。

まず、スクリプトの実行順を設定します。
https://docs.unity3d.com/ja/2018.1/Manual/class-MonoManager.html

Script Execution Order (スクリプト実行順設定)
(Edit > Project Settings > Script Execution Order) を使ってスクリプトの実行順を制御できます。

ホームメニューから Edit > project をクリックホームメニューから Edit > project をクリック

「 Script Execution Order」を選択して右側の「+」ボタンを押す「 Script Execution Order」を選択して右側の「+」ボタンを押す

リストの中から「NativeAvator」と「NewBehaviourScript」←(先ほど命名した名前)を選択リストの中から「NativeAvator」と「NewBehaviourScript」←(先ほど命名した名前)を選択

二つ選んだあとの最終的な画面(追加されていることを確認)したあとに「Apply」ボタンをクリック二つ選んだあとの最終的な画面(追加されていることを確認)したあとに
「Apply」ボタンをクリック

背景とナレーションを加えてVTuber風に仕上げる

Xtion2でユニティちゃんを動かすことができたら、背景とナレーションを入れてVTuber風の動画に仕上げてみましょう。

背景の設定

今回はAsset Store内にある「Skybox Series Free」という素材を使用します。「Asset Store」>「All Assets」>「Textures & Materials」>「Skybox Series Free」を選択し、「Download」ボタンをクリック後、「Import」ボタンをクリックして読み込みます。

「Asset store」に入りAll Assetsの部分をクリック「Asset store」に入りAll Assetsの部分をクリック

リストの中から「Texture&Materials」を選択するリストの中から「Texture&Materials」を選択する

「Skybox Series Free」を選択、DownloadしてImportする「Skybox Series Free」を選択、DownloadしてImportする

読み込み後、数ウィンドウが開きますので右下「Import」をクリックします。

Importが終わると、別ウインドウが開くので「Import」を押すImportが終わると、別ウインドウが開くので「Import」を押す

「Window」メニューから「Rendering」>「Lighting Settings」の順にクリックします。

「window」メニュー>「Rendering」>「Lighting Settings」の順でクリック「window」メニュー>「Rendering」>「Lighting Settings」の順でクリック

下図のような別ウィンドウが開きますので、右上の「Skybox Material」右にある丸い部分(下図赤枠)をクリックします。

「Skybox Material」右の丸い部分をクリック「Skybox Material」右の丸い部分をクリック

表示された「Select Material」ウィンドウから好みの背景をクリックすると、ゲームウィンドウ内に背景が反映されます(下図)。

その中で好きな背景を選択するその中で好きな背景を選択する

VTuber風動画完成

背景が入ったらあらためて実際に人の動きと連携させて動かしてみます。

最近はこのような3Dキャラクターを自分の代わりに画面に登場させた動画を配信するVTuberと呼ばれる方が出てきていますが、今回の動画に声を付けてVTuber風(?)に仕上げてみました。

おじさん声ではありますが……なんとなくそれらしい動画になった気がします。

Xtion2ならではのコンテンツ作りに挑戦しよう!

Xtion2で動かしたUnity上のキャラクターにナレーションを入れて上記動画のような形まで作成してきましたが、キャラクターを変えたり(もちろん声も)背景にこだわると、本格的なVTuberのようなコンテンツが作れそうです。

身体にセンサー等をなにも付けずに身体の動きを反映できるXtion2ならではのコンテンツ作りに是非挑戦してみてください。

※今回登場したキャラクター「ユニティちゃん」はユニティちゃんライセンス条項の元に提供されています。

ライタープロフィール パソコン工房NEXMAG
[ネクスマグ] 編集部

パソコンでできるこんなことやあんなこと、便利な使い方など、様々なパソコン活用方法が「わかる!」「みつかる!」記事を書いています。

記事を
シェア