1029 lines
57 KiB
C#
1029 lines
57 KiB
C#
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<OrientedBBox> bounds;
|
|
public List<ProbeVolumeEngineData> data;
|
|
}
|
|
|
|
public partial class HDRenderPipeline
|
|
{
|
|
List<OrientedBBox> m_VisibleProbeVolumeBounds = null;
|
|
List<ProbeVolumeEngineData> 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<OrientedBBox>();
|
|
m_VisibleProbeVolumeData = new List<ProbeVolumeEngineData>();
|
|
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<ProbeVolumeController>();
|
|
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<ProbeVolumeController>();
|
|
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<ProbeVolume> 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<ProbeVolume>();
|
|
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
|