渋谷ほととぎす通信

完全趣味でやってるUnityメモ。説明できないところを説明できるようにするための個人ブログ。昨日の自分より少しでも大きくなれるように。。。 ※所属団体とは一切関係がありません

Unity SetTargetBuffers、Blit、SetRenderTargetの関係性の謎にせまる


f:id:esakun:20150730215258g:plain

世界ふしぎ発見を見ながら記事を書いていたのでタイトルが影響を受けております。
2017年も残りわずか、この記事が今年最後の投稿になるかもしれません。来年もよろしくお願いいたします。


では本題へ

  • camera.SetTargetBuffers
  • Graphics.Blit
  • Graphics.SetRenderTarget

イマイチ理解していなかった上記3メソッドの調査しました。

camera.SetTargetBuffers

SetTargetBuffersはCameraクラスのインスタンスメソッドで、カメラがレンダリングするRenderTextureのバッファを指定します。

var rt = new RenderTexture(300, 200, 24, RenderTextureFormat.ARGB32);
rt.name = "TestRenderTarget";
_camera.SetTargetBuffers (rt.colorBuffer, rt.depthBuffer);

このような感じでカメラにRenderTextureのバッファをセットします。
上記のサンプルコードの場合、このカメラのレンダリング対象がRenderTextureのrtになったため、今まで画面に写っていた画像は映らなくなります。

文字だと分かりづらいのでサンプルをどうぞ。

初期状態

右下にはuGUIのRawImageでRenderTextureをセットしています。

f:id:esakun:20171223215023p:plain:w320

初期状態には何も投影していないため、真っ白な状態です。
※RenderTexture生成時に白色を描画しています

SetRenderBuffers実行後

f:id:esakun:20171223215447p:plain:w320

SetRenderBuffersを実行したので、今まで映し出されていた画面表示が消え、カメラのレンダーターゲットがRenderTextureに移ったことが分かります。

void OnPostRender()
{
    Debug.Log(RenderTexture.active);
}

カメラのレンダーターゲットがRenderTextureに移った事は、RenderTexture.activeをOnPostRender(レンダリング終了時)のログを取得してみると分かります。

ここまでで、SetTargetBuffersはカメラのレンダリング先を変更できるということがわかりました。

Graphics.Blit

Graphics.Blitは引数で渡されたRenderTextureをターゲットのRenderTextureにコピーするスタティックメソッドです。

Graphics.Blit(src, dst);

この場合テクスチャsrcをRenderTextureのdstにコピーします。

もう1つの機能としてdstにnullを代入することで、srcを画面に直接描画することが出来ます。
この機能を使い、カメラからRenderTextureに描画されている画像を画面に写してみます。 Unity - Scripting API: Graphics.Blitより

// dstを指定しないことでnullが代入されるオーバーロード
void OnPostRender()
{
    // rtを画面に直接描画
    Graphics.Blit(rt, material); 
}

※上記materialは入力画像をそのまま出力した単純なシェーダです

しかしこのままでは画面に描画されません。

Graphics.SetRenderTarget

最後に登場したSetRenderTargetはメソッド名の通り、引数に渡したRenderTextureやRenderBufferをレンダーターゲットに指定することが出来ます。

先のRenderTextureのrtを画面に表示させるためにGraphics.Blitを実行しましたが、残念ながら思うような挙動をしませんでした。
理由としてはカメラのレンダーターゲットが未だにRenderTextureのrtを指しているからです。

画面に描画するタイミングで、SetRenderTargetを使ってカメラのレンダーターゲットを解除する必要があります。

void OnPostRender()
{
    // カメラのレンダーターゲットを解除
    Graphics.RenderTarget(null);
    // rtを画面に直接描画
    Graphics.Blit (rt, material);
}

と、引数にnullを代入することでレンダーターゲットがRenderTextureから外れ、画面にRenderTextureが描画されるようになります。

カメラのレンダーターゲットからRenderTextureが外れたのであればGraphics.Blitは不要ではないか?という疑問が湧いてきます。

しかし、カメラがレンダリングするバッファはRenderTexutreのrtと指定されているため、この設定が外れるわけではありません。あくまで一時的にレンダーターゲットから外しているだけで、次フレームの描画ループになったときには再度元のレンダーターゲットに戻っています。

余談

Graphics.RenderTarget(null)

RenderTexture.active = null

この2つの記述は内部処理として同じなので覚えておきたいところです。

Unity - Scripting API: Graphics.SetRenderTargetより


最後に

検証プロジェクトを作ってみてやっと理解できた気がします。
SetRenderBuffersは一度指定すると解除することが出来ないっぽいのですが、そういうものなのでしょうか...調べても分かりませんでした。

また今回の流れを図でまとめています。

1.初期状態

f:id:esakun:20171224003131p:plain:w414

2.SetTargetBuffers実行

f:id:esakun:20171224003335p:plain:w414

3.Graphics.Blit実行

f:id:esakun:20171224003350p:plain

4.Graphics.SetRenderTarget(null)を実行

f:id:esakun:20171224003402p:plain


CameraクラスにSetRenderBuffersと同じようなtargetTextureプロパティも存在します。
コチラはまた余力がある際に調査しいます。


今回のサンプルプロジェクトです。

素材はコチラのサイトから使わせてもらいました。

検証環境

  • Unity2017.3.0f3
  • macOS Sierra 10.12.6

参考記事