using System; using UnityEngine.Rendering; using System.Collections.Generic; using System.Runtime.InteropServices; namespace UnityEngine.Rendering.HighDefinition { // Optimized version of 'ProbeVolumeArtistParameters'. // Currently 128-bytes. // TODO: pack better. This data structure contains a bunch of UNORMs. [GenerateHLSL] internal struct ProbeVolumeEngineData { public Vector3 debugColor; public float weight; public Vector3 rcpPosFaceFade; public float rcpDistFadeLen; public Vector3 rcpNegFaceFade; public float endTimesRcpDistFadeLen; public Vector3 scale; public int payloadIndex; public Vector3 bias; public int volumeBlendMode; public Vector4 octahedralDepthScaleBias; public Vector3 resolution; public uint lightLayers; public Vector3 resolutionInverse; public float normalBiasWS; public static ProbeVolumeEngineData GetNeutralValues() { ProbeVolumeEngineData data; data.debugColor = Vector3.zero; data.weight = 0.0f; data.rcpPosFaceFade = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue); data.rcpDistFadeLen = 0; data.rcpNegFaceFade = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue); data.endTimesRcpDistFadeLen = 1; data.scale = Vector3.zero; data.payloadIndex = -1; data.bias = Vector3.zero; data.volumeBlendMode = 0; data.octahedralDepthScaleBias = Vector4.zero; data.resolution = Vector3.zero; data.lightLayers = 0; data.resolutionInverse = Vector3.zero; data.normalBiasWS = 0.0f; return data; } } [GenerateHLSL] internal enum LeakMitigationMode { NormalBias = 0, GeometricFilter, ProbeValidityFilter, OctahedralDepthOcclusionFilter } struct ProbeVolumeList { public List bounds; public List data; } public partial class HDRenderPipeline { List m_VisibleProbeVolumeBounds = null; List m_VisibleProbeVolumeData = null; internal const int k_MaxVisibleProbeVolumeCount = 512; // Static keyword is required here else we get a "DestroyBuffer can only be called from the main thread" static ComputeBuffer s_VisibleProbeVolumeBoundsBuffer = null; static ComputeBuffer s_VisibleProbeVolumeDataBuffer = null; static ComputeBuffer s_VisibleProbeVolumeBoundsBufferDefault = null; static ComputeBuffer s_VisibleProbeVolumeDataBufferDefault = null; // Is the feature globally disabled? bool m_SupportProbeVolume = false; // Pre-allocate sort keys array to max size to avoid creating allocations / garbage at runtime. uint[] m_ProbeVolumeSortKeys = new uint[k_MaxVisibleProbeVolumeCount]; static ComputeShader s_ProbeVolumeAtlasBlitCS = null; static ComputeShader s_ProbeVolumeAtlasOctahedralDepthBlitCS = null; static ComputeShader s_ProbeVolumeAtlasOctahedralDepthConvolveCS = null; static int s_ProbeVolumeAtlasBlitKernel = -1; static int s_ProbeVolumeAtlasOctahedralDepthBlitKernel = -1; static int s_ProbeVolumeAtlasOctahedralDepthConvolveKernel = -1; static ComputeBuffer s_ProbeVolumeAtlasBlitDataSHL01Buffer = null; static ComputeBuffer s_ProbeVolumeAtlasBlitDataSHL2Buffer = null; static ComputeBuffer s_ProbeVolumeAtlasBlitDataValidityBuffer = null; static ComputeBuffer s_ProbeVolumeAtlasOctahedralDepthBuffer = null; static int s_ProbeVolumeAtlasResolution; static int s_ProbeVolumeAtlasOctahedralDepthResolution; static int k_MaxProbeVolumeAtlasOctahedralDepthProbeCount; internal const int k_ProbeOctahedralDepthWidth = 8; internal const int k_ProbeOctahedralDepthHeight = 8; internal const UnityEngine.Experimental.Rendering.GraphicsFormat k_ProbeVolumeAtlasFormat = UnityEngine.Experimental.Rendering.GraphicsFormat.R16G16B16A16_SFloat; internal const UnityEngine.Experimental.Rendering.GraphicsFormat k_ProbeVolumeOctahedralDepthAtlasFormat = UnityEngine.Experimental.Rendering.GraphicsFormat.R32G32_SFloat; // float2(mean, variance) static int s_MaxProbeVolumeProbeCount; static int s_MaxProbeVolumeProbeOctahedralDepthCount; RTHandle m_ProbeVolumeAtlasSHRTHandle; int m_ProbeVolumeAtlasSHRTDepthSliceCount; Texture3DAtlasDynamic probeVolumeAtlas = null; RTHandle m_ProbeVolumeAtlasOctahedralDepthRTHandle; Texture2DAtlasDynamic probeVolumeAtlasOctahedralDepth = null; bool isClearProbeVolumeAtlasRequested = false; // Preallocated scratch memory for storing ambient probe packed SH coefficients, which are used as a fallback when probe volume weight < 1.0. static Vector4[] s_AmbientProbeFallbackPackedCoeffs = new Vector4[7]; void InitializeProbeVolumes() { if (ShaderConfig.s_EnableProbeVolumes == 0) return; m_SupportProbeVolume = asset.currentPlatformRenderPipelineSettings.supportProbeVolume && (ShaderConfig.s_EnableProbeVolumes == 1); s_ProbeVolumeAtlasResolution = asset.currentPlatformRenderPipelineSettings.probeVolumeSettings.atlasResolution; if (GetApproxProbeVolumeAtlasSizeInByte(s_ProbeVolumeAtlasResolution) > HDRenderPipeline.k_MaxCacheSize) { s_ProbeVolumeAtlasResolution = GetMaxProbeVolumeAtlasSizeForWeightInByte(HDRenderPipeline.k_MaxCacheSize); } // TODO: Preallocating compute buffer for this worst case of a single probe volume that consumes the whole atlas is a memory hog. // May want to look at dynamic resizing of compute buffer based on use, or more simply, slicing it up across multiple dispatches for massive volumes. s_MaxProbeVolumeProbeCount = s_ProbeVolumeAtlasResolution * s_ProbeVolumeAtlasResolution * s_ProbeVolumeAtlasResolution; s_MaxProbeVolumeProbeOctahedralDepthCount = s_MaxProbeVolumeProbeCount * k_ProbeOctahedralDepthWidth * k_ProbeOctahedralDepthHeight; s_ProbeVolumeAtlasOctahedralDepthResolution = asset.currentPlatformRenderPipelineSettings.probeVolumeSettings.atlasOctahedralDepthResolution; if (GetApproxProbeVolumeOctahedralDepthAtlasSizeInByte(s_ProbeVolumeAtlasOctahedralDepthResolution) > HDRenderPipeline.k_MaxCacheSize) { s_ProbeVolumeAtlasOctahedralDepthResolution = GetMaxProbeVolumeOctahedralDepthAtlasSizeForWeightInByte(HDRenderPipeline.k_MaxCacheSize); } k_MaxProbeVolumeAtlasOctahedralDepthProbeCount = (s_ProbeVolumeAtlasOctahedralDepthResolution / k_ProbeOctahedralDepthWidth) * (s_ProbeVolumeAtlasOctahedralDepthResolution / k_ProbeOctahedralDepthWidth); if (m_SupportProbeVolume) { CreateProbeVolumeBuffers(); s_ProbeVolumeAtlasBlitCS = asset.renderPipelineResources.shaders.probeVolumeAtlasBlitCS; s_ProbeVolumeAtlasBlitKernel = s_ProbeVolumeAtlasBlitCS.FindKernel("ProbeVolumeAtlasBlitKernel"); s_ProbeVolumeAtlasOctahedralDepthBlitCS = asset.renderPipelineResources.shaders.probeVolumeAtlasOctahedralDepthBlitCS; s_ProbeVolumeAtlasOctahedralDepthBlitKernel = s_ProbeVolumeAtlasOctahedralDepthBlitCS.FindKernel("ProbeVolumeAtlasOctahedralDepthBlitKernel"); s_ProbeVolumeAtlasOctahedralDepthConvolveCS = asset.renderPipelineResources.shaders.probeVolumeAtlasOctahedralDepthConvolveCS; s_ProbeVolumeAtlasOctahedralDepthConvolveKernel = s_ProbeVolumeAtlasOctahedralDepthConvolveCS.FindKernel("ProbeVolumeAtlasOctahedralDepthConvolveKernel"); } // Need Default / Fallback buffers for binding in case when ShaderConfig has activated probe volume code, // and probe volumes has been enabled in the HDRenderPipelineAsset, // but probe volumes is disabled in the current camera's frame settings. // This can go away if we add a global keyword for using / completely stripping probe volume code per camera. CreateProbeVolumeBuffersDefault(); #if UNITY_EDITOR UnityEditor.Lightmapping.lightingDataCleared += OnLightingDataCleared; #endif } internal void CreateProbeVolumeBuffersDefault() { s_VisibleProbeVolumeBoundsBufferDefault = new ComputeBuffer(1, Marshal.SizeOf(typeof(OrientedBBox))); s_VisibleProbeVolumeDataBufferDefault = new ComputeBuffer(1, Marshal.SizeOf(typeof(ProbeVolumeEngineData))); } static internal int GetDepthSliceCountFromEncodingMode(ProbeVolumesEncodingModes encodingMode) { switch (encodingMode) { case ProbeVolumesEncodingModes.SphericalHarmonicsL0: { // One "texture slice" for our single RGB SH DC term. Validity is placed in the alpha channel. return 1; } case ProbeVolumesEncodingModes.SphericalHarmonicsL1: { // One "texture slice" per [R, G, and B] SH 4x float coefficients + one "texture slice" for float4(validity, unassigned, unassigned, unassigned). return 4; } case ProbeVolumesEncodingModes.SphericalHarmonicsL2: { // One "texture slice" per 4x float coefficients, with the Validity term placed in the alpha channel of the last slice. return 7; } default: { Debug.Assert(false, "Error: Encountered unsupported probe volumes encoding mode in ShaderConfig.cs. Please set a valid enum value for ShaderOptions.ProbeVolumesEncodingMode."); return 0; } } } // Used for displaying memory cost in HDRenderPipelineAsset UI. internal static long GetApproxProbeVolumeAtlasSizeInByte(int resolution) { int depthSliceCount = GetDepthSliceCountFromEncodingMode(ShaderConfig.s_ProbeVolumesEncodingMode); return (long)(resolution * resolution * resolution * depthSliceCount) * (long)HDUtils.GetFormatSizeInBytes(k_ProbeVolumeAtlasFormat); } internal static int GetMaxProbeVolumeAtlasSizeForWeightInByte(long weight) { int depthSliceCount = GetDepthSliceCountFromEncodingMode(ShaderConfig.s_ProbeVolumesEncodingMode); int theoricalResult = Mathf.FloorToInt(Mathf.Pow(weight / ((long)depthSliceCount * (long)HDUtils.GetFormatSizeInBytes(k_ProbeVolumeAtlasFormat)), 1.0f / 3.0f)); return Mathf.Clamp(theoricalResult, 1, SystemInfo.maxTextureSize); } internal static long GetApproxProbeVolumeOctahedralDepthAtlasSizeInByte(int resolution) { return (long)(resolution * resolution) * (long)HDUtils.GetFormatSizeInBytes(k_ProbeVolumeOctahedralDepthAtlasFormat); } internal static int GetMaxProbeVolumeOctahedralDepthAtlasSizeForWeightInByte(long weight) { int theoricalResult = Mathf.FloorToInt(Mathf.Pow(weight / (long)HDUtils.GetFormatSizeInBytes(k_ProbeVolumeAtlasFormat), 1.0f / 2.0f)); return Mathf.Clamp(theoricalResult, 1, SystemInfo.maxTextureSize); } internal void CreateProbeVolumeBuffers() { m_VisibleProbeVolumeBounds = new List(); m_VisibleProbeVolumeData = new List(); s_VisibleProbeVolumeBoundsBuffer = new ComputeBuffer(k_MaxVisibleProbeVolumeCount, Marshal.SizeOf(typeof(OrientedBBox))); s_VisibleProbeVolumeDataBuffer = new ComputeBuffer(k_MaxVisibleProbeVolumeCount, Marshal.SizeOf(typeof(ProbeVolumeEngineData))); s_ProbeVolumeAtlasBlitDataSHL01Buffer = new ComputeBuffer(s_MaxProbeVolumeProbeCount * ProbeVolumePayload.GetDataSHL01Stride(), Marshal.SizeOf(typeof(float))); if (ShaderConfig.s_ProbeVolumesEncodingMode == ProbeVolumesEncodingModes.SphericalHarmonicsL2) { s_ProbeVolumeAtlasBlitDataSHL2Buffer = new ComputeBuffer(s_MaxProbeVolumeProbeCount * ProbeVolumePayload.GetDataSHL2Stride(), Marshal.SizeOf(typeof(float))); } s_ProbeVolumeAtlasBlitDataValidityBuffer = new ComputeBuffer(s_MaxProbeVolumeProbeCount, Marshal.SizeOf(typeof(float))); m_ProbeVolumeAtlasSHRTDepthSliceCount = GetDepthSliceCountFromEncodingMode(ShaderConfig.s_ProbeVolumesEncodingMode); m_ProbeVolumeAtlasSHRTHandle = RTHandles.Alloc( width: s_ProbeVolumeAtlasResolution, height: s_ProbeVolumeAtlasResolution, slices: s_ProbeVolumeAtlasResolution * m_ProbeVolumeAtlasSHRTDepthSliceCount, dimension: TextureDimension.Tex3D, colorFormat: k_ProbeVolumeAtlasFormat, enableRandomWrite: true, useMipMap: false, name: "ProbeVolumeAtlasSH" ); probeVolumeAtlas = new Texture3DAtlasDynamic(s_ProbeVolumeAtlasResolution, s_ProbeVolumeAtlasResolution, s_ProbeVolumeAtlasResolution, k_MaxVisibleProbeVolumeCount, m_ProbeVolumeAtlasSHRTHandle); if (ShaderConfig.s_ProbeVolumesBilateralFilteringMode == ProbeVolumesBilateralFilteringModes.OctahedralDepth) { s_ProbeVolumeAtlasOctahedralDepthBuffer = new ComputeBuffer(s_MaxProbeVolumeProbeOctahedralDepthCount, Marshal.SizeOf(typeof(float))); // TODO: (Nick): Might be able drop precision down to half-floats, since we only need to encode depth data up to one probe spacing distance away. Could rescale depth data to this range before encoding. m_ProbeVolumeAtlasOctahedralDepthRTHandle = RTHandles.Alloc( width: s_ProbeVolumeAtlasOctahedralDepthResolution, height: s_ProbeVolumeAtlasOctahedralDepthResolution, slices: 1, dimension: TextureDimension.Tex2D, colorFormat: k_ProbeVolumeOctahedralDepthAtlasFormat, enableRandomWrite: true, useMipMap: false, name: "ProbeVolumeAtlasOctahedralDepthMeanAndVariance" ); probeVolumeAtlasOctahedralDepth = new Texture2DAtlasDynamic( s_ProbeVolumeAtlasOctahedralDepthResolution, s_ProbeVolumeAtlasOctahedralDepthResolution, k_MaxVisibleProbeVolumeCount, m_ProbeVolumeAtlasOctahedralDepthRTHandle ); } } internal void DestroyProbeVolumeBuffers() { CoreUtils.SafeRelease(s_VisibleProbeVolumeBoundsBufferDefault); CoreUtils.SafeRelease(s_VisibleProbeVolumeDataBufferDefault); CoreUtils.SafeRelease(s_VisibleProbeVolumeBoundsBuffer); CoreUtils.SafeRelease(s_VisibleProbeVolumeDataBuffer); CoreUtils.SafeRelease(s_ProbeVolumeAtlasBlitDataSHL01Buffer); CoreUtils.SafeRelease(s_ProbeVolumeAtlasBlitDataSHL2Buffer); CoreUtils.SafeRelease(s_ProbeVolumeAtlasBlitDataValidityBuffer); CoreUtils.SafeRelease(s_ProbeVolumeAtlasOctahedralDepthBuffer); if (m_ProbeVolumeAtlasSHRTHandle != null) RTHandles.Release(m_ProbeVolumeAtlasSHRTHandle); if (probeVolumeAtlas != null) probeVolumeAtlas.Release(); if (m_ProbeVolumeAtlasOctahedralDepthRTHandle != null) RTHandles.Release(m_ProbeVolumeAtlasOctahedralDepthRTHandle); if (probeVolumeAtlasOctahedralDepth != null) probeVolumeAtlasOctahedralDepth.Release(); m_VisibleProbeVolumeBounds = null; m_VisibleProbeVolumeData = null; } void CleanupProbeVolumes() { DestroyProbeVolumeBuffers(); #if UNITY_EDITOR UnityEditor.Lightmapping.lightingDataCleared -= OnLightingDataCleared; #endif } internal void OnLightingDataCleared() { // User requested all lighting data to be cleared. // Clear out all block allocations in atlas, and clear out texture data. // Clearing out texture data is not strictly necessary, // but it makes the display atlas debug view more readable. // Note: We do this lazily, in order to trigger the clear during the // next frame's render loop on the command buffer. isClearProbeVolumeAtlasRequested = true; } unsafe void UpdateShaderVariablesGlobalProbeVolumesDefault(ref ShaderVariablesGlobal cb, HDCamera hdCamera) { cb._EnableProbeVolumes = 0; cb._ProbeVolumeCount = 0; cb._ProbeVolumeLeakMitigationMode = (int)LeakMitigationMode.NormalBias; cb._ProbeVolumeBilateralFilterWeightMin = 0.0f; cb._ProbeVolumeBilateralFilterWeight = 0.0f; // Need to populate ambient probe fallback even in the default case, // As if the feature is enabled in the ShaderConfig, but disabled in the HDRenderPipelineAsset, we need to fallback to ambient probe only. SphericalHarmonicsL2 ambientProbeFallbackSH = m_SkyManager.GetAmbientProbe(hdCamera); SphericalHarmonicMath.PackCoefficients(s_AmbientProbeFallbackPackedCoeffs, ambientProbeFallbackSH); for (int i = 0; i < 7; ++i) for (int j = 0; j < 4; ++j) cb._ProbeVolumeAmbientProbeFallbackPackedCoeffs[i * 4 + j] = s_AmbientProbeFallbackPackedCoeffs[i][j]; } unsafe void UpdateShaderVariablesGlobalProbeVolumes(ref ShaderVariablesGlobal cb, HDCamera hdCamera) { if (ShaderConfig.s_EnableProbeVolumes == 0) return; if (!m_SupportProbeVolume) { UpdateShaderVariablesGlobalProbeVolumesDefault(ref cb, hdCamera); return; } cb._EnableProbeVolumes = hdCamera.frameSettings.IsEnabled(FrameSettingsField.ProbeVolume) ? 1u : 0u; cb._ProbeVolumeCount = (uint)m_VisibleProbeVolumeBounds.Count; cb._ProbeVolumeAtlasResolutionAndSliceCount = new Vector4( s_ProbeVolumeAtlasResolution, s_ProbeVolumeAtlasResolution, s_ProbeVolumeAtlasResolution, m_ProbeVolumeAtlasSHRTDepthSliceCount ); cb._ProbeVolumeAtlasResolutionAndSliceCountInverse = new Vector4( 1.0f / (float)s_ProbeVolumeAtlasResolution, 1.0f / (float)s_ProbeVolumeAtlasResolution, 1.0f / (float)s_ProbeVolumeAtlasResolution, 1.0f / (float)m_ProbeVolumeAtlasSHRTDepthSliceCount ); if (ShaderConfig.s_ProbeVolumesBilateralFilteringMode == ProbeVolumesBilateralFilteringModes.OctahedralDepth) { cb._ProbeVolumeAtlasOctahedralDepthResolutionAndInverse = new Vector4( m_ProbeVolumeAtlasOctahedralDepthRTHandle.rt.width, m_ProbeVolumeAtlasOctahedralDepthRTHandle.rt.height, 1.0f / (float)m_ProbeVolumeAtlasOctahedralDepthRTHandle.rt.width, 1.0f / (float)m_ProbeVolumeAtlasOctahedralDepthRTHandle.rt.height ); } else { cb._ProbeVolumeAtlasOctahedralDepthResolutionAndInverse = Vector4.zero; } var settings = hdCamera.volumeStack.GetComponent(); LeakMitigationMode leakMitigationMode = (settings == null) ? LeakMitigationMode.NormalBias : settings.leakMitigationMode.value; float bilateralFilterWeight = (settings == null) ? 0.0f : settings.bilateralFilterWeight.value; if (leakMitigationMode != LeakMitigationMode.NormalBias) { if (bilateralFilterWeight < 1e-5f) { // If bilateralFilterWeight is effectively zero, then we are simply doing trilinear filtering. // In this case we can avoid the performance cost of computing our bilateral filter entirely. leakMitigationMode = LeakMitigationMode.NormalBias; } } cb._ProbeVolumeLeakMitigationMode = (int)leakMitigationMode; cb._ProbeVolumeBilateralFilterWeightMin = 1e-5f; cb._ProbeVolumeBilateralFilterWeight = bilateralFilterWeight; SphericalHarmonicsL2 ambientProbeFallbackSH = m_SkyManager.GetAmbientProbe(hdCamera); SphericalHarmonicMath.PackCoefficients(s_AmbientProbeFallbackPackedCoeffs, ambientProbeFallbackSH); for (int i = 0; i < 7; ++i) for (int j = 0; j < 4; ++j) cb._ProbeVolumeAmbientProbeFallbackPackedCoeffs[i * 4 + j] = s_AmbientProbeFallbackPackedCoeffs[i][j]; } void PushProbeVolumesGlobalParams(HDCamera hdCamera, CommandBuffer cmd) { Debug.Assert(ShaderConfig.s_EnableProbeVolumes == 1); Debug.Assert(m_SupportProbeVolume); cmd.SetGlobalBuffer(HDShaderIDs._ProbeVolumeBounds, s_VisibleProbeVolumeBoundsBuffer); cmd.SetGlobalBuffer(HDShaderIDs._ProbeVolumeDatas, s_VisibleProbeVolumeDataBuffer); cmd.SetGlobalTexture(HDShaderIDs._ProbeVolumeAtlasSH, m_ProbeVolumeAtlasSHRTHandle); if (ShaderConfig.s_ProbeVolumesBilateralFilteringMode == ProbeVolumesBilateralFilteringModes.OctahedralDepth) { cmd.SetGlobalTexture(HDShaderIDs._ProbeVolumeAtlasOctahedralDepth, m_ProbeVolumeAtlasOctahedralDepthRTHandle); } } internal void PushProbeVolumesGlobalParamsDefault(HDCamera hdCamera, CommandBuffer cmd) { Debug.Assert(ShaderConfig.s_EnableProbeVolumes == 1); Debug.Assert(hdCamera.frameSettings.IsEnabled(FrameSettingsField.ProbeVolume) == false); cmd.SetGlobalBuffer(HDShaderIDs._ProbeVolumeBounds, s_VisibleProbeVolumeBoundsBufferDefault); cmd.SetGlobalBuffer(HDShaderIDs._ProbeVolumeDatas, s_VisibleProbeVolumeDataBufferDefault); cmd.SetGlobalTexture(HDShaderIDs._ProbeVolumeAtlasSH, TextureXR.GetBlackTexture3D()); if (ShaderConfig.s_ProbeVolumesBilateralFilteringMode == ProbeVolumesBilateralFilteringModes.OctahedralDepth) { cmd.SetGlobalTexture(HDShaderIDs._ProbeVolumeAtlasOctahedralDepth, Texture2D.blackTexture); } } internal void ReleaseProbeVolumeFromAtlas(ProbeVolume volume) { if (ShaderConfig.s_EnableProbeVolumes == 0) return; if (!m_SupportProbeVolume) return; int key = volume.GetID(); probeVolumeAtlas.ReleaseTextureSlot(key); if (ShaderConfig.s_ProbeVolumesBilateralFilteringMode == ProbeVolumesBilateralFilteringModes.OctahedralDepth) { probeVolumeAtlasOctahedralDepth.ReleaseTextureSlot(key); } } internal bool EnsureProbeVolumeInAtlas(ScriptableRenderContext renderContext, CommandBuffer cmd, ProbeVolume volume) { int key = volume.GetID(); int width = volume.parameters.resolutionX; int height = volume.parameters.resolutionY; int depth = volume.parameters.resolutionZ; int size = volume.parameters.resolutionX * volume.parameters.resolutionY * volume.parameters.resolutionZ; Debug.Assert(size > 0, "ProbeVolume: Encountered probe volume with resolution set to zero on all three axes."); // TODO: Store volume resolution inside the atlas's key->bias dictionary. // If resolution has changed since upload, need to free previous allocation from atlas, // and attempt to allocate a new chunk from the atlas for the new resolution settings. // Currently atlas allocator only handles splitting. Need to add merging of neighboring, empty chunks to avoid fragmentation. bool isSlotAllocated = probeVolumeAtlas.EnsureTextureSlot(out bool isUploadNeeded, out volume.parameters.scale, out volume.parameters.bias, key, width, height, depth); if (isSlotAllocated) { if (isUploadNeeded || volume.dataUpdated) { ProbeVolumePayload payload = volume.GetPayload(); if (ProbeVolumePayload.IsNull(ref payload) || !volume.IsAssetCompatible()) { ReleaseProbeVolumeFromAtlas(volume); return false; } int sizeSHCoefficientsL01 = size * ProbeVolumePayload.GetDataSHL01Stride(); int sizeSHCoefficientsL2 = size * ProbeVolumePayload.GetDataSHL2Stride(); Debug.Assert(payload.dataSHL01.Length == sizeSHCoefficientsL01, "ProbeVolume: The probe volume baked data and its resolution are out of sync! Volume data length is " + payload.dataSHL01.Length + ", but resolution * SH stride size is " + sizeSHCoefficientsL01 + "."); if (ShaderConfig.s_ProbeVolumesEncodingMode == ProbeVolumesEncodingModes.SphericalHarmonicsL2) { Debug.Assert(payload.dataSHL2.Length == sizeSHCoefficientsL2, "ProbeVolume: The probe volume baked data and its resolution are out of sync! Volume data length is " + payload.dataSHL2.Length + ", but resolution * SH stride size is " + sizeSHCoefficientsL2 + "."); } if (size > s_MaxProbeVolumeProbeCount) { Debug.LogWarning("ProbeVolume: probe volume baked data size exceeds the currently max supported blitable size. Volume data size is " + size + ", but s_MaxProbeVolumeProbeCount is " + s_MaxProbeVolumeProbeCount + ". Please decrease ProbeVolume resolution, or increase ProbeVolumeLighting.s_MaxProbeVolumeProbeCount."); return false; } //Debug.Log("Uploading Probe Volume Data with key " + key + " at scale bias = " + volume.parameters.scaleBias); cmd.SetComputeVectorParam(s_ProbeVolumeAtlasBlitCS, HDShaderIDs._ProbeVolumeResolution, new Vector3( volume.parameters.resolutionX, volume.parameters.resolutionY, volume.parameters.resolutionZ )); cmd.SetComputeVectorParam(s_ProbeVolumeAtlasBlitCS, HDShaderIDs._ProbeVolumeResolutionInverse, new Vector3( 1.0f / (float)volume.parameters.resolutionX, 1.0f / (float)volume.parameters.resolutionY, 1.0f / (float)volume.parameters.resolutionZ )); cmd.SetComputeVectorParam(s_ProbeVolumeAtlasBlitCS, HDShaderIDs._ProbeVolumeAtlasScale, volume.parameters.scale ); cmd.SetComputeVectorParam(s_ProbeVolumeAtlasBlitCS, HDShaderIDs._ProbeVolumeAtlasBias, volume.parameters.bias ); cmd.SetComputeVectorParam(s_ProbeVolumeAtlasBlitCS, HDShaderIDs._ProbeVolumeAtlasResolutionAndSliceCount, new Vector4( s_ProbeVolumeAtlasResolution, s_ProbeVolumeAtlasResolution, s_ProbeVolumeAtlasResolution, m_ProbeVolumeAtlasSHRTDepthSliceCount )); cmd.SetComputeVectorParam(s_ProbeVolumeAtlasBlitCS, HDShaderIDs._ProbeVolumeAtlasResolutionAndSliceCountInverse, new Vector4( 1.0f / (float)s_ProbeVolumeAtlasResolution, 1.0f / (float)s_ProbeVolumeAtlasResolution, 1.0f / (float)s_ProbeVolumeAtlasResolution, 1.0f / (float)m_ProbeVolumeAtlasSHRTDepthSliceCount )); s_ProbeVolumeAtlasBlitDataSHL01Buffer.SetData(payload.dataSHL01); s_ProbeVolumeAtlasBlitDataValidityBuffer.SetData(payload.dataValidity); cmd.SetComputeIntParam(s_ProbeVolumeAtlasBlitCS, HDShaderIDs._ProbeVolumeAtlasReadBufferCount, size); cmd.SetComputeBufferParam(s_ProbeVolumeAtlasBlitCS, s_ProbeVolumeAtlasBlitKernel, HDShaderIDs._ProbeVolumeAtlasReadSHL01Buffer, s_ProbeVolumeAtlasBlitDataSHL01Buffer); if (ShaderConfig.s_ProbeVolumesEncodingMode == ProbeVolumesEncodingModes.SphericalHarmonicsL2) { s_ProbeVolumeAtlasBlitDataSHL2Buffer.SetData(payload.dataSHL2); cmd.SetComputeBufferParam(s_ProbeVolumeAtlasBlitCS, s_ProbeVolumeAtlasBlitKernel, HDShaderIDs._ProbeVolumeAtlasReadSHL2Buffer, s_ProbeVolumeAtlasBlitDataSHL2Buffer); } cmd.SetComputeBufferParam(s_ProbeVolumeAtlasBlitCS, s_ProbeVolumeAtlasBlitKernel, HDShaderIDs._ProbeVolumeAtlasReadValidityBuffer, s_ProbeVolumeAtlasBlitDataValidityBuffer); cmd.SetComputeTextureParam(s_ProbeVolumeAtlasBlitCS, s_ProbeVolumeAtlasBlitKernel, HDShaderIDs._ProbeVolumeAtlasWriteTextureSH, m_ProbeVolumeAtlasSHRTHandle); // TODO: Determine optimal batch size. const int kBatchSize = 256; int numThreadGroups = Mathf.CeilToInt((float)size / (float)kBatchSize); cmd.DispatchCompute(s_ProbeVolumeAtlasBlitCS, s_ProbeVolumeAtlasBlitKernel, numThreadGroups, 1, 1); return true; } return false; } if (!isSlotAllocated) { Debug.LogWarning("ProbeVolume: Texture Atlas failed to allocate space for texture { key: " + key + "width: " + width + ", height: " + height + ", depth: " + depth + "}"); } return false; } internal bool EnsureProbeVolumeInAtlasOctahedralDepth(ScriptableRenderContext renderContext, CommandBuffer cmd, ProbeVolume volume) { int key = volume.GetID(); int width = volume.parameters.resolutionX * volume.parameters.resolutionZ * k_ProbeOctahedralDepthWidth; int height = volume.parameters.resolutionY * k_ProbeOctahedralDepthHeight; int size = volume.parameters.resolutionX * volume.parameters.resolutionY * volume.parameters.resolutionZ * k_ProbeOctahedralDepthWidth * k_ProbeOctahedralDepthHeight; Debug.Assert(size > 0, "ProbeVolume: Encountered probe volume with resolution set to zero on all three axes."); // TODO: Store volume resolution inside the atlas's key->bias dictionary. // If resolution has changed since upload, need to free previous allocation from atlas, // and attempt to allocate a new chunk from the atlas for the new resolution settings. // Currently atlas allocator only handles splitting. Need to add merging of neighboring, empty chunks to avoid fragmentation. bool isSlotAllocated = probeVolumeAtlasOctahedralDepth.EnsureTextureSlot(out bool isUploadNeeded, out volume.parameters.octahedralDepthScaleBias, key, width, height); if (isSlotAllocated) { if (isUploadNeeded || volume.dataUpdated) { ProbeVolumePayload payload = volume.GetPayload(); if (payload.dataOctahedralDepth == null || payload.dataOctahedralDepth.Length == 0 || !volume.IsAssetCompatible()) { ReleaseProbeVolumeFromAtlas(volume); return false; } // Blit: { Debug.Assert(payload.dataOctahedralDepth.Length == size, "ProbeVolume: The probe volume baked data and its resolution are out of sync! Volume data length is " + payload.dataOctahedralDepth.Length + ", but resolution size is " + size + "."); if (size > s_MaxProbeVolumeProbeOctahedralDepthCount) { Debug.LogWarning("ProbeVolume: probe volume octahedral depth baked data size exceeds the currently max supported blitable size. Volume data size is " + size + ", but s_MaxProbeVolumeProbeCount is " + s_MaxProbeVolumeProbeOctahedralDepthCount + ". Please decrease ProbeVolume resolution, or increase ProbeVolumeLighting.s_MaxProbeVolumeProbeCount."); return false; } //Debug.Log("Uploading Probe Volume Data with key " + key + " at scale bias = " + volume.parameters.scaleBias); cmd.SetComputeVectorParam(s_ProbeVolumeAtlasOctahedralDepthBlitCS, HDShaderIDs._ProbeVolumeResolution, new Vector3( volume.parameters.resolutionX, volume.parameters.resolutionY, volume.parameters.resolutionZ )); cmd.SetComputeVectorParam(s_ProbeVolumeAtlasOctahedralDepthBlitCS, HDShaderIDs._ProbeVolumeResolutionInverse, new Vector3( 1.0f / (float)volume.parameters.resolutionX, 1.0f / (float)volume.parameters.resolutionY, 1.0f / (float)volume.parameters.resolutionZ )); cmd.SetComputeVectorParam(s_ProbeVolumeAtlasOctahedralDepthBlitCS, HDShaderIDs._ProbeVolumeAtlasOctahedralDepthScaleBias, volume.parameters.octahedralDepthScaleBias ); cmd.SetComputeVectorParam(s_ProbeVolumeAtlasOctahedralDepthBlitCS, HDShaderIDs._ProbeVolumeAtlasOctahedralDepthResolutionAndInverse, new Vector4( m_ProbeVolumeAtlasOctahedralDepthRTHandle.rt.width, m_ProbeVolumeAtlasOctahedralDepthRTHandle.rt.height, 1.0f / (float)m_ProbeVolumeAtlasOctahedralDepthRTHandle.rt.width, 1.0f / (float)m_ProbeVolumeAtlasOctahedralDepthRTHandle.rt.height )); cmd.SetComputeVectorParam(s_ProbeVolumeAtlasOctahedralDepthBlitCS, HDShaderIDs._ProbeVolumeAtlasResolutionAndSliceCount, new Vector4( s_ProbeVolumeAtlasResolution, s_ProbeVolumeAtlasResolution, s_ProbeVolumeAtlasResolution, m_ProbeVolumeAtlasSHRTDepthSliceCount )); cmd.SetComputeVectorParam(s_ProbeVolumeAtlasOctahedralDepthBlitCS, HDShaderIDs._ProbeVolumeAtlasResolutionAndSliceCountInverse, new Vector4( 1.0f / (float)s_ProbeVolumeAtlasResolution, 1.0f / (float)s_ProbeVolumeAtlasResolution, 1.0f / (float)s_ProbeVolumeAtlasResolution, 1.0f / (float)m_ProbeVolumeAtlasSHRTDepthSliceCount )); s_ProbeVolumeAtlasOctahedralDepthBuffer.SetData(payload.dataOctahedralDepth); cmd.SetComputeIntParam(s_ProbeVolumeAtlasOctahedralDepthBlitCS, HDShaderIDs._ProbeVolumeAtlasOctahedralDepthReadBufferCount, size); cmd.SetComputeBufferParam(s_ProbeVolumeAtlasOctahedralDepthBlitCS, s_ProbeVolumeAtlasOctahedralDepthBlitKernel, HDShaderIDs._ProbeVolumeAtlasOctahedralDepthReadBuffer, s_ProbeVolumeAtlasOctahedralDepthBuffer); cmd.SetComputeTextureParam(s_ProbeVolumeAtlasOctahedralDepthBlitCS, s_ProbeVolumeAtlasOctahedralDepthBlitKernel, HDShaderIDs._ProbeVolumeAtlasOctahedralDepthWriteTexture, m_ProbeVolumeAtlasOctahedralDepthRTHandle); // TODO: Determine optimal batch size. const int kBatchSize = 256; int numThreadGroups = Mathf.CeilToInt((float)size / (float)kBatchSize); cmd.DispatchCompute(s_ProbeVolumeAtlasOctahedralDepthBlitCS, s_ProbeVolumeAtlasOctahedralDepthBlitKernel, numThreadGroups, 1, 1); } // Convolve: { Vector4 probeVolumeAtlasOctahedralDepthScaleBiasTexels = new Vector4( Mathf.Floor(volume.parameters.octahedralDepthScaleBias.x * s_ProbeVolumeAtlasOctahedralDepthResolution + 0.5f), Mathf.Floor(volume.parameters.octahedralDepthScaleBias.y * s_ProbeVolumeAtlasOctahedralDepthResolution + 0.5f), Mathf.Floor(volume.parameters.octahedralDepthScaleBias.z * s_ProbeVolumeAtlasOctahedralDepthResolution + 0.5f), Mathf.Floor(volume.parameters.octahedralDepthScaleBias.w * s_ProbeVolumeAtlasOctahedralDepthResolution + 0.5f) ); cmd.SetComputeVectorParam(s_ProbeVolumeAtlasOctahedralDepthConvolveCS, HDShaderIDs._ProbeVolumeAtlasOctahedralDepthScaleBiasTexels, probeVolumeAtlasOctahedralDepthScaleBiasTexels ); cmd.SetComputeTextureParam(s_ProbeVolumeAtlasOctahedralDepthConvolveCS, s_ProbeVolumeAtlasOctahedralDepthConvolveKernel, HDShaderIDs._ProbeVolumeAtlasOctahedralDepthRWTexture, m_ProbeVolumeAtlasOctahedralDepthRTHandle); cmd.SetComputeIntParam(s_ProbeVolumeAtlasOctahedralDepthConvolveCS, HDShaderIDs._FilterSampleCount, 16); // TODO: Expose cmd.SetComputeFloatParam(s_ProbeVolumeAtlasOctahedralDepthConvolveCS, HDShaderIDs._FilterSharpness, 6.0f); // TODO: Expose // Warning: This kernel numthreads must be an integer multiple of OCTAHEDRAL_DEPTH_RESOLUTION // We read + write from the same texture, so any partial work would pollute / cause feedback into neighboring chunks. int widthPixels = (int)(volume.parameters.octahedralDepthScaleBias.x * (float)s_ProbeVolumeAtlasOctahedralDepthResolution); int heightPixels = (int)(volume.parameters.octahedralDepthScaleBias.y * (float)s_ProbeVolumeAtlasOctahedralDepthResolution); int probeCountX = widthPixels / k_ProbeOctahedralDepthWidth; int probeCountY = heightPixels / k_ProbeOctahedralDepthHeight; Debug.Assert((k_ProbeOctahedralDepthWidth == k_ProbeOctahedralDepthHeight) && (k_ProbeOctahedralDepthWidth == 8)); Debug.Assert((probeCountX * k_ProbeOctahedralDepthWidth) == widthPixels); Debug.Assert((probeCountY * k_ProbeOctahedralDepthHeight) == heightPixels); cmd.DispatchCompute(s_ProbeVolumeAtlasOctahedralDepthConvolveCS, s_ProbeVolumeAtlasOctahedralDepthConvolveKernel, probeCountX, probeCountY, 1); } return true; } return false; } if (!isSlotAllocated) { Debug.LogWarning("ProbeVolume: Texture Atlas failed to allocate space for octahedral depth texture { key: " + key + " width: " + width + ", height: " + height); } return false; } internal void ClearProbeVolumeAtlasIfRequested(CommandBuffer cmd) { if (!isClearProbeVolumeAtlasRequested) { return; } isClearProbeVolumeAtlasRequested = false; probeVolumeAtlas.ResetAllocator(); cmd.SetRenderTarget(m_ProbeVolumeAtlasSHRTHandle.rt, 0, CubemapFace.Unknown, 0); cmd.ClearRenderTarget(false, true, Color.black, 0.0f); if (ShaderConfig.s_ProbeVolumesBilateralFilteringMode == ProbeVolumesBilateralFilteringModes.OctahedralDepth) { probeVolumeAtlasOctahedralDepth.ResetAllocator(); cmd.SetRenderTarget(m_ProbeVolumeAtlasOctahedralDepthRTHandle.rt, 0, CubemapFace.Unknown, 0); cmd.ClearRenderTarget(false, true, Color.black, 0.0f); } } ProbeVolumeList PrepareVisibleProbeVolumeList(ScriptableRenderContext renderContext, HDCamera hdCamera, CommandBuffer cmd) { ProbeVolumeList probeVolumes = new ProbeVolumeList(); if (ShaderConfig.s_EnableProbeVolumes == 0) return probeVolumes; if (!hdCamera.frameSettings.IsEnabled(FrameSettingsField.ProbeVolume)) { PushProbeVolumesGlobalParamsDefault(hdCamera, cmd); } else { PrepareVisibleProbeVolumeListBuffers(renderContext, hdCamera, cmd, ref probeVolumes); PushProbeVolumesGlobalParams(hdCamera, cmd); } return probeVolumes; } void PrepareVisibleProbeVolumeListBuffers(ScriptableRenderContext renderContext, HDCamera hdCamera, CommandBuffer cmd, ref ProbeVolumeList probeVolumes) { var settings = hdCamera.volumeStack.GetComponent(); bool octahedralDepthOcclusionFilterIsEnabled = ShaderConfig.s_ProbeVolumesBilateralFilteringMode == ProbeVolumesBilateralFilteringModes.OctahedralDepth && settings.leakMitigationMode.value == LeakMitigationMode.OctahedralDepthOcclusionFilter; using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.PrepareProbeVolumeList))) { ClearProbeVolumeAtlasIfRequested(cmd); Vector3 camPosition = hdCamera.camera.transform.position; Vector3 camOffset = Vector3.zero;// World-origin-relative if (ShaderConfig.s_CameraRelativeRendering != 0) { camOffset = camPosition; // Camera-relative } m_VisibleProbeVolumeBounds.Clear(); m_VisibleProbeVolumeData.Clear(); // Collect all visible finite volume data, and upload it to the GPU. List volumes = ProbeVolumeManager.manager.volumes; int probeVolumesCount = Math.Min(volumes.Count, k_MaxVisibleProbeVolumeCount); int sortCount = 0; // Sort probe volumes smallest from smallest to largest volume. // Same as is done with reflection probes. // See LightLoop.cs::PrepareLightsForGPU() for original example of this. for (int probeVolumesIndex = 0; (probeVolumesIndex < volumes.Count) && (sortCount < probeVolumesCount); probeVolumesIndex++) { ProbeVolume volume = volumes[probeVolumesIndex]; #if UNITY_EDITOR if (!volume.IsAssetCompatible()) continue; #endif if (volume.probeVolumeAsset == null || !volume.probeVolumeAsset.IsDataAssigned()) continue; if (ShaderConfig.s_ProbeVolumesAdditiveBlending == 0 && volume.parameters.volumeBlendMode != VolumeBlendMode.Normal) { // Non-normal blend mode volumes are not supported. Skip. continue; } float probeVolumeDepthFromCameraWS = Vector3.Dot(hdCamera.camera.transform.forward, volume.transform.position - camPosition); if (probeVolumeDepthFromCameraWS >= volume.parameters.distanceFadeEnd) { // Probe volume is completely faded out from distance fade optimization. // Do not bother adding it to the list, it would evaluate to zero weight. continue; } // TODO: cache these? var obb = new OrientedBBox(Matrix4x4.TRS(volume.transform.position, volume.transform.rotation, volume.parameters.size)); // Handle camera-relative rendering. obb.center -= camOffset; // Frustum cull on the CPU for now. TODO: do it on the GPU. if (GeometryUtils.Overlap(obb, hdCamera.frustum, hdCamera.frustum.planes.Length, hdCamera.frustum.corners.Length)) { var logVolume = CalculateProbeVolumeLogVolume(volume.parameters.size); m_ProbeVolumeSortKeys[sortCount++] = PackProbeVolumeSortKey(volume.parameters.volumeBlendMode, logVolume, probeVolumesIndex); } } CoreUnsafeUtils.QuickSort(m_ProbeVolumeSortKeys, 0, sortCount - 1); // Call our own quicksort instead of Array.Sort(sortKeys, 0, sortCount) so we don't allocate memory (note the SortCount-1 that is different from original call). for (int sortIndex = 0; sortIndex < sortCount; ++sortIndex) { // In 1. we have already classify and sorted the probe volume, we need to use this sorted order here uint sortKey = m_ProbeVolumeSortKeys[sortIndex]; int probeVolumesIndex; UnpackProbeVolumeSortKey(sortKey, out probeVolumesIndex); ProbeVolume volume = volumes[probeVolumesIndex]; // TODO: cache these? var obb = new OrientedBBox(Matrix4x4.TRS(volume.transform.position, volume.transform.rotation, volume.parameters.size)); // Handle camera-relative rendering. obb.center -= camOffset; // TODO: cache these? var data = volume.parameters.ConvertToEngineData(); // Note: The system is not aware of slice packing in Z. // Need to modify scale and bias terms just before uploading to GPU. // TODO: Should we make it aware earlier up the chain? data.scale.z = data.scale.z / (float)m_ProbeVolumeAtlasSHRTDepthSliceCount; data.bias.z = data.bias.z / (float)m_ProbeVolumeAtlasSHRTDepthSliceCount; m_VisibleProbeVolumeBounds.Add(obb); m_VisibleProbeVolumeData.Add(data); } s_VisibleProbeVolumeBoundsBuffer.SetData(m_VisibleProbeVolumeBounds); s_VisibleProbeVolumeDataBuffer.SetData(m_VisibleProbeVolumeData); // Fill the struct with pointers in order to share the data with the light loop. probeVolumes.bounds = m_VisibleProbeVolumeBounds; probeVolumes.data = m_VisibleProbeVolumeData; // For now, only upload one volume per frame. // This is done: // 1) To timeslice upload cost across N frames for N volumes. // 2) To avoid creating a sync point between compute buffer upload and each volume upload. const int volumeUploadedToAtlasSHCapacity = 1; int volumeUploadedToAtlasOctahedralDepthCapacity = octahedralDepthOcclusionFilterIsEnabled ? 1 : 0; int volumeUploadedToAtlasSHCount = 0; int volumeUploadedToAtlasOctahedralDepthCount = 0; for (int sortIndex = 0; sortIndex < sortCount; ++sortIndex) { uint sortKey = m_ProbeVolumeSortKeys[sortIndex]; int probeVolumesIndex; UnpackProbeVolumeSortKey(sortKey, out probeVolumesIndex); ProbeVolume volume = volumes[probeVolumesIndex]; if (volumeUploadedToAtlasSHCount < volumeUploadedToAtlasSHCapacity) { bool volumeWasUploaded = EnsureProbeVolumeInAtlas(renderContext, cmd, volume); if (volumeWasUploaded) ++volumeUploadedToAtlasSHCount; } if (volumeUploadedToAtlasOctahedralDepthCount < volumeUploadedToAtlasOctahedralDepthCapacity) { bool volumeWasUploaded = EnsureProbeVolumeInAtlasOctahedralDepth(renderContext, cmd, volume); if (volumeWasUploaded) ++volumeUploadedToAtlasOctahedralDepthCount; } if (volumeUploadedToAtlasSHCount == volumeUploadedToAtlasSHCapacity && volumeUploadedToAtlasOctahedralDepthCount == volumeUploadedToAtlasOctahedralDepthCapacity) { // Met our capacity this frame. Early out. break; } } return; } } internal static float CalculateProbeVolumeLogVolume(Vector3 size) { //Notes: // - 1+ term is to prevent having negative values in the log result // - 1000* is too keep 3 digit after the dot while we truncate the result later // - 1048575 is 2^20-1 as we pack the result on 20bit later float boxVolume = 8f * size.x * size.y * size.z; float logVolume = Mathf.Clamp(Mathf.Log(1 + boxVolume, 1.05f) * 1000, 0, 1048575); return logVolume; } internal static void UnpackProbeVolumeSortKey(uint sortKey, out int probeIndex) { const uint PROBE_VOLUME_MASK = (1 << 11) - 1; probeIndex = (int)(sortKey & PROBE_VOLUME_MASK); } internal static uint PackProbeVolumeSortKey(VolumeBlendMode volumeBlendMode, float logVolume, int probeVolumeIndex) { // 1 bit blendMode, 20 bit volume, 11 bit index Debug.Assert(logVolume >= 0.0f && (uint)logVolume < (1 << 20)); Debug.Assert(probeVolumeIndex >= 0 && (uint)probeVolumeIndex < (1 << 11)); const uint VOLUME_MASK = (1 << 20) - 1; const uint INDEX_MASK = (1 << 11) - 1; // Sort probe volumes primarily by blend mode, and secondarily by size. // In the lightloop, this means we will evaluate all Additive and Subtractive blending volumes first, // and finally our Normal (over) blending volumes. // This allows us to early out during the Normal blend volumes if opacity has reached 1.0 across all threads. uint blendModeBits = ((volumeBlendMode != VolumeBlendMode.Normal) ? 0u : 1u) << 31; uint logVolumeBits = ((uint)logVolume & VOLUME_MASK) << 11; uint indexBits = (uint)probeVolumeIndex & INDEX_MASK; return blendModeBits | logVolumeBits | indexBits; } void RenderProbeVolumeDebugOverlay(in DebugParameters debugParameters, CommandBuffer cmd) { if (!m_SupportProbeVolume) return; LightingDebugSettings lightingDebug = debugParameters.debugDisplaySettings.data.lightingDebugSettings; if (lightingDebug.probeVolumeDebugMode != ProbeVolumeDebugMode.None) { using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.ProbeVolumeDebug))) { if (lightingDebug.probeVolumeDebugMode == ProbeVolumeDebugMode.VisualizeAtlas) { DisplayProbeVolumeAtlas(cmd, debugParameters.probeVolumeOverlayParameters, debugParameters.debugOverlay); } } } } struct ProbeVolumeDebugOverlayParameters { public Material material; public Vector4 validRange; public Vector4 textureViewScale; public Vector4 textureViewBias; public Vector3 textureViewResolution; public Vector4 atlasResolutionAndSliceCount; public Vector4 atlasResolutionAndSliceCountInverse; public Vector4 atlasTextureOctahedralDepthScaleBias; public int sliceMode; public RTHandle probeVolumeAtlas; public RTHandle probeVolumeAtlasOctahedralDepth; } ProbeVolumeDebugOverlayParameters PrepareProbeVolumeOverlayParameters(LightingDebugSettings lightingDebug) { ProbeVolumeDebugOverlayParameters parameters = new ProbeVolumeDebugOverlayParameters(); parameters.material = m_DebugDisplayProbeVolumeMaterial; parameters.sliceMode = (int)lightingDebug.probeVolumeAtlasSliceMode; parameters.validRange = new Vector4(lightingDebug.probeVolumeMinValue, 1.0f / (lightingDebug.probeVolumeMaxValue - lightingDebug.probeVolumeMinValue)); parameters.textureViewScale = new Vector3(1.0f, 1.0f, 1.0f); parameters.textureViewBias = new Vector3(0.0f, 0.0f, 0.0f); parameters.textureViewResolution = new Vector3(s_ProbeVolumeAtlasResolution, s_ProbeVolumeAtlasResolution, s_ProbeVolumeAtlasResolution); parameters.atlasTextureOctahedralDepthScaleBias = new Vector4(1.0f, 1.0f, 0.0f, 0.0f); #if UNITY_EDITOR if (UnityEditor.Selection.activeGameObject != null) { var selectedProbeVolume = UnityEditor.Selection.activeGameObject.GetComponent(); if (selectedProbeVolume != null) { // User currently has a probe volume selected. // Compute a scaleBias term so that atlas view automatically zooms into selected probe volume. int selectedProbeVolumeKey = selectedProbeVolume.GetID(); if (probeVolumeAtlas.TryGetScaleBias(out Vector3 selectedProbeVolumeScale, out Vector3 selectedProbeVolumeBias, selectedProbeVolumeKey)) { parameters.textureViewScale = selectedProbeVolumeScale; parameters.textureViewBias = selectedProbeVolumeBias; parameters.textureViewResolution = new Vector3( selectedProbeVolume.parameters.resolutionX, selectedProbeVolume.parameters.resolutionY, selectedProbeVolume.parameters.resolutionZ ); } if (ShaderConfig.s_ProbeVolumesBilateralFilteringMode == ProbeVolumesBilateralFilteringModes.OctahedralDepth) { if (probeVolumeAtlasOctahedralDepth.TryGetScaleBias(out Vector4 selectedProbeVolumeOctahedralDepthScaleBias, selectedProbeVolumeKey)) { parameters.atlasTextureOctahedralDepthScaleBias = selectedProbeVolumeOctahedralDepthScaleBias; } } } } #endif // Note: The system is not aware of slice packing in Z. // Need to modify scale and bias terms just before uploading to GPU. // TODO: Should we make it aware earlier up the chain? parameters.textureViewScale.z = parameters.textureViewScale.z / m_ProbeVolumeAtlasSHRTDepthSliceCount; parameters.textureViewBias.z = parameters.textureViewBias.z / m_ProbeVolumeAtlasSHRTDepthSliceCount; parameters.atlasResolutionAndSliceCount = new Vector4(s_ProbeVolumeAtlasResolution, s_ProbeVolumeAtlasResolution, s_ProbeVolumeAtlasResolution, m_ProbeVolumeAtlasSHRTDepthSliceCount); parameters.atlasResolutionAndSliceCountInverse = new Vector4(1.0f / s_ProbeVolumeAtlasResolution, 1.0f / s_ProbeVolumeAtlasResolution, 1.0f / s_ProbeVolumeAtlasResolution, 1.0f / m_ProbeVolumeAtlasSHRTDepthSliceCount); parameters.probeVolumeAtlas = m_ProbeVolumeAtlasSHRTHandle; parameters.probeVolumeAtlasOctahedralDepth = m_ProbeVolumeAtlasOctahedralDepthRTHandle; return parameters; } static void DisplayProbeVolumeAtlas(CommandBuffer cmd, in ProbeVolumeDebugOverlayParameters parameters, DebugOverlay debugOverlay) { MaterialPropertyBlock propertyBlock = new MaterialPropertyBlock(); propertyBlock.SetTexture(HDShaderIDs._AtlasTextureSH, parameters.probeVolumeAtlas); propertyBlock.SetVector(HDShaderIDs._TextureViewScale, parameters.textureViewScale); propertyBlock.SetVector(HDShaderIDs._TextureViewBias, parameters.textureViewBias); propertyBlock.SetVector(HDShaderIDs._TextureViewResolution, parameters.textureViewResolution); cmd.SetGlobalVector(HDShaderIDs._ProbeVolumeAtlasResolutionAndSliceCount, parameters.atlasResolutionAndSliceCount); cmd.SetGlobalVector(HDShaderIDs._ProbeVolumeAtlasResolutionAndSliceCountInverse, parameters.atlasResolutionAndSliceCountInverse); if (ShaderConfig.s_ProbeVolumesBilateralFilteringMode == ProbeVolumesBilateralFilteringModes.OctahedralDepth) { propertyBlock.SetTexture(HDShaderIDs._AtlasTextureOctahedralDepth, parameters.probeVolumeAtlasOctahedralDepth); propertyBlock.SetVector(HDShaderIDs._AtlasTextureOctahedralDepthScaleBias, parameters.atlasTextureOctahedralDepthScaleBias); } propertyBlock.SetVector(HDShaderIDs._ValidRange, parameters.validRange); propertyBlock.SetInt(HDShaderIDs._ProbeVolumeAtlasSliceMode, parameters.sliceMode); debugOverlay.SetViewport(cmd); cmd.DrawProcedural(Matrix4x4.identity, parameters.material, parameters.material.FindPass("ProbeVolume"), MeshTopology.Triangles, 3, 1, propertyBlock); debugOverlay.Next(); } } // class ProbeVolumeLighting } // namespace UnityEngine.Experimental.Rendering.HDPipeline