develog.me

into VR

Oculus Riftが到着してからやったこと(随時更新)

https://lh4.googleusercontent.com/-WJUZhTSAE4A/UY3W6UEc8PI/AAAAAAAAALg/a6BiVZjMqwk/s600/IMG_2795.jpg

早速!。。って何からやったものか

changelog

  • up 2013-06-09 Oculus + Unity + Razer Hydraで VRDemo Tuscany を動かす 追加
  • up 2013-06-08 いろいろ追加
  • up 2013-05-19 VMD Loaderまで
  • up 2013-05-12 PMD Loaderまで
  • new 2013-05-11 開発環境構築途中まで

とりあえず初体験してみる

すごいガジェットが手に入った!! けど、何からしていいものか、とりあえずOculusの実力を体験してみる。

その前に色々準備

PCと接続

  • 本体は実際に頭に装着するHeadsetと、電源ON/OFF・ブライトネス・コントラスト操作のControl Boxがケーブルで接続されている。
  • Control Boxに電源通してPCとUSB接続。あと映像のためのHDMIかDIV挿せばOK。

その1 Steamゲームで遊ぶ

Team Fortress 2 で遊んでみる - 無料

  • ユーザー登録とSteamクライアントインストール。
  • Oculus VRのHatCodeを使ってゲームをインストール
    1. クライアントインストール後に「ライブラリ」の左下、「ゲームを追加…」から「Steamでアイテムを有効化する…」を選ぶ。
    2. HatCodeを使ってゲームを有効化する。
    3. ダウンロード、インストールが終わったら、「ライブラリ」から「Team Fortress2」を選んで右クリック。「プロパティ」を開く。
    4. 「一般」の「起動設定…」に -vr を入力。
    5. 現在は関係ないが、ちょっと前に画面分割されなくなる不具合があったみたいで、念のため「ベータ」で「prerelease」選ぶ。
    6. ゲーム開始。「プレイ」かダブルクリックで。最初のメニュー画面は左右分割されない。ゲームプレイ時の画面でようやく分割表示される。(ロード画面も分割されない。)
    7. 文字は荒い。ゲーム中のInfoは左だけ表示で右にボタンあったりすると押せない。
      1. vr_calibration コマンドやら手はあるかとは思うがそこまで詳しく見てない。
      2. 詳しくはこちらで Oculus Rift User Guide - Official TF2 Wiki | Official Team Fortress Wiki

Half-Life 2 で遊んでみる - 有料

その2 ユーザー作成ゲームで遊ぶ

@warapuriさん作、巨人立体機動ゲーム

  • Titan - UnityGames
    • 進撃の巨人風ゲーム。
    • v0.0.5の時点では人類に巨人を倒すすべはありません。捕食対象です
    • 立体機動で空を縦横無尽に飛び回る爽快感は凄い。
    • Macでは無理っぽい?VMでやっても真っ黒
    • Ver. 0.0.5
      • 巨人がプレイヤーを食べるようになりました
    • Ver. 0.0.4
      • 巨人が自分を追いかけて捕まえるようにしました
    • Ver. 0.0.3
      • 巨人がこっちを見るようになりました
    • Ver. 0.0.2
      • 鎖で引っ張られている最中は↑方向(頭方向)に力が加わるようにしました
    • Ver. 0.0.1
      • 敵は倒せません
      • 見る以外何もできませんので絶望感を味わって下さい

ギロチン体験

Oculus Developer Forums - Oculus Showcase

その3 YouTube

その4 UnityでParaisoを作る

  • 導入
    • 注意
      • Unityの正しい知識が欠如しています。間違いは Pull Request Twitterからご指摘ください。
      • MMD for Unity SVNから直接Checkoupしてきた r129 使用
        • MacUnity では、 PMD Loader でモデルデータをConvertすると、生成される Materials Physics ファイルが文字化けして、参照に失敗するみたいなので Windows必須
      • ただし、 Windows 32bit では Oculus Unity でdll参照時に64bitのdllをみてエラーっぽい(未確認)ので、実行は MacUnity でやっている。
    • Unity導入
      • UnityでOculusを使うにはProライセンスが必要(133,875円)
        • Oculusユーザーは4ヶ月Trialできるらしいけど?
        • すでにインストール済みで普通のLICENSEの人は、「Manage License」から適当にライセンスリセットで途中からUnityのライセンス切り替えができる。
    • MikuMikuDanceを手に入れる
    • Oculus VR Developer登録してSDKを入手する
  • 手順
    1. Unity新規プロジェクト作成〜Oculus・フィールド下準備
      1. OculusUnityIntegrationに入ってるサンプル「が僕達の新居にふさわしいので、この中で同居します。フヒヒ」『内にMMDのモデルを配置する』
        OculusUnityIntegrationGuide.pdfを参考に、元となるプロジェクトをUnityで準備する
        1. Unityで新規プロジェクトる
        2. プロジェクトにサンプル読み込ませる(Windowsだと強制終了食らったのでMacで作業 Oculus Unity v0.22)
          • メニューの Assets -> Inport Package -> Custom Package で OculusUnityIntegrationTuscanyDemo.unitypackage を選ぶ
          • Project ビュー の Tuscany Scenes SRDemo_Tuscany.unity をダブルクリック 出てきた ダイアログは Don’t Save を選ぶ
        3. Scene に表示されるはず。上の 押して試してみる。
          • 操作はFPS風のWASD(詳しくはOculusUnityIntegrationGuide.pdfのp5, 2.2)
    2. ミクに裸Yシャツになってもらう UnityでMMDモデルを追加する(MMD for Unity、MFU r129)
      • 全面的にこちらの記事を参考に作る
        【MMDforUnity】UnityでMMDモデルを動かす: 名も無き製作室
        1. Unityで作ったプロジェクトでMMDが動くようにPluginとか追加
          • SVNからチェックアウトしてきた mmd-for-unity/trunk 下の MikuMikuDance for Unity フォルダごと Project View に D&D
            • メニューバーの PluginsMMD Loader が追加されたら成功
        2. 嫁を手に入れる
        3. MMDモデルファイル .pmd を変換してUnityプロジェクトに読み込ませる
          1. Unityの Project ViewMMDModel フォルダを追加して、 MMDDaccs_0150 フォルダごとどらっぐどろっぷ
            • ものによって依存とかあるみたいで、単品で最低限コピーするとめんどくさそう。
            • これがいいのか悪いのか知らないけど ハーレム 後からばかすか入れるつもりで管理が用意だからこうする。
          2. PMD Loader を使って .pmd ファイルを Convert
            • PMD Loader 起動
              • メニューバー Plugins から、 MMD Loader > PMD Loader
            • Convert対象の .pmd ファイルを選択して Convert
              • Project ViewMiku wDummy.pmd を、 PMD file : None(Object) の上にD&D
                • (それか None(Object) の右にある丸アイコンをクリックして出るとこから選ぶ)
              • Convert クリック
              • 正常に取り込めたら、 Hierarchy View に生成されたモデルを Project View の同じく生成された Asset (白い四角アイコン)に D&D
                • これをやらないと VMD LoaderPrefab の選択肢に出てこない。
            • TroubleShooting
              • NullReferenceException: Object reference not set to an instance of an object
                • メッセージ押せばエラー発生箇所がでる。コード嫁
              • 取り込んだモデルのテクスチャがかけている、真っ白、真っ黒
                • .pmd が参照するテクスチャ画像ファイルを Project View に持ってき漏れている。
                  そもそもモデルのディレクトリまるごとコピーしていれば問題ない
              • ただでさえ天使で眩しいミクさんが眩しすぎて辛い
                • 取り込んだ Materials の設定で光の当たり方を変える必要がある。
                • Project View の生成した Prefab (青い四角アイコン)と同じ階層にある Materials フォルダ以下のファイルを全選択
                • Inspector ViewShader で Diffuse を選ぶ
              • 浮気したわけでもないのに天使のはずのミクさんの顔の影が怖い
                • 他の物体から影を計算する Receive Shadows が効いている。
                • 本来は正しくモデルを直すべきだが、一生を共にするつもりはないので OFF にする。
                • Project View の生成した Prefab (青い四角アイコン) ファイルを選択
                • Inspector ViewSkinned Mesh Renderer Receive ShadowsOFF
              • ネクタイ・スカートがめり込む
                • いろいろ方法があるみたい。
                • Ki式初音ミクAct4 さんの場合は、
                  • MFUr122 から r129 に変えて取り込み直したら、スカートめり込まないようになった。
                  • ネクタイは ネクタイ1Configurable JointAngular XMotionLimitedLocked で(Limitedのまましきい値設定でも良さそう)
          3. 晴れて配置が終わったら、巨人ミクさんのサイズを変える
            • Convert直後に配置されるミクは駆逐対象サイズなので、自分の性癖の身長に変更
              1. Project View の生成した Prefab (青い四角アイコン) ファイルを選択
              2. ScaleX Y Z を次の値で統一する。
                目安としては、ペドは 0.1以下 、ロリは 0.1 、普通は 0.15 あたりで。モデルによって変わるかもしれない
    3. 配置したモデルにMotionを割り当てる
      1. MMDのモーションデータ .vmd を変換する
        1. モーションデータを拾ってくる VPVP wiki - モーションデータ/日常系・その他
        2. VMD Loader を使って .vmd ファイルを Convert
          • VMDLoder 起動
          • メニューバー Plugins から、 MMD Loader > VMD Loader
        3. PMD Prefab には先程生成してこちょこちょした Prefab ファイル
          • PMD Loader 取込、 Convert 直後に次の操作が必要(上から読んでいたら実行済)
          • Hierarchy View に生成されたモデルを Project View の同じく生成された Asset (白い四角アイコン)に D&D 青い四角アイコンに変わればOK
          • しないと VMDLoder の選択肢で、 PMDLoader でConvertしたモデルが選べない。
        4. VMD file歩く10.vmd
        5. よく知らないけど Create Asset にチェック
        6. よく知らないけど Interpolation Quality はそのまま 1
        7. Convert
        8. Prefab と同じ階層に Animation フォルダが生成され、その中に 再生ボタンっぽいアイコンのファイルができる。
      2. モデルに変換したモーションデータをバインドする
        1. このままじゃモデルにバインドされてないので、再生ボタンっぽいアイコンのファイルを選んで青い四角アイコンの Prefab に D&D
        2. 一回しかモーションしてくれないので Loop させる
          • 再生ボタンっぽいアイコンのファイルを選んで Inspector ViewWrap Mode から Loop を選ぶ
        3. ここまでを確認
          • 押して試してみる。
          • 操作はFPS風のWASD(詳しくはOculusUnityIntegrationGuide.pdfのp5, 2.2)

Oculusに関連するもの

ガジェット

DELTA SIX

Omni by Virtuix

Leap Motion

Razer Hydra

ゲーム

Oculus + Unity + Razer Hydraで VRDemo Tuscany を動かす

  • Sixense Razer Hydra Oculus Rift Tuscany Demo RELEASED
    • SourceCodeの公開渋ってるから自分でやってみた
  • ポイントは3つ
    • RazerHydraをUnityで使えるようにするAssetを入れる
    • 手のオブジェクトを移動に追従するようにする
      • Hierarchy でオブジェクトの入れ子を変更
        • OVRPlayerController/OVRCameraController/CameraLeft/Left Hand
        • OVRPlayerController/OVRCameraController/CameraRight/Right Hand
    • RazerHydraのJoystickで移動する
      • OVRPlayerController.cs 書き換え( // Razer Hydra の3箇所)
        public virtual void UpdateMovement()
        {
          // Do not apply input if we are showing a level selection display
          if(OVRMainMenu.sShowLevels == false)
          {
            bool moveForward = false;
            bool moveLeft    = false;
            bool moveRight   = false;
            bool moveBack    = false;
        
            MoveScale = 1.0f;
        
            // * * * * * * * * * * *
            // Keyboard input
        
            // Move
        
            // WASD
            if (Input.GetKey(KeyCode.W)) moveForward = true;
            if (Input.GetKey(KeyCode.A)) moveLeft  = true;
            if (Input.GetKey(KeyCode.S)) moveBack    = true; 
            if (Input.GetKey(KeyCode.D)) moveRight   = true; 
            // Arrow keys
            if (Input.GetKey(KeyCode.UpArrow))    moveForward = true;
            if (Input.GetKey(KeyCode.LeftArrow))  moveLeft    = true;
            if (Input.GetKey(KeyCode.DownArrow))  moveBack    = true; 
            if (Input.GetKey(KeyCode.RightArrow)) moveRight   = true; 
        
            if ( (moveForward && moveLeft) || (moveForward && moveRight) ||
               (moveBack && moveLeft)    || (moveBack && moveRight) )
              MoveScale = 0.70710678f;
        
            // No positional movement if we are in the air
            if (!Controller.isGrounded) 
              MoveScale = 0.0f;
        
            MoveScale *= DeltaTime;
        
            // Compute this for key movement
            float moveInfluence = Acceleration * 0.1f * MoveScale * MoveScaleMultiplier;
        
            // Run!
            if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift))
              moveInfluence *= 2.0f;
        
            if(DirXform != null)
            {
              if (moveForward)
                MoveThrottle += DirXform.TransformDirection(Vector3.forward * moveInfluence);
              if (moveBack)
                MoveThrottle += DirXform.TransformDirection(Vector3.back * moveInfluence) * BackAndSideDampen;
              if (moveLeft)
                MoveThrottle += DirXform.TransformDirection(Vector3.left * moveInfluence) * BackAndSideDampen;
              if (moveRight)
                MoveThrottle += DirXform.TransformDirection(Vector3.right * moveInfluence) * BackAndSideDampen;
            }
        
            // Rotate
        
            // compute for key rotation
            float rotateInfluence = DeltaTime * RotationAmount * RotationScaleMultiplier;
        
            //reduce by half to avoid getting ill
            if (Input.GetKey(KeyCode.Q)) 
              YRotation -= rotateInfluence * 0.5f;  
            if (Input.GetKey(KeyCode.E)) 
              YRotation += rotateInfluence * 0.5f; 
        
            // * * * * * * * * * * *
            // Mouse input
        
            // Move
        
            // Rotate
            float deltaRotation = 0.0f;
            if(AllowMouseRotation == false)
              deltaRotation = Input.GetAxis("Mouse X") * rotateInfluence * 3.25f;
        
            float filteredDeltaRotation = (sDeltaRotationOld * 0.0f) + (deltaRotation * 1.0f);
            YRotation += filteredDeltaRotation;
            sDeltaRotationOld = filteredDeltaRotation;
        
            // * * * * * * * * * * *
            // XBox controller input  
        
            // Compute this for xinput movement
            moveInfluence = Acceleration * 0.1f * MoveScale * MoveScaleMultiplier;
        
            // Run!
            moveInfluence *= 1.0f + 
                       OVRGamepadController.GPC_GetAxis((int)OVRGamepadController.Axis.LeftTrigger);
        
            // Razer Hydra
            uint buttonsCount = 0;
            string buttonsText = "";
            string buttonsText2 = "";
            if (SixenseInput.Controllers[0].Enabled) {
              foreach (SixenseButtons button in System.Enum.GetValues(typeof(SixenseButtons))) {
                if (SixenseInput.Controllers[0].GetButton(button) && (buttonsCount < 4)) {
                  if ( buttonsText != "" ) {
                    buttonsText += " | ";
                  }
                  buttonsText += button;
                  buttonsCount++;
                } else if (SixenseInput.Controllers[0].GetButton(button) && (buttonsCount >= 4)) {
                  if (buttonsText2 != "") {
                    buttonsText2 += " | ";
                  }
                  buttonsText2 += button;
                  buttonsCount++;
                }
        
                if (SixenseInput.Controllers[0].GetButtonDown(button)) {
                  Debug.Log("Pressed = " + button + ":" + buttonsText2);
                }
        
                if (SixenseInput.Controllers[0].GetButtonUp(button)) {
                  Debug.Log("Released = " + button + ":" + buttonsText2);
                }
              }
            }
            //guiText.guiText.text = "Buttons = " + buttonsText;
            //guiText2.guiText.text = "" + buttonsText2;
        
        
            // Move
            if(DirXform != null)
            {
              float leftAxisY = 
              OVRGamepadController.GPC_GetAxis((int)OVRGamepadController.Axis.LeftYAxis);
        
              float leftAxisX = 
              OVRGamepadController.GPC_GetAxis((int)OVRGamepadController.Axis.LeftXAxis);
        
              // RazerHydra
              if (SixenseInput.Controllers[0] != null) {
                leftAxisY = SixenseInput.Controllers[0].JoystickY;
                leftAxisX = SixenseInput.Controllers[0].JoystickX;
                if (buttonsText == "JOYSTICK") {
                  leftAxisY *= 2.0f;
                  leftAxisX *= 2.0f;
                }
              }
        
        
              if(leftAxisY > 0.0f)
                  MoveThrottle += leftAxisY *
                DirXform.TransformDirection(Vector3.forward * moveInfluence);
        
              if(leftAxisY < 0.0f)
                  MoveThrottle += Mathf.Abs(leftAxisY) *    
                DirXform.TransformDirection(Vector3.back * moveInfluence) * BackAndSideDampen;
        
              if(leftAxisX < 0.0f)
                  MoveThrottle += Mathf.Abs(leftAxisX) *
                DirXform.TransformDirection(Vector3.left * moveInfluence) * BackAndSideDampen;
        
              if(leftAxisX > 0.0f)
                MoveThrottle += leftAxisX *
                DirXform.TransformDirection(Vector3.right * moveInfluence) * BackAndSideDampen;
        
            }
        
            float rightAxisX = 
            OVRGamepadController.GPC_GetAxis((int)OVRGamepadController.Axis.RightXAxis);
        
            // RazerHydra
            if (SixenseInput.Controllers[1] != null) {
              rightAxisX = SixenseInput.Controllers[1].JoystickX;
            }
        
        
            // Rotate
            YRotation += rightAxisX * rotateInfluence;    
          }
        
          // Update cameras direction and rotation
          SetCameras();
        
        }