目次
はじめに
こんばんは、代表の堂前です!
今回はUnityにおいて主にイメージエフェクト系で活用することになるOnRenderImageについて取り上げます。
各種アセットを利用していると気にしないことが多いですが、内部挙動を一緒に見ていきましょう。
※検証したのはMacのUnity5.4.1系になります。
イメージエフェクトの流れ
今回は標準のイメージエフェクトの「Bloom (Optimized)」を適応してテストします。
そのBloomですがスクリプトとして付与されているので、「Edit Script」で内部の処理が見れるようになっています。
そしてその中にOnRenderImage()の存在も確認出来ます。
OnRenderImage()内で元画像の加工を行い、最終的な絵を作って出力するのですが、それは描画的にどういう扱いになっているでしょうか?
Frame Debuggerで見てみましょう。
「Camera.ImageEffects」でイメージフィルタを適応しているのは分かるかと思いますが、「Grab RenderTexture」は何をしているものでしょう?
「Grab RenderTexture」
「Grab RenderTexture」で何をしているか調べるため、今回は「Intel® Graphics Performance Analyzers」を用いて内部処理を解析してみました。
(今回はWindows(DirectX9)版も同時に調査しています。)
![]() |
![]() |
DirectX9版 | OpenGL版 |
こちらもDrawCall毎で調査できますが、該当箇所の処理はそれぞれ以下になっていました。
DirectX9版 | OpenGL版 |
![]() |
![]() |
StretchRect() | glBlitFramebuffer() |
早い話、これらは「RenderTextureのコピー処理」となっています。
つまりOnRenderImage()は以下の流れになっていると推測できます。
描いていた絵(dest)を複製(src)する
↓
複製した絵(src)を利用してイメージエフェクト後の絵を作る
↓
デフォルトターゲット(dest)に書き戻す
「src」「dst」は、OnRenderImage()の引数と思って下さい。
描画の一般的な処理として、srcとdestは同一のものだと絵が確実に崩れてしまうので、一旦コピーして使うということを行っています。
もう少し分かりやすく図式します。
これがOnRenderImageの大まかな流れです。
OnPostRender()の利用
OnRenderImage()はイメージエフェクトを作成するには理には叶っていて便利は便利ですが、都度、コピー処理が走るのが負荷的に気になります。
ここで提案するのはOnRenderImage()の代わりにOnPostRender()を利用する形です。
要点としては以下になります。
・予めデフォルトターゲットと同一サイズのRenderTextureを用意。
・モデル等は↑に書き込む。
・OnPostRender()でそのRenderTextureを用いて加工し、それをデフォルトターゲットに戻す。
文だけだと分かりづらいので、こちらもフローを用いて説明します。
コピー処理が無いことでフローがスッキリしました。
そしてコピー処理分の負荷も削減できます。
ただ、自前でRenderTextureを用意するなどの管理がやや面倒にはなります。
そこは対応を頑張ってもらう事になります。
OnRenderImage()のいいところ
ここまでOnRenderImage()の利点を挙げていなかったので、最後にいいところを2点ほど挙げます。
・不透明→半透明の描画の間に処理を挟める。
([ImageEffectOpaque]を利用。SSAOやEdgeDetection、GlobalFog等で利用。)
・複数のOnRenderImageがある際、「Grab RenderTexture」が一つにまとまる。
後者だけ詳細に説明します。
例えばブルームの他にColor Correction Curvesも適応するとします。
この場合「Grab RenderTextureが2回走るのでは?」と想像しますが、Frame Debuggerで見る限り1回にまとめてくれるようです。(例外あり)
負荷をとことこ削りたい場合はOnPostRender()主体で構成していくと良いのですが、この様にある程度コピー処理を自重する仕組みもあるので、負荷をあまり気にしない場合はOnRenderImage()で作ってしまうのも良いと思います。
(※2016/10/18 22:20追記)
Grab RenderTextureについて、ItoZakura様より情報を頂きました。(ありがとうございます)
@logicalbeat_jp 記事へのコメント失礼いたします。Grab RenderTextureですが、イメージエフェクトを使用していても品質設定でアンチエイリアスを無効にしていると行われませんので、イメージエフェクトではなくアンチエイリアス用の処理ではないかと思います。
— ItoZakura (@ito_zakura) 2016年10月18日
@logicalbeat_jp また、カメラがOnRenderImageを持っていると、そのレンダーターゲットは内部でイメージエフェクト用のRenderTextureに差し替えられるそうです。https://t.co/1rH4Chq9io
以上、連投失礼いたしました。— ItoZakura (@ito_zakura) 2016年10月18日
このGrab RenderTextureが入るのはアンチエイリアス(以下AA)が有効の時のみ、ということです。
確認してみましょう!
同様のシーンをAA無しで描画し、Frame Debuggerで確認をしてみます。
確かにGrab RenderTextureは無くなってます!
ここでポイントなのは、↑は球体の描画を選択していますが、RenderTargetが「ImageEffects Temp」に差し代わってます!
確かに内部でテンポラリを用意して描画しているということになります。
となると、OnRenderImageも結構凄いんですね・・・。
失礼致しました。
となると、OnPostRenderでの代替のメリットは「RenderTextureの管理をしっかりしたい」「デフォルトレンダーターゲットと同一解像度で無くても良い」くらいになりますね。
【免責事項】
本サイトでの情報を利用することによる損害等に対し、株式会社ロジカルビートは一切の責任を負いません。
「【Unity】OnRenderImageの内部挙動」への1件のフィードバック
コメントは受け付けていません。