From 9589c93f77cab7f6f8ecda70962878c162b01e12 Mon Sep 17 00:00:00 2001 From: Robert Cupisz Date: Thu, 6 Apr 2017 13:53:09 +0200 Subject: [PATCH] =?UTF-8?q?Improved=20transmission=20with=20an=20integral?= =?UTF-8?q?=20over=20slice=20depth=20-=20behaves=20nic=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/VolumetricFog/Scripts/VolumetricFog.cs | 2 +- Assets/VolumetricFog/Shaders/Scatter.compute | 40 ++++++++++--------- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/Assets/VolumetricFog/Scripts/VolumetricFog.cs b/Assets/VolumetricFog/Scripts/VolumetricFog.cs index eada458..3cb18fe 100644 --- a/Assets/VolumetricFog/Scripts/VolumetricFog.cs +++ b/Assets/VolumetricFog/Scripts/VolumetricFog.cs @@ -455,7 +455,7 @@ public class VolumetricFog : MonoBehaviour // Compensate for more light and density being injected in per world space meter when near and far are closer. // TODO: Not quite correct yet. float depthCompensation = (farClip - nearClip) * 0.01f; - m_InjectLightingAndDensity.SetFloat("_Density", m_GlobalDensityMult * 0.001f * depthCompensation); + m_InjectLightingAndDensity.SetFloat("_Density", m_GlobalDensityMult * 0.128f * depthCompensation); m_InjectLightingAndDensity.SetFloat("_Intensity", m_GlobalIntensityMult); m_InjectLightingAndDensity.SetFloat("_Anisotropy", m_Anisotropy); m_InjectLightingAndDensity.SetTexture(kernel, "_VolumeInject", m_VolumeInject); diff --git a/Assets/VolumetricFog/Shaders/Scatter.compute b/Assets/VolumetricFog/Shaders/Scatter.compute index ef2a757..d8d651d 100644 --- a/Assets/VolumetricFog/Shaders/Scatter.compute +++ b/Assets/VolumetricFog/Shaders/Scatter.compute @@ -2,37 +2,41 @@ // https://bartwronski.com/publications/ #pragma kernel CSMain -#define VOLUME_DEPTH 128 +#define VOLUME_DEPTH 128.0 Texture3D _VolumeInject; RWTexture3D _VolumeScatter; -float Extinction(float density) +float4 ScatterStep(float3 accumulatedLight, float accumulatedTransmittance, float3 sliceLight, float sliceDensity) { - return exp(-density); -} + sliceDensity = max(sliceDensity, 0.000001); + float sliceTransmittance = exp(-sliceDensity / VOLUME_DEPTH); -void WriteOutput(in uint3 pos, in float4 colorAndDensity) -{ - _VolumeScatter[pos] = float4(colorAndDensity.rgb, Extinction(colorAndDensity.a)); -} + // Seb Hillaire's improved transmission by calculating an integral over slice depth instead of + // constant per slice value. Light still constant per slice, but that's acceptable. See slide 28 of + // Physically-based & Unified Volumetric Rendering in Frostbite + // http://www.frostbite.com/2015/08/physically-based-unified-volumetric-rendering-in-frostbite/ + float3 sliceLightIntegral = sliceLight * (1.0 - sliceTransmittance) / sliceDensity; -float4 AccumulateScattering(float4 colorAndDensityFront, float4 colorAndDensityBack) -{ - float3 light = colorAndDensityFront.rgb + saturate(Extinction(colorAndDensityFront.a)) * colorAndDensityBack.rgb; - return float4(light.rgb, colorAndDensityFront.a + colorAndDensityBack.a); + accumulatedLight += sliceLightIntegral * accumulatedTransmittance; + accumulatedTransmittance *= sliceTransmittance; + + return float4(accumulatedLight, accumulatedTransmittance); } [numthreads(32, 2, 1)] void CSMain (uint3 id : SV_DispatchThreadID) { - float4 currentSlice = _VolumeInject[uint3(id.xy, 0)]; - WriteOutput(uint3(id.xy, 0), currentSlice); + // Store transmission in .a, as opposed to density in _VolumeInject + float4 accum = float4(0, 0, 0, 1); + uint3 pos = uint3(id.xy, 0); + uint steps = VOLUME_DEPTH; - for(uint z = 1; z < VOLUME_DEPTH; z++) + for(uint z = 0; z < steps; z++) { - float4 nextValue = _VolumeInject[uint3(id.xy, z)]; - currentSlice = AccumulateScattering(currentSlice, nextValue); - WriteOutput(uint3(id.xy, z), currentSlice); + pos.z = z; + float4 slice = _VolumeInject[pos]; + accum = ScatterStep(accum.rgb, accum.a, slice.rgb, slice.a); + _VolumeScatter[pos] = accum; } }