- 投稿日:2019-05-23T21:28:13+09:00
Unity でVRのテレポートを一番簡単な実装
概要
この記事は、VR上でよく目にする「テレポート」を実現するための手法を書いてあります。ハンドデバイスの先に放物線状に線が表示されて、ボタンを押すとその着地点にワープするというアレです。
順番としては
- VRのためのセットアップ
- VRTKインストール(Oculus Integrationも)
- ゲーム画面のセットアップ
- デバイス入力
- テレポートシステム
とやっていきます。
システム要件
- Unity 2018.3.0f2
- Oculus GO
- Asset Storeで無料で取得できるアセット
- VRTK (4.0.0)
- Oculus Integration (1.36)
Android プラットフォームに切り替え
PlayerSettings の設定
Oculus GO用にPlayerSettingsをいくつか変更します。
XR Settings
Other Settings
- Package Name を適当に自分のドメインを入れる
- Minimum API Level を「Android 4.4 'KitKat' (API level 19)」を選択
Scripting Runtime Version を「.NET 4.x Equivalent」(デフォルトでなっているはず)
VRTKとOculus Integrationのインストール
ハンドデバイスやテレポートシステムを簡易的に使うには「VRTK」が一番簡単で良いだろうということで、VRTKを使うことにします。VRTKは最新版のv4.0.0を使うことにします。ただし、最新版はGitHubにしかない(2019/5/22現在)ので、gitコマンドで持ってくる必要があります。
ターミナル(もしくはcmd)で プロジェクトの Assets/ 以下に行って、以下のコマンドを打ちます
git clone --recurse-submodules https://github.com/ExtendRealityLtd/VRTK.git cd VRTK git submodule init && git submodule updateGitコマンドがない、というかたは
https://github.com/ExtendRealityLtd/VRTK
からダウンロードしてください。その場合は
https://github.com/ExtendRealityLtd/Zinnia.Unity/tree/91b8e16fc366cf6022440ed1efbe7ce4e636eae4
もダウンロードして
Assets/VRTK/Dependency/Zinnia.Unity/
に配置する必要があるので気をつけてください。
なお、現段階(2019/5/22現在)では以下のようなエラーが出てしまいます。
Assets/VRTK/Dependencies/Zinnia.Unity/Editor/Data/Collection/ObservableListEditor.cs(17,41): error CS0246: The type or namespace name 'InspectorEditor' could not be found (are you missing a using directive or an assembly reference?)仕方ないので、手っ取り早く以下のファイルを消します
Assets/VRTK/Dependencies/Zinnia.Unity/Editor/Data/Collection/ObservableListEditor.cs正常にインポートできれば、最後にウィンドウが出ます。「Add Input Mappings」を押します。
Oculus Integration は AssetStore からダウンロードしてインポートします。ただし全部必要ないので、「Platform」「VR」だけチェックが入るようにしましょう。
ゲーム画面のセットアップ
とりあえずステージとなるものとプレイヤーとなるものを作っています。
(ここでやっていることはこの通りではなくて良いです。ステージとなるCubeと、プレイヤーとなるオブジェクトを作っていれば何でも良いです)
- GameObject→3DObject→Cube で立方体を作って、Position(0,-1,0), Scale(20,1,20)。名前をFloorにする。
- GameObject→3DObject→Capsule でカプセルを作って、Position(0,0,0), Scale(1,0.5,1)。名前をHeadにする。
- GameObject→3DObject→Cube で立方体を作って、Position(0,0,0.2), Scale(0.7,0.2,0.7)。名前をHMDにして、Headにドラッグアンドドロップ(以下D&D)して子オブジェクトにする.
- GameObject→Create Empty で空オブジェクトを作って、Position(0,0,0)で名前をPlayer にする.
- Head を Player にD&Dして子オブジェクトにする.
こんな感じになるかと思います。(ちなみにこのプレイヤーは次回に使います。この時点では動きません)
VRTKのセットアップ
まずはVRTK で基本となる「TrackedAlias」を設定します。これは様々なデバイスを利用できるようにVRTK側でまとめてくれているものです。これを経由することでいちいちOculusはこれでVIVEはこれでとか設定しなくても良くなるというものです。
Assets/VRTK/Prefabs/CameraRig/TrackedAlias/TrackedAlias.prefab
次にOculus のカメラ
Assets/Oculus/VR/Prefabs/OVRCameraRig.prefab
もシーンにD&Dします。
シーンに元々ある MainCamera は削除しておきましょう。
シーンのルートに空オブジェクト(Position(0,0,0)に)を作って「OVRCameraRigRoot」という名前にして、それに先程の「OVRCameraRig」オブジェクトをD&Dして子オブジェクトにします。
シーン上の「OVRCameraRig」を選択して、OVRManager の項目で以下の項目を変更します。
- Tracking Origin Type → Floor Level
- Reset Tracker On Load → チェック
「OVRCameraRigRoot」に「Linked Alias Association Collection」をアタッチします。そして以下のように設定します。
- OVRCameraRigRoot → PlayArea
- OVRCameraRigRoot>OVRCameraRig → Headset
- OVRCameraRigRoot>OVRCameraRig>CenterEyeAnchor → Headset Camera
- OVRCameraRigRoot>OVRCameraRig>LeftHandAnchor → Left Controller
- OVRCameraRigRoot>OVRCameraRig>RightHandAnchor → Right Controller
TrackedAliasを選択して、Inspector(右の詳細画面)の一番下にある Elements の Size を 1 にします。
Element 0 のところに先程作った「OVRCameraRigRoot」をD&Dします。
デバイス入力
VRTKでデバイス入力をするためにはまず、VRTKの基本となる部分を進めます。
Assets/VRTK/Prefabs/CameraRig/UnityXRCameraRig/InputMappings/UnityXR.OpenVR.RightController.prefab
続けて
- Assets/VRTK/Prefabs/Pointers/ObjectPointer.Curved.prefab
PointerFacadeの各項目に下のようにアサインします。
- TrackedAlias>Aliases>RightControllerAlias → Folow Source
- UnityXR.OpenVR.RightController>Trackpad>Touch[17] → Activation Action
- UnityXR.OpenVR.RightController>Trackpad>Press[9] → Selection Action
一旦このシーンをセーブしておいてください。ファイル名は"Game"とかにしておきましょう。
この段階でOculus GO にビルドしてみましょう。デバイスのタッチ部に触ると、棒から放物線が出るはずです。テレポートシステム
Assets/VRTK/Prefabs/Locomotion/Teleporters/Teleporter.Instant.prefab をシーンにD&Dします。
シーンに配置した「Teleporter.Instant」を選択して、TeleportFacadeに以下のようにアサインします。
- TrackedAlias>Aliases>PlayAreaAlias → Target
- TrackedAlias>Aliases>HeadsetAlias → Offset
- TrackedAlias>Aliases>SceneCameras → Camera Validity
次にパッドボタンを押した時にテレポートが発動できるようにします。
「ObjectPointer.Curved」を選択して、
- 一番下の方にある「Selected (Event)」の「+」ボタンをクリック
- 「Teleporter.Instant」を項目にD&D
- 「No Function」 を 「TeleporterFacade→Teleport」 に変更
これで再度シーンをセーブしてOculus GOでビルドしてみて、ちゃんとテレポートシステムが作動しているか確認してください。
次回は「UnityでマルチプレイVRを一番簡単に実現する方法」を解説していきます。
- 投稿日:2019-05-23T21:28:13+09:00
Unity でVRのテレポートを一番簡単に実現する方法
概要
この記事は、VR上でよく目にする「テレポート」を実現するための手法を書いてあります。ハンドデバイスの先に放物線状に線が表示されて、ボタンを押すとその着地点にワープするというアレです。
順番としては
- VRのためのセットアップ
- VRTKインストール(Oculus Integrationも)
- ゲーム画面のセットアップ
- デバイス入力
- テレポートシステム
とやっていきます。
システム要件
- Unity 2018.3.0f2
- Oculus GO
- Asset Storeで無料で取得できるアセット
- VRTK (4.0.0)
- Oculus Integration (1.36)
Android プラットフォームに切り替え
PlayerSettings の設定
Oculus GO用にPlayerSettingsをいくつか変更します。
XR Settings
Other Settings
- Package Name を適当に自分のドメインを入れる
- Minimum API Level を「Android 4.4 'KitKat' (API level 19)」を選択
Scripting Runtime Version を「.NET 4.x Equivalent」(デフォルトでなっているはず)
VRTKとOculus Integrationのインストール
ハンドデバイスやテレポートシステムを簡易的に使うには「VRTK」が一番簡単で良いだろうということで、VRTKを使うことにします。VRTKは最新版のv4.0.0を使うことにします。ただし、最新版はGitHubにしかない(2019/5/22現在)ので、gitコマンドで持ってくる必要があります。
ターミナル(もしくはcmd)で プロジェクトの Assets/ 以下に行って、以下のコマンドを打ちます
git clone --recurse-submodules https://github.com/ExtendRealityLtd/VRTK.git cd VRTK git submodule init && git submodule updateGitコマンドがない、というかたは
https://github.com/ExtendRealityLtd/VRTK
からダウンロードしてください。その場合は
https://github.com/ExtendRealityLtd/Zinnia.Unity/tree/91b8e16fc366cf6022440ed1efbe7ce4e636eae4
もダウンロードして
Assets/VRTK/Dependency/Zinnia.Unity/
に配置する必要があるので気をつけてください。
なお、現段階(2019/5/22現在)では以下のようなエラーが出てしまいます。
Assets/VRTK/Dependencies/Zinnia.Unity/Editor/Data/Collection/ObservableListEditor.cs(17,41): error CS0246: The type or namespace name 'InspectorEditor' could not be found (are you missing a using directive or an assembly reference?)仕方ないので、手っ取り早く以下のファイルを消します
Assets/VRTK/Dependencies/Zinnia.Unity/Editor/Data/Collection/ObservableListEditor.cs正常にインポートできれば、最後にウィンドウが出ます。「Add Input Mappings」を押します。
Oculus Integration は AssetStore からダウンロードしてインポートします。ただし全部必要ないので、「Platform」「VR」だけチェックが入るようにしましょう。
ゲーム画面のセットアップ
とりあえずステージとなるものとプレイヤーとなるものを作っています。
(ここでやっていることはこの通りではなくて良いです。ステージとなるCubeと、プレイヤーとなるオブジェクトを作っていれば何でも良いです)
- GameObject→3DObject→Cube で立方体を作って、Position(0,-1,0), Scale(20,1,20)。名前をFloorにする。
- GameObject→3DObject→Capsule でカプセルを作って、Position(0,0,0), Scale(1,0.5,1)。名前をHeadにする。
- GameObject→3DObject→Cube で立方体を作って、Position(0,0,0.2), Scale(0.7,0.2,0.7)。名前をHMDにして、Headにドラッグアンドドロップ(以下D&D)して子オブジェクトにする.
- GameObject→Create Empty で空オブジェクトを作って、Position(0,0,0)で名前をPlayer にする.
- Head を Player にD&Dして子オブジェクトにする.
こんな感じになるかと思います。(ちなみにこのプレイヤーは次回に使います。この時点では動きません)
VRTKのセットアップ
まずはVRTK で基本となる「TrackedAlias」を設定します。これは様々なデバイスを利用できるようにVRTK側でまとめてくれているものです。これを経由することでいちいちOculusはこれでVIVEはこれでとか設定しなくても良くなるというものです。
Assets/VRTK/Prefabs/CameraRig/TrackedAlias/TrackedAlias.prefab
次にOculus のカメラ
Assets/Oculus/VR/Prefabs/OVRCameraRig.prefab
もシーンにD&Dします。
シーンに元々ある MainCamera は削除しておきましょう。
シーンのルートに空オブジェクト(Position(0,0,0)に)を作って「OVRCameraRigRoot」という名前にして、それに先程の「OVRCameraRig」オブジェクトをD&Dして子オブジェクトにします。
シーン上の「OVRCameraRig」を選択して、OVRManager の項目で以下の項目を変更します。
- Tracking Origin Type → Floor Level
- Reset Tracker On Load → チェック
「OVRCameraRigRoot」に「Linked Alias Association Collection」をアタッチします。そして以下のように設定します。
- OVRCameraRigRoot → PlayArea
- OVRCameraRigRoot>OVRCameraRig → Headset
- OVRCameraRigRoot>OVRCameraRig>CenterEyeAnchor → Headset Camera
- OVRCameraRigRoot>OVRCameraRig>LeftHandAnchor → Left Controller
- OVRCameraRigRoot>OVRCameraRig>RightHandAnchor → Right Controller
TrackedAliasを選択して、Inspector(右の詳細画面)の一番下にある Elements の Size を 1 にします。
Element 0 のところに先程作った「OVRCameraRigRoot」をD&Dします。
デバイス入力
VRTKでデバイス入力をするためにはまず、VRTKの基本となる部分を進めます。
Assets/VRTK/Prefabs/CameraRig/UnityXRCameraRig/InputMappings/UnityXR.OpenVR.RightController.prefab
続けて
- Assets/VRTK/Prefabs/Pointers/ObjectPointer.Curved.prefab
PointerFacadeの各項目に下のようにアサインします。
- TrackedAlias>Aliases>RightControllerAlias → Folow Source
- UnityXR.OpenVR.RightController>Trackpad>Touch[17] → Activation Action
- UnityXR.OpenVR.RightController>Trackpad>Press[9] → Selection Action
一旦このシーンをセーブしておいてください。ファイル名は"Game"とかにしておきましょう。
この段階でOculus GO にビルドしてみましょう。デバイスのタッチ部に触ると、棒から放物線が出るはずです。テレポートシステム
Assets/VRTK/Prefabs/Locomotion/Teleporters/Teleporter.Instant.prefab をシーンにD&Dします。
シーンに配置した「Teleporter.Instant」を選択して、TeleportFacadeに以下のようにアサインします。
- TrackedAlias>Aliases>PlayAreaAlias → Target
- TrackedAlias>Aliases>HeadsetAlias → Offset
- TrackedAlias>Aliases>SceneCameras → Camera Validity
次にパッドボタンを押した時にテレポートが発動できるようにします。
「ObjectPointer.Curved」を選択して、
- 一番下の方にある「Selected (Event)」の「+」ボタンをクリック
- 「Teleporter.Instant」を項目にD&D
- 「No Function」 を 「TeleporterFacade→Teleport」 に変更
これで再度シーンをセーブしてOculus GOでビルドしてみて、ちゃんとテレポートシステムが作動しているか確認してください。
次回は「UnityでマルチプレイVRを一番簡単に実現する方法」を解説していきます。
- 投稿日:2019-05-23T12:52:20+09:00
Unity+Oculus GO メモ
準備
下記、URLの記事を参考にすればできるので省略、PlayerSetting周りまではこの記事で可能
AssetStoreからOculuc Integration を入れる所までやっておく
(バージョンは1.37)●参照URL
https://framesynthesis.jp/tech/unity/oculusgo/テストシーン作成
参考URL:http://kan-kikuchi.hatenablog.com/entry/Oculus_Integration_2
参照URLを元に作成していく、Ver1.37のPrefabには
TrackedRemote
がいないので焦った。代わりを探してみたところ、
OVRControllerPrefab
に名前が変わっていたので、
こちらを使う。下図のような配置にしてOculusGoにビルドするとGo内でコントローラーが表示された。使用するPrefabは
VR⇒Prefabs⇒OVRCameraRig
VR⇒Prefabs⇒OVRControllerPrefab
- 投稿日:2019-05-23T12:35:13+09:00
[Unity] ComputeShaderでモブを動かす【その3:車に道路上を走らせる+パーティクル版】
経緯
コンピュートシェーダー(ComputeShader)を学ぶため、自動車を動かす交通シミュレーターもどきを作ってみようと思いました。個々の自動車がそれぞれ衝突を回避しつつ適切な経路で目的地に移動できるようになるのが目標です。
前回作成した車用ポリゴン を 前々回作った道路 の上で走らせてみました。
また前回、ジオメトリシェーダーとコンピュートシェーダーがMacでは同時に使えないことが判明したので、車をパーティクルシステムで代用する方法も試してみました。
◀【その2:車を生成する】
ジオメトリシェーダー版
一旦Macのことは置いておいて、ジオメトリシェーダーで描画する車との組み合わせをやってみました。
C#側実装
そろそろ実装も複雑になってきたし、張り付けても見づらくなってきたので GitHub にリポジトリを作りました。実際のソースはこちらをご覧ください。
(初回からの分も入ってます)
https://github.com/ShinodaNaoki/learnComputeShaderCarRepository
ComputeBuffer を直接コントローラーから弄るのも煩雑なので、 CarRepository なるクラスを作って管理させることにしました。同時にこれは ComputeBuffer で使う struct をラップした Car オブジェクトとしてコントローラー側から扱えるようにしたものです。
紆余曲折ありましたが、車のstructについて、色やサイズなどの(一度車を生成したら)不変の静的情報と、速度や向きなどの動的情報に分割しました。
こうすることで、 ComputeBuffer.GetData() の負荷が幾分抑えられると考えています。ICarStaticInfo.cspublic interface ICarStaticInfo { /// <summary> /// サイズ /// </summary> Vector3 size { get; } /// <summary> /// 色 /// </summary> Color color { get; } }ICarDynamicInfo.cspublic interface ICarDynamicInfo { /// <summary> /// 座標 /// </summary> Vector2 pos { get; set; } /// <summary> /// 向き(進行方向) /// </summary> Vector2 direction { get; set; } /// <summary> /// 速度 /// </summary> float velocity { get; set; } }CarTemplate
車種ごとの色と巡航速度の雛形情報を保持するクラスです。CarRepositoryでは車種を指定するだけで、この雛形情報を元に ICarStaticInfo, ICarDynamicInfo を生成します。
RoadPlane
道路上を走らせるため、車の初期配置情報を持つ EntryPoint なるものを提供するようにしました。
RoadPlane02.cspublic class EntryPoint { /// <summary> /// 座標 /// </summary> public readonly Vector2 pos; public readonly Vector2 dir; public EntryPoint(Vector2 pos, Vector2 dir) { this.pos = pos; this.dir = dir; } } public class RoadPlane02 : MonoBehaviour { // ..中略.. private Vector2 ToWorldPos(Vector2 local) { // Planeのmeshサイズは10なので、なんで20なのかよくわからないけど、ぴったり合う var scale = 20f * transform.localScale.x / MAP_SIZE; // Plane が(0,0)に配置されてる前提だと、座標の起点は -MAP_SIZE/2 にある var half = MAP_SIZE / 2; return new Vector2((local.x - half) * scale, (local.y - half) * scale); } private void InitializeEntryPoints(Road02[] roads) { entryPoints = new List<EntryPoint>(); foreach(Road02 road in roads) { var dir = (road.pos2 - road.pos1).normalized; var cross = new Vector2(-dir.y, dir.x); // dirと直交するベクトル var offset = cross * LANE_WIDTH / 2; var step = cross * LANE_WIDTH; // 上りレーン for(int i = 0; i< road.lanes.x; i++) { entryPoints.Add(new EntryPoint(ToWorldPos(road.pos1 + offset), dir)); offset += step; } cross *= -1; dir *= -1; offset = cross * LANE_WIDTH / 2; step = cross * LANE_WIDTH; // 下りレーン for (int i = 0; i < road.lanes.y; i++) { entryPoints.Add(new EntryPoint(ToWorldPos(road.pos2 + offset), dir)); offset += step; } } }今回も、道路は直線オンリーなので、一度 EntryPoint に車を置いたら後は道をはみ出る心配もなく直進させるだけです。
CarsController
ややこしいところは CarRepository に委譲したので、比較的すっきりしてます。
CarsContorller02public class CarsController02 : MonoBehaviour { // ..中略.. CarRepository<Car02s,Car02> factory; void OnDisable() { // コンピュートバッファは明示的に破棄しないと怒られます factory.ReleaseBuffers(); } void Start() { material = new Material(carShader); InitializeComputeBuffer(); } void Update() { carComputeShader.SetBuffer(0, "CarsStatic", factory.StaticInfoBuffer); carComputeShader.SetBuffer(0, "CarsDynamic", factory.DynamicInfoBuffer); carComputeShader.SetFloat("DeltaTime", Time.deltaTime); carComputeShader.Dispatch(0, factory.Length / 8 + 1, 1, 1); } void InitializeComputeBuffer() { factory = new CarRepository<Car02s,Car02>(MAX_CARS, CarTemplate02.dictionary); factory.AssignBuffers(); RoadPlane02 roadPlane = GetComponent<RoadPlane02>(); var entries = roadPlane.EntryPoints; // 配列に初期値を代入する for (int i = 0; i < MAX_CARS; i++) { var entry = entries[ Random.Range(0,entries.Count) ]; factory.CreateRandomType(entry.pos, entry.dir); } factory.ApplyData(); } void OnRenderObject() { // 車データバッファをマテリアルに設定 material.SetBuffer("CarsStatic", factory.StaticInfoBuffer); material.SetBuffer("CarsDynamic", factory.DynamicInfoBuffer); // レンダリングを開始 material.SetPass(0); // オブジェクトをレンダリング Graphics.DrawProcedural(MeshTopology.Points, factory.ActiveCars); } }シェーダー実装
シェーダーは道路用は前々回のまま、車用もおおむねそのままです。
ただ、structを静的情報と動的情報に分割したので、その対応が入ってます。
詳しくは GitHub 上でご確認ください。DrivingComputeShader02.compute
結果1 (road03.scene)
冒頭にも貼りましたが、こんな感じです。
車はランダムに車種を変えてますが、車種ごとに色と速度が決まっているので整列してるように見えますね。(実際は同じ車種同士、同じ位置に数千台重なっています!)パーティクル版
次に、車をパーティクルシステムで代用する方法を試してみました。
と言っても凝ったのは面倒なの矢印のテクスチャをQuadで表示するだけにします。
Emissionはコードで制御するので、設定値は0だけどチェックは入れておかないとダメみたい。
C#側実装
CarController
最初はスクリプト(C#)側でパーティクル設定するようにしてみました。
CarsController03.csprivate void cpu_UpdateParticles() { var cars = factory.GetCars(); var emitter = particlesSystem.emission; emitter.rateOverTime = cars.Length; int numParticlesAlive = particlesSystem.GetParticles(particles, cars.Length); int i = 0; foreach (var car in cars) { var par = particles[i]; var pos = car.Dynamic.pos; par.position = new Vector3(pos.x, 0.1f, pos.y) * 0.5f; par.startSize3D = car.Static.size; par.startColor = car.Static.color; var dir = car.Dynamic.direction; par.rotation3D = new Vector3(1, 1 - dir.y, dir.x) * 90; particles[i] = par; i++; } while(i < numParticlesAlive) { particles[i++].startLifetime = 0; } particlesSystem.SetParticles(particles, cars.Length); }結果2 (road04.scene)
これでも動きますが、パフォーマンスが気になったので調べてみました。パフォーマンス改良
パフォーマンス比較1
一万台の車を動かした状態です。
【ジオメトリシェーダー版】
【パーティクルCPU設定版】
あからさまですね。毎フレーム10000回ループしてるんだから、当然ですか。
GPUの負荷増加はパーティクルとジオメトリシェーダーの違いだと思います。面倒くさがって透明ありテクスチャを使ってますが、矢印程度ならmeshにしたほうが幾分マシになったかもしれませんね。なお、ComputeBuffer.GetData() で GPU側で得た結果を取得するだけなら、比較的高速(下記の図で 1.03ms と 0.60ms)なので毎フレームやっても問題はなさそうでした。
これは静的情報と動的情報に分ける前の状態なので、最終的にはさらにこれの約半分になってます。CarController改良
ParticleSystem.Particle も struct なので、そのまま ComputeBuffer に使えるんじゃないか?
そうしたらGPU側でパーティクルを設定できて、パフォーマンス大幅向上できるんじゃないか?
と考えました。実際やってる人がいました!
https://github.com/sugi-cho/Unity-ParticleSystem-GPUUpdateただ、Unityバージョンの違いのせいか、この人のcgincでは上手く動かなかったので、即席で実際の構造を出力するコードを作って調べてみました。
こんな感じで出力されました。どうやら最後のm_Flagsが足りなかった模様。
早速これを使って試してみました。CarsController03.csprivate void gpu_UpdateParticles() { var cars = factory.GetCars(); var emitter = particlesSystem.emission; emitter.rateOverTime = cars.Length; int numParticlesAlive = cars.Length; particlesSystem.GetParticles(particles, cars.Length); particleBuffer.GetData(particles, 0, 0, numParticlesAlive); particlesSystem.SetParticles(particles, numParticlesAlive); }パーティクル対応シェーダー
DrivingComputeShader03.compute// 出力先のパーティクルバッファ RWStructuredBuffer<Particle> Particles; // Particle構造体へコピー inline void PrepareParticle(in CarS carS, in CarD carD, inout Particle p) { p.m_Position = half3(carD.pos.x, 0.1, carD.pos.y) * 0.5; p.m_StartSize = carS.size; p.m_StartColor = Particle_ColorToUint(carS.col); const float radian90 = 3.14159 / 2; p.m_Rotation = half3(1, 1 - carD.dir.y, carD.dir.x) * radian90; // 角度単位がC#と違う p.m_StartLifetime = 1; p.m_Lifetime = 1; } [numthreads(8,1,1)] void CSMain (uint3 id : SV_DispatchThreadID) { CarD carD = CarsDynamic[id.x]; // それぞれの位置情報に移動ベクトルを加算 (0.28はkm/hをm/sに変換する係数) carD.pos += carD.dir * carD.velocity * DeltaTime * 0.28; CarsDynamic[id.x] = carD; PrepareParticle(CarsStatic[id.x], carD, Particles[id.x]); }ところで、シェーダーの関数はCみたいに利用より先に定義記述しないとダメみたい?
パフォーマンス比較2
これも一万台の車を動かした状態です。
見ての通り、かなり効果がありました!
縦軸のスケールが約半分くらいになってるので、一見した以上に高速化されてます。流石にジオメトリシェーダー版ほどではないですが、近いレベルになったかと思います。
これ実装した後で、パーティクルにもGPUインスタンシング機能があるらしいことを知ったのですが、既に本来の目標からだいぶ横道に逸れているので、これ以上の深入りはしないでおきます。?
気づいたこと・まとめ
(※まだ大目標は完了じゃないですが)
複数のComputeBufferを使ったり、GPUで計算した結果をCPUで使ったり、別のシェーダーに渡したりする方法がわかった。当たり前だが、SetDataした後の配列を保持しておいてもGetDataしない限り、GPU側の変更は反映されない。
Macでは一部機能に制限があることを知った。
ComputeBuffer でやりとりする構造体はC#:HLSL間で矛盾があってもチェックされない。例えば float3 が float4 になってたりすると、一見訳のわからないことが起きたりする。Particleの構造体のメンバー数が間違ってた時もおかしな挙動になりました。
ComputeBuffer は確保した数だけ埋める必要がある。あとでセット数を減らすと、以前のデータがGPU上に残ってる(少なくともUnityの再生・停止ぐらいでは消えない)模様。ちゃんとReleaseするのはもちろん、Bufferサイズを適切に管理する必要がある。
- 投稿日:2019-05-23T10:07:09+09:00
Unityからブラウザを起動
// Unityからブラウザを起動
Application.OpenURL(url);
- 投稿日:2019-05-23T01:15:21+09:00
HoloLensアプリ開発手順 備忘録 (ボタン機能(Compound Button、Interaction Receiverなど))
はじめに
すでに沢山の方が開発手順の記事を記載してますが、個人の知識整理として投稿します。
HoloLens用アプリにおけるボタン追加、ボタン押下による処理について記載しています。
※2019/05/22時点の記事になります。1.シーンにHolographic Buttonを追加する
2.InteractionReceiverを継承したスクリプトをアタッチしたオブジェクトを追加する
3.イベント処理を追加する環境
Unity:2017.4.27f1 Personal(64bit)
MRTK:HoloToolkit-Unity-2017.4.3.0-Refresh.unitypackageシーンにHolographic Buttonを追加する
ProjectウインドウのHoloToolkit内のHolographicButtonをHierarchyウインドウに追加する。
※HolographicButtonは HoloToolkit/UX/Prefab/Buttons/の配下にある。HolographicButtonにはCompound Buttonというスクリプトがアタッチしている。このCompound Buttonがボタンそのものを表すスクリプトで、このスクリプトによってボタンの状態がイベントとして送信される仕組みとのこと。Cubeなどのオブジェクトにこのスクリプトをアタッチすることで、色々なオブジェクトを「ボタン」として扱うことができる。
※Compound Buttonスクリプトは HoloToolkit/UX/Scripts/Buttons/の配下にある。InteractionReceiverを使用してボタン押下イベントを処理する
HolographicButtonから送信されるイベントを処理するには、InteractionReceiverを継承したスクリプトをアタッチしたオブジェクトを作成する。
1.Hierarchyウインドウに空のGameObject(※)を追加する。
2.追加した空のGameObjectを選択し、Inspectorウインドウで「Add Component」で、新しいスクリプトを追加する。
3.追加したスクリプトを編集する。using UnityEngine; using HoloToolkit.Unity.Receivers; public class Receiver : InteractionReceiver { // InteractionReceiverを継承させる }4.InteractionReceiverを追加することで、UnityのInspectorウインドウのスクリプトに「Interactables」という項目が追加される。
ここにはInteractionReceiverで受け取るイベントの送信元GameObjectを設定する。デフォルトではSize(送信元GameObjectの個数) : 0 となっている。この値を変更することでElement 0 ~ n が追加で表示されるようになる。このElementにHierarchyウインドウから送信元GameObjectをドラッグアンドドロップすることで設定する。5.イベント発生時の処理を追加する。イベント発生時の処理は以下の様にinteractionReceiverで追加される各種メソッドをオーバーライドすることで実装する。
public class Receiver : InteractionReceiver { // メソッドをオーバーライドして処理を追加する protected override void InputClicked(GameObject obj, InputEventData eventData) { base.InputClicked(obj, eventData); // どのオブジェクトからのイベントかはobj.nameで判断できる switch (obj.name) ... } }今回はボタンのクリックイベントを使用したいので、InputClicked()メソッドをオーバーライドする(他にもFocusEnter()など様々なイベントが用意されている)。どのオブジェクトからのイベントかは引数objのフィールドobj.nameを参照することで判別できる。
使用例
今回はボタンを押下すると、キューブの色と、3Dテキストの文字が変わるサンプルを作成して動作を確認。
※キューブと3Dテキストを操作するためにプロパティとしてCube、DisplayMessageを持たせています。
using UnityEngine; using HoloToolkit.Unity.Receivers; using HoloToolkit.Unity.InputModule; public class Receiver : InteractionReceiver { public GameObject Cube; private Renderer cubeRenderer; public GameObject DisplayMessage; private TextMesh dispMsgText; private bool flg = false; // Use this for initialization void Start() { cubeRenderer = Cube.GetComponent<Renderer>(); dispMsgText = DisplayMessage.GetComponentInChildren<TextMesh>(); } protected override void InputClicked(GameObject obj, InputClickedEventData eventData) { base.InputClicked(obj, eventData); switch (obj.name) { case "ButtonColorChange": // フラグ(true/false)の値でキューブの色、3Dテキストを赤・緑で切り替える if (flg) { cubeRenderer.material.color = Color.green; dispMsgText.text = "Green!"; flg = false; } else { cubeRenderer.material.color = Color.red; dispMsgText.text = "Red!"; flg = true; } break; default: break; } } }実行結果
ボタン押下でキューブの色と、3Dテキストが交互に切り替わった。