LoginSignup
8
2

More than 5 years have passed since last update.

[Unity] ステンシルバッファをImageEffectで利用する方法

Posted at

はじめに

検索トップに出る以下の記事内容が上手く動かなかったため、本記事を作成しました。
◆[Unity] ステンシルバッファをImageEffectで利用するシンプルなサンプル

2018-03-17_03h43_17.png

本記事は、Unity2017.3.1f1で動作を確認しています。
サンプルコードは図の通り動作しますが、
私自身なぜこのように記述するのか、説明ができない部分があります。
疑問点の共有、ならびに問題解決の一助となりましたら幸いです。

また、標準のポストプロセス機能は既にPostProcessingStackに置き換わっていて、
ImageEffectは古い技術となりますが、現在でも独自のポストプロセスを実装することは可能です。

ステンシルが動かない?

はじめに、私が悩まされた箇所から説明します。
後述するサンプルが動かない場合、カメラのAllow MSAA設定を確認してください。

2018-03-17_02h00_39.png

デフォルトでカメラのMSAAが有効になっていますが、MSAAが有効になっていると、ImageEffectのOnRenderImage関数ではステンシルバッファが参照できないようです。

以下はFrameDebuggerの画面です。

2018-03-17_03h45_29.png

シーン上にあるカメラのうち一つでもMSAAが有効になっていると、画像のようにシーンのレンダリング後/ImageEffectの処理前に、MSAAの描画パスが追加されます。
おそらく、この描画パスで(ステンシルバッファを含む)デプスバッファの情報が捨てられてしまうために、後続のImageEffectではステンシルバッファが参照できません。

MSAAを無効にすることで、問題を回避できます。

ステンシルを書き込むシェーダー

基本的なシェーダの書き方については割愛します。

2018-03-17_03h43_10.png

StencilWriter.shader
Shader "SampleShader/StencilWriter" {
    Properties{
        _StencilRef("Stencil Reference", float) = 1
    }
    SubShader{
        Tags{ "Queue" = "Transparent" }

        Stencil{
            Ref [_StencilRef]
            Comp Always
            Pass Replace
        }

        Pass{
            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag

            struct appdata {
                float4 vertex : POSITION;
            };

            struct v2f {
                float4 pos : SV_POSITION;
            };

            v2f vert(appdata v) {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                return o;
            }

            half4 frag(v2f i) : SV_Target{
                return half4(1,0,0,1);
            }
            ENDCG
        }
    }
}

ここで書き込んだステンシル値を使用して、
図の赤色部分だけを後のImageEffectで加工します。

ステンシルバッファを利用するImageEffect

元記事と異なる内容です。

Stencil.cs
using UnityEngine;

public class StencilMask : MonoBehaviour {

    public Shader shader = null;
    private Material material = null;

    void OnEnable() {
        if (material == null) {
            material = new Material(shader);
        }
    }

    void OnRenderImage(RenderTexture source, RenderTexture dest) {
        RenderTexture rt = RenderTexture.GetTemporary (source.width, source.height, 24, source.format);

        Graphics.SetRenderTarget(rt.colorBuffer, source.depthBuffer);
        Graphics.Blit(source, rt, material);
        Graphics.Blit(rt, dest);

        RenderTexture.ReleaseTemporary(rt);
    }
}

元記事では[ImageEffectOpaque]タグが必須と書かれていましたが、
[ImageEffectOpaque]タグはステンシルとは関係がなく、
ImageEffectの処理順を変えるだけのものです。
◆Unityマニュアル:グラフィックスコマンドバッファ

通常のBlitでは描画の際、描画対象となるカラーバッファのみが使用されます。
SetRenderTarget関数を使用して、描画対象のデプスバッファを明示することで、次に実行されるBlitにデプスバッファを渡すことができます。

destがスクリーン(null)であるために、colorBufferは参照できません。
このためRenderTextureを使用します。

sourceのdepthBufferを使用するにもかかわらず、
RenderTexture rtのデプスを24ビットとしていることには疑問が残りますが、
0ビットした場合、ステンシルテストが必ずパスしてしまいます。

なお、Blit関数のマニュアルには、
デプスやステンシルを使用する場合、GL関数を使用してBlitと同等の処理を実装するよう、
記載されています。
https://docs.unity3d.com/ja/2017.3/ScriptReference/Graphics.Blit.html

シェーダー

2018-03-17_04h21_20.png

StencilPicker.shader
Shader "SampleShader/StencilPicker" {
    Properties{
        _StencilRef("Stencil Reference", float) = 1
    }
    SubShader{
        Cull Off
        ZWrite Off
        ZTest Always

        Stencil{
            Ref [_StencilRef]
            Comp Equal
        }

        Pass{
            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag

            struct appdata {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            v2f vert (appdata v) {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }

            fixed4 frag (v2f i) : SV_Target {
                fixed4 col = fixed4(frac(i.uv * float2(16, 9)),1,1); // 市松模様
                return col;
            }
            ENDCG
        }
    }
}

結果

以下のようにStencilWriterで描画した部分だけに、StencilPickerが描画されます。

2018-03-17_03h43_17.png

2018-03-17_03h46_37.png

8
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
2