using System; using Unity.Mathematics; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; using static UnityEngine.Rendering.Universal.UniversalRenderPipeline.Profiling.Pipeline; namespace X.Rendering.Feature { public class SoftShadowMask : ScriptableRendererFeature { [Serializable] private class Settings { public bool BlurMask = false; public Material MaskMat = null; public Material BlurMat = null; public int BlurIterations = 3; public float BlurSpread = 0.6f; } [SerializeField] private Settings settings = new(); SoftShadowMaskPass shadowMaskPass; public override void Create() { shadowMaskPass = new(settings); } public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData) { renderer.EnqueuePass(shadowMaskPass); } class SoftShadowMaskPass : ScriptableRenderPass { internal static readonly int SoftShadowMaskTextureId = Shader.PropertyToID("_SoftShadowMaskTexture"); internal static readonly GlobalKeyword softShadowMaskKeyword = GlobalKeyword.Create("_SOFTSHADOW_MASK"); private Settings settings; private ProfilingSampler profiler; Vector4[] offsetArray = new Vector4[4]; RTHandle softShadowMask; RTHandle buffer0; RTHandle buffer1; public SoftShadowMaskPass(Settings settings) { this.settings = settings; renderPassEvent = RenderPassEvent.AfterRenderingPrePasses; profiler = new(nameof(SoftShadowMaskPass)); } public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { var cmd = renderingData.commandBuffer; using var scp = new ProfilingScope(cmd, profiler); cmd.EnableKeyword(softShadowMaskKeyword); var w = renderingData.cameraData.cameraTargetDescriptor.width; var h = renderingData.cameraData.cameraTargetDescriptor.height; float2 wh = new float2 { x = w, y = h }; { offsetArray[0] = new float4(new float2(-1, 1) / wh, new float2(1, 1) / wh); offsetArray[1] = new float4(new float2(-1, -1) / wh, new float2(1, -1) / wh); offsetArray[2] = new float4(new float2(-2, -2) / wh, new float2(2, 2) / wh); offsetArray[3] = new float4(new float2(-2, 2) / wh, new float2(2, -2) / wh); cmd.SetGlobalVectorArray("_Offset5", offsetArray); } cmd.SetGlobalVector("_MaskSizeAndInvRenderSize", new Vector4(w / 4, h / 4, 1f / w, 1f / h)); var desc = new RenderTextureDescriptor() { bindMS = false, colorFormat = RenderTextureFormat.R8, width = w / 4, height = h / 4, enableRandomWrite = false, depthBufferBits = 0, dimension = TextureDimension.Tex2D, msaaSamples = 1, }; RenderingUtils.ReAllocateIfNeeded(ref softShadowMask, desc); cmd.SetGlobalTexture("_CameraDepthTexture", renderingData.cameraData.renderer.cameraDepthTargetHandle); cmd.SetRenderTarget(softShadowMask, loadAction: RenderBufferLoadAction.DontCare, storeAction: RenderBufferStoreAction.Store); cmd.DrawProcedural(Matrix4x4.identity, settings.MaskMat, 0, MeshTopology.Triangles, 3); if (settings.BlurMask) { cmd.BeginSample("SoftShadowMask Blur"); //desc.memoryless = RenderTextureMemoryless.Color; RenderingUtils.ReAllocateIfNeeded(ref buffer0, desc, name: "buffer0"); RenderingUtils.ReAllocateIfNeeded(ref buffer1, desc, name: "buffer1"); cmd.SetGlobalTexture("_MainTex", softShadowMask); for (int i = 0; i < settings.BlurIterations; i++) { settings.BlurMat.SetFloat("_BlurSize", i * settings.BlurSpread); cmd.SetRenderTarget(buffer0, loadAction: RenderBufferLoadAction.DontCare, storeAction: RenderBufferStoreAction.Store); cmd.DrawProcedural(Matrix4x4.identity, settings.BlurMat, 0, MeshTopology.Triangles, 3); cmd.SetGlobalTexture("_MainTex", buffer0); cmd.SetRenderTarget(buffer1, loadAction: RenderBufferLoadAction.DontCare, storeAction: RenderBufferStoreAction.Store); cmd.DrawProcedural(Matrix4x4.identity, settings.BlurMat, 1, MeshTopology.Triangles, 3); cmd.SetGlobalTexture("_MainTex", buffer1); } cmd.EndSample("SoftShadowMask Blur"); cmd.SetGlobalTexture(SoftShadowMaskTextureId, buffer1); } else { cmd.SetGlobalTexture(SoftShadowMaskTextureId, softShadowMask); } } public override void OnFinishCameraStackRendering(CommandBuffer cmd) { base.OnFinishCameraStackRendering(cmd); cmd.DisableKeyword(softShadowMaskKeyword); } } } }