From 2f64702f54ccae6d801a91809b20113a7b915504 Mon Sep 17 00:00:00 2001 From: Tobias Alexander Franke Date: Fri, 17 Apr 2020 13:49:17 +0200 Subject: [PATCH 01/26] Simplify EvaluateProbeVolumes interface and builtin SampleBakedGI code --- .../Runtime/Lighting/LightLoop/LightLoop.hlsl | 96 ++++++++++--------- .../Lighting/ProbeVolume/ProbeVolume.hlsl | 12 +-- .../Runtime/Material/BuiltinGIUtilities.hlsl | 55 ++++------- 3 files changed, 71 insertions(+), 92 deletions(-) diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.hlsl index 7a90477f77a..ef648c0eadd 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.hlsl @@ -473,64 +473,66 @@ void LightLoop( float3 V, PositionInputs posInput, PreLightData preLightData, BS #endif #if SHADEROPTIONS_PROBE_VOLUMES_EVALUATION_MODE == PROBEVOLUMESEVALUATIONMODES_LIGHT_LOOP - bool uninitialized = IsUninitializedGI(builtinData.bakeDiffuseLighting); - builtinData.bakeDiffuseLighting = uninitialized ? float3(0.0, 0.0, 0.0) : builtinData.bakeDiffuseLighting; + if (featureFlags & LIGHTFEATUREFLAGS_PROBE_VOLUME) + { + bool uninitialized = IsUninitializedGI(builtinData.bakeDiffuseLighting); + builtinData.bakeDiffuseLighting = uninitialized ? float3(0.0, 0.0, 0.0) : builtinData.bakeDiffuseLighting; #if !SHADEROPTIONS_PROBE_VOLUMES_ADDITIVE_BLENDING - if (uninitialized) + if (uninitialized) #endif - { - // Need to make sure not to apply ModifyBakedDiffuseLighting() twice to our bakeDiffuseLighting data, which could happen if we are dealing with initialized data (light maps). - // Create a local BuiltinData variable here, and then add results to builtinData.bakeDiffuseLighting at the end. - BuiltinData builtinDataProbeVolumes; - ZERO_INITIALIZE(BuiltinData, builtinDataProbeVolumes); - - // For now, to match what we are doing in material pass evaluation, we simply call evaluate twice. - // Once for the front face, and once for the back face. - // This makes supporting transmission simple, and this support was especially important for supporting the fallback path with ambient probe. - // An alternative to calling evaluate twice (and looping over the probe data twice), would be to loop over the data once, but accumulate front face and backface values. - // Another alternative would be to accumulate + blend raw SH data, and then evaluate for both the front facing and backfacing BSDF outside of the probe volume loop. - // We should compare these techniques in our next round of profiling work. - float probeVolumeHierarchyWeightFrontFace = uninitialized ? 0.0f : 1.0f; - float probeVolumeHierarchyWeightBackFace = uninitialized ? 0.0f : 1.0f; - - // Note: we aren't suppose to access normalWS in lightloop, but bsdfData.normalWS is always define for any material. So this is safe. - builtinDataProbeVolumes.bakeDiffuseLighting = EvaluateProbeVolumesLightLoop(probeVolumeHierarchyWeightFrontFace, posInput, bsdfData.normalWS, builtinData.renderingLayers, featureFlags); - builtinDataProbeVolumes.backBakeDiffuseLighting = EvaluateProbeVolumesLightLoop(probeVolumeHierarchyWeightBackFace, posInput, -bsdfData.normalWS, builtinData.renderingLayers, featureFlags); - - builtinDataProbeVolumes.bakeDiffuseLighting += EvaluateProbeVolumeAmbientProbeFallback(probeVolumeHierarchyWeightFrontFace, bsdfData.normalWS); - builtinDataProbeVolumes.backBakeDiffuseLighting += EvaluateProbeVolumeAmbientProbeFallback(probeVolumeHierarchyWeightBackFace, -bsdfData.normalWS); - - // TODO: clean this case later to share more code, for now just reproduce the same behavior that is happening in PostInitBuiltinData() - - // Apply control from the indirect lighting volume settings (Remember there is no emissive here at this step) - builtinDataProbeVolumes.bakeDiffuseLighting *= _IndirectLightingMultiplier.x; - builtinDataProbeVolumes.backBakeDiffuseLighting *= _IndirectLightingMultiplier.x; - - #ifdef MODIFY_BAKED_DIFFUSE_LIGHTING - #ifdef DEBUG_DISPLAY + { + // Need to make sure not to apply ModifyBakedDiffuseLighting() twice to our bakeDiffuseLighting data, which could happen if we are dealing with initialized data (light maps). + // Create a local BuiltinData variable here, and then add results to builtinData.bakeDiffuseLighting at the end. + BuiltinData builtinDataProbeVolumes; + ZERO_INITIALIZE(BuiltinData, builtinDataProbeVolumes); + + // For now, to match what we are doing in material pass evaluation, we simply call evaluate twice. + // Once for the front face, and once for the back face. + // This makes supporting transmission simple, and this support was especially important for supporting the fallback path with ambient probe. + // An alternative to calling evaluate twice (and looping over the probe data twice), would be to loop over the data once, but accumulate front face and backface values. + // Another alternative would be to accumulate + blend raw SH data, and then evaluate for both the front facing and backfacing BSDF outside of the probe volume loop. + // We should compare these techniques in our next round of profiling work. + float probeVolumeHierarchyWeightFrontFace = uninitialized ? 0.0f : 1.0f; + float probeVolumeHierarchyWeightBackFace = uninitialized ? 0.0f : 1.0f; + + // Note: we aren't suppose to access normalWS in lightloop, but bsdfData.normalWS is always define for any material. So this is safe. + builtinDataProbeVolumes.bakeDiffuseLighting = EvaluateProbeVolumes(probeVolumeHierarchyWeightFrontFace, posInput, bsdfData.normalWS, builtinData.renderingLayers); + builtinDataProbeVolumes.backBakeDiffuseLighting = EvaluateProbeVolumes(probeVolumeHierarchyWeightBackFace, posInput, -bsdfData.normalWS, builtinData.renderingLayers); + + builtinDataProbeVolumes.bakeDiffuseLighting += EvaluateProbeVolumeAmbientProbeFallback(probeVolumeHierarchyWeightFrontFace, bsdfData.normalWS); + builtinDataProbeVolumes.backBakeDiffuseLighting += EvaluateProbeVolumeAmbientProbeFallback(probeVolumeHierarchyWeightBackFace, -bsdfData.normalWS); + + // TODO: clean this case later to share more code, for now just reproduce the same behavior that is happening in PostInitBuiltinData() + + // Apply control from the indirect lighting volume settings (Remember there is no emissive here at this step) + builtinDataProbeVolumes.bakeDiffuseLighting *= _IndirectLightingMultiplier.x; + builtinDataProbeVolumes.backBakeDiffuseLighting *= _IndirectLightingMultiplier.x; + +#ifdef MODIFY_BAKED_DIFFUSE_LIGHTING +#ifdef DEBUG_DISPLAY // When the lux meter is enabled, we don't want the albedo of the material to modify the diffuse baked lighting if (_DebugLightingMode != DEBUGLIGHTINGMODE_LUX_METER) - #endif +#endif ModifyBakedDiffuseLighting(V, posInput, preLightData, bsdfData, builtinDataProbeVolumes); - #endif +#endif - #if (SHADERPASS == SHADERPASS_DEFERRED_LIGHTING) - // If we are deferred we should apply baked AO here as it was already apply for lightmap. - // But in deferred ambientOcclusion is white so we should use specularOcclusion instead. It is the - // same case than for Microshadow so we can reuse this function. It should not be apply in forward - // as in this case the baked AO is correctly apply in PostBSDF() - // This is apply only on bakeDiffuseLighting as ModifyBakedDiffuseLighting combine both bakeDiffuseLighting and backBakeDiffuseLighting - builtinDataProbeVolumes.bakeDiffuseLighting *= GetAmbientOcclusionForMicroShadowing(bsdfData); - #endif +#if (SHADERPASS == SHADERPASS_DEFERRED_LIGHTING) + // If we are deferred we should apply baked AO here as it was already apply for lightmap. + // But in deferred ambientOcclusion is white so we should use specularOcclusion instead. It is the + // same case than for Microshadow so we can reuse this function. It should not be apply in forward + // as in this case the baked AO is correctly apply in PostBSDF() + // This is apply only on bakeDiffuseLighting as ModifyBakedDiffuseLighting combine both bakeDiffuseLighting and backBakeDiffuseLighting + builtinDataProbeVolumes.bakeDiffuseLighting *= GetAmbientOcclusionForMicroShadowing(bsdfData); +#endif - ApplyDebugToBuiltinData(builtinDataProbeVolumes); + ApplyDebugToBuiltinData(builtinDataProbeVolumes); - // Note: builtinDataProbeVolumes.bakeDiffuseLighting and builtinDataProbeVolumes.backBakeDiffuseLighting were combine inside of ModifyBakedDiffuseLighting(). - builtinData.bakeDiffuseLighting += builtinDataProbeVolumes.bakeDiffuseLighting; + // Note: builtinDataProbeVolumes.bakeDiffuseLighting and builtinDataProbeVolumes.backBakeDiffuseLighting were combine inside of ModifyBakedDiffuseLighting(). + builtinData.bakeDiffuseLighting += builtinDataProbeVolumes.bakeDiffuseLighting; + } } - #endif #if !defined(_SURFACE_TYPE_TRANSPARENT) diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl index b26f72f11c7..94d354680a5 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl @@ -125,11 +125,7 @@ void EvaluateProbeVolumeOctahedralDepthOcclusionFilterWeights( } } -#if SHADEROPTIONS_PROBE_VOLUMES_EVALUATION_MODE == PROBEVOLUMESEVALUATIONMODES_MATERIAL_PASS -float3 EvaluateProbeVolumesMaterialPass(inout float probeVolumeHierarchyWeight, PositionInputs posInput, float3 normalWS, uint renderingLayers) -#else // SHADEROPTIONS_PROBE_VOLUMES_EVALUATION_MODE == PROBEVOLUMESEVALUATIONMODES_LIGHT_LOOP -float3 EvaluateProbeVolumesLightLoop(inout float probeVolumeHierarchyWeight, PositionInputs posInput, float3 normalWS, uint renderingLayers, uint featureFlags) -#endif +float3 EvaluateProbeVolumes(inout float probeVolumeHierarchyWeight, PositionInputs posInput, float3 normalWS, uint renderingLayers) { #if !SHADEROPTIONS_PROBE_VOLUMES_ADDITIVE_BLENDING if (probeVolumeHierarchyWeight >= 1.0) { return float3(0.0, 0.0, 0.0); } @@ -139,11 +135,7 @@ float3 EvaluateProbeVolumesLightLoop(inout float probeVolumeHierarchyWeight, Pos float3 positionRWS = posInput.positionWS; float positionLinearDepth = posInput.linearDepth; - if (_EnableProbeVolumes -#if SHADEROPTIONS_PROBE_VOLUMES_EVALUATION_MODE == PROBEVOLUMESEVALUATIONMODES_LIGHT_LOOP - && (featureFlags & LIGHTFEATUREFLAGS_PROBE_VOLUME) -#endif - ) + if (_EnableProbeVolumes) { uint probeVolumeStart, probeVolumeCount; diff --git a/com.unity.render-pipelines.high-definition/Runtime/Material/BuiltinGIUtilities.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Material/BuiltinGIUtilities.hlsl index 0fc5da7c729..09c1c2c9b96 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Material/BuiltinGIUtilities.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/Material/BuiltinGIUtilities.hlsl @@ -94,39 +94,6 @@ void EvaluateLightProbeBuiltin(float3 positionRWS, float3 normalWS, float3 backN } } -void EvaluateProbeVolumes( PositionInputs posInputs, float3 normalWS, float3 backNormalWS, uint renderingLayers, - inout float3 bakeDiffuseLighting, inout float3 backBakeDiffuseLighting, inout float probeVolumeHierarchyWeight) -{ - // SHADEROPTIONS_PROBE_VOLUMES can be defined in ShaderConfig.cs.hlsl but set to 0 for disabled. - #if SHADEROPTIONS_PROBE_VOLUMES_EVALUATION_MODE == PROBEVOLUMESEVALUATIONMODES_LIGHT_LOOP - // If probe volumes are evaluated in the lightloop, we place a sentinel value to detect that no lightmap data is present at the current pixel, - // and we can safely overwrite baked data value with value from probe volume evaluation in light loop. - return UNINITIALIZED_GI; - #elif SHADEROPTIONS_PROBE_VOLUMES_EVALUATION_MODE == PROBEVOLUMESEVALUATIONMODES_MATERIAL_PASS - #ifdef SHADERPASS - #if SHADERPASS == SHADERPASS_GBUFFER || SHADERPASS == SHADERPASS_FORWARD - - #if SHADERPASS == SHADERPASS_GBUFFER || (SHADERPASS == SHADERPASS_FORWARD && defined(USE_FPTL_LIGHTLIST)) - // posInputs.tileCoord will be zeroed out in GBuffer pass. - // posInputs.tileCoord will be incorrect for probe volumes (which use clustered) in forward if forward lightloop is using FTPL lightlist (i.e: in ForwardOnly lighting configuration). - // Need to manually compute tile coord here. - float2 positionSS = posInputs.positionNDC.xy * _ScreenSize.xy; - uint2 tileCoord = uint2(positionSS) / ProbeVolumeGetTileSize(); - posInputs.tileCoord = tileCoord; - #endif - - // TODO: In a future PR, we will update EvaluateProbeVolumes to support a single call that evaluates front and back facing normals. - // For now, we simply call Evaluate 2x, and pay the additional cost when backBakeDiffuseLighting is in use. - float backProbeVolumeHierarchyWeight = probeVolumeHierarchyWeight; - bakeDiffuseLighting += EvaluateProbeVolumesMaterialPass(posInputs, normalWS, renderingLayers, probeVolumeHierarchyWeight); - backBakeDiffuseLighting += EvaluateProbeVolumesMaterialPass(posInputs, backNormalWS, renderingLayers, backProbeVolumeHierarchyWeight); - - EvaluateProbeVolumeAmbientProbeFallback(normalWS, backNormalWS, bakeDiffuseLighting, backBakeDiffuseLighting, probeVolumeHierarchyWeight); - #endif - #endif // #ifdef SHADERPASS - #endif -} - // No need to initialize bakeDiffuseLighting and backBakeDiffuseLighting must be initialize outside the function void SampleBakedGI( PositionInputs posInputs, @@ -141,7 +108,7 @@ void SampleBakedGI( float3 positionRWS = posInputs.positionWS; #define SAMPLE_LIGHTMAP (defined(LIGHTMAP_ON) || defined(DYNAMICLIGHTMAP_ON)) -#define SAMPLE_PROBEVOLUME (SHADEROPTIONS_PROBE_VOLUMES_EVALUATION_MODE != PROBEVOLUMESEVALUATIONMODES_DISABLED) \ +#define SAMPLE_PROBEVOLUME (SHADEROPTIONS_PROBE_VOLUMES_EVALUATION_MODE == PROBEVOLUMESEVALUATIONMODES_MATERIAL_PASS) \ && (!SAMPLE_LIGHTMAP || SHADEROPTIONS_PROBE_VOLUMES_ADDITIVE_BLENDING) #define SAMPLE_PROBEVOLUME_BUILTIN (!SAMPLE_LIGHTMAP && !SAMPLE_PROBEVOLUME) @@ -167,7 +134,25 @@ void SampleBakedGI( #else float probeVolumeHierarchyWeight = 0.0f; #endif - EvaluateProbeVolumes(posInputs, normalWS, backNormalWS, renderingLayers, bakeDiffuseLighting, backBakeDiffuseLighting, probeVolumeHierarchyWeight); + +#ifdef SHADERPASS +#if SHADERPASS == SHADERPASS_GBUFFER || SHADERPASS == SHADERPASS_FORWARD + +#if SHADERPASS == SHADERPASS_GBUFFER || (SHADERPASS == SHADERPASS_FORWARD && defined(USE_FPTL_LIGHTLIST)) + // posInputs.tileCoord will be zeroed out in GBuffer pass. + // posInputs.tileCoord will be incorrect for probe volumes (which use clustered) in forward if forward lightloop is using FTPL lightlist (i.e: in ForwardOnly lighting configuration). + // Need to manually compute tile coord here. + float2 positionSS = posInputs.positionNDC.xy * _ScreenSize.xy; + uint2 tileCoord = uint2(positionSS) / ProbeVolumeGetTileSize(); + posInputs.tileCoord = tileCoord; + #endif + + combinedGI += EvaluateProbeVolumes(probeVolumeHierarchyWeight, posInputs, normalWS, renderingLayers); + combinedGI += EvaluateProbeVolumeAmbientProbeFallback(probeVolumeHierarchyWeight, normalWS); +#endif + +#endif +>>>>>>> 4bc7b73f4e... Simplify EvaluateProbeVolumes interface and builtin SampleBakedGI code #endif #if SAMPLE_PROBEVOLUME_BUILTIN From ca0cbbc30698382b5f3ce66a36065904a1bf95ff Mon Sep 17 00:00:00 2001 From: Tobias Alexander Franke Date: Thu, 30 Apr 2020 15:53:30 +0200 Subject: [PATCH 02/26] Fix clearing of uninitialized GI --- .../Runtime/Lighting/LightLoop/LightLoop.hlsl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.hlsl index ef648c0eadd..f866c603226 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.hlsl @@ -473,11 +473,11 @@ void LightLoop( float3 V, PositionInputs posInput, PreLightData preLightData, BS #endif #if SHADEROPTIONS_PROBE_VOLUMES_EVALUATION_MODE == PROBEVOLUMESEVALUATIONMODES_LIGHT_LOOP + bool uninitialized = IsUninitializedGI(builtinData.bakeDiffuseLighting); + builtinData.bakeDiffuseLighting = uninitialized ? float3(0.0, 0.0, 0.0) : builtinData.bakeDiffuseLighting; + if (featureFlags & LIGHTFEATUREFLAGS_PROBE_VOLUME) { - bool uninitialized = IsUninitializedGI(builtinData.bakeDiffuseLighting); - builtinData.bakeDiffuseLighting = uninitialized ? float3(0.0, 0.0, 0.0) : builtinData.bakeDiffuseLighting; - #if !SHADEROPTIONS_PROBE_VOLUMES_ADDITIVE_BLENDING if (uninitialized) #endif From a1f06f6d85a32928bfd4f38ca093b9d4c2ee4326 Mon Sep 17 00:00:00 2001 From: pastasfuture Date: Thu, 28 May 2020 18:26:31 -0700 Subject: [PATCH 03/26] Added ProbeVolumesEncodingMode to ShaderOptions in ShaderConfig to allow users to configure their project to store and sample probe volumes as SH0 terms, SH1 terms, or SH2 terms, for different performance / quality tradeoffs. Data pipeline for this seems to be working now (though only properly verified with SH1 at the moment). Next step is to update sampling / evaluation code to handle SH0, and SH2 variants. --- .../Runtime/ShaderConfig.cs | 12 +- .../Runtime/ShaderConfig.cs.hlsl | 10 +- .../Lighting/ProbeVolume/ProbeVolume.cs | 431 ++++++++++++++++-- .../Lighting/ProbeVolume/ProbeVolumeAsset.cs | 143 +++--- .../ProbeVolume/ProbeVolumeAtlasBlit.compute | 86 +++- .../ProbeVolume/ProbeVolumeLighting.cs | 60 ++- .../Runtime/Lighting/SphericalHarmonics.cs | 116 ++++- .../Lighting/SphericalHarmonics.cs.hlsl | 22 +- 8 files changed, 746 insertions(+), 134 deletions(-) diff --git a/com.unity.render-pipelines.high-definition-config/Runtime/ShaderConfig.cs b/com.unity.render-pipelines.high-definition-config/Runtime/ShaderConfig.cs index dd3ff265869..8c63f893794 100644 --- a/com.unity.render-pipelines.high-definition-config/Runtime/ShaderConfig.cs +++ b/com.unity.render-pipelines.high-definition-config/Runtime/ShaderConfig.cs @@ -20,6 +20,14 @@ public enum ProbeVolumesEvaluationModes MaterialPass = 2, } + [GenerateHLSL(PackingRules.Exact)] + public enum ProbeVolumesEncodingModes + { + SphericalHarmonicsL0 = 0, + SphericalHarmonicsL1 = 1, + SphericalHarmonicsL2 = 2 + } + [GenerateHLSL(PackingRules.Exact)] public enum ShaderOptions { @@ -45,8 +53,9 @@ public enum ShaderOptions // and inside of the editor run: // Edit->Render Pipeline->Generate Shader Includes // Probe Volumes feature must also be enabled inside of your HDRenderPipelineAsset. - ProbeVolumesEvaluationMode = ProbeVolumesEvaluationModes.Disabled, + ProbeVolumesEvaluationMode = ProbeVolumesEvaluationModes.LightLoop, ProbeVolumesAdditiveBlending = 1, + ProbeVolumesEncodingMode = ProbeVolumesEncodingModes.SphericalHarmonicsL1, AreaLights = 1, @@ -69,6 +78,7 @@ public class ShaderConfig public static int s_PrecomputedAtmosphericAttenuation = (int)ShaderOptions.PrecomputedAtmosphericAttenuation; public static ProbeVolumesEvaluationModes s_ProbeVolumesEvaluationMode = (ProbeVolumesEvaluationModes)ShaderOptions.ProbeVolumesEvaluationMode; public static int s_ProbeVolumesAdditiveBlending = (int)ShaderOptions.ProbeVolumesAdditiveBlending; + public static ProbeVolumesEncodingModes s_ProbeVolumesEncodingMode = (ProbeVolumesEncodingModes)ShaderOptions.ProbeVolumesEncodingMode; public static int s_AreaLights = (int)ShaderOptions.AreaLights; public static int s_BarnDoor = (int)ShaderOptions.BarnDoor; [System.Obsolete("Deferred shadow can now assume any value, so this field is not used anymore.")] diff --git a/com.unity.render-pipelines.high-definition-config/Runtime/ShaderConfig.cs.hlsl b/com.unity.render-pipelines.high-definition-config/Runtime/ShaderConfig.cs.hlsl index 07c87f79be8..55f81db06cb 100644 --- a/com.unity.render-pipelines.high-definition-config/Runtime/ShaderConfig.cs.hlsl +++ b/com.unity.render-pipelines.high-definition-config/Runtime/ShaderConfig.cs.hlsl @@ -18,6 +18,13 @@ #define PROBEVOLUMESEVALUATIONMODES_LIGHT_LOOP (1) #define PROBEVOLUMESEVALUATIONMODES_MATERIAL_PASS (2) +// +// UnityEngine.Rendering.HighDefinition.ProbeVolumesEncodingModes: static fields +// +#define PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L0 (0) +#define PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L1 (1) +#define PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L2 (2) + // // UnityEngine.Rendering.HighDefinition.ShaderOptions: static fields // @@ -26,8 +33,9 @@ #define SHADEROPTIONS_PRECOMPUTED_ATMOSPHERIC_ATTENUATION (0) #define SHADEROPTIONS_RAYTRACING (0) #define SHADEROPTIONS_XR_MAX_VIEWS (2) -#define SHADEROPTIONS_PROBE_VOLUMES_EVALUATION_MODE (0) +#define SHADEROPTIONS_PROBE_VOLUMES_EVALUATION_MODE (1) #define SHADEROPTIONS_PROBE_VOLUMES_ADDITIVE_BLENDING (1) +#define SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE (1) #define SHADEROPTIONS_AREA_LIGHTS (1) #define SHADEROPTIONS_DEFERRED_SHADOW_FILTERING (1) #define SHADEROPTIONS_BARN_DOOR (0) diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.cs b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.cs index e5b42e8642c..edc9734d96e 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.cs @@ -21,6 +21,258 @@ internal enum VolumeBlendMode Subtractive } + // Container structure for managing a probe volume's payload. + // Spherical Harmonics data is stored as a flat float coefficients array. + // encodingMode defines the view for dataSH, specifically the stride at which coefficients map to a single probe's SH sample. + // In the future, encodingMode could possibly be extended to handle different compression modes as well. + [Serializable] + internal struct ProbeVolumePayload + { + public ProbeVolumesEncodingModes encodingMode; + public float[] dataSH; + public float[] dataValidity; + public float[] dataOctahedralDepth; + + public static int GetSHStride(ProbeVolumesEncodingModes encodingMode) + { + switch (encodingMode) + { + case ProbeVolumesEncodingModes.SphericalHarmonicsL0: return 3; + case ProbeVolumesEncodingModes.SphericalHarmonicsL1: return 12; + case ProbeVolumesEncodingModes.SphericalHarmonicsL2: return 27; + default: + { + Debug.Assert(false, "Error: encountered invalid encodingMode."); + return 0; + } + } + } + + public static int GetLength(ref ProbeVolumePayload payload) + { + return payload.dataValidity.Length; + } + + public static void Allocate(ref ProbeVolumePayload payload, ProbeVolumesEncodingModes encodingMode, int length) + { + payload.encodingMode = encodingMode; + payload.dataSH = new float[length * GetSHStride(encodingMode)]; + + // TODO: Only allocate dataValidity and dataOctahedralDepth if those payload slices are in use. + payload.dataValidity = new float[length]; + payload.dataOctahedralDepth = new float[length * 8 * 8]; + } + + public static void Ensure(ref ProbeVolumePayload payload, ProbeVolumesEncodingModes encodingMode, int length) + { + if (payload.encodingMode != encodingMode + || payload.dataSH == null + || payload.dataSH.Length != (length * GetSHStride(encodingMode))) + { + ProbeVolumePayload.Dispose(ref payload); + ProbeVolumePayload.Allocate(ref payload, encodingMode, length); + } + } + + public static void Dispose(ref ProbeVolumePayload payload) + { + payload.dataSH = null; + payload.dataValidity = null; + payload.dataOctahedralDepth = null; + } + + public static void Copy(ref ProbeVolumePayload payloadSrc, ref ProbeVolumePayload payloadDst) + { + Debug.Assert(ProbeVolumePayload.GetLength(ref payloadSrc) == ProbeVolumePayload.GetLength(ref payloadDst)); + + ProbeVolumePayload.Copy(ref payloadSrc, ref payloadDst, ProbeVolumePayload.GetLength(ref payloadSrc)); + } + + public static void Copy(ref ProbeVolumePayload payloadSrc, ref ProbeVolumePayload payloadDst, int length) + { + Debug.Assert(payloadSrc.encodingMode == payloadDst.encodingMode); + + Array.Copy(payloadSrc.dataSH, payloadDst.dataSH, length * GetSHStride(payloadSrc.encodingMode)); + + Array.Copy(payloadSrc.dataValidity, payloadDst.dataValidity, length); + + if (payloadSrc.dataOctahedralDepth != null && payloadDst.dataOctahedralDepth != null) + { + Array.Copy(payloadSrc.dataOctahedralDepth, payloadDst.dataOctahedralDepth, length * 8 * 8); + } + } + + public static void GetSphericalHarmonicsL0FromIndex(ref SphericalHarmonicsL0 sh, ref ProbeVolumePayload payload, int indexProbe) + { + Debug.Assert(payload.encodingMode == ProbeVolumesEncodingModes.SphericalHarmonicsL0); + + int stride = GetSHStride(ProbeVolumesEncodingModes.SphericalHarmonicsL0); + int indexDataBase = indexProbe * stride; + int indexDataEnd = indexDataBase + stride; + + Debug.Assert(payload.dataSH != null); + Debug.Assert(payload.dataSH.Length >= indexDataEnd); + + sh.shrgb.x = payload.dataSH[indexDataBase + 0]; + sh.shrgb.y = payload.dataSH[indexDataBase + 1]; + sh.shrgb.z = payload.dataSH[indexDataBase + 2]; + } + + public static void GetSphericalHarmonicsL1FromIndex(ref SphericalHarmonicsL1 sh, ref ProbeVolumePayload payload, int indexProbe) + { + Debug.Assert(payload.encodingMode == ProbeVolumesEncodingModes.SphericalHarmonicsL1); + + int stride = GetSHStride(ProbeVolumesEncodingModes.SphericalHarmonicsL1); + int indexDataBase = indexProbe * stride; + int indexDataEnd = indexDataBase + stride; + + Debug.Assert(payload.dataSH != null); + Debug.Assert(payload.dataSH.Length >= indexDataEnd); + + sh.shAr.x = payload.dataSH[indexDataBase + 0]; + sh.shAr.y = payload.dataSH[indexDataBase + 1]; + sh.shAr.z = payload.dataSH[indexDataBase + 2]; + sh.shAr.w = payload.dataSH[indexDataBase + 3]; + + sh.shAg.x = payload.dataSH[indexDataBase + 4]; + sh.shAg.y = payload.dataSH[indexDataBase + 5]; + sh.shAg.z = payload.dataSH[indexDataBase + 6]; + sh.shAg.w = payload.dataSH[indexDataBase + 7]; + + sh.shAb.x = payload.dataSH[indexDataBase + 8]; + sh.shAb.y = payload.dataSH[indexDataBase + 9]; + sh.shAb.z = payload.dataSH[indexDataBase + 10]; + sh.shAb.w = payload.dataSH[indexDataBase + 11]; + } + + public static void GetSphericalHarmonicsL2FromIndex(ref SphericalHarmonicsL2 sh, ref ProbeVolumePayload payload, int indexProbe) + { + Debug.Assert(payload.encodingMode == ProbeVolumesEncodingModes.SphericalHarmonicsL2); + + int stride = GetSHStride(ProbeVolumesEncodingModes.SphericalHarmonicsL2); + int indexDataBase = indexProbe * stride; + int indexDataEnd = indexDataBase + stride; + + Debug.Assert(payload.dataSH != null); + Debug.Assert(payload.dataSH.Length >= indexDataEnd); + + sh[0, 0] = payload.dataSH[indexDataBase + 0]; + sh[0, 1] = payload.dataSH[indexDataBase + 1]; + sh[0, 2] = payload.dataSH[indexDataBase + 2]; + sh[0, 3] = payload.dataSH[indexDataBase + 3]; + sh[0, 4] = payload.dataSH[indexDataBase + 4]; + sh[0, 5] = payload.dataSH[indexDataBase + 5]; + sh[0, 6] = payload.dataSH[indexDataBase + 6]; + sh[0, 7] = payload.dataSH[indexDataBase + 7]; + sh[0, 8] = payload.dataSH[indexDataBase + 8]; + + sh[1, 0] = payload.dataSH[indexDataBase + 9]; + sh[1, 1] = payload.dataSH[indexDataBase + 10]; + sh[1, 2] = payload.dataSH[indexDataBase + 11]; + sh[1, 3] = payload.dataSH[indexDataBase + 12]; + sh[1, 4] = payload.dataSH[indexDataBase + 13]; + sh[1, 5] = payload.dataSH[indexDataBase + 14]; + sh[1, 6] = payload.dataSH[indexDataBase + 15]; + sh[1, 7] = payload.dataSH[indexDataBase + 16]; + sh[1, 8] = payload.dataSH[indexDataBase + 17]; + + sh[2, 0] = payload.dataSH[indexDataBase + 18]; + sh[2, 1] = payload.dataSH[indexDataBase + 19]; + sh[2, 2] = payload.dataSH[indexDataBase + 20]; + sh[2, 3] = payload.dataSH[indexDataBase + 21]; + sh[2, 4] = payload.dataSH[indexDataBase + 22]; + sh[2, 5] = payload.dataSH[indexDataBase + 23]; + sh[2, 6] = payload.dataSH[indexDataBase + 24]; + sh[2, 7] = payload.dataSH[indexDataBase + 25]; + sh[2, 8] = payload.dataSH[indexDataBase + 26]; + } + + public static void SetSphericalHarmonicsL0FromIndex(ref ProbeVolumePayload payload, ref SphericalHarmonicsL0 sh, int indexProbe) + { + Debug.Assert(payload.encodingMode == ProbeVolumesEncodingModes.SphericalHarmonicsL0); + + int stride = GetSHStride(ProbeVolumesEncodingModes.SphericalHarmonicsL0); + int indexDataBase = indexProbe * stride; + int indexDataEnd = indexDataBase + stride; + + Debug.Assert(payload.dataSH != null); + Debug.Assert(payload.dataSH.Length >= indexDataEnd); + + payload.dataSH[indexDataBase + 0] = sh.shrgb.x; + payload.dataSH[indexDataBase + 1] = sh.shrgb.y; + payload.dataSH[indexDataBase + 2] = sh.shrgb.z; + } + + public static void SetSphericalHarmonicsL1FromIndex(ref ProbeVolumePayload payload, ref SphericalHarmonicsL1 sh, int indexProbe) + { + Debug.Assert(payload.encodingMode == ProbeVolumesEncodingModes.SphericalHarmonicsL1); + + int stride = GetSHStride(ProbeVolumesEncodingModes.SphericalHarmonicsL1); + int indexDataBase = indexProbe * stride; + int indexDataEnd = indexDataBase + stride; + + Debug.Assert(payload.dataSH != null); + Debug.Assert(payload.dataSH.Length >= indexDataEnd); + + payload.dataSH[indexDataBase + 0] = sh.shAr.x; + payload.dataSH[indexDataBase + 1] = sh.shAr.y; + payload.dataSH[indexDataBase + 2] = sh.shAr.z; + payload.dataSH[indexDataBase + 3] = sh.shAr.w; + + payload.dataSH[indexDataBase + 4] = sh.shAg.x; + payload.dataSH[indexDataBase + 5] = sh.shAg.y; + payload.dataSH[indexDataBase + 6] = sh.shAg.z; + payload.dataSH[indexDataBase + 7] = sh.shAg.w; + + payload.dataSH[indexDataBase + 8] = sh.shAb.x; + payload.dataSH[indexDataBase + 9] = sh.shAb.y; + payload.dataSH[indexDataBase + 10] = sh.shAb.z; + payload.dataSH[indexDataBase + 11] = sh.shAb.w; + } + + public static void SetSphericalHarmonicsL2FromIndex(ref ProbeVolumePayload payload, ref SphericalHarmonicsL2 sh, int indexProbe) + { + Debug.Assert(payload.encodingMode == ProbeVolumesEncodingModes.SphericalHarmonicsL2); + + int stride = GetSHStride(ProbeVolumesEncodingModes.SphericalHarmonicsL2); + int indexDataBase = indexProbe * stride; + int indexDataEnd = indexDataBase + stride; + + Debug.Assert(payload.dataSH != null); + Debug.Assert(payload.dataSH.Length >= indexDataEnd); + + payload.dataSH[indexDataBase + 0] = sh[0, 0]; + payload.dataSH[indexDataBase + 1] = sh[0, 1]; + payload.dataSH[indexDataBase + 2] = sh[0, 2]; + payload.dataSH[indexDataBase + 3] = sh[0, 3]; + payload.dataSH[indexDataBase + 4] = sh[0, 4]; + payload.dataSH[indexDataBase + 5] = sh[0, 5]; + payload.dataSH[indexDataBase + 6] = sh[0, 6]; + payload.dataSH[indexDataBase + 7] = sh[0, 7]; + payload.dataSH[indexDataBase + 8] = sh[0, 8]; + + payload.dataSH[indexDataBase + 9] = sh[1, 0]; + payload.dataSH[indexDataBase + 10] = sh[1, 1]; + payload.dataSH[indexDataBase + 11] = sh[1, 2]; + payload.dataSH[indexDataBase + 12] = sh[1, 3]; + payload.dataSH[indexDataBase + 13] = sh[1, 4]; + payload.dataSH[indexDataBase + 14] = sh[1, 5]; + payload.dataSH[indexDataBase + 15] = sh[1, 6]; + payload.dataSH[indexDataBase + 16] = sh[1, 7]; + payload.dataSH[indexDataBase + 17] = sh[1, 8]; + + payload.dataSH[indexDataBase + 18] = sh[2, 0]; + payload.dataSH[indexDataBase + 19] = sh[2, 1]; + payload.dataSH[indexDataBase + 20] = sh[2, 2]; + payload.dataSH[indexDataBase + 21] = sh[2, 3]; + payload.dataSH[indexDataBase + 22] = sh[2, 4]; + payload.dataSH[indexDataBase + 23] = sh[2, 5]; + payload.dataSH[indexDataBase + 24] = sh[2, 6]; + payload.dataSH[indexDataBase + 25] = sh[2, 7]; + payload.dataSH[indexDataBase + 26] = sh[2, 8]; + } + } + // Rather than hashing all the inputs that define a Probe Volume's bake state into a 128-bit int (16-bytes), // we simply store the raw state values (56-bytes) // While this is 3.5x more memory, it's still fairly low, and avoids the runtime cost of string appending garbage creation. @@ -36,6 +288,10 @@ internal struct ProbeVolumeSettingsKey public int resolutionX; public int resolutionY; public int resolutionZ; + public ProbeVolumesEncodingModes encodingMode; + public float backfaceTolerance; + public int dilationIterations; + } [Serializable] @@ -248,7 +504,10 @@ internal class ProbeVolume : MonoBehaviour size = Vector3.zero, resolutionX = 0, resolutionY = 0, - resolutionZ = 0 + resolutionZ = 0, + encodingMode = (ProbeVolumesEncodingModes)0, + backfaceTolerance = 0.0f, + dilationIterations = 0 }; internal bool dataUpdated = false; @@ -271,48 +530,91 @@ private void BakeKeyClear() size = Vector3.zero, resolutionX = 0, resolutionY = 0, - resolutionZ = 0 + resolutionZ = 0, + encodingMode = (ProbeVolumesEncodingModes)0, + backfaceTolerance = 0.0f, + dilationIterations = 0 }; } - internal (SphericalHarmonicsL1[], float[], float[]) GetData() + internal ProbeVolumePayload GetPayload() { dataUpdated = false; if (!probeVolumeAsset) - return (null, null, null); + { + return new ProbeVolumePayload() + { + encodingMode = ShaderConfig.s_ProbeVolumesEncodingMode, + dataValidity = null, + dataOctahedralDepth = null + }; + } - return (probeVolumeAsset.dataSH, probeVolumeAsset.dataValidity, probeVolumeAsset.dataOctahedralDepth); + return probeVolumeAsset.payload; } - protected void Awake() + bool CheckMigrationRequirement() { - Migrate(); + if (probeVolumeAsset == null) return false; + if (probeVolumeAsset.Version == (int)ProbeVolumeAsset.AssetVersion.Current) return false; + return true; } - bool CheckMigrationRequirement() + void Migrate() { - if (probeVolumeAsset && probeVolumeAsset.Version == (int)ProbeVolumeAsset.AssetVersion.Current) - return false; - - return false; + // Must not be called at deserialization time if require other component + while (CheckMigrationRequirement()) + { + ApplyMigration(); + } } void ApplyMigration() { + switch ((ProbeVolumeAsset.AssetVersion)probeVolumeAsset.Version) + { + case ProbeVolumeAsset.AssetVersion.First: + ApplyMigrationAddProbeVolumesAtlasEncodingModes(); + break; + + case ProbeVolumeAsset.AssetVersion.AddProbeVolumesAtlasEncodingModes: + default: + // No migration required. + break; + } } - void Migrate() + void ApplyMigrationAddProbeVolumesAtlasEncodingModes() { - // Must not be called at deserialization time if require other component - while (CheckMigrationRequirement()) + Debug.Assert(probeVolumeAsset != null && probeVolumeAsset.Version == (int)ProbeVolumeAsset.AssetVersion.First); + + probeVolumeAsset.m_Version = (int)ProbeVolumeAsset.AssetVersion.AddProbeVolumesAtlasEncodingModes; + + int probeLength = probeVolumeAsset.dataSH.Length; + probeVolumeAsset.payload = new ProbeVolumePayload { - ApplyMigration(); + encodingMode = ProbeVolumesEncodingModes.SphericalHarmonicsL1, + dataSH = new float[probeLength * ProbeVolumePayload.GetSHStride(ProbeVolumesEncodingModes.SphericalHarmonicsL1)], + dataValidity = probeVolumeAsset.dataValidity, + dataOctahedralDepth = probeVolumeAsset.dataOctahedralDepth + }; + + int shStride = ProbeVolumePayload.GetSHStride(ProbeVolumesEncodingModes.SphericalHarmonicsL1); + for (int i = 0; i < probeLength; ++i) + { + ProbeVolumePayload.SetSphericalHarmonicsL1FromIndex(ref probeVolumeAsset.payload, ref probeVolumeAsset.dataSH[i], i); } + + probeVolumeAsset.dataSH = null; + probeVolumeAsset.dataValidity = null; + probeVolumeAsset.dataOctahedralDepth = null; } protected void OnEnable() { + Migrate(); + #if UNITY_EDITOR OnValidate(); #endif @@ -347,7 +649,8 @@ internal bool IsAssetCompatible() { return parameters.resolutionX == probeVolumeAsset.resolutionX && parameters.resolutionY == probeVolumeAsset.resolutionY && - parameters.resolutionZ == probeVolumeAsset.resolutionZ; + parameters.resolutionZ == probeVolumeAsset.resolutionZ && + probeVolumeAsset.payload.encodingMode == ShaderConfig.s_ProbeVolumesEncodingMode; // TODO: Create runtime transforms between different encoding types to avoid having to rebake. } return false; @@ -426,9 +729,6 @@ internal void OnProbesBakeCompleted() return; int numProbes = parameters.resolutionX * parameters.resolutionY * parameters.resolutionZ; - SphericalHarmonicsL1[] data = new SphericalHarmonicsL1[numProbes]; - float[] dataValidity = new float[numProbes]; - float[] dataOctahedralDepth = new float[numProbes * 8 * 8]; var sh = new NativeArray(numProbes, Allocator.Temp, NativeArrayOptions.UninitializedMemory); var validity = new NativeArray(numProbes, Allocator.Temp, NativeArrayOptions.UninitializedMemory); @@ -436,31 +736,78 @@ internal void OnProbesBakeCompleted() if(UnityEditor.Experimental.Lightmapping.GetAdditionalBakedProbes(GetID(), sh, validity, octahedralDepth)) { - // TODO: Remove this data copy. - for (int i = 0, iLen = data.Length; i < iLen; ++i) + if (!probeVolumeAsset || GetID() != probeVolumeAsset.instanceID) + probeVolumeAsset = ProbeVolumeAsset.CreateAsset(GetID()); + + probeVolumeAsset.instanceID = GetID(); + probeVolumeAsset.resolutionX = parameters.resolutionX; + probeVolumeAsset.resolutionY = parameters.resolutionY; + probeVolumeAsset.resolutionZ = parameters.resolutionZ; + + ProbeVolumePayload.Ensure(ref probeVolumeAsset.payload, ShaderConfig.s_ProbeVolumesEncodingMode, numProbes); + + int shStride = ProbeVolumePayload.GetSHStride(probeVolumeAsset.payload.encodingMode); + + // TODO: Remove this data copy. Would require the lightmapper to have GetAdditionalBakedProbes with L0, L1, and L2 variants. + switch (probeVolumeAsset.payload.encodingMode) { - data[i].shAr = new Vector4(sh[i][0, 3], sh[i][0, 1], sh[i][0, 2], sh[i][0, 0] - sh[i][0, 6]); - data[i].shAg = new Vector4(sh[i][1, 3], sh[i][1, 1], sh[i][1, 2], sh[i][1, 0] - sh[i][1, 6]); - data[i].shAb = new Vector4(sh[i][2, 3], sh[i][2, 1], sh[i][2, 2], sh[i][2, 0] - sh[i][2, 6]); + case ProbeVolumesEncodingModes.SphericalHarmonicsL0: + { + for (int i = 0, iLen = sh.Length; i < iLen; ++i) + { + SphericalHarmonicsL0 sh0 = new SphericalHarmonicsL0 + { + // TODO: May need some additional data transform here to handle downgrading from SH2 to SH0. + shrgb = new Vector3(sh[i][0, 0], sh[i][1, 0], sh[i][2, 0]) + }; - dataValidity[i] = validity[i]; + ProbeVolumePayload.SetSphericalHarmonicsL0FromIndex(ref probeVolumeAsset.payload, ref sh0, i); + } + break; + } - for (int j = 0; j < 64; ++j) + case ProbeVolumesEncodingModes.SphericalHarmonicsL1: + { + for (int i = 0, iLen = sh.Length; i < iLen; ++i) + { + SphericalHarmonicsL1 sh1 = new SphericalHarmonicsL1 + { + shAr = new Vector4(sh[i][0, 3], sh[i][0, 1], sh[i][0, 2], sh[i][0, 0] - sh[i][0, 6]), + shAg = new Vector4(sh[i][1, 3], sh[i][1, 1], sh[i][1, 2], sh[i][1, 0] - sh[i][1, 6]), + shAb = new Vector4(sh[i][2, 3], sh[i][2, 1], sh[i][2, 2], sh[i][2, 0] - sh[i][2, 6]) + }; + + ProbeVolumePayload.SetSphericalHarmonicsL1FromIndex(ref probeVolumeAsset.payload, ref sh1, i); + } + break; + } + + case ProbeVolumesEncodingModes.SphericalHarmonicsL2: { - dataOctahedralDepth[i * 64 + j] = octahedralDepth[i * 64 + j]; + for (int i = 0, iLen = sh.Length; i < iLen; ++i) + { + SphericalHarmonicsL2 sh2 = sh[i]; + ProbeVolumePayload.SetSphericalHarmonicsL2FromIndex(ref probeVolumeAsset.payload, ref sh2, i); + } + break; + } + + default: + { + Debug.Assert(false, "Error: Encountered unsupported probe volume payload encoding mode: " + probeVolumeAsset.payload.encodingMode); + break; } } - if (!probeVolumeAsset || GetID() != probeVolumeAsset.instanceID) - probeVolumeAsset = ProbeVolumeAsset.CreateAsset(GetID()); + for (int i = 0, iLen = sh.Length; i < iLen; ++i) + { + probeVolumeAsset.payload.dataValidity[i] = validity[i]; - probeVolumeAsset.instanceID = GetID(); - probeVolumeAsset.dataSH = data; - probeVolumeAsset.dataValidity = dataValidity; - probeVolumeAsset.dataOctahedralDepth = dataOctahedralDepth; - probeVolumeAsset.resolutionX = parameters.resolutionX; - probeVolumeAsset.resolutionY = parameters.resolutionY; - probeVolumeAsset.resolutionZ = parameters.resolutionZ; + for (int j = 0; j < 64; ++j) + { + probeVolumeAsset.payload.dataOctahedralDepth[i * 64 + j] = octahedralDepth[i * 64 + j]; + } + } if (UnityEditor.Lightmapping.giWorkflowMode != UnityEditor.Lightmapping.GIWorkflowMode.Iterative) UnityEditor.EditorUtility.SetDirty(probeVolumeAsset); @@ -494,7 +841,10 @@ private static ProbeVolumeSettingsKey ComputeProbeVolumeSettingsKeyFromProbeVolu size = probeVolume.parameters.size, resolutionX = probeVolume.parameters.resolutionX, resolutionY = probeVolume.parameters.resolutionY, - resolutionZ = probeVolume.parameters.resolutionZ + resolutionZ = probeVolume.parameters.resolutionZ, + encodingMode = probeVolume.probeVolumeAsset.payload.encodingMode, + backfaceTolerance = probeVolume.parameters.backfaceTolerance, + dilationIterations = probeVolume.parameters.dilationIterations }; } @@ -506,7 +856,10 @@ private static bool ProbeVolumeSettingsKeyEquals(ref ProbeVolumeSettingsKey a, r && (a.size == b.size) && (a.resolutionX == b.resolutionX) && (a.resolutionY == b.resolutionY) - && (a.resolutionZ == b.resolutionZ); + && (a.resolutionZ == b.resolutionZ) + && (a.encodingMode == b.encodingMode) + && (a.backfaceTolerance == b.backfaceTolerance) + && (a.dilationIterations == b.dilationIterations); } private void SetupProbePositions() diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeAsset.cs b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeAsset.cs index 055375bb0f4..3179c51e54b 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeAsset.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeAsset.cs @@ -10,20 +10,31 @@ internal class ProbeVolumeAsset : ScriptableObject internal enum AssetVersion { First, + AddProbeVolumesAtlasEncodingModes, // Add new version here and they will automatically be the Current one Max, Current = Max - 1 } - [SerializeField] protected internal int m_Version = (int)AssetVersion.First; + [SerializeField] protected internal int m_Version = (int)AssetVersion.Current; [SerializeField] internal int Version { get => m_Version; } [SerializeField] internal int instanceID; + // dataSH, dataValidity, and dataOctahedralDepth is from AssetVersion.First. In versions AddProbeVolumesAtlasEncodingModes or greater, this should be null. [SerializeField] internal SphericalHarmonicsL1[] dataSH = null; [SerializeField] internal float[] dataValidity = null; [SerializeField] internal float[] dataOctahedralDepth = null; + + [SerializeField] internal ProbeVolumePayload payload = new ProbeVolumePayload() + { + encodingMode = ShaderConfig.s_ProbeVolumesEncodingMode, + dataSH = null, + dataValidity = null, + dataOctahedralDepth = null + }; + [SerializeField] internal int resolutionX; [SerializeField] internal int resolutionY; [SerializeField] internal int resolutionZ; @@ -83,6 +94,11 @@ internal static ProbeVolumeAsset CreateAsset(int id = -1) return asset; } + internal bool IsDataAssigned() + { + return payload.dataSH != null; + } + protected internal static Vector3Int[] s_Offsets = new Vector3Int[] { // middle slice new Vector3Int( 1, 0, 0), @@ -120,74 +136,68 @@ internal static ProbeVolumeAsset CreateAsset(int id = -1) new Vector3Int( 0, -1, -1), }; - protected internal int IndexAt(Vector3Int pos) + protected internal int ComputeIndex1DFrom3D(Vector3Int pos) { return pos.x + pos.y * resolutionX + pos.z * resolutionX * resolutionY; } - (SphericalHarmonicsL1, float) Sample(SphericalHarmonicsL1[] dataSH, float[] dataValidity, Vector3Int pos) - { - if (pos.x < 0 || pos.y < 0 || pos.z < 0 || pos.x >= resolutionX || pos.y >= resolutionY || pos.z >= resolutionZ) - return (new SphericalHarmonicsL1(), 1); - - int index = IndexAt(pos); - - SphericalHarmonicsL1 sh = dataSH[index]; - float v = dataValidity[index]; - - return (sh, v); - } - - bool OverwriteInvalidProbe(SphericalHarmonicsL1[] dataSrc, SphericalHarmonicsL1[] dataDst, float[] dataValiditySrc, float[] dataValidityDst, Vector3Int pos, float backfaceTolerance) + bool OverwriteInvalidProbe(ref ProbeVolumePayload payloadSrc, ref ProbeVolumePayload payloadDst, Vector3Int index3D, float backfaceTolerance) { - (SphericalHarmonicsL1 center, float validityCenter) = Sample(dataSrc, dataValiditySrc, pos); - - int centerIndex = IndexAt(pos); - - dataDst[centerIndex] = center; - dataValidityDst[centerIndex] = validityCenter; + Debug.Assert(payloadSrc.encodingMode == payloadDst.encodingMode); - if (validityCenter <= backfaceTolerance) - return true; + int shStride = ProbeVolumePayload.GetSHStride(payloadSrc.encodingMode); + int centerIndex = ComputeIndex1DFrom3D(index3D); - int weights = 0; - SphericalHarmonicsL1 result = new SphericalHarmonicsL1(); - float validity = 0; + // Account for center sample accumulation weight, already assigned. + float weights = 1.0f - payloadDst.dataValidity[centerIndex]; foreach (Vector3Int offset in s_Offsets) { - Vector3Int samplePos = pos + offset; + Vector3Int sampleIndex3D = index3D + offset; + + if (sampleIndex3D.x < 0 || sampleIndex3D.y < 0 || sampleIndex3D.z < 0 + || sampleIndex3D.x >= resolutionX || sampleIndex3D.y >= resolutionY || sampleIndex3D.z >= resolutionZ) + { + continue; + } - (SphericalHarmonicsL1 sample, float sampleValidity) = Sample(dataSrc, dataValiditySrc, samplePos); + int sampleIndex1D = ComputeIndex1DFrom3D(sampleIndex3D); - if (sampleValidity > backfaceTolerance) - // invalid sample, don't use + float sampleValidity = payloadSrc.dataValidity[sampleIndex1D]; + if (sampleValidity > 0.999f) + { + // Sample will have effectively zero contribution. Early out. continue; + } - result.shAr += sample.shAr; - result.shAg += sample.shAg; - result.shAb += sample.shAb; + float sampleWeight = 1.0f - sampleValidity; + weights += sampleWeight; - validity += sampleValidity; + for (int c = 0; c < shStride; ++c) + { + payloadDst.dataSH[centerIndex * shStride + c] += payloadSrc.dataSH[sampleIndex1D * shStride + c] * sampleWeight; + } - weights++; + payloadDst.dataValidity[centerIndex] += sampleValidity * sampleWeight; } - if (weights > 0) + if (weights > 0.0f) { - result.shAr /= weights; - result.shAg /= weights; - result.shAb /= weights; - validity /= weights; + float weightsNormalization = 1.0f / weights; + for (int c = 0; c < shStride; ++c) + { + payloadDst.dataSH[centerIndex * shStride + c] *= weightsNormalization; + } - dataDst[centerIndex] = result; - dataValidityDst[centerIndex] = validity; + payloadDst.dataValidity[centerIndex] *= weightsNormalization; return true; } - - // Haven't managed to overwrite an invalid probe - return false; + else + { + // Haven't managed to overwrite an invalid probe + return false; + } } void DilateIntoInvalidProbes(float backfaceTolerance, int dilateIterations) @@ -195,26 +205,49 @@ void DilateIntoInvalidProbes(float backfaceTolerance, int dilateIterations) if (dilateIterations == 0) return; - SphericalHarmonicsL1[] dataBis = new SphericalHarmonicsL1[dataSH.Length]; - float[] dataValidityBis = new float[dataSH.Length]; + ProbeVolumePayload payloadBackbuffer = new ProbeVolumePayload(); + ProbeVolumePayload.Allocate(ref payloadBackbuffer, payload.encodingMode, ProbeVolumePayload.GetLength(ref payload)); int i = 0; for (; i < dilateIterations; ++i) { bool invalidProbesRemaining = false; + // First, copy data from source to destination to seed our center sample. + ProbeVolumePayload.Copy(ref payload, ref payloadBackbuffer); + + // Foreach probe, gather neighboring probe data, weighted by validity. + // TODO: "validity" is actually stored as how occluded the surface is, so it is really inverse validity. + // We should probably rename this to avoid confusion. for (int z = 0; z < resolutionZ; ++z) + { for (int y = 0; y < resolutionY; ++y) + { for (int x = 0; x < resolutionX; ++x) - invalidProbesRemaining |= !OverwriteInvalidProbe(dataSH, dataBis, dataValidity, dataValidityBis, new Vector3Int(x, y, z), backfaceTolerance); + { + Vector3Int index3D = new Vector3Int(x, y, z); + int index1D = ComputeIndex1DFrom3D(index3D); + float validity = payloadBackbuffer.dataValidity[index1D]; + if (validity <= backfaceTolerance) + { + // "validity" aka occlusion is low enough for our theshold. + // No need to gather + filter neighbors. + continue; + } + + invalidProbesRemaining |= !OverwriteInvalidProbe(ref payload, ref payloadBackbuffer, index3D, backfaceTolerance); + } + } + } // Swap buffers - (dataSH, dataBis) = (dataBis, dataSH); - (dataValidity, dataValidityBis) = (dataValidityBis, dataValidity); + (payload, payloadBackbuffer) = (payloadBackbuffer, payload); if (!invalidProbesRemaining) break; } + + ProbeVolumePayload.Dispose(ref payloadBackbuffer); } internal void Dilate(float backfaceTolerance, int dilationIterations) @@ -222,12 +255,14 @@ internal void Dilate(float backfaceTolerance, int dilationIterations) if (backfaceTolerance == this.backfaceTolerance && dilationIterations == this.dilationIterations) return; - float[] validityBackup = new float[dataValidity.Length]; - Array.Copy(dataValidity, validityBackup, dataValidity.Length); + // Validity data will be overwritten during dilation as a per-probe quality heuristic. + // We want to retain original validity data for use bilateral filter on the GPU at runtime. + float[] validityBackup = new float[payload.dataValidity.Length]; + Array.Copy(payload.dataValidity, validityBackup, payload.dataValidity.Length); DilateIntoInvalidProbes(backfaceTolerance, dilationIterations); - Array.Copy(validityBackup, dataValidity, validityBackup.Length); + Array.Copy(validityBackup, payload.dataValidity, validityBackup.Length); this.backfaceTolerance = backfaceTolerance; this.dilationIterations = dilationIterations; diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeAtlasBlit.compute b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeAtlasBlit.compute index fcd21c05183..8728d200764 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeAtlasBlit.compute +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeAtlasBlit.compute @@ -6,10 +6,11 @@ #pragma only_renderers d3d11 playstation xboxone vulkan metal switch +#include "Packages/com.unity.render-pipelines.high-definition-config/Runtime/ShaderConfig.cs.hlsl" #include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/SphericalHarmonics.cs.hlsl" #include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeLighting.cs.hlsl" -StructuredBuffer _ProbeVolumeAtlasReadBuffer; +StructuredBuffer _ProbeVolumeAtlasReadBuffer; StructuredBuffer _ProbeVolumeAtlasReadValidityBuffer; RWTexture3D _ProbeVolumeAtlasWriteTextureSH; @@ -50,10 +51,6 @@ void PROBE_VOLUME_ATLAS_BLIT_KERNEL(uint groupThreadId : SV_GroupThreadID, uint writeIndex += (uint3)floor(_ProbeVolumeAtlasBias * _ProbeVolumeAtlasResolutionAndSliceCount.xyz); - _ProbeVolumeAtlasWriteTextureSH[uint3(writeIndex.x, writeIndex.y, writeIndex.z + _ProbeVolumeAtlasResolutionAndSliceCount.z * 0)] = _ProbeVolumeAtlasReadBuffer[readIndex].shAr; - _ProbeVolumeAtlasWriteTextureSH[uint3(writeIndex.x, writeIndex.y, writeIndex.z + _ProbeVolumeAtlasResolutionAndSliceCount.z * 1)] = _ProbeVolumeAtlasReadBuffer[readIndex].shAg; - _ProbeVolumeAtlasWriteTextureSH[uint3(writeIndex.x, writeIndex.y, writeIndex.z + _ProbeVolumeAtlasResolutionAndSliceCount.z * 2)] = _ProbeVolumeAtlasReadBuffer[readIndex].shAb; - // Convert from "Occlusion" representation from Lightmapper into "Validity" representation which can be directly fed into bilateral filter. // This avoids computing 1.0 - occlusion terms per 8 probes per volume per pixel per frame. // TODO: Could additionally pre-compute and power transform here to modify curvature of validity data for filter. @@ -61,5 +58,84 @@ void PROBE_VOLUME_ATLAS_BLIT_KERNEL(uint groupThreadId : SV_GroupThreadID, uint float occlusion = _ProbeVolumeAtlasReadValidityBuffer[readIndex]; float validity = 1.0 - occlusion; +#if SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L0 + const uint SH_STRIDE = 3; + _ProbeVolumeAtlasWriteTextureSH[uint3(writeIndex.x, writeIndex.y, writeIndex.z + _ProbeVolumeAtlasResolutionAndSliceCount.z * 0)] = float4( + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 0 + 0], + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 0 + 1], + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 0 + 2], + validity // last channel in float4 is unused by SH0 terms, so we take this opportunity to pack validity into it. + ); + +#elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L1 + const uint SH_STRIDE = 12; + _ProbeVolumeAtlasWriteTextureSH[uint3(writeIndex.x, writeIndex.y, writeIndex.z + _ProbeVolumeAtlasResolutionAndSliceCount.z * 0)] = float4( + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 0 + 0], + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 0 + 1], + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 0 + 2], + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 0 + 3] + ); + _ProbeVolumeAtlasWriteTextureSH[uint3(writeIndex.x, writeIndex.y, writeIndex.z + _ProbeVolumeAtlasResolutionAndSliceCount.z * 1)] = float4( + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 4 + 0], + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 4 + 1], + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 4 + 2], + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 4 + 3] + ); + _ProbeVolumeAtlasWriteTextureSH[uint3(writeIndex.x, writeIndex.y, writeIndex.z + _ProbeVolumeAtlasResolutionAndSliceCount.z * 2)] = float4( + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 8 + 0], + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 8 + 1], + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 8 + 2], + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 8 + 3] + ); _ProbeVolumeAtlasWriteTextureSH[uint3(writeIndex.x, writeIndex.y, writeIndex.z + _ProbeVolumeAtlasResolutionAndSliceCount.z * 3)] = float4(validity, 0.0, 0.0, 0.0); + +#elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L2 + const uint SH_STRIDE = 27; + + _ProbeVolumeAtlasWriteTextureSH[uint3(writeIndex.x, writeIndex.y, writeIndex.z + _ProbeVolumeAtlasResolutionAndSliceCount.z * 0)] = float4( + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 0 + 0], + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 0 + 1], + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 0 + 2], + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 0 + 3] + ); + _ProbeVolumeAtlasWriteTextureSH[uint3(writeIndex.x, writeIndex.y, writeIndex.z + _ProbeVolumeAtlasResolutionAndSliceCount.z * 1)] = float4( + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 4 + 0], + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 4 + 1], + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 4 + 2], + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 4 + 3] + ); + _ProbeVolumeAtlasWriteTextureSH[uint3(writeIndex.x, writeIndex.y, writeIndex.z + _ProbeVolumeAtlasResolutionAndSliceCount.z * 2)] = float4( + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 8 + 0], + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 8 + 1], + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 8 + 2], + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 8 + 3] + ); + _ProbeVolumeAtlasWriteTextureSH[uint3(writeIndex.x, writeIndex.y, writeIndex.z + _ProbeVolumeAtlasResolutionAndSliceCount.z * 3)] = float4( + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 12 + 0], + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 12 + 1], + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 12 + 2], + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 12 + 3] + ); + _ProbeVolumeAtlasWriteTextureSH[uint3(writeIndex.x, writeIndex.y, writeIndex.z + _ProbeVolumeAtlasResolutionAndSliceCount.z * 4)] = float4( + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 16 + 0], + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 16 + 1], + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 16 + 2], + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 16 + 3] + ); + _ProbeVolumeAtlasWriteTextureSH[uint3(writeIndex.x, writeIndex.y, writeIndex.z + _ProbeVolumeAtlasResolutionAndSliceCount.z * 5)] = float4( + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 20 + 0], + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 20 + 1], + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 20 + 2], + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 20 + 3] + ); + _ProbeVolumeAtlasWriteTextureSH[uint3(writeIndex.x, writeIndex.y, writeIndex.z + _ProbeVolumeAtlasResolutionAndSliceCount.z * 6)] = float4( + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 24 + 0], + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 24 + 1], + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 24 + 2], + validity // last channel in float4 is unused by SH2 terms, so we take this opportunity to pack validity into it. + ); + +#else + #error "Unsupported ShaderOptions.ProbeVolumesAtlasEncodingMode."; +#endif } diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeLighting.cs b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeLighting.cs index 108509dce40..d447a1bca0f 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeLighting.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeLighting.cs @@ -107,7 +107,8 @@ public partial class HDRenderPipeline // With current settings this compute buffer will take 1024 * 1024 * sizeof(float) * coefficientCount (12) bytes ~= 50.3 MB. static int s_MaxProbeVolumeProbeCount = 1024 * 1024; RTHandle m_ProbeVolumeAtlasSHRTHandle; - int m_ProbeVolumeAtlasSHRTDepthSliceCount = 4; // one texture per [RGB] SH coefficients + one texture for float4(validity, unassigned, unassigned, unassigned). + + int m_ProbeVolumeAtlasSHRTDepthSliceCount; Texture3DAtlasDynamic probeVolumeAtlas = null; RTHandle m_ProbeVolumeAtlasOctahedralDepthRTHandle; @@ -167,10 +168,40 @@ internal void CreateProbeVolumeBuffers() 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_ProbeVolumeAtlasBlitDataBuffer = new ComputeBuffer(s_MaxProbeVolumeProbeCount, Marshal.SizeOf(typeof(SphericalHarmonicsL1))); + s_ProbeVolumeAtlasBlitDataBuffer = new ComputeBuffer(s_MaxProbeVolumeProbeCount * ProbeVolumePayload.GetSHStride(ShaderConfig.s_ProbeVolumesEncodingMode), Marshal.SizeOf(typeof(float))); s_ProbeVolumeAtlasBlitDataValidityBuffer = new ComputeBuffer(s_MaxProbeVolumeProbeCount, Marshal.SizeOf(typeof(float))); s_ProbeVolumeAtlasOctahedralDepthBuffer = new ComputeBuffer(s_MaxProbeVolumeProbeCount, Marshal.SizeOf(typeof(float))); + switch (ShaderConfig.s_ProbeVolumesEncodingMode) + { + case ProbeVolumesEncodingModes.SphericalHarmonicsL0: + { + // One "texture slice" for our single RGB SH DC term. Validity is placed in the alpha channel. + m_ProbeVolumeAtlasSHRTDepthSliceCount = 1; + break; + } + + case ProbeVolumesEncodingModes.SphericalHarmonicsL1: + { + // One "texture slice" per [R, G, and B] SH 4x float coefficients + one "texture slice" for float4(validity, unassigned, unassigned, unassigned). + m_ProbeVolumeAtlasSHRTDepthSliceCount = 4; + break; + } + + case ProbeVolumesEncodingModes.SphericalHarmonicsL2: + { + // One "texture slice" per 4x float coefficients, with the Validity term placed in the alpha channel of the last slice. + m_ProbeVolumeAtlasSHRTDepthSliceCount = 7; + break; + } + + default: + { + Debug.Assert(false, "Error: Encountered unsupported probe volumes encoding mode in ShaderConfig.cs. Please set a valid enum value for ShaderOptions.ProbeVolumesEncodingMode."); + break; + } + } + m_ProbeVolumeAtlasSHRTHandle = RTHandles.Alloc( width: s_ProbeVolumeAtlasWidth, height: s_ProbeVolumeAtlasHeight, @@ -392,14 +423,16 @@ internal bool EnsureProbeVolumeInAtlas(ScriptableRenderContext renderContext, Co { if (isUploadNeeded || volume.dataUpdated) { - (var data, var dataValidity, var dataOctahedralDepth) = volume.GetData(); + ProbeVolumePayload payload = volume.GetPayload(); - if (data == null || data.Length == 0 || !volume.IsAssetCompatible()) + if (payload.dataSH == null || payload.dataSH.Length == 0 || !volume.IsAssetCompatible()) { ReleaseProbeVolumeFromAtlas(volume); return false; } + int sizeSHCoefficients = size * ProbeVolumePayload.GetSHStride(payload.encodingMode); + //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, @@ -429,11 +462,11 @@ internal bool EnsureProbeVolumeInAtlas(ScriptableRenderContext renderContext, Co 1.0f / (float)s_ProbeVolumeAtlasDepth, 1.0f / (float)m_ProbeVolumeAtlasSHRTDepthSliceCount )); - Debug.Assert(data.Length == size, "ProbeVolume: The probe volume baked data and its resolution are out of sync! Volume data length is " + data.Length + ", but resolution size is " + size + "."); - Debug.Assert(size < s_MaxProbeVolumeProbeCount, "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."); + Debug.Assert(payload.dataSH.Length == sizeSHCoefficients, "ProbeVolume: The probe volume baked data and its resolution are out of sync! Volume data length is " + payload.dataSH.Length + ", but resolution * SH stride size is " + sizeSHCoefficients + "."); + Debug.Assert(size <= s_MaxProbeVolumeProbeCount, "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."); - s_ProbeVolumeAtlasBlitDataBuffer.SetData(data); - s_ProbeVolumeAtlasBlitDataValidityBuffer.SetData(dataValidity); + s_ProbeVolumeAtlasBlitDataBuffer.SetData(payload.dataSH); + s_ProbeVolumeAtlasBlitDataValidityBuffer.SetData(payload.dataValidity); cmd.SetComputeIntParam(s_ProbeVolumeAtlasBlitCS, HDShaderIDs._ProbeVolumeAtlasReadBufferCount, size); cmd.SetComputeBufferParam(s_ProbeVolumeAtlasBlitCS, s_ProbeVolumeAtlasBlitKernel, HDShaderIDs._ProbeVolumeAtlasReadBuffer, s_ProbeVolumeAtlasBlitDataBuffer); cmd.SetComputeBufferParam(s_ProbeVolumeAtlasBlitCS, s_ProbeVolumeAtlasBlitKernel, HDShaderIDs._ProbeVolumeAtlasReadValidityBuffer, s_ProbeVolumeAtlasBlitDataValidityBuffer); @@ -471,9 +504,9 @@ internal bool EnsureProbeVolumeInAtlasOctahedralDepth(ScriptableRenderContext re { if (isUploadNeeded || volume.dataUpdated) { - (var data, var dataValidity, var dataOctahedralDepth) = volume.GetData(); + ProbeVolumePayload payload = volume.GetPayload(); - if (data == null || data.Length == 0 || !volume.IsAssetCompatible()) + if (payload.dataOctahedralDepth == null || payload.dataOctahedralDepth.Length == 0 || !volume.IsAssetCompatible()) { ReleaseProbeVolumeFromAtlas(volume); return false; @@ -513,9 +546,9 @@ internal bool EnsureProbeVolumeInAtlasOctahedralDepth(ScriptableRenderContext re 1.0f / (float)s_ProbeVolumeAtlasDepth, 1.0f / (float)m_ProbeVolumeAtlasSHRTDepthSliceCount )); - Debug.Assert(dataOctahedralDepth.Length == size, "ProbeVolume: The probe volume baked data and its resolution are out of sync! Volume data length is " + dataOctahedralDepth.Length + ", but resolution size is " + size + "."); + 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 + "."); - s_ProbeVolumeAtlasOctahedralDepthBuffer.SetData(dataOctahedralDepth); + 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); @@ -625,6 +658,9 @@ ProbeVolumeList PrepareVisibleProbeVolumeList(ScriptableRenderContext renderCont 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. diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/SphericalHarmonics.cs b/com.unity.render-pipelines.high-definition/Runtime/Lighting/SphericalHarmonics.cs index 8729a8dfcf6..3772c569b30 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/SphericalHarmonics.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/SphericalHarmonics.cs @@ -30,6 +30,60 @@ public static void GetCornetteShanksPhaseFunction(ZonalHarmonicsL2 zh, float ani } } + [Serializable] + [GenerateHLSL] + internal struct SphericalHarmonicsL0 + { + public Vector3 shrgb; + + public static readonly SphericalHarmonicsL0 zero = new SphericalHarmonicsL0 + { + shrgb = Vector3.zero + }; + + // These operators are implemented so that SphericalHarmonicsL0 matches API of SphericalHarmonicsL2. + public static SphericalHarmonicsL0 operator +(SphericalHarmonicsL0 lhs, SphericalHarmonicsL0 rhs) => new SphericalHarmonicsL0() + { + shrgb = lhs.shrgb + rhs.shrgb + }; + + public static SphericalHarmonicsL0 operator -(SphericalHarmonicsL0 lhs, SphericalHarmonicsL0 rhs) => new SphericalHarmonicsL0() + { + shrgb = lhs.shrgb - rhs.shrgb + }; + + public static SphericalHarmonicsL0 operator *(SphericalHarmonicsL0 lhs, float rhs) => new SphericalHarmonicsL0() + { + shrgb = lhs.shrgb * rhs + }; + + public static SphericalHarmonicsL0 operator /(SphericalHarmonicsL0 lhs, float rhs) => new SphericalHarmonicsL0() + { + shrgb = lhs.shrgb / rhs + }; + + public static bool operator ==(SphericalHarmonicsL0 lhs, SphericalHarmonicsL0 rhs) + { + return lhs.shrgb == rhs.shrgb; + } + + public static bool operator !=(SphericalHarmonicsL0 lhs, SphericalHarmonicsL0 rhs) + { + return !(lhs == rhs); + } + + public override bool Equals(object other) + { + if (!(other is SphericalHarmonicsL0)) return false; + return this == (SphericalHarmonicsL0) other; + } + + public override int GetHashCode() + { + return 17 * 23 + shrgb.GetHashCode(); + } + } + [Serializable] [GenerateHLSL] internal struct SphericalHarmonicsL1 @@ -38,13 +92,63 @@ internal struct SphericalHarmonicsL1 public Vector4 shAg; public Vector4 shAb; - public static SphericalHarmonicsL1 GetNeutralValues() + public static readonly SphericalHarmonicsL1 zero = new SphericalHarmonicsL1 { - SphericalHarmonicsL1 sh; - sh.shAr = Vector4.zero; - sh.shAg = Vector4.zero; - sh.shAb = Vector4.zero; - return sh; + shAr = Vector4.zero, + shAg = Vector4.zero, + shAb = Vector4.zero + }; + + // These operators are implemented so that SphericalHarmonicsL1 matches API of SphericalHarmonicsL2. + public static SphericalHarmonicsL1 operator +(SphericalHarmonicsL1 lhs, SphericalHarmonicsL1 rhs) => new SphericalHarmonicsL1() + { + shAr = lhs.shAr + rhs.shAr, + shAg = lhs.shAg + rhs.shAg, + shAb = lhs.shAb + rhs.shAb + }; + + public static SphericalHarmonicsL1 operator -(SphericalHarmonicsL1 lhs, SphericalHarmonicsL1 rhs) => new SphericalHarmonicsL1() + { + shAr = lhs.shAr - rhs.shAr, + shAg = lhs.shAg - rhs.shAg, + shAb = lhs.shAb - rhs.shAb + }; + + public static SphericalHarmonicsL1 operator *(SphericalHarmonicsL1 lhs, float rhs) => new SphericalHarmonicsL1() + { + shAr = lhs.shAr * rhs, + shAg = lhs.shAg * rhs, + shAb = lhs.shAb * rhs + }; + + public static SphericalHarmonicsL1 operator /(SphericalHarmonicsL1 lhs, float rhs) => new SphericalHarmonicsL1() + { + shAr = lhs.shAr / rhs, + shAg = lhs.shAg / rhs, + shAb = lhs.shAb / rhs + }; + + public static bool operator ==(SphericalHarmonicsL1 lhs, SphericalHarmonicsL1 rhs) + { + return lhs.shAr == rhs.shAr + && lhs.shAg == rhs.shAg + && lhs.shAb == rhs.shAb; + } + + public static bool operator !=(SphericalHarmonicsL1 lhs, SphericalHarmonicsL1 rhs) + { + return !(lhs == rhs); + } + + public override bool Equals(object other) + { + if (!(other is SphericalHarmonicsL1)) return false; + return this == (SphericalHarmonicsL1) other; + } + + public override int GetHashCode() + { + return ((17 * 23 + shAr.GetHashCode()) * 23 + shAg.GetHashCode()) * 23 + shAb.GetHashCode(); } } diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/SphericalHarmonics.cs.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Lighting/SphericalHarmonics.cs.hlsl index 9cefb24e29a..21449991a3b 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/SphericalHarmonics.cs.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/SphericalHarmonics.cs.hlsl @@ -4,29 +4,19 @@ #ifndef SPHERICALHARMONICS_CS_HLSL #define SPHERICALHARMONICS_CS_HLSL -// Generated from UnityEngine.Rendering.HighDefinition.SphericalHarmonicsL1 +// Generated from UnityEngine.Rendering.HighDefinition.SphericalHarmonicsL0 // PackingRules = Exact -struct SphericalHarmonicsL1 +struct SphericalHarmonicsL0 { - float4 shAr; - float4 shAg; - float4 shAb; + float3 shrgb; }; // -// Accessors for UnityEngine.Rendering.HighDefinition.SphericalHarmonicsL1 +// Accessors for UnityEngine.Rendering.HighDefinition.SphericalHarmonicsL0 // -float4 GetShAr(SphericalHarmonicsL1 value) +float3 GetShrgb(SphericalHarmonicsL0 value) { - return value.shAr; -} -float4 GetShAg(SphericalHarmonicsL1 value) -{ - return value.shAg; -} -float4 GetShAb(SphericalHarmonicsL1 value) -{ - return value.shAb; + return value.shrgb; } #endif From ade9534af43d5b0561bfd3c8986d5dadb7a7346f Mon Sep 17 00:00:00 2001 From: pastasfuture Date: Sun, 31 May 2020 14:43:38 -0700 Subject: [PATCH 04/26] Probe Volumes: SH0, SH1, and SH2 evaluation based on ShaderConfig fully working now. Lots of additional cleanup tasks as well. --- .../ProbeVolume/ProbeVolumeUI.Drawer.cs | 21 ++- .../ProbeVolume/ProbeVolumeUI.Skin.cs | 6 +- .../ProbeVolume/SerializedProbeVolume.cs | 2 + .../Runtime/Lighting/LightLoop/LightLoop.cs | 46 +++--- .../Runtime/Lighting/LightLoop/LightLoop.hlsl | 8 +- .../Lighting/ProbeVolume/ProbeVolume.cs | 75 ++++++++-- .../Lighting/ProbeVolume/ProbeVolume.hlsl | 139 ++++++++++++------ .../ProbeVolume/ProbeVolumeController.cs | 3 - .../ProbeVolume/ProbeVolumeLighting.cs | 92 +++++++----- .../ProbeVolume/ProbeVolumeLighting.cs.hlsl | 6 +- .../Lighting/SphericalHarmonics.cs.hlsl | 25 ++++ .../Runtime/Material/BuiltinGIUtilities.hlsl | 4 +- .../ShaderLibrary/ShaderVariablesGlobal.cs | 2 +- .../ShaderVariablesGlobal.cs.hlsl | 2 +- 14 files changed, 302 insertions(+), 129 deletions(-) diff --git a/com.unity.render-pipelines.high-definition/Editor/Lighting/ProbeVolume/ProbeVolumeUI.Drawer.cs b/com.unity.render-pipelines.high-definition/Editor/Lighting/ProbeVolume/ProbeVolumeUI.Drawer.cs index 93912f86c1d..275393b0cca 100644 --- a/com.unity.render-pipelines.high-definition/Editor/Lighting/ProbeVolume/ProbeVolumeUI.Drawer.cs +++ b/com.unity.render-pipelines.high-definition/Editor/Lighting/ProbeVolume/ProbeVolumeUI.Drawer.cs @@ -81,13 +81,19 @@ static void Drawer_FeatureEnableInfo(SerializedProbeVolume serialized, Editor ow static void Drawer_BakeToolBar(SerializedProbeVolume serialized, Editor owner) { + var asset = serialized.probeVolumeAsset.objectReferenceValue as ProbeVolumeAsset; + if (asset != null && asset.payload.encodingMode != ShaderConfig.s_ProbeVolumesEncodingMode) + { + EditorGUILayout.HelpBox(Styles.k_featureEncodingModeMismatchError, MessageType.Error); + } + EditorGUILayout.PropertyField(serialized.probeVolumeAsset, Styles.s_DataAssetLabel); EditorGUILayout.Slider(serialized.backfaceTolerance, 0.0f, 1.0f, Styles.s_BackfaceToleranceLabel); EditorGUILayout.PropertyField(serialized.dilationIterations, Styles.s_DilationIterationLabel); GUILayout.BeginHorizontal(); - if (GUILayout.Button("Bake Selected")) + if (GUILayout.Button(Styles.k_BakeSelectedText)) { ProbeVolumeManager.BakeSelected(); } @@ -266,7 +272,20 @@ static void Drawer_VolumeContent(SerializedProbeVolume serialized, Editor owner) EditorGUILayout.PropertyField(serialized.lightLayers); EditorGUILayout.PropertyField(serialized.volumeBlendMode, Styles.s_VolumeBlendModeLabel); EditorGUILayout.Slider(serialized.weight, 0.0f, 1.0f, Styles.s_WeightLabel); + { + EditorGUI.BeginChangeCheck(); + float normalBiasWS = EditorGUILayout.FloatField(Styles.s_NormalBiasWSLabel, serialized.normalBiasWS.floatValue); + if (EditorGUI.EndChangeCheck()) + { + serialized.normalBiasWS.floatValue = Mathf.Max(0, normalBiasWS); + } + } EditorGUILayout.PropertyField(serialized.debugColor, Styles.s_DebugColorLabel); + + if (serialized.volumeBlendMode.intValue != (int)VolumeBlendMode.Normal) + { + EditorGUILayout.HelpBox(Styles.k_featureAdditiveBlendingDisabledError, MessageType.Error); + } } } } diff --git a/com.unity.render-pipelines.high-definition/Editor/Lighting/ProbeVolume/ProbeVolumeUI.Skin.cs b/com.unity.render-pipelines.high-definition/Editor/Lighting/ProbeVolume/ProbeVolumeUI.Skin.cs index 64a75ecc673..19609bdd2e6 100644 --- a/com.unity.render-pipelines.high-definition/Editor/Lighting/ProbeVolume/ProbeVolumeUI.Skin.cs +++ b/com.unity.render-pipelines.high-definition/Editor/Lighting/ProbeVolume/ProbeVolumeUI.Skin.cs @@ -7,10 +7,13 @@ static partial class ProbeVolumeUI internal static class Styles { internal const string k_featureWarning = "Warning: Probe Volumes is a highly experimental feature.\nIt is disabled by default for this reason.\nIt's functionality is subject to breaking changes and whole sale removal.\nIt is not recommended for use outside of for providing feedback.\nIt should not be used in production."; - internal const string k_featureEnableInfo = "\nProbe Volumes feature is disabled. To enable, set:\nProbeVolumesEvaluationMode = ProbeVolumesEvaluationModes.MaterialPass\ninside of ShaderConfig.cs and inside of the editor run:\nEdit->Render Pipeline->Generate Shader Includes\nProbe Volumes feature must also be enabled inside of your HDRenderPipelineAsset."; + internal const string k_featureEnableInfo = "\nProbe Volumes feature is disabled. To enable, set:\nProbeVolumesEvaluationMode = ProbeVolumesEvaluationModes.MaterialPass\ninside of ShaderConfig.cs. Then inside of the editor run:\nEdit->Render Pipeline->Generate Shader Includes\nProbe Volumes feature must also be enabled inside of your HDRenderPipelineAsset."; + internal const string k_featureAdditiveBlendingDisabledError = "Error: ProbeVolumesAdditiveBlending feature is disabled inside of ShaderConfig.cs.\nThis probe volume will not be rendered.\nTo enable, set:\nProbeVolumesAdditiveBlending = 1\ninside of ShaderConfig.cs. Then inside of the editor run:\nEdit->Render Pipeline->Generate Shader Includes."; + internal const string k_featureEncodingModeMismatchError = "Error: ProbeVolumesEncodingMode inside of ShaderConfig.cs is configured to a different mode than this probe volume was previously baked with.\nThis probe volume will not be rendered.\nTo correct this:\nA) Rebake this probe volume.\nor\nB) Change ProbeVolumesEncodingMode inside of ShaderConfig.cs. Then inside of the editor run:\nEdit->Render Pipeline->Generate Shader Includes."; internal const string k_VolumeHeader = "Volume"; internal const string k_ProbesHeader = "Probes"; internal const string k_BakingHeader = "Baking"; + internal const string k_BakeSelectedText = "Bake Selected"; internal static readonly GUIContent[] s_Toolbar_Contents = new GUIContent[] { @@ -38,6 +41,7 @@ internal static class Styles internal static readonly GUIContent s_VolumeBlendModeLabel = new GUIContent("Volume Blend Mode", "A blending mode for the entire volume when overlapping others."); internal static readonly GUIContent s_WeightLabel = new GUIContent("Weight", "Weigh the probe contribution for the entire volume."); + internal static readonly GUIContent s_NormalBiasWSLabel = new GUIContent("Normal Bias", "Controls the distance in world space units to bias along the surface normal to mitigate light leaking and self-shadowing artifacts.\nA value of 0.0 is physically accurate, but can result in self shadowing artifacts on surfaces that contribute to GI.\nIncrease value to mitigate self shadowing artifacts.\nSignificantly large values can have performance implications, as normal bias will dilate a probe volumes bounding box, causing it to be sampled in additional neighboring tiles / clusters."); internal static readonly GUIContent s_BackfaceToleranceLabel = new GUIContent("Backface Tolerance", "The percentage of backfaces sampled per probe is acceptable before probe will receive dilated data."); internal static readonly GUIContent s_DilationIterationLabel = new GUIContent("Dilation Iterations", "The number of iterations Dilation copies over data from each probe to its neighbors."); diff --git a/com.unity.render-pipelines.high-definition/Editor/Lighting/ProbeVolume/SerializedProbeVolume.cs b/com.unity.render-pipelines.high-definition/Editor/Lighting/ProbeVolume/SerializedProbeVolume.cs index b596aedacf2..75e0735da2b 100644 --- a/com.unity.render-pipelines.high-definition/Editor/Lighting/ProbeVolume/SerializedProbeVolume.cs +++ b/com.unity.render-pipelines.high-definition/Editor/Lighting/ProbeVolume/SerializedProbeVolume.cs @@ -19,6 +19,7 @@ class SerializedProbeVolume internal SerializedProperty volumeBlendMode; internal SerializedProperty weight; + internal SerializedProperty normalBiasWS; internal SerializedProperty size; @@ -59,6 +60,7 @@ internal SerializedProbeVolume(SerializedObject serializedObject) volumeBlendMode = probeVolumeParams.FindPropertyRelative("volumeBlendMode"); weight = probeVolumeParams.FindPropertyRelative("weight"); + normalBiasWS = probeVolumeParams.FindPropertyRelative("normalBiasWS"); size = probeVolumeParams.FindPropertyRelative("size"); diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs index ccfa707d79d..e69714a4113 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs @@ -1976,10 +1976,10 @@ void GetEnvLightVolumeDataAndBound(HDProbe probe, LightVolumeType lightVolumeTyp m_lightList.lightsPerView[viewIndex].lightVolumes.Add(lightVolumeData); } - void AddBoxVolumeDataAndBound(OrientedBBox obb, LightCategory category, LightFeatureFlags featureFlags, Matrix4x4 worldToView, int viewIndex, bool isProbeVolume = false, float normalBiasDilation = 0.0f) + void CreateBoxVolumeDataAndBound(out LightVolumeData volumeData, out SFiniteLightBound bound, OrientedBBox obb, LightCategory category, LightFeatureFlags featureFlags, Matrix4x4 worldToView, float normalBiasDilation = 0.0f) { - var bound = new SFiniteLightBound(); - var volumeData = new LightVolumeData(); + volumeData = new LightVolumeData(); + bound = new SFiniteLightBound(); // Used in Probe Volumes: // Conservatively dilate bounds used for tile / cluster assignment by normal bias. @@ -2016,19 +2016,6 @@ void AddBoxVolumeDataAndBound(OrientedBBox obb, LightCategory category, LightFea volumeData.lightAxisZ = forwardVS; volumeData.boxInnerDist = extents - k_BoxCullingExtentThreshold; // We have no blend range, but the culling code needs a small EPS value for some reason??? volumeData.boxInvRange.Set(1.0f / k_BoxCullingExtentThreshold.x, 1.0f / k_BoxCullingExtentThreshold.y, 1.0f / k_BoxCullingExtentThreshold.z); - - if (isProbeVolume && (ShaderConfig.s_ProbeVolumesEvaluationMode == ProbeVolumesEvaluationModes.MaterialPass)) - { - // Only probe volume evaluation in the material pass use these custom probe volume specific lists. - // Probe volumes evaluated in the light loop, as well as other volume data such as Decals get folded into the standard list data. - m_lightList.lightsPerView[viewIndex].probeVolumesBounds.Add(bound); - m_lightList.lightsPerView[viewIndex].probeVolumesLightVolumes.Add(volumeData); - } - else - { - m_lightList.lightsPerView[viewIndex].bounds.Add(bound); - m_lightList.lightsPerView[viewIndex].lightVolumes.Add(volumeData); - } } internal int GetCurrentShadowCount() @@ -2674,13 +2661,11 @@ bool PrepareLightsForGPU(CommandBuffer cmd, HDCamera hdCamera, CullingResults cu m_DensityVolumeCount = densityVolumes.bounds != null ? densityVolumes.bounds.Count : 0; m_ProbeVolumeCount = probeVolumes.bounds != null ? probeVolumes.bounds.Count : 0; - float probeVolumeNormalBiasWS = 0.0f; + bool probeVolumeNormalBiasEnabled = false; if (ShaderConfig.s_ProbeVolumesEvaluationMode != ProbeVolumesEvaluationModes.Disabled) { var settings = hdCamera.volumeStack.GetComponent(); - probeVolumeNormalBiasWS = (settings == null || (settings.leakMitigationMode.value != LeakMitigationMode.NormalBias && settings.leakMitigationMode.value != LeakMitigationMode.OctahedralDepthOcclusionFilter)) - ? 0.0f - : settings.normalBiasWS.value; + probeVolumeNormalBiasEnabled = !(settings == null || (settings.leakMitigationMode.value != LeakMitigationMode.NormalBias && settings.leakMitigationMode.value != LeakMitigationMode.OctahedralDepthOcclusionFilter)); } for (int viewIndex = 0; viewIndex < hdCamera.viewCount; ++viewIndex) @@ -2697,15 +2682,30 @@ bool PrepareLightsForGPU(CommandBuffer cmd, HDCamera hdCamera, CullingResults cu { // Density volumes are not lights and therefore should not affect light classification. LightFeatureFlags featureFlags = 0; - AddBoxVolumeDataAndBound(densityVolumes.bounds[i], LightCategory.DensityVolume, featureFlags, worldToViewCR, viewIndex); + CreateBoxVolumeDataAndBound(out LightVolumeData volumeData, out SFiniteLightBound bound, densityVolumes.bounds[i], LightCategory.DensityVolume, featureFlags, worldToViewCR); + m_lightList.lightsPerView[viewIndex].lightVolumes.Add(volumeData); + m_lightList.lightsPerView[viewIndex].bounds.Add(bound); } - for (int i = 0, n = m_ProbeVolumeCount; i < n; i++) { // Probe volumes are not lights and therefore should not affect light classification. LightFeatureFlags featureFlags = 0; - AddBoxVolumeDataAndBound(probeVolumes.bounds[i], LightCategory.ProbeVolume, featureFlags, worldToViewCR, viewIndex, isProbeVolume: true, probeVolumeNormalBiasWS); + float probeVolumeNormalBiasWS = probeVolumeNormalBiasEnabled ? probeVolumes.data[i].normalBiasWS : 0.0f; + CreateBoxVolumeDataAndBound(out LightVolumeData volumeData, out SFiniteLightBound bound, probeVolumes.bounds[i], LightCategory.ProbeVolume, featureFlags, worldToViewCR, probeVolumeNormalBiasWS); + if (ShaderConfig.s_ProbeVolumesEvaluationMode == ProbeVolumesEvaluationModes.MaterialPass) + { + // Only probe volume evaluation in the material pass use these custom probe volume specific lists. + // Probe volumes evaluated in the light loop, as well as other volume data such as Decals get folded into the standard list data. + m_lightList.lightsPerView[viewIndex].probeVolumesLightVolumes.Add(volumeData); + m_lightList.lightsPerView[viewIndex].probeVolumesBounds.Add(bound); + } + else + { + + m_lightList.lightsPerView[viewIndex].lightVolumes.Add(volumeData); + m_lightList.lightsPerView[viewIndex].bounds.Add(bound); + } } } diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.hlsl index f866c603226..dde5ace4607 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.hlsl @@ -497,11 +497,11 @@ void LightLoop( float3 V, PositionInputs posInput, PreLightData preLightData, BS float probeVolumeHierarchyWeightBackFace = uninitialized ? 0.0f : 1.0f; // Note: we aren't suppose to access normalWS in lightloop, but bsdfData.normalWS is always define for any material. So this is safe. - builtinDataProbeVolumes.bakeDiffuseLighting = EvaluateProbeVolumes(probeVolumeHierarchyWeightFrontFace, posInput, bsdfData.normalWS, builtinData.renderingLayers); - builtinDataProbeVolumes.backBakeDiffuseLighting = EvaluateProbeVolumes(probeVolumeHierarchyWeightBackFace, posInput, -bsdfData.normalWS, builtinData.renderingLayers); + builtinDataProbeVolumes.bakeDiffuseLighting = EvaluateProbeVolumes(posInput, bsdfData.normalWS, builtinData.renderingLayers, probeVolumeHierarchyWeightFrontFace); + builtinDataProbeVolumes.backBakeDiffuseLighting = EvaluateProbeVolumes(posInput, -bsdfData.normalWS, builtinData.renderingLayers, probeVolumeHierarchyWeightBackFace); - builtinDataProbeVolumes.bakeDiffuseLighting += EvaluateProbeVolumeAmbientProbeFallback(probeVolumeHierarchyWeightFrontFace, bsdfData.normalWS); - builtinDataProbeVolumes.backBakeDiffuseLighting += EvaluateProbeVolumeAmbientProbeFallback(probeVolumeHierarchyWeightBackFace, -bsdfData.normalWS); + builtinDataProbeVolumes.bakeDiffuseLighting += EvaluateProbeVolumeAmbientProbeFallback(bsdfData.normalWS, probeVolumeHierarchyWeightFrontFace); + builtinDataProbeVolumes.backBakeDiffuseLighting += EvaluateProbeVolumeAmbientProbeFallback(-bsdfData.normalWS, probeVolumeHierarchyWeightBackFace); // TODO: clean this case later to share more code, for now just reproduce the same behavior that is happening in PostInitBuiltinData() diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.cs b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.cs index edc9734d96e..d975c64c63e 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.cs @@ -327,6 +327,7 @@ internal struct ProbeVolumeArtistParameters public VolumeBlendMode volumeBlendMode; public float weight; + public float normalBiasWS; public float backfaceTolerance; public int dilationIterations; @@ -395,6 +396,7 @@ public ProbeVolumeArtistParameters(Color debugColor) this.densityZ = (float)this.resolutionZ / this.size.z; this.volumeBlendMode = VolumeBlendMode.Normal; this.weight = 1; + this.normalBiasWS = 0.0f; this.dilationIterations = 2; this.backfaceTolerance = 0.25f; this.lightLayers = LightLayerEnum.LightLayerDefault; @@ -446,6 +448,7 @@ internal ProbeVolumeEngineData ConvertToEngineData() ProbeVolumeEngineData data = new ProbeVolumeEngineData(); data.weight = this.weight; + data.normalBiasWS = this.normalBiasWS; data.debugColor.x = this.debugColor.r; data.debugColor.y = this.debugColor.g; @@ -478,8 +481,6 @@ internal ProbeVolumeEngineData ConvertToEngineData() data.lightLayers = (uint)this.lightLayers; - data.unused = 0.0f; - return data; } @@ -644,15 +645,28 @@ protected void OnDisable() } internal bool IsAssetCompatible() + { + return IsAssetCompatibleResolution() && IsAssetCompatibleEncodingMode(); + } + + internal bool IsAssetCompatibleResolution() { if (probeVolumeAsset) { return parameters.resolutionX == probeVolumeAsset.resolutionX && parameters.resolutionY == probeVolumeAsset.resolutionY && - parameters.resolutionZ == probeVolumeAsset.resolutionZ && - probeVolumeAsset.payload.encodingMode == ShaderConfig.s_ProbeVolumesEncodingMode; // TODO: Create runtime transforms between different encoding types to avoid having to rebake. + parameters.resolutionZ == probeVolumeAsset.resolutionZ; } + return false; + } + internal bool IsAssetCompatibleEncodingMode() + { + if (probeVolumeAsset) + { + // TODO: Create runtime transforms between different encoding types to avoid having to rebake. + return probeVolumeAsset.payload.encodingMode == ShaderConfig.s_ProbeVolumesEncodingMode; + } return false; } @@ -688,13 +702,19 @@ protected void OnValidate() if (probeVolumeAsset) { - if (!IsAssetCompatible()) + if (!IsAssetCompatibleResolution()) { Debug.LogWarningFormat("The asset \"{0}\" assigned to Probe Volume \"{1}\" does not have matching data dimensions ({2}x{3}x{4} vs. {5}x{6}x{7}), please rebake.", probeVolumeAsset.name, this.name, probeVolumeAsset.resolutionX, probeVolumeAsset.resolutionY, probeVolumeAsset.resolutionZ, parameters.resolutionX, parameters.resolutionY, parameters.resolutionZ); } + else if (!IsAssetCompatibleEncodingMode()) + { + Debug.LogWarningFormat("The asset \"{0}\" assigned to Probe Volume \"{1}\" does not have matching encoding mode ({2} vs. {3}), please rebake.", + probeVolumeAsset.name, this.name, + probeVolumeAsset.payload.encodingMode, ShaderConfig.s_ProbeVolumesEncodingMode); + } dataUpdated = true; } @@ -770,11 +790,17 @@ internal void OnProbesBakeCompleted() { for (int i = 0, iLen = sh.Length; i < iLen; ++i) { + // https://www.ppsloan.org/publications/StupidSH36.pdf + // The shader and, by extension, SetSHConstants expect the values to be normalized. + // The data in the SH probe sample passed here is expected to already be normalized + // with kNormalizationConstants. + // + // Constant + Linear SphericalHarmonicsL1 sh1 = new SphericalHarmonicsL1 { - shAr = new Vector4(sh[i][0, 3], sh[i][0, 1], sh[i][0, 2], sh[i][0, 0] - sh[i][0, 6]), - shAg = new Vector4(sh[i][1, 3], sh[i][1, 1], sh[i][1, 2], sh[i][1, 0] - sh[i][1, 6]), - shAb = new Vector4(sh[i][2, 3], sh[i][2, 1], sh[i][2, 2], sh[i][2, 0] - sh[i][2, 6]) + shAr = new Vector4(sh[i][0, 3], sh[i][0, 1], sh[i][0, 2], sh[i][0, 0]), + shAg = new Vector4(sh[i][1, 3], sh[i][1, 1], sh[i][1, 2], sh[i][1, 0]), + shAb = new Vector4(sh[i][2, 3], sh[i][2, 1], sh[i][2, 2], sh[i][2, 0]) }; ProbeVolumePayload.SetSphericalHarmonicsL1FromIndex(ref probeVolumeAsset.payload, ref sh1, i); @@ -786,8 +812,35 @@ internal void OnProbesBakeCompleted() { for (int i = 0, iLen = sh.Length; i < iLen; ++i) { - SphericalHarmonicsL2 sh2 = sh[i]; - ProbeVolumePayload.SetSphericalHarmonicsL2FromIndex(ref probeVolumeAsset.payload, ref sh2, i); + // https://www.ppsloan.org/publications/StupidSH36.pdf + // The shader and, by extension, SetSHConstants expect the values to be normalized. + // The data in the SH probe sample passed here is expected to already be normalized + // with kNormalizationConstants. + // + // Constant + Linear + for (int iC = 0; iC < 3; iC++) + { + // In the shader we multiply the normal is not swizzled, so it's normal.xyz. + // Swizzle the coefficients to be in { x, y, z, DC } order. + probeVolumeAsset.payload.dataSH[i * shStride + iC * 4 + 0] = sh[i][iC, 3]; + probeVolumeAsset.payload.dataSH[i * shStride + iC * 4 + 1] = sh[i][iC, 1]; + probeVolumeAsset.payload.dataSH[i * shStride + iC * 4 + 2] = sh[i][iC, 2]; + probeVolumeAsset.payload.dataSH[i * shStride + iC * 4 + 3] = sh[i][iC, 0] - sh[i][iC, 6]; + } + + // Quadratic polynomials + for (int iC = 0; iC < 3; iC++) + { + probeVolumeAsset.payload.dataSH[i * shStride + (iC + 3) * 4 + 0] = sh[i][iC, 4]; + probeVolumeAsset.payload.dataSH[i * shStride + (iC + 3) * 4 + 1] = sh[i][iC, 5]; + probeVolumeAsset.payload.dataSH[i * shStride + (iC + 3) * 4 + 2] = sh[i][iC, 6] * 3.0f; + probeVolumeAsset.payload.dataSH[i * shStride + (iC + 3) * 4 + 3] = sh[i][iC, 7]; + } + + // Final quadratic polynomial + probeVolumeAsset.payload.dataSH[i * shStride + 6 * 4 + 0] = sh[i][0, 8]; + probeVolumeAsset.payload.dataSH[i * shStride + 6 * 4 + 1] = sh[i][1, 8]; + probeVolumeAsset.payload.dataSH[i * shStride + 6 * 4 + 2] = sh[i][2, 8]; } break; } @@ -842,7 +895,7 @@ private static ProbeVolumeSettingsKey ComputeProbeVolumeSettingsKeyFromProbeVolu resolutionX = probeVolume.parameters.resolutionX, resolutionY = probeVolume.parameters.resolutionY, resolutionZ = probeVolume.parameters.resolutionZ, - encodingMode = probeVolume.probeVolumeAsset.payload.encodingMode, + encodingMode = probeVolume.probeVolumeAsset ? probeVolume.probeVolumeAsset.payload.encodingMode : (ProbeVolumesEncodingModes)0, backfaceTolerance = probeVolume.parameters.backfaceTolerance, dilationIterations = probeVolume.parameters.dilationIterations }; diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl index 94d354680a5..03b32850235 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl @@ -1,6 +1,7 @@ #ifndef __PROBEVOLUME_HLSL__ #define __PROBEVOLUME_HLSL__ +#include "Packages/com.unity.render-pipelines.high-definition-config/Runtime/ShaderConfig.cs.hlsl" #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Packing.hlsl" #include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.cs.hlsl" #include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeLightLoopDef.hlsl" @@ -22,6 +23,32 @@ float ProbeVolumeComputeFadeFactor( return dstF * fade; } +float ProbeVolumeSampleValidity(float3 probeVolumeAtlasUVW) +{ +#if SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L0 + return SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 0), 0).w; +#elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L1 + return SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 3), 0).x; +#elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L2 + return SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 6), 0).w; +#else + return 0.0; +#endif +} + +float ProbeVolumeLoadValidity(int3 probeVolumeAtlasTexelCoord) +{ +#if SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L0 + return LOAD_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, int3(probeVolumeAtlasTexelCoord.x, probeVolumeAtlasTexelCoord.y, probeVolumeAtlasTexelCoord.z + _ProbeVolumeAtlasResolutionAndSliceCount.z * 0), 0).w; +#elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L1 + return LOAD_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, int3(probeVolumeAtlasTexelCoord.x, probeVolumeAtlasTexelCoord.y, probeVolumeAtlasTexelCoord.z + _ProbeVolumeAtlasResolutionAndSliceCount.z * 3), 0).x; +#elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L2 + return LOAD_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, int3(probeVolumeAtlasTexelCoord.x, probeVolumeAtlasTexelCoord.y, probeVolumeAtlasTexelCoord.z + _ProbeVolumeAtlasResolutionAndSliceCount.z * 6), 0).w; +#else + return 0.0; +#endif +} + void EvaluateProbeVolumeOctahedralDepthOcclusionFilterWeights( out float weights[8], float3 probeVolumeTexel3DMin, @@ -125,10 +152,10 @@ void EvaluateProbeVolumeOctahedralDepthOcclusionFilterWeights( } } -float3 EvaluateProbeVolumes(inout float probeVolumeHierarchyWeight, PositionInputs posInput, float3 normalWS, uint renderingLayers) +float3 EvaluateProbeVolumes(PositionInputs posInput, float3 normalWS, uint renderingLayers, inout float weightHierarchy) { #if !SHADEROPTIONS_PROBE_VOLUMES_ADDITIVE_BLENDING - if (probeVolumeHierarchyWeight >= 1.0) { return float3(0.0, 0.0, 0.0); } + if (weightHierarchy >= 1.0) { return float3(0.0, 0.0, 0.0); } #endif float3 probeVolumeDiffuseLighting = float3(0.0, 0.0, 0.0); @@ -208,10 +235,10 @@ float3 EvaluateProbeVolumes(inout float probeVolumeHierarchyWeight, PositionInpu // Probe volumes are sorted primarily by blend mode, and secondarily by size. // 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 if our probeVolumeHierarchyWeight reaches 1.0, since we know we will only ever process more VOLUMEBLENDMODE_NORMAL volumes, + // This allows us to early out if our weightHierarchy reaches 1.0, since we know we will only ever process more VOLUMEBLENDMODE_NORMAL volumes, // whos weight will always evaluate to zero. #if defined(PLATFORM_SUPPORTS_WAVE_INTRINSICS) - if (WaveActiveMin(probeVolumeHierarchyWeight) >= 1.0 + if (WaveActiveMin(weightHierarchy) >= 1.0 #if SHADEROPTIONS_PROBE_VOLUMES_ADDITIVE_BLENDING && WaveActiveAllTrue(s_probeVolumeData.volumeBlendMode == VOLUMEBLENDMODE_NORMAL) #endif @@ -234,20 +261,26 @@ float3 EvaluateProbeVolumes(inout float probeVolumeHierarchyWeight, PositionInpu const bool isWeightAccumulated = true; #endif - if (probeVolumeHierarchyWeight >= 1.0 && isWeightAccumulated) { continue; } + if (weightHierarchy >= 1.0 && isWeightAccumulated) { continue; } if (!IsMatchingLightLayer(s_probeVolumeData.lightLayers, renderingLayers)) { continue; } - float weight = 0.0; - float4 sampleShAr = 0.0; - float4 sampleShAg = 0.0; - float4 sampleShAb = 0.0; + float weightCurrent = 0.0; + +#if SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L0 + const uint SH_STRIDE = 3u; +#elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L1 + const uint SH_STRIDE = 12u; +#elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L2 + const uint SH_STRIDE = 27u; +#endif + float4 sampleDataSH[(SH_STRIDE + 3u) / 4u]; { float3x3 obbFrame = float3x3(s_probeVolumeBounds.right, s_probeVolumeBounds.up, cross(s_probeVolumeBounds.right, s_probeVolumeBounds.up)); float3 obbExtents = float3(s_probeVolumeBounds.extentX, s_probeVolumeBounds.extentY, s_probeVolumeBounds.extentZ); // Note: When normal bias is > 0, bounds using in tile / cluster assignment are conservatively dilated CPU side to handle worst case normal bias. - float3 samplePositionWS = normalWS * _ProbeVolumeNormalBiasWS + positionRWS; + float3 samplePositionWS = normalWS * s_probeVolumeData.normalBiasWS + positionRWS; float3 samplePositionBS = mul(obbFrame, samplePositionWS - s_probeVolumeBounds.center); float3 samplePositionBCS = samplePositionBS * rcp(obbExtents); @@ -266,14 +299,14 @@ float3 EvaluateProbeVolumes(inout float probeVolumeHierarchyWeight, PositionInpu #if SHADEROPTIONS_PROBE_VOLUMES_ADDITIVE_BLENDING if (s_probeVolumeData.volumeBlendMode == VOLUMEBLENDMODE_ADDITIVE) - weight = fadeFactor; + weightCurrent = fadeFactor; else if (s_probeVolumeData.volumeBlendMode == VOLUMEBLENDMODE_SUBTRACTIVE) - weight = -fadeFactor; + weightCurrent = -fadeFactor; else #endif { - // Alpha composite: weight = (1.0f - probeVolumeHierarchyWeight) * fadeFactor; - weight = probeVolumeHierarchyWeight * -fadeFactor + fadeFactor; + // Alpha composite: weight = (1.0f - weightHierarchy) * fadeFactor; + weightCurrent = weightHierarchy * -fadeFactor + fadeFactor; } // TODO: Cleanup / optimize this math. @@ -312,15 +345,15 @@ float3 EvaluateProbeVolumes(inout float probeVolumeHierarchyWeight, PositionInpu // This would allow us to use a single channel format, rather than wasting memory with float4(validity, unused, unused, unused). // It would also allow us to use a different texture format (i.e: 1x8bpp rather than 4x16bpp). // Currently just using a texture slice for convenience, and with the idea that MAYBE we will end up using the remaining 3 channels. - probeWeightBSW = max(_ProbeVolumeBilateralFilterWeightMin, LOAD_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, int3(probeVolumeTexel3DMin.x + 0, probeVolumeTexel3DMin.y + 0, probeVolumeTexel3DMin.z + 0 + _ProbeVolumeAtlasResolutionAndSliceCount.z * 3), 0).x); - probeWeightBSE = max(_ProbeVolumeBilateralFilterWeightMin, LOAD_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, int3(probeVolumeTexel3DMin.x + 1, probeVolumeTexel3DMin.y + 0, probeVolumeTexel3DMin.z + 0 + _ProbeVolumeAtlasResolutionAndSliceCount.z * 3), 0).x); - probeWeightBNW = max(_ProbeVolumeBilateralFilterWeightMin, LOAD_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, int3(probeVolumeTexel3DMin.x + 0, probeVolumeTexel3DMin.y + 0, probeVolumeTexel3DMin.z + 1 + _ProbeVolumeAtlasResolutionAndSliceCount.z * 3), 0).x); - probeWeightBNE = max(_ProbeVolumeBilateralFilterWeightMin, LOAD_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, int3(probeVolumeTexel3DMin.x + 1, probeVolumeTexel3DMin.y + 0, probeVolumeTexel3DMin.z + 1 + _ProbeVolumeAtlasResolutionAndSliceCount.z * 3), 0).x); - - probeWeightTSW = max(_ProbeVolumeBilateralFilterWeightMin, LOAD_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, int3(probeVolumeTexel3DMin.x + 0, probeVolumeTexel3DMin.y + 1, probeVolumeTexel3DMin.z + 0 + _ProbeVolumeAtlasResolutionAndSliceCount.z * 3), 0).x); - probeWeightTSE = max(_ProbeVolumeBilateralFilterWeightMin, LOAD_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, int3(probeVolumeTexel3DMin.x + 1, probeVolumeTexel3DMin.y + 1, probeVolumeTexel3DMin.z + 1 + _ProbeVolumeAtlasResolutionAndSliceCount.z * 3), 0).x); - probeWeightTNW = max(_ProbeVolumeBilateralFilterWeightMin, LOAD_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, int3(probeVolumeTexel3DMin.x + 0, probeVolumeTexel3DMin.y + 1, probeVolumeTexel3DMin.z + 1 + _ProbeVolumeAtlasResolutionAndSliceCount.z * 3), 0).x); - probeWeightTNE = max(_ProbeVolumeBilateralFilterWeightMin, LOAD_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, int3(probeVolumeTexel3DMin.x + 1, probeVolumeTexel3DMin.y + 1, probeVolumeTexel3DMin.z + 1 + _ProbeVolumeAtlasResolutionAndSliceCount.z * 3), 0).x); + probeWeightBSW = max(_ProbeVolumeBilateralFilterWeightMin, ProbeVolumeLoadValidity(int3(probeVolumeTexel3DMin.x + 0, probeVolumeTexel3DMin.y + 0, probeVolumeTexel3DMin.z + 0))); + probeWeightBSE = max(_ProbeVolumeBilateralFilterWeightMin, ProbeVolumeLoadValidity(int3(probeVolumeTexel3DMin.x + 1, probeVolumeTexel3DMin.y + 0, probeVolumeTexel3DMin.z + 0))); + probeWeightBNW = max(_ProbeVolumeBilateralFilterWeightMin, ProbeVolumeLoadValidity(int3(probeVolumeTexel3DMin.x + 0, probeVolumeTexel3DMin.y + 0, probeVolumeTexel3DMin.z + 1))); + probeWeightBNE = max(_ProbeVolumeBilateralFilterWeightMin, ProbeVolumeLoadValidity(int3(probeVolumeTexel3DMin.x + 1, probeVolumeTexel3DMin.y + 0, probeVolumeTexel3DMin.z + 1))); + + probeWeightTSW = max(_ProbeVolumeBilateralFilterWeightMin, ProbeVolumeLoadValidity(int3(probeVolumeTexel3DMin.x + 0, probeVolumeTexel3DMin.y + 1, probeVolumeTexel3DMin.z + 0))); + probeWeightTSE = max(_ProbeVolumeBilateralFilterWeightMin, ProbeVolumeLoadValidity(int3(probeVolumeTexel3DMin.x + 1, probeVolumeTexel3DMin.y + 1, probeVolumeTexel3DMin.z + 1))); + probeWeightTNW = max(_ProbeVolumeBilateralFilterWeightMin, ProbeVolumeLoadValidity(int3(probeVolumeTexel3DMin.x + 0, probeVolumeTexel3DMin.y + 1, probeVolumeTexel3DMin.z + 1))); + probeWeightTNE = max(_ProbeVolumeBilateralFilterWeightMin, ProbeVolumeLoadValidity(int3(probeVolumeTexel3DMin.x + 1, probeVolumeTexel3DMin.y + 1, probeVolumeTexel3DMin.z + 1))); } else if (_ProbeVolumeLeakMitigationMode == LEAKMITIGATIONMODE_OCTAHEDRAL_DEPTH_OCCLUSION_FILTER) { @@ -433,43 +466,64 @@ float3 EvaluateProbeVolumes(inout float probeVolumeHierarchyWeight, PositionInpu #ifdef DEBUG_DISPLAY if (_DebugProbeVolumeMode == PROBEVOLUMEDEBUGMODE_VISUALIZE_VALIDITY) { - float validity = SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 3), 0).x; + float validity = ProbeVolumeSampleValidity(probeVolumeAtlasUVW); // Pack validity into SH data so that we can access it later for our debug mode. - sampleShAr = float4(validity, 0.0, 0.0, 0.0); - sampleShAg = 0.0; - sampleShAb = 0.0; + sampleDataSH[0] = float4(validity, 0.0, 0.0, 0.0); } else #endif { - sampleShAr = SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 0), 0); - sampleShAg = SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 1), 0); - sampleShAb = SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 2), 0); +#if SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L0 + sampleDataSH[0] = SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 0), 0); + +#elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L1 + sampleDataSH[0] = SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 0), 0); + sampleDataSH[1] = SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 1), 0); + sampleDataSH[2] = SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 2), 0); + +#elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L2 + sampleDataSH[0] = SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 0), 0); + sampleDataSH[1] = SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 1), 0); + sampleDataSH[2] = SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 2), 0); + sampleDataSH[3] = SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 3), 0); + sampleDataSH[4] = SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 4), 0); + sampleDataSH[5] = SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 5), 0); + sampleDataSH[6] = SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 6), 0); + +#endif } } // When probe volumes are evaluated in the material pass, BSDF modulation is applied as a post operation, outside of this function. - float3 sampleOutgoingRadiance = SHEvalLinearL0L1(normalWS, sampleShAr, sampleShAg, sampleShAb); +#if SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L0 + float3 sampleOutgoingRadiance = sampleDataSH[0].rgb; + +#elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L1 + float3 sampleOutgoingRadiance = SHEvalLinearL0L1(normalWS, sampleDataSH[0], sampleDataSH[1], sampleDataSH[2]); + +#elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L2 + float3 sampleOutgoingRadiance = SampleSH9(sampleDataSH, normalWS); +#endif #ifdef DEBUG_DISPLAY if (_DebugProbeVolumeMode == PROBEVOLUMEDEBUGMODE_VISUALIZE_DEBUG_COLORS) { - probeVolumeDiffuseLighting += s_probeVolumeData.debugColor * weight; + probeVolumeDiffuseLighting += s_probeVolumeData.debugColor * weightCurrent; } else if (_DebugProbeVolumeMode == PROBEVOLUMEDEBUGMODE_VISUALIZE_VALIDITY) { - float validity = sampleShAr.x; - probeVolumeDiffuseLighting += lerp(float3(1, 0, 0), float3(0, 1, 0), validity) * weight; + float validity = sampleDataSH[0].x; + probeVolumeDiffuseLighting += lerp(float3(1, 0, 0), float3(0, 1, 0), validity) * weightCurrent; } else #endif { - probeVolumeDiffuseLighting += sampleOutgoingRadiance * weight; + probeVolumeDiffuseLighting += sampleOutgoingRadiance * weightCurrent; } if (isWeightAccumulated) - probeVolumeHierarchyWeight += weight; + weightHierarchy += weightCurrent; } } @@ -479,20 +533,21 @@ float3 EvaluateProbeVolumes(inout float probeVolumeHierarchyWeight, PositionInpu } // Fallback to global ambient probe lighting when probe volume lighting weight is not fully saturated. -void EvaluateProbeVolumeAmbientProbeFallback(float3 normalWS, float3 backNormalWS, inout float3 bakeDiffuseLighting, inout float3 backBakeDiffuseLighting, inout float probeVolumeHierarchyWeight) +float3 EvaluateProbeVolumeAmbientProbeFallback(float3 normalWS, inout float weightHierarchy) { - if (probeVolumeHierarchyWeight < 1.0 + float3 sampleAmbientProbeOutgoingRadiance = float3(0.0, 0.0, 0.0); + if (weightHierarchy < 1.0 #ifdef DEBUG_DISPLAY && (_DebugProbeVolumeMode != PROBEVOLUMEDEBUGMODE_VISUALIZE_DEBUG_COLORS) && (_DebugProbeVolumeMode != PROBEVOLUMEDEBUGMODE_VISUALIZE_VALIDITY) #endif ) { - float fallbackWeight = 1.0 - probeVolumeHierarchyWeight; - bakeDiffuseLighting += SampleSH9(_ProbeVolumeAmbientProbeFallbackPackedCoeffs, normalWS) * fallbackWeight; - backBakeDiffuseLighting += SampleSH9(_ProbeVolumeAmbientProbeFallbackPackedCoeffs, backNormalWS) * fallbackWeight; - probeVolumeHierarchyWeight = 1.0; + sampleAmbientProbeOutgoingRadiance = SampleSH9(_ProbeVolumeAmbientProbeFallbackPackedCoeffs, normalWS) * (1.0 - weightHierarchy); + weightHierarchy = 1.0; } + + return sampleAmbientProbeOutgoingRadiance; } #endif // __PROBEVOLUME_HLSL__ diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeController.cs b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeController.cs index 7de47cb4545..9baaa887c2c 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeController.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeController.cs @@ -17,9 +17,6 @@ public LeakMitigationModeParameter(LeakMitigationMode value, bool overrideState [SerializeField, Tooltip("Selects the heuristic used for mitigating light leaking and self-shadowing artifacts when sampling from the probe volumes.")] internal LeakMitigationModeParameter leakMitigationMode = new LeakMitigationModeParameter(LeakMitigationMode.NormalBias); - [SerializeField, Tooltip("Controls the distance in world space to bias along the surface normal to mitigate light leaking self-shadow artifacts.")] - internal MinFloatParameter normalBiasWS = new MinFloatParameter(0.0f, 0.0f); - [SerializeField, Tooltip("Controls the strength of our bilateral filter. 0.0 falls back to trilinear filtering. 1.0 is maximum cross term (geometric or validity).")] internal ClampedFloatParameter bilateralFilterWeight = new ClampedFloatParameter(1.0f, 0.0f, 1.0f); diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeLighting.cs b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeLighting.cs index d447a1bca0f..a4f66ad39b6 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeLighting.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeLighting.cs @@ -25,7 +25,7 @@ internal struct ProbeVolumeEngineData public Vector3 resolution; public uint lightLayers; public Vector3 resolutionInverse; - public float unused; + public float normalBiasWS; public static ProbeVolumeEngineData GetNeutralValues() { @@ -45,7 +45,7 @@ public static ProbeVolumeEngineData GetNeutralValues() data.resolution = Vector3.zero; data.lightLayers = 0; data.resolutionInverse = Vector3.zero; - data.unused = 0.0f; + data.normalBiasWS = 0.0f; return data; } @@ -102,10 +102,8 @@ public partial class HDRenderPipeline internal const int k_ProbeOctahedralDepthWidth = 8; internal const int k_ProbeOctahedralDepthHeight = 8; - // 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. - // With current settings this compute buffer will take 1024 * 1024 * sizeof(float) * coefficientCount (12) bytes ~= 50.3 MB. - static int s_MaxProbeVolumeProbeCount = 1024 * 1024; + static int s_MaxProbeVolumeProbeCount; + static int s_MaxProbeVolumeProbeOctahedralDepthCount; RTHandle m_ProbeVolumeAtlasSHRTHandle; int m_ProbeVolumeAtlasSHRTDepthSliceCount; @@ -128,7 +126,11 @@ void InitializeProbeVolumes() s_ProbeVolumeAtlasWidth = asset.currentPlatformRenderPipelineSettings.probeVolumeSettings.atlasWidth; s_ProbeVolumeAtlasHeight = asset.currentPlatformRenderPipelineSettings.probeVolumeSettings.atlasHeight; s_ProbeVolumeAtlasDepth = asset.currentPlatformRenderPipelineSettings.probeVolumeSettings.atlasDepth; + + // 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_ProbeVolumeAtlasWidth * s_ProbeVolumeAtlasHeight * s_ProbeVolumeAtlasDepth; + s_MaxProbeVolumeProbeOctahedralDepthCount = s_MaxProbeVolumeProbeCount * k_ProbeOctahedralDepthWidth * k_ProbeOctahedralDepthHeight; s_ProbeVolumeAtlasOctahedralDepthWidth = asset.currentPlatformRenderPipelineSettings.probeVolumeSettings.atlasOctahedralDepthWidth; s_ProbeVolumeAtlasOctahedralDepthHeight = asset.currentPlatformRenderPipelineSettings.probeVolumeSettings.atlasOctahedralDepthHeight; @@ -162,46 +164,48 @@ internal void CreateProbeVolumeBuffersDefault() s_VisibleProbeVolumeDataBufferDefault = new ComputeBuffer(1, Marshal.SizeOf(typeof(ProbeVolumeEngineData))); } - internal void CreateProbeVolumeBuffers() + static internal int GetDepthSliceCountFromEncodingMode(ProbeVolumesEncodingModes encodingMode) { - 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_ProbeVolumeAtlasBlitDataBuffer = new ComputeBuffer(s_MaxProbeVolumeProbeCount * ProbeVolumePayload.GetSHStride(ShaderConfig.s_ProbeVolumesEncodingMode), Marshal.SizeOf(typeof(float))); - s_ProbeVolumeAtlasBlitDataValidityBuffer = new ComputeBuffer(s_MaxProbeVolumeProbeCount, Marshal.SizeOf(typeof(float))); - s_ProbeVolumeAtlasOctahedralDepthBuffer = new ComputeBuffer(s_MaxProbeVolumeProbeCount, Marshal.SizeOf(typeof(float))); - - switch (ShaderConfig.s_ProbeVolumesEncodingMode) + switch (encodingMode) { case ProbeVolumesEncodingModes.SphericalHarmonicsL0: { // One "texture slice" for our single RGB SH DC term. Validity is placed in the alpha channel. - m_ProbeVolumeAtlasSHRTDepthSliceCount = 1; - break; + 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). - m_ProbeVolumeAtlasSHRTDepthSliceCount = 4; - break; + 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. - m_ProbeVolumeAtlasSHRTDepthSliceCount = 7; - break; + 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."); - break; + return 0; } } + } + 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_ProbeVolumeAtlasBlitDataBuffer = new ComputeBuffer(s_MaxProbeVolumeProbeCount * ProbeVolumePayload.GetSHStride(ShaderConfig.s_ProbeVolumesEncodingMode), Marshal.SizeOf(typeof(float))); + s_ProbeVolumeAtlasBlitDataValidityBuffer = new ComputeBuffer(s_MaxProbeVolumeProbeCount, Marshal.SizeOf(typeof(float))); + s_ProbeVolumeAtlasOctahedralDepthBuffer = new ComputeBuffer(s_MaxProbeVolumeProbeOctahedralDepthCount, Marshal.SizeOf(typeof(float))); + + m_ProbeVolumeAtlasSHRTDepthSliceCount = GetDepthSliceCountFromEncodingMode(ShaderConfig.s_ProbeVolumesEncodingMode); + m_ProbeVolumeAtlasSHRTHandle = RTHandles.Alloc( width: s_ProbeVolumeAtlasWidth, height: s_ProbeVolumeAtlasHeight, @@ -286,7 +290,6 @@ unsafe void UpdateShaderVariablesGlobalProbeVolumesDefault(ref ShaderVariablesGl cb._EnableProbeVolumes = 0; cb._ProbeVolumeCount = 0; cb._ProbeVolumeLeakMitigationMode = (int)LeakMitigationMode.NormalBias; - cb._ProbeVolumeNormalBiasWS = 0.0f; cb._ProbeVolumeBilateralFilterWeightMin = 0.0f; cb._ProbeVolumeBilateralFilterWeight = 0.0f; @@ -335,15 +338,9 @@ unsafe void UpdateShaderVariablesGlobalProbeVolumes(ref ShaderVariablesGlobal cb LeakMitigationMode leakMitigationMode = (settings == null) ? LeakMitigationMode.NormalBias : settings.leakMitigationMode.value; - float normalBiasWS = (settings == null) ? 0.0f : settings.normalBiasWS.value; float bilateralFilterWeight = (settings == null) ? 0.0f : settings.bilateralFilterWeight.value; if (leakMitigationMode != LeakMitigationMode.NormalBias) { - if (leakMitigationMode != LeakMitigationMode.OctahedralDepthOcclusionFilter) - { - normalBiasWS = 0.0f; - } - if (bilateralFilterWeight < 1e-5f) { // If bilateralFilterWeight is effectively zero, then we are simply doing trilinear filtering. @@ -353,7 +350,6 @@ unsafe void UpdateShaderVariablesGlobalProbeVolumes(ref ShaderVariablesGlobal cb } cb._ProbeVolumeLeakMitigationMode = (int)leakMitigationMode; - cb._ProbeVolumeNormalBiasWS = normalBiasWS; cb._ProbeVolumeBilateralFilterWeightMin = 1e-5f; cb._ProbeVolumeBilateralFilterWeight = bilateralFilterWeight; @@ -433,6 +429,14 @@ internal bool EnsureProbeVolumeInAtlas(ScriptableRenderContext renderContext, Co int sizeSHCoefficients = size * ProbeVolumePayload.GetSHStride(payload.encodingMode); + Debug.Assert(payload.dataSH.Length == sizeSHCoefficients, "ProbeVolume: The probe volume baked data and its resolution are out of sync! Volume data length is " + payload.dataSH.Length + ", but resolution * SH stride size is " + sizeSHCoefficients + "."); + + 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, @@ -462,9 +466,7 @@ internal bool EnsureProbeVolumeInAtlas(ScriptableRenderContext renderContext, Co 1.0f / (float)s_ProbeVolumeAtlasDepth, 1.0f / (float)m_ProbeVolumeAtlasSHRTDepthSliceCount )); - Debug.Assert(payload.dataSH.Length == sizeSHCoefficients, "ProbeVolume: The probe volume baked data and its resolution are out of sync! Volume data length is " + payload.dataSH.Length + ", but resolution * SH stride size is " + sizeSHCoefficients + "."); - Debug.Assert(size <= s_MaxProbeVolumeProbeCount, "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."); - + s_ProbeVolumeAtlasBlitDataBuffer.SetData(payload.dataSH); s_ProbeVolumeAtlasBlitDataValidityBuffer.SetData(payload.dataValidity); cmd.SetComputeIntParam(s_ProbeVolumeAtlasBlitCS, HDShaderIDs._ProbeVolumeAtlasReadBufferCount, size); @@ -482,7 +484,11 @@ internal bool EnsureProbeVolumeInAtlas(ScriptableRenderContext renderContext, Co return false; } - Debug.Assert(isSlotAllocated, "ProbeVolume: Texture Atlas failed to allocate space for texture { key: " + key + "width: " + width + ", height: " + height + ", depth: " + depth + "}"); + if (!isSlotAllocated) + { + Debug.LogWarning("ProbeVolume: Texture Atlas failed to allocate space for texture { key: " + key + "width: " + width + ", height: " + height + ", depth: " + depth + "}"); + } + return false; } @@ -514,6 +520,14 @@ internal bool EnsureProbeVolumeInAtlasOctahedralDepth(ScriptableRenderContext re // 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, @@ -546,7 +560,7 @@ internal bool EnsureProbeVolumeInAtlasOctahedralDepth(ScriptableRenderContext re 1.0f / (float)s_ProbeVolumeAtlasDepth, 1.0f / (float)m_ProbeVolumeAtlasSHRTDepthSliceCount )); - 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 + "."); + s_ProbeVolumeAtlasOctahedralDepthBuffer.SetData(payload.dataOctahedralDepth); cmd.SetComputeIntParam(s_ProbeVolumeAtlasOctahedralDepthBlitCS, HDShaderIDs._ProbeVolumeAtlasOctahedralDepthReadBufferCount, size); @@ -594,7 +608,11 @@ internal bool EnsureProbeVolumeInAtlasOctahedralDepth(ScriptableRenderContext re return false; } - Debug.Assert(isSlotAllocated, "ProbeVolume: Texture Atlas failed to allocate space for octahedral depth texture { key: " + key + " width: " + width + ", height: " + height); + if (!isSlotAllocated) + { + Debug.LogWarning("ProbeVolume: Texture Atlas failed to allocate space for octahedral depth texture { key: " + key + " width: " + width + ", height: " + height); + } + return false; } diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeLighting.cs.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeLighting.cs.hlsl index d60a1d6c898..3abb9fa64c1 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeLighting.cs.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeLighting.cs.hlsl @@ -30,7 +30,7 @@ struct ProbeVolumeEngineData float3 resolution; uint lightLayers; float3 resolutionInverse; - float unused; + float normalBiasWS; }; // @@ -92,9 +92,9 @@ float3 GetResolutionInverse(ProbeVolumeEngineData value) { return value.resolutionInverse; } -float GetUnused(ProbeVolumeEngineData value) +float GetNormalBiasWS(ProbeVolumeEngineData value) { - return value.unused; + return value.normalBiasWS; } #endif diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/SphericalHarmonics.cs.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Lighting/SphericalHarmonics.cs.hlsl index 21449991a3b..6617fdb0255 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/SphericalHarmonics.cs.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/SphericalHarmonics.cs.hlsl @@ -19,4 +19,29 @@ float3 GetShrgb(SphericalHarmonicsL0 value) return value.shrgb; } +// Generated from UnityEngine.Rendering.HighDefinition.SphericalHarmonicsL1 +// PackingRules = Exact +struct SphericalHarmonicsL1 +{ + float4 shAr; + float4 shAg; + float4 shAb; +}; + +// +// Accessors for UnityEngine.Rendering.HighDefinition.SphericalHarmonicsL1 +// +float4 GetShAr(SphericalHarmonicsL1 value) +{ + return value.shAr; +} +float4 GetShAg(SphericalHarmonicsL1 value) +{ + return value.shAg; +} +float4 GetShAb(SphericalHarmonicsL1 value) +{ + return value.shAb; +} + #endif diff --git a/com.unity.render-pipelines.high-definition/Runtime/Material/BuiltinGIUtilities.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Material/BuiltinGIUtilities.hlsl index 09c1c2c9b96..75b944d37e5 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Material/BuiltinGIUtilities.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/Material/BuiltinGIUtilities.hlsl @@ -147,8 +147,8 @@ void SampleBakedGI( posInputs.tileCoord = tileCoord; #endif - combinedGI += EvaluateProbeVolumes(probeVolumeHierarchyWeight, posInputs, normalWS, renderingLayers); - combinedGI += EvaluateProbeVolumeAmbientProbeFallback(probeVolumeHierarchyWeight, normalWS); + combinedGI += EvaluateProbeVolumes(posInputs, normalWS, renderingLayers, probeVolumeHierarchyWeight); + combinedGI += EvaluateProbeVolumeAmbientProbeFallback(normalWS, probeVolumeHierarchyWeight); #endif #endif diff --git a/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariablesGlobal.cs b/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariablesGlobal.cs index ca44206beb9..70fe06dc515 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariablesGlobal.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariablesGlobal.cs @@ -246,9 +246,9 @@ unsafe struct ShaderVariablesGlobal public Vector4 _ProbeVolumeAtlasOctahedralDepthResolutionAndInverse; public int _ProbeVolumeLeakMitigationMode; - public float _ProbeVolumeNormalBiasWS; public float _ProbeVolumeBilateralFilterWeightMin; public float _ProbeVolumeBilateralFilterWeight; + public float _Pad7; [HLSLArray(7, typeof(Vector4))] public fixed float _ProbeVolumeAmbientProbeFallbackPackedCoeffs[7 * 4]; // 3 bands of SH, packed for storing global ambient probe lighting as fallback to probe volumes. diff --git a/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariablesGlobal.cs.hlsl b/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariablesGlobal.cs.hlsl index ce07922e4f1..5f7df77ad9c 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariablesGlobal.cs.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariablesGlobal.cs.hlsl @@ -138,9 +138,9 @@ GLOBAL_CBUFFER_START(ShaderVariablesGlobal, b0) float4 _ProbeVolumeAtlasResolutionAndSliceCountInverse; float4 _ProbeVolumeAtlasOctahedralDepthResolutionAndInverse; int _ProbeVolumeLeakMitigationMode; - float _ProbeVolumeNormalBiasWS; float _ProbeVolumeBilateralFilterWeightMin; float _ProbeVolumeBilateralFilterWeight; + float _Pad7; float4 _ProbeVolumeAmbientProbeFallbackPackedCoeffs[7]; CBUFFER_END From a9264b7a365794742db14efe205119a440c645e5 Mon Sep 17 00:00:00 2001 From: pastasfuture Date: Sun, 31 May 2020 15:39:24 -0700 Subject: [PATCH 05/26] Probe Volumes: Instead of specifying AtlasWidth, AtlasHeight and AtlasDepth for probe volume atlas on HDRenderPipelineAsset, have user simply specify AtlasResolution and use for all axes. Same treatment for AtlasOctahedralDepth. --- .../RenderPipeline/HDRenderPipelineUI.Skin.cs | 12 +-- .../RenderPipeline/HDRenderPipelineUI.cs | 39 +++---- .../SerializedGlobalProbeVolumeSettings.cs | 14 +-- .../ProbeVolume/GlobalProbeVolumeSettings.cs | 14 +-- .../ProbeVolume/ProbeVolumeLighting.cs | 100 ++++++++---------- 5 files changed, 73 insertions(+), 106 deletions(-) diff --git a/com.unity.render-pipelines.high-definition/Editor/RenderPipeline/HDRenderPipelineUI.Skin.cs b/com.unity.render-pipelines.high-definition/Editor/RenderPipeline/HDRenderPipelineUI.Skin.cs index a78024c06b3..76d39b47036 100644 --- a/com.unity.render-pipelines.high-definition/Editor/RenderPipeline/HDRenderPipelineUI.Skin.cs +++ b/com.unity.render-pipelines.high-definition/Editor/RenderPipeline/HDRenderPipelineUI.Skin.cs @@ -117,11 +117,8 @@ public class GeneralSection public static readonly GUIContent LODBias = EditorGUIUtility.TrTextContent("LOD Bias"); internal static readonly GUIContent supportProbeVolumeContent = EditorGUIUtility.TrTextContent("Probe Volume", "When enabled, HDRP allocates Shader variants and memory for probe volume based GI. This allows you to use probe volumes in your Unity Project."); internal const string probeVolumeInfo = "Warning: Probe Volumes is a highly experimental feature.\nIt is disabled by default for this reason.\nIt's functionality is subject to breaking changes and whole sale removal.\nIt is not recommended for use outside of for providing feedback.\nIt should not be used in production.\nTo enable, set:\nProbeVolumesEvaluationMode = ProbeVolumesEvaluationModes.MaterialPass\ninside of ShaderConfig.cs\and inside of the editor run:\nEdit->Render Pipeline->Generate Shader Includes\nProbe Volumes feature must also be enabled here."; - internal static readonly GUIContent probeVolumeAtlasWidth = EditorGUIUtility.TrTextContent("Atlas Width", "Width (resolution in X) of the atlas containing visible ProbeVolumes."); - internal static readonly GUIContent probeVolumeAtlasHeight = EditorGUIUtility.TrTextContent("Atlas Height", "Height (resolution in Y) of the atlas containing visible ProbeVolumes."); - internal static readonly GUIContent probeVolumeAtlasDepth = EditorGUIUtility.TrTextContent("Atlas Depth", "Depth (resolution in Z) of the atlas containing visible ProbeVolumes."); - internal static readonly GUIContent probeVolumeAtlasOctahedralDepthWidth = EditorGUIUtility.TrTextContent("Octahedral Depth Atlas Width", "Width of the atlas containing visible ProbeVolumes octahedral depth data"); - internal static readonly GUIContent probeVolumeAtlasOctahedralDepthHeight = EditorGUIUtility.TrTextContent("Octahedral Depth Atlas Height", "Height of the atlas containing visible ProbeVolumes octahedral depth data."); + internal static readonly GUIContent probeVolumeAtlasResolution = EditorGUIUtility.TrTextContent("Atlas Resolution", "Resolution of the 3D texture atlas containing visible ProbeVolumes."); + internal static readonly GUIContent probeVolumeAtlasOctahedralDepthResolution = EditorGUIUtility.TrTextContent("Octahedral Depth Atlas Resolution", "Resolution of the 2D texture atlas containing visible ProbeVolumes octahedral depth data."); public const string cacheErrorFormat = "This configuration will lead to more than 2 GB reserved for this cache at runtime! ({0} requested) Only {1} element will be reserved instead."; @@ -234,10 +231,7 @@ public class GeneralSection { supportTransparentDepthPrepass , shaderVariantDrawback }, { supportTransparentDepthPostpass , shaderVariantDrawback }, { supportRaytracing , string.Format("{0}, {1}", memoryDrawback, lotShaderVariantDrawback) }, - { supportProbeVolumeContent , string.Format("{0}, {1}", memoryDrawback, shaderVariantDrawback) }, - { probeVolumeAtlasWidth , memoryDrawback }, - { probeVolumeAtlasHeight , memoryDrawback }, - { probeVolumeAtlasDepth , memoryDrawback }, + { supportProbeVolumeContent , string.Format("{0}, {1}", memoryDrawback, shaderVariantDrawback) } }; public static Dictionary supportLitShaderModeDrawbacks = new Dictionary diff --git a/com.unity.render-pipelines.high-definition/Editor/RenderPipeline/HDRenderPipelineUI.cs b/com.unity.render-pipelines.high-definition/Editor/RenderPipeline/HDRenderPipelineUI.cs index ad24fb02a96..1de66f8ca8e 100644 --- a/com.unity.render-pipelines.high-definition/Editor/RenderPipeline/HDRenderPipelineUI.cs +++ b/com.unity.render-pipelines.high-definition/Editor/RenderPipeline/HDRenderPipelineUI.cs @@ -878,38 +878,29 @@ static void Drawer_SectionLightingUnsorted(SerializedHDRenderPipelineAsset seria EditorGUILayout.HelpBox(Styles.probeVolumeInfo, MessageType.Warning); EditorGUI.BeginChangeCheck(); - EditorGUILayout.DelayedIntField(serialized.renderPipelineSettings.probeVolumeSettings.atlasWidth, Styles.probeVolumeAtlasWidth); + EditorGUILayout.DelayedIntField(serialized.renderPipelineSettings.probeVolumeSettings.atlasResolution, Styles.probeVolumeAtlasResolution); if (EditorGUI.EndChangeCheck()) - serialized.renderPipelineSettings.probeVolumeSettings.atlasWidth.intValue = Mathf.Max(serialized.renderPipelineSettings.probeVolumeSettings.atlasWidth.intValue, 0); + serialized.renderPipelineSettings.probeVolumeSettings.atlasResolution.intValue = Mathf.Max(serialized.renderPipelineSettings.probeVolumeSettings.atlasResolution.intValue, 0); EditorGUI.BeginChangeCheck(); - EditorGUILayout.DelayedIntField(serialized.renderPipelineSettings.probeVolumeSettings.atlasHeight, Styles.probeVolumeAtlasHeight); + EditorGUILayout.DelayedIntField(serialized.renderPipelineSettings.probeVolumeSettings.atlasOctahedralDepthResolution, Styles.probeVolumeAtlasOctahedralDepthResolution); if (EditorGUI.EndChangeCheck()) - serialized.renderPipelineSettings.probeVolumeSettings.atlasHeight.intValue = Mathf.Max(serialized.renderPipelineSettings.probeVolumeSettings.atlasHeight.intValue, 0); + serialized.renderPipelineSettings.probeVolumeSettings.atlasOctahedralDepthResolution.intValue = Mathf.Max(serialized.renderPipelineSettings.probeVolumeSettings.atlasOctahedralDepthResolution.intValue, 0); - EditorGUI.BeginChangeCheck(); - EditorGUILayout.DelayedIntField(serialized.renderPipelineSettings.probeVolumeSettings.atlasDepth, Styles.probeVolumeAtlasDepth); - if (EditorGUI.EndChangeCheck()) - serialized.renderPipelineSettings.probeVolumeSettings.atlasDepth.intValue = Mathf.Max(serialized.renderPipelineSettings.probeVolumeSettings.atlasDepth.intValue, 0); - - EditorGUI.BeginChangeCheck(); - EditorGUILayout.DelayedIntField(serialized.renderPipelineSettings.probeVolumeSettings.atlasOctahedralDepthWidth, Styles.probeVolumeAtlasOctahedralDepthWidth); - if (EditorGUI.EndChangeCheck()) - serialized.renderPipelineSettings.probeVolumeSettings.atlasOctahedralDepthWidth.intValue = Mathf.Max(serialized.renderPipelineSettings.probeVolumeSettings.atlasOctahedralDepthWidth.intValue, 0); - - EditorGUI.BeginChangeCheck(); - EditorGUILayout.DelayedIntField(serialized.renderPipelineSettings.probeVolumeSettings.atlasOctahedralDepthHeight, Styles.probeVolumeAtlasOctahedralDepthHeight); - if (EditorGUI.EndChangeCheck()) - serialized.renderPipelineSettings.probeVolumeSettings.atlasOctahedralDepthHeight.intValue = Mathf.Max(serialized.renderPipelineSettings.probeVolumeSettings.atlasOctahedralDepthHeight.intValue, 0); + if (serialized.renderPipelineSettings.probeVolumeSettings.atlasResolution.intValue <= 0) + { + // Detected legacy probe volume atlas (atlasResolution did not exist. Was explicitly defined by atlasWidth, atlasHeight, atlasDepth). + // Initialize with default values. + // TODO: (Nick) This can be removed in release. It's currently here to reduce user pain on internal projects actively using this WIP tech. + serialized.renderPipelineSettings.probeVolumeSettings.atlasResolution.intValue = GlobalProbeVolumeSettings.@default.atlasResolution; + } - if (serialized.renderPipelineSettings.probeVolumeSettings.atlasDepth.intValue <= 0) + if (serialized.renderPipelineSettings.probeVolumeSettings.atlasOctahedralDepthResolution.intValue <= 0) { - // Detected legacy 2D probe volume atlas (degenerate Z axis resolution). - // Initialize with default 3D atlas values. + // Detected legacy probe volume atlas (atlasOctahedralDepthResolution did not exist. Was explicitly defined by atlasWidth, atlasHeight, atlasDepth). + // Initialize with default values. // TODO: (Nick) This can be removed in release. It's currently here to reduce user pain on internal projects actively using this WIP tech. - serialized.renderPipelineSettings.probeVolumeSettings.atlasWidth.intValue = GlobalProbeVolumeSettings.@default.atlasWidth; - serialized.renderPipelineSettings.probeVolumeSettings.atlasHeight.intValue = GlobalProbeVolumeSettings.@default.atlasHeight; - serialized.renderPipelineSettings.probeVolumeSettings.atlasDepth.intValue = GlobalProbeVolumeSettings.@default.atlasDepth; + serialized.renderPipelineSettings.probeVolumeSettings.atlasOctahedralDepthResolution.intValue = GlobalProbeVolumeSettings.@default.atlasOctahedralDepthResolution; } --EditorGUI.indentLevel; diff --git a/com.unity.render-pipelines.high-definition/Editor/RenderPipeline/Settings/SerializedGlobalProbeVolumeSettings.cs b/com.unity.render-pipelines.high-definition/Editor/RenderPipeline/Settings/SerializedGlobalProbeVolumeSettings.cs index 7392db6e50e..5c416b17898 100644 --- a/com.unity.render-pipelines.high-definition/Editor/RenderPipeline/Settings/SerializedGlobalProbeVolumeSettings.cs +++ b/com.unity.render-pipelines.high-definition/Editor/RenderPipeline/Settings/SerializedGlobalProbeVolumeSettings.cs @@ -7,21 +7,15 @@ class SerializedGlobalProbeVolumeSettings { internal SerializedProperty root; - internal SerializedProperty atlasWidth; - internal SerializedProperty atlasHeight; - internal SerializedProperty atlasDepth; - internal SerializedProperty atlasOctahedralDepthWidth; - internal SerializedProperty atlasOctahedralDepthHeight; + internal SerializedProperty atlasResolution; + internal SerializedProperty atlasOctahedralDepthResolution; internal SerializedGlobalProbeVolumeSettings(SerializedProperty root) { this.root = root; - atlasWidth = root.Find((GlobalProbeVolumeSettings s) => s.atlasWidth); - atlasHeight = root.Find((GlobalProbeVolumeSettings s) => s.atlasHeight); - atlasDepth = root.Find((GlobalProbeVolumeSettings s) => s.atlasDepth); - atlasOctahedralDepthWidth = root.Find((GlobalProbeVolumeSettings s) => s.atlasOctahedralDepthWidth); - atlasOctahedralDepthHeight = root.Find((GlobalProbeVolumeSettings s) => s.atlasOctahedralDepthHeight); + atlasResolution = root.Find((GlobalProbeVolumeSettings s) => s.atlasResolution); + atlasOctahedralDepthResolution = root.Find((GlobalProbeVolumeSettings s) => s.atlasOctahedralDepthResolution); } } } diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/GlobalProbeVolumeSettings.cs b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/GlobalProbeVolumeSettings.cs index dbd83a06b24..018202f0b6d 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/GlobalProbeVolumeSettings.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/GlobalProbeVolumeSettings.cs @@ -8,17 +8,11 @@ internal struct GlobalProbeVolumeSettings /// Default GlobalProbeVolumeSettings internal static readonly GlobalProbeVolumeSettings @default = new GlobalProbeVolumeSettings() { - atlasWidth = 128, - atlasHeight = 128, - atlasDepth = 512, - atlasOctahedralDepthWidth = 2048, - atlasOctahedralDepthHeight = 2048 + atlasResolution = 128, + atlasOctahedralDepthResolution = 2048 }; - [SerializeField] internal int atlasWidth; - [SerializeField] internal int atlasHeight; - [SerializeField] internal int atlasDepth; - [SerializeField] internal int atlasOctahedralDepthWidth; - [SerializeField] internal int atlasOctahedralDepthHeight; + [SerializeField] internal int atlasResolution; + [SerializeField] internal int atlasOctahedralDepthResolution; } } diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeLighting.cs b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeLighting.cs index a4f66ad39b6..d9fcdcf8ee5 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeLighting.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeLighting.cs @@ -93,11 +93,8 @@ public partial class HDRenderPipeline static ComputeBuffer s_ProbeVolumeAtlasBlitDataBuffer = null; static ComputeBuffer s_ProbeVolumeAtlasBlitDataValidityBuffer = null; static ComputeBuffer s_ProbeVolumeAtlasOctahedralDepthBuffer = null; - static int s_ProbeVolumeAtlasWidth; - static int s_ProbeVolumeAtlasHeight; - static int s_ProbeVolumeAtlasDepth; - static int s_ProbeVolumeAtlasOctahedralDepthWidth; - static int s_ProbeVolumeAtlasOctahedralDepthHeight; + static int s_ProbeVolumeAtlasResolution; + static int s_ProbeVolumeAtlasOctahedralDepthResolution; static int k_MaxProbeVolumeAtlasOctahedralDepthProbeCount; internal const int k_ProbeOctahedralDepthWidth = 8; internal const int k_ProbeOctahedralDepthHeight = 8; @@ -123,18 +120,15 @@ void InitializeProbeVolumes() m_SupportProbeVolume = asset.currentPlatformRenderPipelineSettings.supportProbeVolume; - s_ProbeVolumeAtlasWidth = asset.currentPlatformRenderPipelineSettings.probeVolumeSettings.atlasWidth; - s_ProbeVolumeAtlasHeight = asset.currentPlatformRenderPipelineSettings.probeVolumeSettings.atlasHeight; - s_ProbeVolumeAtlasDepth = asset.currentPlatformRenderPipelineSettings.probeVolumeSettings.atlasDepth; + s_ProbeVolumeAtlasResolution = asset.currentPlatformRenderPipelineSettings.probeVolumeSettings.atlasResolution; // 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_ProbeVolumeAtlasWidth * s_ProbeVolumeAtlasHeight * s_ProbeVolumeAtlasDepth; + s_MaxProbeVolumeProbeCount = s_ProbeVolumeAtlasResolution * s_ProbeVolumeAtlasResolution * s_ProbeVolumeAtlasResolution; s_MaxProbeVolumeProbeOctahedralDepthCount = s_MaxProbeVolumeProbeCount * k_ProbeOctahedralDepthWidth * k_ProbeOctahedralDepthHeight; - s_ProbeVolumeAtlasOctahedralDepthWidth = asset.currentPlatformRenderPipelineSettings.probeVolumeSettings.atlasOctahedralDepthWidth; - s_ProbeVolumeAtlasOctahedralDepthHeight = asset.currentPlatformRenderPipelineSettings.probeVolumeSettings.atlasOctahedralDepthHeight; - k_MaxProbeVolumeAtlasOctahedralDepthProbeCount = (s_ProbeVolumeAtlasOctahedralDepthWidth / k_ProbeOctahedralDepthWidth) * (s_ProbeVolumeAtlasOctahedralDepthHeight / k_ProbeOctahedralDepthWidth); + s_ProbeVolumeAtlasOctahedralDepthResolution = asset.currentPlatformRenderPipelineSettings.probeVolumeSettings.atlasOctahedralDepthResolution; + k_MaxProbeVolumeAtlasOctahedralDepthProbeCount = (s_ProbeVolumeAtlasOctahedralDepthResolution / k_ProbeOctahedralDepthWidth) * (s_ProbeVolumeAtlasOctahedralDepthResolution / k_ProbeOctahedralDepthWidth); if (m_SupportProbeVolume) { @@ -207,9 +201,9 @@ internal void CreateProbeVolumeBuffers() m_ProbeVolumeAtlasSHRTDepthSliceCount = GetDepthSliceCountFromEncodingMode(ShaderConfig.s_ProbeVolumesEncodingMode); m_ProbeVolumeAtlasSHRTHandle = RTHandles.Alloc( - width: s_ProbeVolumeAtlasWidth, - height: s_ProbeVolumeAtlasHeight, - slices: s_ProbeVolumeAtlasDepth * m_ProbeVolumeAtlasSHRTDepthSliceCount, + width: s_ProbeVolumeAtlasResolution, + height: s_ProbeVolumeAtlasResolution, + slices: s_ProbeVolumeAtlasResolution * m_ProbeVolumeAtlasSHRTDepthSliceCount, dimension: TextureDimension.Tex3D, colorFormat: UnityEngine.Experimental.Rendering.GraphicsFormat.R16G16B16A16_SFloat,//GraphicsFormat.B10G11R11_UFloatPack32, enableRandomWrite: true, @@ -217,12 +211,12 @@ internal void CreateProbeVolumeBuffers() name: "ProbeVolumeAtlasSH" ); - probeVolumeAtlas = new Texture3DAtlasDynamic(s_ProbeVolumeAtlasWidth, s_ProbeVolumeAtlasHeight, s_ProbeVolumeAtlasDepth, k_MaxVisibleProbeVolumeCount, m_ProbeVolumeAtlasSHRTHandle); + probeVolumeAtlas = new Texture3DAtlasDynamic(s_ProbeVolumeAtlasResolution, s_ProbeVolumeAtlasResolution, s_ProbeVolumeAtlasResolution, k_MaxVisibleProbeVolumeCount, m_ProbeVolumeAtlasSHRTHandle); // 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_ProbeVolumeAtlasOctahedralDepthWidth, - height: s_ProbeVolumeAtlasOctahedralDepthHeight, + width: s_ProbeVolumeAtlasOctahedralDepthResolution, + height: s_ProbeVolumeAtlasOctahedralDepthResolution, slices: 1, dimension: TextureDimension.Tex2D, colorFormat: UnityEngine.Experimental.Rendering.GraphicsFormat.R32G32_SFloat, // float2(mean, variance) @@ -232,8 +226,8 @@ internal void CreateProbeVolumeBuffers() ); probeVolumeAtlasOctahedralDepth = new Texture2DAtlasDynamic( - s_ProbeVolumeAtlasOctahedralDepthWidth, - s_ProbeVolumeAtlasOctahedralDepthHeight, + s_ProbeVolumeAtlasOctahedralDepthResolution, + s_ProbeVolumeAtlasOctahedralDepthResolution, k_MaxVisibleProbeVolumeCount, m_ProbeVolumeAtlasOctahedralDepthRTHandle ); @@ -316,15 +310,15 @@ unsafe void UpdateShaderVariablesGlobalProbeVolumes(ref ShaderVariablesGlobal cb cb._EnableProbeVolumes = hdCamera.frameSettings.IsEnabled(FrameSettingsField.ProbeVolume) ? 1u : 0u; cb._ProbeVolumeCount = (uint)m_VisibleProbeVolumeBounds.Count; cb._ProbeVolumeAtlasResolutionAndSliceCount = new Vector4( - s_ProbeVolumeAtlasWidth, - s_ProbeVolumeAtlasHeight, - s_ProbeVolumeAtlasDepth, + s_ProbeVolumeAtlasResolution, + s_ProbeVolumeAtlasResolution, + s_ProbeVolumeAtlasResolution, m_ProbeVolumeAtlasSHRTDepthSliceCount ); cb._ProbeVolumeAtlasResolutionAndSliceCountInverse = new Vector4( - 1.0f / (float)s_ProbeVolumeAtlasWidth, - 1.0f / (float)s_ProbeVolumeAtlasHeight, - 1.0f / (float)s_ProbeVolumeAtlasDepth, + 1.0f / (float)s_ProbeVolumeAtlasResolution, + 1.0f / (float)s_ProbeVolumeAtlasResolution, + 1.0f / (float)s_ProbeVolumeAtlasResolution, 1.0f / (float)m_ProbeVolumeAtlasSHRTDepthSliceCount ); cb._ProbeVolumeAtlasOctahedralDepthResolutionAndInverse = new Vector4( @@ -455,15 +449,15 @@ internal bool EnsureProbeVolumeInAtlas(ScriptableRenderContext renderContext, Co volume.parameters.bias ); cmd.SetComputeVectorParam(s_ProbeVolumeAtlasBlitCS, HDShaderIDs._ProbeVolumeAtlasResolutionAndSliceCount, new Vector4( - s_ProbeVolumeAtlasWidth, - s_ProbeVolumeAtlasHeight, - s_ProbeVolumeAtlasDepth, + s_ProbeVolumeAtlasResolution, + s_ProbeVolumeAtlasResolution, + s_ProbeVolumeAtlasResolution, m_ProbeVolumeAtlasSHRTDepthSliceCount )); cmd.SetComputeVectorParam(s_ProbeVolumeAtlasBlitCS, HDShaderIDs._ProbeVolumeAtlasResolutionAndSliceCountInverse, new Vector4( - 1.0f / (float)s_ProbeVolumeAtlasWidth, - 1.0f / (float)s_ProbeVolumeAtlasHeight, - 1.0f / (float)s_ProbeVolumeAtlasDepth, + 1.0f / (float)s_ProbeVolumeAtlasResolution, + 1.0f / (float)s_ProbeVolumeAtlasResolution, + 1.0f / (float)s_ProbeVolumeAtlasResolution, 1.0f / (float)m_ProbeVolumeAtlasSHRTDepthSliceCount )); @@ -549,15 +543,15 @@ internal bool EnsureProbeVolumeInAtlasOctahedralDepth(ScriptableRenderContext re 1.0f / (float)m_ProbeVolumeAtlasOctahedralDepthRTHandle.rt.height )); cmd.SetComputeVectorParam(s_ProbeVolumeAtlasOctahedralDepthBlitCS, HDShaderIDs._ProbeVolumeAtlasResolutionAndSliceCount, new Vector4( - s_ProbeVolumeAtlasWidth, - s_ProbeVolumeAtlasHeight, - s_ProbeVolumeAtlasDepth, + s_ProbeVolumeAtlasResolution, + s_ProbeVolumeAtlasResolution, + s_ProbeVolumeAtlasResolution, m_ProbeVolumeAtlasSHRTDepthSliceCount )); cmd.SetComputeVectorParam(s_ProbeVolumeAtlasOctahedralDepthBlitCS, HDShaderIDs._ProbeVolumeAtlasResolutionAndSliceCountInverse, new Vector4( - 1.0f / (float)s_ProbeVolumeAtlasWidth, - 1.0f / (float)s_ProbeVolumeAtlasHeight, - 1.0f / (float)s_ProbeVolumeAtlasDepth, + 1.0f / (float)s_ProbeVolumeAtlasResolution, + 1.0f / (float)s_ProbeVolumeAtlasResolution, + 1.0f / (float)s_ProbeVolumeAtlasResolution, 1.0f / (float)m_ProbeVolumeAtlasSHRTDepthSliceCount )); @@ -576,10 +570,10 @@ internal bool EnsureProbeVolumeInAtlasOctahedralDepth(ScriptableRenderContext re // Convolve: { Vector4 probeVolumeAtlasOctahedralDepthScaleBiasTexels = new Vector4( - Mathf.Floor(volume.parameters.octahedralDepthScaleBias.x * s_ProbeVolumeAtlasOctahedralDepthWidth + 0.5f), - Mathf.Floor(volume.parameters.octahedralDepthScaleBias.y * s_ProbeVolumeAtlasOctahedralDepthHeight + 0.5f), - Mathf.Floor(volume.parameters.octahedralDepthScaleBias.z * s_ProbeVolumeAtlasOctahedralDepthWidth + 0.5f), - Mathf.Floor(volume.parameters.octahedralDepthScaleBias.w * s_ProbeVolumeAtlasOctahedralDepthHeight + 0.5f) + 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, @@ -593,8 +587,8 @@ internal bool EnsureProbeVolumeInAtlasOctahedralDepth(ScriptableRenderContext re // 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_ProbeVolumeAtlasOctahedralDepthWidth); - int heightPixels = (int)(volume.parameters.octahedralDepthScaleBias.y * (float)s_ProbeVolumeAtlasOctahedralDepthHeight); + 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)); @@ -837,7 +831,7 @@ void DisplayProbeVolumeAtlas(CommandBuffer cmd, Material debugMaterial, float sc Vector4 validRange = new Vector4(minValue, 1.0f / (maxValue - minValue)); Vector3 textureViewScale = new Vector3(1.0f, 1.0f, 1.0f); Vector3 textureViewBias = new Vector3(0.0f, 0.0f, 0.0f); - Vector3 textureViewResolution = new Vector3(s_ProbeVolumeAtlasWidth, s_ProbeVolumeAtlasHeight, s_ProbeVolumeAtlasDepth); + Vector3 textureViewResolution = new Vector3(s_ProbeVolumeAtlasResolution, s_ProbeVolumeAtlasResolution, s_ProbeVolumeAtlasResolution); Vector4 atlasTextureOctahedralDepthScaleBias = new Vector4(1.0f, 1.0f, 0.0f, 0.0f); #if UNITY_EDITOR @@ -879,16 +873,16 @@ void DisplayProbeVolumeAtlas(CommandBuffer cmd, Material debugMaterial, float sc propertyBlock.SetVector(HDShaderIDs._TextureViewBias, textureViewBias); propertyBlock.SetVector(HDShaderIDs._TextureViewResolution, textureViewResolution); cmd.SetGlobalVector(HDShaderIDs._ProbeVolumeAtlasResolutionAndSliceCount, new Vector4( - s_ProbeVolumeAtlasWidth, - s_ProbeVolumeAtlasHeight, - s_ProbeVolumeAtlasDepth, - m_ProbeVolumeAtlasSHRTDepthSliceCount + s_ProbeVolumeAtlasResolution, + s_ProbeVolumeAtlasResolution, + s_ProbeVolumeAtlasResolution, + m_ProbeVolumeAtlasSHRTDepthSliceCount )); cmd.SetGlobalVector(HDShaderIDs._ProbeVolumeAtlasResolutionAndSliceCountInverse, new Vector4( - 1.0f / (float)s_ProbeVolumeAtlasWidth, - 1.0f / (float)s_ProbeVolumeAtlasHeight, - 1.0f / (float)s_ProbeVolumeAtlasDepth, - 1.0f / (float)m_ProbeVolumeAtlasSHRTDepthSliceCount + 1.0f / (float)s_ProbeVolumeAtlasResolution, + 1.0f / (float)s_ProbeVolumeAtlasResolution, + 1.0f / (float)s_ProbeVolumeAtlasResolution, + 1.0f / (float)m_ProbeVolumeAtlasSHRTDepthSliceCount )); propertyBlock.SetTexture(HDShaderIDs._AtlasTextureOctahedralDepth, m_ProbeVolumeAtlasOctahedralDepthRTHandle); From 6393f55a770c7b1eb40d2b180a390afde4813dbd Mon Sep 17 00:00:00 2001 From: pastasfuture Date: Sun, 31 May 2020 17:07:25 -0700 Subject: [PATCH 06/26] Probe Volumes: HDRenderPipelineAsset: Add memory usage information to UI and ensure we never allocate the ProbeVolumeAtlas or OctahedralDepthAtlas to be larger than k_MaxCacheSize --- .../RenderPipeline/HDRenderPipelineUI.cs | 34 ++++++++++++++++ .../ProbeVolume/ProbeVolumeLighting.cs | 40 ++++++++++++++++++- 2 files changed, 72 insertions(+), 2 deletions(-) diff --git a/com.unity.render-pipelines.high-definition/Editor/RenderPipeline/HDRenderPipelineUI.cs b/com.unity.render-pipelines.high-definition/Editor/RenderPipeline/HDRenderPipelineUI.cs index 1de66f8ca8e..23df7c09ad0 100644 --- a/com.unity.render-pipelines.high-definition/Editor/RenderPipeline/HDRenderPipelineUI.cs +++ b/com.unity.render-pipelines.high-definition/Editor/RenderPipeline/HDRenderPipelineUI.cs @@ -880,12 +880,46 @@ static void Drawer_SectionLightingUnsorted(SerializedHDRenderPipelineAsset seria EditorGUI.BeginChangeCheck(); EditorGUILayout.DelayedIntField(serialized.renderPipelineSettings.probeVolumeSettings.atlasResolution, Styles.probeVolumeAtlasResolution); if (EditorGUI.EndChangeCheck()) + { serialized.renderPipelineSettings.probeVolumeSettings.atlasResolution.intValue = Mathf.Max(serialized.renderPipelineSettings.probeVolumeSettings.atlasResolution.intValue, 0); + } + else + { + long currentCache = HDRenderPipeline.GetApproxProbeVolumeAtlasSizeInByte(serialized.renderPipelineSettings.probeVolumeSettings.atlasResolution.intValue); + if (currentCache > HDRenderPipeline.k_MaxCacheSize) + { + int reserved = HDRenderPipeline.GetMaxProbeVolumeAtlasSizeForWeightInByte(HDRenderPipeline.k_MaxCacheSize); + string message = string.Format(Styles.cacheErrorFormat, HDEditorUtils.HumanizeWeight(currentCache), reserved); + EditorGUILayout.HelpBox(message, MessageType.Error); + } + else + { + string message = string.Format(Styles.cacheInfoFormat, HDEditorUtils.HumanizeWeight(currentCache)); + EditorGUILayout.HelpBox(message, MessageType.Info); + } + } EditorGUI.BeginChangeCheck(); EditorGUILayout.DelayedIntField(serialized.renderPipelineSettings.probeVolumeSettings.atlasOctahedralDepthResolution, Styles.probeVolumeAtlasOctahedralDepthResolution); if (EditorGUI.EndChangeCheck()) + { serialized.renderPipelineSettings.probeVolumeSettings.atlasOctahedralDepthResolution.intValue = Mathf.Max(serialized.renderPipelineSettings.probeVolumeSettings.atlasOctahedralDepthResolution.intValue, 0); + } + else + { + long currentCache = HDRenderPipeline.GetApproxProbeVolumeOctahedralDepthAtlasSizeInByte(serialized.renderPipelineSettings.probeVolumeSettings.atlasOctahedralDepthResolution.intValue); + if (currentCache > HDRenderPipeline.k_MaxCacheSize) + { + int reserved = HDRenderPipeline.GetMaxProbeVolumeOctahedralDepthAtlasSizeForWeightInByte(HDRenderPipeline.k_MaxCacheSize); + string message = string.Format(Styles.cacheErrorFormat, HDEditorUtils.HumanizeWeight(currentCache), reserved); + EditorGUILayout.HelpBox(message, MessageType.Error); + } + else + { + string message = string.Format(Styles.cacheInfoFormat, HDEditorUtils.HumanizeWeight(currentCache)); + EditorGUILayout.HelpBox(message, MessageType.Info); + } + } if (serialized.renderPipelineSettings.probeVolumeSettings.atlasResolution.intValue <= 0) { diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeLighting.cs b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeLighting.cs index d9fcdcf8ee5..f66ea0458ba 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeLighting.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeLighting.cs @@ -98,6 +98,8 @@ public partial class HDRenderPipeline 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; @@ -121,6 +123,10 @@ void InitializeProbeVolumes() m_SupportProbeVolume = asset.currentPlatformRenderPipelineSettings.supportProbeVolume; 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. @@ -128,6 +134,11 @@ void InitializeProbeVolumes() 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) @@ -188,6 +199,31 @@ static internal int GetDepthSliceCountFromEncodingMode(ProbeVolumesEncodingModes } } + // 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(); @@ -205,7 +241,7 @@ internal void CreateProbeVolumeBuffers() height: s_ProbeVolumeAtlasResolution, slices: s_ProbeVolumeAtlasResolution * m_ProbeVolumeAtlasSHRTDepthSliceCount, dimension: TextureDimension.Tex3D, - colorFormat: UnityEngine.Experimental.Rendering.GraphicsFormat.R16G16B16A16_SFloat,//GraphicsFormat.B10G11R11_UFloatPack32, + colorFormat: k_ProbeVolumeAtlasFormat, enableRandomWrite: true, useMipMap: false, name: "ProbeVolumeAtlasSH" @@ -219,7 +255,7 @@ internal void CreateProbeVolumeBuffers() height: s_ProbeVolumeAtlasOctahedralDepthResolution, slices: 1, dimension: TextureDimension.Tex2D, - colorFormat: UnityEngine.Experimental.Rendering.GraphicsFormat.R32G32_SFloat, // float2(mean, variance) + colorFormat: k_ProbeVolumeOctahedralDepthAtlasFormat, enableRandomWrite: true, useMipMap: false, name: "ProbeVolumeAtlasOctahedralDepthMeanAndVariance" From 204a94d9e2315a4217fdc7dd20bca8846af62b99 Mon Sep 17 00:00:00 2001 From: pastasfuture Date: Sun, 31 May 2020 17:31:41 -0700 Subject: [PATCH 07/26] Probe Volumes: Bugfix for Probe Volume UI: Only display Additive blendmode warning if additive blend support is disabled in ShaderConfig --- .../Editor/Lighting/ProbeVolume/ProbeVolumeUI.Drawer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.unity.render-pipelines.high-definition/Editor/Lighting/ProbeVolume/ProbeVolumeUI.Drawer.cs b/com.unity.render-pipelines.high-definition/Editor/Lighting/ProbeVolume/ProbeVolumeUI.Drawer.cs index 275393b0cca..a394d90fc26 100644 --- a/com.unity.render-pipelines.high-definition/Editor/Lighting/ProbeVolume/ProbeVolumeUI.Drawer.cs +++ b/com.unity.render-pipelines.high-definition/Editor/Lighting/ProbeVolume/ProbeVolumeUI.Drawer.cs @@ -282,7 +282,7 @@ static void Drawer_VolumeContent(SerializedProbeVolume serialized, Editor owner) } EditorGUILayout.PropertyField(serialized.debugColor, Styles.s_DebugColorLabel); - if (serialized.volumeBlendMode.intValue != (int)VolumeBlendMode.Normal) + if (ShaderConfig.s_ProbeVolumesAdditiveBlending == 0 && serialized.volumeBlendMode.intValue != (int)VolumeBlendMode.Normal) { EditorGUILayout.HelpBox(Styles.k_featureAdditiveBlendingDisabledError, MessageType.Error); } From abdc3c9e935bfea05a52c8d6e99caf699ad9de27 Mon Sep 17 00:00:00 2001 From: pastasfuture Date: Sun, 31 May 2020 17:32:38 -0700 Subject: [PATCH 08/26] Probe Volumes: Bugfix: DataIsAssigned function needed in build as well. --- .../Runtime/Lighting/ProbeVolume/ProbeVolumeAsset.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeAsset.cs b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeAsset.cs index 3179c51e54b..8e6bfd60028 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeAsset.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeAsset.cs @@ -42,6 +42,11 @@ internal enum AssetVersion [SerializeField] internal float backfaceTolerance; [SerializeField] internal int dilationIterations; + internal bool IsDataAssigned() + { + return payload.dataSH != null; + } + #if UNITY_EDITOR // Debug only: Uncomment out if you want to manually create a probe volume asset and type in data into the inspector. // This is not a user facing workflow we are supporting. @@ -94,11 +99,6 @@ internal static ProbeVolumeAsset CreateAsset(int id = -1) return asset; } - internal bool IsDataAssigned() - { - return payload.dataSH != null; - } - protected internal static Vector3Int[] s_Offsets = new Vector3Int[] { // middle slice new Vector3Int( 1, 0, 0), From a8b14d44e2460c38ea65ad313c433ae2a0811cdc Mon Sep 17 00:00:00 2001 From: pastasfuture Date: Sun, 31 May 2020 22:22:26 -0700 Subject: [PATCH 09/26] Probe Volumes: Accumulate SH coefficients, and evaluate SH at normal after accumulation. This allows us to avoid 2x accumulations, 1 for forward normal, and 1 for backward (transmission) normal. Light loop mode is tooled up to do single evaluation. Material Pass mode still accumulates twice, because SampleBakedGI() is called twice. Need to modify SampleBakedGI() signature to take both normals and internally accumulate front and back lighting. --- .../Runtime/Lighting/LightLoop/LightLoop.hlsl | 17 +-- .../Lighting/ProbeVolume/ProbeVolume.hlsl | 135 +++++++++++------- .../Runtime/Material/BuiltinGIUtilities.hlsl | 8 +- 3 files changed, 97 insertions(+), 63 deletions(-) diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.hlsl index dde5ace4607..e6314068c00 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.hlsl @@ -487,19 +487,16 @@ void LightLoop( float3 V, PositionInputs posInput, PreLightData preLightData, BS BuiltinData builtinDataProbeVolumes; ZERO_INITIALIZE(BuiltinData, builtinDataProbeVolumes); - // For now, to match what we are doing in material pass evaluation, we simply call evaluate twice. - // Once for the front face, and once for the back face. - // This makes supporting transmission simple, and this support was especially important for supporting the fallback path with ambient probe. - // An alternative to calling evaluate twice (and looping over the probe data twice), would be to loop over the data once, but accumulate front face and backface values. - // Another alternative would be to accumulate + blend raw SH data, and then evaluate for both the front facing and backfacing BSDF outside of the probe volume loop. - // We should compare these techniques in our next round of profiling work. - float probeVolumeHierarchyWeightFrontFace = uninitialized ? 0.0f : 1.0f; - float probeVolumeHierarchyWeightBackFace = uninitialized ? 0.0f : 1.0f; + float probeVolumeHierarchyWeight = uninitialized ? 0.0f : 1.0f; // Note: we aren't suppose to access normalWS in lightloop, but bsdfData.normalWS is always define for any material. So this is safe. - builtinDataProbeVolumes.bakeDiffuseLighting = EvaluateProbeVolumes(posInput, bsdfData.normalWS, builtinData.renderingLayers, probeVolumeHierarchyWeightFrontFace); - builtinDataProbeVolumes.backBakeDiffuseLighting = EvaluateProbeVolumes(posInput, -bsdfData.normalWS, builtinData.renderingLayers, probeVolumeHierarchyWeightBackFace); + ProbeVolumeCoefficients coefficients; + EvaluateProbeVolumes(posInput, bsdfData.normalWS, builtinData.renderingLayers, coefficients, probeVolumeHierarchyWeight); + builtinDataProbeVolumes.bakeDiffuseLighting += EvaluateProbeVolumeCoefficients(bsdfData.normalWS, coefficients); + builtinDataProbeVolumes.backBakeDiffuseLighting += EvaluateProbeVolumeCoefficients(-bsdfData.normalWS, coefficients); + float probeVolumeHierarchyWeightFrontFace = probeVolumeHierarchyWeight; + float probeVolumeHierarchyWeightBackFace = probeVolumeHierarchyWeight; builtinDataProbeVolumes.bakeDiffuseLighting += EvaluateProbeVolumeAmbientProbeFallback(bsdfData.normalWS, probeVolumeHierarchyWeightFrontFace); builtinDataProbeVolumes.backBakeDiffuseLighting += EvaluateProbeVolumeAmbientProbeFallback(-bsdfData.normalWS, probeVolumeHierarchyWeightBackFace); diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl index 03b32850235..87779a1f721 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl @@ -152,10 +152,40 @@ void EvaluateProbeVolumeOctahedralDepthOcclusionFilterWeights( } } -float3 EvaluateProbeVolumes(PositionInputs posInput, float3 normalWS, uint renderingLayers, inout float weightHierarchy) +struct ProbeVolumeCoefficients { +#if SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L0 + float4 data[(3u + 3u) / 4u]; +#elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L1 + float4 data[(12u + 3u) / 4u]; +#elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L2 + float4 data[(27u + 3u) / 4u]; +#endif +}; + +void EvaluateProbeVolumes(PositionInputs posInput, float3 normalWS, uint renderingLayers, out ProbeVolumeCoefficients coefficients, inout float weightHierarchy) +{ +#if SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L0 + coefficients.data[0] = float4(0.0, 0.0, 0.0, 0.0); + +#elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L1 + coefficients.data[0] = float4(0.0, 0.0, 0.0, 0.0); + coefficients.data[1] = float4(0.0, 0.0, 0.0, 0.0); + coefficients.data[2] = float4(0.0, 0.0, 0.0, 0.0); + +#elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L2 + coefficients.data[0] = float4(0.0, 0.0, 0.0, 0.0); + coefficients.data[1] = float4(0.0, 0.0, 0.0, 0.0); + coefficients.data[2] = float4(0.0, 0.0, 0.0, 0.0); + coefficients.data[3] = float4(0.0, 0.0, 0.0, 0.0); + coefficients.data[4] = float4(0.0, 0.0, 0.0, 0.0); + coefficients.data[5] = float4(0.0, 0.0, 0.0, 0.0); + coefficients.data[6] = float4(0.0, 0.0, 0.0, 0.0); + +#endif + #if !SHADEROPTIONS_PROBE_VOLUMES_ADDITIVE_BLENDING - if (weightHierarchy >= 1.0) { return float3(0.0, 0.0, 0.0); } + if (weightHierarchy >= 1.0) { return; } #endif float3 probeVolumeDiffuseLighting = float3(0.0, 0.0, 0.0); @@ -244,7 +274,7 @@ float3 EvaluateProbeVolumes(PositionInputs posInput, float3 normalWS, uint rende #endif ) { - return probeVolumeDiffuseLighting; + return; } #endif @@ -266,15 +296,6 @@ float3 EvaluateProbeVolumes(PositionInputs posInput, float3 normalWS, uint rende if (!IsMatchingLightLayer(s_probeVolumeData.lightLayers, renderingLayers)) { continue; } float weightCurrent = 0.0; - -#if SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L0 - const uint SH_STRIDE = 3u; -#elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L1 - const uint SH_STRIDE = 12u; -#elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L2 - const uint SH_STRIDE = 27u; -#endif - float4 sampleDataSH[(SH_STRIDE + 3u) / 4u]; { float3x3 obbFrame = float3x3(s_probeVolumeBounds.right, s_probeVolumeBounds.up, cross(s_probeVolumeBounds.right, s_probeVolumeBounds.up)); float3 obbExtents = float3(s_probeVolumeBounds.extentX, s_probeVolumeBounds.extentY, s_probeVolumeBounds.extentZ); @@ -464,72 +485,82 @@ float3 EvaluateProbeVolumes(PositionInputs posInput, float3 normalWS, uint rende float3 probeVolumeAtlasUVW = probeVolumeTexel3D * s_probeVolumeData.resolutionInverse * s_probeVolumeData.scale + s_probeVolumeData.bias; #ifdef DEBUG_DISPLAY - if (_DebugProbeVolumeMode == PROBEVOLUMEDEBUGMODE_VISUALIZE_VALIDITY) + if (_DebugProbeVolumeMode == PROBEVOLUMEDEBUGMODE_VISUALIZE_DEBUG_COLORS) + { + // Pack debug color into SH data so that we can access it later for our debug mode. + coefficients.data[0].xyz += s_probeVolumeData.debugColor * weightCurrent; + } + else if (_DebugProbeVolumeMode == PROBEVOLUMEDEBUGMODE_VISUALIZE_VALIDITY) { float validity = ProbeVolumeSampleValidity(probeVolumeAtlasUVW); // Pack validity into SH data so that we can access it later for our debug mode. - sampleDataSH[0] = float4(validity, 0.0, 0.0, 0.0); + coefficients.data[0].x += validity * weightCurrent; } else #endif { #if SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L0 - sampleDataSH[0] = SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 0), 0); + coefficients.data[0] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 0), 0) * weightCurrent; #elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L1 - sampleDataSH[0] = SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 0), 0); - sampleDataSH[1] = SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 1), 0); - sampleDataSH[2] = SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 2), 0); + coefficients.data[0] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 0), 0) * weightCurrent; + coefficients.data[1] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 1), 0) * weightCurrent; + coefficients.data[2] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 2), 0) * weightCurrent; #elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L2 - sampleDataSH[0] = SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 0), 0); - sampleDataSH[1] = SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 1), 0); - sampleDataSH[2] = SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 2), 0); - sampleDataSH[3] = SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 3), 0); - sampleDataSH[4] = SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 4), 0); - sampleDataSH[5] = SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 5), 0); - sampleDataSH[6] = SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 6), 0); + coefficients.data[0] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 0), 0) * weightCurrent; + coefficients.data[1] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 1), 0) * weightCurrent; + coefficients.data[2] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 2), 0) * weightCurrent; + coefficients.data[3] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 3), 0) * weightCurrent; + coefficients.data[4] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 4), 0) * weightCurrent; + coefficients.data[5] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 5), 0) * weightCurrent; + coefficients.data[6] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 6), 0) * weightCurrent; #endif } } - // When probe volumes are evaluated in the material pass, BSDF modulation is applied as a post operation, outside of this function. -#if SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L0 - float3 sampleOutgoingRadiance = sampleDataSH[0].rgb; + if (isWeightAccumulated) + weightHierarchy += weightCurrent; + } + } -#elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L1 - float3 sampleOutgoingRadiance = SHEvalLinearL0L1(normalWS, sampleDataSH[0], sampleDataSH[1], sampleDataSH[2]); + } +} -#elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L2 - float3 sampleOutgoingRadiance = SampleSH9(sampleDataSH, normalWS); -#endif +float3 EvaluateProbeVolumeCoefficients(float3 normalWS, ProbeVolumeCoefficients coefficients) +{ #ifdef DEBUG_DISPLAY - if (_DebugProbeVolumeMode == PROBEVOLUMEDEBUGMODE_VISUALIZE_DEBUG_COLORS) - { - probeVolumeDiffuseLighting += s_probeVolumeData.debugColor * weightCurrent; - } - else if (_DebugProbeVolumeMode == PROBEVOLUMEDEBUGMODE_VISUALIZE_VALIDITY) - { - float validity = sampleDataSH[0].x; - probeVolumeDiffuseLighting += lerp(float3(1, 0, 0), float3(0, 1, 0), validity) * weightCurrent; - } - else + if (_DebugProbeVolumeMode == PROBEVOLUMEDEBUGMODE_VISUALIZE_DEBUG_COLORS) + { + float3 debugColors = coefficients.data[0].rgb; + return debugColors; + } + else if (_DebugProbeVolumeMode == PROBEVOLUMEDEBUGMODE_VISUALIZE_VALIDITY) + { + float validity = coefficients.data[0].x; + return lerp(float3(1, 0, 0), float3(0, 1, 0), validity); + } + else #endif - { - probeVolumeDiffuseLighting += sampleOutgoingRadiance * weightCurrent; - } + { + // When probe volumes are evaluated in the material pass, BSDF modulation is applied as a post operation, outside of this function. +#if SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L0 + float3 sampleOutgoingRadiance = coefficients.data[0].rgb; - if (isWeightAccumulated) - weightHierarchy += weightCurrent; - } - } +#elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L1 + float3 sampleOutgoingRadiance = SHEvalLinearL0L1(normalWS, coefficients.data[0], coefficients.data[1], coefficients.data[2]); - } +#elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L2 + float3 sampleOutgoingRadiance = SampleSH9(coefficients, normalWS); +#else + float3 sampleOutgoingRadiance = 0.0; +#endif - return probeVolumeDiffuseLighting; + return sampleOutgoingRadiance; + } } // Fallback to global ambient probe lighting when probe volume lighting weight is not fully saturated. diff --git a/com.unity.render-pipelines.high-definition/Runtime/Material/BuiltinGIUtilities.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Material/BuiltinGIUtilities.hlsl index 75b944d37e5..05ee97d0f4d 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Material/BuiltinGIUtilities.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/Material/BuiltinGIUtilities.hlsl @@ -147,7 +147,13 @@ void SampleBakedGI( posInputs.tileCoord = tileCoord; #endif - combinedGI += EvaluateProbeVolumes(posInputs, normalWS, renderingLayers, probeVolumeHierarchyWeight); + // TODO: SampleBakedGI() will be called twice, once for the front facing direction, and once for the back facing direction (for transmission). + // For Probe Volumes specifically, this is not necessary - it would be better to return the coefficients, and just evaluate those coefficients twice (once for for each normal). + // We already do this in LightLoop evaluation mode. + // This would require the caller to track whether or not it needs to be called a second time. + ProbeVolumeCoefficients coefficients; + EvaluateProbeVolumes(posInputs, normalWS, renderingLayers, coefficients, probeVolumeHierarchyWeight); + combinedGI += EvaluateProbeVolumeCoefficients(normalWS, coefficients); combinedGI += EvaluateProbeVolumeAmbientProbeFallback(normalWS, probeVolumeHierarchyWeight); #endif From 58c13fa8b44816055adca75dde8a9e5e9e587864 Mon Sep 17 00:00:00 2001 From: pastasfuture Date: Sun, 31 May 2020 22:35:00 -0700 Subject: [PATCH 10/26] Probe Volumes: Rename EvaluateProbeVolumes() to AccumulateProbeVolumes() to make it clear that evaluation happens in a following EvaluateProbeVolumeCoefficients() function. --- .../Runtime/Lighting/LightLoop/LightLoop.hlsl | 2 +- .../Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl | 2 +- .../Runtime/Material/BuiltinGIUtilities.hlsl | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.hlsl index e6314068c00..a8b025480c9 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.hlsl @@ -491,7 +491,7 @@ void LightLoop( float3 V, PositionInputs posInput, PreLightData preLightData, BS // Note: we aren't suppose to access normalWS in lightloop, but bsdfData.normalWS is always define for any material. So this is safe. ProbeVolumeCoefficients coefficients; - EvaluateProbeVolumes(posInput, bsdfData.normalWS, builtinData.renderingLayers, coefficients, probeVolumeHierarchyWeight); + AccumulateProbeVolumes(posInput, bsdfData.normalWS, builtinData.renderingLayers, coefficients, probeVolumeHierarchyWeight); builtinDataProbeVolumes.bakeDiffuseLighting += EvaluateProbeVolumeCoefficients(bsdfData.normalWS, coefficients); builtinDataProbeVolumes.backBakeDiffuseLighting += EvaluateProbeVolumeCoefficients(-bsdfData.normalWS, coefficients); diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl index 87779a1f721..7a5b1bde012 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl @@ -163,7 +163,7 @@ struct ProbeVolumeCoefficients #endif }; -void EvaluateProbeVolumes(PositionInputs posInput, float3 normalWS, uint renderingLayers, out ProbeVolumeCoefficients coefficients, inout float weightHierarchy) +void AccumulateProbeVolumes(PositionInputs posInput, float3 normalWS, uint renderingLayers, out ProbeVolumeCoefficients coefficients, inout float weightHierarchy) { #if SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L0 coefficients.data[0] = float4(0.0, 0.0, 0.0, 0.0); diff --git a/com.unity.render-pipelines.high-definition/Runtime/Material/BuiltinGIUtilities.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Material/BuiltinGIUtilities.hlsl index 05ee97d0f4d..edf3bf5b297 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Material/BuiltinGIUtilities.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/Material/BuiltinGIUtilities.hlsl @@ -152,7 +152,7 @@ void SampleBakedGI( // We already do this in LightLoop evaluation mode. // This would require the caller to track whether or not it needs to be called a second time. ProbeVolumeCoefficients coefficients; - EvaluateProbeVolumes(posInputs, normalWS, renderingLayers, coefficients, probeVolumeHierarchyWeight); + AccumulateProbeVolumes(posInputs, normalWS, renderingLayers, coefficients, probeVolumeHierarchyWeight); combinedGI += EvaluateProbeVolumeCoefficients(normalWS, coefficients); combinedGI += EvaluateProbeVolumeAmbientProbeFallback(normalWS, probeVolumeHierarchyWeight); #endif From 10b36c89d6d7af4d6bee0936835b89e42d31f574 Mon Sep 17 00:00:00 2001 From: pastasfuture Date: Mon, 1 Jun 2020 14:09:16 -0700 Subject: [PATCH 11/26] Probe Volumes: Major cleanup to AccumulateProbeVolumes(), pulling out the majority of the logic into self contained supporting functions. --- .../Runtime/Lighting/LightLoop/LightLoop.cs | 6 +- .../Runtime/Lighting/LightLoop/LightLoop.hlsl | 4 +- .../Lighting/ProbeVolume/ProbeVolume.hlsl | 621 +++++++++--------- .../ProbeVolume/ProbeVolumeLightLoopDef.hlsl | 102 ++- .../Runtime/Material/BuiltinGIUtilities.hlsl | 39 +- 5 files changed, 417 insertions(+), 355 deletions(-) diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs index e69714a4113..09bc7e290f3 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs @@ -3007,7 +3007,11 @@ static void BuildPerTileLightList(in BuildGPULightListParameters parameters, in if (parameters.probeVolumeEnabled) { - // TODO: Verify that we should be globally enabling ProbeVolume feature for all tiles here, or if we should be using per-tile culling. + // If probe volume feature is enabled, we toggle this feature on for all tiles. + // This is necessary because all tiles must sample ambient probe fallback. + // It is possible we could save a little bit of work by having 2x feature flags for probe volumes: + // one specifiying which tiles contain probe volumes, + // and another triggered for all tiles to handle fallback. baseFeatureFlags |= (uint)LightFeatureFlags.ProbeVolume; } diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.hlsl index a8b025480c9..216193fa845 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.hlsl @@ -476,6 +476,8 @@ void LightLoop( float3 V, PositionInputs posInput, PreLightData preLightData, BS bool uninitialized = IsUninitializedGI(builtinData.bakeDiffuseLighting); builtinData.bakeDiffuseLighting = uninitialized ? float3(0.0, 0.0, 0.0) : builtinData.bakeDiffuseLighting; + // If probe volume feature is enabled, this bit is enabled for all tiles to handle ambient probe fallback. + // No need to branch internally on _EnableProbeVolumes uniform. if (featureFlags & LIGHTFEATUREFLAGS_PROBE_VOLUME) { #if !SHADEROPTIONS_PROBE_VOLUMES_ADDITIVE_BLENDING @@ -494,7 +496,7 @@ void LightLoop( float3 V, PositionInputs posInput, PreLightData preLightData, BS AccumulateProbeVolumes(posInput, bsdfData.normalWS, builtinData.renderingLayers, coefficients, probeVolumeHierarchyWeight); builtinDataProbeVolumes.bakeDiffuseLighting += EvaluateProbeVolumeCoefficients(bsdfData.normalWS, coefficients); builtinDataProbeVolumes.backBakeDiffuseLighting += EvaluateProbeVolumeCoefficients(-bsdfData.normalWS, coefficients); - + float probeVolumeHierarchyWeightFrontFace = probeVolumeHierarchyWeight; float probeVolumeHierarchyWeightBackFace = probeVolumeHierarchyWeight; builtinDataProbeVolumes.bakeDiffuseLighting += EvaluateProbeVolumeAmbientProbeFallback(bsdfData.normalWS, probeVolumeHierarchyWeightFrontFace); diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl index 7a5b1bde012..53fa399c709 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl @@ -152,6 +152,213 @@ void EvaluateProbeVolumeOctahedralDepthOcclusionFilterWeights( } } +void ProbeVolumeComputeOBBBoundsToFrame(OrientedBBox probeVolumeBounds, out float3x3 obbFrame, out float3 obbExtents, out float3 obbCenter) +{ + obbFrame = float3x3(probeVolumeBounds.right, probeVolumeBounds.up, cross(probeVolumeBounds.right, probeVolumeBounds.up)); + obbExtents = float3(probeVolumeBounds.extentX, probeVolumeBounds.extentY, probeVolumeBounds.extentZ); + obbCenter = probeVolumeBounds.center; +} + + +void ProbeVolumeComputeTexel3DAndWeight( + float weightHierarchy, + ProbeVolumeEngineData probeVolumeData, + float3x3 obbFrame, + float3 obbExtents, + float3 obbCenter, + float3 samplePositionWS, + float samplePositionLinearDepth, + out float3 probeVolumeTexel3D, + out float weight) +{ + float3 samplePositionBS = mul(obbFrame, samplePositionWS - obbCenter); + float3 samplePositionBCS = samplePositionBS * rcp(obbExtents); + float3 samplePositionBNDC = samplePositionBCS * 0.5 + 0.5; + float3 probeVolumeUVW = clamp(samplePositionBNDC.xyz, 0.5 * probeVolumeData.resolutionInverse, 1.0 - probeVolumeData.resolutionInverse * 0.5); + probeVolumeTexel3D = probeVolumeUVW * probeVolumeData.resolution; + + float fadeFactor = ProbeVolumeComputeFadeFactor( + samplePositionBNDC, + samplePositionLinearDepth, + probeVolumeData.rcpPosFaceFade, + probeVolumeData.rcpNegFaceFade, + probeVolumeData.rcpDistFadeLen, + probeVolumeData.endTimesRcpDistFadeLen + ); + + weight = fadeFactor * probeVolumeData.weight; + +#if SHADEROPTIONS_PROBE_VOLUMES_ADDITIVE_BLENDING + if (probeVolumeData.volumeBlendMode == VOLUMEBLENDMODE_ADDITIVE) + weight = fadeFactor; + else if (probeVolumeData.volumeBlendMode == VOLUMEBLENDMODE_SUBTRACTIVE) + weight = -fadeFactor; + else +#endif + { + // Alpha composite: weight = (1.0f - weightHierarchy) * fadeFactor; + weight = weightHierarchy * -fadeFactor + fadeFactor; + } +} + +float3 ProbeVolumeComputeTexel3DFromBilateralFilter( + float3 probeVolumeTexel3D, + ProbeVolumeEngineData probeVolumeData, + float3 positionUnbiasedWS, + float3 positionBiasedWS, + float3 normalWS, + float3x3 obbFrame, + float3 obbExtents, + float3 obbCenter) +{ + if (_ProbeVolumeLeakMitigationMode == LEAKMITIGATIONMODE_NORMAL_BIAS) { return probeVolumeTexel3D; } + + float3 probeVolumeTexel3DMin = floor(probeVolumeTexel3D - 0.5) + 0.5; + + float probeWeightBSW = 1.0; + float probeWeightBSE = 1.0; + float probeWeightBNW = 1.0; + float probeWeightBNE = 1.0; + float probeWeightTSW = 1.0; + float probeWeightTSE = 1.0; + float probeWeightTNW = 1.0; + float probeWeightTNE = 1.0; + if (_ProbeVolumeLeakMitigationMode == LEAKMITIGATIONMODE_GEOMETRIC_FILTER) + { + // Compute Geometric Weights based on surface position + normal, and direction to probe (similar to projected area calculation for point lights). + // source: https://advances.realtimerendering.com/s2015/SIGGRAPH_2015_Remedy_Notes.pdf + probeWeightBSW = max(_ProbeVolumeBilateralFilterWeightMin, saturate(dot(normalWS, normalize(float3(probeVolumeTexel3DMin.x + 0.0, probeVolumeTexel3DMin.y + 0.0, probeVolumeTexel3DMin.z + 0.0) - probeVolumeTexel3D)))); + probeWeightBSE = max(_ProbeVolumeBilateralFilterWeightMin, saturate(dot(normalWS, normalize(float3(probeVolumeTexel3DMin.x + 1.0, probeVolumeTexel3DMin.y + 0.0, probeVolumeTexel3DMin.z + 0.0) - probeVolumeTexel3D)))); + probeWeightBNW = max(_ProbeVolumeBilateralFilterWeightMin, saturate(dot(normalWS, normalize(float3(probeVolumeTexel3DMin.x + 0.0, probeVolumeTexel3DMin.y + 0.0, probeVolumeTexel3DMin.z + 1.0) - probeVolumeTexel3D)))); + probeWeightBNE = max(_ProbeVolumeBilateralFilterWeightMin, saturate(dot(normalWS, normalize(float3(probeVolumeTexel3DMin.x + 1.0, probeVolumeTexel3DMin.y + 0.0, probeVolumeTexel3DMin.z + 1.0) - probeVolumeTexel3D)))); + + probeWeightTSW = max(_ProbeVolumeBilateralFilterWeightMin, saturate(dot(normalWS, normalize(float3(probeVolumeTexel3DMin.x + 0.0, probeVolumeTexel3DMin.y + 1.0, probeVolumeTexel3DMin.z + 0.0) - probeVolumeTexel3D)))); + probeWeightTSE = max(_ProbeVolumeBilateralFilterWeightMin, saturate(dot(normalWS, normalize(float3(probeVolumeTexel3DMin.x + 1.0, probeVolumeTexel3DMin.y + 1.0, probeVolumeTexel3DMin.z + 0.0) - probeVolumeTexel3D)))); + probeWeightTNW = max(_ProbeVolumeBilateralFilterWeightMin, saturate(dot(normalWS, normalize(float3(probeVolumeTexel3DMin.x + 0.0, probeVolumeTexel3DMin.y + 1.0, probeVolumeTexel3DMin.z + 1.0) - probeVolumeTexel3D)))); + probeWeightTNE = max(_ProbeVolumeBilateralFilterWeightMin, saturate(dot(normalWS, normalize(float3(probeVolumeTexel3DMin.x + 1.0, probeVolumeTexel3DMin.y + 1.0, probeVolumeTexel3DMin.z + 1.0) - probeVolumeTexel3D)))); + } + else if (_ProbeVolumeLeakMitigationMode == LEAKMITIGATIONMODE_PROBE_VALIDITY_FILTER) + { + // TODO: Rather than sampling validity data from a slice in our texture array, we could place it in a different texture resource entirely. + // This would allow us to use a single channel format, rather than wasting memory with float4(validity, unused, unused, unused). + // It would also allow us to use a different texture format (i.e: 1x8bpp rather than 4x16bpp). + // Currently just using a texture slice for convenience, and with the idea that MAYBE we will end up using the remaining 3 channels. + probeWeightBSW = max(_ProbeVolumeBilateralFilterWeightMin, ProbeVolumeLoadValidity(int3(probeVolumeTexel3DMin.x + 0, probeVolumeTexel3DMin.y + 0, probeVolumeTexel3DMin.z + 0))); + probeWeightBSE = max(_ProbeVolumeBilateralFilterWeightMin, ProbeVolumeLoadValidity(int3(probeVolumeTexel3DMin.x + 1, probeVolumeTexel3DMin.y + 0, probeVolumeTexel3DMin.z + 0))); + probeWeightBNW = max(_ProbeVolumeBilateralFilterWeightMin, ProbeVolumeLoadValidity(int3(probeVolumeTexel3DMin.x + 0, probeVolumeTexel3DMin.y + 0, probeVolumeTexel3DMin.z + 1))); + probeWeightBNE = max(_ProbeVolumeBilateralFilterWeightMin, ProbeVolumeLoadValidity(int3(probeVolumeTexel3DMin.x + 1, probeVolumeTexel3DMin.y + 0, probeVolumeTexel3DMin.z + 1))); + + probeWeightTSW = max(_ProbeVolumeBilateralFilterWeightMin, ProbeVolumeLoadValidity(int3(probeVolumeTexel3DMin.x + 0, probeVolumeTexel3DMin.y + 1, probeVolumeTexel3DMin.z + 0))); + probeWeightTSE = max(_ProbeVolumeBilateralFilterWeightMin, ProbeVolumeLoadValidity(int3(probeVolumeTexel3DMin.x + 1, probeVolumeTexel3DMin.y + 1, probeVolumeTexel3DMin.z + 1))); + probeWeightTNW = max(_ProbeVolumeBilateralFilterWeightMin, ProbeVolumeLoadValidity(int3(probeVolumeTexel3DMin.x + 0, probeVolumeTexel3DMin.y + 1, probeVolumeTexel3DMin.z + 1))); + probeWeightTNE = max(_ProbeVolumeBilateralFilterWeightMin, ProbeVolumeLoadValidity(int3(probeVolumeTexel3DMin.x + 1, probeVolumeTexel3DMin.y + 1, probeVolumeTexel3DMin.z + 1))); + } + else if (_ProbeVolumeLeakMitigationMode == LEAKMITIGATIONMODE_OCTAHEDRAL_DEPTH_OCCLUSION_FILTER) + { + // TODO: Evaluate if we should we build this 3x3 matrix and a float3 bias term cpu side to decrease alu at the cost of more bandwidth. + float3 probeVolumeWorldFromTexel3DScale = probeVolumeData.resolutionInverse * 2.0 * obbExtents; // [0, resolution3D] to [0.0, probeVolumeSize3D] + float3x3 probeVolumeWorldFromTexel3DRotationScale = float3x3( + obbFrame[0] * probeVolumeWorldFromTexel3DScale, + obbFrame[1] * probeVolumeWorldFromTexel3DScale, + obbFrame[2] * probeVolumeWorldFromTexel3DScale + ); + float3 probeVolumeWorldFromTexel3DTranslation = mul(obbFrame, -obbExtents) + obbCenter; + + float probeWeights[8]; + EvaluateProbeVolumeOctahedralDepthOcclusionFilterWeights( + probeWeights, + probeVolumeTexel3DMin, + probeVolumeData.resolution, + probeVolumeWorldFromTexel3DRotationScale, + probeVolumeWorldFromTexel3DTranslation, + probeVolumeData.octahedralDepthScaleBias, + _ProbeVolumeAtlasOctahedralDepthResolutionAndInverse, + positionUnbiasedWS, + positionBiasedWS, + normalWS + ); + probeWeightBSW = probeWeights[0]; // (i == 0) => (int3(i, i >> 1, i >> 2) & int3(1, 1, 1)) => (int3(0, 0 >> 1, 0 >> 2) & int3(1, 1, 1)) => int3(0, 0, 0) + probeWeightBSE = probeWeights[1]; // (i == 1) => (int3(i, i >> 1, i >> 2) & int3(1, 1, 1)) => (int3(1, 1 >> 1, 1 >> 2) & int3(1, 1, 1)) => int3(1, 0, 0) + probeWeightBNW = probeWeights[2]; // (i == 2) => (int3(i, i >> 1, i >> 2) & int3(1, 1, 1)) => (int3(2, 2 >> 1, 2 >> 2) & int3(1, 1, 1)) => int3(0, 1, 0) + probeWeightBNE = probeWeights[3]; // (i == 3) => (int3(i, i >> 1, i >> 2) & int3(1, 1, 1)) => (int3(3, 3 >> 1, 3 >> 2) & int3(1, 1, 1)) => int3(1, 1, 0) + + probeWeightTSW = probeWeights[4]; // (i == 4) => (int3(i, i >> 1, i >> 2) & int3(1, 1, 1)) => (int3(4, 4 >> 1, 4 >> 2) & int3(1, 1, 1)) => int3(0, 0, 1) + probeWeightTSE = probeWeights[5]; // (i == 5) => (int3(i, i >> 1, i >> 2) & int3(1, 1, 1)) => (int3(5, 5 >> 1, 5 >> 2) & int3(1, 1, 1)) => int3(1, 0, 1) + probeWeightTNW = probeWeights[6]; // (i == 6) => (int3(i, i >> 1, i >> 2) & int3(1, 1, 1)) => (int3(6, 6 >> 1, 6 >> 2) & int3(1, 1, 1)) => int3(0, 1, 1) + probeWeightTNE = probeWeights[7]; // (i == 7) => (int3(i, i >> 1, i >> 2) & int3(1, 1, 1)) => (int3(7, 7 >> 1, 7 >> 2) & int3(1, 1, 1)) => int3(1, 1, 1) + } + + // Blend between Geometric Weights and simple trilinear filter weights based on user defined _ProbeVolumeBilateralFilterWeight. + { + float3 probeWeightTrilinearMax = frac(probeVolumeTexel3D - 0.5); + float3 probeWeightTrilinearMin = 1.0 - probeWeightTrilinearMax; + + float probeWeightTrilinearBSW = probeWeightTrilinearMin.x * probeWeightTrilinearMin.y * probeWeightTrilinearMin.z; + float probeWeightTrilinearBSE = probeWeightTrilinearMax.x * probeWeightTrilinearMin.y * probeWeightTrilinearMin.z; + float probeWeightTrilinearBNW = probeWeightTrilinearMin.x * probeWeightTrilinearMin.y * probeWeightTrilinearMax.z; + float probeWeightTrilinearBNE = probeWeightTrilinearMax.x * probeWeightTrilinearMin.y * probeWeightTrilinearMax.z; + float probeWeightTrilinearTSW = probeWeightTrilinearMin.x * probeWeightTrilinearMax.y * probeWeightTrilinearMin.z; + float probeWeightTrilinearTSE = probeWeightTrilinearMax.x * probeWeightTrilinearMax.y * probeWeightTrilinearMin.z; + float probeWeightTrilinearTNW = probeWeightTrilinearMin.x * probeWeightTrilinearMax.y * probeWeightTrilinearMax.z; + float probeWeightTrilinearTNE = probeWeightTrilinearMax.x * probeWeightTrilinearMax.y * probeWeightTrilinearMax.z; + + probeWeightBSW = lerp(probeWeightTrilinearBSW, probeWeightTrilinearBSW * probeWeightBSW, _ProbeVolumeBilateralFilterWeight); + probeWeightBSE = lerp(probeWeightTrilinearBSE, probeWeightTrilinearBSE * probeWeightBSE, _ProbeVolumeBilateralFilterWeight); + probeWeightBNW = lerp(probeWeightTrilinearBNW, probeWeightTrilinearBNW * probeWeightBNW, _ProbeVolumeBilateralFilterWeight); + probeWeightBNE = lerp(probeWeightTrilinearBNE, probeWeightTrilinearBNE * probeWeightBNE, _ProbeVolumeBilateralFilterWeight); + + probeWeightTSW = lerp(probeWeightTrilinearTSW, probeWeightTrilinearTSW * probeWeightTSW, _ProbeVolumeBilateralFilterWeight); + probeWeightTSE = lerp(probeWeightTrilinearTSE, probeWeightTrilinearTSE * probeWeightTSE, _ProbeVolumeBilateralFilterWeight); + probeWeightTNW = lerp(probeWeightTrilinearTNW, probeWeightTrilinearTNW * probeWeightTNW, _ProbeVolumeBilateralFilterWeight); + probeWeightTNE = lerp(probeWeightTrilinearTNE, probeWeightTrilinearTNE * probeWeightTNE, _ProbeVolumeBilateralFilterWeight); + } + + float probeWeightTotal = + probeWeightBSW + + probeWeightBSE + + probeWeightBNW + + probeWeightBNE + + probeWeightTSW + + probeWeightTSE + + probeWeightTNW + + probeWeightTNE; + + // Weights are enforced to be > 0.0 to guard against divide by zero. + float probeWeightNormalization = 1.0 / probeWeightTotal; + + probeWeightBSW *= probeWeightNormalization; + probeWeightBSE *= probeWeightNormalization; + probeWeightBNW *= probeWeightNormalization; + probeWeightBNE *= probeWeightNormalization; + probeWeightTSW *= probeWeightNormalization; + probeWeightTSE *= probeWeightNormalization; + probeWeightTNW *= probeWeightNormalization; + probeWeightTNE *= probeWeightNormalization; + + // Finally, update our texture coordinate based on our weights. + // Half-texel offset has been baked into the coordinates. + float3 probeVolumeTexel3DFrac = + float3(0.5, 0.5, 0.5) * probeWeightBSW + + float3(1.5, 0.5, 0.5) * probeWeightBSE + + float3(0.5, 0.5, 1.5) * probeWeightBNW + + float3(1.5, 0.5, 1.5) * probeWeightBNE + + float3(0.5, 1.5, 0.5) * probeWeightTSW + + float3(1.5, 1.5, 0.5) * probeWeightTSE + + float3(0.5, 1.5, 1.5) * probeWeightTNW + + float3(1.5, 1.5, 1.5) * probeWeightTNE; + +#ifdef DEBUG_DISPLAY + // If we are visualizing validity data, we do not want to apply our bilateral filter texture coordinate modification + // because ideally, our filter will avoid sampling from invalid data - making this debug mode useless. + if (_DebugProbeVolumeMode != PROBEVOLUMEDEBUGMODE_VISUALIZE_VALIDITY) +#endif + { + probeVolumeTexel3D = floor(probeVolumeTexel3D - 0.5) + probeVolumeTexel3DFrac; + } + + return probeVolumeTexel3D; +} + struct ProbeVolumeCoefficients { #if SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L0 @@ -165,367 +372,125 @@ struct ProbeVolumeCoefficients void AccumulateProbeVolumes(PositionInputs posInput, float3 normalWS, uint renderingLayers, out ProbeVolumeCoefficients coefficients, inout float weightHierarchy) { -#if SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L0 - coefficients.data[0] = float4(0.0, 0.0, 0.0, 0.0); - -#elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L1 - coefficients.data[0] = float4(0.0, 0.0, 0.0, 0.0); - coefficients.data[1] = float4(0.0, 0.0, 0.0, 0.0); - coefficients.data[2] = float4(0.0, 0.0, 0.0, 0.0); - -#elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L2 - coefficients.data[0] = float4(0.0, 0.0, 0.0, 0.0); - coefficients.data[1] = float4(0.0, 0.0, 0.0, 0.0); - coefficients.data[2] = float4(0.0, 0.0, 0.0, 0.0); - coefficients.data[3] = float4(0.0, 0.0, 0.0, 0.0); - coefficients.data[4] = float4(0.0, 0.0, 0.0, 0.0); - coefficients.data[5] = float4(0.0, 0.0, 0.0, 0.0); - coefficients.data[6] = float4(0.0, 0.0, 0.0, 0.0); - -#endif + ZERO_INITIALIZE(ProbeVolumeCoefficients, coefficients); #if !SHADEROPTIONS_PROBE_VOLUMES_ADDITIVE_BLENDING if (weightHierarchy >= 1.0) { return; } #endif - float3 probeVolumeDiffuseLighting = float3(0.0, 0.0, 0.0); float3 positionRWS = posInput.positionWS; float positionLinearDepth = posInput.linearDepth; - - if (_EnableProbeVolumes) + uint probeVolumeStart, probeVolumeCount; + bool fastPath; + ProbeVolumeGetCountAndStartAndFastPath(posInput, probeVolumeStart, probeVolumeCount, fastPath); + + // Scalarized loop, same rationale of the punctual light version + uint v_probeVolumeListOffset = 0; + uint v_probeVolumeIdx = probeVolumeStart; + while (v_probeVolumeListOffset < probeVolumeCount) { + v_probeVolumeIdx = ProbeVolumeFetchIndex(probeVolumeStart, v_probeVolumeListOffset); + uint s_probeVolumeIdx = ProbeVolumeScalarizeElementIndex(v_probeVolumeIdx, fastPath); + if (s_probeVolumeIdx == -1) { break; } - uint probeVolumeStart, probeVolumeCount; - - bool fastPath = false; - // Fetch first probe volume to provide the scene proxy for screen space computation -#ifndef LIGHTLOOP_DISABLE_TILE_AND_CLUSTER -#if SHADEROPTIONS_PROBE_VOLUMES_EVALUATION_MODE == PROBEVOLUMESEVALUATIONMODES_MATERIAL_PASS - // Access probe volume data from custom probe volume light list data structure. - ProbeVolumeGetCountAndStart(posInput, LIGHTCATEGORY_PROBE_VOLUME, probeVolumeStart, probeVolumeCount); -#else // #if SHADEROPTIONS_PROBE_VOLUMES_EVALUATION_MODE == PROBEVOLUMESEVALUATIONMODES_LIGHT_LOOP - // Access probe volume data from standard lightloop light list data structure. - GetCountAndStart(posInput, LIGHTCATEGORY_PROBE_VOLUME, probeVolumeStart, probeVolumeCount); -#endif + // Scalar load. + ProbeVolumeEngineData s_probeVolumeData = _ProbeVolumeDatas[s_probeVolumeIdx]; + OrientedBBox s_probeVolumeBounds = _ProbeVolumeBounds[s_probeVolumeIdx]; -#if SCALARIZE_LIGHT_LOOP - // Fast path is when we all pixels in a wave is accessing same tile or cluster. - uint probeVolumeStartFirstLane = WaveReadLaneFirst(probeVolumeStart); - fastPath = WaveActiveAllTrue(probeVolumeStart == probeVolumeStartFirstLane); -#endif + if (ProbeVolumeIsAllWavesComplete(weightHierarchy, s_probeVolumeData.volumeBlendMode)) { return; } -#else // LIGHTLOOP_DISABLE_TILE_AND_CLUSTER - probeVolumeCount = _ProbeVolumeCount; - probeVolumeStart = 0; -#endif - -#if SCALARIZE_LIGHT_LOOP - if (fastPath) - { - probeVolumeStart = probeVolumeStartFirstLane; - } -#endif - - // Scalarized loop, same rationale of the punctual light version - uint v_probeVolumeListOffset = 0; - uint v_probeVolumeIdx = probeVolumeStart; - while (v_probeVolumeListOffset < probeVolumeCount) + // If current scalar and vector light index match, we process the light. The v_probeVolumeListOffset for current thread is increased. + // Note that the following should really be ==, however, since helper lanes are not considered by WaveActiveMin, such helper lanes could + // end up with a unique v_envLightIdx value that is smaller than s_envLightIdx hence being stuck in a loop. All the active lanes will not have this problem. + if (s_probeVolumeIdx >= v_probeVolumeIdx) { - #if SHADEROPTIONS_PROBE_VOLUMES_EVALUATION_MODE == PROBEVOLUMESEVALUATIONMODES_MATERIAL_PASS - // Access probe volume data from custom probe volume light list data structure. - v_probeVolumeIdx = ProbeVolumeFetchIndex(probeVolumeStart, v_probeVolumeListOffset); - #else // #if SHADEROPTIONS_PROBE_VOLUMES_EVALUATION_MODE == PROBEVOLUMESEVALUATIONMODES_LIGHT_LOOP - // Access probe volume data from standard lightloop light list data structure. - v_probeVolumeIdx = FetchIndex(probeVolumeStart, v_probeVolumeListOffset); - #endif - - uint s_probeVolumeIdx = v_probeVolumeIdx; - -#if SCALARIZE_LIGHT_LOOP - if (!fastPath) - { - s_probeVolumeIdx = WaveActiveMin(v_probeVolumeIdx); - // If we are not in fast path, s_probeVolumeIdx is not scalar - // If WaveActiveMin returns 0xffffffff it means that all lanes are actually dead, so we can safely ignore the loop and move forward. - // This could happen as an helper lane could reach this point, hence having a valid v_lightIdx, but their values will be ignored by the WaveActiveMin - if (s_probeVolumeIdx == -1) - { - break; - } - } - // Note that the WaveReadLaneFirst should not be needed, but the compiler might insist in putting the result in VGPR. - // However, we are certain at this point that the index is scalar. - s_probeVolumeIdx = WaveReadLaneFirst(s_probeVolumeIdx); - -#endif - - // Scalar load. - ProbeVolumeEngineData s_probeVolumeData = _ProbeVolumeDatas[s_probeVolumeIdx]; - OrientedBBox s_probeVolumeBounds = _ProbeVolumeBounds[s_probeVolumeIdx]; - - // Probe volumes are sorted primarily by blend mode, and secondarily by size. - // 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 if our weightHierarchy reaches 1.0, since we know we will only ever process more VOLUMEBLENDMODE_NORMAL volumes, - // whos weight will always evaluate to zero. -#if defined(PLATFORM_SUPPORTS_WAVE_INTRINSICS) - if (WaveActiveMin(weightHierarchy) >= 1.0 -#if SHADEROPTIONS_PROBE_VOLUMES_ADDITIVE_BLENDING - && WaveActiveAllTrue(s_probeVolumeData.volumeBlendMode == VOLUMEBLENDMODE_NORMAL) -#endif - ) - { - return; - } -#endif - - // If current scalar and vector light index match, we process the light. The v_probeVolumeListOffset for current thread is increased. - // Note that the following should really be ==, however, since helper lanes are not considered by WaveActiveMin, such helper lanes could - // end up with a unique v_envLightIdx value that is smaller than s_envLightIdx hence being stuck in a loop. All the active lanes will not have this problem. - if (s_probeVolumeIdx >= v_probeVolumeIdx) - { - v_probeVolumeListOffset++; + v_probeVolumeListOffset++; #if SHADEROPTIONS_PROBE_VOLUMES_ADDITIVE_BLENDING - bool isWeightAccumulated = s_probeVolumeData.volumeBlendMode == VOLUMEBLENDMODE_NORMAL; + bool isWeightAccumulated = s_probeVolumeData.volumeBlendMode == VOLUMEBLENDMODE_NORMAL; #else - const bool isWeightAccumulated = true; + const bool isWeightAccumulated = true; #endif - if (weightHierarchy >= 1.0 && isWeightAccumulated) { continue; } + if (weightHierarchy >= 1.0 && isWeightAccumulated) { continue; } - if (!IsMatchingLightLayer(s_probeVolumeData.lightLayers, renderingLayers)) { continue; } + if (!IsMatchingLightLayer(s_probeVolumeData.lightLayers, renderingLayers)) { continue; } - float weightCurrent = 0.0; - { - float3x3 obbFrame = float3x3(s_probeVolumeBounds.right, s_probeVolumeBounds.up, cross(s_probeVolumeBounds.right, s_probeVolumeBounds.up)); - float3 obbExtents = float3(s_probeVolumeBounds.extentX, s_probeVolumeBounds.extentY, s_probeVolumeBounds.extentZ); - - // Note: When normal bias is > 0, bounds using in tile / cluster assignment are conservatively dilated CPU side to handle worst case normal bias. - float3 samplePositionWS = normalWS * s_probeVolumeData.normalBiasWS + positionRWS; - float3 samplePositionBS = mul(obbFrame, samplePositionWS - s_probeVolumeBounds.center); - float3 samplePositionBCS = samplePositionBS * rcp(obbExtents); - - float3 samplePositionBNDC = samplePositionBCS * 0.5 + 0.5; - - float fadeFactor = ProbeVolumeComputeFadeFactor( - samplePositionBNDC, - positionLinearDepth, - s_probeVolumeData.rcpPosFaceFade, - s_probeVolumeData.rcpNegFaceFade, - s_probeVolumeData.rcpDistFadeLen, - s_probeVolumeData.endTimesRcpDistFadeLen - ); - - fadeFactor *= s_probeVolumeData.weight; - -#if SHADEROPTIONS_PROBE_VOLUMES_ADDITIVE_BLENDING - if (s_probeVolumeData.volumeBlendMode == VOLUMEBLENDMODE_ADDITIVE) - weightCurrent = fadeFactor; - else if (s_probeVolumeData.volumeBlendMode == VOLUMEBLENDMODE_SUBTRACTIVE) - weightCurrent = -fadeFactor; - else -#endif - { - // Alpha composite: weight = (1.0f - weightHierarchy) * fadeFactor; - weightCurrent = weightHierarchy * -fadeFactor + fadeFactor; - } - - // TODO: Cleanup / optimize this math. - float3 probeVolumeUVW = clamp(samplePositionBNDC.xyz, 0.5 * s_probeVolumeData.resolutionInverse, 1.0 - s_probeVolumeData.resolutionInverse * 0.5); - float3 probeVolumeTexel3D = probeVolumeUVW * s_probeVolumeData.resolution; - - if (_ProbeVolumeLeakMitigationMode != LEAKMITIGATIONMODE_NORMAL_BIAS) - { - float3 probeVolumeTexel3DMin = floor(probeVolumeTexel3D - 0.5) + 0.5; - - float probeWeightBSW = 1.0; - float probeWeightBSE = 1.0; - float probeWeightBNW = 1.0; - float probeWeightBNE = 1.0; - float probeWeightTSW = 1.0; - float probeWeightTSE = 1.0; - float probeWeightTNW = 1.0; - float probeWeightTNE = 1.0; - if (_ProbeVolumeLeakMitigationMode == LEAKMITIGATIONMODE_GEOMETRIC_FILTER) - { - // Compute Geometric Weights based on surface position + normal, and direction to probe (similar to projected area calculation for point lights). - // source: https://advances.realtimerendering.com/s2015/SIGGRAPH_2015_Remedy_Notes.pdf - probeWeightBSW = max(_ProbeVolumeBilateralFilterWeightMin, saturate(dot(normalWS, normalize(float3(probeVolumeTexel3DMin.x + 0.0, probeVolumeTexel3DMin.y + 0.0, probeVolumeTexel3DMin.z + 0.0) - probeVolumeTexel3D)))); - probeWeightBSE = max(_ProbeVolumeBilateralFilterWeightMin, saturate(dot(normalWS, normalize(float3(probeVolumeTexel3DMin.x + 1.0, probeVolumeTexel3DMin.y + 0.0, probeVolumeTexel3DMin.z + 0.0) - probeVolumeTexel3D)))); - probeWeightBNW = max(_ProbeVolumeBilateralFilterWeightMin, saturate(dot(normalWS, normalize(float3(probeVolumeTexel3DMin.x + 0.0, probeVolumeTexel3DMin.y + 0.0, probeVolumeTexel3DMin.z + 1.0) - probeVolumeTexel3D)))); - probeWeightBNE = max(_ProbeVolumeBilateralFilterWeightMin, saturate(dot(normalWS, normalize(float3(probeVolumeTexel3DMin.x + 1.0, probeVolumeTexel3DMin.y + 0.0, probeVolumeTexel3DMin.z + 1.0) - probeVolumeTexel3D)))); - - probeWeightTSW = max(_ProbeVolumeBilateralFilterWeightMin, saturate(dot(normalWS, normalize(float3(probeVolumeTexel3DMin.x + 0.0, probeVolumeTexel3DMin.y + 1.0, probeVolumeTexel3DMin.z + 0.0) - probeVolumeTexel3D)))); - probeWeightTSE = max(_ProbeVolumeBilateralFilterWeightMin, saturate(dot(normalWS, normalize(float3(probeVolumeTexel3DMin.x + 1.0, probeVolumeTexel3DMin.y + 1.0, probeVolumeTexel3DMin.z + 0.0) - probeVolumeTexel3D)))); - probeWeightTNW = max(_ProbeVolumeBilateralFilterWeightMin, saturate(dot(normalWS, normalize(float3(probeVolumeTexel3DMin.x + 0.0, probeVolumeTexel3DMin.y + 1.0, probeVolumeTexel3DMin.z + 1.0) - probeVolumeTexel3D)))); - probeWeightTNE = max(_ProbeVolumeBilateralFilterWeightMin, saturate(dot(normalWS, normalize(float3(probeVolumeTexel3DMin.x + 1.0, probeVolumeTexel3DMin.y + 1.0, probeVolumeTexel3DMin.z + 1.0) - probeVolumeTexel3D)))); - } - else if (_ProbeVolumeLeakMitigationMode == LEAKMITIGATIONMODE_PROBE_VALIDITY_FILTER) - { - // TODO: Rather than sampling validity data from a slice in our texture array, we could place it in a different texture resource entirely. - // This would allow us to use a single channel format, rather than wasting memory with float4(validity, unused, unused, unused). - // It would also allow us to use a different texture format (i.e: 1x8bpp rather than 4x16bpp). - // Currently just using a texture slice for convenience, and with the idea that MAYBE we will end up using the remaining 3 channels. - probeWeightBSW = max(_ProbeVolumeBilateralFilterWeightMin, ProbeVolumeLoadValidity(int3(probeVolumeTexel3DMin.x + 0, probeVolumeTexel3DMin.y + 0, probeVolumeTexel3DMin.z + 0))); - probeWeightBSE = max(_ProbeVolumeBilateralFilterWeightMin, ProbeVolumeLoadValidity(int3(probeVolumeTexel3DMin.x + 1, probeVolumeTexel3DMin.y + 0, probeVolumeTexel3DMin.z + 0))); - probeWeightBNW = max(_ProbeVolumeBilateralFilterWeightMin, ProbeVolumeLoadValidity(int3(probeVolumeTexel3DMin.x + 0, probeVolumeTexel3DMin.y + 0, probeVolumeTexel3DMin.z + 1))); - probeWeightBNE = max(_ProbeVolumeBilateralFilterWeightMin, ProbeVolumeLoadValidity(int3(probeVolumeTexel3DMin.x + 1, probeVolumeTexel3DMin.y + 0, probeVolumeTexel3DMin.z + 1))); - - probeWeightTSW = max(_ProbeVolumeBilateralFilterWeightMin, ProbeVolumeLoadValidity(int3(probeVolumeTexel3DMin.x + 0, probeVolumeTexel3DMin.y + 1, probeVolumeTexel3DMin.z + 0))); - probeWeightTSE = max(_ProbeVolumeBilateralFilterWeightMin, ProbeVolumeLoadValidity(int3(probeVolumeTexel3DMin.x + 1, probeVolumeTexel3DMin.y + 1, probeVolumeTexel3DMin.z + 1))); - probeWeightTNW = max(_ProbeVolumeBilateralFilterWeightMin, ProbeVolumeLoadValidity(int3(probeVolumeTexel3DMin.x + 0, probeVolumeTexel3DMin.y + 1, probeVolumeTexel3DMin.z + 1))); - probeWeightTNE = max(_ProbeVolumeBilateralFilterWeightMin, ProbeVolumeLoadValidity(int3(probeVolumeTexel3DMin.x + 1, probeVolumeTexel3DMin.y + 1, probeVolumeTexel3DMin.z + 1))); - } - else if (_ProbeVolumeLeakMitigationMode == LEAKMITIGATIONMODE_OCTAHEDRAL_DEPTH_OCCLUSION_FILTER) - { - float3 probeVolumeTexel3DMin = floor(probeVolumeTexel3D - 0.5) + 0.5; - - // TODO: Evaluate if we should we build this 3x3 matrix and a float3 bias term cpu side to decrease alu at the cost of more bandwidth. - float3 probeVolumeWorldFromTexel3DScale = s_probeVolumeData.resolutionInverse * 2.0 * obbExtents; // [0, resolution3D] to [0.0, probeVolumeSize3D] - float3x3 probeVolumeWorldFromTexel3DRotationScale = float3x3( - obbFrame[0] * probeVolumeWorldFromTexel3DScale, - obbFrame[1] * probeVolumeWorldFromTexel3DScale, - obbFrame[2] * probeVolumeWorldFromTexel3DScale - ); - float3 probeVolumeWorldFromTexel3DTranslation = mul(obbFrame, -obbExtents) + s_probeVolumeBounds.center; - - float probeWeights[8]; - EvaluateProbeVolumeOctahedralDepthOcclusionFilterWeights( - probeWeights, - probeVolumeTexel3DMin, - s_probeVolumeData.resolution, - probeVolumeWorldFromTexel3DRotationScale, - probeVolumeWorldFromTexel3DTranslation, - s_probeVolumeData.octahedralDepthScaleBias, - _ProbeVolumeAtlasOctahedralDepthResolutionAndInverse, - positionRWS, // unbiased - samplePositionWS, // biased - normalWS - ); - probeWeightBSW = probeWeights[0]; // (i == 0) => (int3(i, i >> 1, i >> 2) & int3(1, 1, 1)) => (int3(0, 0 >> 1, 0 >> 2) & int3(1, 1, 1)) => int3(0, 0, 0) - probeWeightBSE = probeWeights[1]; // (i == 1) => (int3(i, i >> 1, i >> 2) & int3(1, 1, 1)) => (int3(1, 1 >> 1, 1 >> 2) & int3(1, 1, 1)) => int3(1, 0, 0) - probeWeightBNW = probeWeights[2]; // (i == 2) => (int3(i, i >> 1, i >> 2) & int3(1, 1, 1)) => (int3(2, 2 >> 1, 2 >> 2) & int3(1, 1, 1)) => int3(0, 1, 0) - probeWeightBNE = probeWeights[3]; // (i == 3) => (int3(i, i >> 1, i >> 2) & int3(1, 1, 1)) => (int3(3, 3 >> 1, 3 >> 2) & int3(1, 1, 1)) => int3(1, 1, 0) - - probeWeightTSW = probeWeights[4]; // (i == 4) => (int3(i, i >> 1, i >> 2) & int3(1, 1, 1)) => (int3(4, 4 >> 1, 4 >> 2) & int3(1, 1, 1)) => int3(0, 0, 1) - probeWeightTSE = probeWeights[5]; // (i == 5) => (int3(i, i >> 1, i >> 2) & int3(1, 1, 1)) => (int3(5, 5 >> 1, 5 >> 2) & int3(1, 1, 1)) => int3(1, 0, 1) - probeWeightTNW = probeWeights[6]; // (i == 6) => (int3(i, i >> 1, i >> 2) & int3(1, 1, 1)) => (int3(6, 6 >> 1, 6 >> 2) & int3(1, 1, 1)) => int3(0, 1, 1) - probeWeightTNE = probeWeights[7]; // (i == 7) => (int3(i, i >> 1, i >> 2) & int3(1, 1, 1)) => (int3(7, 7 >> 1, 7 >> 2) & int3(1, 1, 1)) => int3(1, 1, 1) - } - - // Blend between Geometric Weights and simple trilinear filter weights based on user defined _ProbeVolumeBilateralFilterWeight. - { - float3 probeWeightTrilinearMax = frac(probeVolumeTexel3D - 0.5); - float3 probeWeightTrilinearMin = 1.0 - probeWeightTrilinearMax; - - float probeWeightTrilinearBSW = probeWeightTrilinearMin.x * probeWeightTrilinearMin.y * probeWeightTrilinearMin.z; - float probeWeightTrilinearBSE = probeWeightTrilinearMax.x * probeWeightTrilinearMin.y * probeWeightTrilinearMin.z; - float probeWeightTrilinearBNW = probeWeightTrilinearMin.x * probeWeightTrilinearMin.y * probeWeightTrilinearMax.z; - float probeWeightTrilinearBNE = probeWeightTrilinearMax.x * probeWeightTrilinearMin.y * probeWeightTrilinearMax.z; - float probeWeightTrilinearTSW = probeWeightTrilinearMin.x * probeWeightTrilinearMax.y * probeWeightTrilinearMin.z; - float probeWeightTrilinearTSE = probeWeightTrilinearMax.x * probeWeightTrilinearMax.y * probeWeightTrilinearMin.z; - float probeWeightTrilinearTNW = probeWeightTrilinearMin.x * probeWeightTrilinearMax.y * probeWeightTrilinearMax.z; - float probeWeightTrilinearTNE = probeWeightTrilinearMax.x * probeWeightTrilinearMax.y * probeWeightTrilinearMax.z; - - probeWeightBSW = lerp(probeWeightTrilinearBSW, probeWeightTrilinearBSW * probeWeightBSW, _ProbeVolumeBilateralFilterWeight); - probeWeightBSE = lerp(probeWeightTrilinearBSE, probeWeightTrilinearBSE * probeWeightBSE, _ProbeVolumeBilateralFilterWeight); - probeWeightBNW = lerp(probeWeightTrilinearBNW, probeWeightTrilinearBNW * probeWeightBNW, _ProbeVolumeBilateralFilterWeight); - probeWeightBNE = lerp(probeWeightTrilinearBNE, probeWeightTrilinearBNE * probeWeightBNE, _ProbeVolumeBilateralFilterWeight); - - probeWeightTSW = lerp(probeWeightTrilinearTSW, probeWeightTrilinearTSW * probeWeightTSW, _ProbeVolumeBilateralFilterWeight); - probeWeightTSE = lerp(probeWeightTrilinearTSE, probeWeightTrilinearTSE * probeWeightTSE, _ProbeVolumeBilateralFilterWeight); - probeWeightTNW = lerp(probeWeightTrilinearTNW, probeWeightTrilinearTNW * probeWeightTNW, _ProbeVolumeBilateralFilterWeight); - probeWeightTNE = lerp(probeWeightTrilinearTNE, probeWeightTrilinearTNE * probeWeightTNE, _ProbeVolumeBilateralFilterWeight); - } - - float probeWeightTotal = - probeWeightBSW + - probeWeightBSE + - probeWeightBNW + - probeWeightBNE + - probeWeightTSW + - probeWeightTSE + - probeWeightTNW + - probeWeightTNE; - - // Weights are enforced to be > 0.0 to guard against divide by zero. - float probeWeightNormalization = 1.0 / probeWeightTotal; - - probeWeightBSW *= probeWeightNormalization; - probeWeightBSE *= probeWeightNormalization; - probeWeightBNW *= probeWeightNormalization; - probeWeightBNE *= probeWeightNormalization; - probeWeightTSW *= probeWeightNormalization; - probeWeightTSE *= probeWeightNormalization; - probeWeightTNW *= probeWeightNormalization; - probeWeightTNE *= probeWeightNormalization; - - // Finally, update our texture coordinate based on our weights. - // Half-texel offset has been baked into the coordinates. - float3 probeVolumeTexel3DFrac = - float3(0.5, 0.5, 0.5) * probeWeightBSW + - float3(1.5, 0.5, 0.5) * probeWeightBSE + - float3(0.5, 0.5, 1.5) * probeWeightBNW + - float3(1.5, 0.5, 1.5) * probeWeightBNE + - float3(0.5, 1.5, 0.5) * probeWeightTSW + - float3(1.5, 1.5, 0.5) * probeWeightTSE + - float3(0.5, 1.5, 1.5) * probeWeightTNW + - float3(1.5, 1.5, 1.5) * probeWeightTNE; + float weightCurrent = 0.0; + { + float3x3 obbFrame; + float3 obbExtents; + float3 obbCenter; + ProbeVolumeComputeOBBBoundsToFrame(s_probeVolumeBounds, obbFrame, obbExtents, obbCenter); + + // Note: When normal bias is > 0, bounds using in tile / cluster assignment are conservatively dilated CPU side to handle worst case normal bias. + float3 samplePositionWS = normalWS * s_probeVolumeData.normalBiasWS + positionRWS; + + float3 probeVolumeTexel3D; + ProbeVolumeComputeTexel3DAndWeight( + weightHierarchy, + s_probeVolumeData, + obbFrame, + obbExtents, + obbCenter, + samplePositionWS, + positionLinearDepth, + probeVolumeTexel3D, + weightCurrent + ); + + probeVolumeTexel3D = ProbeVolumeComputeTexel3DFromBilateralFilter( + probeVolumeTexel3D, + s_probeVolumeData, + positionRWS, // unbiased + samplePositionWS, // biased + normalWS, + obbFrame, + obbExtents, + obbCenter + ); + float3 probeVolumeAtlasUVW = probeVolumeTexel3D * s_probeVolumeData.resolutionInverse * s_probeVolumeData.scale + s_probeVolumeData.bias; #ifdef DEBUG_DISPLAY - // If we are visualizing validity data, we do not want to apply our bilateral filter texture coordinate modification - // because ideally, our filter will avoid sampling from invalid data - making this debug mode useless. - if (_DebugProbeVolumeMode != PROBEVOLUMEDEBUGMODE_VISUALIZE_VALIDITY) -#endif - { - probeVolumeTexel3D = floor(probeVolumeTexel3D - 0.5) + probeVolumeTexel3DFrac; - } - } - - float3 probeVolumeAtlasUVW = probeVolumeTexel3D * s_probeVolumeData.resolutionInverse * s_probeVolumeData.scale + s_probeVolumeData.bias; + if (_DebugProbeVolumeMode == PROBEVOLUMEDEBUGMODE_VISUALIZE_DEBUG_COLORS) + { + // Pack debug color into SH data so that we can access it later for our debug mode. + coefficients.data[0].xyz += s_probeVolumeData.debugColor * weightCurrent; + } + else if (_DebugProbeVolumeMode == PROBEVOLUMEDEBUGMODE_VISUALIZE_VALIDITY) + { + float validity = ProbeVolumeSampleValidity(probeVolumeAtlasUVW); -#ifdef DEBUG_DISPLAY - if (_DebugProbeVolumeMode == PROBEVOLUMEDEBUGMODE_VISUALIZE_DEBUG_COLORS) - { - // Pack debug color into SH data so that we can access it later for our debug mode. - coefficients.data[0].xyz += s_probeVolumeData.debugColor * weightCurrent; - } - else if (_DebugProbeVolumeMode == PROBEVOLUMEDEBUGMODE_VISUALIZE_VALIDITY) - { - float validity = ProbeVolumeSampleValidity(probeVolumeAtlasUVW); - - // Pack validity into SH data so that we can access it later for our debug mode. - coefficients.data[0].x += validity * weightCurrent; - } - else + // Pack validity into SH data so that we can access it later for our debug mode. + coefficients.data[0].x += validity * weightCurrent; + } + else #endif - { + { #if SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L0 - coefficients.data[0] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 0), 0) * weightCurrent; + coefficients.data[0] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 0), 0) * weightCurrent; #elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L1 - coefficients.data[0] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 0), 0) * weightCurrent; - coefficients.data[1] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 1), 0) * weightCurrent; - coefficients.data[2] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 2), 0) * weightCurrent; + coefficients.data[0] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 0), 0) * weightCurrent; + coefficients.data[1] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 1), 0) * weightCurrent; + coefficients.data[2] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 2), 0) * weightCurrent; #elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L2 - coefficients.data[0] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 0), 0) * weightCurrent; - coefficients.data[1] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 1), 0) * weightCurrent; - coefficients.data[2] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 2), 0) * weightCurrent; - coefficients.data[3] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 3), 0) * weightCurrent; - coefficients.data[4] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 4), 0) * weightCurrent; - coefficients.data[5] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 5), 0) * weightCurrent; - coefficients.data[6] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 6), 0) * weightCurrent; + coefficients.data[0] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 0), 0) * weightCurrent; + coefficients.data[1] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 1), 0) * weightCurrent; + coefficients.data[2] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 2), 0) * weightCurrent; + coefficients.data[3] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 3), 0) * weightCurrent; + coefficients.data[4] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 4), 0) * weightCurrent; + coefficients.data[5] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 5), 0) * weightCurrent; + coefficients.data[6] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 6), 0) * weightCurrent; #endif - } } - - if (isWeightAccumulated) - weightHierarchy += weightCurrent; } - } + if (isWeightAccumulated) + weightHierarchy += weightCurrent; + } } } diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeLightLoopDef.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeLightLoopDef.hlsl index f5b4bd00fcb..4cf104904c3 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeLightLoopDef.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeLightLoopDef.hlsl @@ -58,7 +58,7 @@ uint ProbeVolumeGetLightClusterIndex(uint2 tileIndex, float linearDepth) return ProbeVolumeSnapToClusterIdxFlex(linearDepth, g_fClustBase, k_IsLogBaseBufferEnabled); } -void ProbeVolumeGetCountAndStartCluster(uint2 tileIndex, uint clusterIndex, uint lightCategory, out uint start, out uint lightCount) +void ProbeVolumeMaterialPassGetCountAndStartCluster(uint2 tileIndex, uint clusterIndex, uint lightCategory, out uint start, out uint lightCount) { int nrClusters = (1 << g_iLog2NumClusters); @@ -69,7 +69,7 @@ void ProbeVolumeGetCountAndStartCluster(uint2 tileIndex, uint clusterIndex, uint lightCount = (dataPair >> 27) & 31; } -void ProbeVolumeGetCountAndStartCluster(PositionInputs posInput, uint lightCategory, out uint start, out uint lightCount) +void ProbeVolumeMaterialPassGetCountAndStartCluster(PositionInputs posInput, uint lightCategory, out uint start, out uint lightCount) { // Note: XR depends on unity_StereoEyeIndex already being defined, // which means ShaderVariables.hlsl needs to be defined ahead of this! @@ -77,19 +77,109 @@ void ProbeVolumeGetCountAndStartCluster(PositionInputs posInput, uint lightCateg uint2 tileIndex = posInput.tileCoord; uint clusterIndex = ProbeVolumeGetLightClusterIndex(tileIndex, posInput.linearDepth); - ProbeVolumeGetCountAndStartCluster(tileIndex, clusterIndex, lightCategory, start, lightCount); + ProbeVolumeMaterialPassGetCountAndStartCluster(tileIndex, clusterIndex, lightCategory, start, lightCount); } -void ProbeVolumeGetCountAndStart(PositionInputs posInput, uint lightCategory, out uint start, out uint lightCount) +void ProbeVolumeMaterialPassGetCountAndStart(PositionInputs posInput, uint lightCategory, out uint start, out uint lightCount) { - ProbeVolumeGetCountAndStartCluster(posInput, lightCategory, start, lightCount); + ProbeVolumeMaterialPassGetCountAndStartCluster(posInput, lightCategory, start, lightCount); } -uint ProbeVolumeFetchIndex(uint lightStart, uint lightOffset) +uint ProbeVolumeMaterialPassFetchIndex(uint lightStart, uint lightOffset) { return g_vProbeVolumesLightListGlobal[lightStart + lightOffset]; } #endif // endof SHADEROPTIONS_PROBE_VOLUMES_EVALUATION_MODE == PROBEVOLUMESEVALUATIONMODES_MATERIAL_PASS +void ProbeVolumeGetCountAndStart(PositionInputs posInput, out uint probeVolumeStart, out uint probeVolumeCount) +{ +#ifndef LIGHTLOOP_DISABLE_TILE_AND_CLUSTER +#if SHADEROPTIONS_PROBE_VOLUMES_EVALUATION_MODE == PROBEVOLUMESEVALUATIONMODES_MATERIAL_PASS + // Access probe volume data from custom probe volume light list data structure. + ProbeVolumeMaterialPassGetCountAndStart(posInput, LIGHTCATEGORY_PROBE_VOLUME, probeVolumeStart, probeVolumeCount); +#else // #if SHADEROPTIONS_PROBE_VOLUMES_EVALUATION_MODE == PROBEVOLUMESEVALUATIONMODES_LIGHT_LOOP + // Access probe volume data from standard lightloop light list data structure. + GetCountAndStart(posInput, LIGHTCATEGORY_PROBE_VOLUME, probeVolumeStart, probeVolumeCount); +#endif + +#else // LIGHTLOOP_DISABLE_TILE_AND_CLUSTER + probeVolumeCount = _ProbeVolumeCount; + probeVolumeStart = 0; +#endif +} + +void ProbeVolumeGetCountAndStartAndFastPath(PositionInputs posInput, out uint probeVolumeStart, out uint probeVolumeCount, out bool fastPath) +{ + // Fetch first probe volume to provide the scene proxy for screen space computation + ProbeVolumeGetCountAndStart(posInput, probeVolumeStart, probeVolumeCount); + fastPath = false; + +#if SCALARIZE_LIGHT_LOOP + // Fast path is when we all pixels in a wave is accessing same tile or cluster. + uint probeVolumeStartFirstLane = WaveReadLaneFirst(probeVolumeStart); + fastPath = WaveActiveAllTrue(probeVolumeStart == probeVolumeStartFirstLane); + if (fastPath) + { + probeVolumeStart = probeVolumeStartFirstLane; + } +#endif +} + +uint ProbeVolumeFetchIndex(uint probeVolumeStart, uint probeVolumeOffset) +{ + #if SHADEROPTIONS_PROBE_VOLUMES_EVALUATION_MODE == PROBEVOLUMESEVALUATIONMODES_MATERIAL_PASS + // Access probe volume data from custom probe volume light list data structure. + return ProbeVolumeMaterialPassFetchIndex(probeVolumeStart, probeVolumeOffset); +#else // #if SHADEROPTIONS_PROBE_VOLUMES_EVALUATION_MODE == PROBEVOLUMESEVALUATIONMODES_LIGHT_LOOP + // Access probe volume data from standard lightloop light list data structure. + return FetchIndex(probeVolumeStart, probeVolumeOffset); +#endif +} + +// This function scalarize an index accross all lanes. To be effecient it must be used in the context +// of the scalarization of a loop. It is to use with IsFastPath so it can optimize the number of +// element to load, which is optimal when all the lanes are contained into a tile. +uint ProbeVolumeScalarizeElementIndex(uint v_elementIdx, bool fastPath) +{ + uint s_elementIdx = v_elementIdx; +#if SCALARIZE_LIGHT_LOOP + if (!fastPath) + { + // If we are not in fast path, v_elementIdx is not scalar, so we need to query the Min value across the wave. + s_elementIdx = WaveActiveMin(v_elementIdx); + // If WaveActiveMin returns 0xffffffff it means that all lanes are actually dead, so we can safely ignore the loop and move forward. + // This could happen as an helper lane could reach this point, hence having a valid v_elementIdx, but their values will be ignored by the WaveActiveMin + if (s_elementIdx == -1) + { + return -1; + } + } + // Note that the WaveReadLaneFirst should not be needed, but the compiler might insist in putting the result in VGPR. + // However, we are certain at this point that the index is scalar. + s_elementIdx = WaveReadLaneFirst(s_elementIdx); +#endif + return s_elementIdx; +} + +bool ProbeVolumeIsAllWavesComplete(float weightHierarchy, int volumeBlendMode) +{ + // Probe volumes are sorted primarily by blend mode, and secondarily by size. + // 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 if our weightHierarchy reaches 1.0, since we know we will only ever process more VOLUMEBLENDMODE_NORMAL volumes, + // whos weight will always evaluate to zero. +#if defined(PLATFORM_SUPPORTS_WAVE_INTRINSICS) + if (WaveActiveMin(weightHierarchy) >= 1.0 +#if SHADEROPTIONS_PROBE_VOLUMES_ADDITIVE_BLENDING + && WaveActiveAllTrue(volumeBlendMode == VOLUMEBLENDMODE_NORMAL) +#endif + ) + { + return true; + } +#endif + + return false; +} + #endif // endof PROBE_VOLUME_LIGHT_LOOP_DEF diff --git a/com.unity.render-pipelines.high-definition/Runtime/Material/BuiltinGIUtilities.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Material/BuiltinGIUtilities.hlsl index edf3bf5b297..dc3a07b44cc 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Material/BuiltinGIUtilities.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/Material/BuiltinGIUtilities.hlsl @@ -129,36 +129,37 @@ void SampleBakedGI( #else // PROBEVOLUMESEVALUATIONMODES_MATERIAL_PASS || PROBEVOLUMESEVALUATIONMODES_DISABLED #if SAMPLE_PROBEVOLUME + if (_EnableProbeVolumes) + { #if SAMPLE_LIGHTMAP - float probeVolumeHierarchyWeight = 1.0f; + float probeVolumeHierarchyWeight = 1.0f; #else - float probeVolumeHierarchyWeight = 0.0f; + float probeVolumeHierarchyWeight = 0.0f; #endif #ifdef SHADERPASS #if SHADERPASS == SHADERPASS_GBUFFER || SHADERPASS == SHADERPASS_FORWARD - #if SHADERPASS == SHADERPASS_GBUFFER || (SHADERPASS == SHADERPASS_FORWARD && defined(USE_FPTL_LIGHTLIST)) - // posInputs.tileCoord will be zeroed out in GBuffer pass. - // posInputs.tileCoord will be incorrect for probe volumes (which use clustered) in forward if forward lightloop is using FTPL lightlist (i.e: in ForwardOnly lighting configuration). - // Need to manually compute tile coord here. - float2 positionSS = posInputs.positionNDC.xy * _ScreenSize.xy; - uint2 tileCoord = uint2(positionSS) / ProbeVolumeGetTileSize(); - posInputs.tileCoord = tileCoord; - #endif + // posInputs.tileCoord will be zeroed out in GBuffer pass. + // posInputs.tileCoord will be incorrect for probe volumes (which use clustered) in forward if forward lightloop is using FTPL lightlist (i.e: in ForwardOnly lighting configuration). + // Need to manually compute tile coord here. + float2 positionSS = posInputs.positionNDC.xy * _ScreenSize.xy; + uint2 tileCoord = uint2(positionSS) / ProbeVolumeGetTileSize(); + posInputs.tileCoord = tileCoord; + #endif + + ProbeVolumeCoefficients coefficients; + AccumulateProbeVolumes(posInputs, normalWS, renderingLayers, coefficients, probeVolumeHierarchyWeight); + bakeDiffuseLighting += EvaluateProbeVolumeCoefficients(normalWS, coefficients); + backBakeDiffuseLighting += EvaluateProbeVolumeCoefficients(backNormalWS, coefficients); - // TODO: SampleBakedGI() will be called twice, once for the front facing direction, and once for the back facing direction (for transmission). - // For Probe Volumes specifically, this is not necessary - it would be better to return the coefficients, and just evaluate those coefficients twice (once for for each normal). - // We already do this in LightLoop evaluation mode. - // This would require the caller to track whether or not it needs to be called a second time. - ProbeVolumeCoefficients coefficients; - AccumulateProbeVolumes(posInputs, normalWS, renderingLayers, coefficients, probeVolumeHierarchyWeight); - combinedGI += EvaluateProbeVolumeCoefficients(normalWS, coefficients); - combinedGI += EvaluateProbeVolumeAmbientProbeFallback(normalWS, probeVolumeHierarchyWeight); + float backProbeVolumeHierarchyWeight = probeVolumeHierarchyWeight; + bakeDiffuseLighting += EvaluateProbeVolumeAmbientProbeFallback(normalWS, probeVolumeHierarchyWeight); + backBakeDiffuseLighting += EvaluateProbeVolumeAmbientProbeFallback(backNormalWS, backProbeVolumeHierarchyWeight); #endif #endif ->>>>>>> 4bc7b73f4e... Simplify EvaluateProbeVolumes interface and builtin SampleBakedGI code + } #endif #if SAMPLE_PROBEVOLUME_BUILTIN From a83f4b31dd0ac61e1c3cabbe5f9293ec40a718bb Mon Sep 17 00:00:00 2001 From: pastasfuture Date: Mon, 1 Jun 2020 18:07:36 -0700 Subject: [PATCH 12/26] Probe Volumes: Add ShaderConfig.s_ProbeVolumesBilaterialFilteringMode and strip out octahedral depth logic and memory allocations when not in use. --- .../Runtime/ShaderConfig.cs | 12 +- .../Runtime/ShaderConfig.cs.hlsl | 10 +- .../RenderPipeline/HDRenderPipelineUI.cs | 5 +- .../DebugDisplayProbeVolume.shader | 10 ++ .../Lighting/ProbeVolume/ProbeVolume.hlsl | 21 +++- .../ProbeVolume/ProbeVolumeLighting.cs | 111 ++++++++++++------ .../ProbeVolumeShaderVariables.hlsl | 3 + .../Lighting/SphericalHarmonics.cs.hlsl | 25 ---- 8 files changed, 127 insertions(+), 70 deletions(-) diff --git a/com.unity.render-pipelines.high-definition-config/Runtime/ShaderConfig.cs b/com.unity.render-pipelines.high-definition-config/Runtime/ShaderConfig.cs index 8c63f893794..029b554cd1d 100644 --- a/com.unity.render-pipelines.high-definition-config/Runtime/ShaderConfig.cs +++ b/com.unity.render-pipelines.high-definition-config/Runtime/ShaderConfig.cs @@ -28,6 +28,14 @@ public enum ProbeVolumesEncodingModes SphericalHarmonicsL2 = 2 } + [GenerateHLSL(PackingRules.Exact)] + public enum ProbeVolumesBilateralFilteringModes + { + Disabled = 0, + Validity = 1, + OctahedralDepth = 2 + } + [GenerateHLSL(PackingRules.Exact)] public enum ShaderOptions { @@ -55,7 +63,8 @@ public enum ShaderOptions // Probe Volumes feature must also be enabled inside of your HDRenderPipelineAsset. ProbeVolumesEvaluationMode = ProbeVolumesEvaluationModes.LightLoop, ProbeVolumesAdditiveBlending = 1, - ProbeVolumesEncodingMode = ProbeVolumesEncodingModes.SphericalHarmonicsL1, + ProbeVolumesBilateralFilteringMode = ProbeVolumesBilateralFilteringModes.Validity, + ProbeVolumesEncodingMode = ProbeVolumesEncodingModes.SphericalHarmonicsL2, AreaLights = 1, @@ -78,6 +87,7 @@ public class ShaderConfig public static int s_PrecomputedAtmosphericAttenuation = (int)ShaderOptions.PrecomputedAtmosphericAttenuation; public static ProbeVolumesEvaluationModes s_ProbeVolumesEvaluationMode = (ProbeVolumesEvaluationModes)ShaderOptions.ProbeVolumesEvaluationMode; public static int s_ProbeVolumesAdditiveBlending = (int)ShaderOptions.ProbeVolumesAdditiveBlending; + public static ProbeVolumesBilateralFilteringModes s_ProbeVolumesBilateralFilteringMode = (ProbeVolumesBilateralFilteringModes)ShaderOptions.ProbeVolumesBilateralFilteringMode; public static ProbeVolumesEncodingModes s_ProbeVolumesEncodingMode = (ProbeVolumesEncodingModes)ShaderOptions.ProbeVolumesEncodingMode; public static int s_AreaLights = (int)ShaderOptions.AreaLights; public static int s_BarnDoor = (int)ShaderOptions.BarnDoor; diff --git a/com.unity.render-pipelines.high-definition-config/Runtime/ShaderConfig.cs.hlsl b/com.unity.render-pipelines.high-definition-config/Runtime/ShaderConfig.cs.hlsl index 55f81db06cb..5f8ad7046e5 100644 --- a/com.unity.render-pipelines.high-definition-config/Runtime/ShaderConfig.cs.hlsl +++ b/com.unity.render-pipelines.high-definition-config/Runtime/ShaderConfig.cs.hlsl @@ -25,6 +25,13 @@ #define PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L1 (1) #define PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L2 (2) +// +// UnityEngine.Rendering.HighDefinition.ProbeVolumesBilateralFilteringModes: static fields +// +#define PROBEVOLUMESBILATERALFILTERINGMODES_DISABLED (0) +#define PROBEVOLUMESBILATERALFILTERINGMODES_VALIDITY (1) +#define PROBEVOLUMESBILATERALFILTERINGMODES_OCTAHEDRAL_DEPTH (2) + // // UnityEngine.Rendering.HighDefinition.ShaderOptions: static fields // @@ -35,7 +42,8 @@ #define SHADEROPTIONS_XR_MAX_VIEWS (2) #define SHADEROPTIONS_PROBE_VOLUMES_EVALUATION_MODE (1) #define SHADEROPTIONS_PROBE_VOLUMES_ADDITIVE_BLENDING (1) -#define SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE (1) +#define SHADEROPTIONS_PROBE_VOLUMES_BILATERAL_FILTERING_MODE (1) +#define SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE (2) #define SHADEROPTIONS_AREA_LIGHTS (1) #define SHADEROPTIONS_DEFERRED_SHADOW_FILTERING (1) #define SHADEROPTIONS_BARN_DOOR (0) diff --git a/com.unity.render-pipelines.high-definition/Editor/RenderPipeline/HDRenderPipelineUI.cs b/com.unity.render-pipelines.high-definition/Editor/RenderPipeline/HDRenderPipelineUI.cs index 23df7c09ad0..5522674d37b 100644 --- a/com.unity.render-pipelines.high-definition/Editor/RenderPipeline/HDRenderPipelineUI.cs +++ b/com.unity.render-pipelines.high-definition/Editor/RenderPipeline/HDRenderPipelineUI.cs @@ -899,14 +899,16 @@ static void Drawer_SectionLightingUnsorted(SerializedHDRenderPipelineAsset seria } } + EditorGUI.BeginDisabledGroup(ShaderConfig.s_ProbeVolumesBilateralFilteringMode != ProbeVolumesBilateralFilteringModes.OctahedralDepth); EditorGUI.BeginChangeCheck(); EditorGUILayout.DelayedIntField(serialized.renderPipelineSettings.probeVolumeSettings.atlasOctahedralDepthResolution, Styles.probeVolumeAtlasOctahedralDepthResolution); if (EditorGUI.EndChangeCheck()) { serialized.renderPipelineSettings.probeVolumeSettings.atlasOctahedralDepthResolution.intValue = Mathf.Max(serialized.renderPipelineSettings.probeVolumeSettings.atlasOctahedralDepthResolution.intValue, 0); } - else + else if (ShaderConfig.s_ProbeVolumesBilateralFilteringMode == ProbeVolumesBilateralFilteringModes.OctahedralDepth) { + // Only display memory allocation info if octahedral depth feature is actually enabled. Only then will memory be allocated. long currentCache = HDRenderPipeline.GetApproxProbeVolumeOctahedralDepthAtlasSizeInByte(serialized.renderPipelineSettings.probeVolumeSettings.atlasOctahedralDepthResolution.intValue); if (currentCache > HDRenderPipeline.k_MaxCacheSize) { @@ -920,6 +922,7 @@ static void Drawer_SectionLightingUnsorted(SerializedHDRenderPipelineAsset seria EditorGUILayout.HelpBox(message, MessageType.Info); } } + EditorGUI.EndDisabledGroup(); if (serialized.renderPipelineSettings.probeVolumeSettings.atlasResolution.intValue <= 0) { diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/DebugDisplayProbeVolume.shader b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/DebugDisplayProbeVolume.shader index d632d66df32..6e2ff294bf7 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/DebugDisplayProbeVolume.shader +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/DebugDisplayProbeVolume.shader @@ -4,6 +4,7 @@ Shader "Hidden/ScriptableRenderPipeline/DebugDisplayProbeVolume" #pragma target 4.5 #pragma only_renderers d3d11 playstation xboxone vulkan metal switch + #include "Packages/com.unity.render-pipelines.high-definition-config/Runtime/ShaderConfig.cs.hlsl" #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl" #include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl" #include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeShaderVariables.hlsl" @@ -20,8 +21,10 @@ Shader "Hidden/ScriptableRenderPipeline/DebugDisplayProbeVolume" SamplerState ltc_linear_clamp_sampler; TEXTURE3D(_AtlasTextureSH); + #if SHADEROPTIONS_PROBE_VOLUMES_BILATERAL_FILTERING == PROBEVOLUMESBILATERALFILTERINGMODES_OCTAHEDRAL_DEPTH TEXTURE2D(_AtlasTextureOctahedralDepth); float4 _AtlasTextureOctahedralDepthScaleBias; + #endif struct Attributes { @@ -78,7 +81,10 @@ Shader "Hidden/ScriptableRenderPipeline/DebugDisplayProbeVolume" float4 valueShAg = saturate((SAMPLE_TEXTURE3D_LOD(_AtlasTextureSH, ltc_linear_clamp_sampler, float3(uvw.x, uvw.y, uvw.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 1), 0) - _ValidRange.x) * _ValidRange.y); float4 valueShAb = saturate((SAMPLE_TEXTURE3D_LOD(_AtlasTextureSH, ltc_linear_clamp_sampler, float3(uvw.x, uvw.y, uvw.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 2), 0) - _ValidRange.x) * _ValidRange.y); float valueValidity = saturate((SAMPLE_TEXTURE3D_LOD(_AtlasTextureSH, ltc_linear_clamp_sampler, float3(uvw.x, uvw.y, uvw.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 3), 0).x - _ValidRange.x) * _ValidRange.y); + + #if SHADEROPTIONS_PROBE_VOLUMES_BILATERAL_FILTERING == PROBEVOLUMESBILATERALFILTERINGMODES_OCTAHEDRAL_DEPTH float2 valueOctahedralDepthMeanAndVariance = saturate((SAMPLE_TEXTURE2D_LOD(_AtlasTextureOctahedralDepth, ltc_linear_clamp_sampler, input.texcoord * _AtlasTextureOctahedralDepthScaleBias.xy + _AtlasTextureOctahedralDepthScaleBias.zw, 0).xy - _ValidRange.x) * _ValidRange.y); + #endif switch (_ProbeVolumeAtlasSliceMode) { @@ -110,6 +116,7 @@ Shader "Hidden/ScriptableRenderPipeline/DebugDisplayProbeVolume" case PROBEVOLUMEATLASSLICEMODE_OCTAHEDRAL_DEPTH: { + #if SHADEROPTIONS_PROBE_VOLUMES_BILATERAL_FILTERING == PROBEVOLUMESBILATERALFILTERINGMODES_OCTAHEDRAL_DEPTH // Tonemap variance with sqrt() to bring it into a more similar scale to mean to make it more readable. return float4( valueOctahedralDepthMeanAndVariance.x, @@ -117,6 +124,9 @@ Shader "Hidden/ScriptableRenderPipeline/DebugDisplayProbeVolume" 0.0f, 1.0f ); + #else + return float4(0.0f, 0.0f, 0.0f, 1.0f); + #endif } default: return float4(0.0, 0.0, 0.0, 1.0); diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl index 53fa399c709..ec31738e2f7 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl @@ -49,6 +49,7 @@ float ProbeVolumeLoadValidity(int3 probeVolumeAtlasTexelCoord) #endif } +#if SHADEROPTIONS_PROBE_VOLUMES_BILATERAL_FILTERING == PROBEVOLUMESBILATERALFILTERINGMODES_OCTAHEDRAL_DEPTH void EvaluateProbeVolumeOctahedralDepthOcclusionFilterWeights( out float weights[8], float3 probeVolumeTexel3DMin, @@ -151,6 +152,7 @@ void EvaluateProbeVolumeOctahedralDepthOcclusionFilterWeights( weights[i] = clamp(weights[i], (RECURSIVE ? 0.1 : 0.0), 1.01); } } +#endif void ProbeVolumeComputeOBBBoundsToFrame(OrientedBBox probeVolumeBounds, out float3x3 obbFrame, out float3 obbExtents, out float3 obbCenter) { @@ -211,6 +213,9 @@ float3 ProbeVolumeComputeTexel3DFromBilateralFilter( float3 obbExtents, float3 obbCenter) { +#if SHADEROPTIONS_PROBE_VOLUMES_BILATERAL_FILTERING == PROBEVOLUMESBILATERALFILTERINGMODES_DISABLED + return probeVolumeTexel3D; +#else if (_ProbeVolumeLeakMitigationMode == LEAKMITIGATIONMODE_NORMAL_BIAS) { return probeVolumeTexel3D; } float3 probeVolumeTexel3DMin = floor(probeVolumeTexel3D - 0.5) + 0.5; @@ -253,6 +258,7 @@ float3 ProbeVolumeComputeTexel3DFromBilateralFilter( probeWeightTNW = max(_ProbeVolumeBilateralFilterWeightMin, ProbeVolumeLoadValidity(int3(probeVolumeTexel3DMin.x + 0, probeVolumeTexel3DMin.y + 1, probeVolumeTexel3DMin.z + 1))); probeWeightTNE = max(_ProbeVolumeBilateralFilterWeightMin, ProbeVolumeLoadValidity(int3(probeVolumeTexel3DMin.x + 1, probeVolumeTexel3DMin.y + 1, probeVolumeTexel3DMin.z + 1))); } +#if SHADEROPTIONS_PROBE_VOLUMES_BILATERAL_FILTERING == PROBEVOLUMESBILATERALFILTERINGMODES_OCTAHEDRAL_DEPTH else if (_ProbeVolumeLeakMitigationMode == LEAKMITIGATIONMODE_OCTAHEDRAL_DEPTH_OCCLUSION_FILTER) { // TODO: Evaluate if we should we build this 3x3 matrix and a float3 bias term cpu side to decrease alu at the cost of more bandwidth. @@ -287,6 +293,12 @@ float3 ProbeVolumeComputeTexel3DFromBilateralFilter( probeWeightTNW = probeWeights[6]; // (i == 6) => (int3(i, i >> 1, i >> 2) & int3(1, 1, 1)) => (int3(6, 6 >> 1, 6 >> 2) & int3(1, 1, 1)) => int3(0, 1, 1) probeWeightTNE = probeWeights[7]; // (i == 7) => (int3(i, i >> 1, i >> 2) & int3(1, 1, 1)) => (int3(7, 7 >> 1, 7 >> 2) & int3(1, 1, 1)) => int3(1, 1, 1) } +#endif + else + { + // Fallback to no bilateral filter if _ProbeVolumeLeakMitigationMode is configured to a mode unsupported in ShaderConfig. + return probeVolumeTexel3D; + } // Blend between Geometric Weights and simple trilinear filter weights based on user defined _ProbeVolumeBilateralFilterWeight. { @@ -357,6 +369,7 @@ float3 ProbeVolumeComputeTexel3DFromBilateralFilter( } return probeVolumeTexel3D; +#endif } struct ProbeVolumeCoefficients @@ -378,8 +391,6 @@ void AccumulateProbeVolumes(PositionInputs posInput, float3 normalWS, uint rende if (weightHierarchy >= 1.0) { return; } #endif - float3 positionRWS = posInput.positionWS; - float positionLinearDepth = posInput.linearDepth; uint probeVolumeStart, probeVolumeCount; bool fastPath; ProbeVolumeGetCountAndStartAndFastPath(posInput, probeVolumeStart, probeVolumeCount, fastPath); @@ -424,7 +435,7 @@ void AccumulateProbeVolumes(PositionInputs posInput, float3 normalWS, uint rende ProbeVolumeComputeOBBBoundsToFrame(s_probeVolumeBounds, obbFrame, obbExtents, obbCenter); // Note: When normal bias is > 0, bounds using in tile / cluster assignment are conservatively dilated CPU side to handle worst case normal bias. - float3 samplePositionWS = normalWS * s_probeVolumeData.normalBiasWS + positionRWS; + float3 samplePositionWS = normalWS * s_probeVolumeData.normalBiasWS + posInput.positionWS; float3 probeVolumeTexel3D; ProbeVolumeComputeTexel3DAndWeight( @@ -434,7 +445,7 @@ void AccumulateProbeVolumes(PositionInputs posInput, float3 normalWS, uint rende obbExtents, obbCenter, samplePositionWS, - positionLinearDepth, + posInput.linearDepth, probeVolumeTexel3D, weightCurrent ); @@ -442,7 +453,7 @@ void AccumulateProbeVolumes(PositionInputs posInput, float3 normalWS, uint rende probeVolumeTexel3D = ProbeVolumeComputeTexel3DFromBilateralFilter( probeVolumeTexel3D, s_probeVolumeData, - positionRWS, // unbiased + posInput.positionWS, // unbiased samplePositionWS, // biased normalWS, obbFrame, diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeLighting.cs b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeLighting.cs index f66ea0458ba..368b0a8d8e7 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeLighting.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeLighting.cs @@ -232,8 +232,7 @@ internal void CreateProbeVolumeBuffers() s_VisibleProbeVolumeDataBuffer = new ComputeBuffer(k_MaxVisibleProbeVolumeCount, Marshal.SizeOf(typeof(ProbeVolumeEngineData))); s_ProbeVolumeAtlasBlitDataBuffer = new ComputeBuffer(s_MaxProbeVolumeProbeCount * ProbeVolumePayload.GetSHStride(ShaderConfig.s_ProbeVolumesEncodingMode), Marshal.SizeOf(typeof(float))); s_ProbeVolumeAtlasBlitDataValidityBuffer = new ComputeBuffer(s_MaxProbeVolumeProbeCount, Marshal.SizeOf(typeof(float))); - s_ProbeVolumeAtlasOctahedralDepthBuffer = new ComputeBuffer(s_MaxProbeVolumeProbeOctahedralDepthCount, Marshal.SizeOf(typeof(float))); - + m_ProbeVolumeAtlasSHRTDepthSliceCount = GetDepthSliceCountFromEncodingMode(ShaderConfig.s_ProbeVolumesEncodingMode); m_ProbeVolumeAtlasSHRTHandle = RTHandles.Alloc( @@ -249,24 +248,29 @@ internal void CreateProbeVolumeBuffers() probeVolumeAtlas = new Texture3DAtlasDynamic(s_ProbeVolumeAtlasResolution, s_ProbeVolumeAtlasResolution, s_ProbeVolumeAtlasResolution, k_MaxVisibleProbeVolumeCount, m_ProbeVolumeAtlasSHRTHandle); - // 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 - ); + 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() @@ -357,12 +361,21 @@ unsafe void UpdateShaderVariablesGlobalProbeVolumes(ref ShaderVariablesGlobal cb 1.0f / (float)s_ProbeVolumeAtlasResolution, 1.0f / (float)m_ProbeVolumeAtlasSHRTDepthSliceCount ); - 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 - ); + + 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) @@ -404,7 +417,11 @@ void PushProbeVolumesGlobalParams(HDCamera hdCamera, CommandBuffer cmd) cmd.SetGlobalBuffer(HDShaderIDs._ProbeVolumeBounds, s_VisibleProbeVolumeBoundsBuffer); cmd.SetGlobalBuffer(HDShaderIDs._ProbeVolumeDatas, s_VisibleProbeVolumeDataBuffer); cmd.SetGlobalTexture(HDShaderIDs._ProbeVolumeAtlasSH, m_ProbeVolumeAtlasSHRTHandle); - cmd.SetGlobalTexture(HDShaderIDs._ProbeVolumeAtlasOctahedralDepth, m_ProbeVolumeAtlasOctahedralDepthRTHandle); + + if (ShaderConfig.s_ProbeVolumesBilateralFilteringMode == ProbeVolumesBilateralFilteringModes.OctahedralDepth) + { + cmd.SetGlobalTexture(HDShaderIDs._ProbeVolumeAtlasOctahedralDepth, m_ProbeVolumeAtlasOctahedralDepthRTHandle); + } } internal void PushProbeVolumesGlobalParamsDefault(HDCamera hdCamera, CommandBuffer cmd) @@ -412,7 +429,11 @@ internal void PushProbeVolumesGlobalParamsDefault(HDCamera hdCamera, CommandBuff cmd.SetGlobalBuffer(HDShaderIDs._ProbeVolumeBounds, s_VisibleProbeVolumeBoundsBufferDefault); cmd.SetGlobalBuffer(HDShaderIDs._ProbeVolumeDatas, s_VisibleProbeVolumeDataBufferDefault); cmd.SetGlobalTexture(HDShaderIDs._ProbeVolumeAtlasSH, TextureXR.GetBlackTexture3D()); - cmd.SetGlobalTexture(HDShaderIDs._ProbeVolumeAtlasOctahedralDepth, Texture2D.blackTexture); + + if (ShaderConfig.s_ProbeVolumesBilateralFilteringMode == ProbeVolumesBilateralFilteringModes.OctahedralDepth) + { + cmd.SetGlobalTexture(HDShaderIDs._ProbeVolumeAtlasOctahedralDepth, Texture2D.blackTexture); + } } internal void ReleaseProbeVolumeFromAtlas(ProbeVolume volume) @@ -426,7 +447,11 @@ internal void ReleaseProbeVolumeFromAtlas(ProbeVolume volume) int key = volume.GetID(); probeVolumeAtlas.ReleaseTextureSlot(key); - probeVolumeAtlasOctahedralDepth.ReleaseTextureSlot(key); + + if (ShaderConfig.s_ProbeVolumesBilateralFilteringMode == ProbeVolumesBilateralFilteringModes.OctahedralDepth) + { + probeVolumeAtlasOctahedralDepth.ReleaseTextureSlot(key); + } } internal bool EnsureProbeVolumeInAtlas(ScriptableRenderContext renderContext, CommandBuffer cmd, ProbeVolume volume) @@ -655,9 +680,12 @@ internal void ClearProbeVolumeAtlasIfRequested(CommandBuffer cmd) cmd.SetRenderTarget(m_ProbeVolumeAtlasSHRTHandle.rt, 0, CubemapFace.Unknown, 0); cmd.ClearRenderTarget(false, true, Color.black, 0.0f); - probeVolumeAtlasOctahedralDepth.ResetAllocator(); - cmd.SetRenderTarget(m_ProbeVolumeAtlasOctahedralDepthRTHandle.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) @@ -671,7 +699,9 @@ ProbeVolumeList PrepareVisibleProbeVolumeList(ScriptableRenderContext renderCont return probeVolumes; var settings = hdCamera.volumeStack.GetComponent(); - bool octahedralDepthOcclusionFilterIsEnabled = settings.leakMitigationMode.value == LeakMitigationMode.OctahedralDepthOcclusionFilter; + bool octahedralDepthOcclusionFilterIsEnabled = + ShaderConfig.s_ProbeVolumesBilateralFilteringMode == ProbeVolumesBilateralFilteringModes.OctahedralDepth + && settings.leakMitigationMode.value == LeakMitigationMode.OctahedralDepthOcclusionFilter; using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.PrepareProbeVolumeList))) { @@ -889,9 +919,13 @@ void DisplayProbeVolumeAtlas(CommandBuffer cmd, Material debugMaterial, float sc selectedProbeVolume.parameters.resolutionZ ); } - if (probeVolumeAtlasOctahedralDepth.TryGetScaleBias(out Vector4 selectedProbeVolumeOctahedralDepthScaleBias, selectedProbeVolumeKey)) + + if (ShaderConfig.s_ProbeVolumesBilateralFilteringMode == ProbeVolumesBilateralFilteringModes.OctahedralDepth) { - atlasTextureOctahedralDepthScaleBias = selectedProbeVolumeOctahedralDepthScaleBias; + if (probeVolumeAtlasOctahedralDepth.TryGetScaleBias(out Vector4 selectedProbeVolumeOctahedralDepthScaleBias, selectedProbeVolumeKey)) + { + atlasTextureOctahedralDepthScaleBias = selectedProbeVolumeOctahedralDepthScaleBias; + } } } } @@ -921,8 +955,11 @@ void DisplayProbeVolumeAtlas(CommandBuffer cmd, Material debugMaterial, float sc 1.0f / (float)m_ProbeVolumeAtlasSHRTDepthSliceCount )); - propertyBlock.SetTexture(HDShaderIDs._AtlasTextureOctahedralDepth, m_ProbeVolumeAtlasOctahedralDepthRTHandle); - propertyBlock.SetVector(HDShaderIDs._AtlasTextureOctahedralDepthScaleBias, atlasTextureOctahedralDepthScaleBias); + if (ShaderConfig.s_ProbeVolumesBilateralFilteringMode == ProbeVolumesBilateralFilteringModes.OctahedralDepth) + { + propertyBlock.SetTexture(HDShaderIDs._AtlasTextureOctahedralDepth, m_ProbeVolumeAtlasOctahedralDepthRTHandle); + propertyBlock.SetVector(HDShaderIDs._AtlasTextureOctahedralDepthScaleBias, atlasTextureOctahedralDepthScaleBias); + } propertyBlock.SetVector(HDShaderIDs._ValidRange, validRange); propertyBlock.SetInt(HDShaderIDs._ProbeVolumeAtlasSliceMode, sliceMode); cmd.SetViewport(new Rect(screenX, screenY, screenSizeX, screenSizeY)); diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeShaderVariables.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeShaderVariables.hlsl index cd95990c98c..55dd6fe473a 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeShaderVariables.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeShaderVariables.hlsl @@ -15,7 +15,10 @@ StructuredBuffer _ProbeVolumeDatas; TEXTURE3D(_ProbeVolumeAtlasSH); +#if SHADEROPTIONS_PROBE_VOLUMES_BILATERAL_FILTERING == PROBEVOLUMESBILATERALFILTERINGMODES_OCTAHEDRAL_DEPTH TEXTURE2D(_ProbeVolumeAtlasOctahedralDepth); #endif +#endif + #endif // endof PROBE_VOLUME_SHADER_VARIABLES diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/SphericalHarmonics.cs.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Lighting/SphericalHarmonics.cs.hlsl index 6617fdb0255..21449991a3b 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/SphericalHarmonics.cs.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/SphericalHarmonics.cs.hlsl @@ -19,29 +19,4 @@ float3 GetShrgb(SphericalHarmonicsL0 value) return value.shrgb; } -// Generated from UnityEngine.Rendering.HighDefinition.SphericalHarmonicsL1 -// PackingRules = Exact -struct SphericalHarmonicsL1 -{ - float4 shAr; - float4 shAg; - float4 shAb; -}; - -// -// Accessors for UnityEngine.Rendering.HighDefinition.SphericalHarmonicsL1 -// -float4 GetShAr(SphericalHarmonicsL1 value) -{ - return value.shAr; -} -float4 GetShAg(SphericalHarmonicsL1 value) -{ - return value.shAg; -} -float4 GetShAb(SphericalHarmonicsL1 value) -{ - return value.shAb; -} - #endif From 2b830005d53196f83168e3dc93a3951919ad9916 Mon Sep 17 00:00:00 2001 From: pastasfuture Date: Mon, 1 Jun 2020 18:37:31 -0700 Subject: [PATCH 13/26] Probe Volumes: Do not allocate or bake octahedral depth data in ProbeVolumeAsset if ShaderConfig does not enable octahedral depth bilateral filtering. --- .../ProbeVolume/ProbeVolumeUI.Drawer.cs | 12 ++++++ .../ProbeVolume/ProbeVolumeUI.Skin.cs | 2 + .../Lighting/ProbeVolume/ProbeVolume.cs | 37 +++++++++++++------ 3 files changed, 40 insertions(+), 11 deletions(-) diff --git a/com.unity.render-pipelines.high-definition/Editor/Lighting/ProbeVolume/ProbeVolumeUI.Drawer.cs b/com.unity.render-pipelines.high-definition/Editor/Lighting/ProbeVolume/ProbeVolumeUI.Drawer.cs index a394d90fc26..e8ea9e459fe 100644 --- a/com.unity.render-pipelines.high-definition/Editor/Lighting/ProbeVolume/ProbeVolumeUI.Drawer.cs +++ b/com.unity.render-pipelines.high-definition/Editor/Lighting/ProbeVolume/ProbeVolumeUI.Drawer.cs @@ -87,6 +87,18 @@ static void Drawer_BakeToolBar(SerializedProbeVolume serialized, Editor owner) EditorGUILayout.HelpBox(Styles.k_featureEncodingModeMismatchError, MessageType.Error); } + if (ShaderConfig.s_ProbeVolumesBilateralFilteringMode == ProbeVolumesBilateralFilteringModes.OctahedralDepth + && asset != null && asset.payload.dataOctahedralDepth == null) + { + EditorGUILayout.HelpBox(Styles.k_featureOctahedralDepthEnabledNoData, MessageType.Error); + } + + if (ShaderConfig.s_ProbeVolumesBilateralFilteringMode != ProbeVolumesBilateralFilteringModes.OctahedralDepth + && asset != null && asset.payload.dataOctahedralDepth != null) + { + EditorGUILayout.HelpBox(Styles.k_featureOctahedralDepthDisableYesData, MessageType.Error); + } + EditorGUILayout.PropertyField(serialized.probeVolumeAsset, Styles.s_DataAssetLabel); EditorGUILayout.Slider(serialized.backfaceTolerance, 0.0f, 1.0f, Styles.s_BackfaceToleranceLabel); diff --git a/com.unity.render-pipelines.high-definition/Editor/Lighting/ProbeVolume/ProbeVolumeUI.Skin.cs b/com.unity.render-pipelines.high-definition/Editor/Lighting/ProbeVolume/ProbeVolumeUI.Skin.cs index 19609bdd2e6..769a2965c74 100644 --- a/com.unity.render-pipelines.high-definition/Editor/Lighting/ProbeVolume/ProbeVolumeUI.Skin.cs +++ b/com.unity.render-pipelines.high-definition/Editor/Lighting/ProbeVolume/ProbeVolumeUI.Skin.cs @@ -10,6 +10,8 @@ internal static class Styles internal const string k_featureEnableInfo = "\nProbe Volumes feature is disabled. To enable, set:\nProbeVolumesEvaluationMode = ProbeVolumesEvaluationModes.MaterialPass\ninside of ShaderConfig.cs. Then inside of the editor run:\nEdit->Render Pipeline->Generate Shader Includes\nProbe Volumes feature must also be enabled inside of your HDRenderPipelineAsset."; internal const string k_featureAdditiveBlendingDisabledError = "Error: ProbeVolumesAdditiveBlending feature is disabled inside of ShaderConfig.cs.\nThis probe volume will not be rendered.\nTo enable, set:\nProbeVolumesAdditiveBlending = 1\ninside of ShaderConfig.cs. Then inside of the editor run:\nEdit->Render Pipeline->Generate Shader Includes."; internal const string k_featureEncodingModeMismatchError = "Error: ProbeVolumesEncodingMode inside of ShaderConfig.cs is configured to a different mode than this probe volume was previously baked with.\nThis probe volume will not be rendered.\nTo correct this:\nA) Rebake this probe volume.\nor\nB) Change ProbeVolumesEncodingMode inside of ShaderConfig.cs. Then inside of the editor run:\nEdit->Render Pipeline->Generate Shader Includes."; + internal const string k_featureOctahedralDepthEnabledNoData = "Error: ProbeVolumesBilateralFilteringMode inside of ShaderConfig.cs is set to OctahedralDepth, but asset was baked with OctahedralDepth disabled.\nPlease rebake if you would like this probe volume to use octahedral depth filtering."; + internal const string k_featureOctahedralDepthDisableYesData = "Error: ProbeVolumesBilateralFilteringMode inside of ShaderConfig.cs is not set to OctahedralDepth, but was baked with OctahedralDepth enabled.\nPlease rebake to discard octahedral depth data from asset."; internal const string k_VolumeHeader = "Volume"; internal const string k_ProbesHeader = "Probes"; internal const string k_BakingHeader = "Baking"; diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.cs b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.cs index d975c64c63e..e95fbff7335 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.cs @@ -60,7 +60,12 @@ public static void Allocate(ref ProbeVolumePayload payload, ProbeVolumesEncoding // TODO: Only allocate dataValidity and dataOctahedralDepth if those payload slices are in use. payload.dataValidity = new float[length]; - payload.dataOctahedralDepth = new float[length * 8 * 8]; + + payload.dataOctahedralDepth = null; + if (ShaderConfig.s_ProbeVolumesBilateralFilteringMode == ProbeVolumesBilateralFilteringModes.OctahedralDepth) + { + payload.dataOctahedralDepth = new float[length * 8 * 8]; + } } public static void Ensure(ref ProbeVolumePayload payload, ProbeVolumesEncodingModes encodingMode, int length) @@ -752,9 +757,22 @@ internal void OnProbesBakeCompleted() var sh = new NativeArray(numProbes, Allocator.Temp, NativeArrayOptions.UninitializedMemory); var validity = new NativeArray(numProbes, Allocator.Temp, NativeArrayOptions.UninitializedMemory); - var octahedralDepth = new NativeArray(numProbes * 8 * 8, Allocator.Temp, NativeArrayOptions.UninitializedMemory); - if(UnityEditor.Experimental.Lightmapping.GetAdditionalBakedProbes(GetID(), sh, validity, octahedralDepth)) + bool bakeIsSuccessful = false; + NativeArray octahedralDepth; + if (ShaderConfig.s_ProbeVolumesBilateralFilteringMode == ProbeVolumesBilateralFilteringModes.OctahedralDepth) + { + octahedralDepth = new NativeArray(numProbes * 8 * 8, Allocator.Temp, NativeArrayOptions.UninitializedMemory); + bakeIsSuccessful = UnityEditor.Experimental.Lightmapping.GetAdditionalBakedProbes(GetID(), sh, validity, octahedralDepth); + } + else + { + // Suppress octahedralDepth uninitialized warnings. + octahedralDepth = new NativeArray(0, Allocator.Temp, NativeArrayOptions.UninitializedMemory); + bakeIsSuccessful = UnityEditor.Experimental.Lightmapping.GetAdditionalBakedProbes(GetID(), sh, validity); + } + + if(bakeIsSuccessful) { if (!probeVolumeAsset || GetID() != probeVolumeAsset.instanceID) probeVolumeAsset = ProbeVolumeAsset.CreateAsset(GetID()); @@ -852,14 +870,11 @@ internal void OnProbesBakeCompleted() } } - for (int i = 0, iLen = sh.Length; i < iLen; ++i) - { - probeVolumeAsset.payload.dataValidity[i] = validity[i]; + validity.CopyTo(probeVolumeAsset.payload.dataValidity); - for (int j = 0; j < 64; ++j) - { - probeVolumeAsset.payload.dataOctahedralDepth[i * 64 + j] = octahedralDepth[i * 64 + j]; - } + if (ShaderConfig.s_ProbeVolumesBilateralFilteringMode == ProbeVolumesBilateralFilteringModes.OctahedralDepth) + { + octahedralDepth.CopyTo(probeVolumeAsset.payload.dataOctahedralDepth); } if (UnityEditor.Lightmapping.giWorkflowMode != UnityEditor.Lightmapping.GIWorkflowMode.Iterative) @@ -872,7 +887,7 @@ internal void OnProbesBakeCompleted() sh.Dispose(); validity.Dispose(); - octahedralDepth.Dispose(); + if (ShaderConfig.s_ProbeVolumesBilateralFilteringMode == ProbeVolumesBilateralFilteringModes.OctahedralDepth) { octahedralDepth.Dispose(); } } internal void OnBakeCompleted() From 357fdf1350e9eaf14cc33385196022febcc27ff7 Mon Sep 17 00:00:00 2001 From: pastasfuture Date: Mon, 1 Jun 2020 18:46:28 -0700 Subject: [PATCH 14/26] Probe Volumes: Always allocate and pass octahedralDepth array into GetAdditionalBakedProbes() because the API call without the octahedralDepth argument has been flagged as obsolete. In the future, we should remove this unnecessary allocation. --- .../Lighting/ProbeVolume/ProbeVolume.cs | 22 +++++-------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.cs b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.cs index e95fbff7335..23f1a400c91 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.cs @@ -757,22 +757,12 @@ internal void OnProbesBakeCompleted() var sh = new NativeArray(numProbes, Allocator.Temp, NativeArrayOptions.UninitializedMemory); var validity = new NativeArray(numProbes, Allocator.Temp, NativeArrayOptions.UninitializedMemory); - - bool bakeIsSuccessful = false; - NativeArray octahedralDepth; - if (ShaderConfig.s_ProbeVolumesBilateralFilteringMode == ProbeVolumesBilateralFilteringModes.OctahedralDepth) - { - octahedralDepth = new NativeArray(numProbes * 8 * 8, Allocator.Temp, NativeArrayOptions.UninitializedMemory); - bakeIsSuccessful = UnityEditor.Experimental.Lightmapping.GetAdditionalBakedProbes(GetID(), sh, validity, octahedralDepth); - } - else - { - // Suppress octahedralDepth uninitialized warnings. - octahedralDepth = new NativeArray(0, Allocator.Temp, NativeArrayOptions.UninitializedMemory); - bakeIsSuccessful = UnityEditor.Experimental.Lightmapping.GetAdditionalBakedProbes(GetID(), sh, validity); - } - if(bakeIsSuccessful) + // TODO: Currently, we need to always allocate and pass this octahedralDepth array into GetAdditionalBakedProbes(). + // In the future, we should add an API call for GetAdditionalBakedProbes() without octahedralDepth required. + var octahedralDepth = new NativeArray(numProbes * 8 * 8, Allocator.Temp, NativeArrayOptions.UninitializedMemory); + + if(UnityEditor.Experimental.Lightmapping.GetAdditionalBakedProbes(GetID(), sh, validity, octahedralDepth)) { if (!probeVolumeAsset || GetID() != probeVolumeAsset.instanceID) probeVolumeAsset = ProbeVolumeAsset.CreateAsset(GetID()); @@ -887,7 +877,7 @@ internal void OnProbesBakeCompleted() sh.Dispose(); validity.Dispose(); - if (ShaderConfig.s_ProbeVolumesBilateralFilteringMode == ProbeVolumesBilateralFilteringModes.OctahedralDepth) { octahedralDepth.Dispose(); } + octahedralDepth.Dispose(); } internal void OnBakeCompleted() From c886f8938d0b52395a0cc390df5c5126df2ca448 Mon Sep 17 00:00:00 2001 From: pastasfuture Date: Tue, 2 Jun 2020 00:53:19 -0700 Subject: [PATCH 15/26] Probe Volumes: Explicit ProbeVolumeAccumulateSphericalHarmonicsL0, L1, and L2 variants exist so we can choose to sample any order we want in any atlas configuration. i.e: sample L0 for vfx with L2 atlas. Upsampling is lossy, but supported, i.e: sample L2 in L0 atlas. Atlas coefficient layout was changed to accomodate this, and ProbeVolumeAccumulate function was moved to its own include so that each order variant can be generated with the preprocessor. --- .../Runtime/Lighting/LightLoop/LightLoop.hlsl | 22 +- .../Lighting/ProbeVolume/ProbeVolume.cs | 147 ++++++---- .../Lighting/ProbeVolume/ProbeVolume.hlsl | 274 ++++++++++-------- .../ProbeVolume/ProbeVolumeAccumulate.hlsl | 152 ++++++++++ .../ProbeVolumeAccumulate.hlsl.meta | 10 + .../ProbeVolume/ProbeVolumeAtlasBlit.compute | 101 ++++--- .../Runtime/Material/BuiltinGIUtilities.hlsl | 22 +- 7 files changed, 507 insertions(+), 221 deletions(-) create mode 100644 com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeAccumulate.hlsl create mode 100644 com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeAccumulate.hlsl.meta diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.hlsl index 216193fa845..d54bee80d41 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.hlsl @@ -492,11 +492,23 @@ void LightLoop( float3 V, PositionInputs posInput, PreLightData preLightData, BS float probeVolumeHierarchyWeight = uninitialized ? 0.0f : 1.0f; // Note: we aren't suppose to access normalWS in lightloop, but bsdfData.normalWS is always define for any material. So this is safe. - ProbeVolumeCoefficients coefficients; - AccumulateProbeVolumes(posInput, bsdfData.normalWS, builtinData.renderingLayers, coefficients, probeVolumeHierarchyWeight); - builtinDataProbeVolumes.bakeDiffuseLighting += EvaluateProbeVolumeCoefficients(bsdfData.normalWS, coefficients); - builtinDataProbeVolumes.backBakeDiffuseLighting += EvaluateProbeVolumeCoefficients(-bsdfData.normalWS, coefficients); - +#if SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L0 + ProbeVolumeSphericalHarmonicsL0 coefficients; + ProbeVolumeAccumulateSphericalHarmonicsL0(posInput, bsdfData.normalWS, builtinData.renderingLayers, coefficients, probeVolumeHierarchyWeight); + builtinDataProbeVolumes.bakeDiffuseLighting += EvaluateProbeVolumeSphericalHarmonicsL0(bsdfData.normalWS, coefficients); + builtinDataProbeVolumes.backBakeDiffuseLighting += EvaluateProbeVolumeSphericalHarmonicsL0(-bsdfData.normalWS, coefficients); +#elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L1 + ProbeVolumeSphericalHarmonicsL1 coefficients; + ProbeVolumeAccumulateSphericalHarmonicsL1(posInput, bsdfData.normalWS, builtinData.renderingLayers, coefficients, probeVolumeHierarchyWeight); + builtinDataProbeVolumes.bakeDiffuseLighting += EvaluateProbeVolumeSphericalHarmonicsL1(bsdfData.normalWS, coefficients); + builtinDataProbeVolumes.backBakeDiffuseLighting += EvaluateProbeVolumeSphericalHarmonicsL1(-bsdfData.normalWS, coefficients); +#elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L2 + ProbeVolumeSphericalHarmonicsL2 coefficients; + ProbeVolumeAccumulateSphericalHarmonicsL2(posInput, bsdfData.normalWS, builtinData.renderingLayers, coefficients, probeVolumeHierarchyWeight); + builtinDataProbeVolumes.bakeDiffuseLighting += EvaluateProbeVolumeSphericalHarmonicsL2(bsdfData.normalWS, coefficients); + builtinDataProbeVolumes.backBakeDiffuseLighting += EvaluateProbeVolumeSphericalHarmonicsL2(-bsdfData.normalWS, coefficients); +#endif + float probeVolumeHierarchyWeightFrontFace = probeVolumeHierarchyWeight; float probeVolumeHierarchyWeightBackFace = probeVolumeHierarchyWeight; builtinDataProbeVolumes.bakeDiffuseLighting += EvaluateProbeVolumeAmbientProbeFallback(bsdfData.normalWS, probeVolumeHierarchyWeightFrontFace); diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.cs b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.cs index 23f1a400c91..18291ff7c06 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.cs @@ -776,20 +776,52 @@ internal void OnProbesBakeCompleted() int shStride = ProbeVolumePayload.GetSHStride(probeVolumeAsset.payload.encodingMode); - // TODO: Remove this data copy. Would require the lightmapper to have GetAdditionalBakedProbes with L0, L1, and L2 variants. + // SH Coefficients are serialized in this order, regardless of their format. + // SH0 will only serialize the first 3, + // SH1 will only serialize the first 12 + // SH2 will serialize all 27 + // This is not the order SphericalHarmonicsL2 natively stores these coefficients, + // and it is also not the order that GPU EntityLighting.hlsl functions expect them in. + // This order is optimized for minimizing the number of coefficients fetched on the GPU + // when sampling various SH formats. + // i.e: If the atlas is configured for SH2, but only SH0 is requested by a specific shader, + // only the first three coefficients need to be fetched. + // The atlasing code may make changes to the way this data is laid out in textures, + // but having them laid out in polynomial order on disk makes writing the atlas transcodings easier. + // Note: the coefficients in the L2 case are not fully normalized, + // The data in the SH probe sample passed here is expected to already be normalized with kNormalizationConstants. + // The complete normalization must be deferred until sample time on the GPU, since it should only be applied for SH2. + // GPU code will be responsible for performing final normalization + swizzle into formats + // that SampleSH9(), and SHEvalLinearL0L1() expect. + // Note: the names for these coefficients is consistent with Unity's internal spherical harmonics use, + // and are originally from: https://www.ppsloan.org/publications/StupidSH36.pdf + /* + { + // Constant: (used by L0, L1, and L2) + shAr.w, shAg.w, shAb.w, + + // Linear: (used by L1 and L2) + shAr.x, shAr.y, shAr.z, + shAg.x, shAg.y, shAg.z, + shAb.x, shAb.y, shAb.z, + + // Quadratic: (used by L2) + shBr.x, shBr.y, shBr.z, shBr.w, + shBg.x, shBg.y, shBg.z, shBg.w, + shBb.x, shBb.y, shBb.z, shBb.w, + shCr.x, shCr.y, shCr.z + } + */ switch (probeVolumeAsset.payload.encodingMode) { case ProbeVolumesEncodingModes.SphericalHarmonicsL0: { for (int i = 0, iLen = sh.Length; i < iLen; ++i) { - SphericalHarmonicsL0 sh0 = new SphericalHarmonicsL0 - { - // TODO: May need some additional data transform here to handle downgrading from SH2 to SH0. - shrgb = new Vector3(sh[i][0, 0], sh[i][1, 0], sh[i][2, 0]) - }; - - ProbeVolumePayload.SetSphericalHarmonicsL0FromIndex(ref probeVolumeAsset.payload, ref sh0, i); + // Constant (DC terms): + probeVolumeAsset.payload.dataSH[i * shStride + 0] = sh[i][0, 0]; // shAr.w + probeVolumeAsset.payload.dataSH[i * shStride + 1] = sh[i][1, 0]; // shAg.w + probeVolumeAsset.payload.dataSH[i * shStride + 2] = sh[i][2, 0]; // shAb.w } break; } @@ -798,20 +830,24 @@ internal void OnProbesBakeCompleted() { for (int i = 0, iLen = sh.Length; i < iLen; ++i) { - // https://www.ppsloan.org/publications/StupidSH36.pdf - // The shader and, by extension, SetSHConstants expect the values to be normalized. - // The data in the SH probe sample passed here is expected to already be normalized - // with kNormalizationConstants. - // - // Constant + Linear - SphericalHarmonicsL1 sh1 = new SphericalHarmonicsL1 - { - shAr = new Vector4(sh[i][0, 3], sh[i][0, 1], sh[i][0, 2], sh[i][0, 0]), - shAg = new Vector4(sh[i][1, 3], sh[i][1, 1], sh[i][1, 2], sh[i][1, 0]), - shAb = new Vector4(sh[i][2, 3], sh[i][2, 1], sh[i][2, 2], sh[i][2, 0]) - }; - - ProbeVolumePayload.SetSphericalHarmonicsL1FromIndex(ref probeVolumeAsset.payload, ref sh1, i); + // Constant (DC terms): + probeVolumeAsset.payload.dataSH[i * shStride + 0] = sh[i][0, 0]; // shAr.w + probeVolumeAsset.payload.dataSH[i * shStride + 1] = sh[i][1, 0]; // shAg.w + probeVolumeAsset.payload.dataSH[i * shStride + 2] = sh[i][2, 0]; // shAb.w + + // Linear: (used by L1 and L2) + // Swizzle the coefficients to be in { x, y, z } order. + probeVolumeAsset.payload.dataSH[i * shStride + 3] = sh[i][0, 3]; // shAr.x + probeVolumeAsset.payload.dataSH[i * shStride + 4] = sh[i][0, 1]; // shAr.y + probeVolumeAsset.payload.dataSH[i * shStride + 5] = sh[i][0, 2]; // shAr.z + + probeVolumeAsset.payload.dataSH[i * shStride + 3] = sh[i][1, 3]; // shAg.x + probeVolumeAsset.payload.dataSH[i * shStride + 4] = sh[i][1, 1]; // shAg.y + probeVolumeAsset.payload.dataSH[i * shStride + 5] = sh[i][1, 2]; // shAg.z + + probeVolumeAsset.payload.dataSH[i * shStride + 6] = sh[i][2, 3]; // shAb.x + probeVolumeAsset.payload.dataSH[i * shStride + 7] = sh[i][2, 1]; // shAb.y + probeVolumeAsset.payload.dataSH[i * shStride + 8] = sh[i][2, 2]; // shAb.z } break; } @@ -820,35 +856,44 @@ internal void OnProbesBakeCompleted() { for (int i = 0, iLen = sh.Length; i < iLen; ++i) { - // https://www.ppsloan.org/publications/StupidSH36.pdf - // The shader and, by extension, SetSHConstants expect the values to be normalized. - // The data in the SH probe sample passed here is expected to already be normalized - // with kNormalizationConstants. - // - // Constant + Linear - for (int iC = 0; iC < 3; iC++) - { - // In the shader we multiply the normal is not swizzled, so it's normal.xyz. - // Swizzle the coefficients to be in { x, y, z, DC } order. - probeVolumeAsset.payload.dataSH[i * shStride + iC * 4 + 0] = sh[i][iC, 3]; - probeVolumeAsset.payload.dataSH[i * shStride + iC * 4 + 1] = sh[i][iC, 1]; - probeVolumeAsset.payload.dataSH[i * shStride + iC * 4 + 2] = sh[i][iC, 2]; - probeVolumeAsset.payload.dataSH[i * shStride + iC * 4 + 3] = sh[i][iC, 0] - sh[i][iC, 6]; - } - - // Quadratic polynomials - for (int iC = 0; iC < 3; iC++) - { - probeVolumeAsset.payload.dataSH[i * shStride + (iC + 3) * 4 + 0] = sh[i][iC, 4]; - probeVolumeAsset.payload.dataSH[i * shStride + (iC + 3) * 4 + 1] = sh[i][iC, 5]; - probeVolumeAsset.payload.dataSH[i * shStride + (iC + 3) * 4 + 2] = sh[i][iC, 6] * 3.0f; - probeVolumeAsset.payload.dataSH[i * shStride + (iC + 3) * 4 + 3] = sh[i][iC, 7]; - } - - // Final quadratic polynomial - probeVolumeAsset.payload.dataSH[i * shStride + 6 * 4 + 0] = sh[i][0, 8]; - probeVolumeAsset.payload.dataSH[i * shStride + 6 * 4 + 1] = sh[i][1, 8]; - probeVolumeAsset.payload.dataSH[i * shStride + 6 * 4 + 2] = sh[i][2, 8]; + // Constant (DC terms): + probeVolumeAsset.payload.dataSH[i * shStride + 0] = sh[i][0, 0]; // shAr.w + probeVolumeAsset.payload.dataSH[i * shStride + 1] = sh[i][1, 0]; // shAg.w + probeVolumeAsset.payload.dataSH[i * shStride + 2] = sh[i][2, 0]; // shAb.w + + // Linear: (used by L1 and L2) + // Swizzle the coefficients to be in { x, y, z } order. + probeVolumeAsset.payload.dataSH[i * shStride + 3] = sh[i][0, 3]; // shAr.x + probeVolumeAsset.payload.dataSH[i * shStride + 4] = sh[i][0, 1]; // shAr.y + probeVolumeAsset.payload.dataSH[i * shStride + 5] = sh[i][0, 2]; // shAr.z + + probeVolumeAsset.payload.dataSH[i * shStride + 6] = sh[i][1, 3]; // shAg.x + probeVolumeAsset.payload.dataSH[i * shStride + 7] = sh[i][1, 1]; // shAg.y + probeVolumeAsset.payload.dataSH[i * shStride + 8] = sh[i][1, 2]; // shAg.z + + probeVolumeAsset.payload.dataSH[i * shStride + 9] = sh[i][2, 3]; // shAb.x + probeVolumeAsset.payload.dataSH[i * shStride + 10] = sh[i][2, 1]; // shAb.y + probeVolumeAsset.payload.dataSH[i * shStride + 11] = sh[i][2, 2]; // shAb.z + + // Quadratic: (used by L2) + probeVolumeAsset.payload.dataSH[i * shStride + 12] = sh[i][0, 4]; // shBr.x + probeVolumeAsset.payload.dataSH[i * shStride + 13] = sh[i][0, 5]; // shBr.y + probeVolumeAsset.payload.dataSH[i * shStride + 14] = sh[i][0, 6]; // shBr.z + probeVolumeAsset.payload.dataSH[i * shStride + 15] = sh[i][0, 7]; // shBr.w + + probeVolumeAsset.payload.dataSH[i * shStride + 16] = sh[i][1, 4]; // shBg.x + probeVolumeAsset.payload.dataSH[i * shStride + 17] = sh[i][1, 5]; // shBg.y + probeVolumeAsset.payload.dataSH[i * shStride + 18] = sh[i][1, 6]; // shBg.z + probeVolumeAsset.payload.dataSH[i * shStride + 19] = sh[i][1, 7]; // shBg.w + + probeVolumeAsset.payload.dataSH[i * shStride + 20] = sh[i][2, 4]; // shBb.x + probeVolumeAsset.payload.dataSH[i * shStride + 21] = sh[i][2, 5]; // shBb.y + probeVolumeAsset.payload.dataSH[i * shStride + 22] = sh[i][2, 6]; // shBb.z + probeVolumeAsset.payload.dataSH[i * shStride + 23] = sh[i][2, 7]; // shBb.w + + probeVolumeAsset.payload.dataSH[i * shStride + 24] = sh[i][0, 8]; // shCr.x + probeVolumeAsset.payload.dataSH[i * shStride + 25] = sh[i][1, 8]; // shCr.y + probeVolumeAsset.payload.dataSH[i * shStride + 26] = sh[i][2, 8]; // shCr.z } break; } diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl index ec31738e2f7..165aab70343 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl @@ -372,140 +372,131 @@ float3 ProbeVolumeComputeTexel3DFromBilateralFilter( #endif } -struct ProbeVolumeCoefficients +struct ProbeVolumeSphericalHarmonicsL0 { -#if SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L0 - float4 data[(3u + 3u) / 4u]; -#elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L1 - float4 data[(12u + 3u) / 4u]; -#elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L2 - float4 data[(27u + 3u) / 4u]; -#endif + float4 data[1]; }; -void AccumulateProbeVolumes(PositionInputs posInput, float3 normalWS, uint renderingLayers, out ProbeVolumeCoefficients coefficients, inout float weightHierarchy) +struct ProbeVolumeSphericalHarmonicsL1 { - ZERO_INITIALIZE(ProbeVolumeCoefficients, coefficients); + float4 data[3]; +}; -#if !SHADEROPTIONS_PROBE_VOLUMES_ADDITIVE_BLENDING - if (weightHierarchy >= 1.0) { return; } -#endif +struct ProbeVolumeSphericalHarmonicsL2 +{ + float4 data[7]; +}; - uint probeVolumeStart, probeVolumeCount; - bool fastPath; - ProbeVolumeGetCountAndStartAndFastPath(posInput, probeVolumeStart, probeVolumeCount, fastPath); +// See ProbeVolumeAtlasBlit.compute for atlas coefficient layout information. +void ProbeVolumeSampleAccumulateSphericalHarmonicsL0(float3 probeVolumeAtlasUVW, float weight, inout ProbeVolumeSphericalHarmonicsL0 coefficients) +{ + coefficients.data[0].xyz += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 0), 0).xyz * weight; +} - // Scalarized loop, same rationale of the punctual light version - uint v_probeVolumeListOffset = 0; - uint v_probeVolumeIdx = probeVolumeStart; - while (v_probeVolumeListOffset < probeVolumeCount) - { - v_probeVolumeIdx = ProbeVolumeFetchIndex(probeVolumeStart, v_probeVolumeListOffset); - uint s_probeVolumeIdx = ProbeVolumeScalarizeElementIndex(v_probeVolumeIdx, fastPath); - if (s_probeVolumeIdx == -1) { break; } +void ProbeVolumeSampleAccumulateSphericalHarmonicsL1(float3 probeVolumeAtlasUVW, float weight, inout ProbeVolumeSphericalHarmonicsL1 coefficients) +{ +#if SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L0 + // Requesting SH1, but atlas only contains SH0. + // Only accumulate SH0 coefficients. + coefficients.data[0].xyz += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 0), 0).xyz * weight; + +#elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L1 || SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L2 + coefficients.data[0] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 0), 0) * weight; + coefficients.data[1] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 1), 0) * weight; + coefficients.data[2] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 2), 0) * weight; +#endif +} - // Scalar load. - ProbeVolumeEngineData s_probeVolumeData = _ProbeVolumeDatas[s_probeVolumeIdx]; - OrientedBBox s_probeVolumeBounds = _ProbeVolumeBounds[s_probeVolumeIdx]; +void ProbeVolumeSampleAccumulateSphericalHarmonicsL2(float3 probeVolumeAtlasUVW, float weight, inout ProbeVolumeSphericalHarmonicsL2 coefficients) +{ +#if SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L0 + // Requesting SH2, but atlas only contains SH0. + // Only accumulate SH0 coefficients. + coefficients.data[0].xyz += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 0), 0).xyz * weight; - if (ProbeVolumeIsAllWavesComplete(weightHierarchy, s_probeVolumeData.volumeBlendMode)) { return; } +#elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L1 + // Requesting SH2, but atlas only contains SH1. + // Only accumulate SH1 coefficients. + coefficients.data[0] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 0), 0) * weight; + coefficients.data[1] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 1), 0) * weight; + coefficients.data[2] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 2), 0) * weight; - // If current scalar and vector light index match, we process the light. The v_probeVolumeListOffset for current thread is increased. - // Note that the following should really be ==, however, since helper lanes are not considered by WaveActiveMin, such helper lanes could - // end up with a unique v_envLightIdx value that is smaller than s_envLightIdx hence being stuck in a loop. All the active lanes will not have this problem. - if (s_probeVolumeIdx >= v_probeVolumeIdx) - { - v_probeVolumeListOffset++; +#elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L2 + coefficients.data[0] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 0), 0) * weight; + coefficients.data[1] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 1), 0) * weight; + coefficients.data[2] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 2), 0) * weight; -#if SHADEROPTIONS_PROBE_VOLUMES_ADDITIVE_BLENDING - bool isWeightAccumulated = s_probeVolumeData.volumeBlendMode == VOLUMEBLENDMODE_NORMAL; -#else - const bool isWeightAccumulated = true; -#endif + coefficients.data[3] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 3), 0) * weight; + coefficients.data[4] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 4), 0) * weight; + coefficients.data[5] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 5), 0) * weight; - if (weightHierarchy >= 1.0 && isWeightAccumulated) { continue; } + coefficients.data[6].xyz += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 6), 0).xyz * weight; +#endif +} - if (!IsMatchingLightLayer(s_probeVolumeData.lightLayers, renderingLayers)) { continue; } - float weightCurrent = 0.0; - { - float3x3 obbFrame; - float3 obbExtents; - float3 obbCenter; - ProbeVolumeComputeOBBBoundsToFrame(s_probeVolumeBounds, obbFrame, obbExtents, obbCenter); - - // Note: When normal bias is > 0, bounds using in tile / cluster assignment are conservatively dilated CPU side to handle worst case normal bias. - float3 samplePositionWS = normalWS * s_probeVolumeData.normalBiasWS + posInput.positionWS; - - float3 probeVolumeTexel3D; - ProbeVolumeComputeTexel3DAndWeight( - weightHierarchy, - s_probeVolumeData, - obbFrame, - obbExtents, - obbCenter, - samplePositionWS, - posInput.linearDepth, - probeVolumeTexel3D, - weightCurrent - ); - - probeVolumeTexel3D = ProbeVolumeComputeTexel3DFromBilateralFilter( - probeVolumeTexel3D, - s_probeVolumeData, - posInput.positionWS, // unbiased - samplePositionWS, // biased - normalWS, - obbFrame, - obbExtents, - obbCenter - ); - float3 probeVolumeAtlasUVW = probeVolumeTexel3D * s_probeVolumeData.resolutionInverse * s_probeVolumeData.scale + s_probeVolumeData.bias; +// Utility functions for converting from atlas coefficients layout, into the layout that our EntityLighting.hlsl evaluation functions expect. +void ProbeVolumeSwizzleAndNormalizeSphericalHarmonicsL0(inout ProbeVolumeSphericalHarmonicsL0 coefficients) +{ + // Nothing to do here. DC terms are already normalized and stored in RGB order. +} +void ProbeVolumeSwizzleAndNormalizeSphericalHarmonicsL1(inout ProbeVolumeSphericalHarmonicsL1 coefficients) +{ #ifdef DEBUG_DISPLAY - if (_DebugProbeVolumeMode == PROBEVOLUMEDEBUGMODE_VISUALIZE_DEBUG_COLORS) - { - // Pack debug color into SH data so that we can access it later for our debug mode. - coefficients.data[0].xyz += s_probeVolumeData.debugColor * weightCurrent; - } - else if (_DebugProbeVolumeMode == PROBEVOLUMEDEBUGMODE_VISUALIZE_VALIDITY) - { - float validity = ProbeVolumeSampleValidity(probeVolumeAtlasUVW); - - // Pack validity into SH data so that we can access it later for our debug mode. - coefficients.data[0].x += validity * weightCurrent; - } - else + if (_DebugProbeVolumeMode == PROBEVOLUMEDEBUGMODE_VISUALIZE_DEBUG_COLORS || _DebugProbeVolumeMode == PROBEVOLUMEDEBUGMODE_VISUALIZE_VALIDITY) + { + // coefficients are storing debug info. Do not swizzle or normalize. + return; + } #endif - { -#if SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L0 - coefficients.data[0] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 0), 0) * weightCurrent; -#elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L1 - coefficients.data[0] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 0), 0) * weightCurrent; - coefficients.data[1] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 1), 0) * weightCurrent; - coefficients.data[2] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 2), 0) * weightCurrent; + // SHEvalLinearL0L1() expects coefficients in real4 shAr, real4 shAg, real4 shAb vectors whos channels are laid out {x, y, z, DC} + float4 shAr = float4(coefficients.data[0].w, coefficients.data[1].x, coefficients.data[1].y, coefficients.data[0].x); + float4 shAg = float4(coefficients.data[1].z, coefficients.data[1].w, coefficients.data[2].x, coefficients.data[0].y); + float4 shAb = float4(coefficients.data[2].y, coefficients.data[2].z, coefficients.data[2].w, coefficients.data[0].z); -#elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L2 - coefficients.data[0] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 0), 0) * weightCurrent; - coefficients.data[1] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 1), 0) * weightCurrent; - coefficients.data[2] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 2), 0) * weightCurrent; - coefficients.data[3] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 3), 0) * weightCurrent; - coefficients.data[4] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 4), 0) * weightCurrent; - coefficients.data[5] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 5), 0) * weightCurrent; - coefficients.data[6] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 6), 0) * weightCurrent; + coefficients.data[0] = shAr; + coefficients.data[1] = shAg; + coefficients.data[2] = shAb; +} +void ProbeVolumeSwizzleAndNormalizeSphericalHarmonicsL2(inout ProbeVolumeSphericalHarmonicsL2 coefficients) +{ +#ifdef DEBUG_DISPLAY + if (_DebugProbeVolumeMode == PROBEVOLUMEDEBUGMODE_VISUALIZE_DEBUG_COLORS || _DebugProbeVolumeMode == PROBEVOLUMEDEBUGMODE_VISUALIZE_VALIDITY) + { + // coefficients are storing debug info. Do not swizzle or normalize. + return; + } #endif - } - } - if (isWeightAccumulated) - weightHierarchy += weightCurrent; - } - } + // SampleSH9() expects coefficients in shAr, shAg, shAb, shBr, shBg, shBb, shCr vectors. + float4 shAr = float4(coefficients.data[0].w, coefficients.data[1].x, coefficients.data[1].y, coefficients.data[0].x); + float4 shAg = float4(coefficients.data[1].z, coefficients.data[1].w, coefficients.data[2].x, coefficients.data[0].y); + float4 shAb = float4(coefficients.data[2].y, coefficients.data[2].z, coefficients.data[2].w, coefficients.data[0].z); + + coefficients.data[0] = shAr; + coefficients.data[1] = shAg; + coefficients.data[2] = shAb; + + // coefficients[3] through coefficients[6] are already laid out in shBr, shBg, shBb, shCr order. + // Now just need to perform final SH2 normalization: + // Again, normalization from: https://www.ppsloan.org/publications/StupidSH36.pdf + // Appendix A10 Shader/CPU code for Irradiance Environment Maps + + // Normalize DC term: + coefficients.data[0].w -= coefficients.data[3].z; + coefficients.data[1].w -= coefficients.data[4].z; + coefficients.data[2].w -= coefficients.data[5].z; + + // Normalize Quadratic term: + coefficients.data[3].z *= 3.0; + coefficients.data[4].z *= 3.0; + coefficients.data[5].z *= 3.0; } -float3 EvaluateProbeVolumeCoefficients(float3 normalWS, ProbeVolumeCoefficients coefficients) +float3 EvaluateProbeVolumeSphericalHarmonicsL0(float3 normalWS, ProbeVolumeSphericalHarmonicsL0 coefficients) { #ifdef DEBUG_DISPLAY @@ -522,19 +513,51 @@ float3 EvaluateProbeVolumeCoefficients(float3 normalWS, ProbeVolumeCoefficients else #endif { - // When probe volumes are evaluated in the material pass, BSDF modulation is applied as a post operation, outside of this function. -#if SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L0 float3 sampleOutgoingRadiance = coefficients.data[0].rgb; + return sampleOutgoingRadiance; + } +} -#elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L1 - float3 sampleOutgoingRadiance = SHEvalLinearL0L1(normalWS, coefficients.data[0], coefficients.data[1], coefficients.data[2]); +float3 EvaluateProbeVolumeSphericalHarmonicsL1(float3 normalWS, ProbeVolumeSphericalHarmonicsL1 coefficients) +{ -#elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L2 - float3 sampleOutgoingRadiance = SampleSH9(coefficients, normalWS); -#else - float3 sampleOutgoingRadiance = 0.0; +#ifdef DEBUG_DISPLAY + if (_DebugProbeVolumeMode == PROBEVOLUMEDEBUGMODE_VISUALIZE_DEBUG_COLORS) + { + float3 debugColors = coefficients.data[0].rgb; + return debugColors; + } + else if (_DebugProbeVolumeMode == PROBEVOLUMEDEBUGMODE_VISUALIZE_VALIDITY) + { + float validity = coefficients.data[0].x; + return lerp(float3(1, 0, 0), float3(0, 1, 0), validity); + } + else #endif + { + float3 sampleOutgoingRadiance = SHEvalLinearL0L1(normalWS, coefficients.data[0], coefficients.data[1], coefficients.data[2]); + return sampleOutgoingRadiance; + } +} + +float3 EvaluateProbeVolumeSphericalHarmonicsL2(float3 normalWS, ProbeVolumeSphericalHarmonicsL2 coefficients) +{ +#ifdef DEBUG_DISPLAY + if (_DebugProbeVolumeMode == PROBEVOLUMEDEBUGMODE_VISUALIZE_DEBUG_COLORS) + { + float3 debugColors = coefficients.data[0].rgb; + return debugColors; + } + else if (_DebugProbeVolumeMode == PROBEVOLUMEDEBUGMODE_VISUALIZE_VALIDITY) + { + float validity = coefficients.data[0].x; + return lerp(float3(1, 0, 0), float3(0, 1, 0), validity); + } + else +#endif + { + float3 sampleOutgoingRadiance = SampleSH9(coefficients.data, normalWS); return sampleOutgoingRadiance; } } @@ -557,4 +580,19 @@ float3 EvaluateProbeVolumeAmbientProbeFallback(float3 normalWS, inout float weig return sampleAmbientProbeOutgoingRadiance; } +// Generate ProbeVolumeAccumulateSphericalHarmonicsL0 function: +#define PROBE_VOLUMES_ACCUMULATE_MODE PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L0 +#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeAccumulate.hlsl" +#undef PROBE_VOLUMES_ACCUMULATE_MODE + +// Generate ProbeVolumeAccumulateSphericalHarmonicsL1 function: +#define PROBE_VOLUMES_ACCUMULATE_MODE PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L1 +#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeAccumulate.hlsl" +#undef PROBE_VOLUMES_ACCUMULATE_MODE + +// Generate ProbeVolumeAccumulateSphericalHarmonicsL2 function: +#define PROBE_VOLUMES_ACCUMULATE_MODE PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L2 +#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeAccumulate.hlsl" +#undef PROBE_VOLUMES_ACCUMULATE_MODE + #endif // __PROBEVOLUME_HLSL__ diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeAccumulate.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeAccumulate.hlsl new file mode 100644 index 00000000000..7d03758a082 --- /dev/null +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeAccumulate.hlsl @@ -0,0 +1,152 @@ +// This file should only be included inside of ProbeVolume.hlsl. +// There are no #ifndef HEADER guards to stop multiple inclusion, as this is simply used for code gen. + +#ifndef PROBE_VOLUMES_ACCUMULATE_MODE + #error "PROBE_VOLUMES_ACCUMULATE_MODE must be defined as 0, 1, or 2 before including ProbeVolumeAccumulate.hlsl. 0 triggers generation of SH0 variant, 1 triggers generation of SH1 variant, and 2 triggers generation of SH2 variant."; +#endif + +#if (PROBE_VOLUMES_ACCUMULATE_MODE < 0) || (PROBE_VOLUMES_ACCUMULATE_MODE > 2) + #error "PROBE_VOLUMES_ACCUMULATE_MODE must be defined as 0, 1, or 2 before including ProbeVolumeAccumulate.hlsl. 0 triggers generation of SH0 variant, 1 triggers generation of SH1 variant, and 2 triggers generation of SH2 variant."; +#endif + +#if PROBE_VOLUMES_ACCUMULATE_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L0 + void ProbeVolumeAccumulateSphericalHarmonicsL0( +#elif PROBE_VOLUMES_ACCUMULATE_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L1 + void ProbeVolumeAccumulateSphericalHarmonicsL1( +#elif PROBE_VOLUMES_ACCUMULATE_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L2 + void ProbeVolumeAccumulateSphericalHarmonicsL2( +#endif + PositionInputs posInput, float3 normalWS, uint renderingLayers, +#if PROBE_VOLUMES_ACCUMULATE_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L0 + out ProbeVolumeSphericalHarmonicsL0 coefficients, +#elif PROBE_VOLUMES_ACCUMULATE_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L1 + out ProbeVolumeSphericalHarmonicsL1 coefficients, +#elif PROBE_VOLUMES_ACCUMULATE_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L2 + out ProbeVolumeSphericalHarmonicsL2 coefficients, +#endif + inout float weightHierarchy) +{ + +#if PROBE_VOLUMES_ACCUMULATE_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L0 + ZERO_INITIALIZE(ProbeVolumeSphericalHarmonicsL0, coefficients); +#elif PROBE_VOLUMES_ACCUMULATE_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L1 + ZERO_INITIALIZE(ProbeVolumeSphericalHarmonicsL1, coefficients); +#elif PROBE_VOLUMES_ACCUMULATE_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L2 + ZERO_INITIALIZE(ProbeVolumeSphericalHarmonicsL2, coefficients); +#endif + + +#if !SHADEROPTIONS_PROBE_VOLUMES_ADDITIVE_BLENDING + if (weightHierarchy >= 1.0) { return; } +#endif + + uint probeVolumeStart, probeVolumeCount; + bool fastPath; + ProbeVolumeGetCountAndStartAndFastPath(posInput, probeVolumeStart, probeVolumeCount, fastPath); + + // Scalarized loop, same rationale of the punctual light version + uint v_probeVolumeListOffset = 0; + uint v_probeVolumeIdx = probeVolumeStart; + while (v_probeVolumeListOffset < probeVolumeCount) + { + v_probeVolumeIdx = ProbeVolumeFetchIndex(probeVolumeStart, v_probeVolumeListOffset); + uint s_probeVolumeIdx = ProbeVolumeScalarizeElementIndex(v_probeVolumeIdx, fastPath); + if (s_probeVolumeIdx == -1) { break; } + + // Scalar load. + ProbeVolumeEngineData s_probeVolumeData = _ProbeVolumeDatas[s_probeVolumeIdx]; + OrientedBBox s_probeVolumeBounds = _ProbeVolumeBounds[s_probeVolumeIdx]; + + if (ProbeVolumeIsAllWavesComplete(weightHierarchy, s_probeVolumeData.volumeBlendMode)) { break; } + + // If current scalar and vector light index match, we process the light. The v_probeVolumeListOffset for current thread is increased. + // Note that the following should really be ==, however, since helper lanes are not considered by WaveActiveMin, such helper lanes could + // end up with a unique v_envLightIdx value that is smaller than s_envLightIdx hence being stuck in a loop. All the active lanes will not have this problem. + if (s_probeVolumeIdx >= v_probeVolumeIdx) + { + v_probeVolumeListOffset++; + +#if SHADEROPTIONS_PROBE_VOLUMES_ADDITIVE_BLENDING + bool isWeightAccumulated = s_probeVolumeData.volumeBlendMode == VOLUMEBLENDMODE_NORMAL; +#else + const bool isWeightAccumulated = true; +#endif + + if (weightHierarchy >= 1.0 && isWeightAccumulated) { continue; } + + if (!IsMatchingLightLayer(s_probeVolumeData.lightLayers, renderingLayers)) { continue; } + + float weightCurrent = 0.0; + { + float3x3 obbFrame; + float3 obbExtents; + float3 obbCenter; + ProbeVolumeComputeOBBBoundsToFrame(s_probeVolumeBounds, obbFrame, obbExtents, obbCenter); + + // Note: When normal bias is > 0, bounds using in tile / cluster assignment are conservatively dilated CPU side to handle worst case normal bias. + float3 samplePositionWS = normalWS * s_probeVolumeData.normalBiasWS + posInput.positionWS; + + float3 probeVolumeTexel3D; + ProbeVolumeComputeTexel3DAndWeight( + weightHierarchy, + s_probeVolumeData, + obbFrame, + obbExtents, + obbCenter, + samplePositionWS, + posInput.linearDepth, + probeVolumeTexel3D, + weightCurrent + ); + + probeVolumeTexel3D = ProbeVolumeComputeTexel3DFromBilateralFilter( + probeVolumeTexel3D, + s_probeVolumeData, + posInput.positionWS, // unbiased + samplePositionWS, // biased + normalWS, + obbFrame, + obbExtents, + obbCenter + ); + float3 probeVolumeAtlasUVW = probeVolumeTexel3D * s_probeVolumeData.resolutionInverse * s_probeVolumeData.scale + s_probeVolumeData.bias; + +#ifdef DEBUG_DISPLAY + if (_DebugProbeVolumeMode == PROBEVOLUMEDEBUGMODE_VISUALIZE_DEBUG_COLORS) + { + // Pack debug color into SH data so that we can access it later for our debug mode. + coefficients.data[0].xyz += s_probeVolumeData.debugColor * weightCurrent; + } + else if (_DebugProbeVolumeMode == PROBEVOLUMEDEBUGMODE_VISUALIZE_VALIDITY) + { + float validity = ProbeVolumeSampleValidity(probeVolumeAtlasUVW); + + // Pack validity into SH data so that we can access it later for our debug mode. + coefficients.data[0].x += validity * weightCurrent; + } + else +#endif + { +#if PROBE_VOLUMES_ACCUMULATE_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L0 + ProbeVolumeSampleAccumulateSphericalHarmonicsL0(probeVolumeAtlasUVW, weightCurrent, coefficients); +#elif PROBE_VOLUMES_ACCUMULATE_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L1 + ProbeVolumeSampleAccumulateSphericalHarmonicsL1(probeVolumeAtlasUVW, weightCurrent, coefficients); +#elif PROBE_VOLUMES_ACCUMULATE_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L2 + ProbeVolumeSampleAccumulateSphericalHarmonicsL2(probeVolumeAtlasUVW, weightCurrent, coefficients); +#endif + } + } + + if (isWeightAccumulated) + weightHierarchy += weightCurrent; + } + } + +#if PROBE_VOLUMES_ACCUMULATE_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L0 + ProbeVolumeSwizzleAndNormalizeSphericalHarmonicsL0(coefficients); +#elif PROBE_VOLUMES_ACCUMULATE_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L1 + ProbeVolumeSwizzleAndNormalizeSphericalHarmonicsL1(coefficients); +#elif PROBE_VOLUMES_ACCUMULATE_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L2 + ProbeVolumeSwizzleAndNormalizeSphericalHarmonicsL2(coefficients); +#endif +} diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeAccumulate.hlsl.meta b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeAccumulate.hlsl.meta new file mode 100644 index 00000000000..c0e1264acb6 --- /dev/null +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeAccumulate.hlsl.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 7112f74f4985d2644ab1c94e30088c45 +ShaderImporter: + externalObjects: {} + defaultTextures: [] + nonModifiableTextures: [] + preprocessorOverride: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeAtlasBlit.compute b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeAtlasBlit.compute index 8728d200764..1e4ceb26f0a 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeAtlasBlit.compute +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeAtlasBlit.compute @@ -58,34 +58,51 @@ void PROBE_VOLUME_ATLAS_BLIT_KERNEL(uint groupThreadId : SV_GroupThreadID, uint float occlusion = _ProbeVolumeAtlasReadValidityBuffer[readIndex]; float validity = 1.0 - occlusion; + // See ProbeVolume.cs::OnBakeComplete() for more info on _ProbeVolumeAtlasReadBuffer layout. + // This shader must stay in sync with that layout: + // { + // // Constant: (used by L0, L1, and L2) + // shAr.w, shAg.w, shAb.w, + + // // Linear: (used by L1 and L2) + // shAr.x, shAr.y, shAr.z, + // shAg.x, shAg.y, shAg.z, + // shAb.x, shAb.y, shAb.z, + + // // Quadratic: (used by L2) + // shBr.x, shBr.y, shBr.z, shBr.w, + // shBg.x, shBg.y, shBg.z, shBg.w, + // shBb.x, shBb.y, shBb.z, shBb.w, + // shCr.x, shCr.y, shCr.z + // } #if SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L0 const uint SH_STRIDE = 3; _ProbeVolumeAtlasWriteTextureSH[uint3(writeIndex.x, writeIndex.y, writeIndex.z + _ProbeVolumeAtlasResolutionAndSliceCount.z * 0)] = float4( - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 0 + 0], - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 0 + 1], - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 0 + 2], + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 0], // shAr.w + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 1], // shAg.w + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 2], // shAb.w validity // last channel in float4 is unused by SH0 terms, so we take this opportunity to pack validity into it. ); #elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L1 const uint SH_STRIDE = 12; _ProbeVolumeAtlasWriteTextureSH[uint3(writeIndex.x, writeIndex.y, writeIndex.z + _ProbeVolumeAtlasResolutionAndSliceCount.z * 0)] = float4( - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 0 + 0], - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 0 + 1], - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 0 + 2], - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 0 + 3] + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 0 + 0], // shAr.w + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 0 + 1], // shAg.w + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 0 + 2], // shAb.w + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 0 + 3] // shAr.x ); _ProbeVolumeAtlasWriteTextureSH[uint3(writeIndex.x, writeIndex.y, writeIndex.z + _ProbeVolumeAtlasResolutionAndSliceCount.z * 1)] = float4( - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 4 + 0], - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 4 + 1], - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 4 + 2], - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 4 + 3] + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 4 + 0], // shAr.y + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 4 + 1], // shAr.z + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 4 + 2], // shAg.x + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 4 + 3] // shAg.y ); _ProbeVolumeAtlasWriteTextureSH[uint3(writeIndex.x, writeIndex.y, writeIndex.z + _ProbeVolumeAtlasResolutionAndSliceCount.z * 2)] = float4( - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 8 + 0], - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 8 + 1], - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 8 + 2], - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 8 + 3] + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 8 + 0], // shAg.z + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 8 + 1], // shAb.x + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 8 + 2], // shAb.y + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 8 + 3] // shAb.z ); _ProbeVolumeAtlasWriteTextureSH[uint3(writeIndex.x, writeIndex.y, writeIndex.z + _ProbeVolumeAtlasResolutionAndSliceCount.z * 3)] = float4(validity, 0.0, 0.0, 0.0); @@ -93,45 +110,45 @@ void PROBE_VOLUME_ATLAS_BLIT_KERNEL(uint groupThreadId : SV_GroupThreadID, uint const uint SH_STRIDE = 27; _ProbeVolumeAtlasWriteTextureSH[uint3(writeIndex.x, writeIndex.y, writeIndex.z + _ProbeVolumeAtlasResolutionAndSliceCount.z * 0)] = float4( - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 0 + 0], - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 0 + 1], - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 0 + 2], - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 0 + 3] + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 0 + 0], // shAr.w + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 0 + 1], // shAg.w + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 0 + 2], // shAb.w + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 0 + 3] // shAr.x ); _ProbeVolumeAtlasWriteTextureSH[uint3(writeIndex.x, writeIndex.y, writeIndex.z + _ProbeVolumeAtlasResolutionAndSliceCount.z * 1)] = float4( - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 4 + 0], - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 4 + 1], - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 4 + 2], - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 4 + 3] + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 4 + 0], // shAr.y + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 4 + 1], // shAr.z + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 4 + 2], // shAg.x + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 4 + 3] // shAg.y ); _ProbeVolumeAtlasWriteTextureSH[uint3(writeIndex.x, writeIndex.y, writeIndex.z + _ProbeVolumeAtlasResolutionAndSliceCount.z * 2)] = float4( - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 8 + 0], - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 8 + 1], - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 8 + 2], - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 8 + 3] + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 8 + 0], // shAg.z + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 8 + 1], // shAb.x + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 8 + 2], // shAb.y + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 8 + 3] // shAb.z ); _ProbeVolumeAtlasWriteTextureSH[uint3(writeIndex.x, writeIndex.y, writeIndex.z + _ProbeVolumeAtlasResolutionAndSliceCount.z * 3)] = float4( - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 12 + 0], - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 12 + 1], - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 12 + 2], - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 12 + 3] + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 12 + 0], // shBr.x + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 12 + 1], // shBr.y + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 12 + 2], // shBr.z + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 12 + 3] // shBr.w ); _ProbeVolumeAtlasWriteTextureSH[uint3(writeIndex.x, writeIndex.y, writeIndex.z + _ProbeVolumeAtlasResolutionAndSliceCount.z * 4)] = float4( - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 16 + 0], - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 16 + 1], - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 16 + 2], - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 16 + 3] + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 16 + 0], // shBg.x + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 16 + 1], // shBg.y + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 16 + 2], // shBg.z + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 16 + 3] // shBg.w ); _ProbeVolumeAtlasWriteTextureSH[uint3(writeIndex.x, writeIndex.y, writeIndex.z + _ProbeVolumeAtlasResolutionAndSliceCount.z * 5)] = float4( - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 20 + 0], - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 20 + 1], - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 20 + 2], - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 20 + 3] + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 20 + 0], // shBb.x + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 20 + 1], // shBb.y + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 20 + 2], // shBb.z + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 20 + 3] // shBb.w ); _ProbeVolumeAtlasWriteTextureSH[uint3(writeIndex.x, writeIndex.y, writeIndex.z + _ProbeVolumeAtlasResolutionAndSliceCount.z * 6)] = float4( - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 24 + 0], - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 24 + 1], - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 24 + 2], + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 24 + 0], // shCr.x + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 24 + 1], // shCr.y + _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 24 + 2], // shCr.z validity // last channel in float4 is unused by SH2 terms, so we take this opportunity to pack validity into it. ); diff --git a/com.unity.render-pipelines.high-definition/Runtime/Material/BuiltinGIUtilities.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Material/BuiltinGIUtilities.hlsl index dc3a07b44cc..0ce6e6e9d7a 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Material/BuiltinGIUtilities.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/Material/BuiltinGIUtilities.hlsl @@ -148,14 +148,26 @@ void SampleBakedGI( posInputs.tileCoord = tileCoord; #endif - ProbeVolumeCoefficients coefficients; - AccumulateProbeVolumes(posInputs, normalWS, renderingLayers, coefficients, probeVolumeHierarchyWeight); - bakeDiffuseLighting += EvaluateProbeVolumeCoefficients(normalWS, coefficients); - backBakeDiffuseLighting += EvaluateProbeVolumeCoefficients(backNormalWS, coefficients); - +#if SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L0 + ProbeVolumeSphericalHarmonicsL0 coefficients; + ProbeVolumeAccumulateSphericalHarmonicsL0(posInputs, normalWS, renderingLayers, coefficients, probeVolumeHierarchyWeight); + bakeDiffuseLighting += EvaluateProbeVolumeSphericalHarmonicsL0(normalWS, coefficients); + backBakeDiffuseLighting += EvaluateProbeVolumeSphericalHarmonicsL2(backNormalWS, coefficients); +#elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L1 + ProbeVolumeSphericalHarmonicsL1 coefficients; + ProbeVolumeAccumulateSphericalHarmonicsL1(posInputs, normalWS, renderingLayers, coefficients, probeVolumeHierarchyWeight); + bakeDiffuseLighting += EvaluateProbeVolumeSphericalHarmonicsL1(normalWS, coefficients); + backBakeDiffuseLighting += EvaluateProbeVolumeSphericalHarmonicsL2(backNormalWS, coefficients); +#elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L2 + ProbeVolumeSphericalHarmonicsL2 coefficients; + ProbeVolumeAccumulateSphericalHarmonicsL2(posInputs, normalWS, renderingLayers, coefficients, probeVolumeHierarchyWeight); + bakeDiffuseLighting += EvaluateProbeVolumeSphericalHarmonicsL2(normalWS, coefficients); + backBakeDiffuseLighting += EvaluateProbeVolumeSphericalHarmonicsL2(backNormalWS, coefficients); +#endif float backProbeVolumeHierarchyWeight = probeVolumeHierarchyWeight; bakeDiffuseLighting += EvaluateProbeVolumeAmbientProbeFallback(normalWS, probeVolumeHierarchyWeight); backBakeDiffuseLighting += EvaluateProbeVolumeAmbientProbeFallback(backNormalWS, backProbeVolumeHierarchyWeight); + #endif #endif From c82b63fddeca091ec79017e8dfc7dea9b881b751 Mon Sep 17 00:00:00 2001 From: pastasfuture Date: Mon, 8 Jun 2020 12:33:04 -0700 Subject: [PATCH 16/26] Probe Volumes: Add simple method for configuring what order of SH Probe Volumes is sampled at per shader (via a define before include). --- .../Runtime/Lighting/LightLoop/LightLoop.hlsl | 16 +++++++++++++--- .../Runtime/VFXGraph/Shaders/VFXLit.hlsl | 2 ++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.hlsl index d54bee80d41..71a952ee846 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.hlsl @@ -2,6 +2,16 @@ #if SHADEROPTIONS_PROBE_VOLUMES_EVALUATION_MODE == PROBEVOLUMESEVALUATIONMODES_LIGHT_LOOP #include "Packages/com.unity.render-pipelines.high-definition/Runtime/Material/BuiltinUtilities.hlsl" + +#ifndef PROBE_VOLUMES_SAMPLING_MODE +// Default to sampling probe volumes at native atlas encoding mode. +// Users can override this by defining PROBE_VOLUMES_SAMPLING_MODE before including LightLoop.hlsl +// TODO: It's likely we will want to extend this out to simply be shader LOD quality levels, +// as there are other parameters such as bilateral filtering, additive blending, and normal bias +// that we will want to disable for a low quality high performance mode. +#define PROBE_VOLUMES_SAMPLING_MODE SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE +#endif + #endif // We perform scalarization only for forward rendering as for deferred loads will already be scalar since tiles will match waves and therefore all threads will read from the same tile. @@ -492,17 +502,17 @@ void LightLoop( float3 V, PositionInputs posInput, PreLightData preLightData, BS float probeVolumeHierarchyWeight = uninitialized ? 0.0f : 1.0f; // Note: we aren't suppose to access normalWS in lightloop, but bsdfData.normalWS is always define for any material. So this is safe. -#if SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L0 +#if PROBE_VOLUMES_SAMPLING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L0 ProbeVolumeSphericalHarmonicsL0 coefficients; ProbeVolumeAccumulateSphericalHarmonicsL0(posInput, bsdfData.normalWS, builtinData.renderingLayers, coefficients, probeVolumeHierarchyWeight); builtinDataProbeVolumes.bakeDiffuseLighting += EvaluateProbeVolumeSphericalHarmonicsL0(bsdfData.normalWS, coefficients); builtinDataProbeVolumes.backBakeDiffuseLighting += EvaluateProbeVolumeSphericalHarmonicsL0(-bsdfData.normalWS, coefficients); -#elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L1 +#elif PROBE_VOLUMES_SAMPLING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L1 ProbeVolumeSphericalHarmonicsL1 coefficients; ProbeVolumeAccumulateSphericalHarmonicsL1(posInput, bsdfData.normalWS, builtinData.renderingLayers, coefficients, probeVolumeHierarchyWeight); builtinDataProbeVolumes.bakeDiffuseLighting += EvaluateProbeVolumeSphericalHarmonicsL1(bsdfData.normalWS, coefficients); builtinDataProbeVolumes.backBakeDiffuseLighting += EvaluateProbeVolumeSphericalHarmonicsL1(-bsdfData.normalWS, coefficients); -#elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L2 +#elif PROBE_VOLUMES_SAMPLING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L2 ProbeVolumeSphericalHarmonicsL2 coefficients; ProbeVolumeAccumulateSphericalHarmonicsL2(posInput, bsdfData.normalWS, builtinData.renderingLayers, coefficients, probeVolumeHierarchyWeight); builtinDataProbeVolumes.bakeDiffuseLighting += EvaluateProbeVolumeSphericalHarmonicsL2(bsdfData.normalWS, coefficients); diff --git a/com.unity.render-pipelines.high-definition/Runtime/VFXGraph/Shaders/VFXLit.hlsl b/com.unity.render-pipelines.high-definition/Runtime/VFXGraph/Shaders/VFXLit.hlsl index 1458abb58f9..a6ef2f67cc5 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/VFXGraph/Shaders/VFXLit.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/VFXGraph/Shaders/VFXLit.hlsl @@ -30,6 +30,8 @@ #else #include "Packages/com.unity.render-pipelines.high-definition/Runtime/Material/Lit/Lit.hlsl" #endif + // Make VFX only sample probe volumes as SH0 for performance. + #define PROBE_VOLUMES_SAMPLING_MODE PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L0 #include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.hlsl" #else // (SHADERPASS == SHADERPASS_FORWARD) From 3693943d7be41520e2c622024afc8ce8b091569f Mon Sep 17 00:00:00 2001 From: pastasfuture Date: Mon, 8 Jun 2020 12:38:55 -0700 Subject: [PATCH 17/26] Probe Volumes: Fix to ProbeVolumeUI string names --- .../Lighting/ProbeVolume/ProbeVolumeUI.Drawer.cs | 12 ++++++------ .../Lighting/ProbeVolume/ProbeVolumeUI.Skin.cs | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/com.unity.render-pipelines.high-definition/Editor/Lighting/ProbeVolume/ProbeVolumeUI.Drawer.cs b/com.unity.render-pipelines.high-definition/Editor/Lighting/ProbeVolume/ProbeVolumeUI.Drawer.cs index e8ea9e459fe..a4cd186d68e 100644 --- a/com.unity.render-pipelines.high-definition/Editor/Lighting/ProbeVolume/ProbeVolumeUI.Drawer.cs +++ b/com.unity.render-pipelines.high-definition/Editor/Lighting/ProbeVolume/ProbeVolumeUI.Drawer.cs @@ -71,12 +71,12 @@ static bool IsFeatureDisabled(SerializedProbeVolume serialized, Editor owner) static void Drawer_FeatureWarningMessage(SerializedProbeVolume serialized, Editor owner) { - EditorGUILayout.HelpBox(Styles.k_featureWarning, MessageType.Warning); + EditorGUILayout.HelpBox(Styles.k_FeatureWarning, MessageType.Warning); } static void Drawer_FeatureEnableInfo(SerializedProbeVolume serialized, Editor owner) { - EditorGUILayout.HelpBox(Styles.k_featureEnableInfo, MessageType.Error); + EditorGUILayout.HelpBox(Styles.k_FeatureEnableInfo, MessageType.Error); } static void Drawer_BakeToolBar(SerializedProbeVolume serialized, Editor owner) @@ -84,19 +84,19 @@ static void Drawer_BakeToolBar(SerializedProbeVolume serialized, Editor owner) var asset = serialized.probeVolumeAsset.objectReferenceValue as ProbeVolumeAsset; if (asset != null && asset.payload.encodingMode != ShaderConfig.s_ProbeVolumesEncodingMode) { - EditorGUILayout.HelpBox(Styles.k_featureEncodingModeMismatchError, MessageType.Error); + EditorGUILayout.HelpBox(Styles.k_FeatureEncodingModeMismatchError, MessageType.Error); } if (ShaderConfig.s_ProbeVolumesBilateralFilteringMode == ProbeVolumesBilateralFilteringModes.OctahedralDepth && asset != null && asset.payload.dataOctahedralDepth == null) { - EditorGUILayout.HelpBox(Styles.k_featureOctahedralDepthEnabledNoData, MessageType.Error); + EditorGUILayout.HelpBox(Styles.k_FeatureOctahedralDepthEnabledNoData, MessageType.Error); } if (ShaderConfig.s_ProbeVolumesBilateralFilteringMode != ProbeVolumesBilateralFilteringModes.OctahedralDepth && asset != null && asset.payload.dataOctahedralDepth != null) { - EditorGUILayout.HelpBox(Styles.k_featureOctahedralDepthDisableYesData, MessageType.Error); + EditorGUILayout.HelpBox(Styles.k_FeatureOctahedralDepthDisableYesData, MessageType.Error); } EditorGUILayout.PropertyField(serialized.probeVolumeAsset, Styles.s_DataAssetLabel); @@ -296,7 +296,7 @@ static void Drawer_VolumeContent(SerializedProbeVolume serialized, Editor owner) if (ShaderConfig.s_ProbeVolumesAdditiveBlending == 0 && serialized.volumeBlendMode.intValue != (int)VolumeBlendMode.Normal) { - EditorGUILayout.HelpBox(Styles.k_featureAdditiveBlendingDisabledError, MessageType.Error); + EditorGUILayout.HelpBox(Styles.k_FeatureAdditiveBlendingDisabledError, MessageType.Error); } } } diff --git a/com.unity.render-pipelines.high-definition/Editor/Lighting/ProbeVolume/ProbeVolumeUI.Skin.cs b/com.unity.render-pipelines.high-definition/Editor/Lighting/ProbeVolume/ProbeVolumeUI.Skin.cs index 769a2965c74..660e5bf3a81 100644 --- a/com.unity.render-pipelines.high-definition/Editor/Lighting/ProbeVolume/ProbeVolumeUI.Skin.cs +++ b/com.unity.render-pipelines.high-definition/Editor/Lighting/ProbeVolume/ProbeVolumeUI.Skin.cs @@ -6,12 +6,12 @@ static partial class ProbeVolumeUI { internal static class Styles { - internal const string k_featureWarning = "Warning: Probe Volumes is a highly experimental feature.\nIt is disabled by default for this reason.\nIt's functionality is subject to breaking changes and whole sale removal.\nIt is not recommended for use outside of for providing feedback.\nIt should not be used in production."; - internal const string k_featureEnableInfo = "\nProbe Volumes feature is disabled. To enable, set:\nProbeVolumesEvaluationMode = ProbeVolumesEvaluationModes.MaterialPass\ninside of ShaderConfig.cs. Then inside of the editor run:\nEdit->Render Pipeline->Generate Shader Includes\nProbe Volumes feature must also be enabled inside of your HDRenderPipelineAsset."; - internal const string k_featureAdditiveBlendingDisabledError = "Error: ProbeVolumesAdditiveBlending feature is disabled inside of ShaderConfig.cs.\nThis probe volume will not be rendered.\nTo enable, set:\nProbeVolumesAdditiveBlending = 1\ninside of ShaderConfig.cs. Then inside of the editor run:\nEdit->Render Pipeline->Generate Shader Includes."; - internal const string k_featureEncodingModeMismatchError = "Error: ProbeVolumesEncodingMode inside of ShaderConfig.cs is configured to a different mode than this probe volume was previously baked with.\nThis probe volume will not be rendered.\nTo correct this:\nA) Rebake this probe volume.\nor\nB) Change ProbeVolumesEncodingMode inside of ShaderConfig.cs. Then inside of the editor run:\nEdit->Render Pipeline->Generate Shader Includes."; - internal const string k_featureOctahedralDepthEnabledNoData = "Error: ProbeVolumesBilateralFilteringMode inside of ShaderConfig.cs is set to OctahedralDepth, but asset was baked with OctahedralDepth disabled.\nPlease rebake if you would like this probe volume to use octahedral depth filtering."; - internal const string k_featureOctahedralDepthDisableYesData = "Error: ProbeVolumesBilateralFilteringMode inside of ShaderConfig.cs is not set to OctahedralDepth, but was baked with OctahedralDepth enabled.\nPlease rebake to discard octahedral depth data from asset."; + internal const string k_FeatureWarning = "Warning: Probe Volumes is a highly experimental feature.\nIt is disabled by default for this reason.\nIt's functionality is subject to breaking changes and whole sale removal.\nIt is not recommended for use outside of for providing feedback.\nIt should not be used in production."; + internal const string k_FeatureEnableInfo = "\nProbe Volumes feature is disabled. To enable, set:\nProbeVolumesEvaluationMode = ProbeVolumesEvaluationModes.MaterialPass\ninside of ShaderConfig.cs. Then inside of the editor run:\nEdit->Render Pipeline->Generate Shader Includes\nProbe Volumes feature must also be enabled inside of your HDRenderPipelineAsset."; + internal const string k_FeatureAdditiveBlendingDisabledError = "Error: ProbeVolumesAdditiveBlending feature is disabled inside of ShaderConfig.cs.\nThis probe volume will not be rendered.\nTo enable, set:\nProbeVolumesAdditiveBlending = 1\ninside of ShaderConfig.cs. Then inside of the editor run:\nEdit->Render Pipeline->Generate Shader Includes."; + internal const string k_FeatureEncodingModeMismatchError = "Error: ProbeVolumesEncodingMode inside of ShaderConfig.cs is configured to a different mode than this probe volume was previously baked with.\nThis probe volume will not be rendered.\nTo correct this:\nA) Rebake this probe volume.\nor\nB) Change ProbeVolumesEncodingMode inside of ShaderConfig.cs. Then inside of the editor run:\nEdit->Render Pipeline->Generate Shader Includes."; + internal const string k_FeatureOctahedralDepthEnabledNoData = "Error: ProbeVolumesBilateralFilteringMode inside of ShaderConfig.cs is set to OctahedralDepth, but asset was baked with OctahedralDepth disabled.\nPlease rebake if you would like this probe volume to use octahedral depth filtering."; + internal const string k_FeatureOctahedralDepthDisableYesData = "Error: ProbeVolumesBilateralFilteringMode inside of ShaderConfig.cs is not set to OctahedralDepth, but was baked with OctahedralDepth enabled.\nPlease rebake to discard octahedral depth data from asset."; internal const string k_VolumeHeader = "Volume"; internal const string k_ProbesHeader = "Probes"; internal const string k_BakingHeader = "Baking"; From ea4c88d211ab9fc861c32d7e6decf99c7f3026c8 Mon Sep 17 00:00:00 2001 From: pastasfuture Date: Mon, 8 Jun 2020 12:41:46 -0700 Subject: [PATCH 18/26] Probe Volumes: Move LightLoop::CreateBoxVolumeDataAndBound out parameters to end of args to match HDRP convention. --- .../Runtime/Lighting/LightLoop/LightLoop.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs index 09bc7e290f3..5e1d62716c1 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs @@ -1976,7 +1976,7 @@ void GetEnvLightVolumeDataAndBound(HDProbe probe, LightVolumeType lightVolumeTyp m_lightList.lightsPerView[viewIndex].lightVolumes.Add(lightVolumeData); } - void CreateBoxVolumeDataAndBound(out LightVolumeData volumeData, out SFiniteLightBound bound, OrientedBBox obb, LightCategory category, LightFeatureFlags featureFlags, Matrix4x4 worldToView, float normalBiasDilation = 0.0f) + void CreateBoxVolumeDataAndBound(OrientedBBox obb, LightCategory category, LightFeatureFlags featureFlags, Matrix4x4 worldToView, float normalBiasDilation, out LightVolumeData volumeData, out SFiniteLightBound bound) { volumeData = new LightVolumeData(); bound = new SFiniteLightBound(); @@ -2682,7 +2682,7 @@ bool PrepareLightsForGPU(CommandBuffer cmd, HDCamera hdCamera, CullingResults cu { // Density volumes are not lights and therefore should not affect light classification. LightFeatureFlags featureFlags = 0; - CreateBoxVolumeDataAndBound(out LightVolumeData volumeData, out SFiniteLightBound bound, densityVolumes.bounds[i], LightCategory.DensityVolume, featureFlags, worldToViewCR); + CreateBoxVolumeDataAndBound(densityVolumes.bounds[i], LightCategory.DensityVolume, featureFlags, worldToViewCR, 0.0f, out LightVolumeData volumeData, out SFiniteLightBound bound); m_lightList.lightsPerView[viewIndex].lightVolumes.Add(volumeData); m_lightList.lightsPerView[viewIndex].bounds.Add(bound); } @@ -2692,7 +2692,7 @@ bool PrepareLightsForGPU(CommandBuffer cmd, HDCamera hdCamera, CullingResults cu // Probe volumes are not lights and therefore should not affect light classification. LightFeatureFlags featureFlags = 0; float probeVolumeNormalBiasWS = probeVolumeNormalBiasEnabled ? probeVolumes.data[i].normalBiasWS : 0.0f; - CreateBoxVolumeDataAndBound(out LightVolumeData volumeData, out SFiniteLightBound bound, probeVolumes.bounds[i], LightCategory.ProbeVolume, featureFlags, worldToViewCR, probeVolumeNormalBiasWS); + CreateBoxVolumeDataAndBound(probeVolumes.bounds[i], LightCategory.ProbeVolume, featureFlags, worldToViewCR, probeVolumeNormalBiasWS, out LightVolumeData volumeData, out SFiniteLightBound bound); if (ShaderConfig.s_ProbeVolumesEvaluationMode == ProbeVolumesEvaluationModes.MaterialPass) { // Only probe volume evaluation in the material pass use these custom probe volume specific lists. From f84dedda3f94984d107ba9c1ca0e9a1b4fc24a88 Mon Sep 17 00:00:00 2001 From: pastasfuture Date: Mon, 8 Jun 2020 12:43:22 -0700 Subject: [PATCH 19/26] Probe Volumes: Remove redundant include from DebugDisplayProbeVolume.shader --- .../Runtime/Lighting/ProbeVolume/DebugDisplayProbeVolume.shader | 1 - 1 file changed, 1 deletion(-) diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/DebugDisplayProbeVolume.shader b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/DebugDisplayProbeVolume.shader index 6e2ff294bf7..b518dc774f4 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/DebugDisplayProbeVolume.shader +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/DebugDisplayProbeVolume.shader @@ -4,7 +4,6 @@ Shader "Hidden/ScriptableRenderPipeline/DebugDisplayProbeVolume" #pragma target 4.5 #pragma only_renderers d3d11 playstation xboxone vulkan metal switch - #include "Packages/com.unity.render-pipelines.high-definition-config/Runtime/ShaderConfig.cs.hlsl" #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl" #include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl" #include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeShaderVariables.hlsl" From b962581044d0c57d47cc29c241f16a2bf3b1c992 Mon Sep 17 00:00:00 2001 From: pastasfuture Date: Mon, 8 Jun 2020 18:29:20 -0700 Subject: [PATCH 20/26] Probe Volumes: Only support SH1 and SH2 atlas (SH0 was not needed). Change / cleanup payload data layout. Now SH0 and SH1 terms are stored in one float array, and SH2 quadratic terms are stored in seperate float array. This allows us to always bake and serialize SH2, but conditionally upload only SH0 and SH1 terms if user only requests SH1 atlas. This will also make it easier for SH2 terms to be stripped from the build in the future. --- .../ProbeVolume/ProbeVolumeUI.Drawer.cs | 4 - .../ProbeVolume/ProbeVolumeUI.Skin.cs | 1 - .../Lighting/ProbeVolume/ProbeVolume.cs | 623 ++++++++---------- .../Lighting/ProbeVolume/ProbeVolume.hlsl | 22 +- .../Lighting/ProbeVolume/ProbeVolumeAsset.cs | 35 +- .../ProbeVolume/ProbeVolumeAtlasBlit.compute | 102 ++- ...robeVolumeAtlasOctahedralDepthBlit.compute | 1 - .../ProbeVolume/ProbeVolumeLighting.cs | 34 +- .../ProbeVolumeShaderVariables.hlsl | 1 - .../Runtime/Lighting/SphericalHarmonics.cs | 55 -- .../Lighting/SphericalHarmonics.cs.hlsl | 22 - .../Lighting/SphericalHarmonics.cs.hlsl.meta | 9 - .../RenderPipeline/HDStringConstants.cs | 3 +- 13 files changed, 366 insertions(+), 546 deletions(-) delete mode 100644 com.unity.render-pipelines.high-definition/Runtime/Lighting/SphericalHarmonics.cs.hlsl delete mode 100644 com.unity.render-pipelines.high-definition/Runtime/Lighting/SphericalHarmonics.cs.hlsl.meta diff --git a/com.unity.render-pipelines.high-definition/Editor/Lighting/ProbeVolume/ProbeVolumeUI.Drawer.cs b/com.unity.render-pipelines.high-definition/Editor/Lighting/ProbeVolume/ProbeVolumeUI.Drawer.cs index a4cd186d68e..266f281c701 100644 --- a/com.unity.render-pipelines.high-definition/Editor/Lighting/ProbeVolume/ProbeVolumeUI.Drawer.cs +++ b/com.unity.render-pipelines.high-definition/Editor/Lighting/ProbeVolume/ProbeVolumeUI.Drawer.cs @@ -82,10 +82,6 @@ static void Drawer_FeatureEnableInfo(SerializedProbeVolume serialized, Editor ow static void Drawer_BakeToolBar(SerializedProbeVolume serialized, Editor owner) { var asset = serialized.probeVolumeAsset.objectReferenceValue as ProbeVolumeAsset; - if (asset != null && asset.payload.encodingMode != ShaderConfig.s_ProbeVolumesEncodingMode) - { - EditorGUILayout.HelpBox(Styles.k_FeatureEncodingModeMismatchError, MessageType.Error); - } if (ShaderConfig.s_ProbeVolumesBilateralFilteringMode == ProbeVolumesBilateralFilteringModes.OctahedralDepth && asset != null && asset.payload.dataOctahedralDepth == null) diff --git a/com.unity.render-pipelines.high-definition/Editor/Lighting/ProbeVolume/ProbeVolumeUI.Skin.cs b/com.unity.render-pipelines.high-definition/Editor/Lighting/ProbeVolume/ProbeVolumeUI.Skin.cs index 660e5bf3a81..78eaca10d7e 100644 --- a/com.unity.render-pipelines.high-definition/Editor/Lighting/ProbeVolume/ProbeVolumeUI.Skin.cs +++ b/com.unity.render-pipelines.high-definition/Editor/Lighting/ProbeVolume/ProbeVolumeUI.Skin.cs @@ -9,7 +9,6 @@ internal static class Styles internal const string k_FeatureWarning = "Warning: Probe Volumes is a highly experimental feature.\nIt is disabled by default for this reason.\nIt's functionality is subject to breaking changes and whole sale removal.\nIt is not recommended for use outside of for providing feedback.\nIt should not be used in production."; internal const string k_FeatureEnableInfo = "\nProbe Volumes feature is disabled. To enable, set:\nProbeVolumesEvaluationMode = ProbeVolumesEvaluationModes.MaterialPass\ninside of ShaderConfig.cs. Then inside of the editor run:\nEdit->Render Pipeline->Generate Shader Includes\nProbe Volumes feature must also be enabled inside of your HDRenderPipelineAsset."; internal const string k_FeatureAdditiveBlendingDisabledError = "Error: ProbeVolumesAdditiveBlending feature is disabled inside of ShaderConfig.cs.\nThis probe volume will not be rendered.\nTo enable, set:\nProbeVolumesAdditiveBlending = 1\ninside of ShaderConfig.cs. Then inside of the editor run:\nEdit->Render Pipeline->Generate Shader Includes."; - internal const string k_FeatureEncodingModeMismatchError = "Error: ProbeVolumesEncodingMode inside of ShaderConfig.cs is configured to a different mode than this probe volume was previously baked with.\nThis probe volume will not be rendered.\nTo correct this:\nA) Rebake this probe volume.\nor\nB) Change ProbeVolumesEncodingMode inside of ShaderConfig.cs. Then inside of the editor run:\nEdit->Render Pipeline->Generate Shader Includes."; internal const string k_FeatureOctahedralDepthEnabledNoData = "Error: ProbeVolumesBilateralFilteringMode inside of ShaderConfig.cs is set to OctahedralDepth, but asset was baked with OctahedralDepth disabled.\nPlease rebake if you would like this probe volume to use octahedral depth filtering."; internal const string k_FeatureOctahedralDepthDisableYesData = "Error: ProbeVolumesBilateralFilteringMode inside of ShaderConfig.cs is not set to OctahedralDepth, but was baked with OctahedralDepth enabled.\nPlease rebake to discard octahedral depth data from asset."; internal const string k_VolumeHeader = "Volume"; diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.cs b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.cs index 18291ff7c06..94092cd699f 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.cs @@ -22,41 +22,87 @@ internal enum VolumeBlendMode } // Container structure for managing a probe volume's payload. - // Spherical Harmonics data is stored as a flat float coefficients array. - // encodingMode defines the view for dataSH, specifically the stride at which coefficients map to a single probe's SH sample. - // In the future, encodingMode could possibly be extended to handle different compression modes as well. + // Spherical Harmonics L2 data is stored across two flat float coefficients arrays, one for L0 and L1 terms, and one for L2 terms. + // Storing these as two seperate arrays makes it easy for us to conditionally only upload SHL01 terms when the render pipeline is + // configured to unly use an SphericalHarmonicsL1 atlas. + // It will also enable us in the future to strip the SHL2 coefficients from the project at build time if only SHL1 is requested. + // SH Coefficients are serialized in this order, regardless of their format. + // SH1 will only serialize the first 12 + // SH2 will serialize all 27 + // This is not the order SphericalHarmonicsL2 natively stores these coefficients, + // and it is also not the order that GPU EntityLighting.hlsl functions expect them in. + // This order is optimized for minimizing the number of coefficients fetched on the GPU + // when sampling various SH formats. + // i.e: If the atlas is configured for SH2, but only SH0 is requested by a specific shader, + // only the first three coefficients need to be fetched. + // The atlasing code may make changes to the way this data is laid out in textures, + // but having them laid out in polynomial order on disk makes writing the atlas transcodings easier. + // Note: the coefficients in the L2 case are not fully normalized, + // The data in the SH probe sample passed here is expected to already be normalized with kNormalizationConstants. + // The complete normalization must be deferred until sample time on the GPU, since it should only be applied for SH2. + // GPU code will be responsible for performing final normalization + swizzle into formats + // that SampleSH9(), and SHEvalLinearL0L1() expect. + // Note: the names for these coefficients is consistent with Unity's internal spherical harmonics use, + // and are originally from: https://www.ppsloan.org/publications/StupidSH36.pdf + /* + { + // Constant: (used by L0, L1, and L2) + shAr.w, shAg.w, shAb.w, + + // Linear: (used by L1 and L2) + shAr.x, shAr.y, shAr.z, + shAg.x, shAg.y, shAg.z, + shAb.x, shAb.y, shAb.z, + + // Quadratic: (used by L2) + shBr.x, shBr.y, shBr.z, shBr.w, + shBg.x, shBg.y, shBg.z, shBg.w, + shBb.x, shBb.y, shBb.z, shBb.w, + shCr.x, shCr.y, shCr.z + } + */ [Serializable] internal struct ProbeVolumePayload { - public ProbeVolumesEncodingModes encodingMode; - public float[] dataSH; + public float[] dataSHL01; + public float[] dataSHL2; public float[] dataValidity; public float[] dataOctahedralDepth; - public static int GetSHStride(ProbeVolumesEncodingModes encodingMode) + public static readonly ProbeVolumePayload zero = new ProbeVolumePayload { - switch (encodingMode) - { - case ProbeVolumesEncodingModes.SphericalHarmonicsL0: return 3; - case ProbeVolumesEncodingModes.SphericalHarmonicsL1: return 12; - case ProbeVolumesEncodingModes.SphericalHarmonicsL2: return 27; - default: - { - Debug.Assert(false, "Error: encountered invalid encodingMode."); - return 0; - } - } + dataSHL01 = null, + dataSHL2 = null, + dataValidity = null, + dataOctahedralDepth = null + }; + + public static int GetDataSHL01Stride() + { + return 4 * 3; + } + + public static int GetDataSHL2Stride() + { + return 9 * 3 - GetDataSHL01Stride(); + } + + public static bool IsNull(ref ProbeVolumePayload payload) + { + return payload.dataSHL01 == null; } public static int GetLength(ref ProbeVolumePayload payload) { + // No need to explicitly store probe length - dataValidity is one value per probe, so we can just query the length here. return payload.dataValidity.Length; } - public static void Allocate(ref ProbeVolumePayload payload, ProbeVolumesEncodingModes encodingMode, int length) + public static void Allocate(ref ProbeVolumePayload payload, int length) { - payload.encodingMode = encodingMode; - payload.dataSH = new float[length * GetSHStride(encodingMode)]; + payload.dataSHL01 = new float[length * GetDataSHL01Stride()]; + payload.dataSHL2 = new float[length * GetDataSHL2Stride()]; + // TODO: Only allocate dataValidity and dataOctahedralDepth if those payload slices are in use. payload.dataValidity = new float[length]; @@ -68,20 +114,20 @@ public static void Allocate(ref ProbeVolumePayload payload, ProbeVolumesEncoding } } - public static void Ensure(ref ProbeVolumePayload payload, ProbeVolumesEncodingModes encodingMode, int length) + public static void Ensure(ref ProbeVolumePayload payload, int length) { - if (payload.encodingMode != encodingMode - || payload.dataSH == null - || payload.dataSH.Length != (length * GetSHStride(encodingMode))) + if (payload.dataSHL01 == null + || payload.dataSHL01.Length != (length * GetDataSHL01Stride())) { ProbeVolumePayload.Dispose(ref payload); - ProbeVolumePayload.Allocate(ref payload, encodingMode, length); + ProbeVolumePayload.Allocate(ref payload, length); } } public static void Dispose(ref ProbeVolumePayload payload) { - payload.dataSH = null; + payload.dataSHL01 = null; + payload.dataSHL2 = null; payload.dataValidity = null; payload.dataOctahedralDepth = null; } @@ -95,9 +141,8 @@ public static void Copy(ref ProbeVolumePayload payloadSrc, ref ProbeVolumePayloa public static void Copy(ref ProbeVolumePayload payloadSrc, ref ProbeVolumePayload payloadDst, int length) { - Debug.Assert(payloadSrc.encodingMode == payloadDst.encodingMode); - - Array.Copy(payloadSrc.dataSH, payloadDst.dataSH, length * GetSHStride(payloadSrc.encodingMode)); + Array.Copy(payloadSrc.dataSHL01, payloadDst.dataSHL01, length * GetDataSHL01Stride()); + Array.Copy(payloadSrc.dataSHL2, payloadDst.dataSHL2, length * GetDataSHL2Stride()); Array.Copy(payloadSrc.dataValidity, payloadDst.dataValidity, length); @@ -107,174 +152,201 @@ public static void Copy(ref ProbeVolumePayload payloadSrc, ref ProbeVolumePayloa } } - public static void GetSphericalHarmonicsL0FromIndex(ref SphericalHarmonicsL0 sh, ref ProbeVolumePayload payload, int indexProbe) - { - Debug.Assert(payload.encodingMode == ProbeVolumesEncodingModes.SphericalHarmonicsL0); - - int stride = GetSHStride(ProbeVolumesEncodingModes.SphericalHarmonicsL0); - int indexDataBase = indexProbe * stride; - int indexDataEnd = indexDataBase + stride; - - Debug.Assert(payload.dataSH != null); - Debug.Assert(payload.dataSH.Length >= indexDataEnd); - - sh.shrgb.x = payload.dataSH[indexDataBase + 0]; - sh.shrgb.y = payload.dataSH[indexDataBase + 1]; - sh.shrgb.z = payload.dataSH[indexDataBase + 2]; - } - public static void GetSphericalHarmonicsL1FromIndex(ref SphericalHarmonicsL1 sh, ref ProbeVolumePayload payload, int indexProbe) { - Debug.Assert(payload.encodingMode == ProbeVolumesEncodingModes.SphericalHarmonicsL1); - - int stride = GetSHStride(ProbeVolumesEncodingModes.SphericalHarmonicsL1); - int indexDataBase = indexProbe * stride; - int indexDataEnd = indexDataBase + stride; - - Debug.Assert(payload.dataSH != null); - Debug.Assert(payload.dataSH.Length >= indexDataEnd); - - sh.shAr.x = payload.dataSH[indexDataBase + 0]; - sh.shAr.y = payload.dataSH[indexDataBase + 1]; - sh.shAr.z = payload.dataSH[indexDataBase + 2]; - sh.shAr.w = payload.dataSH[indexDataBase + 3]; - - sh.shAg.x = payload.dataSH[indexDataBase + 4]; - sh.shAg.y = payload.dataSH[indexDataBase + 5]; - sh.shAg.z = payload.dataSH[indexDataBase + 6]; - sh.shAg.w = payload.dataSH[indexDataBase + 7]; - - sh.shAb.x = payload.dataSH[indexDataBase + 8]; - sh.shAb.y = payload.dataSH[indexDataBase + 9]; - sh.shAb.z = payload.dataSH[indexDataBase + 10]; - sh.shAb.w = payload.dataSH[indexDataBase + 11]; + int strideSHL01 = GetDataSHL01Stride(); + int indexDataBaseSHL01 = indexProbe * strideSHL01; + int indexDataEndSHL01 = indexDataBaseSHL01 + strideSHL01; + + Debug.Assert(payload.dataSHL01 != null); + Debug.Assert(payload.dataSHL01.Length >= indexDataEndSHL01); + + // Constant (DC terms): + sh.shAr.w = payload.dataSHL01[indexDataBaseSHL01 + 0]; // shAr.w + sh.shAg.w = payload.dataSHL01[indexDataBaseSHL01 + 1]; // shAg.w + sh.shAb.w = payload.dataSHL01[indexDataBaseSHL01 + 2]; // shAb.w + + // Linear: (used by L1 and L2) + // Swizzle the coefficients to be in { x, y, z } order. + sh.shAr.x = payload.dataSHL01[indexDataBaseSHL01 + 3]; // shAr.x + sh.shAr.y = payload.dataSHL01[indexDataBaseSHL01 + 4]; // shAr.y + sh.shAr.z = payload.dataSHL01[indexDataBaseSHL01 + 5]; // shAr.z + + sh.shAg.x = payload.dataSHL01[indexDataBaseSHL01 + 6]; // shAg.x + sh.shAg.y = payload.dataSHL01[indexDataBaseSHL01 + 7]; // shAg.y + sh.shAg.z = payload.dataSHL01[indexDataBaseSHL01 + 8]; // shAg.z + + sh.shAb.x = payload.dataSHL01[indexDataBaseSHL01 + 9]; // shAb.x + sh.shAb.y = payload.dataSHL01[indexDataBaseSHL01 + 10]; // shAb.y + sh.shAb.z = payload.dataSHL01[indexDataBaseSHL01 + 11]; // shAb.z } public static void GetSphericalHarmonicsL2FromIndex(ref SphericalHarmonicsL2 sh, ref ProbeVolumePayload payload, int indexProbe) { - Debug.Assert(payload.encodingMode == ProbeVolumesEncodingModes.SphericalHarmonicsL2); - - int stride = GetSHStride(ProbeVolumesEncodingModes.SphericalHarmonicsL2); - int indexDataBase = indexProbe * stride; - int indexDataEnd = indexDataBase + stride; - - Debug.Assert(payload.dataSH != null); - Debug.Assert(payload.dataSH.Length >= indexDataEnd); - - sh[0, 0] = payload.dataSH[indexDataBase + 0]; - sh[0, 1] = payload.dataSH[indexDataBase + 1]; - sh[0, 2] = payload.dataSH[indexDataBase + 2]; - sh[0, 3] = payload.dataSH[indexDataBase + 3]; - sh[0, 4] = payload.dataSH[indexDataBase + 4]; - sh[0, 5] = payload.dataSH[indexDataBase + 5]; - sh[0, 6] = payload.dataSH[indexDataBase + 6]; - sh[0, 7] = payload.dataSH[indexDataBase + 7]; - sh[0, 8] = payload.dataSH[indexDataBase + 8]; - - sh[1, 0] = payload.dataSH[indexDataBase + 9]; - sh[1, 1] = payload.dataSH[indexDataBase + 10]; - sh[1, 2] = payload.dataSH[indexDataBase + 11]; - sh[1, 3] = payload.dataSH[indexDataBase + 12]; - sh[1, 4] = payload.dataSH[indexDataBase + 13]; - sh[1, 5] = payload.dataSH[indexDataBase + 14]; - sh[1, 6] = payload.dataSH[indexDataBase + 15]; - sh[1, 7] = payload.dataSH[indexDataBase + 16]; - sh[1, 8] = payload.dataSH[indexDataBase + 17]; - - sh[2, 0] = payload.dataSH[indexDataBase + 18]; - sh[2, 1] = payload.dataSH[indexDataBase + 19]; - sh[2, 2] = payload.dataSH[indexDataBase + 20]; - sh[2, 3] = payload.dataSH[indexDataBase + 21]; - sh[2, 4] = payload.dataSH[indexDataBase + 22]; - sh[2, 5] = payload.dataSH[indexDataBase + 23]; - sh[2, 6] = payload.dataSH[indexDataBase + 24]; - sh[2, 7] = payload.dataSH[indexDataBase + 25]; - sh[2, 8] = payload.dataSH[indexDataBase + 26]; - } - - public static void SetSphericalHarmonicsL0FromIndex(ref ProbeVolumePayload payload, ref SphericalHarmonicsL0 sh, int indexProbe) - { - Debug.Assert(payload.encodingMode == ProbeVolumesEncodingModes.SphericalHarmonicsL0); - - int stride = GetSHStride(ProbeVolumesEncodingModes.SphericalHarmonicsL0); - int indexDataBase = indexProbe * stride; - int indexDataEnd = indexDataBase + stride; - - Debug.Assert(payload.dataSH != null); - Debug.Assert(payload.dataSH.Length >= indexDataEnd); - - payload.dataSH[indexDataBase + 0] = sh.shrgb.x; - payload.dataSH[indexDataBase + 1] = sh.shrgb.y; - payload.dataSH[indexDataBase + 2] = sh.shrgb.z; + int strideSHL01 = GetDataSHL01Stride(); + int indexDataBaseSHL01 = indexProbe * strideSHL01; + int indexDataEndSHL01 = indexDataBaseSHL01 + strideSHL01; + + Debug.Assert(payload.dataSHL01 != null); + Debug.Assert(payload.dataSHL01.Length >= indexDataEndSHL01); + + int strideSHL2 = GetDataSHL2Stride(); + int indexDataBaseSHL2 = indexProbe * strideSHL2; + int indexDataEndSHL2 = indexDataBaseSHL2 + strideSHL2; + + Debug.Assert(payload.dataSHL2 != null); + Debug.Assert(payload.dataSHL2.Length >= indexDataEndSHL2); + + // Constant (DC terms): + sh[0, 0] = payload.dataSHL01[indexDataBaseSHL01 + 0]; // shAr.w + sh[1, 0] = payload.dataSHL01[indexDataBaseSHL01 + 1]; // shAg.w + sh[2, 0] = payload.dataSHL01[indexDataBaseSHL01 + 2]; // shAb.w + + // Linear: (used by L1 and L2) + // Swizzle the coefficients to be in { x, y, z } order. + sh[0, 3] = payload.dataSHL01[indexDataBaseSHL01 + 3]; // shAr.x + sh[0, 1] = payload.dataSHL01[indexDataBaseSHL01 + 4]; // shAr.y + sh[0, 2] = payload.dataSHL01[indexDataBaseSHL01 + 5]; // shAr.z + + sh[1, 3] = payload.dataSHL01[indexDataBaseSHL01 + 6]; // shAg.x + sh[1, 1] = payload.dataSHL01[indexDataBaseSHL01 + 7]; // shAg.y + sh[1, 2] = payload.dataSHL01[indexDataBaseSHL01 + 8]; // shAg.z + + sh[2, 3] = payload.dataSHL01[indexDataBaseSHL01 + 9]; // shAb.x + sh[2, 1] = payload.dataSHL01[indexDataBaseSHL01 + 10]; // shAb.y + sh[2, 2] = payload.dataSHL01[indexDataBaseSHL01 + 11]; // shAb.z + + // Quadratic: (used by L2) + sh[0, 4] = payload.dataSHL2[indexDataBaseSHL2 + 0]; // shBr.x + sh[0, 5] = payload.dataSHL2[indexDataBaseSHL2 + 1]; // shBr.y + sh[0, 6] = payload.dataSHL2[indexDataBaseSHL2 + 2]; // shBr.z + sh[0, 7] = payload.dataSHL2[indexDataBaseSHL2 + 3]; // shBr.w + + sh[1, 4] = payload.dataSHL2[indexDataBaseSHL2 + 4]; // shBg.x + sh[1, 5] = payload.dataSHL2[indexDataBaseSHL2 + 5]; // shBg.y + sh[1, 6] = payload.dataSHL2[indexDataBaseSHL2 + 6]; // shBg.z + sh[1, 7] = payload.dataSHL2[indexDataBaseSHL2 + 7]; // shBg.w + + sh[2, 4] = payload.dataSHL2[indexDataBaseSHL2 + 8]; // shBb.x + sh[2, 5] = payload.dataSHL2[indexDataBaseSHL2 + 9]; // shBb.y + sh[2, 6] = payload.dataSHL2[indexDataBaseSHL2 + 10]; // shBb.z + sh[2, 7] = payload.dataSHL2[indexDataBaseSHL2 + 11]; // shBb.w + + sh[0, 8] = payload.dataSHL2[indexDataBaseSHL2 + 12]; // shCr.x + sh[1, 8] = payload.dataSHL2[indexDataBaseSHL2 + 13]; // shCr.y + sh[2, 8] = payload.dataSHL2[indexDataBaseSHL2 + 14]; // shCr.z } - public static void SetSphericalHarmonicsL1FromIndex(ref ProbeVolumePayload payload, ref SphericalHarmonicsL1 sh, int indexProbe) + public static void SetSphericalHarmonicsL1FromIndex(ref ProbeVolumePayload payload, SphericalHarmonicsL1 sh, int indexProbe) { - Debug.Assert(payload.encodingMode == ProbeVolumesEncodingModes.SphericalHarmonicsL1); - - int stride = GetSHStride(ProbeVolumesEncodingModes.SphericalHarmonicsL1); - int indexDataBase = indexProbe * stride; - int indexDataEnd = indexDataBase + stride; - - Debug.Assert(payload.dataSH != null); - Debug.Assert(payload.dataSH.Length >= indexDataEnd); - - payload.dataSH[indexDataBase + 0] = sh.shAr.x; - payload.dataSH[indexDataBase + 1] = sh.shAr.y; - payload.dataSH[indexDataBase + 2] = sh.shAr.z; - payload.dataSH[indexDataBase + 3] = sh.shAr.w; - - payload.dataSH[indexDataBase + 4] = sh.shAg.x; - payload.dataSH[indexDataBase + 5] = sh.shAg.y; - payload.dataSH[indexDataBase + 6] = sh.shAg.z; - payload.dataSH[indexDataBase + 7] = sh.shAg.w; - - payload.dataSH[indexDataBase + 8] = sh.shAb.x; - payload.dataSH[indexDataBase + 9] = sh.shAb.y; - payload.dataSH[indexDataBase + 10] = sh.shAb.z; - payload.dataSH[indexDataBase + 11] = sh.shAb.w; + int strideSHL01 = GetDataSHL01Stride(); + int indexDataBaseSHL01 = indexProbe * strideSHL01; + int indexDataEndSHL01 = indexDataBaseSHL01 + strideSHL01; + + Debug.Assert(payload.dataSHL01 != null); + Debug.Assert(payload.dataSHL01.Length >= indexDataEndSHL01); + + int strideSHL2 = GetDataSHL2Stride(); + int indexDataBaseSHL2 = indexProbe * strideSHL2; + int indexDataEndSHL2 = indexDataBaseSHL2 + strideSHL2; + + Debug.Assert(payload.dataSHL2 != null); + Debug.Assert(payload.dataSHL2.Length >= indexDataEndSHL2); + + // Constant (DC terms): + payload.dataSHL01[indexDataBaseSHL01 + 0] = sh.shAr.w; + payload.dataSHL01[indexDataBaseSHL01 + 1] = sh.shAg.w; + payload.dataSHL01[indexDataBaseSHL01 + 2] = sh.shAb.w; + + // Linear: (used by L1 and L2) + // Swizzle the coefficients to be in { x, y, z } order. + payload.dataSHL01[indexDataBaseSHL01 + 3] = sh.shAr.x; + payload.dataSHL01[indexDataBaseSHL01 + 4] = sh.shAr.y; + payload.dataSHL01[indexDataBaseSHL01 + 5] = sh.shAr.z; + + payload.dataSHL01[indexDataBaseSHL01 + 6] = sh.shAg.x; + payload.dataSHL01[indexDataBaseSHL01 + 7] = sh.shAg.y; + payload.dataSHL01[indexDataBaseSHL01 + 8] = sh.shAg.z; + + payload.dataSHL01[indexDataBaseSHL01 + 9] = sh.shAb.x; + payload.dataSHL01[indexDataBaseSHL01 + 10] = sh.shAb.y; + payload.dataSHL01[indexDataBaseSHL01 + 11] = sh.shAb.z; + + // Quadratic: (used by L2) + payload.dataSHL2[indexDataBaseSHL2 + 0] = 0.0f; // shBr.x + payload.dataSHL2[indexDataBaseSHL2 + 1] = 0.0f; // shBr.y + payload.dataSHL2[indexDataBaseSHL2 + 2] = 0.0f; // shBr.z + payload.dataSHL2[indexDataBaseSHL2 + 3] = 0.0f; // shBr.w + + payload.dataSHL2[indexDataBaseSHL2 + 4] = 0.0f; // shBg.x + payload.dataSHL2[indexDataBaseSHL2 + 5] = 0.0f; // shBg.y + payload.dataSHL2[indexDataBaseSHL2 + 6] = 0.0f; // shBg.z + payload.dataSHL2[indexDataBaseSHL2 + 7] = 0.0f; // shBg.w + + payload.dataSHL2[indexDataBaseSHL2 + 8] = 0.0f; // shBb.x + payload.dataSHL2[indexDataBaseSHL2 + 9] = 0.0f; // shBb.y + payload.dataSHL2[indexDataBaseSHL2 + 10] = 0.0f; // shBb.z + payload.dataSHL2[indexDataBaseSHL2 + 11] = 0.0f; // shBb.w + + payload.dataSHL2[indexDataBaseSHL2 + 12] = 0.0f; // shCr.x + payload.dataSHL2[indexDataBaseSHL2 + 13] = 0.0f; // shCr.y + payload.dataSHL2[indexDataBaseSHL2 + 14] = 0.0f; // shCr.z } - public static void SetSphericalHarmonicsL2FromIndex(ref ProbeVolumePayload payload, ref SphericalHarmonicsL2 sh, int indexProbe) + public static void SetSphericalHarmonicsL2FromIndex(ref ProbeVolumePayload payload, SphericalHarmonicsL2 sh, int indexProbe) { - Debug.Assert(payload.encodingMode == ProbeVolumesEncodingModes.SphericalHarmonicsL2); - - int stride = GetSHStride(ProbeVolumesEncodingModes.SphericalHarmonicsL2); - int indexDataBase = indexProbe * stride; - int indexDataEnd = indexDataBase + stride; - - Debug.Assert(payload.dataSH != null); - Debug.Assert(payload.dataSH.Length >= indexDataEnd); - - payload.dataSH[indexDataBase + 0] = sh[0, 0]; - payload.dataSH[indexDataBase + 1] = sh[0, 1]; - payload.dataSH[indexDataBase + 2] = sh[0, 2]; - payload.dataSH[indexDataBase + 3] = sh[0, 3]; - payload.dataSH[indexDataBase + 4] = sh[0, 4]; - payload.dataSH[indexDataBase + 5] = sh[0, 5]; - payload.dataSH[indexDataBase + 6] = sh[0, 6]; - payload.dataSH[indexDataBase + 7] = sh[0, 7]; - payload.dataSH[indexDataBase + 8] = sh[0, 8]; - - payload.dataSH[indexDataBase + 9] = sh[1, 0]; - payload.dataSH[indexDataBase + 10] = sh[1, 1]; - payload.dataSH[indexDataBase + 11] = sh[1, 2]; - payload.dataSH[indexDataBase + 12] = sh[1, 3]; - payload.dataSH[indexDataBase + 13] = sh[1, 4]; - payload.dataSH[indexDataBase + 14] = sh[1, 5]; - payload.dataSH[indexDataBase + 15] = sh[1, 6]; - payload.dataSH[indexDataBase + 16] = sh[1, 7]; - payload.dataSH[indexDataBase + 17] = sh[1, 8]; - - payload.dataSH[indexDataBase + 18] = sh[2, 0]; - payload.dataSH[indexDataBase + 19] = sh[2, 1]; - payload.dataSH[indexDataBase + 20] = sh[2, 2]; - payload.dataSH[indexDataBase + 21] = sh[2, 3]; - payload.dataSH[indexDataBase + 22] = sh[2, 4]; - payload.dataSH[indexDataBase + 23] = sh[2, 5]; - payload.dataSH[indexDataBase + 24] = sh[2, 6]; - payload.dataSH[indexDataBase + 25] = sh[2, 7]; - payload.dataSH[indexDataBase + 26] = sh[2, 8]; + int strideSHL01 = GetDataSHL01Stride(); + int indexDataBaseSHL01 = indexProbe * strideSHL01; + int indexDataEndSHL01 = indexDataBaseSHL01 + strideSHL01; + + Debug.Assert(payload.dataSHL01 != null); + Debug.Assert(payload.dataSHL01.Length >= indexDataEndSHL01); + + int strideSHL2 = GetDataSHL2Stride(); + int indexDataBaseSHL2 = indexProbe * strideSHL2; + int indexDataEndSHL2 = indexDataBaseSHL2 + strideSHL2; + + Debug.Assert(payload.dataSHL2 != null); + Debug.Assert(payload.dataSHL2.Length >= indexDataEndSHL2); + + // Constant (DC terms): + payload.dataSHL01[indexDataBaseSHL01 + 0] = sh[0, 0]; // shAr.w + payload.dataSHL01[indexDataBaseSHL01 + 1] = sh[1, 0]; // shAg.w + payload.dataSHL01[indexDataBaseSHL01 + 2] = sh[2, 0]; // shAb.w + + // Linear: (used by L1 and L2) + // Swizzle the coefficients to be in { x, y, z } order. + payload.dataSHL01[indexDataBaseSHL01 + 3] = sh[0, 3]; // shAr.x + payload.dataSHL01[indexDataBaseSHL01 + 4] = sh[0, 1]; // shAr.y + payload.dataSHL01[indexDataBaseSHL01 + 5] = sh[0, 2]; // shAr.z + + payload.dataSHL01[indexDataBaseSHL01 + 6] = sh[1, 3]; // shAg.x + payload.dataSHL01[indexDataBaseSHL01 + 7] = sh[1, 1]; // shAg.y + payload.dataSHL01[indexDataBaseSHL01 + 8] = sh[1, 2]; // shAg.z + + payload.dataSHL01[indexDataBaseSHL01 + 9] = sh[2, 3]; // shAb.x + payload.dataSHL01[indexDataBaseSHL01 + 10] = sh[2, 1]; // shAb.y + payload.dataSHL01[indexDataBaseSHL01 + 11] = sh[2, 2]; // shAb.z + + // Quadratic: (used by L2) + payload.dataSHL2[indexDataBaseSHL2 + 0] = sh[0, 4]; // shBr.x + payload.dataSHL2[indexDataBaseSHL2 + 1] = sh[0, 5]; // shBr.y + payload.dataSHL2[indexDataBaseSHL2 + 2] = sh[0, 6]; // shBr.z + payload.dataSHL2[indexDataBaseSHL2 + 3] = sh[0, 7]; // shBr.w + + payload.dataSHL2[indexDataBaseSHL2 + 4] = sh[1, 4]; // shBg.x + payload.dataSHL2[indexDataBaseSHL2 + 5] = sh[1, 5]; // shBg.y + payload.dataSHL2[indexDataBaseSHL2 + 6] = sh[1, 6]; // shBg.z + payload.dataSHL2[indexDataBaseSHL2 + 7] = sh[1, 7]; // shBg.w + + payload.dataSHL2[indexDataBaseSHL2 + 8] = sh[2, 4]; // shBb.x + payload.dataSHL2[indexDataBaseSHL2 + 9] = sh[2, 5]; // shBb.y + payload.dataSHL2[indexDataBaseSHL2 + 10] = sh[2, 6]; // shBb.z + payload.dataSHL2[indexDataBaseSHL2 + 11] = sh[2, 7]; // shBb.w + + payload.dataSHL2[indexDataBaseSHL2 + 12] = sh[0, 8]; // shCr.x + payload.dataSHL2[indexDataBaseSHL2 + 13] = sh[1, 8]; // shCr.y + payload.dataSHL2[indexDataBaseSHL2 + 14] = sh[2, 8]; // shCr.z } } @@ -293,7 +365,6 @@ internal struct ProbeVolumeSettingsKey public int resolutionX; public int resolutionY; public int resolutionZ; - public ProbeVolumesEncodingModes encodingMode; public float backfaceTolerance; public int dilationIterations; @@ -511,7 +582,6 @@ internal class ProbeVolume : MonoBehaviour resolutionX = 0, resolutionY = 0, resolutionZ = 0, - encodingMode = (ProbeVolumesEncodingModes)0, backfaceTolerance = 0.0f, dilationIterations = 0 }; @@ -537,7 +607,6 @@ private void BakeKeyClear() resolutionX = 0, resolutionY = 0, resolutionZ = 0, - encodingMode = (ProbeVolumesEncodingModes)0, backfaceTolerance = 0.0f, dilationIterations = 0 }; @@ -547,15 +616,7 @@ internal ProbeVolumePayload GetPayload() { dataUpdated = false; - if (!probeVolumeAsset) - { - return new ProbeVolumePayload() - { - encodingMode = ShaderConfig.s_ProbeVolumesEncodingMode, - dataValidity = null, - dataOctahedralDepth = null - }; - } + if (!probeVolumeAsset) { return ProbeVolumePayload.zero; } return probeVolumeAsset.payload; } @@ -598,18 +659,12 @@ void ApplyMigrationAddProbeVolumesAtlasEncodingModes() probeVolumeAsset.m_Version = (int)ProbeVolumeAsset.AssetVersion.AddProbeVolumesAtlasEncodingModes; int probeLength = probeVolumeAsset.dataSH.Length; - probeVolumeAsset.payload = new ProbeVolumePayload - { - encodingMode = ProbeVolumesEncodingModes.SphericalHarmonicsL1, - dataSH = new float[probeLength * ProbeVolumePayload.GetSHStride(ProbeVolumesEncodingModes.SphericalHarmonicsL1)], - dataValidity = probeVolumeAsset.dataValidity, - dataOctahedralDepth = probeVolumeAsset.dataOctahedralDepth - }; - - int shStride = ProbeVolumePayload.GetSHStride(ProbeVolumesEncodingModes.SphericalHarmonicsL1); + + ProbeVolumePayload.Allocate(ref probeVolumeAsset.payload, probeLength); + for (int i = 0; i < probeLength; ++i) { - ProbeVolumePayload.SetSphericalHarmonicsL1FromIndex(ref probeVolumeAsset.payload, ref probeVolumeAsset.dataSH[i], i); + ProbeVolumePayload.SetSphericalHarmonicsL1FromIndex(ref probeVolumeAsset.payload, probeVolumeAsset.dataSH[i], i); } probeVolumeAsset.dataSH = null; @@ -651,7 +706,7 @@ protected void OnDisable() internal bool IsAssetCompatible() { - return IsAssetCompatibleResolution() && IsAssetCompatibleEncodingMode(); + return IsAssetCompatibleResolution(); } internal bool IsAssetCompatibleResolution() @@ -665,16 +720,6 @@ internal bool IsAssetCompatibleResolution() return false; } - internal bool IsAssetCompatibleEncodingMode() - { - if (probeVolumeAsset) - { - // TODO: Create runtime transforms between different encoding types to avoid having to rebake. - return probeVolumeAsset.payload.encodingMode == ShaderConfig.s_ProbeVolumesEncodingMode; - } - return false; - } - #if UNITY_EDITOR protected void Update() { @@ -714,12 +759,6 @@ protected void OnValidate() probeVolumeAsset.resolutionX, probeVolumeAsset.resolutionY, probeVolumeAsset.resolutionZ, parameters.resolutionX, parameters.resolutionY, parameters.resolutionZ); } - else if (!IsAssetCompatibleEncodingMode()) - { - Debug.LogWarningFormat("The asset \"{0}\" assigned to Probe Volume \"{1}\" does not have matching encoding mode ({2} vs. {3}), please rebake.", - probeVolumeAsset.name, this.name, - probeVolumeAsset.payload.encodingMode, ShaderConfig.s_ProbeVolumesEncodingMode); - } dataUpdated = true; } @@ -772,137 +811,13 @@ internal void OnProbesBakeCompleted() probeVolumeAsset.resolutionY = parameters.resolutionY; probeVolumeAsset.resolutionZ = parameters.resolutionZ; - ProbeVolumePayload.Ensure(ref probeVolumeAsset.payload, ShaderConfig.s_ProbeVolumesEncodingMode, numProbes); + ProbeVolumePayload.Ensure(ref probeVolumeAsset.payload, numProbes); - int shStride = ProbeVolumePayload.GetSHStride(probeVolumeAsset.payload.encodingMode); - - // SH Coefficients are serialized in this order, regardless of their format. - // SH0 will only serialize the first 3, - // SH1 will only serialize the first 12 - // SH2 will serialize all 27 - // This is not the order SphericalHarmonicsL2 natively stores these coefficients, - // and it is also not the order that GPU EntityLighting.hlsl functions expect them in. - // This order is optimized for minimizing the number of coefficients fetched on the GPU - // when sampling various SH formats. - // i.e: If the atlas is configured for SH2, but only SH0 is requested by a specific shader, - // only the first three coefficients need to be fetched. - // The atlasing code may make changes to the way this data is laid out in textures, - // but having them laid out in polynomial order on disk makes writing the atlas transcodings easier. - // Note: the coefficients in the L2 case are not fully normalized, - // The data in the SH probe sample passed here is expected to already be normalized with kNormalizationConstants. - // The complete normalization must be deferred until sample time on the GPU, since it should only be applied for SH2. - // GPU code will be responsible for performing final normalization + swizzle into formats - // that SampleSH9(), and SHEvalLinearL0L1() expect. - // Note: the names for these coefficients is consistent with Unity's internal spherical harmonics use, - // and are originally from: https://www.ppsloan.org/publications/StupidSH36.pdf - /* - { - // Constant: (used by L0, L1, and L2) - shAr.w, shAg.w, shAb.w, - - // Linear: (used by L1 and L2) - shAr.x, shAr.y, shAr.z, - shAg.x, shAg.y, shAg.z, - shAb.x, shAb.y, shAb.z, - - // Quadratic: (used by L2) - shBr.x, shBr.y, shBr.z, shBr.w, - shBg.x, shBg.y, shBg.z, shBg.w, - shBb.x, shBb.y, shBb.z, shBb.w, - shCr.x, shCr.y, shCr.z - } - */ - switch (probeVolumeAsset.payload.encodingMode) + // Always serialize L0, L1 and L2 coefficients, even if atlas is configured to only store L1. + // In the future we will strip the L2 coefficients from the project at build time if L2 is never used. + for (int i = 0, iLen = sh.Length; i < iLen; ++i) { - case ProbeVolumesEncodingModes.SphericalHarmonicsL0: - { - for (int i = 0, iLen = sh.Length; i < iLen; ++i) - { - // Constant (DC terms): - probeVolumeAsset.payload.dataSH[i * shStride + 0] = sh[i][0, 0]; // shAr.w - probeVolumeAsset.payload.dataSH[i * shStride + 1] = sh[i][1, 0]; // shAg.w - probeVolumeAsset.payload.dataSH[i * shStride + 2] = sh[i][2, 0]; // shAb.w - } - break; - } - - case ProbeVolumesEncodingModes.SphericalHarmonicsL1: - { - for (int i = 0, iLen = sh.Length; i < iLen; ++i) - { - // Constant (DC terms): - probeVolumeAsset.payload.dataSH[i * shStride + 0] = sh[i][0, 0]; // shAr.w - probeVolumeAsset.payload.dataSH[i * shStride + 1] = sh[i][1, 0]; // shAg.w - probeVolumeAsset.payload.dataSH[i * shStride + 2] = sh[i][2, 0]; // shAb.w - - // Linear: (used by L1 and L2) - // Swizzle the coefficients to be in { x, y, z } order. - probeVolumeAsset.payload.dataSH[i * shStride + 3] = sh[i][0, 3]; // shAr.x - probeVolumeAsset.payload.dataSH[i * shStride + 4] = sh[i][0, 1]; // shAr.y - probeVolumeAsset.payload.dataSH[i * shStride + 5] = sh[i][0, 2]; // shAr.z - - probeVolumeAsset.payload.dataSH[i * shStride + 3] = sh[i][1, 3]; // shAg.x - probeVolumeAsset.payload.dataSH[i * shStride + 4] = sh[i][1, 1]; // shAg.y - probeVolumeAsset.payload.dataSH[i * shStride + 5] = sh[i][1, 2]; // shAg.z - - probeVolumeAsset.payload.dataSH[i * shStride + 6] = sh[i][2, 3]; // shAb.x - probeVolumeAsset.payload.dataSH[i * shStride + 7] = sh[i][2, 1]; // shAb.y - probeVolumeAsset.payload.dataSH[i * shStride + 8] = sh[i][2, 2]; // shAb.z - } - break; - } - - case ProbeVolumesEncodingModes.SphericalHarmonicsL2: - { - for (int i = 0, iLen = sh.Length; i < iLen; ++i) - { - // Constant (DC terms): - probeVolumeAsset.payload.dataSH[i * shStride + 0] = sh[i][0, 0]; // shAr.w - probeVolumeAsset.payload.dataSH[i * shStride + 1] = sh[i][1, 0]; // shAg.w - probeVolumeAsset.payload.dataSH[i * shStride + 2] = sh[i][2, 0]; // shAb.w - - // Linear: (used by L1 and L2) - // Swizzle the coefficients to be in { x, y, z } order. - probeVolumeAsset.payload.dataSH[i * shStride + 3] = sh[i][0, 3]; // shAr.x - probeVolumeAsset.payload.dataSH[i * shStride + 4] = sh[i][0, 1]; // shAr.y - probeVolumeAsset.payload.dataSH[i * shStride + 5] = sh[i][0, 2]; // shAr.z - - probeVolumeAsset.payload.dataSH[i * shStride + 6] = sh[i][1, 3]; // shAg.x - probeVolumeAsset.payload.dataSH[i * shStride + 7] = sh[i][1, 1]; // shAg.y - probeVolumeAsset.payload.dataSH[i * shStride + 8] = sh[i][1, 2]; // shAg.z - - probeVolumeAsset.payload.dataSH[i * shStride + 9] = sh[i][2, 3]; // shAb.x - probeVolumeAsset.payload.dataSH[i * shStride + 10] = sh[i][2, 1]; // shAb.y - probeVolumeAsset.payload.dataSH[i * shStride + 11] = sh[i][2, 2]; // shAb.z - - // Quadratic: (used by L2) - probeVolumeAsset.payload.dataSH[i * shStride + 12] = sh[i][0, 4]; // shBr.x - probeVolumeAsset.payload.dataSH[i * shStride + 13] = sh[i][0, 5]; // shBr.y - probeVolumeAsset.payload.dataSH[i * shStride + 14] = sh[i][0, 6]; // shBr.z - probeVolumeAsset.payload.dataSH[i * shStride + 15] = sh[i][0, 7]; // shBr.w - - probeVolumeAsset.payload.dataSH[i * shStride + 16] = sh[i][1, 4]; // shBg.x - probeVolumeAsset.payload.dataSH[i * shStride + 17] = sh[i][1, 5]; // shBg.y - probeVolumeAsset.payload.dataSH[i * shStride + 18] = sh[i][1, 6]; // shBg.z - probeVolumeAsset.payload.dataSH[i * shStride + 19] = sh[i][1, 7]; // shBg.w - - probeVolumeAsset.payload.dataSH[i * shStride + 20] = sh[i][2, 4]; // shBb.x - probeVolumeAsset.payload.dataSH[i * shStride + 21] = sh[i][2, 5]; // shBb.y - probeVolumeAsset.payload.dataSH[i * shStride + 22] = sh[i][2, 6]; // shBb.z - probeVolumeAsset.payload.dataSH[i * shStride + 23] = sh[i][2, 7]; // shBb.w - - probeVolumeAsset.payload.dataSH[i * shStride + 24] = sh[i][0, 8]; // shCr.x - probeVolumeAsset.payload.dataSH[i * shStride + 25] = sh[i][1, 8]; // shCr.y - probeVolumeAsset.payload.dataSH[i * shStride + 26] = sh[i][2, 8]; // shCr.z - } - break; - } - - default: - { - Debug.Assert(false, "Error: Encountered unsupported probe volume payload encoding mode: " + probeVolumeAsset.payload.encodingMode); - break; - } + ProbeVolumePayload.SetSphericalHarmonicsL2FromIndex(ref probeVolumeAsset.payload, sh[i], i); } validity.CopyTo(probeVolumeAsset.payload.dataValidity); @@ -945,7 +860,6 @@ private static ProbeVolumeSettingsKey ComputeProbeVolumeSettingsKeyFromProbeVolu resolutionX = probeVolume.parameters.resolutionX, resolutionY = probeVolume.parameters.resolutionY, resolutionZ = probeVolume.parameters.resolutionZ, - encodingMode = probeVolume.probeVolumeAsset ? probeVolume.probeVolumeAsset.payload.encodingMode : (ProbeVolumesEncodingModes)0, backfaceTolerance = probeVolume.parameters.backfaceTolerance, dilationIterations = probeVolume.parameters.dilationIterations }; @@ -960,7 +874,6 @@ private static bool ProbeVolumeSettingsKeyEquals(ref ProbeVolumeSettingsKey a, r && (a.resolutionX == b.resolutionX) && (a.resolutionY == b.resolutionY) && (a.resolutionZ == b.resolutionZ) - && (a.encodingMode == b.encodingMode) && (a.backfaceTolerance == b.backfaceTolerance) && (a.dilationIterations == b.dilationIterations); } diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl index 165aab70343..2b819e7d85c 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl @@ -25,9 +25,7 @@ float ProbeVolumeComputeFadeFactor( float ProbeVolumeSampleValidity(float3 probeVolumeAtlasUVW) { -#if SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L0 - return SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 0), 0).w; -#elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L1 +#if SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L1 return SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 3), 0).x; #elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L2 return SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 6), 0).w; @@ -38,9 +36,7 @@ float ProbeVolumeSampleValidity(float3 probeVolumeAtlasUVW) float ProbeVolumeLoadValidity(int3 probeVolumeAtlasTexelCoord) { -#if SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L0 - return LOAD_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, int3(probeVolumeAtlasTexelCoord.x, probeVolumeAtlasTexelCoord.y, probeVolumeAtlasTexelCoord.z + _ProbeVolumeAtlasResolutionAndSliceCount.z * 0), 0).w; -#elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L1 +#if SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L1 return LOAD_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, int3(probeVolumeAtlasTexelCoord.x, probeVolumeAtlasTexelCoord.y, probeVolumeAtlasTexelCoord.z + _ProbeVolumeAtlasResolutionAndSliceCount.z * 3), 0).x; #elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L2 return LOAD_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, int3(probeVolumeAtlasTexelCoord.x, probeVolumeAtlasTexelCoord.y, probeVolumeAtlasTexelCoord.z + _ProbeVolumeAtlasResolutionAndSliceCount.z * 6), 0).w; @@ -395,12 +391,7 @@ void ProbeVolumeSampleAccumulateSphericalHarmonicsL0(float3 probeVolumeAtlasUVW, void ProbeVolumeSampleAccumulateSphericalHarmonicsL1(float3 probeVolumeAtlasUVW, float weight, inout ProbeVolumeSphericalHarmonicsL1 coefficients) { -#if SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L0 - // Requesting SH1, but atlas only contains SH0. - // Only accumulate SH0 coefficients. - coefficients.data[0].xyz += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 0), 0).xyz * weight; - -#elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L1 || SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L2 +#if SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L1 || SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L2 coefficients.data[0] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 0), 0) * weight; coefficients.data[1] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 1), 0) * weight; coefficients.data[2] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 2), 0) * weight; @@ -409,12 +400,7 @@ void ProbeVolumeSampleAccumulateSphericalHarmonicsL1(float3 probeVolumeAtlasUVW, void ProbeVolumeSampleAccumulateSphericalHarmonicsL2(float3 probeVolumeAtlasUVW, float weight, inout ProbeVolumeSphericalHarmonicsL2 coefficients) { -#if SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L0 - // Requesting SH2, but atlas only contains SH0. - // Only accumulate SH0 coefficients. - coefficients.data[0].xyz += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 0), 0).xyz * weight; - -#elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L1 +#if SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L1 // Requesting SH2, but atlas only contains SH1. // Only accumulate SH1 coefficients. coefficients.data[0] += SAMPLE_TEXTURE3D_LOD(_ProbeVolumeAtlasSH, s_linear_clamp_sampler, float3(probeVolumeAtlasUVW.x, probeVolumeAtlasUVW.y, probeVolumeAtlasUVW.z + _ProbeVolumeAtlasResolutionAndSliceCountInverse.w * 0), 0) * weight; diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeAsset.cs b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeAsset.cs index 8e6bfd60028..000943f933b 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeAsset.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeAsset.cs @@ -27,13 +27,7 @@ internal enum AssetVersion [SerializeField] internal float[] dataOctahedralDepth = null; - [SerializeField] internal ProbeVolumePayload payload = new ProbeVolumePayload() - { - encodingMode = ShaderConfig.s_ProbeVolumesEncodingMode, - dataSH = null, - dataValidity = null, - dataOctahedralDepth = null - }; + [SerializeField] internal ProbeVolumePayload payload = ProbeVolumePayload.zero; [SerializeField] internal int resolutionX; [SerializeField] internal int resolutionY; @@ -44,7 +38,7 @@ internal enum AssetVersion internal bool IsDataAssigned() { - return payload.dataSH != null; + return payload.dataSHL01 != null; } #if UNITY_EDITOR @@ -143,9 +137,8 @@ protected internal int ComputeIndex1DFrom3D(Vector3Int pos) bool OverwriteInvalidProbe(ref ProbeVolumePayload payloadSrc, ref ProbeVolumePayload payloadDst, Vector3Int index3D, float backfaceTolerance) { - Debug.Assert(payloadSrc.encodingMode == payloadDst.encodingMode); - - int shStride = ProbeVolumePayload.GetSHStride(payloadSrc.encodingMode); + int strideSHL01 = ProbeVolumePayload.GetDataSHL01Stride(); + int strideSHL2 = ProbeVolumePayload.GetDataSHL2Stride(); int centerIndex = ComputeIndex1DFrom3D(index3D); // Account for center sample accumulation weight, already assigned. @@ -173,9 +166,13 @@ bool OverwriteInvalidProbe(ref ProbeVolumePayload payloadSrc, ref ProbeVolumePay float sampleWeight = 1.0f - sampleValidity; weights += sampleWeight; - for (int c = 0; c < shStride; ++c) + for (int c = 0; c < strideSHL01; ++c) { - payloadDst.dataSH[centerIndex * shStride + c] += payloadSrc.dataSH[sampleIndex1D * shStride + c] * sampleWeight; + payloadDst.dataSHL01[centerIndex * strideSHL01 + c] += payloadSrc.dataSHL01[sampleIndex1D * strideSHL01 + c] * sampleWeight; + } + for (int c = 0; c < strideSHL2; ++c) + { + payloadDst.dataSHL2[centerIndex * strideSHL2 + c] += payloadSrc.dataSHL2[sampleIndex1D * strideSHL2 + c] * sampleWeight; } payloadDst.dataValidity[centerIndex] += sampleValidity * sampleWeight; @@ -184,9 +181,13 @@ bool OverwriteInvalidProbe(ref ProbeVolumePayload payloadSrc, ref ProbeVolumePay if (weights > 0.0f) { float weightsNormalization = 1.0f / weights; - for (int c = 0; c < shStride; ++c) + for (int c = 0; c < strideSHL01; ++c) + { + payloadDst.dataSHL01[centerIndex * strideSHL01 + c] *= weightsNormalization; + } + for (int c = 0; c < strideSHL2; ++c) { - payloadDst.dataSH[centerIndex * shStride + c] *= weightsNormalization; + payloadDst.dataSHL2[centerIndex * strideSHL2 + c] *= weightsNormalization; } payloadDst.dataValidity[centerIndex] *= weightsNormalization; @@ -205,8 +206,8 @@ void DilateIntoInvalidProbes(float backfaceTolerance, int dilateIterations) if (dilateIterations == 0) return; - ProbeVolumePayload payloadBackbuffer = new ProbeVolumePayload(); - ProbeVolumePayload.Allocate(ref payloadBackbuffer, payload.encodingMode, ProbeVolumePayload.GetLength(ref payload)); + ProbeVolumePayload payloadBackbuffer = ProbeVolumePayload.zero; + ProbeVolumePayload.Allocate(ref payloadBackbuffer, ProbeVolumePayload.GetLength(ref payload)); int i = 0; for (; i < dilateIterations; ++i) diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeAtlasBlit.compute b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeAtlasBlit.compute index 1e4ceb26f0a..73c5cbb1206 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeAtlasBlit.compute +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeAtlasBlit.compute @@ -7,10 +7,12 @@ #pragma only_renderers d3d11 playstation xboxone vulkan metal switch #include "Packages/com.unity.render-pipelines.high-definition-config/Runtime/ShaderConfig.cs.hlsl" -#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/SphericalHarmonics.cs.hlsl" #include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeLighting.cs.hlsl" -StructuredBuffer _ProbeVolumeAtlasReadBuffer; +StructuredBuffer _ProbeVolumeAtlasReadSHL01Buffer; +#if SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L2 +StructuredBuffer _ProbeVolumeAtlasReadSHL2Buffer; +#endif StructuredBuffer _ProbeVolumeAtlasReadValidityBuffer; RWTexture3D _ProbeVolumeAtlasWriteTextureSH; @@ -58,7 +60,7 @@ void PROBE_VOLUME_ATLAS_BLIT_KERNEL(uint groupThreadId : SV_GroupThreadID, uint float occlusion = _ProbeVolumeAtlasReadValidityBuffer[readIndex]; float validity = 1.0 - occlusion; - // See ProbeVolume.cs::OnBakeComplete() for more info on _ProbeVolumeAtlasReadBuffer layout. + // See ProbeVolumePayload for more info on _ProbeVolumeAtlasReadBufferSHLX layouts. // This shader must stay in sync with that layout: // { // // Constant: (used by L0, L1, and L2) @@ -75,80 +77,74 @@ void PROBE_VOLUME_ATLAS_BLIT_KERNEL(uint groupThreadId : SV_GroupThreadID, uint // shBb.x, shBb.y, shBb.z, shBb.w, // shCr.x, shCr.y, shCr.z // } -#if SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L0 - const uint SH_STRIDE = 3; - _ProbeVolumeAtlasWriteTextureSH[uint3(writeIndex.x, writeIndex.y, writeIndex.z + _ProbeVolumeAtlasResolutionAndSliceCount.z * 0)] = float4( - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 0], // shAr.w - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 1], // shAg.w - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 2], // shAb.w - validity // last channel in float4 is unused by SH0 terms, so we take this opportunity to pack validity into it. - ); -#elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L1 - const uint SH_STRIDE = 12; + const uint SH_STRIDE_L01 = 4 * 3; + const uint SH_STRIDE_L2 = (9 * 3) - SH_STRIDE_L01; + +#if SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L1 + _ProbeVolumeAtlasWriteTextureSH[uint3(writeIndex.x, writeIndex.y, writeIndex.z + _ProbeVolumeAtlasResolutionAndSliceCount.z * 0)] = float4( - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 0 + 0], // shAr.w - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 0 + 1], // shAg.w - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 0 + 2], // shAb.w - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 0 + 3] // shAr.x + _ProbeVolumeAtlasReadSHL01Buffer[readIndex * SH_STRIDE_L01 + 0], // shAr.w + _ProbeVolumeAtlasReadSHL01Buffer[readIndex * SH_STRIDE_L01 + 1], // shAg.w + _ProbeVolumeAtlasReadSHL01Buffer[readIndex * SH_STRIDE_L01 + 2], // shAb.w + _ProbeVolumeAtlasReadSHL01Buffer[readIndex * SH_STRIDE_L01 + 3] // shAr.x ); _ProbeVolumeAtlasWriteTextureSH[uint3(writeIndex.x, writeIndex.y, writeIndex.z + _ProbeVolumeAtlasResolutionAndSliceCount.z * 1)] = float4( - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 4 + 0], // shAr.y - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 4 + 1], // shAr.z - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 4 + 2], // shAg.x - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 4 + 3] // shAg.y + _ProbeVolumeAtlasReadSHL01Buffer[readIndex * SH_STRIDE_L01 + 4], // shAr.y + _ProbeVolumeAtlasReadSHL01Buffer[readIndex * SH_STRIDE_L01 + 5], // shAr.z + _ProbeVolumeAtlasReadSHL01Buffer[readIndex * SH_STRIDE_L01 + 6], // shAg.x + _ProbeVolumeAtlasReadSHL01Buffer[readIndex * SH_STRIDE_L01 + 7] // shAg.y ); _ProbeVolumeAtlasWriteTextureSH[uint3(writeIndex.x, writeIndex.y, writeIndex.z + _ProbeVolumeAtlasResolutionAndSliceCount.z * 2)] = float4( - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 8 + 0], // shAg.z - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 8 + 1], // shAb.x - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 8 + 2], // shAb.y - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 8 + 3] // shAb.z + _ProbeVolumeAtlasReadSHL01Buffer[readIndex * SH_STRIDE_L01 + 8], // shAg.z + _ProbeVolumeAtlasReadSHL01Buffer[readIndex * SH_STRIDE_L01 + 9], // shAb.x + _ProbeVolumeAtlasReadSHL01Buffer[readIndex * SH_STRIDE_L01 + 10], // shAb.y + _ProbeVolumeAtlasReadSHL01Buffer[readIndex * SH_STRIDE_L01 + 11] // shAb.z ); _ProbeVolumeAtlasWriteTextureSH[uint3(writeIndex.x, writeIndex.y, writeIndex.z + _ProbeVolumeAtlasResolutionAndSliceCount.z * 3)] = float4(validity, 0.0, 0.0, 0.0); #elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L2 - const uint SH_STRIDE = 27; _ProbeVolumeAtlasWriteTextureSH[uint3(writeIndex.x, writeIndex.y, writeIndex.z + _ProbeVolumeAtlasResolutionAndSliceCount.z * 0)] = float4( - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 0 + 0], // shAr.w - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 0 + 1], // shAg.w - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 0 + 2], // shAb.w - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 0 + 3] // shAr.x + _ProbeVolumeAtlasReadSHL01Buffer[readIndex * SH_STRIDE_L01 + 0], // shAr.w + _ProbeVolumeAtlasReadSHL01Buffer[readIndex * SH_STRIDE_L01 + 1], // shAg.w + _ProbeVolumeAtlasReadSHL01Buffer[readIndex * SH_STRIDE_L01 + 2], // shAb.w + _ProbeVolumeAtlasReadSHL01Buffer[readIndex * SH_STRIDE_L01 + 3] // shAr.x ); _ProbeVolumeAtlasWriteTextureSH[uint3(writeIndex.x, writeIndex.y, writeIndex.z + _ProbeVolumeAtlasResolutionAndSliceCount.z * 1)] = float4( - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 4 + 0], // shAr.y - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 4 + 1], // shAr.z - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 4 + 2], // shAg.x - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 4 + 3] // shAg.y + _ProbeVolumeAtlasReadSHL01Buffer[readIndex * SH_STRIDE_L01 + 4], // shAr.y + _ProbeVolumeAtlasReadSHL01Buffer[readIndex * SH_STRIDE_L01 + 5], // shAr.z + _ProbeVolumeAtlasReadSHL01Buffer[readIndex * SH_STRIDE_L01 + 6], // shAg.x + _ProbeVolumeAtlasReadSHL01Buffer[readIndex * SH_STRIDE_L01 + 7] // shAg.y ); _ProbeVolumeAtlasWriteTextureSH[uint3(writeIndex.x, writeIndex.y, writeIndex.z + _ProbeVolumeAtlasResolutionAndSliceCount.z * 2)] = float4( - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 8 + 0], // shAg.z - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 8 + 1], // shAb.x - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 8 + 2], // shAb.y - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 8 + 3] // shAb.z + _ProbeVolumeAtlasReadSHL01Buffer[readIndex * SH_STRIDE_L01 + 8], // shAg.z + _ProbeVolumeAtlasReadSHL01Buffer[readIndex * SH_STRIDE_L01 + 9], // shAb.x + _ProbeVolumeAtlasReadSHL01Buffer[readIndex * SH_STRIDE_L01 + 10], // shAb.y + _ProbeVolumeAtlasReadSHL01Buffer[readIndex * SH_STRIDE_L01 + 11] // shAb.z ); _ProbeVolumeAtlasWriteTextureSH[uint3(writeIndex.x, writeIndex.y, writeIndex.z + _ProbeVolumeAtlasResolutionAndSliceCount.z * 3)] = float4( - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 12 + 0], // shBr.x - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 12 + 1], // shBr.y - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 12 + 2], // shBr.z - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 12 + 3] // shBr.w + _ProbeVolumeAtlasReadSHL2Buffer[readIndex * SH_STRIDE_L2 + 0], // shBr.x + _ProbeVolumeAtlasReadSHL2Buffer[readIndex * SH_STRIDE_L2 + 1], // shBr.y + _ProbeVolumeAtlasReadSHL2Buffer[readIndex * SH_STRIDE_L2 + 2], // shBr.z + _ProbeVolumeAtlasReadSHL2Buffer[readIndex * SH_STRIDE_L2 + 3] // shBr.w ); _ProbeVolumeAtlasWriteTextureSH[uint3(writeIndex.x, writeIndex.y, writeIndex.z + _ProbeVolumeAtlasResolutionAndSliceCount.z * 4)] = float4( - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 16 + 0], // shBg.x - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 16 + 1], // shBg.y - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 16 + 2], // shBg.z - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 16 + 3] // shBg.w + _ProbeVolumeAtlasReadSHL2Buffer[readIndex * SH_STRIDE_L2 + 4], // shBg.x + _ProbeVolumeAtlasReadSHL2Buffer[readIndex * SH_STRIDE_L2 + 5], // shBg.y + _ProbeVolumeAtlasReadSHL2Buffer[readIndex * SH_STRIDE_L2 + 6], // shBg.z + _ProbeVolumeAtlasReadSHL2Buffer[readIndex * SH_STRIDE_L2 + 7] // shBg.w ); _ProbeVolumeAtlasWriteTextureSH[uint3(writeIndex.x, writeIndex.y, writeIndex.z + _ProbeVolumeAtlasResolutionAndSliceCount.z * 5)] = float4( - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 20 + 0], // shBb.x - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 20 + 1], // shBb.y - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 20 + 2], // shBb.z - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 20 + 3] // shBb.w + _ProbeVolumeAtlasReadSHL2Buffer[readIndex * SH_STRIDE_L2 + 8], // shBb.x + _ProbeVolumeAtlasReadSHL2Buffer[readIndex * SH_STRIDE_L2 + 9], // shBb.y + _ProbeVolumeAtlasReadSHL2Buffer[readIndex * SH_STRIDE_L2 + 10], // shBb.z + _ProbeVolumeAtlasReadSHL2Buffer[readIndex * SH_STRIDE_L2 + 11] // shBb.w ); _ProbeVolumeAtlasWriteTextureSH[uint3(writeIndex.x, writeIndex.y, writeIndex.z + _ProbeVolumeAtlasResolutionAndSliceCount.z * 6)] = float4( - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 24 + 0], // shCr.x - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 24 + 1], // shCr.y - _ProbeVolumeAtlasReadBuffer[readIndex * SH_STRIDE + 24 + 2], // shCr.z + _ProbeVolumeAtlasReadSHL2Buffer[readIndex * SH_STRIDE_L2 + 12], // shCr.x + _ProbeVolumeAtlasReadSHL2Buffer[readIndex * SH_STRIDE_L2 + 13], // shCr.y + _ProbeVolumeAtlasReadSHL2Buffer[readIndex * SH_STRIDE_L2 + 14], // shCr.z validity // last channel in float4 is unused by SH2 terms, so we take this opportunity to pack validity into it. ); diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeAtlasOctahedralDepthBlit.compute b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeAtlasOctahedralDepthBlit.compute index 6396dc507af..605bcb0b6f8 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeAtlasOctahedralDepthBlit.compute +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeAtlasOctahedralDepthBlit.compute @@ -6,7 +6,6 @@ #pragma only_renderers d3d11 playstation xboxone vulkan metal switch -#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/SphericalHarmonics.cs.hlsl" #include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeLighting.cs.hlsl" StructuredBuffer _ProbeVolumeAtlasOctahedralDepthReadBuffer; diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeLighting.cs b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeLighting.cs index 368b0a8d8e7..c5ad4b6309c 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeLighting.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeLighting.cs @@ -90,7 +90,8 @@ public partial class HDRenderPipeline static int s_ProbeVolumeAtlasBlitKernel = -1; static int s_ProbeVolumeAtlasOctahedralDepthBlitKernel = -1; static int s_ProbeVolumeAtlasOctahedralDepthConvolveKernel = -1; - static ComputeBuffer s_ProbeVolumeAtlasBlitDataBuffer = null; + static ComputeBuffer s_ProbeVolumeAtlasBlitDataSHL01Buffer = null; + static ComputeBuffer s_ProbeVolumeAtlasBlitDataSHL2Buffer = null; static ComputeBuffer s_ProbeVolumeAtlasBlitDataValidityBuffer = null; static ComputeBuffer s_ProbeVolumeAtlasOctahedralDepthBuffer = null; static int s_ProbeVolumeAtlasResolution; @@ -230,7 +231,11 @@ internal void CreateProbeVolumeBuffers() 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_ProbeVolumeAtlasBlitDataBuffer = new ComputeBuffer(s_MaxProbeVolumeProbeCount * ProbeVolumePayload.GetSHStride(ShaderConfig.s_ProbeVolumesEncodingMode), Marshal.SizeOf(typeof(float))); + 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); @@ -279,7 +284,8 @@ internal void DestroyProbeVolumeBuffers() CoreUtils.SafeRelease(s_VisibleProbeVolumeDataBufferDefault); CoreUtils.SafeRelease(s_VisibleProbeVolumeBoundsBuffer); CoreUtils.SafeRelease(s_VisibleProbeVolumeDataBuffer); - CoreUtils.SafeRelease(s_ProbeVolumeAtlasBlitDataBuffer); + CoreUtils.SafeRelease(s_ProbeVolumeAtlasBlitDataSHL01Buffer); + CoreUtils.SafeRelease(s_ProbeVolumeAtlasBlitDataSHL2Buffer); CoreUtils.SafeRelease(s_ProbeVolumeAtlasBlitDataValidityBuffer); CoreUtils.SafeRelease(s_ProbeVolumeAtlasOctahedralDepthBuffer); @@ -476,16 +482,21 @@ internal bool EnsureProbeVolumeInAtlas(ScriptableRenderContext renderContext, Co { ProbeVolumePayload payload = volume.GetPayload(); - if (payload.dataSH == null || payload.dataSH.Length == 0 || !volume.IsAssetCompatible()) + if (ProbeVolumePayload.IsNull(ref payload) || !volume.IsAssetCompatible()) { ReleaseProbeVolumeFromAtlas(volume); return false; } - int sizeSHCoefficients = size * ProbeVolumePayload.GetSHStride(payload.encodingMode); + 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 + "."); + } - Debug.Assert(payload.dataSH.Length == sizeSHCoefficients, "ProbeVolume: The probe volume baked data and its resolution are out of sync! Volume data length is " + payload.dataSH.Length + ", but resolution * SH stride size is " + sizeSHCoefficients + "."); - 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."); @@ -522,10 +533,15 @@ internal bool EnsureProbeVolumeInAtlas(ScriptableRenderContext renderContext, Co 1.0f / (float)m_ProbeVolumeAtlasSHRTDepthSliceCount )); - s_ProbeVolumeAtlasBlitDataBuffer.SetData(payload.dataSH); + s_ProbeVolumeAtlasBlitDataSHL01Buffer.SetData(payload.dataSHL01); s_ProbeVolumeAtlasBlitDataValidityBuffer.SetData(payload.dataValidity); cmd.SetComputeIntParam(s_ProbeVolumeAtlasBlitCS, HDShaderIDs._ProbeVolumeAtlasReadBufferCount, size); - cmd.SetComputeBufferParam(s_ProbeVolumeAtlasBlitCS, s_ProbeVolumeAtlasBlitKernel, HDShaderIDs._ProbeVolumeAtlasReadBuffer, s_ProbeVolumeAtlasBlitDataBuffer); + 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); diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeShaderVariables.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeShaderVariables.hlsl index 55dd6fe473a..24ea41ebe80 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeShaderVariables.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeShaderVariables.hlsl @@ -1,7 +1,6 @@ #ifndef PROBE_VOLUME_SHADER_VARIABLES #define PROBE_VOLUME_SHADER_VARIABLES -#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/SphericalHarmonics.cs.hlsl" #include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeLighting.cs.hlsl" #if SHADEROPTIONS_PROBE_VOLUMES_EVALUATION_MODE == PROBEVOLUMESEVALUATIONMODES_MATERIAL_PASS diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/SphericalHarmonics.cs b/com.unity.render-pipelines.high-definition/Runtime/Lighting/SphericalHarmonics.cs index 3772c569b30..bcd796eb711 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/SphericalHarmonics.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/SphericalHarmonics.cs @@ -31,61 +31,6 @@ public static void GetCornetteShanksPhaseFunction(ZonalHarmonicsL2 zh, float ani } [Serializable] - [GenerateHLSL] - internal struct SphericalHarmonicsL0 - { - public Vector3 shrgb; - - public static readonly SphericalHarmonicsL0 zero = new SphericalHarmonicsL0 - { - shrgb = Vector3.zero - }; - - // These operators are implemented so that SphericalHarmonicsL0 matches API of SphericalHarmonicsL2. - public static SphericalHarmonicsL0 operator +(SphericalHarmonicsL0 lhs, SphericalHarmonicsL0 rhs) => new SphericalHarmonicsL0() - { - shrgb = lhs.shrgb + rhs.shrgb - }; - - public static SphericalHarmonicsL0 operator -(SphericalHarmonicsL0 lhs, SphericalHarmonicsL0 rhs) => new SphericalHarmonicsL0() - { - shrgb = lhs.shrgb - rhs.shrgb - }; - - public static SphericalHarmonicsL0 operator *(SphericalHarmonicsL0 lhs, float rhs) => new SphericalHarmonicsL0() - { - shrgb = lhs.shrgb * rhs - }; - - public static SphericalHarmonicsL0 operator /(SphericalHarmonicsL0 lhs, float rhs) => new SphericalHarmonicsL0() - { - shrgb = lhs.shrgb / rhs - }; - - public static bool operator ==(SphericalHarmonicsL0 lhs, SphericalHarmonicsL0 rhs) - { - return lhs.shrgb == rhs.shrgb; - } - - public static bool operator !=(SphericalHarmonicsL0 lhs, SphericalHarmonicsL0 rhs) - { - return !(lhs == rhs); - } - - public override bool Equals(object other) - { - if (!(other is SphericalHarmonicsL0)) return false; - return this == (SphericalHarmonicsL0) other; - } - - public override int GetHashCode() - { - return 17 * 23 + shrgb.GetHashCode(); - } - } - - [Serializable] - [GenerateHLSL] internal struct SphericalHarmonicsL1 { public Vector4 shAr; diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/SphericalHarmonics.cs.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Lighting/SphericalHarmonics.cs.hlsl deleted file mode 100644 index 21449991a3b..00000000000 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/SphericalHarmonics.cs.hlsl +++ /dev/null @@ -1,22 +0,0 @@ -// -// This file was automatically generated. Please don't edit by hand. -// - -#ifndef SPHERICALHARMONICS_CS_HLSL -#define SPHERICALHARMONICS_CS_HLSL -// Generated from UnityEngine.Rendering.HighDefinition.SphericalHarmonicsL0 -// PackingRules = Exact -struct SphericalHarmonicsL0 -{ - float3 shrgb; -}; - -// -// Accessors for UnityEngine.Rendering.HighDefinition.SphericalHarmonicsL0 -// -float3 GetShrgb(SphericalHarmonicsL0 value) -{ - return value.shrgb; -} - -#endif diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/SphericalHarmonics.cs.hlsl.meta b/com.unity.render-pipelines.high-definition/Runtime/Lighting/SphericalHarmonics.cs.hlsl.meta deleted file mode 100644 index 3e6fd49363e..00000000000 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/SphericalHarmonics.cs.hlsl.meta +++ /dev/null @@ -1,9 +0,0 @@ -fileFormatVersion: 2 -guid: 2dd87cf254bbc1e4ab12e29178b187c7 -ShaderImporter: - externalObjects: {} - defaultTextures: [] - nonModifiableTextures: [] - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDStringConstants.cs b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDStringConstants.cs index a7a11ebbf88..ece2ca0c8ed 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDStringConstants.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDStringConstants.cs @@ -831,7 +831,8 @@ static class HDShaderIDs public static readonly int _ProbeVolumeAtlasScale = Shader.PropertyToID("_ProbeVolumeAtlasScale"); public static readonly int _ProbeVolumeAtlasBias = Shader.PropertyToID("_ProbeVolumeAtlasBias"); public static readonly int _ProbeVolumeAtlasReadBufferCount = Shader.PropertyToID("_ProbeVolumeAtlasReadBufferCount"); - public static readonly int _ProbeVolumeAtlasReadBuffer = Shader.PropertyToID("_ProbeVolumeAtlasReadBuffer"); + public static readonly int _ProbeVolumeAtlasReadSHL01Buffer = Shader.PropertyToID("_ProbeVolumeAtlasReadSHL01Buffer"); + public static readonly int _ProbeVolumeAtlasReadSHL2Buffer = Shader.PropertyToID("_ProbeVolumeAtlasReadSHL2Buffer"); public static readonly int _ProbeVolumeAtlasReadValidityBuffer = Shader.PropertyToID("_ProbeVolumeAtlasReadValidityBuffer"); public static readonly int _ProbeVolumeAtlasWriteTextureSH = Shader.PropertyToID("_ProbeVolumeAtlasWriteTextureSH"); public static readonly int _ProbeVolumeAtlasOctahedralDepthScaleBias = Shader.PropertyToID("_ProbeVolumeAtlasOctahedralDepthScaleBias"); From 03e1ea6af60c5d38815f9fe73b911340bc9620cc Mon Sep 17 00:00:00 2001 From: pastasfuture Date: Mon, 8 Jun 2020 19:43:48 -0700 Subject: [PATCH 21/26] Probe Volumes: Wrap supporting accumulation, evaluation, and fallback function calls into ProbeVolumeEvaluateSphericalHarmonics() function to hide static branches around SH order from LightLoop and SampleBakedGI. --- .../Runtime/Lighting/LightLoop/LightLoop.hlsl | 42 ++++-------------- .../Lighting/ProbeVolume/ProbeVolume.hlsl | 44 ++++++++++++++++--- .../Runtime/Material/BuiltinGIUtilities.hlsl | 29 ++++-------- .../Runtime/VFXGraph/Shaders/VFXLit.hlsl | 6 ++- 4 files changed, 61 insertions(+), 60 deletions(-) diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.hlsl index 71a952ee846..becceb1e9a6 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.hlsl @@ -2,16 +2,6 @@ #if SHADEROPTIONS_PROBE_VOLUMES_EVALUATION_MODE == PROBEVOLUMESEVALUATIONMODES_LIGHT_LOOP #include "Packages/com.unity.render-pipelines.high-definition/Runtime/Material/BuiltinUtilities.hlsl" - -#ifndef PROBE_VOLUMES_SAMPLING_MODE -// Default to sampling probe volumes at native atlas encoding mode. -// Users can override this by defining PROBE_VOLUMES_SAMPLING_MODE before including LightLoop.hlsl -// TODO: It's likely we will want to extend this out to simply be shader LOD quality levels, -// as there are other parameters such as bilateral filtering, additive blending, and normal bias -// that we will want to disable for a low quality high performance mode. -#define PROBE_VOLUMES_SAMPLING_MODE SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE -#endif - #endif // We perform scalarization only for forward rendering as for deferred loads will already be scalar since tiles will match waves and therefore all threads will read from the same tile. @@ -502,29 +492,15 @@ void LightLoop( float3 V, PositionInputs posInput, PreLightData preLightData, BS float probeVolumeHierarchyWeight = uninitialized ? 0.0f : 1.0f; // Note: we aren't suppose to access normalWS in lightloop, but bsdfData.normalWS is always define for any material. So this is safe. -#if PROBE_VOLUMES_SAMPLING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L0 - ProbeVolumeSphericalHarmonicsL0 coefficients; - ProbeVolumeAccumulateSphericalHarmonicsL0(posInput, bsdfData.normalWS, builtinData.renderingLayers, coefficients, probeVolumeHierarchyWeight); - builtinDataProbeVolumes.bakeDiffuseLighting += EvaluateProbeVolumeSphericalHarmonicsL0(bsdfData.normalWS, coefficients); - builtinDataProbeVolumes.backBakeDiffuseLighting += EvaluateProbeVolumeSphericalHarmonicsL0(-bsdfData.normalWS, coefficients); -#elif PROBE_VOLUMES_SAMPLING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L1 - ProbeVolumeSphericalHarmonicsL1 coefficients; - ProbeVolumeAccumulateSphericalHarmonicsL1(posInput, bsdfData.normalWS, builtinData.renderingLayers, coefficients, probeVolumeHierarchyWeight); - builtinDataProbeVolumes.bakeDiffuseLighting += EvaluateProbeVolumeSphericalHarmonicsL1(bsdfData.normalWS, coefficients); - builtinDataProbeVolumes.backBakeDiffuseLighting += EvaluateProbeVolumeSphericalHarmonicsL1(-bsdfData.normalWS, coefficients); -#elif PROBE_VOLUMES_SAMPLING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L2 - ProbeVolumeSphericalHarmonicsL2 coefficients; - ProbeVolumeAccumulateSphericalHarmonicsL2(posInput, bsdfData.normalWS, builtinData.renderingLayers, coefficients, probeVolumeHierarchyWeight); - builtinDataProbeVolumes.bakeDiffuseLighting += EvaluateProbeVolumeSphericalHarmonicsL2(bsdfData.normalWS, coefficients); - builtinDataProbeVolumes.backBakeDiffuseLighting += EvaluateProbeVolumeSphericalHarmonicsL2(-bsdfData.normalWS, coefficients); -#endif - - float probeVolumeHierarchyWeightFrontFace = probeVolumeHierarchyWeight; - float probeVolumeHierarchyWeightBackFace = probeVolumeHierarchyWeight; - builtinDataProbeVolumes.bakeDiffuseLighting += EvaluateProbeVolumeAmbientProbeFallback(bsdfData.normalWS, probeVolumeHierarchyWeightFrontFace); - builtinDataProbeVolumes.backBakeDiffuseLighting += EvaluateProbeVolumeAmbientProbeFallback(-bsdfData.normalWS, probeVolumeHierarchyWeightBackFace); - - // TODO: clean this case later to share more code, for now just reproduce the same behavior that is happening in PostInitBuiltinData() + ProbeVolumeEvaluateSphericalHarmonics( + posInput, + bsdfData.normalWS, + -bsdfData.normalWS, + builtinData.renderingLayers, + probeVolumeHierarchyWeight, + builtinDataProbeVolumes.bakeDiffuseLighting, + builtinDataProbeVolumes.backBakeDiffuseLighting + ); // Apply control from the indirect lighting volume settings (Remember there is no emissive here at this step) builtinDataProbeVolumes.bakeDiffuseLighting *= _IndirectLightingMultiplier.x; diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl index 2b819e7d85c..84ef3a7902a 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl @@ -482,7 +482,7 @@ void ProbeVolumeSwizzleAndNormalizeSphericalHarmonicsL2(inout ProbeVolumeSpheric coefficients.data[5].z *= 3.0; } -float3 EvaluateProbeVolumeSphericalHarmonicsL0(float3 normalWS, ProbeVolumeSphericalHarmonicsL0 coefficients) +float3 ProbeVolumeEvaluateSphericalHarmonicsL0(float3 normalWS, ProbeVolumeSphericalHarmonicsL0 coefficients) { #ifdef DEBUG_DISPLAY @@ -504,7 +504,7 @@ float3 EvaluateProbeVolumeSphericalHarmonicsL0(float3 normalWS, ProbeVolumeSpher } } -float3 EvaluateProbeVolumeSphericalHarmonicsL1(float3 normalWS, ProbeVolumeSphericalHarmonicsL1 coefficients) +float3 ProbeVolumeEvaluateSphericalHarmonicsL1(float3 normalWS, ProbeVolumeSphericalHarmonicsL1 coefficients) { #ifdef DEBUG_DISPLAY @@ -526,7 +526,7 @@ float3 EvaluateProbeVolumeSphericalHarmonicsL1(float3 normalWS, ProbeVolumeSpher } } -float3 EvaluateProbeVolumeSphericalHarmonicsL2(float3 normalWS, ProbeVolumeSphericalHarmonicsL2 coefficients) +float3 ProbeVolumeEvaluateSphericalHarmonicsL2(float3 normalWS, ProbeVolumeSphericalHarmonicsL2 coefficients) { #ifdef DEBUG_DISPLAY @@ -549,7 +549,7 @@ float3 EvaluateProbeVolumeSphericalHarmonicsL2(float3 normalWS, ProbeVolumeSpher } // Fallback to global ambient probe lighting when probe volume lighting weight is not fully saturated. -float3 EvaluateProbeVolumeAmbientProbeFallback(float3 normalWS, inout float weightHierarchy) +float3 ProbeVolumeEvaluateAmbientProbeFallback(float3 normalWS, float weightHierarchy) { float3 sampleAmbientProbeOutgoingRadiance = float3(0.0, 0.0, 0.0); if (weightHierarchy < 1.0 @@ -560,7 +560,6 @@ float3 EvaluateProbeVolumeAmbientProbeFallback(float3 normalWS, inout float weig ) { sampleAmbientProbeOutgoingRadiance = SampleSH9(_ProbeVolumeAmbientProbeFallbackPackedCoeffs, normalWS) * (1.0 - weightHierarchy); - weightHierarchy = 1.0; } return sampleAmbientProbeOutgoingRadiance; @@ -581,4 +580,39 @@ float3 EvaluateProbeVolumeAmbientProbeFallback(float3 normalWS, inout float weig #include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolumeAccumulate.hlsl" #undef PROBE_VOLUMES_ACCUMULATE_MODE +#ifndef PROBE_VOLUMES_SAMPLING_MODE +// Default to sampling probe volumes at native atlas encoding mode. +// Users can override this by defining PROBE_VOLUMES_SAMPLING_MODE before including LightLoop.hlsl +// TODO: It's likely we will want to extend this out to simply be shader LOD quality levels, +// as there are other parameters such as bilateral filtering, additive blending, and normal bias +// that we will want to disable for a low quality high performance mode. +#define PROBE_VOLUMES_SAMPLING_MODE SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE +#endif + +void ProbeVolumeEvaluateSphericalHarmonics(PositionInputs posInput, float3 normalWS, float3 backNormalWS, uint renderingLayers, float weightHierarchy, inout float3 bakeDiffuseLighting, inout float3 backBakeDiffuseLighting) +{ +#if PROBE_VOLUMES_SAMPLING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L0 + ProbeVolumeSphericalHarmonicsL0 coefficients; + ProbeVolumeAccumulateSphericalHarmonicsL0(posInput, normalWS, renderingLayers, coefficients, weightHierarchy); + bakeDiffuseLighting += ProbeVolumeEvaluateSphericalHarmonicsL0(normalWS, coefficients); + backBakeDiffuseLighting += ProbeVolumeEvaluateSphericalHarmonicsL0(backNormalWS, coefficients); + +#elif PROBE_VOLUMES_SAMPLING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L1 + ProbeVolumeSphericalHarmonicsL1 coefficients; + ProbeVolumeAccumulateSphericalHarmonicsL1(posInput, normalWS, renderingLayers, coefficients, weightHierarchy); + bakeDiffuseLighting += ProbeVolumeEvaluateSphericalHarmonicsL1(normalWS, coefficients); + backBakeDiffuseLighting += ProbeVolumeEvaluateSphericalHarmonicsL1(backNormalWS, coefficients); + +#elif PROBE_VOLUMES_SAMPLING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L2 + ProbeVolumeSphericalHarmonicsL2 coefficients; + ProbeVolumeAccumulateSphericalHarmonicsL2(posInput, normalWS, renderingLayers, coefficients, weightHierarchy); + bakeDiffuseLighting += ProbeVolumeEvaluateSphericalHarmonicsL2(normalWS, coefficients); + backBakeDiffuseLighting += ProbeVolumeEvaluateSphericalHarmonicsL2(backNormalWS, coefficients); + +#endif + + bakeDiffuseLighting += ProbeVolumeEvaluateAmbientProbeFallback(normalWS, weightHierarchy); + backBakeDiffuseLighting += ProbeVolumeEvaluateAmbientProbeFallback(backNormalWS, weightHierarchy); +} + #endif // __PROBEVOLUME_HLSL__ diff --git a/com.unity.render-pipelines.high-definition/Runtime/Material/BuiltinGIUtilities.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Material/BuiltinGIUtilities.hlsl index 0ce6e6e9d7a..1bba1ad2a07 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Material/BuiltinGIUtilities.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/Material/BuiltinGIUtilities.hlsl @@ -148,26 +148,15 @@ void SampleBakedGI( posInputs.tileCoord = tileCoord; #endif -#if SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L0 - ProbeVolumeSphericalHarmonicsL0 coefficients; - ProbeVolumeAccumulateSphericalHarmonicsL0(posInputs, normalWS, renderingLayers, coefficients, probeVolumeHierarchyWeight); - bakeDiffuseLighting += EvaluateProbeVolumeSphericalHarmonicsL0(normalWS, coefficients); - backBakeDiffuseLighting += EvaluateProbeVolumeSphericalHarmonicsL2(backNormalWS, coefficients); -#elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L1 - ProbeVolumeSphericalHarmonicsL1 coefficients; - ProbeVolumeAccumulateSphericalHarmonicsL1(posInputs, normalWS, renderingLayers, coefficients, probeVolumeHierarchyWeight); - bakeDiffuseLighting += EvaluateProbeVolumeSphericalHarmonicsL1(normalWS, coefficients); - backBakeDiffuseLighting += EvaluateProbeVolumeSphericalHarmonicsL2(backNormalWS, coefficients); -#elif SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE == PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L2 - ProbeVolumeSphericalHarmonicsL2 coefficients; - ProbeVolumeAccumulateSphericalHarmonicsL2(posInputs, normalWS, renderingLayers, coefficients, probeVolumeHierarchyWeight); - bakeDiffuseLighting += EvaluateProbeVolumeSphericalHarmonicsL2(normalWS, coefficients); - backBakeDiffuseLighting += EvaluateProbeVolumeSphericalHarmonicsL2(backNormalWS, coefficients); -#endif - float backProbeVolumeHierarchyWeight = probeVolumeHierarchyWeight; - bakeDiffuseLighting += EvaluateProbeVolumeAmbientProbeFallback(normalWS, probeVolumeHierarchyWeight); - backBakeDiffuseLighting += EvaluateProbeVolumeAmbientProbeFallback(backNormalWS, backProbeVolumeHierarchyWeight); - + ProbeVolumeEvaluateSphericalHarmonics( + posInputs, + normalWS, + backNormalWS, + renderingLayers, + probeVolumeHierarchyWeight, + bakeDiffuseLighting, + backBakeDiffuseLighting + ); #endif #endif diff --git a/com.unity.render-pipelines.high-definition/Runtime/VFXGraph/Shaders/VFXLit.hlsl b/com.unity.render-pipelines.high-definition/Runtime/VFXGraph/Shaders/VFXLit.hlsl index a6ef2f67cc5..52c493d8436 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/VFXGraph/Shaders/VFXLit.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/VFXGraph/Shaders/VFXLit.hlsl @@ -8,6 +8,9 @@ #error SHADERPASS must be defined (at) this point #endif +// Make VFX only sample probe volumes as SH0 for performance. +#define PROBE_VOLUMES_SAMPLING_MODE PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L0 + #include "Packages/com.unity.render-pipelines.high-definition/Runtime/Material/Material.hlsl" #if (SHADERPASS == SHADERPASS_FORWARD) @@ -30,8 +33,7 @@ #else #include "Packages/com.unity.render-pipelines.high-definition/Runtime/Material/Lit/Lit.hlsl" #endif - // Make VFX only sample probe volumes as SH0 for performance. - #define PROBE_VOLUMES_SAMPLING_MODE PROBEVOLUMESENCODINGMODES_SPHERICAL_HARMONICS_L0 + #include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.hlsl" #else // (SHADERPASS == SHADERPASS_FORWARD) From cda553742b0ab62a40e915072f84da9dc4c72d55 Mon Sep 17 00:00:00 2001 From: sebastienlagarde Date: Tue, 9 Jun 2020 02:18:14 +0200 Subject: [PATCH 22/26] Probe Volumes: Update SampleBakedGI() API (#759) * Probe Volumes: Update SampleBakedGI() and supporting internal functions signature in order to evaluate lighting along front and back facing normals at the same time. * Add some comment Co-authored-by: pastasfuture --- .../Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl | 1 + 1 file changed, 1 insertion(+) diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl index 84ef3a7902a..ced15ec084a 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl @@ -559,6 +559,7 @@ float3 ProbeVolumeEvaluateAmbientProbeFallback(float3 normalWS, float weightHier #endif ) { + sampleAmbientProbeOutgoingRadiance = SampleSH9(_ProbeVolumeAmbientProbeFallbackPackedCoeffs, normalWS) * (1.0 - weightHierarchy); } From c685c00e060662051a89c26e29681a7e9c90693f Mon Sep 17 00:00:00 2001 From: pastasfuture Date: Tue, 9 Jun 2020 01:58:38 -0700 Subject: [PATCH 23/26] Probe Volumes: Fix DebugViewTiles shader after updates to ProbeVolumeGetCountAndStart() --- .../Runtime/Debug/DebugViewTiles.shader | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/com.unity.render-pipelines.high-definition/Runtime/Debug/DebugViewTiles.shader b/com.unity.render-pipelines.high-definition/Runtime/Debug/DebugViewTiles.shader index 50eb0437b7a..10ece7ce5c5 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Debug/DebugViewTiles.shader +++ b/com.unity.render-pipelines.high-definition/Runtime/Debug/DebugViewTiles.shader @@ -202,7 +202,7 @@ Shader "Hidden/HDRP/DebugViewTiles" // the count should be zero. uint start; uint count; - ProbeVolumeGetCountAndStart(posInput, category, start, count); + ProbeVolumeGetCountAndStart(posInput, start, count); n += count; #endif } @@ -254,7 +254,7 @@ Shader "Hidden/HDRP/DebugViewTiles" if (category == LIGHTCATEGORY_PROBE_VOLUME) { #if defined(USE_CLUSTERED_LIGHTLIST) - ProbeVolumeGetCountAndStart(mousePosInput, category, start, count); + ProbeVolumeGetCountAndStart(mousePosInput, start, count); n += count; #endif } From dd36bbf5ae1c2b03fe56e72616b365ce8478e0e2 Mon Sep 17 00:00:00 2001 From: pastasfuture Date: Tue, 9 Jun 2020 02:13:37 -0700 Subject: [PATCH 24/26] Probe Volumes: Set ShaderConfig evaluation mode to be disabled by default. --- .../Runtime/ShaderConfig.cs | 4 ++-- .../Runtime/ShaderConfig.cs.hlsl | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/com.unity.render-pipelines.high-definition-config/Runtime/ShaderConfig.cs b/com.unity.render-pipelines.high-definition-config/Runtime/ShaderConfig.cs index 029b554cd1d..21a5d052928 100644 --- a/com.unity.render-pipelines.high-definition-config/Runtime/ShaderConfig.cs +++ b/com.unity.render-pipelines.high-definition-config/Runtime/ShaderConfig.cs @@ -61,10 +61,10 @@ public enum ShaderOptions // and inside of the editor run: // Edit->Render Pipeline->Generate Shader Includes // Probe Volumes feature must also be enabled inside of your HDRenderPipelineAsset. - ProbeVolumesEvaluationMode = ProbeVolumesEvaluationModes.LightLoop, + ProbeVolumesEvaluationMode = ProbeVolumesEvaluationModes.Disabled, ProbeVolumesAdditiveBlending = 1, ProbeVolumesBilateralFilteringMode = ProbeVolumesBilateralFilteringModes.Validity, - ProbeVolumesEncodingMode = ProbeVolumesEncodingModes.SphericalHarmonicsL2, + ProbeVolumesEncodingMode = ProbeVolumesEncodingModes.SphericalHarmonicsL1, AreaLights = 1, diff --git a/com.unity.render-pipelines.high-definition-config/Runtime/ShaderConfig.cs.hlsl b/com.unity.render-pipelines.high-definition-config/Runtime/ShaderConfig.cs.hlsl index 5f8ad7046e5..445d13dda83 100644 --- a/com.unity.render-pipelines.high-definition-config/Runtime/ShaderConfig.cs.hlsl +++ b/com.unity.render-pipelines.high-definition-config/Runtime/ShaderConfig.cs.hlsl @@ -40,10 +40,10 @@ #define SHADEROPTIONS_PRECOMPUTED_ATMOSPHERIC_ATTENUATION (0) #define SHADEROPTIONS_RAYTRACING (0) #define SHADEROPTIONS_XR_MAX_VIEWS (2) -#define SHADEROPTIONS_PROBE_VOLUMES_EVALUATION_MODE (1) +#define SHADEROPTIONS_PROBE_VOLUMES_EVALUATION_MODE (0) #define SHADEROPTIONS_PROBE_VOLUMES_ADDITIVE_BLENDING (1) #define SHADEROPTIONS_PROBE_VOLUMES_BILATERAL_FILTERING_MODE (1) -#define SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE (2) +#define SHADEROPTIONS_PROBE_VOLUMES_ENCODING_MODE (1) #define SHADEROPTIONS_AREA_LIGHTS (1) #define SHADEROPTIONS_DEFERRED_SHADOW_FILTERING (1) #define SHADEROPTIONS_BARN_DOOR (0) From afb0e934a29593bf425b13a47866a2556f7aedb5 Mon Sep 17 00:00:00 2001 From: pastasfuture Date: Tue, 9 Jun 2020 13:35:48 -0700 Subject: [PATCH 25/26] Probe Volumes: Update EvaluateOctahedralDepth function name to match surrounding code convention. --- .../Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl index ced15ec084a..23943dfde83 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl @@ -46,7 +46,7 @@ float ProbeVolumeLoadValidity(int3 probeVolumeAtlasTexelCoord) } #if SHADEROPTIONS_PROBE_VOLUMES_BILATERAL_FILTERING == PROBEVOLUMESBILATERALFILTERINGMODES_OCTAHEDRAL_DEPTH -void EvaluateProbeVolumeOctahedralDepthOcclusionFilterWeights( +void ProbeVolumeEvaluateOctahedralDepthOcclusionFilterWeights( out float weights[8], float3 probeVolumeTexel3DMin, float3 probeVolumeResolution, @@ -267,7 +267,7 @@ float3 ProbeVolumeComputeTexel3DFromBilateralFilter( float3 probeVolumeWorldFromTexel3DTranslation = mul(obbFrame, -obbExtents) + obbCenter; float probeWeights[8]; - EvaluateProbeVolumeOctahedralDepthOcclusionFilterWeights( + ProbeVolumeEvaluateOctahedralDepthOcclusionFilterWeights( probeWeights, probeVolumeTexel3DMin, probeVolumeData.resolution, From 6b510b7aa3f0b602db5fbcd7d9659036d84dcc41 Mon Sep 17 00:00:00 2001 From: pastasfuture Date: Tue, 9 Jun 2020 19:53:27 -0700 Subject: [PATCH 26/26] Probe Volumes: Fix up ShaderVariablesGlobal after naming conflict from pulling in lastest. --- .../Runtime/ShaderLibrary/ShaderVariablesGlobal.cs | 2 +- .../Runtime/ShaderLibrary/ShaderVariablesGlobal.cs.hlsl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariablesGlobal.cs b/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariablesGlobal.cs index 70fe06dc515..05d74267db8 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariablesGlobal.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariablesGlobal.cs @@ -248,7 +248,7 @@ unsafe struct ShaderVariablesGlobal public int _ProbeVolumeLeakMitigationMode; public float _ProbeVolumeBilateralFilterWeightMin; public float _ProbeVolumeBilateralFilterWeight; - public float _Pad7; + public float _Pad8; [HLSLArray(7, typeof(Vector4))] public fixed float _ProbeVolumeAmbientProbeFallbackPackedCoeffs[7 * 4]; // 3 bands of SH, packed for storing global ambient probe lighting as fallback to probe volumes. diff --git a/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariablesGlobal.cs.hlsl b/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariablesGlobal.cs.hlsl index 5f7df77ad9c..bca98645a2e 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariablesGlobal.cs.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariablesGlobal.cs.hlsl @@ -140,7 +140,7 @@ GLOBAL_CBUFFER_START(ShaderVariablesGlobal, b0) int _ProbeVolumeLeakMitigationMode; float _ProbeVolumeBilateralFilterWeightMin; float _ProbeVolumeBilateralFilterWeight; - float _Pad7; + float _Pad8; float4 _ProbeVolumeAmbientProbeFallbackPackedCoeffs[7]; CBUFFER_END