1. サイトトップ
  2. ブログ
  3. Unity
  4. 【Unity】OnRenderImageの内部挙動

【Unity】OnRenderImageの内部挙動

はじめに

こんばんは、代表の堂前です!

今回はUnityにおいて主にイメージエフェクト系で活用することになるOnRenderImageについて取り上げます。
各種アセットを利用していると気にしないことが多いですが、内部挙動を一緒に見ていきましょう。

※検証したのはMacのUnity5.4.1系になります。


イメージエフェクトの流れ

今回は標準のイメージエフェクトの「Bloom (Optimized)」を適応してテストします。

unity_onrenderimage_bloom

そのBloomですがスクリプトとして付与されているので、「Edit Script」で内部の処理が見れるようになっています。
そしてその中にOnRenderImage()の存在も確認出来ます。

unity_onrenderimage_editscript

OnRenderImage()内で元画像の加工を行い、最終的な絵を作って出力するのですが、それは描画的にどういう扱いになっているでしょうか?
Frame Debuggerで見てみましょう。

unity_onrenderimage_grabsingle

「Camera.ImageEffects」でイメージフィルタを適応しているのは分かるかと思いますが、「Grab RenderTexture」は何をしているものでしょう?


「Grab RenderTexture」

「Grab RenderTexture」で何をしているか調べるため、今回は「Intel® Graphics Performance Analyzers」を用いて内部処理を解析してみました。
(今回はWindows(DirectX9)版も同時に調査しています。)

 unity_onrenderimage_intelgraphicsanalyzerdx9 unity_onrenderimage_intelgraphicsanalyzeropengl
DirectX9版 OpenGL版

こちらもDrawCall毎で調査できますが、該当箇所の処理はそれぞれ以下になっていました。

DirectX9版 OpenGL版
unity_onrenderimage_intelgraphicsanalyzer_stretchrect unity_onrenderimage_intelgraphicsanalyzer_glblitframebuffer
StretchRect() glBlitFramebuffer()

早い話、これらは「RenderTextureのコピー処理」となっています。
つまりOnRenderImage()は以下の流れになっていると推測できます。

描いていた絵(dest)を複製(src)する
  ↓
複製した絵(src)を利用してイメージエフェクト後の絵を作る
  ↓
デフォルトターゲット(dest)に書き戻す

「src」「dst」は、OnRenderImage()の引数と思って下さい。
描画の一般的な処理として、srcとdestは同一のものだと絵が確実に崩れてしまうので、一旦コピーして使うということを行っています。

もう少し分かりやすく図式します。

unity_onrenderimage_flow0

これがOnRenderImageの大まかな流れです。


OnPostRender()の利用

OnRenderImage()はイメージエフェクトを作成するには理には叶っていて便利は便利ですが、都度、コピー処理が走るのが負荷的に気になります。

ここで提案するのはOnRenderImage()の代わりにOnPostRender()を利用する形です。
要点としては以下になります。

・予めデフォルトターゲットと同一サイズのRenderTextureを用意。
・モデル等は↑に書き込む。
・OnPostRender()でそのRenderTextureを用いて加工し、それをデフォルトターゲットに戻す。

文だけだと分かりづらいので、こちらもフローを用いて説明します。

unity_onrenderimage_flow1

コピー処理が無いことでフローがスッキリしました。
そしてコピー処理分の負荷も削減できます。

ただ、自前でRenderTextureを用意するなどの管理がやや面倒にはなります。
そこは対応を頑張ってもらう事になります。


OnRenderImage()のいいところ

ここまでOnRenderImage()の利点を挙げていなかったので、最後にいいところを2点ほど挙げます。

・不透明→半透明の描画の間に処理を挟める。
[ImageEffectOpaque]を利用。SSAOEdgeDetectionGlobalFog等で利用。)
・複数のOnRenderImageがある際、「Grab RenderTexture」が一つにまとまる。

後者だけ詳細に説明します。
例えばブルームの他にColor Correction Curvesも適応するとします。

unity_onrenderimage_multieffect

この場合「Grab RenderTextureが2回走るのでは?」と想像しますが、Frame Debuggerで見る限り1回にまとめてくれるようです。(例外あり)

unity_onrenderimage_multigrab

負荷をとことこ削りたい場合はOnPostRender()主体で構成していくと良いのですが、この様にある程度コピー処理を自重する仕組みもあるので、負荷をあまり気にしない場合はOnRenderImage()で作ってしまうのも良いと思います。


(※2016/10/18 22:20追記)

Grab RenderTextureについて、ItoZakura様より情報を頂きました。(ありがとうございます)

このGrab RenderTextureが入るのはアンチエイリアス(以下AA)が有効の時のみ、ということです。
確認してみましょう!

unity_onrenderimage_quality

同様のシーンをAA無しで描画し、Frame Debuggerで確認をしてみます。
確かにGrab RenderTextureは無くなってます!

unity_onrenderimage_noaa

ここでポイントなのは、↑は球体の描画を選択していますが、RenderTargetが「ImageEffects Temp」に差し代わってます!
確かに内部でテンポラリを用意して描画しているということになります。

 

となると、OnRenderImageも結構凄いんですね・・・。
失礼致しました。

となると、OnPostRenderでの代替のメリットは「RenderTextureの管理をしっかりしたい」「デフォルトレンダーターゲットと同一解像度で無くても良い」くらいになりますね。


 

 

【免責事項】
本サイトでの情報を利用することによる損害等に対し、株式会社ロジカルビートは一切の責任を負いません。

One thought on “【Unity】OnRenderImageの内部挙動

Comments are closed.