2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ImageEffectの様にシンプルにBlit()するだけのRenderGraph対応済みRendererFeature

2
Last updated at Posted at 2026-04-11

image.png

Unity6.3からコンパチビリティモードも無くなり、本格的にRenderGraph対応をしていかなければならなくなってきましたね。皆さんRenderGraph対応してますか?

ただ「そんな大仰なRendererFeatureを組みたいわけではなくて、単純にシェーダを1つ嚙ませてPostProcessっぽい事がしたいだけなんだけどなぁ・・・」という時にわざわざRenderGraphの作法から調べるのも面倒なので、Blit()だけを行うシンプルなRendrGraph対応済みRendererFeatureを書いておきます

前提

今回のコードは

  • Unity6000.4.2f1
  • Windows

環境で記載しています

先にシェーダから

本題のRendererFeatureに入る前に、シェーダのコードを書いておきましょう。
私のQiita記事ではお馴染みのMonochromeシェーダです

Monochrome.shader
Shader "ScreenPocket/URP/PostProcess/Monochrome"
{
    HLSLINCLUDE
        #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
        #include "Packages/com.unity.render-pipelines.core/Runtime/Utilities/Blit.hlsl"
    
        half4 Frag(Varyings input) : SV_Target
        {
            half4 col = SAMPLE_TEXTURE2D_X( _BlitTexture, sampler_PointClamp, input.texcoord );
            half luminance = Luminance(col.rgb);
            return half4(luminance,luminance,luminance,1);
        }
    ENDHLSL
    
    SubShader
    {
        Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline"}
        LOD 100
        Cull Off ZWrite Off ZTest Always

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

たった30行のシンプルなヤツです。
_BlitTextureで色を引いてLuminance()で輝度抽出するだけ。

コード

という事でRendererFeatureのコードです

SimpleBlitFeature.cs
using System;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.RenderGraphModule;
using UnityEngine.Rendering.Universal;

namespace ScreenPocket.Rendering.Runtime
{
    /// <summary>
    /// ただコピーバッファにBlit()して、マテリアルを当てつつBlit()で書き戻すだけのRendererFeature
    /// </summary>
    public sealed class SimpleBlitFeature : ScriptableRendererFeature
    {
        /// <summary>
        /// Blit用のPass
        /// </summary>
        private sealed class Pass : ScriptableRenderPass, IDisposable
        {
            private const string PassName = "Simple Blit Pass";
            private Material _blitMaterial;
            
            /// <summary>
            /// コンストラクタ
            /// </summary>
            /// <param name="evt"></param>
            /// <param name="blitShader"></param>
            public Pass(RenderPassEvent evt, Shader blitShader)
            {
                renderPassEvent = evt;
                profilingSampler = new ProfilingSampler(PassName);

                if (blitShader != null)
                {
                    _blitMaterial = new Material(blitShader);
                }
            }
            
            /// <summary>
            /// 受け渡し用Data
            /// </summary>
            private class PassData
            {
                public Material material;
                public TextureHandle inputTexture;
            }
            
            /// <summary>
            /// Blit()実行
            /// </summary>
            /// <param name="data"></param>
            /// <param name="context"></param>
            private static void ExecuteBlit(PassData data, RasterGraphContext context)
            {
                if (data.material == null)
                {
                    Blitter.BlitTexture(context.cmd, data.inputTexture, new Vector4(1, 1, 0, 0), 0f, false);
                    return;
                }
                
                //Materialの指定があるならこちら
                Blitter.BlitTexture(context.cmd, data.inputTexture, new Vector4(1, 1, 0, 0), data.material, 0);
            }

            public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData) 
            {
                var resourceData = frameData.Get<UniversalResourceData>();
                var cameraData = frameData.Get<UniversalCameraData>();
                
                //コピー用テクスチャを作る
                var colorCopyDescriptor = cameraData.cameraTargetDescriptor;
                //色をコピーしたいだけなのでMSAAやらDepthは不要
                colorCopyDescriptor.msaaSamples = 1;
                colorCopyDescriptor.depthBufferBits = (int)DepthBits.None;
                var copiedColorTexture = UniversalRenderer.CreateRenderGraphTexture(renderGraph, colorCopyDescriptor, "_CopyTexture", false);
                
                //先ずフレームバッファを一時バッファにコピー
                using (var builder = renderGraph.AddRasterRenderPass<PassData>("SimpleBlit_CopyColor", out var data, profilingSampler))
                {
                    data.inputTexture = resourceData.activeColorTexture;
                    builder.UseTexture(resourceData.activeColorTexture);
                    //Blit先の指定 コピー用テクスチャへ
                    builder.SetRenderAttachment(copiedColorTexture, 0);
                    builder.SetRenderFunc<PassData>(ExecuteBlit);
                }

                //一時バッファにコピーしたテクスチャをマテリアルで加工しつつ書き戻す
                using (var builder = renderGraph.AddRasterRenderPass<PassData>("SimpleBlit_Main", out var data, profilingSampler))
                {
                    data.material = _blitMaterial;
                    data.inputTexture = copiedColorTexture;
                    builder.UseTexture(copiedColorTexture);
                    //Blit先の指定 もとのActiveColorへ
                    builder.SetRenderAttachment(resourceData.activeColorTexture, 0);
                    builder.SetRenderFunc<PassData>(ExecuteBlit);
                }
            }

            /// <summary>
            /// 片付け
            /// </summary>
            public void Dispose()
            {
                if (_blitMaterial != null)
                {
                    CoreUtils.Destroy(_blitMaterial);
                    _blitMaterial = null;
                }
            }
        }// end class Pass

        /// <summary>
        /// 処理タイミング
        /// </summary>
        [SerializeField] private RenderPassEvent _renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing;
        /// <summary>
        /// 処理タイミングのOffset
        /// </summary>
        [SerializeField] private int _renderPassEventOffset;

        /// <summary>
        /// 書き戻しの際に適用するシェーダ
        /// </summary>
        [SerializeField] private Shader _blitShader;

        private Pass _pass;
        /// <summary>
        /// Pass作成
        /// </summary>
        public override void Create()
        {
            _pass = new Pass(_renderPassEvent + _renderPassEventOffset,  _blitShader);
        }

        /// <summary>
        /// Pass登録
        /// </summary>
        /// <param name="renderer"></param>
        /// <param name="renderingData"></param>
        public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
        {
            renderer.EnqueuePass(_pass);
        }

        /// <summary>
        /// 片付け
        /// </summary>
        /// <param name="disposing"></param>
        protected override void Dispose(bool disposing)
        {
            if (_pass != null)
            {
                _pass.Dispose();
                _pass = null;
            }
            base.Dispose(disposing);
        }
    }
}


コメント込みでの160行程度なので、まぁそこそこシンプルなのではないでしょうか?

やっていることは昔のGetTemporary()でやっていた時よろしく(と言って伝わるのかしら?)、カラーバッファから一度コピー用のテクスチャにBlit()した後で、マテリアルを当てつつコピー用からカラーバッファに書き戻している感じです

使い方

冒頭のMonochorme.shaderをプロジェクトに追加、RendererFeatureをプロジェクトに追加し、URPのRendererにSimpleBlitFeatureを追加してシェーダを当て込めば動作するはずです
image.png

FrameDebuggerで確認すると、SimpleBlitPassが追加されていることが見て取れるはずです

image.png

終わりに

という事で、手軽?に使いやすいRendererFeatureでした。
ImageEffect的にPostProcess的なシェーダを試す際に役に立つのではないでしょうか?

私がQiitaを書き始めた時はImageEffectだったのが、RendererFeatureになり、RenderGraphになり・・・と、移り変わりを感じますね。日々勉強ですねぇ・・・

2
0
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
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?