// Stylized Water 3 by Staggart Creations (http://staggart.xyz) // COPYRIGHT PROTECTED UNDER THE UNITY ASSET STORE EULA (https://unity.com/legal/as-terms) // • Copying or referencing source code for the production of new asset store, or public, content is strictly prohibited! // • Uploading this file to a public repository will subject it to an automated DMCA takedown request. using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.Experimental.Rendering; using UnityEngine.Rendering; #if URP using UnityEngine.Rendering.RenderGraphModule; using UnityEngine.Rendering.Universal; namespace StylizedWater3 { public class HeightPrePass : ScriptableRenderPass { private const string profilerTag = "Water Height Prepass"; private static readonly ProfilingSampler profilerSampler = new ProfilingSampler(profilerTag); /// /// Using this as a value comparison in shader code to determine if not water is being hit /// public const float VOID_THRESHOLD = -1000f; private static readonly Color targetClearColor = new Color(VOID_THRESHOLD, 0, 0, 0); [Serializable] public class Settings { public bool enable = true; public float range = 500f; [Range(1f, 8f)] public int cellsPerUnit = 4; public int maxResolution = 4096; [Tooltip("[When in Play mode] Skips processing the height prepass for the scene-view camera. This helps keep the rendering centered around the main camera when in Play mode.")] public bool disableInSceneView = true; /// /// Returns the enabled state, either from the settings or if forced because it is required by other functionality /// public bool isEnabled => enable || StylizedWaterRenderFeature.RequireHeightPrePass || HeightQuerySystem.RequiresHeightPrepass; } //Render pass FilteringSettings m_FilteringSettings; RenderStateBlock m_RenderStateBlock; private readonly List m_ShaderTagIdList = new List() { new (ShaderParams.LightModes.WaterHeight) }; public HeightPrePass() { m_FilteringSettings = new FilteringSettings(RenderQueueRange.all, LayerMask.GetMask("Water")); m_RenderStateBlock = new RenderStateBlock(RenderStateMask.Nothing); } public const string BufferName = "_WaterHeightBuffer"; public static readonly int _WaterHeightBuffer = Shader.PropertyToID(BufferName); private const string CoordsName = "_WaterHeightCoords"; private static readonly int _WaterHeightCoords = Shader.PropertyToID(CoordsName); public static readonly int _WaterHeightPrePassAvailable = Shader.PropertyToID("_WaterHeightPrePassAvailable"); private int resolution; private int m_resolution; private Settings settings; private RendererListParams rendererListParams; private RendererList rendererList; public sealed class RenderTargetDebugContext : RenderTargetDebugger.RenderTarget { public RenderTargetDebugContext() { this.name = "Height Buffer"; this.description = "Water geometry height (red channel). Height displacement from effects (green channel)." + "\n\nUsed by: Water decals, GPU-based buoyancy"; this.textureName = BufferName; this.propertyID = _WaterHeightBuffer; this.order = 0; } } public void Setup(Settings settings) { this.settings = settings; resolution = PlanarProjection.CalculateResolution(settings.range, settings.cellsPerUnit, 16, settings.maxResolution); } public class FrameData : ContextItem { public TextureHandle _WaterHeightBuffer; public override void Reset() { _WaterHeightBuffer = TextureHandle.nullHandle; } } private class PassData { public RendererListHandle rendererListHandle; public TextureHandle renderTarget; public PlanarProjection planarProjection; public Vector4 rendererCoords; } public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameContext) { var renderingData = frameContext.Get(); var cameraData = frameContext.Get(); var lightData = frameContext.Get(); DrawingSettings drawingSettings = CreateDrawingSettings(m_ShaderTagIdList, renderingData, cameraData, lightData, SortingCriteria.RenderQueue | SortingCriteria.SortingLayer | SortingCriteria.CommonTransparent); drawingSettings.perObjectData = PerObjectData.None; rendererListParams.cullingResults = renderingData.cullResults; rendererListParams.drawSettings = drawingSettings; rendererListParams.filteringSettings = m_FilteringSettings; using(var builder = renderGraph.AddRasterRenderPass("Water Height Pre-pass", out var passData)) { //Render target RenderTextureDescriptor renderTargetDescriptor = new RenderTextureDescriptor(resolution, resolution, GraphicsFormat.R16G16_SFloat, 0); passData.renderTarget = UniversalRenderer.CreateRenderGraphTexture(renderGraph, renderTargetDescriptor, BufferName, true, FilterMode.Bilinear, TextureWrapMode.Clamp); //Store render target in RG, so it can be retrieved in other passes FrameData frameData = frameContext.GetOrCreate(); frameData._WaterHeightBuffer = passData.renderTarget; //Mark the texture as readable //builder.UseTexture(passData.renderTarget, AccessFlags.ReadWrite); #if UNITY_EDITOR || DEVELOPMENT_BUILD if (RenderTargetDebugger.InspectedProperty == _WaterHeightBuffer) { StylizedWaterRenderFeature.DebugData debugData = frameContext.Get(); debugData.currentHandle = passData.renderTarget; } #endif passData.rendererListHandle = renderGraph.CreateRendererList(rendererListParams); passData.planarProjection = new PlanarProjection { center = cameraData.camera.transform.position, scale = settings.range, offset = cameraData.camera.transform.forward * ((settings.range * 0.5f) - 5), resolution = resolution }; passData.planarProjection.Recalculate(); passData.planarProjection.SetUV(ref passData.rendererCoords); //Set render target and bind to global property builder.SetRenderAttachment(passData.renderTarget, 0, AccessFlags.Write); //builder.CreateTransientTexture(passData.renderTarget); builder.SetGlobalTextureAfterPass(passData.renderTarget, _WaterHeightBuffer); builder.UseRendererList(passData.rendererListHandle); builder.AllowGlobalStateModification(true); builder.AllowPassCulling(false); builder.SetRenderFunc((PassData data, RasterGraphContext context) => { Execute(context, data); }); } } private readonly int _WorldSpaceCameraPos = Shader.PropertyToID("_WorldSpaceCameraPos"); private void Execute(RasterGraphContext context, PassData data) { var cmd = context.cmd; using (new ProfilingScope(cmd, profilerSampler)) { cmd.ClearRenderTarget(true, true, targetClearColor); cmd.SetGlobalInt(_WaterHeightPrePassAvailable, 1); cmd.EnableShaderKeyword(ShaderParams.Keywords.WaterHeightPass); cmd.SetViewProjectionMatrices(data.planarProjection.view, data.planarProjection.projection); //RenderingUtils.SetViewAndProjectionMatrices(cmd, data.planarProjection.view, data.planarProjection.projection, true); cmd.SetViewport(data.planarProjection.viewportRect); //Is this still needed? //cmd.SetGlobalMatrix("UNITY_MATRIX_V", data.view); cmd.SetGlobalVector(_WaterHeightCoords, data.rendererCoords); //Bug? During this pass the camera position sent to shaderland is that of the scene-view camera (if the tab is open) //Possibly the value from the previous frame/camera. This breaks distance-based effects such as waves. //Force an updated value cmd.SetGlobalVector(_WorldSpaceCameraPos, data.planarProjection.center); cmd.DrawRendererList(data.rendererListHandle); //Reset (breaks VR!) //cmd.SetViewProjectionMatrices(data.ViewMatrix, data.GPUProjectionMatrix); } } public override void OnCameraCleanup(CommandBuffer cmd) { cmd.SetGlobalVector(_WaterHeightCoords, Vector4.zero); cmd.SetGlobalInt(_WaterHeightPrePassAvailable, 0); cmd.DisableShaderKeyword(ShaderParams.Keywords.WaterHeightPass); } public void Dispose() { } #pragma warning disable CS0672 #pragma warning disable CS0618 public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { } #pragma warning restore CS0672 #pragma warning restore CS0618 } } #endif