using System; using UnityEngine.Experimental.Rendering; namespace UnityEngine.Rendering.PostProcessing { // Multi-scale volumetric obscurance // TODO: Fix VR support [UnityEngine.Scripting.Preserve] [Serializable] internal sealed class MultiScaleVO : IAmbientOcclusionMethod { internal enum MipLevel { Original, L1, L2, L3, L4, L5, L6 } enum Pass { DepthCopy, CompositionDeferred, CompositionForward, DebugOverlay } // The arrays below are reused between frames to reduce GC allocation. readonly float[] m_SampleThickness = { Mathf.Sqrt(1f - 0.2f * 0.2f), Mathf.Sqrt(1f - 0.4f * 0.4f), Mathf.Sqrt(1f - 0.6f * 0.6f), Mathf.Sqrt(1f - 0.8f * 0.8f), Mathf.Sqrt(1f - 0.2f * 0.2f - 0.2f * 0.2f), Mathf.Sqrt(1f - 0.2f * 0.2f - 0.4f * 0.4f), Mathf.Sqrt(1f - 0.2f * 0.2f - 0.6f * 0.6f), Mathf.Sqrt(1f - 0.2f * 0.2f - 0.8f * 0.8f), Mathf.Sqrt(1f - 0.4f * 0.4f - 0.4f * 0.4f), Mathf.Sqrt(1f - 0.4f * 0.4f - 0.6f * 0.6f), Mathf.Sqrt(1f - 0.4f * 0.4f - 0.8f * 0.8f), Mathf.Sqrt(1f - 0.6f * 0.6f - 0.6f * 0.6f) }; readonly float[] m_InvThicknessTable = new float[12]; readonly float[] m_SampleWeightTable = new float[12]; readonly int[] m_Widths = new int[7]; readonly int[] m_Heights = new int[7]; // Scaled dimensions used with dynamic resolution readonly int[] m_ScaledWidths = new int[7]; readonly int[] m_ScaledHeights = new int[7]; AmbientOcclusion m_Settings; PropertySheet m_PropertySheet; PostProcessResources m_Resources; // Can't use a temporary because we need to share it between cmdbuffers - also fixes a weird // command buffer warning RenderTexture m_AmbientOnlyAO; RenderTextureFormat m_R8Format; RenderTextureFormat m_R16Format; bool float4Texture = false; readonly RenderTargetIdentifier[] m_MRT = { BuiltinRenderTextureType.GBuffer0, // Albedo, Occ BuiltinRenderTextureType.CameraTarget // Ambient }; public MultiScaleVO(AmbientOcclusion settings) { m_Settings = settings; // R16 is not supported on all platforms as a storage texture format // R8 is not supported on all platforms as a storage texture format m_R8Format = RenderTextureFormat.R8; m_R16Format = RenderTextureFormat.RHalf; #if UNITY_2023_2_OR_NEWER if (!SystemInfo.IsFormatSupported(GraphicsFormatUtility.GetGraphicsFormat(m_R8Format, false), GraphicsFormatUsage.LoadStore)) { if (SystemInfo.IsFormatSupported(GraphicsFormatUtility.GetGraphicsFormat(RenderTextureFormat.ARGB32, false), GraphicsFormatUsage.LoadStore)) { m_R8Format = RenderTextureFormat.ARGB32; float4Texture = true; } } if (!SystemInfo.IsFormatSupported(GraphicsFormatUtility.GetGraphicsFormat(m_R16Format, false), GraphicsFormatUsage.LoadStore)) { if (SystemInfo.IsFormatSupported(GraphicsFormatUtility.GetGraphicsFormat(RenderTextureFormat.RFloat, false), GraphicsFormatUsage.LoadStore)) { m_R16Format = RenderTextureFormat.RFloat; } } #else if (!SystemInfo.IsFormatSupported(GraphicsFormatUtility.GetGraphicsFormat(m_R8Format, false), FormatUsage.LoadStore)) { if (SystemInfo.IsFormatSupported(GraphicsFormatUtility.GetGraphicsFormat(RenderTextureFormat.ARGB32, false), FormatUsage.LoadStore)) { m_R8Format = RenderTextureFormat.ARGB32; float4Texture = true; } } if (!SystemInfo.IsFormatSupported(GraphicsFormatUtility.GetGraphicsFormat(m_R16Format, false), FormatUsage.LoadStore)) { if (SystemInfo.IsFormatSupported(GraphicsFormatUtility.GetGraphicsFormat(RenderTextureFormat.RFloat, false), FormatUsage.LoadStore)) { m_R16Format = RenderTextureFormat.RFloat; } } #endif } public DepthTextureMode GetCameraFlags() { return DepthTextureMode.Depth; } // Special case for AO [because SRPs], please don't do this in other effects, it's bad // practice in this framework public void SetResources(PostProcessResources resources) { m_Resources = resources; } void Alloc(CommandBuffer cmd, int id, MipLevel size, RenderTextureFormat format, bool uav, bool dynamicScale) { int sizeId = (int)size; cmd.GetTemporaryRT(id, new RenderTextureDescriptor { #if UNITY_2019_4_OR_NEWER width = m_Widths[sizeId], height = m_Heights[sizeId], #else width = m_ScaledWidths[sizeId], height = m_ScaledHeights[sizeId], #endif colorFormat = format, depthBufferBits = 0, volumeDepth = 1, autoGenerateMips = false, msaaSamples = 1, #if UNITY_2019_2_OR_NEWER mipCount = 1, #endif #if UNITY_2019_4_OR_NEWER useDynamicScale = dynamicScale, #endif enableRandomWrite = uav, dimension = TextureDimension.Tex2D, sRGB = false }, FilterMode.Point); } void AllocArray(CommandBuffer cmd, int id, MipLevel size, RenderTextureFormat format, bool uav, bool dynamicScale) { int sizeId = (int)size; cmd.GetTemporaryRT(id, new RenderTextureDescriptor { #if UNITY_2019_4_OR_NEWER width = m_Widths[sizeId], height = m_Heights[sizeId], #else width = m_ScaledWidths[sizeId], height = m_ScaledHeights[sizeId], #endif colorFormat = format, depthBufferBits = 0, volumeDepth = 16, autoGenerateMips = false, msaaSamples = 1, #if UNITY_2019_2_OR_NEWER mipCount = 1, #endif #if UNITY_2019_4_OR_NEWER useDynamicScale = dynamicScale, #endif enableRandomWrite = uav, dimension = TextureDimension.Tex2DArray, sRGB = false }, FilterMode.Point); } void Release(CommandBuffer cmd, int id) { cmd.ReleaseTemporaryRT(id); } // Calculate values in _ZBuferParams (built-in shader variable) // We can't use _ZBufferParams in compute shaders, so this function is // used to give the values in it to compute shaders. Vector4 CalculateZBufferParams(Camera camera) { float fpn = camera.farClipPlane / camera.nearClipPlane; if (SystemInfo.usesReversedZBuffer) return new Vector4(fpn - 1f, 1f, 0f, 0f); return new Vector4(1f - fpn, fpn, 0f, 0f); } float CalculateTanHalfFovHeight(Camera camera) { return 1f / camera.projectionMatrix[0, 0]; } Vector2 GetSize(MipLevel mip) { return new Vector2(m_ScaledWidths[(int)mip], m_ScaledHeights[(int)mip]); } Vector3 GetSizeArray(MipLevel mip) { return new Vector3(m_ScaledWidths[(int)mip], m_ScaledHeights[(int)mip], 16); } public void GenerateAOMap(CommandBuffer cmd, Camera camera, RenderTargetIdentifier destination, RenderTargetIdentifier? depthMap, bool invert, bool isMSAA) { // Base size m_Widths[0] = m_ScaledWidths[0] = camera.pixelWidth * (RuntimeUtilities.isSinglePassStereoEnabled ? 2 : 1); m_Heights[0] = m_ScaledHeights[0] = camera.pixelHeight; #if UNITY_2017_3_OR_NEWER m_ScaledWidths[0] = camera.scaledPixelWidth * (RuntimeUtilities.isSinglePassStereoEnabled ? 2 : 1); m_ScaledHeights[0] = camera.scaledPixelHeight; #endif float widthScalingFactor = ScalableBufferManager.widthScaleFactor; float heightScalingFactor = ScalableBufferManager.heightScaleFactor; // L1 -> L6 sizes for (int i = 1; i < 7; i++) { int div = 1 << i; m_Widths[i] = (m_Widths[0] + (div - 1)) / div; m_Heights[i] = (m_Heights[0] + (div - 1)) / div; m_ScaledWidths[i] = Mathf.CeilToInt(m_Widths[i] * widthScalingFactor); m_ScaledHeights[i] = Mathf.CeilToInt(m_Heights[i] * heightScalingFactor); } // Allocate temporary textures PushAllocCommands(cmd, isMSAA, camera); // Render logic PushDownsampleCommands(cmd, camera, depthMap, isMSAA); float tanHalfFovH = CalculateTanHalfFovHeight(camera); PushRenderCommands(cmd, ShaderIDs.TiledDepth1, ShaderIDs.Occlusion1, GetSizeArray(MipLevel.L3), tanHalfFovH, isMSAA); PushRenderCommands(cmd, ShaderIDs.TiledDepth2, ShaderIDs.Occlusion2, GetSizeArray(MipLevel.L4), tanHalfFovH, isMSAA); PushRenderCommands(cmd, ShaderIDs.TiledDepth3, ShaderIDs.Occlusion3, GetSizeArray(MipLevel.L5), tanHalfFovH, isMSAA); PushRenderCommands(cmd, ShaderIDs.TiledDepth4, ShaderIDs.Occlusion4, GetSizeArray(MipLevel.L6), tanHalfFovH, isMSAA); PushUpsampleCommands(cmd, ShaderIDs.LowDepth4, ShaderIDs.Occlusion4, ShaderIDs.LowDepth3, ShaderIDs.Occlusion3, ShaderIDs.Combined3, GetSize(MipLevel.L4), GetSize(MipLevel.L3), isMSAA); PushUpsampleCommands(cmd, ShaderIDs.LowDepth3, ShaderIDs.Combined3, ShaderIDs.LowDepth2, ShaderIDs.Occlusion2, ShaderIDs.Combined2, GetSize(MipLevel.L3), GetSize(MipLevel.L2), isMSAA); PushUpsampleCommands(cmd, ShaderIDs.LowDepth2, ShaderIDs.Combined2, ShaderIDs.LowDepth1, ShaderIDs.Occlusion1, ShaderIDs.Combined1, GetSize(MipLevel.L2), GetSize(MipLevel.L1), isMSAA); PushUpsampleCommands(cmd, ShaderIDs.LowDepth1, ShaderIDs.Combined1, ShaderIDs.LinearDepth, null, destination, GetSize(MipLevel.L1), GetSize(MipLevel.Original), isMSAA, invert); // Cleanup PushReleaseCommands(cmd); } void PushAllocCommands(CommandBuffer cmd, bool isMSAA, Camera camera) { bool dynamicResolutionEnabled = RuntimeUtilities.IsDynamicResolutionEnabled(camera); if (isMSAA) { Alloc(cmd, ShaderIDs.LinearDepth, MipLevel.Original, RenderTextureFormat.RGHalf, true, dynamicResolutionEnabled); Alloc(cmd, ShaderIDs.LowDepth1, MipLevel.L1, RenderTextureFormat.RGFloat, true, dynamicResolutionEnabled); Alloc(cmd, ShaderIDs.LowDepth2, MipLevel.L2, RenderTextureFormat.RGFloat, true, dynamicResolutionEnabled); Alloc(cmd, ShaderIDs.LowDepth3, MipLevel.L3, RenderTextureFormat.RGFloat, true, dynamicResolutionEnabled); Alloc(cmd, ShaderIDs.LowDepth4, MipLevel.L4, RenderTextureFormat.RGFloat, true, dynamicResolutionEnabled); AllocArray(cmd, ShaderIDs.TiledDepth1, MipLevel.L3, RenderTextureFormat.RGHalf, true, dynamicResolutionEnabled); AllocArray(cmd, ShaderIDs.TiledDepth2, MipLevel.L4, RenderTextureFormat.RGHalf, true, dynamicResolutionEnabled); AllocArray(cmd, ShaderIDs.TiledDepth3, MipLevel.L5, RenderTextureFormat.RGHalf, true, dynamicResolutionEnabled); AllocArray(cmd, ShaderIDs.TiledDepth4, MipLevel.L6, RenderTextureFormat.RGHalf, true, dynamicResolutionEnabled); Alloc(cmd, ShaderIDs.Occlusion1, MipLevel.L1, RenderTextureFormat.RG16, true, dynamicResolutionEnabled); Alloc(cmd, ShaderIDs.Occlusion2, MipLevel.L2, RenderTextureFormat.RG16, true, dynamicResolutionEnabled); Alloc(cmd, ShaderIDs.Occlusion3, MipLevel.L3, RenderTextureFormat.RG16, true, dynamicResolutionEnabled); Alloc(cmd, ShaderIDs.Occlusion4, MipLevel.L4, RenderTextureFormat.RG16, true, dynamicResolutionEnabled); Alloc(cmd, ShaderIDs.Combined1, MipLevel.L1, RenderTextureFormat.RG16, true, dynamicResolutionEnabled); Alloc(cmd, ShaderIDs.Combined2, MipLevel.L2, RenderTextureFormat.RG16, true, dynamicResolutionEnabled); Alloc(cmd, ShaderIDs.Combined3, MipLevel.L3, RenderTextureFormat.RG16, true, dynamicResolutionEnabled); } else { Alloc(cmd, ShaderIDs.LinearDepth, MipLevel.Original, m_R16Format, true, dynamicResolutionEnabled); Alloc(cmd, ShaderIDs.LowDepth1, MipLevel.L1, RenderTextureFormat.RFloat, true, dynamicResolutionEnabled); Alloc(cmd, ShaderIDs.LowDepth2, MipLevel.L2, RenderTextureFormat.RFloat, true, dynamicResolutionEnabled); Alloc(cmd, ShaderIDs.LowDepth3, MipLevel.L3, RenderTextureFormat.RFloat, true, dynamicResolutionEnabled); Alloc(cmd, ShaderIDs.LowDepth4, MipLevel.L4, RenderTextureFormat.RFloat, true, dynamicResolutionEnabled); AllocArray(cmd, ShaderIDs.TiledDepth1, MipLevel.L3, m_R16Format, true, dynamicResolutionEnabled); AllocArray(cmd, ShaderIDs.TiledDepth2, MipLevel.L4, m_R16Format, true, dynamicResolutionEnabled); AllocArray(cmd, ShaderIDs.TiledDepth3, MipLevel.L5, m_R16Format, true, dynamicResolutionEnabled); AllocArray(cmd, ShaderIDs.TiledDepth4, MipLevel.L6, m_R16Format, true, dynamicResolutionEnabled); Alloc(cmd, ShaderIDs.Occlusion1, MipLevel.L1, m_R8Format, true, dynamicResolutionEnabled); Alloc(cmd, ShaderIDs.Occlusion2, MipLevel.L2, m_R8Format, true, dynamicResolutionEnabled); Alloc(cmd, ShaderIDs.Occlusion3, MipLevel.L3, m_R8Format, true, dynamicResolutionEnabled); Alloc(cmd, ShaderIDs.Occlusion4, MipLevel.L4, m_R8Format, true, dynamicResolutionEnabled); Alloc(cmd, ShaderIDs.Combined1, MipLevel.L1, m_R8Format, true, dynamicResolutionEnabled); Alloc(cmd, ShaderIDs.Combined2, MipLevel.L2, m_R8Format, true, dynamicResolutionEnabled); Alloc(cmd, ShaderIDs.Combined3, MipLevel.L3, m_R8Format, true, dynamicResolutionEnabled); } } void PushDownsampleCommands(CommandBuffer cmd, Camera camera, RenderTargetIdentifier? depthMap, bool isMSAA) { RenderTargetIdentifier depthMapId; bool needDepthMapRelease = false; if (depthMap != null) { depthMapId = depthMap.Value; } else { // Make a copy of the depth texture, or reuse the resolved depth // buffer (it's only available in some specific situations). if (!RuntimeUtilities.IsResolvedDepthAvailable(camera)) { Alloc(cmd, ShaderIDs.DepthCopy, MipLevel.Original, RenderTextureFormat.RFloat, false, RuntimeUtilities.IsDynamicResolutionEnabled(camera)); depthMapId = new RenderTargetIdentifier(ShaderIDs.DepthCopy); cmd.BlitFullscreenTriangle(BuiltinRenderTextureType.None, depthMapId, m_PropertySheet, (int)Pass.DepthCopy); needDepthMapRelease = true; } else { depthMapId = BuiltinRenderTextureType.ResolvedDepth; } } // 1st downsampling pass. var cs = m_Resources.computeShaders.multiScaleAODownsample1; int kernel = cs.FindKernel(isMSAA ? "MultiScaleVODownsample1_MSAA" : "MultiScaleVODownsample1"); cmd.SetComputeTextureParam(cs, kernel, "LinearZ", ShaderIDs.LinearDepth); cmd.SetComputeTextureParam(cs, kernel, "DS2x", ShaderIDs.LowDepth1); cmd.SetComputeTextureParam(cs, kernel, "DS2xAtlas", ShaderIDs.TiledDepth1); cmd.SetComputeVectorParam(cs, "ZBufferParams", CalculateZBufferParams(camera)); cmd.SetComputeTextureParam(cs, kernel, "Depth", depthMapId); cmd.SetComputeTextureParam(cs, kernel, "DS4x", ShaderIDs.LowDepth2); cmd.SetComputeTextureParam(cs, kernel, "DS4xAtlas", ShaderIDs.TiledDepth2); cmd.DispatchCompute(cs, kernel, m_ScaledWidths[(int)MipLevel.L4], m_ScaledHeights[(int)MipLevel.L4], 1); if (needDepthMapRelease) Release(cmd, ShaderIDs.DepthCopy); // 2nd downsampling pass. cs = m_Resources.computeShaders.multiScaleAODownsample2; kernel = isMSAA ? cs.FindKernel("MultiScaleVODownsample2_MSAA") : cs.FindKernel("MultiScaleVODownsample2"); cmd.SetComputeTextureParam(cs, kernel, "DS4x", ShaderIDs.LowDepth2); cmd.SetComputeTextureParam(cs, kernel, "DS8x", ShaderIDs.LowDepth3); cmd.SetComputeTextureParam(cs, kernel, "DS16x", ShaderIDs.LowDepth4); cmd.SetComputeTextureParam(cs, kernel, "DS8xAtlas", ShaderIDs.TiledDepth3); cmd.SetComputeTextureParam(cs, kernel, "DS16xAtlas", ShaderIDs.TiledDepth4); cmd.DispatchCompute(cs, kernel, m_ScaledWidths[(int)MipLevel.L6], m_ScaledHeights[(int)MipLevel.L6], 1); } void PushRenderCommands(CommandBuffer cmd, int source, int destination, Vector3 sourceSize, float tanHalfFovH, bool isMSAA) { // Here we compute multipliers that convert the center depth value into (the reciprocal // of) sphere thicknesses at each sample location. This assumes a maximum sample radius // of 5 units, but since a sphere has no thickness at its extent, we don't need to // sample that far out. Only samples whole integer offsets with distance less than 25 // are used. This means that there is no sample at (3, 4) because its distance is // exactly 25 (and has a thickness of 0.) // The shaders are set up to sample a circular region within a 5-pixel radius. const float kScreenspaceDiameter = 10f; // SphereDiameter = CenterDepth * ThicknessMultiplier. This will compute the thickness // of a sphere centered at a specific depth. The ellipsoid scale can stretch a sphere // into an ellipsoid, which changes the characteristics of the AO. // TanHalfFovH: Radius of sphere in depth units if its center lies at Z = 1 // ScreenspaceDiameter: Diameter of sample sphere in pixel units // ScreenspaceDiameter / BufferWidth: Ratio of the screen width that the sphere actually covers float thicknessMultiplier = 2f * tanHalfFovH * kScreenspaceDiameter / sourceSize.x; if (RuntimeUtilities.isSinglePassStereoEnabled) thicknessMultiplier *= 2f; // This will transform a depth value from [0, thickness] to [0, 1]. float inverseRangeFactor = 1f / thicknessMultiplier; // The thicknesses are smaller for all off-center samples of the sphere. Compute // thicknesses relative to the center sample. for (int i = 0; i < 12; i++) m_InvThicknessTable[i] = inverseRangeFactor / m_SampleThickness[i]; // These are the weights that are multiplied against the samples because not all samples // are equally important. The farther the sample is from the center location, the less // they matter. We use the thickness of the sphere to determine the weight. The scalars // in front are the number of samples with this weight because we sum the samples // together before multiplying by the weight, so as an aggregate all of those samples // matter more. After generating this table, the weights are normalized. m_SampleWeightTable[0] = 4 * m_SampleThickness[0]; // Axial m_SampleWeightTable[1] = 4 * m_SampleThickness[1]; // Axial m_SampleWeightTable[2] = 4 * m_SampleThickness[2]; // Axial m_SampleWeightTable[3] = 4 * m_SampleThickness[3]; // Axial m_SampleWeightTable[4] = 4 * m_SampleThickness[4]; // Diagonal m_SampleWeightTable[5] = 8 * m_SampleThickness[5]; // L-shaped m_SampleWeightTable[6] = 8 * m_SampleThickness[6]; // L-shaped m_SampleWeightTable[7] = 8 * m_SampleThickness[7]; // L-shaped m_SampleWeightTable[8] = 4 * m_SampleThickness[8]; // Diagonal m_SampleWeightTable[9] = 8 * m_SampleThickness[9]; // L-shaped m_SampleWeightTable[10] = 8 * m_SampleThickness[10]; // L-shaped m_SampleWeightTable[11] = 4 * m_SampleThickness[11]; // Diagonal // Zero out the unused samples. // FIXME: should we support SAMPLE_EXHAUSTIVELY mode? m_SampleWeightTable[0] = 0; m_SampleWeightTable[2] = 0; m_SampleWeightTable[5] = 0; m_SampleWeightTable[7] = 0; m_SampleWeightTable[9] = 0; // Normalize the weights by dividing by the sum of all weights var totalWeight = 0f; foreach (float w in m_SampleWeightTable) totalWeight += w; for (int i = 0; i < m_SampleWeightTable.Length; i++) m_SampleWeightTable[i] /= totalWeight; // Set the arguments for the render kernel. var cs = m_Resources.computeShaders.multiScaleAORender; string kernelName = isMSAA ? "MultiScaleVORender_MSAA_interleaved" : "MultiScaleVORender_interleaved"; if (float4Texture) kernelName += "_Float4"; int kernel = cs.FindKernel(kernelName); cmd.SetComputeFloatParams(cs, "gInvThicknessTable", m_InvThicknessTable); cmd.SetComputeFloatParams(cs, "gSampleWeightTable", m_SampleWeightTable); cmd.SetComputeVectorParam(cs, "gInvSliceDimension", new Vector2(1f / sourceSize.x, 1f / sourceSize.y)); cmd.SetComputeVectorParam(cs, "AdditionalParams", new Vector3(-1f / m_Settings.thicknessModifier.value, m_Settings.intensity.value, m_Settings.zBias.value)); cmd.SetComputeTextureParam(cs, kernel, "DepthTex", source); cmd.SetComputeTextureParam(cs, kernel, "Occlusion", destination); // Calculate the thread group count and add a dispatch command with them. uint xsize, ysize, zsize; cs.GetKernelThreadGroupSizes(kernel, out xsize, out ysize, out zsize); cmd.DispatchCompute( cs, kernel, ((int)sourceSize.x + (int)xsize - 1) / (int)xsize, ((int)sourceSize.y + (int)ysize - 1) / (int)ysize, ((int)sourceSize.z + (int)zsize - 1) / (int)zsize ); } void PushUpsampleCommands(CommandBuffer cmd, int lowResDepth, int interleavedAO, int highResDepth, int? highResAO, RenderTargetIdentifier dest, Vector3 lowResDepthSize, Vector2 highResDepthSize, bool isMSAA, bool invert = false) { var cs = m_Resources.computeShaders.multiScaleAOUpsample; int kernel = 0; if (!isMSAA) { string kernelName = highResAO == null ? invert ? "MultiScaleVOUpSample_invert" : "MultiScaleVOUpSample" : "MultiScaleVOUpSample_blendout"; if (float4Texture) { kernelName += "_Float4"; } kernel = cs.FindKernel(kernelName); } else { string kernelName = highResAO == null ? invert ? "MultiScaleVOUpSample_MSAA_invert" : "MultiScaleVOUpSample_MSAA" : "MultiScaleVOUpSample_MSAA_blendout"; if (float4Texture) { kernelName += "_Float4"; } kernel = cs.FindKernel(kernelName); } float stepSize = 1920f / lowResDepthSize.x; float bTolerance = 1f - Mathf.Pow(10f, m_Settings.blurTolerance.value) * stepSize; bTolerance *= bTolerance; float uTolerance = Mathf.Pow(10f, m_Settings.upsampleTolerance.value); float noiseFilterWeight = 1f / (Mathf.Pow(10f, m_Settings.noiseFilterTolerance.value) + uTolerance); cmd.SetComputeVectorParam(cs, "InvLowResolution", new Vector2(1f / lowResDepthSize.x, 1f / lowResDepthSize.y)); cmd.SetComputeVectorParam(cs, "InvHighResolution", new Vector2(1f / highResDepthSize.x, 1f / highResDepthSize.y)); cmd.SetComputeVectorParam(cs, "AdditionalParams", new Vector4(noiseFilterWeight, stepSize, bTolerance, uTolerance)); cmd.SetComputeTextureParam(cs, kernel, "LoResDB", lowResDepth); cmd.SetComputeTextureParam(cs, kernel, "HiResDB", highResDepth); cmd.SetComputeTextureParam(cs, kernel, "LoResAO1", interleavedAO); if (highResAO != null) cmd.SetComputeTextureParam(cs, kernel, "HiResAO", highResAO.Value); cmd.SetComputeTextureParam(cs, kernel, "AoResult", dest); int xcount = ((int)highResDepthSize.x + 17) / 16; int ycount = ((int)highResDepthSize.y + 17) / 16; cmd.DispatchCompute(cs, kernel, xcount, ycount, 1); } void PushReleaseCommands(CommandBuffer cmd) { Release(cmd, ShaderIDs.LinearDepth); Release(cmd, ShaderIDs.LowDepth1); Release(cmd, ShaderIDs.LowDepth2); Release(cmd, ShaderIDs.LowDepth3); Release(cmd, ShaderIDs.LowDepth4); Release(cmd, ShaderIDs.TiledDepth1); Release(cmd, ShaderIDs.TiledDepth2); Release(cmd, ShaderIDs.TiledDepth3); Release(cmd, ShaderIDs.TiledDepth4); Release(cmd, ShaderIDs.Occlusion1); Release(cmd, ShaderIDs.Occlusion2); Release(cmd, ShaderIDs.Occlusion3); Release(cmd, ShaderIDs.Occlusion4); Release(cmd, ShaderIDs.Combined1); Release(cmd, ShaderIDs.Combined2); Release(cmd, ShaderIDs.Combined3); } void PreparePropertySheet(PostProcessRenderContext context) { var sheet = context.propertySheets.Get(m_Resources.shaders.multiScaleAO); sheet.ClearKeywords(); sheet.properties.SetVector(ShaderIDs.AOColor, Color.white - m_Settings.color.value); m_PropertySheet = sheet; } void CheckAOTexture(PostProcessRenderContext context) { bool AOUpdateNeeded = m_AmbientOnlyAO == null || !m_AmbientOnlyAO.IsCreated() || m_AmbientOnlyAO.width != context.width || m_AmbientOnlyAO.height != context.height; #if UNITY_2017_3_OR_NEWER bool dynamicResolutionEnabled = RuntimeUtilities.IsDynamicResolutionEnabled(context.camera); AOUpdateNeeded = AOUpdateNeeded || m_AmbientOnlyAO.useDynamicScale != dynamicResolutionEnabled; #endif if (AOUpdateNeeded) { RuntimeUtilities.Destroy(m_AmbientOnlyAO); m_AmbientOnlyAO = new RenderTexture(context.width, context.height, 0, m_R8Format, RenderTextureReadWrite.Linear) { hideFlags = HideFlags.DontSave, filterMode = FilterMode.Point, enableRandomWrite = true, #if UNITY_2017_3_OR_NEWER useDynamicScale = dynamicResolutionEnabled #endif }; m_AmbientOnlyAO.Create(); } } void PushDebug(PostProcessRenderContext context) { if (context.IsDebugOverlayEnabled(DebugOverlay.AmbientOcclusion)) context.PushDebugOverlay(context.command, m_AmbientOnlyAO, m_PropertySheet, (int)Pass.DebugOverlay); } public void RenderAfterOpaque(PostProcessRenderContext context) { var cmd = context.command; cmd.BeginSample("Ambient Occlusion"); SetResources(context.resources); PreparePropertySheet(context); CheckAOTexture(context); // In Forward mode, fog is applied at the object level in the grometry pass so we need // to apply it to AO as well or it'll drawn on top of the fog effect. if (context.camera.actualRenderingPath == RenderingPath.Forward && RenderSettings.fog) { m_PropertySheet.EnableKeyword("APPLY_FORWARD_FOG"); m_PropertySheet.properties.SetVector( ShaderIDs.FogParams, new Vector3(RenderSettings.fogDensity, RenderSettings.fogStartDistance, RenderSettings.fogEndDistance) ); } GenerateAOMap(cmd, context.camera, m_AmbientOnlyAO, null, false, false); PushDebug(context); cmd.SetGlobalTexture(ShaderIDs.MSVOcclusionTexture, m_AmbientOnlyAO); cmd.BlitFullscreenTriangle(BuiltinRenderTextureType.None, BuiltinRenderTextureType.CameraTarget, m_PropertySheet, (int)Pass.CompositionForward, RenderBufferLoadAction.Load); cmd.EndSample("Ambient Occlusion"); } public void RenderAmbientOnly(PostProcessRenderContext context) { var cmd = context.command; cmd.BeginSample("Ambient Occlusion Render"); SetResources(context.resources); PreparePropertySheet(context); CheckAOTexture(context); GenerateAOMap(cmd, context.camera, m_AmbientOnlyAO, null, false, false); PushDebug(context); cmd.EndSample("Ambient Occlusion Render"); } public void CompositeAmbientOnly(PostProcessRenderContext context) { var cmd = context.command; cmd.BeginSample("Ambient Occlusion Composite"); cmd.SetGlobalTexture(ShaderIDs.MSVOcclusionTexture, m_AmbientOnlyAO); cmd.BlitFullscreenTriangle(BuiltinRenderTextureType.None, m_MRT, BuiltinRenderTextureType.CameraTarget, m_PropertySheet, (int)Pass.CompositionDeferred); cmd.EndSample("Ambient Occlusion Composite"); } public void Release() { RuntimeUtilities.Destroy(m_AmbientOnlyAO); m_AmbientOnlyAO = null; } } }