diff --git a/Editor/WeatherWindow.cpp b/Editor/WeatherWindow.cpp index 0a8dd7d45..db2fea12e 100644 --- a/Editor/WeatherWindow.cpp +++ b/Editor/WeatherWindow.cpp @@ -11,7 +11,7 @@ using namespace wiGraphics; void WeatherWindow::Create(EditorComponent* editor) { wiWindow::Create("Weather Window"); - SetSize(XMFLOAT2(660, 560)); + SetSize(XMFLOAT2(660, 610)); float x = 180; float y = 20; @@ -19,6 +19,15 @@ void WeatherWindow::Create(EditorComponent* editor) float step = hei + 2; + heightFogCheckBox.Create("Height fog: "); + heightFogCheckBox.SetSize(XMFLOAT2(hei, hei)); + heightFogCheckBox.SetPos(XMFLOAT2(x + 100, y += step)); + heightFogCheckBox.OnClick([&](wiEventArgs args) { + auto& weather = GetWeather(); + weather.SetHeightFog(args.bValue); + }); + AddWidget(&heightFogCheckBox); + fogStartSlider.Create(0, 5000, 0, 100000, "Fog Start: "); fogStartSlider.SetSize(XMFLOAT2(100, hei)); fogStartSlider.SetPos(XMFLOAT2(x, y += step)); @@ -35,13 +44,29 @@ void WeatherWindow::Create(EditorComponent* editor) }); AddWidget(&fogEndSlider); - fogHeightSlider.Create(0, 1, 0, 10000, "Fog Height: "); - fogHeightSlider.SetSize(XMFLOAT2(100, hei)); - fogHeightSlider.SetPos(XMFLOAT2(x, y += step)); - fogHeightSlider.OnSlide([&](wiEventArgs args) { - GetWeather().fogHeight = args.fValue; + fogHeightStartSlider.Create(-100, 100, 1, 10000, "Fog Height Start: "); + fogHeightStartSlider.SetSize(XMFLOAT2(100, hei)); + fogHeightStartSlider.SetPos(XMFLOAT2(x, y += step)); + fogHeightStartSlider.OnSlide([&](wiEventArgs args) { + GetWeather().fogHeightStart = args.fValue; + }); + AddWidget(&fogHeightStartSlider); + + fogHeightEndSlider.Create(-100, 100, 3, 10000, "Fog Height End: "); + fogHeightEndSlider.SetSize(XMFLOAT2(100, hei)); + fogHeightEndSlider.SetPos(XMFLOAT2(x, y += step)); + fogHeightEndSlider.OnSlide([&](wiEventArgs args) { + GetWeather().fogHeightEnd = args.fValue; + }); + AddWidget(&fogHeightEndSlider); + + fogHeightSkySlider.Create(0, 1, 0, 10000, "Fog Height Sky: "); + fogHeightSkySlider.SetSize(XMFLOAT2(100, hei)); + fogHeightSkySlider.SetPos(XMFLOAT2(x, y += step)); + fogHeightSkySlider.OnSlide([&](wiEventArgs args) { + GetWeather().fogHeightSky = args.fValue; }); - AddWidget(&fogHeightSlider); + AddWidget(&fogHeightSkySlider); cloudinessSlider.Create(0, 1, 0.0f, 10000, "Cloudiness: "); cloudinessSlider.SetSize(XMFLOAT2(100, hei)); @@ -445,7 +470,7 @@ void WeatherWindow::Create(EditorComponent* editor) weather.cloudiness = 0.4f; weather.fogStart = 100; weather.fogEnd = 1000; - weather.fogHeight = 0; + weather.fogHeightSky = 0; InvalidateProbes(); @@ -465,7 +490,7 @@ void WeatherWindow::Create(EditorComponent* editor) weather.cloudiness = 0.36f; weather.fogStart = 50; weather.fogEnd = 600; - weather.fogHeight = 0; + weather.fogHeightSky = 0; InvalidateProbes(); @@ -485,7 +510,7 @@ void WeatherWindow::Create(EditorComponent* editor) weather.cloudiness = 0.75f; weather.fogStart = 0; weather.fogEnd = 500; - weather.fogHeight = 0; + weather.fogHeightSky = 0; InvalidateProbes(); @@ -505,7 +530,7 @@ void WeatherWindow::Create(EditorComponent* editor) weather.cloudiness = 0.28f; weather.fogStart = 10; weather.fogEnd = 400; - weather.fogHeight = 0; + weather.fogHeightSky = 0; InvalidateProbes(); @@ -552,9 +577,12 @@ void WeatherWindow::Update() colorgradingButton.SetText(wiHelper::GetFileNameFromPath(weather.colorGradingMapName)); } + heightFogCheckBox.SetCheck(weather.IsHeightFog()); fogStartSlider.SetValue(weather.fogStart); fogEndSlider.SetValue(weather.fogEnd); - fogHeightSlider.SetValue(weather.fogHeight); + fogHeightStartSlider.SetValue(weather.fogHeightStart); + fogHeightEndSlider.SetValue(weather.fogHeightEnd); + fogHeightSkySlider.SetValue(weather.fogHeightSky); cloudinessSlider.SetValue(weather.cloudiness); cloudScaleSlider.SetValue(weather.cloudScale); cloudSpeedSlider.SetValue(weather.cloudSpeed); diff --git a/Editor/WeatherWindow.h b/Editor/WeatherWindow.h index fab3bd118..d8719dce1 100644 --- a/Editor/WeatherWindow.h +++ b/Editor/WeatherWindow.h @@ -14,9 +14,12 @@ public: wiScene::WeatherComponent& GetWeather() const; void InvalidateProbes() const; + wiCheckBox heightFogCheckBox; wiSlider fogStartSlider; wiSlider fogEndSlider; - wiSlider fogHeightSlider; + wiSlider fogHeightStartSlider; + wiSlider fogHeightEndSlider; + wiSlider fogHeightSkySlider; wiSlider cloudinessSlider; wiSlider cloudScaleSlider; wiSlider cloudSpeedSlider; diff --git a/WickedEngine/ArchiveVersionHistory.txt b/WickedEngine/ArchiveVersionHistory.txt index b47f4b2b3..29f1be89a 100644 --- a/WickedEngine/ArchiveVersionHistory.txt +++ b/WickedEngine/ArchiveVersionHistory.txt @@ -1,5 +1,6 @@ This file contains changelog of wiArchive versions +71: serialized WeatherComponent::fogHeightStart and fogHeightEnd 70: serialized VolumetricCloudParameters 69: serialized AABB:: layerMask and userdata parameters 68: serialized MaterialComponent SPECULARMAP (for KHR_materials_specular) diff --git a/WickedEngine/shaders/ShaderInterop_Renderer.h b/WickedEngine/shaders/ShaderInterop_Renderer.h index a4bff3a9a..c4e892227 100644 --- a/WickedEngine/shaders/ShaderInterop_Renderer.h +++ b/WickedEngine/shaders/ShaderInterop_Renderer.h @@ -533,9 +533,10 @@ static const uint OPTION_BIT_VOXELGI_REFLECTIONS_ENABLED = 1 << 3; static const uint OPTION_BIT_VOXELGI_RETARGETTED = 1 << 4; static const uint OPTION_BIT_SIMPLE_SKY = 1 << 5; static const uint OPTION_BIT_REALISTIC_SKY = 1 << 6; -static const uint OPTION_BIT_RAYTRACED_SHADOWS = 1 << 7; -static const uint OPTION_BIT_DISABLE_ALBEDO_MAPS = 1 << 8; -static const uint OPTION_BIT_SHADOW_MASK = 1 << 9; +static const uint OPTION_BIT_HEIGHT_FOG = 1 << 7; +static const uint OPTION_BIT_RAYTRACED_SHADOWS = 1 << 8; +static const uint OPTION_BIT_DISABLE_ALBEDO_MAPS = 1 << 9; +static const uint OPTION_BIT_SHADOW_MASK = 1 << 10; // ---------- Common Constant buffers: ----------------- @@ -562,10 +563,11 @@ CBUFFER(FrameCB, CBSLOT_RENDERER_FRAME) float3 g_xFrame_Ambient; float g_xFrame_Cloudiness; - float3 g_xFrame_padding0; - float g_xFrame_SkyExposure; + float4 g_xFrame_Fog; // Fog Start,End,Height Start,Height End - float3 g_xFrame_Fog; // Fog Start,End,Height + float g_xFrame_padding0; + float g_xFrame_FogHeightSky; + float g_xFrame_SkyExposure; float g_xFrame_VoxelRadianceMaxDistance; // maximum raymarch distance for voxel GI in world-space float g_xFrame_VoxelRadianceDataSize; // voxel half-extent in world space units diff --git a/WickedEngine/shaders/globals.hlsli b/WickedEngine/shaders/globals.hlsli index 280b116f8..18e60ec55 100644 --- a/WickedEngine/shaders/globals.hlsli +++ b/WickedEngine/shaders/globals.hlsli @@ -67,9 +67,47 @@ inline float GetTime() { return g_xFrame_Time; } inline uint2 GetTemporalAASampleRotation() { return uint2((g_xFrame_TemporalAASampleRotation >> 0u) & 0x000000FF, (g_xFrame_TemporalAASampleRotation >> 8) & 0x000000FF); } inline bool IsStaticSky() { return g_xFrame_StaticSkyGamma > 0.0; } -inline float GetFogAmount(float dist) +// Exponential height fog based on: https://www.iquilezles.org/www/articles/fog/fog.htm +// Non constant density function +// distance : sample to point distance +// O : sample position +// V : sample to point vector +inline float GetFogAmount(float distance, float3 O, float3 V) { - return saturate((dist - g_xFrame_Fog.x) / (g_xFrame_Fog.y - g_xFrame_Fog.x)); + float fogDensity = saturate((distance - g_xFrame_Fog.x) / (g_xFrame_Fog.y - g_xFrame_Fog.x)); + + if (g_xFrame_Options & OPTION_BIT_HEIGHT_FOG) + { + float fogHeightStart = g_xFrame_Fog.z; + float fogHeightEnd = g_xFrame_Fog.w; + float fogFalloffScale = 1.0 / max(0.01, fogHeightEnd - fogHeightStart); + + // solve for x, e^(-h * x) = 0.001 + // x = 6.907755 * h^-1 + float fogFalloff = 6.907755 * fogFalloffScale; + + float originHeight = O.y; + float Z = -V.y; + float effectiveZ = max(abs(Z), 0.001); + + float endLineHeight = originHeight + distance * Z; // Isolated vector equation for y + float minLineHeight = min(originHeight, endLineHeight); + float heightLineFalloff = max(minLineHeight - fogHeightStart, 0); + + float baseHeightFogDistance = clamp((fogHeightStart - minLineHeight) / effectiveZ, 0, distance); + float exponentialFogDistance = distance - baseHeightFogDistance; // Exclude distance below base height + float exponentialHeightLineIntegral = exp(-heightLineFalloff * fogFalloff) * (1.0 - exp(-exponentialFogDistance * effectiveZ * fogFalloff)) / (effectiveZ * fogFalloff); + + float opticalDepthHeightFog = fogDensity * (baseHeightFogDistance + exponentialHeightLineIntegral); + float transmittanceHeightFog = exp(-opticalDepthHeightFog); + + float fogAmount = transmittanceHeightFog; + return 1.0 - fogAmount; + } + else + { + return fogDensity; + } } float3 tonemap(float3 x) diff --git a/WickedEngine/shaders/hairparticlePS.hlsl b/WickedEngine/shaders/hairparticlePS.hlsl index f53b5e445..16f0392c8 100644 --- a/WickedEngine/shaders/hairparticlePS.hlsl +++ b/WickedEngine/shaders/hairparticlePS.hlsl @@ -49,7 +49,7 @@ GBuffer main(VertexToPixel input) ApplyLighting(surface, lighting, color); - ApplyFog(dist, color); + ApplyFog(dist, g_xCamera_CamPos, V, color); return CreateGBuffer(color, surface); } diff --git a/WickedEngine/shaders/impostorPS.hlsl b/WickedEngine/shaders/impostorPS.hlsl index 07048c646..bb194fdf2 100644 --- a/WickedEngine/shaders/impostorPS.hlsl +++ b/WickedEngine/shaders/impostorPS.hlsl @@ -47,7 +47,7 @@ GBuffer main(VSOut input) ApplyLighting(surface, lighting, color); - ApplyFog(dist, color); + ApplyFog(dist, g_xCamera_CamPos, V, color); return CreateGBuffer(color, surface); } diff --git a/WickedEngine/shaders/objectHF.hlsli b/WickedEngine/shaders/objectHF.hlsli index d6d656417..cf4534463 100644 --- a/WickedEngine/shaders/objectHF.hlsli +++ b/WickedEngine/shaders/objectHF.hlsli @@ -1166,17 +1166,19 @@ inline void ApplyLighting(in Surface surface, in Lighting lighting, inout float4 color.rgb = lerp(surface.albedo * combined_lighting.diffuse, surface.refraction.rgb, surface.refraction.a) + combined_lighting.specular; } -inline void ApplyFog(in float dist, inout float4 color) +inline void ApplyFog(in float distance, float3 P, float3 V, inout float4 color) { + const float fogAmount = GetFogAmount(distance, P, V); + if (g_xFrame_Options & OPTION_BIT_REALISTIC_SKY) { const float3 skyLuminance = texture_skyluminancelut.SampleLevel(sampler_point_clamp, float2(0.5, 0.5), 0).rgb; - color.rgb = lerp(color.rgb, skyLuminance, GetFogAmount(dist)); + color.rgb = lerp(color.rgb, skyLuminance, fogAmount); } else { const float3 V = float3(0.0, -1.0, 0.0); - color.rgb = lerp(color.rgb, GetDynamicSkyColor(V, false, false, false, true), GetFogAmount(dist)); + color.rgb = lerp(color.rgb, GetDynamicSkyColor(V, false, false, false, true), fogAmount); } } @@ -1927,7 +1929,7 @@ float4 main(PixelInput input, in bool is_frontface : SV_IsFrontFace) : SV_TARGET #ifdef OBJECTSHADER_USE_POSITION3D - ApplyFog(dist, color); + ApplyFog(dist, g_xCamera_CamPos, surface.V, color); #endif // OBJECTSHADER_USE_POSITION3D diff --git a/WickedEngine/shaders/oceanSurfacePS.hlsl b/WickedEngine/shaders/oceanSurfacePS.hlsl index 23631c75d..5a95472db 100644 --- a/WickedEngine/shaders/oceanSurfacePS.hlsl +++ b/WickedEngine/shaders/oceanSurfacePS.hlsl @@ -62,7 +62,7 @@ float4 main(PSIn input) : SV_TARGET ApplyLighting(surface, lighting, color); - ApplyFog(dist, color); + ApplyFog(dist, g_xCamera_CamPos, V, color); return color; } diff --git a/WickedEngine/shaders/skyHF.hlsli b/WickedEngine/shaders/skyHF.hlsli index 9bab7f4b8..73c93103c 100644 --- a/WickedEngine/shaders/skyHF.hlsli +++ b/WickedEngine/shaders/skyHF.hlsli @@ -119,7 +119,7 @@ float3 CustomAtmosphericScattering(float3 V, float3 sunDirection, float3 sunColo const float zenith = V.y; // how much is above (0: horizon, 1: directly above) const float sunScatter = saturate(sunDirection.y + 0.1f); // how much the sun is directly above. Even if sunis at horizon, we add a constant scattering amount so that light still scatters at horizon - const float atmosphereDensity = 0.5 + g_xFrame_Fog.z; // constant of air density, or "fog height" as interpreted here (bigger is more obstruction of sun) + const float atmosphereDensity = 0.5 + g_xFrame_FogHeightSky; // constant of air density, or "fog height" as interpreted here (bigger is more obstruction of sun) const float zenithDensity = atmosphereDensity / pow(max(0.000001f, zenith), 0.75f); const float sunScatterDensity = atmosphereDensity / pow(max(0.000001f, sunScatter), 0.75f); diff --git a/WickedEngine/shaders/volumetricLight_DirectionalPS.hlsl b/WickedEngine/shaders/volumetricLight_DirectionalPS.hlsl index ca3317657..bf02232e4 100644 --- a/WickedEngine/shaders/volumetricLight_DirectionalPS.hlsl +++ b/WickedEngine/shaders/volumetricLight_DirectionalPS.hlsl @@ -48,8 +48,9 @@ float4 main(VertexToPixel input) : SV_TARGET { float3 attenuation = shadowCascade(light, ShPos, ShTex.xy, cascade); - attenuation *= GetFogAmount(cameraDistance - marchedDistance) * scattering; - + // Evaluate sample height for height fog calculation, given 0 for V: + attenuation *= GetFogAmount(cameraDistance - marchedDistance, P, float3(0.0, 0.0, 0.0)) * scattering; + accumulation += attenuation; marchedDistance += stepSize; diff --git a/WickedEngine/shaders/volumetricLight_PointPS.hlsl b/WickedEngine/shaders/volumetricLight_PointPS.hlsl index 23983e30a..2f7a446c9 100644 --- a/WickedEngine/shaders/volumetricLight_PointPS.hlsl +++ b/WickedEngine/shaders/volumetricLight_PointPS.hlsl @@ -49,8 +49,9 @@ float4 main(VertexToPixel input) : SV_TARGET attenuation *= shadowCube(light, L, Lunnormalized); } - attenuation *= GetFogAmount(cameraDistance - marchedDistance); - + // Evaluate sample height for height fog calculation, given 0 for V: + attenuation *= GetFogAmount(cameraDistance - marchedDistance, P, float3(0.0, 0.0, 0.0)); + accumulation += attenuation; marchedDistance += stepSize; diff --git a/WickedEngine/shaders/volumetricLight_SpotPS.hlsl b/WickedEngine/shaders/volumetricLight_SpotPS.hlsl index d90d8effe..3a81c5e86 100644 --- a/WickedEngine/shaders/volumetricLight_SpotPS.hlsl +++ b/WickedEngine/shaders/volumetricLight_SpotPS.hlsl @@ -58,7 +58,8 @@ float4 main(VertexToPixel input) : SV_TARGET } } - attenuation *= GetFogAmount(cameraDistance - marchedDistance); + // Evaluate sample height for exponential fog calculation, given 0 for V: + attenuation *= GetFogAmount(cameraDistance - marchedDistance, P, float3(0.0, 0.0, 0.0)); accumulation += attenuation; } diff --git a/WickedEngine/wiArchive.cpp b/WickedEngine/wiArchive.cpp index 829a002e5..bb0833e42 100644 --- a/WickedEngine/wiArchive.cpp +++ b/WickedEngine/wiArchive.cpp @@ -4,7 +4,7 @@ #include // this should always be only INCREMENTED and only if a new serialization is implemeted somewhere! -uint64_t __archiveVersion = 70; +uint64_t __archiveVersion = 71; // this is the version number of which below the archive is not compatible with the current version uint64_t __archiveVersionBarrier = 22; diff --git a/WickedEngine/wiRenderer.cpp b/WickedEngine/wiRenderer.cpp index 486104a2c..8c9bc5c89 100644 --- a/WickedEngine/wiRenderer.cpp +++ b/WickedEngine/wiRenderer.cpp @@ -3552,7 +3552,8 @@ void UpdatePerFrameData( frameCB.g_xFrame_Cloudiness = vis.scene->weather.cloudiness; frameCB.g_xFrame_CloudScale = vis.scene->weather.cloudScale; frameCB.g_xFrame_CloudSpeed = vis.scene->weather.cloudSpeed; - frameCB.g_xFrame_Fog = float3(vis.scene->weather.fogStart, vis.scene->weather.fogEnd, vis.scene->weather.fogHeight); + frameCB.g_xFrame_Fog = float4(vis.scene->weather.fogStart, vis.scene->weather.fogEnd, vis.scene->weather.fogHeightStart, vis.scene->weather.fogHeightEnd); + frameCB.g_xFrame_FogHeightSky = vis.scene->weather.fogHeightSky; frameCB.g_xFrame_Horizon = vis.scene->weather.horizon; frameCB.g_xFrame_Zenith = vis.scene->weather.zenith; frameCB.g_xFrame_SkyExposure = vis.scene->weather.skyExposure; @@ -3668,6 +3669,10 @@ void UpdatePerFrameData( { frameCB.g_xFrame_Options |= OPTION_BIT_REALISTIC_SKY; } + if (vis.scene->weather.IsHeightFog()) + { + frameCB.g_xFrame_Options |= OPTION_BIT_HEIGHT_FOG; + } if (device->CheckCapability(GRAPHICSDEVICE_CAPABILITY_RAYTRACING_INLINE) && GetRaytracedShadowsEnabled()) { frameCB.g_xFrame_Options |= OPTION_BIT_RAYTRACED_SHADOWS; diff --git a/WickedEngine/wiScene.h b/WickedEngine/wiScene.h index adb8550a7..30d1ff21c 100644 --- a/WickedEngine/wiScene.h +++ b/WickedEngine/wiScene.h @@ -1112,6 +1112,7 @@ namespace wiScene SIMPLE_SKY = 1 << 1, REALISTIC_SKY = 1 << 2, VOLUMETRIC_CLOUDS = 1 << 3, + HEIGHT_FOG = 1 << 4, }; uint32_t _flags = EMPTY; @@ -1119,11 +1120,13 @@ namespace wiScene inline bool IsSimpleSky() const { return _flags & SIMPLE_SKY; } inline bool IsRealisticSky() const { return _flags & REALISTIC_SKY; } inline bool IsVolumetricClouds() const { return _flags & VOLUMETRIC_CLOUDS; } + inline bool IsHeightFog() const { return _flags & HEIGHT_FOG; } inline void SetOceanEnabled(bool value = true) { if (value) { _flags |= OCEAN_ENABLED; } else { _flags &= ~OCEAN_ENABLED; } } inline void SetSimpleSky(bool value = true) { if (value) { _flags |= SIMPLE_SKY; } else { _flags &= ~SIMPLE_SKY; } } inline void SetRealisticSky(bool value = true) { if (value) { _flags |= REALISTIC_SKY; } else { _flags &= ~REALISTIC_SKY; } } inline void SetVolumetricClouds(bool value = true) { if (value) { _flags |= VOLUMETRIC_CLOUDS; } else { _flags &= ~VOLUMETRIC_CLOUDS; } } + inline void SetHeightFog(bool value = true) { if (value) { _flags |= HEIGHT_FOG; } else { _flags &= ~HEIGHT_FOG; } } XMFLOAT3 sunColor = XMFLOAT3(0, 0, 0); XMFLOAT3 sunDirection = XMFLOAT3(0, 1, 0); @@ -1134,7 +1137,9 @@ namespace wiScene XMFLOAT3 ambient = XMFLOAT3(0.2f, 0.2f, 0.2f); float fogStart = 100; float fogEnd = 1000; - float fogHeight = 0; + float fogHeightStart = 1; + float fogHeightEnd = 3; + float fogHeightSky = 0; float cloudiness = 0.0f; float cloudScale = 0.0003f; float cloudSpeed = 0.1f; diff --git a/WickedEngine/wiScene_Serializers.cpp b/WickedEngine/wiScene_Serializers.cpp index 89f56953a..b7070304f 100644 --- a/WickedEngine/wiScene_Serializers.cpp +++ b/WickedEngine/wiScene_Serializers.cpp @@ -939,7 +939,7 @@ namespace wiScene archive >> ambient; archive >> fogStart; archive >> fogEnd; - archive >> fogHeight; + archive >> fogHeightSky; archive >> cloudiness; archive >> cloudScale; archive >> cloudSpeed; @@ -1074,6 +1074,11 @@ namespace wiScene archive >> volumetricCloudParameters.GroundContributionSampleCount; } + if (archive.GetVersion() >= 71) + { + archive >> fogHeightStart; + archive >> fogHeightEnd; + } } else { @@ -1085,7 +1090,7 @@ namespace wiScene archive << ambient; archive << fogStart; archive << fogEnd; - archive << fogHeight; + archive << fogHeightSky; archive << cloudiness; archive << cloudScale; archive << cloudSpeed; @@ -1202,6 +1207,11 @@ namespace wiScene archive << volumetricCloudParameters.GroundContributionSampleCount; } + if (archive.GetVersion() >= 71) + { + archive << fogHeightStart; + archive << fogHeightEnd; + } } } void SoundComponent::Serialize(wiArchive& archive, EntitySerializer& seri)