URP+2DRendererで画面の一部にモザイクやブラーをかける

URP + 2DRenderer にすると _OpaqueueTexutre が利用できない(設定しても画像が設定されない)ため旧来の GrabPass を使用したレンダリング結果に対して効果をつけることが直接はできませんでした。旧来、対処法として RenderTexture を使用した実装などで少し工夫が必要でしたが、最近は _CameraSortingLayerTexture という変数によって _OpaqueueTexutre のようなことができるようなり、これを使用して画面にモザイクやブラーをかけることが可能になりました。

今回は、URP + 2DRenderer + ShaderGraph で画面にモザイクやブラーをかけてみたいと思います。

実装後のイメージは以下の通りです。ちなみにシャドーキャスターやスポットライトにも効果がかかっています。

確認環境

  • Unity 2021.2.5f1
  • Universal RP 12.1.2
  • Visual Studio 2019
  • Windows10

補足: 古い URP(たぶん11.0より前)だとこの方法使用できない?(というか12.0より前だと不具合多数)っぽい?のでURPは念のため 12.0 以上を使用して実装しています。

シーンセットアップ

まず、Renderer 2D Data > Camera Sorting Layer Texture > Foremost Sorting Layer を Disable から Default に変更します。

こうすることでシェーダーのパラメーターで _OpaqueTexture の代わりの _CameraSortingLayerTexture という変数が使用可能になります。

次に SortignLayer を追加します。今回は以下のように最前面を表す TopMost をレイヤーとして追加します。

次にシーンに Canvas を配置してシェーダーを適用する Image を配置します。

Canvas には先ほど追加した Sorting Layer の TopMost を設定しておきます。

また、画面上には適当な位置に Image を配置しておきます。

参考のため Light を以下のような形状で配置し任意のオブジェクトに ShadowCaster 2D を設定しスポットライトから影が落ちている状態にしておきます。

この時、描画結果に正しくライトと影の情報を反映させるために、中央のスポットライトは Target Sorting Layer は TopMost を含む All に

Shadow Caster 2D の影は TopMost を含まないように設定しておきます。

画面にモザイクをかける

準備編

新規に Sheder Graph と Material を作成し、Material に Shader Graph を設定します。

まずシェーダーグラフを適当に作成し「ShaderGraph_Mosaic」という名前で保存します。

次に Material を作成し「Materia_Mosaic」という名前で保存します。

作成した Material に「ShaderGraph_Mosaic」を設定します。

次に Image にこのマテリアルを設定します。

そうすると画面上の Image が灰色になるのでこれで準備は完了です。

ShaderGraph編

上記で準備した「ShaderGraph_Mosaic」をダブルクリックしてシェーダーグラフのエディタを開きます。

右側の Graph Inspector から Graph Setting を以下画像の通り設定します。

  • Material:Sprite Unlit

次に、通常左側にあるペインから「+」>「Texture2D」を選択します。

追加した Texture2D を選択して右側の Inspector から以下画像の通り入力します。

  • Name:CameraOpaque (← 何でもよい)
  • Reference:_CameraSortingLayerTexture
  • Exposed:チェックOFF

エディタ上に以下の通りノードを配置します。

そして Sample Texutre2D ノードの出力を Fragment の Base Clolor に設定します。

これで「Save Asset」を選択してシーンビューに戻ると Material を設定した Image の領域だけモザイクがかかっていると思います。

シーンビューで既にモザイク模様が確認できます。

画面にブラーをかける

いわゆるすりガラスシェーダーですがあんまりうまくできなかったので参考程度としてください。

綺麗なブラーをかける方法は以下記事で紹介しているので以下は参考程度としてください。

takap-tech.com

準備編

ブラー用の ShaderGraph と Material を準備し Image に設定します。モザイクシェーダーの時と同じく「ShaderGraph_Blur」と「Materia_Blur」を作成します。

マテリアルに先ほどと同じく ShaderGraph を設定し Image の Material に設定します。

名前は「ShaderGraph_Blur」と「Materia_Blur」で作成します。

イメージにマテリアルを設定したらモザイクの時と同じように今度も灰色の状態になっていると思います。

ShaderGraph編

上記で準備した「ShaderGraph_Mosaic」をダブルクリックしてシェーダーグラフのエディタを開きます。

右側の Graph Inspector から Graph Setting を以下画像の通り設定します。

  • Material:Sprite Unlit

次に、通常左側にあるペインから「+」>「Texture2D」を選択します。

追加した Texture2D を選択して右側の Inspector から以下画像の通り入力します。

  • Name:CameraSortingLayerTexture(← 何でもよい)
  • Reference:_CameraSortingLayerTexture
  • Exposed:チェックOFF

次に外部からブラーの強度を設定できるように Float 変数を追加します。

Graph Inspector の値は以下の通りです。

  • Name:Blur
  • Reference:_Blur
  • Default:0
  • Min:0
  • Max:30
  • Mode:Slider

次にノードを以下の通り設定します。

LoopX のカスタムファンクションノードは配置時に以下を設定します。

LoopY のカスタムファンクションノードは配置時に以下を設定します。

カスタムノードは Type を String にすると Body という欄が出てきてそこにシェーダーのコードを直接記入できるので以下を設定します。

LoopX 用のコード

// LoopX のコード

col = (0, 0, 0, 0);
float weight_total = 0;
float uvPitchX = 1 / _CameraSortingLayerTexture_TexelSize.w;

for (float x = -blur; x <= blur; x += 1)
{
    float distance_normalized = abs(x / blur);
    float weight = exp(-0.5 * pow(distance_normalized, 2) * 5.0);
    weight_total += weight;
    
    float4 pos = grabPos;
    pos.x = grabPos.x + (x * uvPitchX);
    col += SAMPLE_TEXTURE2D(grabTexture.tex, grabTexture.samplerstate, grabTexture.GetTransformedUV(pos.xy)) * weight;
}

col /= weight_total;

上記をBodyに張り付けます。

LoopY 用のコード

col = (0, 0, 0, 0);
float weight_total = 0;
float uvPitchY = 1 / _CameraSortingLayerTexture_TexelSize.w;

for (float y = -blur; y <= blur; y += 1)
{
    float distance_normalized = abs(y / blur);
    float weight = exp(-0.5 * pow(distance_normalized, 2) * 5.0);
    weight_total += weight;
    
    float4 pos = grabPos;
    pos.y = grabPos.y + (y * uvPitchY);
    col += SAMPLE_TEXTURE2D(grabTexture.tex, grabTexture.samplerstate, grabTexture.GetTransformedUV(pos.xy)) * weight;
}

col /= weight_total;

これも同じく Body に張り付けます。

そして各々のカスタムファンクションノードの出力を以下のように接続して Fragment の Base Color に入力します。

次に Save Asset を選択してシーンビューに戻ります。

まだシーンビュー上だと何も起きていません。

Material_Blur を選択するとのスライダーが表示されているのでスライダーの値を変更します。

スライダーを右に移動させると以下のようにブラー効果が Image の範囲に発生します。

動かすとこんな感じです。

もし動かない場合は

灰色のまま何も描画されない

マテリアルを設定した Image や SpriteRenderer が灰色のままの場合、「Renderer 2D Data」のアセットを Foremost Sorting Layer の設定を確認します。

Disable になってると何も描画されません。エフェクトを設定するレイヤーを指定しましょう。この場合 Effect のレイヤーにモザイクやブラーを設定して、PlayerFront までに効果を適用したいオブジェクトを配置します。

SprieRendererの表示がおかしい

表示内容が無限ループしてるような表示になる場合、オブジェクトの Sorting Layer の設定が間違っている可能性があります。モザイクやブラーをかけるオブジェクトは Foremost Sorting Layer より下のレイヤーに割り当てましょう。Foremost Sorting Layer と同じもしくはそれより上側にオブジェクトを配置すると表示が無限ループして変になります。

最後に

あれ、、、マルチパスできないのかぁ…となってブラーの処理はマルチパス用をシングルパスの Add で加算したため品質が悪めですが GrabPass 代わりの処理の基本的はこのような形になると思います。

参考

以下フォーラムに 2DRenderer で _OpaqueueTexutre の代わりに _CameraSortingLayerTexture 使えよって話が議論されています。

forum.unity.com

以下参考にしています。

qiita.com