#if UNITY_PIPELINE_URP using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; using com.zibra.liquid.Solver; using com.zibra.liquid.Bridge; namespace com.zibra.liquid { /// /// Component responsible for rendering liquid in case of URP. /// /// /// This is not used in case liquid's /// ZibraLiquid.CurrentRenderingMode /// is set to Unity Render. /// public class LiquidURPRenderComponent : ScriptableRendererFeature { #region Public Interface /// /// URP specific liquid rendering settings. /// [System.Serializable] public class LiquidURPRenderSettings { /// /// Globally defines whether liquid renders in URP. /// public bool IsEnabled = true; /// /// Injection point where we will insert liquid rendering. /// /// /// In case of URP, this parameter will be used instead of /// ZibraLiquid.CurrentInjectionPoint. /// public RenderPassEvent InjectionPoint = RenderPassEvent.AfterRenderingSkybox; } /// /// See . /// // Must be called exactly "settings" so Unity shows this as render feature settings in editor public LiquidURPRenderSettings settings = new LiquidURPRenderSettings(); /// /// Creates URP ScriptableRendererFeature. /// public override void Create() { handleSystem.Initialize(0, 0); } /// /// Adds scriptable render passes. /// public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData) { if (!settings.IsEnabled) { return; } if (renderingData.cameraData.cameraType != CameraType.Game && renderingData.cameraData.cameraType != CameraType.SceneView) { return; } Camera camera = renderingData.cameraData.camera; camera.depthTextureMode = DepthTextureMode.Depth; int liquidsToRenderCount = 0; int backgroundsToCopyCount = 0; int liquidsToVisualizeSDFCount = 0; int liquidsToUpscaleCount = 0; foreach (var liquid in ZibraLiquid.AllFluids) { if (liquid != null && liquid.Initialized) { liquidsToRenderCount++; if (liquid.EnableDownscale) { liquidsToUpscaleCount++; } if (liquid.IsBackgroundCopyNeeded(camera)) { backgroundsToCopyCount++; } if (liquid.VisualizeSceneSDF) { liquidsToVisualizeSDFCount++; } } } if (copyPasses == null || copyPasses.Length != backgroundsToCopyCount) { copyPasses = new CopyBackgroundURPRenderPass[backgroundsToCopyCount]; for (int i = 0; i < backgroundsToCopyCount; ++i) { copyPasses[i] = new CopyBackgroundURPRenderPass(settings.InjectionPoint); } } if (liquidNativePasses == null || liquidNativePasses.Length != liquidsToRenderCount) { liquidNativePasses = new LiquidNativeRenderPass[liquidsToRenderCount]; for (int i = 0; i < liquidsToRenderCount; ++i) { liquidNativePasses[i] = new LiquidNativeRenderPass(settings.InjectionPoint); } } if (liquidURPPasses == null || liquidURPPasses.Length != liquidsToRenderCount) { liquidURPPasses = new LiquidURPRenderPass[liquidsToRenderCount]; for (int i = 0; i < liquidsToRenderCount; ++i) { liquidURPPasses[i] = new LiquidURPRenderPass(settings.InjectionPoint); } } if (liquidVisualizeSDFPasses == null || liquidVisualizeSDFPasses.Length != liquidsToVisualizeSDFCount) { liquidVisualizeSDFPasses = new LiquidVisualizeSDFPass[liquidsToVisualizeSDFCount]; for (int i = 0; i < liquidsToVisualizeSDFCount; ++i) { liquidVisualizeSDFPasses[i] = new LiquidVisualizeSDFPass(settings.InjectionPoint); } } if (upscalePasses == null || upscalePasses.Length != liquidsToUpscaleCount) { upscalePasses = new LiquidUpscaleURPRenderPass[liquidsToUpscaleCount]; for (int i = 0; i < liquidsToUpscaleCount; ++i) { upscalePasses[i] = new LiquidUpscaleURPRenderPass(settings.InjectionPoint); } } int currentCopyPass = 0; int currentLiquidPass = 0; int currentVisualizeSDFPass = 0; int currentUpscalePass = 0; foreach (var liquid in ZibraLiquid.AllFluids) { if (liquid != null && liquid.IsRenderingEnabled() && ((camera.cullingMask & (1 << liquid.gameObject.layer)) != 0)) { if (liquid.IsBackgroundCopyNeeded(camera)) { copyPasses[currentCopyPass].liquid = liquid; copyPasses[currentCopyPass].handleSystem = handleSystem; copyPasses[currentCopyPass].ConfigureInput(ScriptableRenderPassInput.Color | ScriptableRenderPassInput.Depth); copyPasses[currentCopyPass].renderPassEvent = settings.InjectionPoint; renderer.EnqueuePass(copyPasses[currentCopyPass]); currentCopyPass++; } liquidNativePasses[currentLiquidPass].liquid = liquid; liquidNativePasses[currentLiquidPass].handleSystem = handleSystem; liquidNativePasses[currentLiquidPass].renderPassEvent = settings.InjectionPoint; renderer.EnqueuePass(liquidNativePasses[currentLiquidPass]); liquidURPPasses[currentLiquidPass].liquid = liquid; liquidURPPasses[currentLiquidPass].handleSystem = handleSystem; liquidURPPasses[currentLiquidPass].ConfigureInput(ScriptableRenderPassInput.Color | ScriptableRenderPassInput.Depth); liquidURPPasses[currentLiquidPass].renderPassEvent = settings.InjectionPoint; renderer.EnqueuePass(liquidURPPasses[currentLiquidPass]); currentLiquidPass++; if (liquid.VisualizeSceneSDF) { liquidVisualizeSDFPasses[currentVisualizeSDFPass].ConfigureInput( ScriptableRenderPassInput.Color | ScriptableRenderPassInput.Depth); liquidVisualizeSDFPasses[currentVisualizeSDFPass].liquid = liquid; liquidVisualizeSDFPasses[currentVisualizeSDFPass].handleSystem = handleSystem; liquidVisualizeSDFPasses[currentVisualizeSDFPass].renderPassEvent = settings.InjectionPoint; renderer.EnqueuePass(liquidVisualizeSDFPasses[currentVisualizeSDFPass]); currentVisualizeSDFPass++; } if (liquid.EnableDownscale) { upscalePasses[currentUpscalePass].liquid = liquid; upscalePasses[currentUpscalePass].handleSystem = handleSystem; upscalePasses[currentUpscalePass].renderPassEvent = settings.InjectionPoint; renderer.EnqueuePass(upscalePasses[currentUpscalePass]); currentUpscalePass++; } } } } #endregion #region Implementation details private class CopyBackgroundURPRenderPass : ScriptableRenderPass { public ZibraLiquid liquid; public RTHandleSystem handleSystem; RTHandle cameraColorTarget; public CopyBackgroundURPRenderPass(RenderPassEvent injectionPoint) { renderPassEvent = injectionPoint; } public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData) { #if UNITY_PIPELINE_URP_13_1_OR_HIGHER cameraColorTarget = renderingData.cameraData.renderer.cameraColorTargetHandle; #else cameraColorTarget = handleSystem.Alloc(renderingData.cameraData.renderer.cameraColorTarget); #endif } public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { Camera camera = renderingData.cameraData.camera; CommandBuffer cmd = CommandBufferPool.Get("ZibraLiquid.Render"); if (liquid.CameraResourcesMap.ContainsKey(camera)) { RTHandle background = handleSystem.Alloc(liquid.CameraResourcesMap[camera].Background); Blit(cmd, cameraColorTarget, background); } context.ExecuteCommandBuffer(cmd); CommandBufferPool.Release(cmd); } } private class LiquidNativeRenderPass : ScriptableRenderPass { public ZibraLiquid liquid; public RTHandleSystem handleSystem; public LiquidNativeRenderPass(RenderPassEvent injectionPoint) { renderPassEvent = injectionPoint; } public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { Camera camera = renderingData.cameraData.camera; camera.depthTextureMode = DepthTextureMode.Depth; CommandBuffer cmd = CommandBufferPool.Get("ZibraLiquid.Render"); liquid.RenderCallBack(renderingData.cameraData.camera, renderingData.cameraData.renderScale); // set initial parameters in the native plugin LiquidBridge.SubmitInstanceEvent(cmd, liquid.CurrentInstanceID, LiquidBridge.EventID.SetCameraParams, liquid.CamNativeParams[camera]); if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.Vulkan) { cmd.SetRenderTarget(liquid.Color0, RenderBufferLoadAction.DontCare, RenderBufferStoreAction.Store, liquid.Depth, RenderBufferLoadAction.DontCare, RenderBufferStoreAction.Store); cmd.ClearRenderTarget(true, true, Color.clear); } liquid.RenderLiquidNative(cmd, renderingData.cameraData.camera); context.ExecuteCommandBuffer(cmd); CommandBufferPool.Release(cmd); } } private class LiquidURPRenderPass : ScriptableRenderPass { public ZibraLiquid liquid; public RTHandleSystem handleSystem; RTHandle cameraColorTarget; static int UpscaleColorTextureID = Shader.PropertyToID("ZibraLiquid_LiquidTempColorTexture"); static int UpscaleDepthTextureID = Shader.PropertyToID("ZibraLiquid_LiquidTempDepthTexture"); RTHandle UpscaleColorTexture; RTHandle UpscaleDepthTexture; public LiquidURPRenderPass(RenderPassEvent injectionPoint) { renderPassEvent = injectionPoint; } public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData) { #if UNITY_PIPELINE_URP_13_1_OR_HIGHER cameraColorTarget = renderingData.cameraData.renderer.cameraColorTargetHandle; #else cameraColorTarget = handleSystem.Alloc(renderingData.cameraData.renderer.cameraColorTarget); #endif } public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor) { if (liquid.EnableDownscale) { RenderTextureDescriptor descriptor = cameraTextureDescriptor; Vector2Int dimensions = new Vector2Int(descriptor.width, descriptor.height); dimensions = liquid.ApplyDownscaleFactor(dimensions); descriptor.width = dimensions.x; descriptor.height = dimensions.y; descriptor.msaaSamples = 1; descriptor.colorFormat = RenderTextureFormat.ARGBHalf; descriptor.depthBufferBits = 0; cmd.GetTemporaryRT(UpscaleColorTextureID, descriptor, FilterMode.Bilinear); descriptor.colorFormat = RenderTextureFormat.Depth; descriptor.depthBufferBits = 32; cmd.GetTemporaryRT(UpscaleDepthTextureID, descriptor, FilterMode.Bilinear); UpscaleColorTexture = handleSystem.Alloc(new RenderTargetIdentifier(UpscaleColorTextureID)); UpscaleDepthTexture = handleSystem.Alloc(new RenderTargetIdentifier(UpscaleDepthTextureID)); ConfigureTarget(UpscaleColorTexture, UpscaleDepthTexture); ConfigureClear(ClearFlag.All, new Color(0, 0, 0, 0)); } else { ConfigureTarget(cameraColorTarget); // ConfigureClear seems to be persistent, so need to reset it ConfigureClear(ClearFlag.None, new Color(0, 0, 0, 0)); } } public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { Camera camera = renderingData.cameraData.camera; camera.depthTextureMode = DepthTextureMode.Depth; CommandBuffer cmd = CommandBufferPool.Get("ZibraLiquid.Render"); if (!liquid.EnableDownscale) { cmd.SetRenderTarget(cameraColorTarget); } liquid.RenderLiquidMain(cmd, camera); context.ExecuteCommandBuffer(cmd); CommandBufferPool.Release(cmd); } public override void OnCameraCleanup(CommandBuffer cmd) { if (liquid.EnableDownscale) { cmd.ReleaseTemporaryRT(UpscaleColorTextureID); cmd.ReleaseTemporaryRT(UpscaleDepthTextureID); } } } private class LiquidVisualizeSDFPass : ScriptableRenderPass { public ZibraLiquid liquid; public RTHandleSystem handleSystem; RTHandle cameraColorTarget; static int UpscaleColorTextureID = Shader.PropertyToID("ZibraLiquid_LiquidTempColorTexture"); static int UpscaleDepthTextureID = Shader.PropertyToID("ZibraLiquid_LiquidTempDepthTexture"); RTHandle UpscaleColorTexture; RTHandle UpscaleDepthTexture; public LiquidVisualizeSDFPass(RenderPassEvent injectionPoint) { renderPassEvent = injectionPoint; } public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData) { #if UNITY_PIPELINE_URP_13_1_OR_HIGHER cameraColorTarget = renderingData.cameraData.renderer.cameraColorTargetHandle; #else cameraColorTarget = handleSystem.Alloc(renderingData.cameraData.renderer.cameraColorTarget); #endif } public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor) { if (liquid.EnableDownscale) { UpscaleColorTexture = handleSystem.Alloc(new RenderTargetIdentifier(UpscaleColorTextureID)); UpscaleDepthTexture = handleSystem.Alloc(new RenderTargetIdentifier(UpscaleDepthTextureID)); ConfigureTarget(UpscaleColorTexture, UpscaleDepthTexture); } else { ConfigureTarget(cameraColorTarget); } } public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { Camera camera = renderingData.cameraData.camera; camera.depthTextureMode = DepthTextureMode.Depth; CommandBuffer cmd = CommandBufferPool.Get("ZibraLiquid.Render"); LiquidBridge.SubmitInstanceEvent(cmd, liquid.CurrentInstanceID, LiquidBridge.EventID.RenderSDF); if (liquid.EnableDownscale) { cmd.SetRenderTarget(UpscaleColorTexture, UpscaleDepthTexture); } else { cmd.SetRenderTarget(cameraColorTarget); } liquid.RenderSDFVisualization(cmd, camera); context.ExecuteCommandBuffer(cmd); CommandBufferPool.Release(cmd); } } private class LiquidUpscaleURPRenderPass : ScriptableRenderPass { public ZibraLiquid liquid; public RTHandleSystem handleSystem; static int UpscaleColorTextureID = Shader.PropertyToID("ZibraLiquid_LiquidTempColorTexture"); static int UpscaleDepthTextureID = Shader.PropertyToID("ZibraLiquid_LiquidTempDepthTexture"); RTHandle UpscaleColorTexture; RTHandle UpscaleDepthTexture; public LiquidUpscaleURPRenderPass(RenderPassEvent injectionPoint) { renderPassEvent = injectionPoint; } public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor) { UpscaleColorTexture = handleSystem.Alloc(new RenderTargetIdentifier(UpscaleColorTextureID)); UpscaleDepthTexture = handleSystem.Alloc(new RenderTargetIdentifier(UpscaleDepthTextureID)); } public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { Camera camera = renderingData.cameraData.camera; camera.depthTextureMode = DepthTextureMode.Depth; CommandBuffer cmd = CommandBufferPool.Get("ZibraLiquid.Render"); liquid.UpscaleLiquidDirect(cmd, camera, UpscaleColorTexture, UpscaleDepthTexture); context.ExecuteCommandBuffer(cmd); CommandBufferPool.Release(cmd); } } RTHandleSystem handleSystem = new RTHandleSystem(); // 1 pass per rendered liquid that requires background copy CopyBackgroundURPRenderPass[] copyPasses; // 1 pass per rendered liquid LiquidNativeRenderPass[] liquidNativePasses; // 1 pass per rendered liquid LiquidURPRenderPass[] liquidURPPasses; // 1 pass per rendered liquid with VisualizeSceneSDF enabled LiquidVisualizeSDFPass[] liquidVisualizeSDFPasses; // 1 pass per rendered liquid that have downscale enabled LiquidUpscaleURPRenderPass[] upscalePasses; #endregion } } #endif // UNITY_PIPELINE_URP