【Unity】見える部分だけにアウトラインを引くカスタムパス

投稿者: | 2024-02-25

カスタムパスを使って簡単にアウトラインを描画するサンプルを詳しく見てみました。

参考:https://github.com/alelievr/HDRP-Custom-Passes

このサンプルでは、遮られた部分にアウトラインが表示されます。

対象のオブジェクトが重なって見える場合、輪郭が繋がります。画面の端にもアウトラインが表示されます。

輪郭抽出するには、まず対象のレイヤーをカスタムカラーバッファに描画します。ピクセルの輝度と隣接するピクセルの輝度を比較してエッジを識別します。

カスタムパス

Custom Pass Volumeコンポーネントでは、独自のカスタムパスが追加されています。

Target Color Bufferは「Custom」になっています。深度はカメラデプスバッファに書き込みます。Target Depth Bufferを「None」にすると、遮蔽されてもアウトラインが描画されるようになります。

Clear Fragsでカラーを破棄しないと、アウトラインが残ります。

レイヤーマスク

カスタムパスで「Selection」レイヤーが選択されています。アウトラインを表示するオブジェクトに同じレイヤーが設定されています。

対象レイヤーをDefaultに設定

スクリプト

カスタムパスのスクリプトを見てみます。SetUpメソッドでは、シェーダー名からマテリアルを取得しています。

        outlineShader = Shader.Find("CustomPass_SG/Outline");
        fullscreenOutline = CoreUtils.CreateEngineMaterial(outlineShader);

Executeメソッドでは、まず設定したレイヤーのみをカスタムカラーバッファにレンダリングします。

        CustomPassUtils.DrawRenderers(ctx, outlineLayer);

対象のレイヤー以外は真っ暗になりました。

カスタムカラーバッファ

プロパティを設定するための、プロパティブロックを追加します。型と名前が同じブロックがすでにあれば上書きされます。

        ctx.propertyBlock.SetColor("_OutlineColor", outlineColor);
        ctx.propertyBlock.SetTexture("_OutlineBuffer", ctx.customColorBuffer.Value);
        ctx.propertyBlock.SetFloat("_Threshold", Mathf.Max(0.000001f, threshold * 0.01f));

SetUpメソッドで作ったマテリアルを使って、フルスクリーンの三角形を描画します。

HDUtils.DrawFullScreen(ctx.cmd, fullscreenOutline, ctx.cameraColorBuffer, shaderPassId: 0, properties: ctx.propertyBlock);

最後にCleanupメソッドでマテリアルを破壊しています。

        CoreUtils.Destroy(fullscreenOutline);

シェーダーグラフ

カスタムパスのSetUpメソッドで設定するシェーダーはシェーダーグラフで作られています。まずカスタムカラーバッファの値の輝度を調べて、しきい値と比較します。

しきい値よりも大きければアウトラインをチェックします。それ以外は透明にします。しきい値は0に設定されています。対象のオブジェクトが描画されている部分だけしきい値よりも大きくなります。

エッジ抽出するには、隣接するピクセルの輝度を調べます。隣接するピクセルの座標を計算するために8個のオフセットが設定されています。オフセットは一辺が2の正方形上にあります。

サブグラフにオフセットを渡して輝度を調べます。輝度がしきい値より小さい場合Trueになります。

すべての結果の論理和を計算します。ひとつでもTrueがあれば最後もTrueになります。

Trueの場合はアウトラインの色、そうでない場合は透明になります。

しきい値が0なので、対象のオブジェクトのあるピクセルでアウトラインを調べます。その内、何も描画されていない暗いピクセルが隣接した部分はTrueになります。

しきい値を上げると、オブジェクトの影の境界などにアウトラインが表示されるようになります。

0
2.52

オフセットの値を大きくすると、アウトラインが太くなります。

サブグラフ

サブグラフでは、ピクセル単位のオフセット値をスクリーンサイズで割って正規化します。「Reciprocal」ノードは、1を入力値で割った結果を返します。

この値をUVに足して、カスタムカラーバッファをサンプリングします。輝度を計算して出力します。

これで簡単にアウトラインが描画できます

参考:
https://github.com/alelievr/HDRP-Custom-Passes
https://docs.unity3d.com/ja/2020.3/ScriptReference/MaterialPropertyBlock.html
https://docs.unity3d.com/ja/Packages/com.unity.shadergraph@10.0/manual/Reciprocal-Node.html

コメントを残す

メールアドレスが公開されることはありません。