かりんちゃんの随心遊戯日誌

ゲームの日記、たまに政治の話、香港の話

研究:自作ScriptableRenderPassでPostProcessing とりあえず一番簡単なものを作る モノクロ

HLSL本がちょうどPost Processingを読んでいるところと、バイブル2ndのURPカスタマイズを読み終わったので、なにか簡単な自作ScriptableRenderPassを練習するかといろいろやりましたが、やはり色々と詰まった…

すると、まずはもっとも簡単なエフェクトから始まるべきではないか、と考えました。

しかし、ネットを調べると、URPバージョンの違いでコードが異なるということと、大抵記事はかなり高難易度向けで、やたらめちゃ長く難しいコードが並んでいるのが多い。

色々と試行錯誤すると、たぶん下のが一番シンプル、だと思います。

 

最も簡単なScriptableRenderPass基盤だが、意外とネットでは情報が少ないです。なので、参考できればどうぞ。ちなみに、HLSL本とバイブル以外に、以下のブログを参考しました(趣旨は同じですが、完全コピーしてもうまく行けないケースがあり):

https://qiita.com/ScreenPocket/items/b45100d6262c9b005a14

https://hacchi-man.hatenablog.com/entry/2022/12/25/220000

https://zenn.dev/sakutaro/articles/full_screen_pass_renderer_feature

 

前提としてはバイブル2ndの「URPカスタマイズ」を消化済み、あるいは同等知識がある方。

 

バージョンは、Unity 2022.3.7f1、URP14.0.8。

まずシェーダから。


Monochrome.shader


Shader "TestingShader/PostProcess/Monochrome"
{
    SubShader
    {
        Cull Off
        ZWrite Off
        ZTest Always

        Pass
        {
            Name "Monochrome"

            HLSLPROGRAM
            #pragma vertex Vert
            #pragma fragment frag

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/Shaders/PostProcessing/Common.hlsl"

            half4 frag(Varyings IN) : SV_Target
            {
                half4 col = SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_PointClamp, IN.texcoord);
                half Y = 0.299f * col.r + 0.587f * col.g + 0.114f * col.b;
                col.rgb = Y;
                return col;
            }
            ENDHLSL
        }
    }
}
  

参考リンクよりも、HLSL本のモノクロ加工を利用した。なので、参考リンクにあったVolumeObjectの手間もいらないのです。VolumeObjectはScriptableRenderPassと関係ないので、除外するほうがいいかもしれない。

また、URP14では、FullscreenVertが利用不可、テスクチャのコードは参考リンク2番目の_BlitTextureを利用しないといけないそうです。したがってインポートのコードもそれに合わせないといけません。

ちなみにもっとも簡単なケース(モノクロなし)は、half Yのところをコメントアウトして、そのままcolを返却するパータン。つまり、何もしない(何の特別なエフェクトもない)


MonochromePass.cs


using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;

public class MonochromePass : ScriptableRenderPass
{
    private const string ProfilerTag = nameof(MonochromePass);
    private Material _material;
    private string _shaderName = "TestingShader/PostProcess/Monochrome";

    public MonochromePass(Shader shader, RenderPassEvent renderPassEvent)
    {
        profilingSampler = new ProfilingSampler(ProfilerTag);
        this.renderPassEvent = renderPassEvent;

        if (shader == null)
        {
            Debug.LogError($"No shader found!!");
            _material = null;
        }
        else
        {
            _material = CoreUtils.CreateEngineMaterial(shader);
        }
    }

    public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
    {
        if (_material == null
            || !renderingData.cameraData.postProcessEnabled
            || renderingData.cameraData.cameraType == CameraType.SceneView
            )
        {
            return;
        }
        
        var cmd = CommandBufferPool.Get(ProfilerTag);
        using (new ProfilingScope(cmd, profilingSampler))
        {
            Blit(cmd, ref renderingData, _material);
        }

        context.ExecuteCommandBuffer(cmd);
        CommandBufferPool.Release(cmd);
    }

    public void Cleanup()
    {
        CoreUtils.Destroy(_material);
    }
}

こちらは参考リンクと大差ないですが、余計な情報を外しました(あくまでScriptableRenderPassを自分の手で作りたいため)

一応、最初は2番目参考リンクのBlit(commandBuffer, handle, handle, _material)を試しましたが、真っ白で何も映らない。すると色々と調べたら、3番目の参考リンクから、同じSourceから同じDestにBlitすると失敗するケースが有るようで、たぶん今回はハマってるかも。しかしRTHandleを挟むと内部メソッドとかはダメだ、という警告文が出ており、そっちでも利用不可。

なので、1番目参考リンクのほうがよいだそうです。


MonochromePostProcessRendererFeature.cs


using UnityEngine;
using UnityEngine.Rendering.Universal;

public class MonochromePostProcessRendererFeature : ScriptableRendererFeature
{
    [SerializeField]
    private Shader _shader;

    private MonochromePass _pass;

    public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
    {
        renderer.EnqueuePass(_pass);
    }

    public override void Create()
    {
        var renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing;
        _pass = new MonochromePass(_shader, renderPassEvent);
    }

    protected override void Dispose(bool disposing)
    {
        _pass.Cleanup();
        base.Dispose(disposing);
    }
}

こっちは特に何も。

 

とりあえずモノクロができた。