コンテンツにスキップ

描画が届くまで(4段)

このチュートリアルで作るもの

Section titled “このチュートリアルで作るもの”

層を1つずつ足しながら、描画が SteamVR のオーバーレイに届くまでを、手を動かして確かめます。OVR 層だけでオーバーレイを出すところから始め、描画層、エンジン層、フレームワーク層の順に足します。OVR 層と描画層は互いを参照しないので、それぞれを試してから、両方を使うエンジン層へ進みます。最後に、フレームワーク層で同じ描画の流れを組みます。

段 足す層 目に見える結果
1 OVR 層 HMD の 1 m 前のオーバーレイに、png の絵が出る
2 描画層 自分で組んだ Layer ツリーが、png の画像になる
3 エンジン層 同じ Layer ツリーが、GL テクスチャ経由でオーバーレイに出る
4 フレームワーク層 ウィジェットで書いた色の四角が、ダッシュボードに出る

最後の段で、フレームワークの FloatSodaApp が、段3で自分が書いた仕事と同じことをしているのが分かります。

段1 OVR OpenVR を呼ぶ 参照: なし
段2 Rendering Layer ツリーを絵にする 参照: なし(SkiaSharp のみ)
段3 Engine GL テクスチャで SteamVR に出す 参照: Abstractions、OVR、Rendering
段4 FloatSoda ウィジェットから Layer ツリーを作る 参照: Abstractions、Engine、Rendering

各段の終わりに、確認の項目と、選択式のクイズが1問あります。仕組みの説明は、層ごとの解説に分けています。

次の3つを用意します。

  • .NET の SDK
  • FloatSoda を clone したフォルダー
  • SteamVR と HMD(段2だけは、HMD なしで進められる)

4段のコードは、1つの作例 samples/FloatSoda.Samples.RenderPath にまとまっています。コードの全文は、このページに載せません。作例のファイルを読みながら進めます。リポジトリのルートで、--step N を付けて段を選びます。準備の詳細は 開発環境を用意する にあります。SteamVR が起動できない、HMD がつながらない場合の直し方は、ここでは扱いません。

1. OVR 層だけでオーバーレイを出す

Section titled “1. OVR 層だけでオーバーレイを出す”

段1は、FloatSoda のほかの層を使わず、FloatSoda.OVR だけで、png の画像をオーバーレイとして HMD の前に出します。出す png は段2が作るので、先に次のコマンドを1回動かします。png は、カレントディレクトリの my_layer_tree.png に保存されます。段2の内容は、次の節で説明します。

ターミナルウィンドウ
dotnet run --project samples/FloatSoda.Samples.RenderPath -- --step 2

SteamVR を起動してから、動かします。HMD の中で、オーバーレイを探します。

ターミナルウィンドウ
dotnet run --project samples/FloatSoda.Samples.RenderPath -- --step 1

別の png を出したいときは、-- --step 1 <png のパス> の形でパスを渡します。省略すると、カレントディレクトリの my_layer_tree.png を使います。

作例は、DeviceTrackedOverlay を HMD から 1 m 前の位置に置き、overlay.Texture.FromFile で png を渡します。

できた? HMD の 1 m 前に付いたオーバーレイに、段2で作った png の絵が出た。

できなかったら

Init_HmdNotFound の例外が出る場合や、オーバーレイが見つからない場合は、SteamVR の状態と HMD の接続を確かめます。原因の見分け方は、OVR 層 の「つまずきやすい点」にあります。段2の png がない場合は、段2に戻ります。

クイズ 段1のオーバーレイの絵は、だれが描いたでしょうか。

  • (a) Skia
  • (b) OpenVR が png を読んで表示した
  • (c) OpenGL
答え

(b) です。overlay.Texture.FromFile が、OpenVR の SetOverlayFromFile を呼びます。FloatSoda は画像のパスを渡すだけで、描いているのは OpenVR です。

OVR 層の型の詳細は、OVR 層 にあります。

2. Layer ツリーを組んで png に描く

Section titled “2. Layer ツリーを組んで png に描く”

段2は、HMD も SteamVR も使わずに、自分で組んだ Layer ツリーを png にします。FloatSoda.Rendering だけを使います。Layer ツリーを組んで png にする基本は、samples/FloatSoda.Samples.PaintingSample のコードで読めます。このページでは、同じ説明を繰り返しません。段2の作例は、その流れを最小にしたものです。

ターミナルウィンドウ
dotnet run --project samples/FloatSoda.Samples.RenderPath -- --step 2

コンソールに、png の保存先のパスが出ます。保存先を変えたいときは、-- --step 2 <png の保存先> の形で渡します。省略すると、カレントディレクトリの my_layer_tree.png です。保存される絵は、白っぽい地に緑の四角です。

できた? 保存先のパスが出て、その png を画像ビューアーで開くと、白っぽい地に緑の四角が見えた。

できなかったら

png が空になる、真っ黒になる場合は、作例の中で PictureLayer に Picture を設定したか、描画の範囲(BeginRecording に渡した矩形)がサイズと合っているかを確かめます。仕組みは 描画層 の「CPU の Bitmap に描く」にあります。

クイズ LayerBitmapRenderer.Render が、root に対して最初に呼ぶのは、どれでしょうか。

  • (a) Paint
  • (b) Layout
  • (c) Clone
答え

(b) です。内部の LayerRenderer.Render が、root.Layout(context) を呼んでから root.Paint(context) を呼びます。

Layer の種類と Clone の共有範囲は、描画層 にあります。合成の Layer を同じ絵に使って見比べる実験は、実験: 描画層 です。

3. Layer ツリーをエンジン層に差し込んで SteamVR に出す

Section titled “3. Layer ツリーをエンジン層に差し込んで SteamVR に出す”

段3は、段2で組んだ Layer ツリーを、png ではなく GL テクスチャとしてオーバーレイに出します。段1の FromFile の代わりに、エンジン層の RenderPostTaskRunner、GLView、Renderer、OverlayWindow を使います。FloatSoda.Engine は、フレームワーク(FloatSoda)を参照しないので、フレームワークなしで書けます。

ターミナルウィンドウ
dotnet run --project samples/FloatSoda.Samples.RenderPath -- --step 3

作例のコードの要点は3つです。

  • RenderPostTaskRunner が、描画スレッドを作る。Start のあと、PostTask に渡した処理が、描画スレッドで実行される
  • GLView、Renderer、OverlayWindow は、描画スレッドの中で作る。OverlayWindow は、Present のたびに GL テクスチャを OpenVR に渡す
  • FramePacer(60) が、描画ループの周期を 60 回/秒にそろえる

作例は、GL のリソースの後始末を省いた最小のコードです。

OverlayWindow は、エンジン層の型です。フレームワーク(FloatSoda)にも、同じ名前の FloatSoda.Widgets.OverlayWindow があります。同じプロジェクトに両方を入れるときは、フレームワークと同じように、using EngineOverlayWindow = FloatSoda.Engine.OverlayWindow; と別名を付けます。

できた? 段2と同じ絵が、FromFile を呼ばずに、オーバーレイに出た。

できなかったら

絵が出ない、例外が出る場合は、GLView を描画スレッド以外で作っていないかを、作例のコードで確かめます。GL を使う処理は、PostTask に入れます。スレッドの制約は、エンジン層 の「描画スレッドは、キューのタスクを実行する」にあります。

クイズ GL テクスチャのハンドルを Texture_t に包んで、オーバーレイに渡すのは、どの型でしょうか。

  • (a) GLView
  • (b) Renderer
  • (c) OverlayWindow
答え

(c) です。OverlayWindow.Present が、Renderer.Render のあとに、テクスチャのハンドルを Texture_t に包み、Overlay.Texture.FromTexture_t で OpenVR に渡します。

エンジン層の詳細は、エンジン層 にあります。

段4は、段3で手書きしたものを、フレームワークに任せます。ウィジェットで色の四角を書き、ダッシュボードに出します。

SteamVR を起動してから、動かします。HMD の中でダッシュボードを開き、RenderPath のタブを選びます。

ターミナルウィンドウ
dotnet run --project samples/FloatSoda.Samples.RenderPath -- --step 4

段3で書いた処理は、フレームワークの次の箇所が受け持っています。

段3で自分が書いたもの フレームワークの担当
OVRApplication を作る FloatSodaApp の起動処理
OverlayIdentity と DeviceTrackedOverlay を作る CreateWindow が作成を予約し、初期化処理が描画スレッドでダッシュボード用のオーバーレイを作る
RenderPostTaskRunner と FramePacer を作って Start する AddFloatSoda が登録し、FloatSodaApp が持って開始する
PostTask で GLView、Renderer、OverlayWindow を作る WidgetBinding.EnsureInitialized
Layer ツリーを手で組む BuildOwner がビルドを進め、RenderPipeline がレイアウトと描画を処理して Layer ツリーを更新する
window.Resize(size) WidgetBinding.DrawFrame が、レイアウト結果のサイズが変わったときに投入する
PostRender(window, layer) WidgetBinding.DrawFrame が、Layer を複製して PostRender に渡す
Enter キーで待ち、Stop を呼ぶ ホスト(host.RunAsync)と、FloatSodaApp の終了処理

できた? ダッシュボードに、色の四角が出た。

できなかったら

起動で例外が出る場合は、利用者向けのガイドの エラーを読んで直す を確かめます。四角が出ない場合は、反応しないときに切り分ける を確かめます。

クイズ 段3で自分が書いた PostRender の呼び出しを、フレームワークでは、何が呼んでいるでしょうか。

  • (a) FloatSodaApp.CreateWindow
  • (b) WidgetBinding.DrawFrame
  • (c) FramePacer
答え

(b) です。WidgetBinding.DrawFrame が、RenderView.Layer を複製して、PostRender で描画スレッドに渡します。

フレームワークの詳細は、フレームワーク層 にあります。

目的に合わせて、続きを選びます。