using System.Collections.Generic; using UnityEngine; using UnityEngine.Experimental.Rendering; using UnityEngine.Rendering; #if URP using UnityEngine.Rendering.Universal; using UnityEngine.VFX; namespace StylizedWater2.DynamicEffects { public class DataRenderPass : ScriptableRenderPass { private const string profilerTag = "Water Dynamic Effects: Data"; private static readonly ProfilingSampler profilerSampler = new ProfilingSampler(profilerTag); private const string LIGHTMODE_TAG = "WaterDynamicEffect"; private float renderRange; private float m_renderRange; private float fadeRange; private int resolution; private bool highPrecision; private bool m_highPrecision; private RTHandle renderTarget; private const string WaterDynamicEffectsBufferName = "_WaterDynamicEffectsBuffer"; private static readonly int _WaterDynamicEffectsBufferID = Shader.PropertyToID(WaterDynamicEffectsBufferName); private const string WaterDynamicEffectsCoordsName = "_WaterDynamicEffectsCoords"; private static readonly int _WaterDynamicEffectsCoords = Shader.PropertyToID(WaterDynamicEffectsCoordsName); public static Vector4 rendererCoords; private static Matrix4x4 projection { set; get; } private static Matrix4x4 view { set; get; } private static Vector3 centerPosition; private static int CurrentResolution; private static float orthoSize; private static Color m_clearColor = new Color(0, 0, 0, 1); private static readonly Quaternion viewRotation = Quaternion.Euler(new Vector3(90f, 0f, 0f)); private static readonly Vector3 viewScale = new Vector3(1, 1, -1); private static Rect viewportRect; //Render pass FilteringSettings m_FilteringSettings; RenderStateBlock m_RenderStateBlock; private readonly List m_ShaderTagIdList = new List() { new ShaderTagId(LIGHTMODE_TAG), //new ShaderTagId("UniversalForward") }; //private static readonly Plane[] frustrumPlanes = new Plane[6]; #if UNITY_2023_1_OR_NEWER private RendererListParams rendererListParams; private RendererList rendererList; #endif private bool enableVFX; public static List visualEffects = new List(); public DataRenderPass() { m_FilteringSettings = new FilteringSettings(RenderQueueRange.all, -1); m_RenderStateBlock = new RenderStateBlock(RenderStateMask.Nothing); } #if UNITY_6000_0_OR_NEWER //Silence warning spam public override void RecordRenderGraph(UnityEngine.Rendering.RenderGraphModule.RenderGraph renderGraph, ContextContainer frameData) { } #endif public void Setup(ref WaterDynamicEffectsRenderFeature.Settings settings, int targetResolution) { this.renderRange = settings.renderRange; //Percentage to units this.fadeRange = (settings.renderRange * 0.5f) * (settings.fadePercentage / 100f); this.enableVFX = settings.enableVFXGraphHooks; this.resolution = targetResolution; this.highPrecision = settings.highPrecision; } //Important to snap the projection to the nearest texel. Otherwise pixel swimming is introduced when moving, due to bilinear filtering private static Vector3 StabilizeProjection(Vector3 pos, float texelSize) { float Snap(float coord, float cellSize) => Mathf.FloorToInt(coord / cellSize) * (cellSize) + (cellSize * 0.5f); return new Vector3(Snap(pos.x, texelSize), Snap(pos.y, texelSize), Snap(pos.z, texelSize)); } private void SetupProjection(CommandBuffer cmd, Camera camera) { centerPosition = camera.transform.position + (camera.transform.forward * (orthoSize - fadeRange)); centerPosition = StabilizeProjection(centerPosition, (orthoSize * 2f) / resolution); //var frustumHeight = 2.0f * renderRange * Mathf.Tan(camera.fieldOfView * 0.5f * Mathf.Deg2Rad); //Still clips, plus doesn't support orthographc var frustumHeight = orthoSize * 2f; centerPosition += (Vector3.up * frustumHeight * 0.5f); projection = Matrix4x4.Ortho(-orthoSize, orthoSize, -orthoSize, orthoSize, 0.03f, frustumHeight); view = Matrix4x4.TRS(centerPosition, viewRotation, viewScale).inverse; cmd.SetViewProjectionMatrices(view, projection); //RenderingUtils.SetViewAndProjectionMatrices(cmd, view, projection, true); viewportRect.width = resolution; viewportRect.height = resolution; cmd.SetViewport(viewportRect); //Don't actually need this, not performing any AABB operations //GeometryUtility.CalculateFrustumPlanes(projection * view, frustrumPlanes); //Position/scale of projection. Converted to a UV in the shader rendererCoords.x = centerPosition.x - orthoSize; rendererCoords.y = centerPosition.z - orthoSize; rendererCoords.z = orthoSize * 2f; rendererCoords.w = 1f; //Enable in shader cmd.SetGlobalVector(_WaterDynamicEffectsCoords, rendererCoords); } private void SetupVFX() { if (enableVFX) { foreach (VisualEffect vfx in visualEffects) { if (!vfx) continue; vfx.SetTexture(WaterDynamicEffectsBufferName, renderTarget); vfx.SetVector4(WaterDynamicEffectsCoordsName, rendererCoords); } } } #if UNITY_6000_0_OR_NEWER #pragma warning disable CS0672 #pragma warning disable CS0618 #endif public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor) { orthoSize = renderRange * 0.5f; if (resolution != CurrentResolution || highPrecision != m_highPrecision ||renderTarget == null) { RTHandles.Release(renderTarget); renderTarget = RTHandles.Alloc(resolution, resolution, 1, DepthBits.None, highPrecision ? GraphicsFormat.R16G16B16A16_SFloat : GraphicsFormat.R8G8B8A8_SNorm, filterMode: FilterMode.Bilinear, wrapMode: TextureWrapMode.Clamp, useMipMap: false, //TODO: Expose option autoGenerateMips: true, name: WaterDynamicEffectsBufferName); } CurrentResolution = resolution; m_highPrecision = highPrecision; cmd.SetGlobalTexture(_WaterDynamicEffectsBufferID, renderTarget); cmd.SetGlobalInt("_WaterDynamicEffectsHighPrecision", highPrecision ? 1 : 0); ConfigureTarget(renderTarget); ConfigureClear(ClearFlag.Color, m_clearColor); } public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { CommandBuffer cmd = CommandBufferPool.Get(); DrawingSettings drawingSettings = CreateDrawingSettings(m_ShaderTagIdList, ref renderingData, SortingCriteria.RenderQueue | SortingCriteria.SortingLayer | SortingCriteria.CommonTransparent); drawingSettings.perObjectData = PerObjectData.None; using (new ProfilingScope(cmd, profilerSampler)) { ref CameraData cameraData = ref renderingData.cameraData; SetupProjection(cmd, cameraData.camera); //Execute current commands first context.ExecuteCommandBuffer(cmd); cmd.Clear(); #if UNITY_2023_1_OR_NEWER rendererListParams.cullingResults = renderingData.cullResults; rendererListParams.drawSettings = drawingSettings; rendererListParams.filteringSettings = m_FilteringSettings; rendererList = context.CreateRendererList(ref rendererListParams); cmd.DrawRendererList(rendererList); #else context.DrawRenderers(renderingData.cullResults, ref drawingSettings, ref m_FilteringSettings, ref m_RenderStateBlock); #endif //context.ExecuteCommandBuffer(cmd); //Restore //cmd.Clear(); cmd.SetViewProjectionMatrices(cameraData.camera.worldToCameraMatrix, cameraData.camera.projectionMatrix); //RenderingUtils.SetViewAndProjectionMatrices(cmd, cameraData.GetViewMatrix(), cameraData.GetGPUProjectionMatrix(), false); } context.ExecuteCommandBuffer(cmd); CommandBufferPool.Release(cmd); SetupVFX(); } public override void FrameCleanup(CommandBuffer cmd) { rendererCoords.w = 0f; //Boolean in shader code, disables sampling cmd.SetGlobalVector(_WaterDynamicEffectsCoords, rendererCoords); cmd.DisableShaderKeyword(WaterDynamicEffectsRenderFeature.KEYWORD); } public void Dispose() { Shader.SetGlobalVector(_WaterDynamicEffectsCoords, Vector4.zero); RTHandles.Release(renderTarget); } } } #endif