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);
}
}
こっちは特に何も。
とりあえずモノクロができた。