From f84257b22640c96c200e9ad83e87c549fea78975 Mon Sep 17 00:00:00 2001 From: Emmanuel Turquin Date: Mon, 20 Sep 2021 19:02:56 +0200 Subject: [PATCH 1/6] Ported integration tests from experimental branch. --- .../PathTracing/Shaders/PathTracingLight.hlsl | 79 ++++++++++++++++++- .../Shaders/PathTracingMain.raytrace | 6 +- .../Shaders/PathTracingVolume.hlsl | 47 +++++++---- .../ShaderPass/ShaderPassPathTracing.hlsl | 5 +- 4 files changed, 113 insertions(+), 24 deletions(-) diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/PathTracing/Shaders/PathTracingLight.hlsl b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/PathTracing/Shaders/PathTracingLight.hlsl index 64009558e37..6e4a1a207a1 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/PathTracing/Shaders/PathTracingLight.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/PathTracing/Shaders/PathTracingLight.hlsl @@ -109,7 +109,7 @@ bool IsDistantLightActive(DirectionalLightData lightData, float3 normal) return dot(normal, lightData.forward) <= sin(lightData.angularDiameter * 0.5); } -LightList CreateLightList(float3 position, float3 normal, uint lightLayers = DEFAULT_LIGHT_LAYERS, bool withLocal = true, bool withDistant = true) +LightList CreateLightList(float3 position, float3 normal, uint lightLayers = DEFAULT_LIGHT_LAYERS, bool withLocal = true, bool withDistant = true, float3 lightPosition = FLT_MAX) { LightList list; uint i; @@ -148,6 +148,9 @@ if (withLocal) const LightData lightData = _LightDatasRT[i]; #endif + if (lightPosition.x != FLT_MAX && any(lightPosition - lightData.positionRWS)) + continue; + if (IsMatchingLightLayer(lightData.lightLayers, lightLayers) && IsPointLightActive(lightData, position, normal)) list.localIndex[list.localPointCount++] = i; } @@ -161,6 +164,9 @@ if (withLocal) const LightData lightData = _LightDatasRT[i]; #endif + if (lightPosition.x != FLT_MAX && any(lightPosition - lightData.positionRWS)) + continue; + if (IsMatchingLightLayer(lightData.lightLayers, lightLayers) && IsRectAreaLightActive(lightData, position, normal)) list.localIndex[list.localCount++] = i; } @@ -709,9 +715,76 @@ float GetLocalLightsInterval(float3 rayOrigin, float3 rayDirection, out float tM return lightCount ? float(localCount) / lightCount : -1.0; } -LightList CreateLightList(float3 position, bool sampleLocalLights) +float PickLocalLightInterval(float3 rayOrigin, float3 rayDirection, uint2 pixelCoord, out float3 lightPosition, out float wPick, out float tMin, out float tMax) +{ + tMin = FLT_MAX; + tMax = 0.0; + + float tLightMin, tLightMax; + float inputSample, wLight, wSum = 0.0; + + // First process point lights + uint i = 0, n = _PunctualLightCountRT, localCount = 0; + for (; i < n; i++) + { + if (GetPointLightInterval(_LightDatasRT[i], rayOrigin, rayDirection, tLightMin, tLightMax)) + { + wLight = 1.0; + wSum += wLight; + wLight /= wSum; + + inputSample = GetSample(pixelCoord, _RaytracingSampleIndex, 4 + i); + if (inputSample < wLight) + { + lightPosition = _LightDatasRT[i].positionRWS; + wPick = wLight; + tMin = tLightMin; + tMax = tLightMax; + } + else + { + wPick *= 1.0 - wLight; + } + + localCount++; + } + } + + // Then area lights + n += _AreaLightCountRT; + for (; i < n; i++) + { + if (GetRectAreaLightInterval(_LightDatasRT[i], rayOrigin, rayDirection, tLightMin, tLightMax)) + { + wLight = 1.0; + wSum += wLight; + wLight /= wSum; + + inputSample = GetSample(pixelCoord, _RaytracingSampleIndex, 4 + i); + if (inputSample < wLight) + { + lightPosition = _LightDatasRT[i].positionRWS; + wPick = wLight; + tMin = tLightMin; + tMax = tLightMax; + } + else + { + wPick *= 1.0 - wLight; + } + + localCount++; + } + } + + uint lightCount = localCount + _DirectionalLightCount; + + return lightCount ? float(localCount) / lightCount : -1.0; +} + +LightList CreateLightList(float3 position, bool sampleLocalLights, float3 lightPosition = FLT_MAX) { - return CreateLightList(position, 0.0, DEFAULT_LIGHT_LAYERS, sampleLocalLights, !sampleLocalLights); + return CreateLightList(position, 0.0, DEFAULT_LIGHT_LAYERS, sampleLocalLights, !sampleLocalLights, lightPosition); } #endif // UNITY_PATH_TRACING_LIGHT_INCLUDED diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/PathTracing/Shaders/PathTracingMain.raytrace b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/PathTracing/Shaders/PathTracingMain.raytrace index acc8fd1423e..a0d36b83934 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/PathTracing/Shaders/PathTracingMain.raytrace +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/PathTracing/Shaders/PathTracingMain.raytrace @@ -46,7 +46,7 @@ void MissCamera(inout PathIntersection pathIntersection : SV_RayPayload) if (_EnableVolumetricFog && _RaytracingMinRecursion <= 1) { - float3 envValue = pathIntersection.value; + float3 lightPosition, envValue = pathIntersection.value; // Generate a 4D unit-square sample for this depth, from our QMC sequence float4 inputSample = GetSample4D(pathIntersection.pixelCoord, _RaytracingSampleIndex, 0); @@ -56,9 +56,9 @@ void MissCamera(inout PathIntersection pathIntersection : SV_RayPayload) pathIntersection.t = FLT_MAX; float pdf = 1.0; bool sampleLocalLights; - if (SampleVolumeScatteringPosition(inputSample.w, pathIntersection.t, pdf, sampleLocalLights)) + if (SampleVolumeScatteringPosition(pathIntersection.pixelCoord, inputSample.w, pathIntersection.t, pdf, sampleLocalLights, lightPosition)) { - ComputeVolumeScattering(pathIntersection, inputSample.xyz, sampleLocalLights); + ComputeVolumeScattering(pathIntersection, inputSample.xyz, sampleLocalLights, lightPosition); // Apply the pdf pathIntersection.value /= pdf; diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/PathTracing/Shaders/PathTracingVolume.hlsl b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/PathTracing/Shaders/PathTracingVolume.hlsl index cba0bea2484..527119ccdb6 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/PathTracing/Shaders/PathTracingVolume.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/PathTracing/Shaders/PathTracingVolume.hlsl @@ -10,7 +10,7 @@ float ComputeHeightFogMultiplier(float height) return ComputeHeightFogMultiplier(height, _HeightFogBaseHeight, _HeightFogExponents); } -bool SampleVolumeScatteringPosition(inout float theSample, inout float t, inout float pdf, out bool sampleLocalLights) +bool SampleVolumeScatteringPosition(uint2 pixelCoord, inout float inputSample, inout float t, inout float pdf, out bool sampleLocalLights, out float3 lightPosition) { sampleLocalLights = false; @@ -23,28 +23,31 @@ bool SampleVolumeScatteringPosition(inout float theSample, inout float t, inout float tFog = min(t, _MaxFogDistance); #ifdef HAS_LIGHTLOOP - float localWeight = GetLocalLightsInterval(WorldRayOrigin(), WorldRayDirection(), tMin, tMax); + //float localWeight = GetLocalLightsInterval(WorldRayOrigin(), WorldRayDirection(), tMin, tMax); + + float lightWeight; + float localWeight = PickLocalLightInterval(WorldRayOrigin(), WorldRayDirection(), pixelCoord, lightPosition, lightWeight, tMin, tMax); if (localWeight < 0.0) return false; - sampleLocalLights = theSample < localWeight; + sampleLocalLights = inputSample < localWeight; if (sampleLocalLights) { tMax = min(tMax, tFog); if (tMin >= tMax) return false; - theSample /= localWeight; - pdfVol *= localWeight; + inputSample /= localWeight; + pdfVol *= localWeight * lightWeight; } else { tMin = 0.0; tMax = tFog; - theSample -= localWeight; - theSample /= 1.0 - localWeight; + inputSample -= localWeight; + inputSample /= 1.0 - localWeight; pdfVol *= 1.0 - localWeight; } #else @@ -57,11 +60,11 @@ bool SampleVolumeScatteringPosition(inout float theSample, inout float t, inout const float transmittanceTMax = max(exp(-tMax * sigmaT), 0.01); const float transmittanceThreshold = t < FLT_MAX ? 1.0 - min(0.5, transmittanceTMax) : 1.0; - if (theSample >= transmittanceThreshold) + if (inputSample >= transmittanceThreshold) { // Re-scale the sample - theSample -= transmittanceThreshold; - theSample /= 1.0 - transmittanceThreshold; + inputSample -= transmittanceThreshold; + inputSample /= 1.0 - transmittanceThreshold; // Adjust the pdf pdf *= 1.0 - transmittanceThreshold; @@ -70,16 +73,28 @@ bool SampleVolumeScatteringPosition(inout float theSample, inout float t, inout } // Re-scale the sample - theSample /= transmittanceThreshold; + inputSample /= transmittanceThreshold; // Adjust the pdf pdf *= pdfVol * transmittanceThreshold; if (sampleLocalLights) { + // const float transmittanceTMin = max(exp(-tMin * sigmaT), 0.01); + + // // Exponential sampling + // float transmittance = transmittanceTMax + inputSample * (transmittanceTMin - transmittanceTMax); + // t = -log(transmittance) / sigmaT; + + // // Adjust the pdf + // pdf *= sigmaT * transmittance / (transmittanceTMin - transmittanceTMax); + + + + // Linear sampling float deltaT = tMax - tMin; - t = tMin + theSample * deltaT; + t = tMin + inputSample * deltaT; // Adjust the pdf pdf /= deltaT; @@ -87,18 +102,18 @@ bool SampleVolumeScatteringPosition(inout float theSample, inout float t, inout else { // Exponential sampling - float transmittance = transmittanceTMax + theSample * (1.0 - transmittanceTMax); + float transmittance = transmittanceTMax + inputSample * (1.0 - transmittanceTMax); t = -log(transmittance) / sigmaT; // Adjust the pdf - pdf *= sigmaT * transmittance; + pdf *= sigmaT * transmittance / (1.0 - transmittanceTMax); } return true; } // Function responsible for volume scattering -void ComputeVolumeScattering(inout PathIntersection pathIntersection : SV_RayPayload, float3 inputSample, bool sampleLocalLights) +void ComputeVolumeScattering(inout PathIntersection pathIntersection : SV_RayPayload, float3 inputSample, bool sampleLocalLights, float3 lightPosition) { // Reset the ray intersection color, which will store our final result pathIntersection.value = 0.0; @@ -115,7 +130,7 @@ void ComputeVolumeScattering(inout PathIntersection pathIntersection : SV_RayPay float3 scatteringPosition = WorldRayOrigin() + pathIntersection.t * WorldRayDirection(); // Create the list of active lights - LightList lightList = CreateLightList(scatteringPosition, sampleLocalLights); + LightList lightList = CreateLightList(scatteringPosition, sampleLocalLights, lightPosition); // Bunch of variables common to material and light sampling float pdf; diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/ShaderPass/ShaderPassPathTracing.hlsl b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/ShaderPass/ShaderPassPathTracing.hlsl index e6cfe902429..8504ae40bfb 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/ShaderPass/ShaderPassPathTracing.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/ShaderPass/ShaderPassPathTracing.hlsl @@ -294,6 +294,7 @@ void ClosestHit(inout PathIntersection pathIntersection : SV_RayPayload, Attribu #ifdef HAS_LIGHTLOOP + float3 lightPosition; float pdf = 1.0; bool sampleLocalLights, sampleVolume = false; @@ -304,11 +305,11 @@ void ClosestHit(inout PathIntersection pathIntersection : SV_RayPayload, Attribu // For the time being, we test for volumetric scattering only on camera rays if (!currentDepth) - sampleVolume = SampleVolumeScatteringPosition(inputSample.w, pathIntersection.t, pdf, sampleLocalLights); + sampleVolume = SampleVolumeScatteringPosition(pathIntersection.pixelCoord, inputSample.w, pathIntersection.t, pdf, sampleLocalLights, lightPosition); } if (sampleVolume) - ComputeVolumeScattering(pathIntersection, inputSample.xyz, sampleLocalLights); + ComputeVolumeScattering(pathIntersection, inputSample.xyz, sampleLocalLights, lightPosition); else ComputeSurfaceScattering(pathIntersection, attributeData, inputSample); From 8872e652816ae29a72c830157284866b115e8045 Mon Sep 17 00:00:00 2001 From: Emmanuel Turquin Date: Tue, 21 Sep 2021 15:15:43 +0200 Subject: [PATCH 2/6] Further refactor. --- .../PathTracing/Shaders/PathTracingLight.hlsl | 228 ++++++++++-------- .../Shaders/PathTracingVolume.hlsl | 40 +-- 2 files changed, 137 insertions(+), 131 deletions(-) diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/PathTracing/Shaders/PathTracingLight.hlsl b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/PathTracing/Shaders/PathTracingLight.hlsl index 6e4a1a207a1..faa8e3e75bf 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/PathTracing/Shaders/PathTracingLight.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/PathTracing/Shaders/PathTracingLight.hlsl @@ -118,59 +118,62 @@ LightList CreateLightList(float3 position, float3 normal, uint lightLayers = DEF list.localCount = 0; list.localPointCount = 0; -if (withLocal) -{ - uint localPointCount, localCount; + if (withLocal) + { + uint localPointCount, localCount; #ifdef USE_LIGHT_CLUSTER - if (PointInsideCluster(position)) - { - list.cellIndex = GetClusterCellIndex(position); - localPointCount = GetPunctualLightClusterCellCount(list.cellIndex); - localCount = GetAreaLightClusterCellCount(list.cellIndex); - } - else - { - localPointCount = 0; - localCount = 0; - } + if (PointInsideCluster(position)) + { + list.cellIndex = GetClusterCellIndex(position); + localPointCount = GetPunctualLightClusterCellCount(list.cellIndex); + localCount = GetAreaLightClusterCellCount(list.cellIndex); + } + else + { + localPointCount = 0; + localCount = 0; + } #else - localPointCount = _PunctualLightCountRT; - localCount = _PunctualLightCountRT + _AreaLightCountRT; + localPointCount = _PunctualLightCountRT; + localCount = _PunctualLightCountRT + _AreaLightCountRT; #endif - // First point lights (including spot lights) - for (i = 0; i < localPointCount && list.localPointCount < MAX_LOCAL_LIGHT_COUNT; i++) - { + // Do we have an imposed local light (identificed by position), for volumetric scattering? + bool forceLightPosition = (lightPosition.x != FLT_MAX); + + // First point lights (including spot lights) + for (i = 0; i < localPointCount && list.localPointCount < MAX_LOCAL_LIGHT_COUNT; i++) + { #ifdef USE_LIGHT_CLUSTER - const LightData lightData = FetchClusterLightIndex(list.cellIndex, i); + const LightData lightData = FetchClusterLightIndex(list.cellIndex, i); #else - const LightData lightData = _LightDatasRT[i]; + const LightData lightData = _LightDatasRT[i]; #endif - if (lightPosition.x != FLT_MAX && any(lightPosition - lightData.positionRWS)) - continue; + if (forceLightPosition && any(lightPosition - lightData.positionRWS)) + continue; - if (IsMatchingLightLayer(lightData.lightLayers, lightLayers) && IsPointLightActive(lightData, position, normal)) - list.localIndex[list.localPointCount++] = i; - } + if (IsMatchingLightLayer(lightData.lightLayers, lightLayers) && IsPointLightActive(lightData, position, normal)) + list.localIndex[list.localPointCount++] = i; + } - // Then rect area lights - for (list.localCount = list.localPointCount; i < localCount && list.localCount < MAX_LOCAL_LIGHT_COUNT; i++) - { + // Then rect area lights + for (list.localCount = list.localPointCount; i < localCount && list.localCount < MAX_LOCAL_LIGHT_COUNT; i++) + { #ifdef USE_LIGHT_CLUSTER - const LightData lightData = FetchClusterLightIndex(list.cellIndex, i); + const LightData lightData = FetchClusterLightIndex(list.cellIndex, i); #else - const LightData lightData = _LightDatasRT[i]; + const LightData lightData = _LightDatasRT[i]; #endif - if (lightPosition.x != FLT_MAX && any(lightPosition - lightData.positionRWS)) - continue; + if (forceLightPosition && any(lightPosition - lightData.positionRWS)) + continue; - if (IsMatchingLightLayer(lightData.lightLayers, lightLayers) && IsRectAreaLightActive(lightData, position, normal)) - list.localIndex[list.localCount++] = i; + if (IsMatchingLightLayer(lightData.lightLayers, lightLayers) && IsRectAreaLightActive(lightData, position, normal)) + list.localIndex[list.localCount++] = i; + } } -} // Then filter the active distant lights (directional) list.distantCount = 0; @@ -679,49 +682,62 @@ bool GetPointLightInterval(LightData lightData, float3 rayOrigin, float3 rayDire return tMin < tMax; } -float GetLocalLightsInterval(float3 rayOrigin, float3 rayDirection, out float tMin, out float tMax) +// This function has been deprecated in favor of PickLocalLightInterval() right below +// float GetLocalLightsInterval(float3 rayOrigin, float3 rayDirection, out float tMin, out float tMax) +// { +// tMin = FLT_MAX; +// tMax = 0.0; + +// float tLightMin, tLightMax; + +// // First process point lights +// uint i = 0, n = _PunctualLightCountRT, localCount = 0; +// for (; i < n; i++) +// { +// if (GetPointLightInterval(_LightDatasRT[i], rayOrigin, rayDirection, tLightMin, tLightMax)) +// { +// tMin = min(tMin, tLightMin); +// tMax = max(tMax, tLightMax); +// localCount++; +// } +// } + +// // Then area lights +// n += _AreaLightCountRT; +// for (; i < n; i++) +// { +// if (GetRectAreaLightInterval(_LightDatasRT[i], rayOrigin, rayDirection, tLightMin, tLightMax)) +// { +// tMin = min(tMin, tLightMin); +// tMax = max(tMax, tLightMax); +// localCount++; +// } +// } + +// uint lightCount = localCount + _DirectionalLightCount; + +// return lightCount ? float(localCount) / lightCount : -1.0; +// } + +float GetLocalLightWeight(LightData lightData, float3 rayOrigin, float3 rayDirection, float tMin, float tMax) { - tMin = FLT_MAX; - tMax = 0.0; + float tDist = clamp(dot(lightData.positionRWS - rayOrigin, rayDirection), tMin, tMax); + float3 vDist = rayOrigin + tDist * rayDirection - lightData.positionRWS; - float tLightMin, tLightMax; + // By offsetting the square distance by 1.0, we reduce the range of the weight to ]0.0, 1.0], + // while avoiding a singularity when distance goes towards 0.0. + float distSq = 1.0 + Length2(vDist); - // First process point lights - uint i = 0, n = _PunctualLightCountRT, localCount = 0; - for (; i < n; i++) - { - if (GetPointLightInterval(_LightDatasRT[i], rayOrigin, rayDirection, tLightMin, tLightMax)) - { - tMin = min(tMin, tLightMin); - tMax = max(tMax, tLightMax); - localCount++; - } - } - - // Then area lights - n += _AreaLightCountRT; - for (; i < n; i++) - { - if (GetRectAreaLightInterval(_LightDatasRT[i], rayOrigin, rayDirection, tLightMin, tLightMax)) - { - tMin = min(tMin, tLightMin); - tMax = max(tMax, tLightMax); - localCount++; - } - } - - uint lightCount = localCount + _DirectionalLightCount; - - return lightCount ? float(localCount) / lightCount : -1.0; + return rcp(distSq); } -float PickLocalLightInterval(float3 rayOrigin, float3 rayDirection, uint2 pixelCoord, out float3 lightPosition, out float wPick, out float tMin, out float tMax) +float PickLocalLightInterval(float3 rayOrigin, float3 rayDirection, inout float inputSample, out float3 lightPosition, out float lightWeight, out float tMin, out float tMax) { tMin = FLT_MAX; tMax = 0.0; float tLightMin, tLightMax; - float inputSample, wLight, wSum = 0.0; + float wLight, wSum = 0.0; // First process point lights uint i = 0, n = _PunctualLightCountRT, localCount = 0; @@ -729,24 +745,31 @@ float PickLocalLightInterval(float3 rayOrigin, float3 rayDirection, uint2 pixelC { if (GetPointLightInterval(_LightDatasRT[i], rayOrigin, rayDirection, tLightMin, tLightMax)) { - wLight = 1.0; - wSum += wLight; - wLight /= wSum; + wLight = GetLocalLightWeight(_LightDatasRT[i], rayOrigin, rayDirection, tLightMin, tLightMax); - inputSample = GetSample(pixelCoord, _RaytracingSampleIndex, 4 + i); - if (inputSample < wLight) - { - lightPosition = _LightDatasRT[i].positionRWS; - wPick = wLight; - tMin = tLightMin; - tMax = tLightMax; - } - else + if (wLight > 0.0) { - wPick *= 1.0 - wLight; - } + wSum += wLight; + wLight /= wSum; + + if (inputSample < wLight) + { + lightPosition = _LightDatasRT[i].positionRWS; + lightWeight = wLight; + tMin = tLightMin; + tMax = tLightMax; + + inputSample /= wLight; + } + else + { + lightWeight *= 1.0 - wLight; - localCount++; + inputSample = (inputSample - wLight) / (1.0 - wLight); + } + + localCount++; + } } } @@ -756,24 +779,31 @@ float PickLocalLightInterval(float3 rayOrigin, float3 rayDirection, uint2 pixelC { if (GetRectAreaLightInterval(_LightDatasRT[i], rayOrigin, rayDirection, tLightMin, tLightMax)) { - wLight = 1.0; - wSum += wLight; - wLight /= wSum; + wLight = GetLocalLightWeight(_LightDatasRT[i], rayOrigin, rayDirection, tLightMin, tLightMax); - inputSample = GetSample(pixelCoord, _RaytracingSampleIndex, 4 + i); - if (inputSample < wLight) + if (wLight > 0.0) { - lightPosition = _LightDatasRT[i].positionRWS; - wPick = wLight; - tMin = tLightMin; - tMax = tLightMax; - } - else - { - wPick *= 1.0 - wLight; - } + wSum += wLight; + wLight /= wSum; - localCount++; + if (inputSample < wLight) + { + lightPosition = _LightDatasRT[i].positionRWS; + lightWeight = wLight; + tMin = tLightMin; + tMax = tLightMax; + + inputSample /= wLight; + } + else + { + lightWeight *= 1.0 - wLight; + + inputSample = (inputSample - wLight) / (1.0 - wLight); + } + + localCount++; + } } } diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/PathTracing/Shaders/PathTracingVolume.hlsl b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/PathTracing/Shaders/PathTracingVolume.hlsl index 527119ccdb6..6d1b727d6c9 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/PathTracing/Shaders/PathTracingVolume.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/PathTracing/Shaders/PathTracingVolume.hlsl @@ -23,10 +23,9 @@ bool SampleVolumeScatteringPosition(uint2 pixelCoord, inout float inputSample, i float tFog = min(t, _MaxFogDistance); #ifdef HAS_LIGHTLOOP - //float localWeight = GetLocalLightsInterval(WorldRayOrigin(), WorldRayDirection(), tMin, tMax); float lightWeight; - float localWeight = PickLocalLightInterval(WorldRayOrigin(), WorldRayDirection(), pixelCoord, lightPosition, lightWeight, tMin, tMax); + float localWeight = PickLocalLightInterval(WorldRayOrigin(), WorldRayDirection(), inputSample, lightPosition, lightWeight, tMin, tMax); if (localWeight < 0.0) return false; @@ -57,6 +56,7 @@ bool SampleVolumeScatteringPosition(uint2 pixelCoord, inout float inputSample, i // FIXME: not quite sure what the sigmaT value is supposed to be... const float sigmaT = _HeightFogBaseExtinction; + const float transmittanceTMin = max(exp(-tMin * sigmaT), 0.01); const float transmittanceTMax = max(exp(-tMax * sigmaT), 0.01); const float transmittanceThreshold = t < FLT_MAX ? 1.0 - min(0.5, transmittanceTMax) : 1.0; @@ -78,36 +78,12 @@ bool SampleVolumeScatteringPosition(uint2 pixelCoord, inout float inputSample, i // Adjust the pdf pdf *= pdfVol * transmittanceThreshold; - if (sampleLocalLights) - { - // const float transmittanceTMin = max(exp(-tMin * sigmaT), 0.01); - - // // Exponential sampling - // float transmittance = transmittanceTMax + inputSample * (transmittanceTMin - transmittanceTMax); - // t = -log(transmittance) / sigmaT; - - // // Adjust the pdf - // pdf *= sigmaT * transmittance / (transmittanceTMin - transmittanceTMax); - - - - - // Linear sampling - float deltaT = tMax - tMin; - t = tMin + inputSample * deltaT; + // Exponential sampling + float transmittance = transmittanceTMax + inputSample * (transmittanceTMin - transmittanceTMax); + t = -log(transmittance) / sigmaT; - // Adjust the pdf - pdf /= deltaT; - } - else - { - // Exponential sampling - float transmittance = transmittanceTMax + inputSample * (1.0 - transmittanceTMax); - t = -log(transmittance) / sigmaT; - - // Adjust the pdf - pdf *= sigmaT * transmittance / (1.0 - transmittanceTMax); - } + // Adjust the pdf + pdf *= sigmaT * transmittance / (transmittanceTMin - transmittanceTMax); return true; } @@ -129,7 +105,7 @@ void ComputeVolumeScattering(inout PathIntersection pathIntersection : SV_RayPay // Compute the scattering position float3 scatteringPosition = WorldRayOrigin() + pathIntersection.t * WorldRayDirection(); - // Create the list of active lights + // Create the list of active lights (a local light can be forced by providing its position) LightList lightList = CreateLightList(scatteringPosition, sampleLocalLights, lightPosition); // Bunch of variables common to material and light sampling From 2fac0b93c145f8a9a806669cb763cb4ee09b4b69 Mon Sep 17 00:00:00 2001 From: Emmanuel Turquin Date: Tue, 21 Sep 2021 18:27:44 +0200 Subject: [PATCH 3/6] Updated changelog. --- com.unity.render-pipelines.high-definition/CHANGELOG.md | 2 ++ .../PathTracing/Shaders/PathTracingVolume.hlsl | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/com.unity.render-pipelines.high-definition/CHANGELOG.md b/com.unity.render-pipelines.high-definition/CHANGELOG.md index 7ac5a5f3d72..677ee10a6e0 100644 --- a/com.unity.render-pipelines.high-definition/CHANGELOG.md +++ b/com.unity.render-pipelines.high-definition/CHANGELOG.md @@ -48,12 +48,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Fixed specular anti aliasing for layeredlit shader. - Fixed lens flare occlusion issues with transparent depth. It had the wrong depth bound (1365098) - Fixed double contribution from the clear coat when having SSR or RTR on the Lit and StackLit shaders (case 1352424). +- Fixed unexpectedly strong contribution from directional lights in path traced volumetric scattering (case 1304688). ### Changed - Visual Environment ambient mode is now Dynamic by default. - Surface ReflectionTypeLoadExceptions in HDUtils.GetRenderPipelineMaterialList(). Without surfacing these exceptions, developers cannot act on any underlying reflection errors in the HDRP assembly. - Improved the DynamicArray class by adding several utility APIs. - Moved AMD FidelityFX shaders to core +- Improved sampling of overlapping point/area lights in path traced volumetric scattering (case 1358777). ## [12.0.0] - 2021-01-11 diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/PathTracing/Shaders/PathTracingVolume.hlsl b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/PathTracing/Shaders/PathTracingVolume.hlsl index 6d1b727d6c9..f52a62edf15 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/PathTracing/Shaders/PathTracingVolume.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/PathTracing/Shaders/PathTracingVolume.hlsl @@ -24,8 +24,8 @@ bool SampleVolumeScatteringPosition(uint2 pixelCoord, inout float inputSample, i #ifdef HAS_LIGHTLOOP - float lightWeight; - float localWeight = PickLocalLightInterval(WorldRayOrigin(), WorldRayDirection(), inputSample, lightPosition, lightWeight, tMin, tMax); + float pickedLightWeight; + float localWeight = PickLocalLightInterval(WorldRayOrigin(), WorldRayDirection(), inputSample, lightPosition, pickedLightWeight, tMin, tMax); if (localWeight < 0.0) return false; @@ -38,7 +38,7 @@ bool SampleVolumeScatteringPosition(uint2 pixelCoord, inout float inputSample, i return false; inputSample /= localWeight; - pdfVol *= localWeight * lightWeight; + pdfVol *= localWeight * pickedLightWeight; } else { From b67279022dbd6e00f6a8032b3b5eacb7923ec62a Mon Sep 17 00:00:00 2001 From: Emmanuel Turquin Date: Wed, 22 Sep 2021 15:43:32 +0200 Subject: [PATCH 4/6] Changed the way fog color is handled when vol scattering is enabled. --- .../PathTracing/Shaders/PathTracingMain.raytrace | 2 +- .../Common/AtmosphericScatteringRayTracing.hlsl | 15 +++------------ 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/PathTracing/Shaders/PathTracingMain.raytrace b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/PathTracing/Shaders/PathTracingMain.raytrace index a0d36b83934..fa80ff8edb0 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/PathTracing/Shaders/PathTracingMain.raytrace +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/PathTracing/Shaders/PathTracingMain.raytrace @@ -64,7 +64,7 @@ void MissCamera(inout PathIntersection pathIntersection : SV_RayPayload) pathIntersection.value /= pdf; // Apply volumetric attenuation - ApplyFogAttenuation(WorldRayOrigin(), WorldRayDirection(), pathIntersection.t, pathIntersection.value); + ApplyFogAttenuation(WorldRayOrigin(), WorldRayDirection(), pathIntersection.t, pathIntersection.value, false); } // Reinject the environment value diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Raytracing/Shaders/Common/AtmosphericScatteringRayTracing.hlsl b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Raytracing/Shaders/Common/AtmosphericScatteringRayTracing.hlsl index 7b47928c9c0..695664cc719 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Raytracing/Shaders/Common/AtmosphericScatteringRayTracing.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Raytracing/Shaders/Common/AtmosphericScatteringRayTracing.hlsl @@ -11,10 +11,7 @@ void ApplyFogAttenuation(float3 origin, float3 direction, float t, inout float3 float absFogBaseHeight = _HeightFogBaseHeight; float fogTransmittance = TransmittanceHeightFog(_HeightFogBaseExtinction, absFogBaseHeight, _HeightFogExponents, direction.y, origin.y, dist); - // This is designed to match the raster volumes... even though I'm not sure why it's working that way - float3 fogColor = useFogColor && !_EnableVolumetricFog ? - GetFogColor(-direction, dist) * _HeightFogBaseScattering.xyz / _HeightFogBaseExtinction : - 0.0; + float3 fogColor = useFogColor? GetFogColor(-direction, dist) * _HeightFogBaseScattering.xyz / _HeightFogBaseExtinction : 0.0; value = lerp(fogColor, value, fogTransmittance); } } @@ -27,10 +24,7 @@ void ApplyFogAttenuation(float3 origin, float3 direction, inout float3 value) float absFogBaseHeight = _HeightFogBaseHeight; float fogTransmittance = TransmittanceHeightFog(_HeightFogBaseExtinction, absFogBaseHeight, _HeightFogExponents, direction.y, origin.y, dist); - // This is designed to match the raster volumes... even though I'm not sure why it's working that way - float3 fogColor = !_EnableVolumetricFog ? - GetFogColor(-direction, dist) * _HeightFogBaseScattering.xyz / _HeightFogBaseExtinction : - 0.0; + float3 fogColor = GetFogColor(-direction, dist) * _HeightFogBaseScattering.xyz / _HeightFogBaseExtinction; value = lerp(fogColor, value, fogTransmittance); } } @@ -43,10 +37,7 @@ void ApplyFogAttenuation(float3 origin, float3 direction, inout float3 value, in float absFogBaseHeight = _HeightFogBaseHeight; float fogTransmittance = TransmittanceHeightFog(_HeightFogBaseExtinction, absFogBaseHeight, _HeightFogExponents, direction.y, origin.y, dist); - // This is designed to match the raster volumes... even though I'm not sure why it's working that way - float3 fogColor = !_EnableVolumetricFog ? - GetFogColor(-direction, dist) * _HeightFogBaseScattering.xyz / _HeightFogBaseExtinction : - 0.0; + float3 fogColor = GetFogColor(-direction, dist) * _HeightFogBaseScattering.xyz / _HeightFogBaseExtinction; value = lerp(fogColor, value, fogTransmittance); alpha = saturate(1.0 - fogTransmittance); } From 3211e736da2cae754e81e9d55ea48693bbd0ab43 Mon Sep 17 00:00:00 2001 From: Emmanuel Turquin Date: Wed, 22 Sep 2021 15:49:25 +0200 Subject: [PATCH 5/6] Updated changelog. --- com.unity.render-pipelines.high-definition/CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/com.unity.render-pipelines.high-definition/CHANGELOG.md b/com.unity.render-pipelines.high-definition/CHANGELOG.md index 4bec1c088b5..245bc6573ca 100644 --- a/com.unity.render-pipelines.high-definition/CHANGELOG.md +++ b/com.unity.render-pipelines.high-definition/CHANGELOG.md @@ -50,14 +50,15 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Fixed double contribution from the clear coat when having SSR or RTR on the Lit and StackLit shaders (case 1352424). - Fixed texture fields for volume parameters accepting textures with wrong dimensions. - Fixed Realtime lightmap not working correctly in player with various lit shader (case 1360021) -- Fixed unexpectedly strong contribution from directional lights in path traced volumetric scattering (case 1304688). +- Fixed unexpectedly strong contribution from directional lights in path-traced volumetric scattering (case 1304688). ### Changed - Visual Environment ambient mode is now Dynamic by default. - Surface ReflectionTypeLoadExceptions in HDUtils.GetRenderPipelineMaterialList(). Without surfacing these exceptions, developers cannot act on any underlying reflection errors in the HDRP assembly. - Improved the DynamicArray class by adding several utility APIs. - Moved AMD FidelityFX shaders to core -- Improved sampling of overlapping point/area lights in path traced volumetric scattering (case 1358777). +- Improved sampling of overlapping point/area lights in path-traced volumetric scattering (case 1358777). +- Path-traced volumetric scattering now takes fog color into account, adding scattered contribution on top of the non-scattered result (cases 1358783, 1346105). ## [12.0.0] - 2021-01-11 From 3841be4b315aa51ea35cf79ab06a4601a0b8bfe0 Mon Sep 17 00:00:00 2001 From: Emmanuel Turquin Date: Wed, 22 Sep 2021 15:57:50 +0200 Subject: [PATCH 6/6] Cosmetic. --- com.unity.render-pipelines.high-definition/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.unity.render-pipelines.high-definition/CHANGELOG.md b/com.unity.render-pipelines.high-definition/CHANGELOG.md index 245bc6573ca..6e7a1f0188c 100644 --- a/com.unity.render-pipelines.high-definition/CHANGELOG.md +++ b/com.unity.render-pipelines.high-definition/CHANGELOG.md @@ -58,7 +58,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Improved the DynamicArray class by adding several utility APIs. - Moved AMD FidelityFX shaders to core - Improved sampling of overlapping point/area lights in path-traced volumetric scattering (case 1358777). -- Path-traced volumetric scattering now takes fog color into account, adding scattered contribution on top of the non-scattered result (cases 1358783, 1346105). +- Path-traced volumetric scattering now takes fog color into account, adding scattered contribution on top of the non-scattered result (cases 1346105, 1358783). ## [12.0.0] - 2021-01-11