【Unity Shader】Built-inのポストエフェクトをURPに変更 #117
はじめに
Bilt-inで記載されているポストエフェクトをURPに置換してみます。
今回対応させて頂くものは、以下サイト様のものになります。
ソースコード
ScriptableRendererFeature
using UnityEngine; using UnityEngine.Rendering.Universal; namespace Day5.Practice2 { public class GlareRendererFeature : ScriptableRendererFeature { [SerializeField] private Shader shader; [SerializeField, Range(0.0f, 1.0f)] private float threshold = 0.5f; [SerializeField, Range(0.5f, 0.95f)] private float attenuation = 0.9f; [SerializeField, Range(0.0f, 10.0f)] private float intensity = 1.0f; private GlarePass glarePass; public override void Create() { glarePass = new GlarePass(shader); } public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData) { glarePass.SetRenderTarget(renderer.cameraColorTarget); glarePass.SetShaderProperty(threshold, attenuation, intensity); renderer.EnqueuePass(glarePass); } } }
ScriptableRenderPass
using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; namespace Day5.Practice2 { public class GlarePass : ScriptableRenderPass { private const string ProfilerTag = nameof(GlarePass); private readonly Material material; private readonly int paramsPropertyId = Shader.PropertyToID("_Params"); private readonly int thresholdPropertyId = Shader.PropertyToID("_Threshold"); private readonly int attenuationPropertyId = Shader.PropertyToID("_Attenuation"); private readonly int intensityPropertyId = Shader.PropertyToID("_Intensity"); private RenderTargetHandle destRenderTargetHandle; private RenderTargetHandle tmpRenderTargetHandle1; private RenderTargetHandle tmpRenderTargetHandle2; private RenderTargetIdentifier cameraColorTarget; private float threshold; private float attenuation; private float intensity; public GlarePass(Shader shader) { material = CoreUtils.CreateEngineMaterial(shader); renderPassEvent = RenderPassEvent.AfterRenderingTransparents; destRenderTargetHandle.Init("_destRT"); tmpRenderTargetHandle1.Init("_TempRT1"); tmpRenderTargetHandle2.Init("_TempRT2"); } public void SetRenderTarget(RenderTargetIdentifier target) { cameraColorTarget = target; } public void SetShaderProperty(float threshold, float attenuation, float intensity) { this.threshold = threshold; this.attenuation = attenuation; this.intensity = intensity; } public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { if (renderingData.cameraData.isSceneViewCamera) { return; } var cmd = CommandBufferPool.Get(ProfilerTag); var descriptor = renderingData.cameraData.cameraTargetDescriptor; cmd.GetTemporaryRT(destRenderTargetHandle.id, descriptor); cmd.GetTemporaryRT(tmpRenderTargetHandle1.id, descriptor); cmd.GetTemporaryRT(tmpRenderTargetHandle2.id, descriptor); material.SetFloat(thresholdPropertyId, threshold); material.SetFloat(attenuationPropertyId, attenuation); material.SetFloat(intensityPropertyId, intensity); cmd.Blit(cameraColorTarget, destRenderTargetHandle.Identifier()); for (var i = 0; i < 4; i++) { cmd.Blit(cameraColorTarget, tmpRenderTargetHandle1.Identifier(), material, 0); var currentSrc = tmpRenderTargetHandle1.Identifier(); var currentTarget = tmpRenderTargetHandle2.Identifier(); var parameters = Vector3.zero; parameters.x = i is 0 or 1 ? -1 : 1; parameters.y = i is 0 or 2 ? -1 : 1; for (var j = 0; j < 4; j++) { parameters.z = j; cmd.SetGlobalVector(paramsPropertyId, parameters); cmd.Blit( currentSrc, currentTarget, material, 1); (currentSrc, currentTarget) = (currentTarget, currentSrc); } cmd.Blit(currentSrc, destRenderTargetHandle.Identifier(), material, 2); } cmd.Blit(destRenderTargetHandle.Identifier(), cameraColorTarget); cmd.ReleaseTemporaryRT(destRenderTargetHandle.id); cmd.ReleaseTemporaryRT(tmpRenderTargetHandle1.id); cmd.ReleaseTemporaryRT(tmpRenderTargetHandle2.id); context.ExecuteCommandBuffer(cmd); CommandBufferPool.Release(cmd); } } }
シェーダー
Shader "Day5/URPGlare" { Properties { _MainTex ("Texture", 2D) = "white" {} } SubShader { Cull Off ZTest Always ZWrite Off Tags { "RenderType"="Opaque" // レンダリングパイプラインをURPにする "Renderpipeline" = "UniversalPipeline" } Pass { // HLSLを記述する HLSLPROGRAM #pragma vertex vert #pragma fragment frag // hlslでよく使用されるマクロをインクルード // #include "UnityCG.cginc"に近い #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" // appdeta -> Attributes struct Attributes { // vertex -> positionOS // OSはObject Spaceの略 float4 positionOS : POSITION; float2 uv : TEXCOORD0; }; // v2f -> Varyings struct Varyings { float2 uv : TEXCOORD0; // vertex -> positionHCS // HSCはHomogeneous Clip Space(等質クリップ座標)の略 float4 positionHCS : SV_POSITION; }; sampler2D _MainTex; float4 _MainTex_ST; float _Threshold; // Texture2Dを宣言 TEXTURE2D(_CameraDepthTexture); // SamplerStateを宣言 SAMPLER(sampler_CameraDepthTexture); Varyings vert (Attributes IN) { Varyings OUT; // UnityObjectToClipPos -> TransformObjectToHClip OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz); OUT.uv = TRANSFORM_TEX(IN.uv, _MainTex); return OUT; } // hlslではfixedが使えないのでhalfにする half4 frag (Varyings IN) : SV_Target { // SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv); // textureとsamplerが必要になった half depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, sampler_CameraDepthTexture, IN.uv); // Linear01Depth(depth); // zBufferParamが必要になった half linear01Depth = Linear01Depth(depth, _ZBufferParams); half4 col = tex2D(_MainTex, IN.uv); half brightness = max(col.r, max(col.g, col.b)); half contribution = max(0, brightness - _Threshold); contribution /= max(brightness, 0.00001); return col * contribution * (1 - linear01Depth); } // HLSLの記述を終える ENDHLSL } Pass { HLSLPROGRAM #pragma vertex vert #pragma fragment frag #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" struct Attributes { float4 positionOS : POSITION; float2 uv : TEXCOORD0; }; struct Varyings { float2 uv : TEXCOORD0; float4 positionHCS : SV_POSITION; half2 uvOffset : TEXCOORD1; half pathFactor : TEXCOORD2; }; sampler2D _MainTex; float4 _MainTex_ST; half4 _MainTex_TexelSize; half3 _Params; float _Attenuation; Varyings vert (Attributes IN) { Varyings OUT; OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz); OUT.uv = TRANSFORM_TEX(IN.uv, _MainTex); OUT.pathFactor = pow(4, _Params.z); OUT.uvOffset = half2(_Params.x, _Params.y) * _MainTex_TexelSize.xy * OUT.pathFactor; return OUT; } half4 frag (Varyings IN) : SV_Target { half4 col = half4(0, 0, 0, 1); half2 uv = IN.uv; [unroll] for (int j = 0; j < 4; j++) { col.rgb += tex2D(_MainTex, uv).rgb * pow(saturate(_Attenuation), j * IN.pathFactor); uv += IN.uvOffset; } return col; } ENDHLSL } Pass { Blend One One ColorMask RGB HLSLPROGRAM #pragma vertex vert #pragma fragment frag #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" struct Attributes { float4 positionOS : POSITION; float2 uv : TEXCOORD0; }; struct Varyings { float2 uv : TEXCOORD0; float4 positionHCS : SV_POSITION; }; sampler2D _MainTex; float4 _MainTex_ST; float _Intensity; Varyings vert (Attributes IN) { Varyings OUT; OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz); OUT.uv = TRANSFORM_TEX(IN.uv, _MainTex); return OUT; } half4 frag (Varyings IN) : SV_Target { return tex2D(_MainTex, IN.uv) * _Intensity; } ENDHLSL } } }
スクリプト側について
ポストエフェクトの解説については元ブログ様を参考にしてください。
また、RendererFeatureについては以下記事で行っています。
URP Assetの設定
Depth Textureの設定をオンにします。
そうすることでDepthTextureである_CameraDepthTexture
が取得できます。
シェーダーをURPに対応
シェーダー
にコメントを記載しているので、そちらを参考にしてください。
以下に自分が気になったマクロの中身を記載します。
TransformObjectToHClip
// Transforms position from object space to homogenous space float4 TransformObjectToHClip(float3 positionOS) { // More efficient than computing M*VP matrix product return mul(GetWorldToHClipMatrix(), mul(GetObjectToWorldMatrix(), float4(positionOS, 1.0))); } // Transform to homogenous clip space float4x4 GetWorldToHClipMatrix() { return UNITY_MATRIX_VP; } // Return the PreTranslated ObjectToWorld Matrix (i.e matrix with _WorldSpaceCameraPos apply to it if we use camera relative rendering) float4x4 GetObjectToWorldMatrix() { return UNITY_MATRIX_M; }
SAMPLE_DEPTH_TEXTURE
#define SAMPLE_DEPTH_TEXTURE(textureName, samplerName, coord2) SAMPLE_TEXTURE2D(textureName, samplerName, coord2).r
Linear01Depth
// Z buffer to linear 0..1 depth (0 at camera position, 1 at far plane). // Does NOT work with orthographic projections. // Does NOT correctly handle oblique view frustums. // zBufferParam = { (f-n)/n, 1, (f-n)/n*f, 1/f } float Linear01Depth(float depth, float4 zBufferParam) { return 1.0 / (zBufferParam.x * depth + zBufferParam.y); }
結果
Threshold
Attenuation
Intensity
不明な点
for文内でのmaterial.SetHoge()
for (var j = 0; j < 4; j++) { parameters.z = j; cmd.SetGlobalVector(paramsPropertyId, parameters); cmd.Blit( currentSrc, currentTarget, material, 1); (currentSrc, currentTarget) = (currentTarget, currentSrc); }
のcmd.SetGlobalVector()
を初めはmaterial.SetVector()
で行っていたが、エフェクトが適応されないでいた。
原因としては、シェーダーにparameters
が常に(1, 1, 3)で渡っていたため。
SetGlobalVector()
にすることで解決したが、詳しい原因は不明。