前ではScriptableRenderPassの話をしましたが、あれは元々自主研究の範囲で、こんな高度の技術はいま仕事でやっているUnityプロジェクトには無縁なんだろう、と思いましたが…まさか今日で活躍することになりました…
いやぁ、運がいいというか悪いというか、まさかバイブルで読んだこのものが、今日の難題を解決してくれたとは
何を何のためにScriptableRenderPassを使いましたか、おそらくゲームでは皆さんよく見かけたものだと思いますが、あれはよく考えたらそんなに甘い効果ではなかった…。
半透明するUIの背景にブラーをかける
よく話ですよね、とくに半透明のGUIでは、場合によって背景はめちゃ邪魔なので、視認性のためにブラーをかけるのがよくあることです。しかし、意外と、そんなに一筋ではいけません。
まず、ブラーをかけるというと、描画工程のポストプロセスに当たります、つまり画面に描画したものを加工する話です。ブラーではピクセルの左右上下の色を混ざって処理するから、個別シェーダ、とくに透明があるものではなく、最後の描画で加工します。
なので、全画面にブラーをかけるなら、割と簡単な話で、Unityでは、調べてないがなんとなく、デフォルトではついてる機能ではないか、と思います。
しかし、GUIは違うです。なぜかというと全画面ではなく、描画したものの一部だけ、しかもGUIの配置によってはバラバラになるし、GUIがダイナミックだと尚更複雑です。
最初その要望を聞いた途端、直感的にすでにこれ簡単ではない話だなと思ってましたが、さすがにScriptableRenderPassを使うのは躊躇うので、一応ネットで探りましたが
結局やはりScriptableRenderPassにたどり着きました…。
コードはちょっと長くなるので今日は疲れましたので省略しますが、概念的に、まず2つシェーダを用意します、一つはブラーのシェーダです、もう一つはGUI用の描画シェーダです。
ブラーのシェーダは単純にテスクチャ、つまり全画面のレンダリングテスクチャのブラーシェーダです。これをRenderFeature→RenderPassという流れでデフォルトシェーダとして設定します。
もう一つGUIのシェーダはそのままマテリアルを用意して、ブラーをかけたいGUIにそのマテリアルに設定します。基本はブラーしたレンダリングテスクチャとGUIを融合するシェーダですが、レンダリングテスクチャはRenderPassが設定します(自分はShader.PropertyToIDで、Executeの頃で直接設定しました)。
するとRenderFeatureはいつも通りだが、RenderPassはdepthBufferBitsが0(ここ重要、最初はこれにハマりました)、TextureWrapMode.Clampのレンダリングテスクチャを最低2枚用意します。ダウンサンプリングしてからブラーするのがよく使われている手法なので、レンダリングテスクチャのWとHは元の半分あるいはそれ以下でいいかもしれない。
そしてExecuteのやることはソース(cameraData.renderer.cameraColorTargetHandle)からRTにブラーシェーダを掛けてBlit。例えばソース→RT1にBlit、RT1→RT2に再Blit、とBlitの回数は純粋にブラーの品質次第ですが、あまり掛けすぎるとやはり重くなるので、自分は3、4回までした。そして最後はできあがったRTをSetGlobalTextureでGUIのシェーダに設定する。
この方法で、GUIシェーダではブラーをかけた背景画像があるので、元のカラーとテスクチャで最終の色を計算する。
という話です。
と、一応ここでひとまず課題はクリアしましたが、問題が残ります。
この方法では、半透明GUIの背景のGUIには通用しないのです。
つまり簡単な話では、対象となるブラーを掛けたい半透明GUIでは、他のGUIがその下にあると、そのへんはブラーの対象外、というか表示されないのです(上の方法を使うと、すでにブラーテスクチャは背景のものが描かれたので、GUIではアルファが255のが一般的、すると勿論、透明ではないから下のGUI描かれない)。
これはネットで見つけたいくつの手本でも試したが、同じ結果でした。
おそらく、レンダリングテスクチャを作成する際、GUIは無視されるだからだろう。だから作成したテスクチャではGUIの部分がいないし、ブラーも対象外。
しかし困ったことに、要望ではそれも表示させて、一緒にブラー掛けてほしいのです。
一旦、対象となるGUIのアルファチャンネルをいじってみたが、後ろのGUIは表示されるようになった、が、ブラーの具合がアルファチャンネルと同時に薄くなっていく…
まあ、ブラーしたものはあくまでテスクチャなので、アルファチャンネルが1以下になると、対象となるテスクチャも自然的に効果が薄くなり、さらに元々ブラーをかけたいGUI以外の背景がそのままBlendされてしまい、ブラーの具合がどんどん減っていくのも、仕方ないことです。
では、どうすればいいか…
カメラを増やすなども考えましたが、これ以上処理を重くにするのはちょっと嫌だな、と、実際やってみるともとのGUIの表示が色々とおかしくなるので、調整がめちゃ面倒だからこれだけのために時間と努力をかけるのはコスパが悪い…
と、要はなんとなく後ろのGUIをぼんやりに確認できればいい、という話ですから、アルファチャンネルを255ではないが、255の近い数値に弄って、後ろのGUIをなんか見えるようにして、色を調整して仕上げました。
255に近い数値では一応ブラーの具合がまだ強いから、GUIもかすかに見えるし見えないし、余計な処理も不必要ですからこれで良かったと思います。
と、こんな感じで、まさか本当に仕事で活用しました ScriptableRenderPass…。
バイブルを読んでこの前に自主研究しててよかったな、と思った。
ちなみにプラグイン使えばいいじゃん?という話だが、前と同じ、自分でなんとかできるレベルなら、できるだけプラグイン頼りたくない。
しかも今回、ScriptableRenderPassの実務経験を積みましたし、シェーダとともに描画工程に対する理解も深くなりました、一石二鳥ではないか。