URPでカスタムポストエフェクト実装する
URPでグレースケールを行うポストエフェクトを作ります
環境
・ Unity2023.3.13f1
・ URP14.0.9
1.シェーダーの用意
URP14環境下でポストエフェクトをRendererFeatureに対応させるにはBlitter
APIを使用する必要があり、合わせてシェーダーも変更する必要があるらしい
Shader "PostEffect/GrayScale" {
Properties
{
[HideInspector] _MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Cull Off ZWrite Off ZTest Always
Tags { "RenderType"="Opaque" "Renderpipeline"="UniversalPipeline" }
Pass
{
HLSLPROGRAM
#pragma vertex Vert
#pragma fragment frag
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.core/Runtime/Utilities/Blit.hlsl"
half4 frag (Varyings i) : SV_Target
{
half4 color = SAMPLE_TEXTURE2D(_BlitTexture, sampler_LinearRepeat, i.texcoord);
half gray = dot(color.rgb, half3(0.2126, 0.7152, 0.0722));
return half4(gray, gray, gray, color.a);
}
ENDHLSL
}
}
}
グレースケールの処理はここ
half gray = dot(color.rgb, half3(0.2126, 0.7152, 0.0722));
return half4(gray, gray, gray, color.a);
2.RenderPassの実装
ScriptableRenderPass
を実装した、グレースケール処理を行う独自パスを実装します。
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
public class GrayScaleRenderPass : ScriptableRenderPass
{
private readonly Material material;
public GrayScaleRenderPass(Shader shader)
{
material = CoreUtils.CreateEngineMaterial(shader);
renderPassEvent = RenderPassEvent.AfterRenderingOpaques;
}
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
if (renderingData.cameraData.isSceneViewCamera) { return; }
var cmd = CommandBufferPool.Get("GrayScaleRenderPass");
using (new ProfilingScope(cmd, new ProfilingSampler("GrayScaleRenderPass")))
{
var handle = renderingData.cameraData.renderer.cameraColorTargetHandle;
Blitter.BlitCameraTexture(cmd, handle, handle, material, 0);
}
context.ExecuteCommandBuffer(cmd);
CommandBufferPool.Release(cmd);
}
}
renderPassEventの設定でどのタイミングでこのパスが実行されるかを決定することができます
今回は、不透明の描画後にGrayScaleRenderPass
が実行されるようにしました
renderPassEvent = RenderPassEvent.AfterRenderingOpaques;
3.RendererFeatureの実装
ScriptableRenderPipeline
に先ほど作成したGrayScaleRenderPass
を渡すためのRendererFeatureを実装します
using UnityEngine;
using UnityEngine.Rendering.Universal;
public class GrayScaleRendererFeature : ScriptableRendererFeature
{
[SerializeField] private Shader _shader;
private GrayScaleRenderPass grayscalePass;
public override void Create()
{
grayscalePass = new GrayScaleRenderPass(_shader);
}
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
renderer.EnqueuePass(grayscalePass);
}
}
レンダーパスが生成された時に生成したレンダーパスをレンダラーに渡しています
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
renderer.EnqueuePass(grayscalePass);
}
4.UniversalRenderDataの作成
「Create」->「Rendering」->「URP Universal Renderer」でUniversalRenderData
のアセットを生成します
5.RenderFeatureの追加
4.で生成したUniversalRenderData
に3.で作成したRendererFeature
を追加します。
作成したシェーダーの参照もここで設定します
6.UniversalRenderDataをパイプラインに追加
7.カメラの設定でグレースケールのUniversalRenderDataを選択する
グレースケールのポストエフェクトがかかっていることが確認できた
FrameDebuggerでみるとDrawOpaqueObjects(不透明の描画)
の後にGrayScaleRenderPass
が実行されているのがわかる
レンダーパスの実行タイミングを変えてみる
スカイボックスの描画の前にしてみる
renderPassEvent = RenderPassEvent.AfterRenderingSkybox;
スカイボックスもグレースケールがかかった
FrameDebuggerで見てみるとCamera.RenderSkybox
の後にGrayScaleRenderPass
が実行されているのがわかる
Discussion