5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

URPで出来るだけシンプルなコードでPostProcess【SwapBuffer編】

Last updated at Posted at 2022-05-17
スクリーンショット 2022-05-18 031555.png

以前書いた

ですが、当時からUniversalRPがどんどん更新されまして、SwapBufferなる便利な仕組みが入りましたので
そちらを利用してポストプロセスを出来るだけシンプルに表記する方法を紹介します。

前回はモノクロ化するポストプロセスを紹介しましたので、今回もモノクロ化するポストプロセスで記述してみます

環境としては

  • Unity2021.3.2f1
  • UniversalRP 12.1.6

を対象としていますが、SwapBufferが有効になったUniversalRP 12以降なら動作対象となるかと思います。

必要なコード

必要なコードは3点。以前の記事では4点でしたが、今回は3つにまとめました

ファイル名 ファイル種別 内容
Monochrome.cs csファイル パラメータ調整用のVolumeComponent
MonochromePostProcessRendererFeature.cs csファイル 描画のメイン部分
Monochrome.shader shaderファイル 描画用のシェーダ

という事で上から見ていきましょう。

Monochrome.cs

これは前回の記事からほぼ変わりません

Monochrome.cs
using System;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;

namespace ScreenPocket.PostProcess
{
    [Serializable, VolumeComponentMenu("ScreenPocket/PostProcess/Monochrome")]
    public sealed class Monochrome : VolumeComponent, IPostProcessComponent
    {
        public ClampedFloatParameter weight = new (0f, 0f, 1f);

        public bool IsActive() => weight.value > 0f;
        public bool IsTileCompatible() => false;
    }
}

weightを触るためのVolumeComponent派生のコンポーネントです。
1年前と比べてCSのバージョンも上がり、ますますシンプルなコードになりました。

MonochromePostProcessRendererFeature.cs

これが今回の肝のコードです

MonochromePostProcessRendererFeature.cs
using JetBrains.Annotations;
using ScreenPocket.PostProcess;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;

namespace ScreenPocket.Rendering
{
    public sealed class MonochromePostProcessRendererFeature : ScriptableRendererFeature
    {
        private sealed class Pass : ScriptableRenderPass
        {
            private Material _material;
            public Pass(RenderPassEvent evt)
            {
                profilingSampler = new ProfilingSampler("Monochrome PostProcess Pass");
                renderPassEvent = evt;
                _material = FindMaterial("ScreenPocket/URP/PostProcess/Monochrome");
            }
            
            [CanBeNull]
            private static Material FindMaterial(string shaderName)
            {
                var shader = Shader.Find(shaderName);
                if (shader == null)
                {
                    Debug.LogError($"Not found shader!{shaderName}");
                    return null;
                }
                return CoreUtils.CreateEngineMaterial(shader);
            }
            
            public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
            {
                if (_material == null)
                {
                    return;
                }

                var stack = VolumeManager.instance.stack;
                var monochrome = stack.GetComponent<Monochrome>();
                
                if (!monochrome.active)
                {
                    return;
                }

                var cmd = CommandBufferPool.Get();
                using (new ProfilingScope(cmd, profilingSampler))
                {
                    _material.SetFloat("_Weight", monochrome.weight.value);
                    Blit(cmd, ref renderingData, _material);//←☆これが今回のポイント!SwapBufferを利用したBlit()!
                }

                context.ExecuteCommandBuffer(cmd);
                CommandBufferPool.Release(cmd);
            }
        }//end class Pass

        
        /// <summary>
        /// ↑で定義したパス
        /// </summary>
        private Pass _pass;
        
        public override void Create()
        {
            //タイミングを調整したい場合は、例えば RenderPassEvent.BeforeRenderingPostProcessing+1 など値を加減算する事でで調整可能
            var timing = RenderPassEvent.BeforeRenderingPostProcessing;
            _pass = new Pass(timing);
        }

        public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
        {
            //Setupしたい場合はここに差し込む事 _pass.Setup(renderer);的な
            renderer.EnqueuePass(_pass);
        }
    }
}

という事で、前回別ファイルに分けた描画パスをクラス内に移動しました。
前回はポストプロセスをまとめる用のPassを想定していましたが、今回はモノクロを描画するためだけのFeatureとPassとなっています。
このコードで1番のポイントが//←☆のコメントを付けた1行。
ScriptableRenderPass.Blit()を使ってBlit()を行っています。

それによって、前回

前回のBlit()部分
        var cmd = CommandBufferPool.Get();
        using (new ProfilingScope(cmd, profilingRenderPostProcessing))
        {
            ref var cameraData = ref renderingData.cameraData;
            var destination = keepFrameBuffer;

            _monochromeMaterial.SetFloat("_Weight", _monochrome.weight.value);

            var width = cameraData.cameraTargetDescriptor.width;
            var height = cameraData.cameraTargetDescriptor.height;

            cmd.GetTemporaryRT(destination, width, height,
                0, FilterMode.Point, RenderTextureFormat.Default);
            cmd.SetGlobalTexture("_MainTex", destination);
            cmd.Blit(_target, destination);
            cmd.Blit(destination, _target, _monochromeMaterial, 0);
            cmd.ReleaseTemporaryRT(destination);
        }
        context.ExecuteCommandBuffer(cmd);
        CommandBufferPool.Release(cmd);

だった部分が、SwapBufferを利用する事で

今回のBlit()部分
        var cmd = CommandBufferPool.Get();
        using (new ProfilingScope(cmd, profilingSampler))
        {
            _material.SetFloat("_Weight", monochrome.weight.value);
            Blit(cmd, ref renderingData, _material);//←☆これが今回のポイント!SwapBufferを利用したBlit()!
        }

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

こんなにすっきり記述することが出来ました!すごい!

Monochrome.shader

最後にマテリアルに設定するためのシェーダファイルです

Monochrome.shader
Shader "ScreenPocket/URP/PostProcess/Monochrome"
{
    HLSLINCLUDE
        #include "Packages/com.unity.render-pipelines.universal/Shaders/PostProcessing/Common.hlsl"

        TEXTURE2D_X(_SourceTex);
        SAMPLER(sampler_SourceTex);
        float _Weight;

        half4 Frag(Varyings input) : SV_Target
        {
            half4 col = SAMPLE_TEXTURE2D_X( _SourceTex, sampler_SourceTex, input.uv );
            half luminance = Luminance(col.rgb);
            return half4(lerp(col.rgb, half3(luminance,luminance,luminance),_Weight),1);
        }
    ENDHLSL
    
    SubShader
    {
        Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline"}
        LOD 100
        Cull Off ZWrite Off ZTest Always

        Pass
        {
            Name "Monochrome"
            HLSLPROGRAM
                #pragma vertex FullscreenVert
                #pragma fragment Frag
            ENDHLSL
        }
    }
}

という事で、前回との内容的な違いとしては _MainTexが_SourceTexにリネームされていることでしょうか。
SwapBufferで渡されるテクスチャ名が_SourceTexなので間違えないようにしておきましょう。

動作確認

今回も、残りの手順を列挙しておきます

  1. PackageManagerでUniversalRPをImport
  2. Projectツリービューで右クリック>Create>Rendering>Universal
  3. ProjectSettingsのGraphics設定画面で作ったURPアセットを設定
  4. 使っているRendererにMonochromePostprocessRendererFeatureを追加する
  5. 新規のGameObjectを作成しVolumeコンポーネントを追加 > VolumeProfileの「new」を選択してProfileを作成する
  6. VolumeProfileにScreenPocket/PostProcess/Monochromeを追加して、Weightを操作する
  7. カメラのPostProcessトグルにチェックを入れる
  8. カメラのVolumeTriggerに5で作ったGameObjectをアタッチする

とまぁ、ズババーッと書きましたが、これについては通常のURPのポストプロセス設定と特に違いは無いです。
ココを掘り下げる予定は無いので、他所のもっと詳細なポストプロセス導入記事を参照すると良いかと。

おわりに

という事で、SwapBufferの恩恵により、よりシンプルにポストプロセスを実装できることになった喜びを共有する記事でした。
たった130行弱でポストプロセスが実装できるとは、良い時代になった物です・・・!

参考資料

追記

Universal13に更新したところ、正しく表示されなくなってしまったようです
記事中の

修正前
                using (new ProfilingScope(cmd, profilingSampler))
                {
                    _material.SetFloat("_Weight", monochrome.weight.value);
                    Blit(cmd, ref renderingData, _material);//
                }

修正後
                using (new ProfilingScope(cmd, profilingSampler))
                {
                    _material.SetFloat("_Weight", monochrome.weight.value);
                    cmd.SetGlobalTexture(Shader.PropertyToID("_SourceTex"), 
                        renderingData.cameraData.renderer.cameraColorTargetHandle.nameID);
                    Blit(cmd, ref renderingData, _material);//
                }

とすることで、とりあえずは対応可能ですが、より良い解消法は調査中です。

5
1
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
5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?