AnyPortrait > マニュアル > 「Command Buffer」の作成

「Command Buffer」の作成


1.4.0

Unityエンジンではレンダリングは自動的に行われますが、スクリプトを作成して機能を拡張することもできます。
これを行うためのさまざまな方法の1つは、「コマンドバッファ(Command Buffer)」を使用することです。
「コマンドバッファ」を簡単にまとめると、「対象のメッシュを指定された条件でレンダリングする要求をスケジュールすること」です。
Unityの公式マニュアルで詳細な説明を確認できます。
- ビルトインレンダーパイプラインのコマンドバッファによる拡張
- スクリプタブルレンダーパイプラインにおけるレンダリングコマンドのスケジューリングと実行
- CommandBuffer API
- CameraEvent API


Unityの「CommandBuffer」クラスを使用すると、AnyPortraitのキャラクターをコマンドバッファを介してレンダリングできますが、ユーザーがアクセスしにくいデータが多いので、それは実装するのが難しいです。
「AnyPortrait v1.4.0」に追加された「apCustomCommandBuffer」クラスは、「CommandBuffer」クラスのAnyPortrait用のバージョンです。
このクラスを使用すると、さまざまなテクニックを簡単に実装できます。


このページでは、「apCustomCommandBuffer」を使用してスクリプトを作成する方法と、いくつかの面白いテクニックを実装する方法について説明します。
apCustomCommandBuffer」クラスの関数の詳細については、関連ページを参照してください。




コマンドバッファで他のカメラにキャラクターを描く




実装に先立ち、コマンドバッファの配信方法を簡単に理解してみましょう。
スクリプトがコマンドバッファを作成してUnityのレンダリングパイプラインに渡すと、レンダリングパイプラインが動作しているときにスケジュールされたコマンドバッファの要求に従って追加のレンダリングが実行されます。
このときに渡さなければならない必須要素は、「メッシュ」と「カメラ」です。
この点を覚えて実装をしましょう。




コマンドバッファを使用する最も簡単で簡単な例を作成しましょう。
「カメラ(Camera 1)」が「キャラクターのメッシュ(Original Meshes)」をレンダリングする環境を用意します。
そして別の「カメラ(Camera 2)」で「コマンドバッファ」によって「キャラクター(Cloned Meshes)」が見えるようにしましょう。
コマンドバッファを作るために必要な要素として「Original Meshes」と「Camera 2」を記憶しましょう。




説明に合わせてUnityシーンを設定しました。
(1) 2つのカメラと1つのAnyPortraitキャラクターが存在することがわかります。
(2) AnyPortrait キャラクターです。
(3) キャラクターを描くメインカメラです。 先の図式での「Camera 1」と同じ役割を果たします。
(4) メインカメラがレンダリングを終了すると追加レンダリングを行うオーバーレイ用カメラです。 前の図式で「Camera 2」と同じ役割を果たします。




各カメラの設定です。
Camera 1」は、メインカメラとして最初にレンダリングするように設定されています。
Camera 2」は「Depth」の値によって「Camera 1」より後でレンダリングされ、「Culling Mask」によってキャラクターがレンダリングされません。




したがって、ゲームを実行すると、キャラクターは「メインカメラ」でのみ描画されます。


それでは、コマンドバッファを作成して更新する簡単なスクリプトを作成しましょう。
次のスクリプトは、「入力されたapPortraitキャラクタ」を「入力されたカメラ」に描画させる非常に簡単な役割を果たします。



スクリプトの重要な構文を1つずつ見てみましょう。



メンバーオブジェクトです。
(1) コマンドバッファの必須要素であるapPortraitとカメラをUnityシーンから接続できるようにpublic変数にします。
(2) 最も重要なコマンドバッファを「apCustomCommandBuffer」型の変数を使用して生成および制御します。



コマンドバッファを生成してレンダリングパイプラインに登録するコード。
(1) 例では「Start」関数で初期化が実行するように作成したため、apPortrait が初期化されていない可能性があり、初期化コードを作成しました。 必須コードではありません。
(2) コマンドバッファインスタンスを作成するときは、カメラとapPortraitを引数として入力し、コマンドバッファの名前を追加します。
(3) 「AddToCamera」関数を使用して、レンダリングパイプラインにコマンドバッファを登録します。
このとき、レンダリング過程でどの時点でコマンドバッファの要求が処理されるかを設定します。
プロジェクトの環境が「Scriptable Render Pipeline (SRP)」の場合は、「AddToCameraSRP」をこの関数の代わりに使用する必要があります。



コマンドバッファを構成する要素である「キャラクター」、「カメラ」、「コマンドバッファを管理するこのスクリプト」のいずれかがシーンに存在しなくなる場合は、上記のコードを必ず呼び出してコマンドバッファをレンダリングパイプラインから削除する必要があります。



コマンドバッファの要素に変更がある場合は、コマンドバッファの内容を再作成する必要があります。
「AnyPortrait」は各フレームごとにメッシュが更新されるため、「Update」または「LateUpdate」関数でコマンドバッファの内容を書き換える必要があります。
(1) コマンドバッファの内容を作成するには、既存のバッファ内容を削除する必要があります。
(2) レンダリングする「View-Projection Matrix」をコマンドバッファに入力します。 「Unity 2019」より前のバージョンではこの関数はサポートされていません。
(3) apPortrait内でどのメッシュをレンダリングするかをコマンドバッファに入力します。 「DrawAllMeshes」は、可能な現在表示されているすべてのメッシュをレンダリングするように要求します。




作成したスクリプトをシーンに入れましょう。
(1) 新しい「GameObject」を作成します。
(2) 作成したスクリプトをコンポーネントとして登録し、「apPortraitキャラクター」と「オーバーレイカメラ(Camera 2)」をそれぞれ入力します。




もう一度ゲームを実行すると、コマンドバッファによって2番目のカメラでもキャラクターが描かれていることがわかります。




コマンドバッファの内容を作成するコードを少しだけ変更しても、レンダリング結果は大きく異なります。
試してみて、次のようにコードを一行追加し、変わった結果を確認しましょう。





「オーバーレイカメラ(Camera 2)」でコマンドバッファによってキャラクタがレンダリングされる直前にレンダリングターゲット、つまり画面の色を紫色(Magenta)で初期化したため、上記のような結果が見られます。




グレースケールでキャラクターをレンダリング


コマンドバッファを使用することは、主に特殊なレンダリング効果を作成するためです。
レンダリング効果を作成するには、別々のマテリアルをコマンドバッファで利用できる必要があります。
今回はコマンドバッファでキャラクターをグレースケールでレンダリングするように実装しましょう。


今回は、キャラクターをグレースケールでレンダリングする「カスタムシェーダ」と、前述の「コマンドバッファスクリプト」を作成する必要があります。
まず、次の「カスタムシェーダ」を新しく作成します。



前述のコマンドバッファスクリプトを変更または新しく作成します。
今回は外部のマテリアルをインポートしてレンダリングに使用するように作成する必要があります。





(1) 完成した「カスタムシェーダ」を利用する「マテリアル(Material)」アセットを作成します。




(1) 先ほど作成した「GameObject」を選択します。
(2) スクリプトの追加された変数にカスタムシェーダを利用する「マテリアルアセット」を割り当てます。




完成した結果です。
コマンドバッファによってレンダリングされると、他のマテリアルに置き換えられてレンダリングされることがわかります。
この方法を適用すると、さまざまなレンダリング手法が利用可能になります。




アウトラインを持つキャラクターをレンダリング


上記の説明をもう少し適用すると、実用的な例を作成できます。
代表的な技術は「アウトライン」です。
ここでは、UVを使ってアウトラインを描く簡単なカスタムシェーダを使って簡単に実装できます。
さらに、コマンドバッファの作成時にレンダリングされる時点を変更する必要があります。
最後に、上記の例とは異なり、この手法ではメインカメラを対象にコマンドバッファが実行されるようにします。


アウトラインを描くカスタムシェーダを作成します。
さまざまな方法でアウトラインを描画しますが、ここでは単に基準で周囲のUVでの「Alpha」値を使用して色を拡張する方法を使用します。
新しいシェーダーアセットを作成し、以下のように作成します。



次に、コマンドバッファスクリプトを作成します。
前の例のコードとほぼ同じように書くだけで、レンダリングの時点を変更する必要があります。
作成したカスタムシェーダは、キャラクターイメージをもう少し拡張して単色で描くコードを持つため、これがアウトラインとして機能するにはキャラクターより先に描かれなければなりません。





(1) アウトラインシェーダを適用した「マテリアル」アセットを作成します。
(2) マテリアルアセットを選択し、アウトラインの太さと色を設定します。




(1) コマンドバッファを制御する「GameObject」を選択します。
(2) アウトラインの場合、オーバーレイカメラではなく、キャラクターをレンダリングする「メインカメラ」が対象になるように設定する必要があります。
(3) アウトラインを描くマテリアルアセットを割り当てます。




ゲームを実行すると、適切なクオリティのアウトラインが描かれていることがわかります。




複数の画像を持つキャラクターをレンダリング


前の説明を通じてコマンドバッファと別のマテリアルを利用してレンダリングを行う過程を習得しました。
今回はキャラクターのイメージが2つ以上の場合に外部のマテリアルを割り当てたい場合です。
画像によって異なる「セカンダリテクスチャ」を適用してレンダリングをしたい場合は、準備する必要のあるマテリアルとコマンドバッファの初期化コードを修正する必要があります。




左の2つのテクスチャを使用するキャラクターを用意しました。
それぞれの画像に対応する右側の補助テクスチャの色が乗算されるマテリアルを作って、コマンドバッファを通してレンダリングをしましょう。


今回も同様にカスタムシェーダを作ってみましょう。
_MainTex」プロパティはAnyPortraitによって管理されているため使用されないため、別々のテクスチャプロパティを作成する必要があります。





コマンドバッファを制御するスクリプトを作成する前に、マテリアルを作成しましょう。
(1) 画像数に合わせてマテリアルを作成します。
(2)_SecondaryTex」属性にそれぞれの補助テクスチャを割り当てます。


次に、2つ以上のマテリアルをコマンドバッファに割り当ててレンダリングするスクリプトを作成しましょう。





(1) 2つのイメージを使用するキャラクターが登場するユニティシーンです。
(2) Unity シーンは前述の説明と同じです。 スクリプトが追加された「GameObject」を選択します。
(3) apPortrait キャラクターが使用するイメージの名前と、それに合ったカスタムシェーダのマテリアルを順番に割り当てます。




ゲームを実行すると、メッシュの画像に対応する代替マテリアルが適切に適用され、上記の結果を見ることができます。




透明な背景のレンダリングテクスチャとしてレンダリング


この問題は、複数のユーザーから要求を受けた内容です。
キャラクターを画面ではなく「レンダーテクスチャ(Render Texture)」を対象にレンダリングを行うと、レンダリングテクスチャの背景を透明にするとメッシュが消える問題が発生します。
レンダリングテクスチャでキャラクターをレンダリングする方法については、関連ページで詳細な説明をご覧ください。
このページでは、レンダリングテクスチャでレンダリングを行う際の問題をコマンドバッファで解決する方法を紹介しながら、「レンダリングターゲット」を制御するスクリプトについて説明します。




キャラクターをレンダリングした結果をレンダリングテクスチャに保存した後、これを別のメッシュに塗りつぶしてレンダリングする方法の概略です。
キャラクターがレンダリングされたレンダーテクスチャは、ゲーム内で多様に活用できるため、よく使われる手法です。
ここでは、まだコマンドバッファは使用されていません。




スキームに合わせてシーンを構想しました。
2つのレイヤー(UI、Default)で構成され、「AnyPortraitキャラクター」、」2つのカメラ」、そしてレンダーテクスチャが施された「Quad Mesh」がシーンに配置されています。
(1) 「キャラクター」とそれをレンダリングする「最初のカメラ」です。 メインカメラではレンダリングされません。
(2) レンダリングテクスチャが適用された[Quad Mesh(RT Quad)]と「メインカメラ」です。




(1) そして最も重要な「レンダーテクスチャ(Render Texture)」アセットです。
(2) このレンダーテクスチャは、キャラクターをレンダリングする最初のカメラの「Target Texture」として登録されます。




キャラクターはメインカメラでレンダリングされないように、「Default」ではなくレイヤー(ここでは UI) に設定されています。




(1) メインカメラでレンダリングされる「Quad Mesh」です。
(2) このメッシュのマテリアルのテクスチャは、最初のカメラで使用される「レンダリングテクスチャ」アセットです。
この「Quad Mesh」のマテリアルと最初のカメラがレンダリングテクスチャを共有するため、最初のカメラでレンダリングする結果が「Quad Mesh」に表示されます。




ゲームを実行すると、Quad Meshで最初のカメラのレンダリング結果、つまりキャラクターが正常に見えることを確認できます。




問題は、レンダリングテクスチャの背景色が不透明でない場合に発生します。
(1) キャラクターをレンダリングする最初のカメラを選択します。
(2) 背景色のプロパティを選択します。 (Clear Flagsは「Solid Color」でなければなりません。)
(3) 背景色の「Alpha」値を下げます。
(4) 背景はもちろん、キャラクターを含むレンダーテクスチャ全体がますます透明になることがわかります。




これを解決する方法です。
不透明な背景のレンダリングテクスチャを完成させ、これとは別に「マスクテクスチャ」を「コマンドバッファ」を用いて生成します。
マスクを使用して、「背景」と「キャラクター」の領域を区別できます。
このマスクテクスチャをレンダリングテクスチャと組み合わせて、背景のみ透明な画像をレンダリングします。


参考
明確に区別して説明するために、カメラで生成する既存のレンダリングテクスチャを「カラーレンダリングテクスチャ(Color Render Texture)」と呼び、コマンドバッファが生成するマスクとして機能するレンダリングテクスチャを「マスクレンダリングテクスチャ(Mask Render Texture)」と呼びます。


この手法を実装するには多くの作業が必要です。
(1) 「Alpha」値を「単色(Grayscale)」で保存する「マスクレンダリング用カスタムシェーダ」を作成します。
(2) 「マスクレンダリングテクスチャ」を追加作成します。
(3) マスクレンダリングテクスチャをレンダリングする「コマンドバッファスクリプト」を作成します。
(4) 2つのレンダリングテクスチャをマージする「Quad Meshに塗りつぶされるシェーダとマテリアル」を作成します。


まず、コマンドバッファで使用するカスタムシェーダ、つまりAlpha値をモノクロで保存するカスタムシェーダを作成しましょう。



その後、コマンドバッファを制御するスクリプトを作成します。
前の例のコードとほとんど同じですが、「レンダーターゲット(Render Target)」としてランダムな「レンダーテクスチャ」を指定する点で違いがあります。



最後に、「Quad Mesh」に塗りつぶすマテリアルのシェーダーを作成します。
「カラーレンダリングテクスチャ」と「マスクレンダリングテクスチャ」を組み合わせて背景のみを透明にする役割をします。





(1) コマンドバッファで使用する「マスクレンダーテクスチャ」と「マテリアル」を作成します。 「Alpha」値を色で保存するカスタムシェーダをマテリアルに適用します。
(2) コマンドバッファを制御する「GameObject」を生成します。
(3) コマンドバッファを制御する「スクリプト」を追加し、「apPortraitキャラクター」、「ファーストカメラ」を割り当て、 (1) で生成したマスク生成用「マテリアル」と「マスクレンダリングテクスチャ」を割り当てます。




画像全体の透明度をマスクレンダリングテクスチャから計算するため、「カラーレンダリングテクスチャ」の背景は元々不透明でなければなりません。
(1) 最初のカメラを選択します。
(2) 背景色を選択して (3) 不透明にします。
このとき、画像の端より少し暗い色に設定すると、よりきれいな結果が得られます。




(1) そして「Quad Mesh」に塗られる「マテリアル」を作成し、2つのレンダリングテクスチャを組み合わせるカスタムシェーダを適用します。
(2) 「最初のプロパティ(_MainTex)」に「カラーレンダリングテクスチャ」を割り当てます。
(3) 「2番目のプロパティ(_MaskTex)」に「マスクレンダーテクスチャ」を割り当てます。




(1)Quad Mesh」を選択します。
(2) 先に作成した「2つのレンダリングテクスチャを組み合わせたマテリアル」をこのメッシュに適用します。




完成した結果です。
透明な背景では、キャラクターだけが鮮明にレンダリングされる素晴らしい結果を得ることができます。




「カラーレンダリングテクスチャ」と「マスクレンダリングテクスチャ」を組み合わせてレンダリングの問題を解決することがわかります。
コマンドバッファは、上記のようにレンダリングの問題を解決するために追加のレンダリングテクスチャなどを作成するときに効果的に使用されます。