This document is about: FUSION 2
SWITCH TO

Texture Drawing

Fusion Industries プロトタイピングアドオン

このアドオンは、テクスチャ変更を同期する方法を提供します。

Texture drawing example

概要

このアドオンのロジックは、描画点の詳細(位置・色など、DrawinPointを参照)をユーザー間で共有するものです。

サンプルのペンが含まれ、描画可能な固定の平面を検知すると点を描画します(BlockingContactをご覧ください)。

機能:

  • 同時編集に対応:複数のペンで同じ平面に同時に描画できます。
  • 描画の補間:リモート上で表示される描画は補間が行われるため、NetworkTransformで位置が補間されているペンと同期されます。
  • 途中参加者に対応:FusionのストリーミングAPIを使用して、描画が行われた後にルームに接続したプレイヤーにも、完全な描画が共有されます。
  • シェーダーベースの描画:テクスチャー編集のパフォーマンスの影響を抑えます。同時編集でパフォーマンスをスケールさせるため、描画バジェットシステムも追加されています。
  • Fusionのメモリアロケーションコストの抑制(描画に大きなネットワーク配列は不要):各描画は点の総数を同期します。同期は、最新の点のネットワーク配列を持つ「ペン」を通して行われ、途中参加者の対応はストリーミングAPIで行われます。
Texture drawing overview

状態権限

各ペンと平面のロジックは、どちらもNetworkBehaviourで、潜在的にペンと描画対象の状態権限者が異なる可能性があります。

  • ペンの状態権限者は、ペンを掴んでいる人
  • 描画対象の平面の状態権限者は、複数のユーザーが同時に描画している際には、描画しているユーザーではない場合がある

描画ステップ

Texture drawing overview

ユーザーがシーンに接続した最初の初期化時に、ユーザーが(最初からいない)途中参加者なら、

  1. TextureDrawingは描画情報の不足を検知して、他のユーザーに不足分をリクエストする
  2. 受信した点をローカルに保存して(途中参加者を参照)、次のRender()呼び出し内でそれらを描画する(パフォーマンスの問題を避けるため、1フレームで描画される最大の「バジェット」が設定され、途中参加者が大量の描画を復元する際にフリーズすることを防ぐ)

ペンによる描画が始まると、
3. ペンは、描画点を追加する平面を検知して(TexturePenを参照)、その情報を得る(座標など)
4. ペンの直近の描画点は、ネットワークのリングバッファに保存されるため(TextureDrawerを参照)、すべてのユーザーは描画点が追加されたことを認識できる
5. ペンは、点情報を直接ローカルのテクスチャに共有する
6. そして、次のRender()内で、テクスチャは即時に更新される

そして、すべてのクライアント上の描画対象の平面では、
7. ペンが新しい描画点を追加したことを検知する
8. ローカルキャッシュに新しい描画点を保存する(TextureDrawingを参照)
9. 次のRender()内で、ペンの補間係数に基づき(補間を参照)、実際のテクスチャに描画する(TextureSurfaceを参照)

テクスチャ描画

このアドオンは、テクスチャを編集する2つの方法を提供しています。

  • デフォルトの方法は、Shader Graphで作成された特別な描画シェーダーを使用します。URPとHDRPレンダーパイプラインに互換性があります。この方法は、高解像度テクスチャに最適です。
  • 代替方法は、サードパーティー製のビットマップ描画APIのProtoTurtle.BitmapDrawingを使用して、テクスチャを編集します。

シェーダーベースの描画

LinePainterシェーダーは、テクスチャ上に線を描画できます。点と描画される線との距離を決定するSubgraphのLineSDKを含んでいます。

描画ロジックは、以下の通りです。

  • 描画対象の平面のマテリアルにRenderTextureを使用する
  • 線を描画する必要が出るたびに、Graphics.Blitを使用して現在のRenderTextureと線の詳細をシェーダーに与えて、結果を保存する
Texture drawing shader logic

クラス詳細

TexturePen

TexturePenは(BlockableTipコンポーネントがある)ペンに付いていて、BlockingSurfaceコンポーネントとの接触の検知を試みます。またこれは、TextureDrawingコンポーネントを持ちます。

接触が検知されると、TextureDrawerAddPointWithThrottleが呼び出され、点を追加するように依頼します。

次のFixedUpdateNetwork()内で、TextureDrawerは依頼されたすべての点についてAddDrawingPointを呼び出します。

  • ネットワークのリングバッファ(ネットワーク配列)に点を保存することで、すべてのユーザーはそれらの点を受信する
  • すぐさまローカルのTextureDrawingに点を渡して、次のRender呼び出しでテクスチャに描画される

リモートクライアントでは、OnNewEntriesがトリガーされて新しい点がリングバッファに追加されたことが通知され、ローカルのTextureDrawingにそれらを保存します。ペンでは、点の位置インデックス(過去に描画された点の数)がデータに追加されて、描画の補間が行われます。

TextureDrawer

これは、DataSyncHelpersアドオンRingBufferSyncBehaviourクラスを継承しています。

ペンと描画対象の平面との接触が検知された時に、テクスチャを編集する描画点を記録するために使用されます。
TexturePenによってAddDrawingPoint()メソッドが呼び出され、最終的にTextureSurfaceに置かれるDrawingPointをリストに追加します。

ローカルで描画点を追加した時か、リモートプレイヤーの新しい点がRingBufferSyncBehaviourOnNewEntriesコールバックで通知された時、TextureDrawingに新しい点を追加するようにリクエストします。

備考:

  • 描画者のリングバッファがすぐ埋まってしまうと、データが失われる可能性があります(現在の設定と通常のネットワーク環境では発生しません)。これを回避するには、TexturePen.maxPointInsertionPerSecondsで送信する点を制限するか、RingBufferSyncBehaviour.BUFFER_SIZEをふやしてください。

TextureDrawing

新しい描画点が追加されて、TextureDrawerがリクエストした線がまだ完了していない場合、TextureDrawingは、前の点と新しい点との間に線を作成します。

TextureDrawingは、TextureDrawer毎に最新の点のキャッシュを持っているため、複数のTextureDrawerが並列に描画を行うことができます。
参照されるTextureSurface上のTextureDrawingは最終的にDraw()メソッドを呼び出して、点を追加したり線を描画したりします。

備考:

補間とそれに関連する描画バジェットは、次のフレームまで遅延することがあります。

補間

通常、リモートユーザー上のTexturePenの位置は(NetworkTransformの補間の実装によって)補間されます。つまり見えている位置はわずかに「過去」のものであるということです。

もしリモートユーザーの描画について、すべての存在する点を即時に描画した場合、実際のペンの動きよりも先に線が表示されることになります。

そのため、リモートユーザーのペンの位置を補間するのと同じ方法で、表示される描画点のインデックスを補間することが望ましいです。

そうするために、TextureDrawingに保存されているすべての点は、位置インデックス(過去に描画された点の数)を保存します。
この方法によって、Render()呼び出し内で補間する位置インデックスを取得することができるようになり、インデックスより低い値の点は描画して、そうでないものは少し待つことができます。

Texture drawing interpolation

描画バジェット

途中参加者が接続した時や、ローカルで再描画を行う時(例えば背景色を変更した時など)は、大量の描画コールがトリガーされ、TextureSurfaceGraphics.Blitから大量のシェーダーが実行されるため、パフォーマンスに影響する可能性があります。

これを防ぐため、TextureDrawing全体で1フレーム辺りの最大描画数を決めます。これは、globalFrameLineDrawingBudgetの値を編集することで、コードから変更できます。

バジェットのロジック:

  • 描画が必要なTextureDrawingコンポーネント間のバジェットを共有する
  • 各描画について、TextureDrawer間でバジェットを共有し、複数のペンが同時に点を追加していく

途中参加者

途中参加者は、Fusion 2のストリーミングAPIを使用して、開始時から追加された描画点の完全なリストを受信します。

TextureDrawingStreamingSyncBehaviourを継承して、そのロジックを変更しています。

StreamingSyncBehaviourはリアルタイムでデータを送信できるので、これで途中参加者に描画点を共有します。

  • DrawingPointsデータはローカルキャッシュに保存される(TextureDrawerのネットワーク変数によって、リアルタイム通信が制御されている)
  • 途中参加者は、データ受信ロジックを変更して、TextureDrawerのネットワーク変数を通して既に受信したデータ(通常は即時)と、受信に数フレームかかる完全なデータのキャッシュを混ぜ合わせます

データのマージにはリコンシリエーションが必要です。完全なキャッシュの最後の点は、先にTextureDrawerから受信した最初の点である(ストリーミングAPIから与えられる完全なキャッシュのバックアップと、TextureDrawerのネットワーク変数から与えられるリアルタイムのデータとの間で、同じ点が含まれる)可能性があり、それらの点が未完了の線に関連している可能性があります。
この問題を解決する簡単な方法は、完全なキャッシュの全ての点を精査して、描画が完了していないすべての線を追加することです。
TextureDrawerデータはより最新のものとなり、いずれにせよ未完了の線を完了させます。

Texture drawing overview

備考:

  • 問題を解決する他の方法として、完全なバイト数を使用して、重複した点を正確に検知します
  • このアプローチは、同時に同じピクセルに描画を行った際に、異なる結果になる可能性があります。これが問題になる場合、StreamingSyncBehaviourのかわりにRingBufferLosslessSyncBehaviourのサブクラスを使用します。描画対象の状態権限を持たない描画者は一時的な線を描画して、正しいTextureDrawingの状態を受信した後、実際の線を描画します。

TextureSurface

TextureSurfaceRedererコンポーネントを参照し、テクスチャ編集用のユーティリティメソッド(テクスチャの初期化・テクスチャの色変更・点の描画・線の描画)を含みます。
そのため、このクラスはネットワーク化されていません。

このクラスは、IRenderTextureProviderインターフェース(DataSyncHelpersアドオン)を実装しています。外部からテクスチャが編集されるとonRedrawRequiredイベントが発生し、TextureDrawingはすべての点の再描画を購読します。

DrawingPoint

このクラスは、平面上の描画点(位置・色・強さ・参照ID)を定義します。referenceIdは、対象のTextureDrawingか、TextureDrawer元を保存するために使用されます。
このクラスは、DataSyncHelpersアドオンのRingBuffer.IRingBufferEntryインターフェースを実装しています。

依存関係

デモ

デモシーンはAssets\Photon\FusionAddons\TextureDrawing\Demo\Scenes\フォルダーにあります。

ダウンロード

このアドオンの最新バージョンは、Industries アドオンのプロジェクトに含まれています。

また、無料のXR アドオンのプロジェクトにも含まれています。

対応するトポロジー

  • 共有モード

サードパーティー

  • ProtoTurtle.BitmapDrawing, MIT license, https://github.com/ProtoTurtle/UnityBitmapDrawing

更新履歴

  • Version 2.1.0: Refactoring to add interpolation
  • Version 2.0.0: First release
Back to top