From e0ddaa3454927440bf48bad5be1f935243a492a9 Mon Sep 17 00:00:00 2001 From: Jiaye Date: Fri, 16 Jan 2026 17:34:48 +0800 Subject: [PATCH 1/9] add dalc sh --- package/Shaders/Common/SharedData.hlsli | 4 + src/State.cpp | 17 ++ src/State.h | 3 + src/Utils/SphericalHarmonics.cpp | 285 ++++++++++++++++++++++++ src/Utils/SphericalHarmonics.h | 58 +++++ 5 files changed, 367 insertions(+) create mode 100644 src/Utils/SphericalHarmonics.cpp create mode 100644 src/Utils/SphericalHarmonics.h diff --git a/package/Shaders/Common/SharedData.hlsli b/package/Shaders/Common/SharedData.hlsli index 99a4f01e22..e17442907e 100644 --- a/package/Shaders/Common/SharedData.hlsli +++ b/package/Shaders/Common/SharedData.hlsli @@ -23,6 +23,10 @@ namespace SharedData bool InMapMenu; // If the world/local map is open (note that the renderer is still deferred here) bool HideSky; // HideSky flag in WorldSpace, e.g. Blackreach float MipBias; // Offset to mip level for TAA sharpness# + float pad0; + float4 AmbientSHR; + float4 AmbientSHG; + float4 AmbientSHB; }; struct GrassLightingSettings diff --git a/src/State.cpp b/src/State.cpp index 8b5ecd5642..589875617a 100644 --- a/src/State.cpp +++ b/src/State.cpp @@ -21,6 +21,7 @@ #include "Utils/FileSystem.h" #include "WeatherManager.h" #include "WeatherVariableRegistry.h" +#include "Utils/SphericalHarmonics.h" void State::Draw() { @@ -849,6 +850,22 @@ void State::UpdateSharedData([[maybe_unused]] bool a_inWorld, [[maybe_unused]] b data.MipBias = 0; } + // DALC to SH + const auto& m = dalcTransform.rotate; + const auto& t = dalcTransform.translate; + float3 dalcColors[6]; + dalcColors[0] = float3{ m.entry[0][0] + t.x, m.entry[1][0] + t.y, m.entry[2][0] + t.z }; // +X + dalcColors[1] = float3{ -m.entry[0][0] + t.x, -m.entry[1][0] + t.y, -m.entry[2][0] + t.z }; // -X + dalcColors[2] = float3{ m.entry[0][1] + t.x, m.entry[1][1] + t.y, m.entry[2][1] + t.z }; // +Y + dalcColors[3] = float3{ -m.entry[0][1] + t.x, -m.entry[1][1] + t.y, -m.entry[2][1] + t.z }; // -Y + dalcColors[4] = float3{ m.entry[0][2] + t.x, m.entry[1][2] + t.y, m.entry[2][2] + t.z }; // +Z + dalcColors[5] = float3{ -m.entry[0][2] + t.x, -m.entry[1][2] + t.y, -m.entry[2][2] + t.z }; // -Z + + SphericalHarmonics::SH2Color dalcSH = SphericalHarmonics::DALCToSH(dalcColors); + data.AmbientSHR = { dalcSH.r.c0, dalcSH.r.c1[0], dalcSH.r.c1[1], dalcSH.r.c1[2] }; + data.AmbientSHG = { dalcSH.g.c0, dalcSH.g.c1[0], dalcSH.g.c1[1], dalcSH.g.c1[2] }; + data.AmbientSHB = { dalcSH.b.c0, dalcSH.b.c1[0], dalcSH.b.c1[1], dalcSH.b.c1[2] }; + sharedDataCB->Update(data); } diff --git a/src/State.h b/src/State.h index 17da41bc64..5682562681 100644 --- a/src/State.h +++ b/src/State.h @@ -211,6 +211,9 @@ class State uint HideSky; float MipBias; float pad0; + float4 AmbientSHR; + float4 AmbientSHG; + float4 AmbientSHB; }; STATIC_ASSERT_ALIGNAS_16(SharedDataCB); diff --git a/src/Utils/SphericalHarmonics.cpp b/src/Utils/SphericalHarmonics.cpp new file mode 100644 index 0000000000..f0f28e0264 --- /dev/null +++ b/src/Utils/SphericalHarmonics.cpp @@ -0,0 +1,285 @@ +#include "SphericalHarmonics.h" + +using namespace SphericalHarmonics; + +SH2 SphericalHarmonics::Evaluate(float3 dir) +{ + SH2 result; + result.c0 = 0.28209479177387814347403972578039f; // L=0 , M= 0 + result.c1[0] = -0.48860251190291992158638462283836f * dir.y; // L=1 , M=-1 + result.c1[1] = 0.48860251190291992158638462283836f * dir.z; // L=1 , M= 0 + result.c1[2] = -0.48860251190291992158638462283836f * dir.x; // L=1 , M= 1 + return result; +} + +float SphericalHarmonics::Dot(SH2 a, SH2 b) +{ + float4 aVec = float4(a.c0, a.c1[0], a.c1[1], a.c1[2]); + float4 bVec = float4(b.c0, b.c1[0], b.c1[1], b.c1[2]); + return aVec.Dot(bVec); +} + +float3 SphericalHarmonics::Dot(SH2Color a, SH2Color b) +{ + return float3( + Dot(a.r, b.r), + Dot(a.g, b.g), + Dot(a.b, b.b) + ); +} + +float SphericalHarmonics::Unproject(float3 dir, SH2 sh) +{ + SH2 basis = Evaluate(dir); + return Dot(sh, basis); +} + +float3 SphericalHarmonics::Unproject(float3 dir, SH2Color sh) +{ + return float3( + Unproject(dir, sh.r), + Unproject(dir, sh.g), + Unproject(dir, sh.b) + ); +} + +SH2 SphericalHarmonics::EvaluateCosineLobe(float3 dir) +{ + SH2 result; + result.c0 = 0.8862269254527580137f; // L=0 , M= 0 + result.c1[0] = -1.0233267079464884885f * dir.y; // L=1 , M=-1 + result.c1[1] = 1.0233267079464884885f * dir.z; // L=1 , M= 0 + result.c1[2] = -1.0233267079464884885f * dir.x; // L=1 , M= 1 + return result; +} + +SH2 SphericalHarmonics::EvaluatePhaseHG(float3 dir, float g) +{ + SH2 result; + const float factor = 0.48860251190291992158638462283836f * g; + result.c0 = 0.28209479177387814347403972578039f; // L=0 , M= 0 + result.c1[0] = -factor * dir.y; // L=1 , M=-1 + result.c1[1] = factor * dir.z; // L=1 , M= 0 + result.c1[2] = -factor * dir.x; // L=1 , M= 1 + return result; +} + +SH2 SphericalHarmonics::Add(SH2 a, SH2 b) +{ + SH2 result; + result.c0 = a.c0 + b.c0; + result.c1[0] = a.c1[0] + b.c1[0]; + result.c1[1] = a.c1[1] + b.c1[1]; + result.c1[2] = a.c1[2] + b.c1[2]; + return result; +} + +SH2Color SphericalHarmonics::Add(SH2Color a, SH2Color b) +{ + SH2Color result; + result.r = Add(a.r, b.r); + result.g = Add(a.g, b.g); + result.b = Add(a.b, b.b); + return result; +} + +SH2 SphericalHarmonics::Scale(SH2 sh, float scale) +{ + SH2 result; + result.c0 = sh.c0 * scale; + result.c1[0] = sh.c1[0] * scale; + result.c1[1] = sh.c1[1] * scale; + result.c1[2] = sh.c1[2] * scale; + return result; +} + +SH2Color SphericalHarmonics::Scale(SH2Color sh, float scale) +{ + SH2Color result; + result.r = Scale(sh.r, scale); + result.g = Scale(sh.g, scale); + result.b = Scale(sh.b, scale); + return result; +} + +SH2 SphericalHarmonics::Rotate(SH2 sh, float3x3 rotMatrix) +{ + SH2 result; + result.c0 = sh.c0; + + float3 c1Vec = float3(sh.c1[0], sh.c1[1], sh.c1[2]); + DirectX::XMVECTOR v = XMLoadFloat3(&c1Vec); + v = DirectX::XMVector3Transform(v, XMLoadFloat3x3(&rotMatrix)); + DirectX::XMStoreFloat3(&c1Vec, v); + + result.c1[0] = c1Vec.x; + result.c1[1] = c1Vec.y; + result.c1[2] = c1Vec.z; + return result; +} + +SH2Color SphericalHarmonics::Rotate(SH2Color sh, float3x3 rotMatrix) +{ + SH2Color result; + result.r = Rotate(sh.r, rotMatrix); + result.g = Rotate(sh.g, rotMatrix); + result.b = Rotate(sh.b, rotMatrix); + return result; +} + +float SphericalHarmonics::FuncProductIntegral(SH2 shL, SH2 shR) +{ + return Dot(shL, shR); +} + +float3 SphericalHarmonics::FuncProductIntegral(SH2Color shL, SH2Color shR) +{ + return float3( + FuncProductIntegral(shL.r, shR.r), + FuncProductIntegral(shL.g, shR.g), + FuncProductIntegral(shL.b, shR.b) + ); +} + +SH2 SphericalHarmonics::Product(SH2 shL, SH2 shR) +{ + const float factor = 1.0f / (2.0f * sqrt(3.14159265358979323846f)); + SH2 result; + result.c0 = factor * Dot(shL, shR); + result.c1[0] = factor * (shL.c1[0] * shR.c1[2] + shL.c1[2] * shR.c1[0]); + result.c1[1] = factor * (shL.c1[1] * shR.c1[2] + shL.c1[2] * shR.c1[1]); + result.c1[2] = factor * (shL.c1[2] * shR.c1[2] + shL.c1[2] * shR.c1[2]); + return result; +} + +SH2Color SphericalHarmonics::Product(SH2Color shL, SH2Color shR) +{ + SH2Color result; + result.r = Product(shL.r, shR.r); + result.g = Product(shL.g, shR.g); + result.b = Product(shL.b, shR.b); + return result; +} + +SH2 SphericalHarmonics::HanningConvolution(SH2 sh, float w) +{ + SH2 result; + float invW = 1.0f / w; + float factorBand1 = (1.0f + cos(3.14159265358979323846f * invW)) / 2.0f; + result.c0 = sh.c0; + result.c1[0] = sh.c1[0] * factorBand1; + result.c1[1] = sh.c1[1] * factorBand1; + result.c1[2] = sh.c1[2] * factorBand1; + return result; +} + +SH2Color SphericalHarmonics::HanningConvolution(SH2Color sh, float w) +{ + SH2Color result; + result.r = HanningConvolution(sh.r, w); + result.g = HanningConvolution(sh.g, w); + result.b = HanningConvolution(sh.b, w); + return result; +} + +SH2 SphericalHarmonics::DiffuseConvolution(SH2 sh) +{ + SH2 result = sh; + result.c0 *= 3.14159265358979323846f; + result.c1[0] *= 2.0943951023931954923f; + result.c1[1] *= 2.0943951023931954923f; + result.c1[2] *= 2.0943951023931954923f; + return result; +} + +SH2Color SphericalHarmonics::DiffuseConvolution(SH2Color sh) +{ + SH2Color result; + result.r = DiffuseConvolution(sh.r); + result.g = DiffuseConvolution(sh.g); + result.b = DiffuseConvolution(sh.b); + return result; +} + +template +static T reflect(const T& i, const T& n) { + return i - n * (2.0f * i.Dot(n)); +} + +template +static T normalize(const T& v) { + return v * (1.0f / sqrtf(v.Dot(v))); +} + +// Author: ProfJack +// Constructs the SH of an approximate specular lobe +SH2 SphericalHarmonics::FauxSpecularLobe(float3 N, float3 V, float roughness) +{ + // https://www.gdcvault.com/play/1026701/Fast-Denoising-With-Self-Stabilizing + // get dominant ggx reflection direction + float f = (1 - roughness) * (sqrt(1 - roughness) + roughness); + float3 R = reflect(-V, N); + float3 D = R * f + N * (1 - f); + float3 dominantDir = normalize(D); + + // lobe half angle + // credit: Olivier Therrien + float roughness2 = roughness * roughness; + float halfAngle = std::clamp(4.1679f * roughness2 * roughness2 - 9.0127f * roughness2 * roughness + 4.6161f * roughness2 + 1.7048f * roughness + 0.1f, 0.0f, 3.14159265358979323846f / 2.0f); + float lerpFactor = halfAngle / (3.14159265358979323846f / 2.0f); + SH2 directional = Evaluate(dominantDir); + SH2 cosineLobe = Scale(EvaluateCosineLobe(dominantDir), 1.0 / 3.14159265358979323846f); + return Add(Scale(directional, lerpFactor), Scale(cosineLobe, 1.0f - lerpFactor)); +} + +float SphericalHarmonics::SHHallucinateZH3Irradiance(SH2 sh, float3 direction) +{ + float3 zonalAxis = normalize(float3(sh.c1[2], sh.c1[0], sh.c1[1])); + float ratio = 0.0; + ratio = abs(zonalAxis.Dot(float3(-sh.c1[2], -sh.c1[0], sh.c1[1]))); + ratio /= sh.c0; + float zonalL2Coeff = sh.c0 * (0.08f * ratio + 0.6f * ratio * ratio); // Curve-fit; Section 3.4.3 + float fZ = zonalAxis.Dot(direction); + float zhDir = sqrt(5.0f / (16.0f * 3.14159265358979323846f)) * (3.0f * fZ * fZ - 1.0f); + // Convolve inSH with the normalized cosine kernel (multiply the L1 band by the zonal scale 2/3), then dot with + // inSH(direction) for linear inSH (Equation 5). + float result = SphericalHarmonics::FuncProductIntegral(sh, SphericalHarmonics::EvaluateCosineLobe(direction)); + // Add irradiance from the ZH3 term. zonalL2Coeff is the ZH3 coefficient for a radiance signal, so we need to + // multiply by 1/4 (the L2 zonal scale for a normalized clamped cosine kernel) to evaluate irradiance. + result += 0.25f * zonalL2Coeff * zhDir; + return std::max(0.0f, result); +} + +float3 SphericalHarmonics::SHHallucinateZH3Irradiance(SH2Color sh, float3 direction) +{ + return float3( + SHHallucinateZH3Irradiance(sh.r, direction), + SHHallucinateZH3Irradiance(sh.g, direction), + SHHallucinateZH3Irradiance(sh.b, direction) + ); +} + +SH2Color SphericalHarmonics::DALCToSH(const float3 dalcColors[6]) +{ + SH2Color result; + + const float weight = (4.0f * 3.14159265358979323846f) / 6.0f; + + float3 dirs[6] = { + float3( 1, 0, 0), // X+ + float3(-1, 0, 0), // X- + float3( 0, 1, 0), // Y+ + float3( 0, -1, 0), // Y- + float3( 0, 0, 1), // Z+ + float3( 0, 0, -1) // Z- + }; + + for (int i = 0; i < 6; i++) + { + SH2 shBasis = Evaluate(dirs[i]); + result.r = Add(result.r, Scale(shBasis, dalcColors[i].x * weight)); + result.g = Add(result.g, Scale(shBasis, dalcColors[i].y * weight)); + result.b = Add(result.b, Scale(shBasis, dalcColors[i].z * weight)); + } + return result; +} \ No newline at end of file diff --git a/src/Utils/SphericalHarmonics.h b/src/Utils/SphericalHarmonics.h new file mode 100644 index 0000000000..6f81eb2af9 --- /dev/null +++ b/src/Utils/SphericalHarmonics.h @@ -0,0 +1,58 @@ +#pragma once +using float3x3 = DirectX::XMFLOAT3X3; + +namespace SphericalHarmonics +{ + struct SH2 + { + float c0; + float c1[3]; + + SH2() : c0(0.0f), c1{0.0f, 0.0f, 0.0f} {} + SH2(float _c0, float _c1_0, float _c1_1, float _c1_2) + : c0(_c0), c1{_c1_0, _c1_1, _c1_2} {} + }; + + struct SH2Color + { + SH2 r; + SH2 g; + SH2 b; + + SH2Color() : r(), g(), b() {} + + explicit SH2Color(const SH2& sh) : r(sh), g(sh), b(sh) {} + + SH2Color(SH2 _r, SH2 _g, SH2 _b) + : r(_r), g(_g), b(_b) {} + }; + + + + SH2 Evaluate(float3 dir); + float Dot(SH2 a, SH2 b); + float3 Dot(SH2Color a, SH2Color b); + float Unproject(float3 dir, SH2 sh); + float3 Unproject(float3 dir, SH2Color sh); + SH2 EvaluateCosineLobe(float3 dir); + SH2 EvaluatePhaseHG(float3 dir, float g); + SH2 Add(SH2 a, SH2 b); + SH2Color Add(SH2Color a, SH2Color b); + SH2 Scale(SH2 sh, float scale); + SH2Color Scale(SH2Color sh, float scale); + SH2 Rotate(SH2 sh, float3x3 rotMatrix); + SH2Color Rotate(SH2Color sh, float3x3 rotMatrix); + float FuncProductIntegral(SH2 shL, SH2 shR); + float3 FuncProductIntegral(SH2Color shL, SH2Color shR); + SH2 Product(SH2 shL, SH2 shR); + SH2Color Product(SH2Color shL, SH2Color shR); + SH2 HanningConvolution(SH2 sh, float w); + SH2Color HanningConvolution(SH2Color sh, float w); + SH2 DiffuseConvolution(SH2 sh); + SH2Color DiffuseConvolution(SH2Color sh); + SH2 FauxSpecularLobe(float3 N, float3 V, float roughness); + float SHHallucinateZH3Irradiance(SH2 sh, float3 direction); + float3 SHHallucinateZH3Irradiance(SH2Color sh, float3 direction); + + SH2Color DALCToSH(const float3 dalcColors[6]); +} \ No newline at end of file From 08d4c5a2c105309ebcc1f9eb6ab50a0eb3b9ef4e Mon Sep 17 00:00:00 2001 From: Jiaye Date: Fri, 16 Jan 2026 21:39:58 +0800 Subject: [PATCH 2/9] replace directional ambient color calculations with GetAmbient function --- .../Shaders/DynamicCubemaps/DynamicCubemaps.hlsli | 4 ++-- features/IBL/Shaders/IBL/IBL.hlsli | 2 +- package/Shaders/Common/ShadowSampling.hlsli | 2 +- package/Shaders/Common/SharedData.hlsli | 6 ++++++ package/Shaders/DeferredCompositeCS.hlsl | 4 ++-- package/Shaders/DistantTree.hlsl | 4 ++-- package/Shaders/Lighting.hlsl | 2 +- package/Shaders/Particle.hlsl | 2 +- package/Shaders/RunGrass.hlsl | 4 ++-- 9 files changed, 18 insertions(+), 12 deletions(-) diff --git a/features/Dynamic Cubemaps/Shaders/DynamicCubemaps/DynamicCubemaps.hlsli b/features/Dynamic Cubemaps/Shaders/DynamicCubemaps/DynamicCubemaps.hlsli index d0173669ea..3e06263bd7 100644 --- a/features/Dynamic Cubemaps/Shaders/DynamicCubemaps/DynamicCubemaps.hlsli +++ b/features/Dynamic Cubemaps/Shaders/DynamicCubemaps/DynamicCubemaps.hlsli @@ -42,7 +42,7 @@ namespace DynamicCubemaps float3 finalIrradiance = 0; float directionalAmbientColorSpecular = Color::RGBToLuminance(Color::Ambient( - max(0, mul(SharedData::DirectionalAmbient, float4(R, 1.0))))) * + max(0, SharedData::GetAmbient(R)))) * Color::ReflectionNormalisationScale; # if defined(IBL) && defined(LIGHTING) @@ -167,7 +167,7 @@ namespace DynamicCubemaps # else float3 finalIrradiance = 0; - float directionalAmbientColorSpecular = Color::RGBToLuminance(Color::Ambient(max(0, mul(SharedData::DirectionalAmbient, float4(R, 1.0))))) * Color::ReflectionNormalisationScale; + float directionalAmbientColorSpecular = Color::RGBToLuminance(Color::Ambient(max(0, SharedData::GetAmbient(R)))) * Color::ReflectionNormalisationScale; # if defined(IBL) && defined(LIGHTING) const bool inWorld = (Permutation::ExtraShaderDescriptor & Permutation::ExtraFlags::InWorld); diff --git a/features/IBL/Shaders/IBL/IBL.hlsli b/features/IBL/Shaders/IBL/IBL.hlsli index 95e1b3cb2e..d095afd4b5 100644 --- a/features/IBL/Shaders/IBL/IBL.hlsli +++ b/features/IBL/Shaders/IBL/IBL.hlsli @@ -71,7 +71,7 @@ namespace ImageBasedLighting float3 GetFogIBLColor(float3 fogColor) { - float3 directionalAmbientColor = max(0, mul(SharedData::DirectionalAmbient, float4(float3(0, 0, 0), 1.0))).xyz; + float3 directionalAmbientColor = max(0, SharedData::GetAmbient(float3(0, 0, 0))).xyz; float3 iblColor = directionalAmbientColor * SharedData::iblSettings.DALCAmount + Color::Saturation(GetSkyDiffuseIBL(float3(0, 0, 0)), SharedData::iblSettings.IBLSaturation) * SharedData::iblSettings.DiffuseIBLScale; if (SharedData::iblSettings.PreserveFogLuminance) { const float fogLuminance = Color::RGBToLuminance(fogColor); diff --git a/package/Shaders/Common/ShadowSampling.hlsli b/package/Shaders/Common/ShadowSampling.hlsli index 7e20540aa8..ef2b9031c2 100644 --- a/package/Shaders/Common/ShadowSampling.hlsli +++ b/package/Shaders/Common/ShadowSampling.hlsli @@ -107,7 +107,7 @@ namespace ShadowSampling void ExtractLighting(float3 inputColor, out float3 dirColor, out float3 ambientColor) #endif { - float3 ambientColorAmb = max(0, mul(SharedData::DirectionalAmbient, float4(0, 0, 1, 1))); + float3 ambientColorAmb = max(0, SharedData::GetAmbient(float3(0, 0, 1))); #if defined(IBL) if (SharedData::iblSettings.EnableDiffuseIBL && (!SharedData::InInterior || SharedData::iblSettings.EnableInterior)) { diff --git a/package/Shaders/Common/SharedData.hlsli b/package/Shaders/Common/SharedData.hlsli index e17442907e..867776818d 100644 --- a/package/Shaders/Common/SharedData.hlsli +++ b/package/Shaders/Common/SharedData.hlsli @@ -3,6 +3,7 @@ #include "Common/FrameBuffer.hlsli" #include "Common/VR.hlsli" +#include "Common/Spherical Harmonics/SphericalHarmonics.hlsli" namespace SharedData { @@ -329,6 +330,11 @@ namespace SharedData return waterData; } + float3 GetAmbient(float3 normal) + { + return SphericalHarmonics::Unproject(AmbientSHR, AmbientSHG, AmbientSHB, normal); + } + #endif // PSHADER } #endif // __SHARED_DATA_DEPENDENCY_HLSL__ diff --git a/package/Shaders/DeferredCompositeCS.hlsl b/package/Shaders/DeferredCompositeCS.hlsl index 4d2cd639c2..65b3deff76 100644 --- a/package/Shaders/DeferredCompositeCS.hlsl +++ b/package/Shaders/DeferredCompositeCS.hlsl @@ -118,7 +118,7 @@ void SampleSSGISpecular(uint2 pixCoord, sh2 lobe, out float ao, out float3 il, i float3 ssgiIl; SampleSSGI(dispatchID.xy, normalWS, ssgiAo, ssgiIl); - float3 directionalAmbientColor = Color::Ambient(max(0, mul(SharedData::DirectionalAmbient, float4(normalWS, 1.0)))); + float3 directionalAmbientColor = Color::Ambient(max(0, SharedData::GetAmbient(normalWS))); directionalAmbientColor *= albedo; directionalAmbientColor = Color::RGBToYCoCg(directionalAmbientColor); @@ -171,7 +171,7 @@ void SampleSSGISpecular(uint2 pixCoord, sh2 lobe, out float ao, out float3 il, i float3 finalIrradiance = 0; - float directionalAmbientColorSpecular = Color::RGBToLuminance(Color::Ambient(max(0, mul(SharedData::DirectionalAmbient, float4(R, 1.0))))) * Color::ReflectionNormalisationScale; + float directionalAmbientColorSpecular = Color::RGBToLuminance(Color::Ambient(max(0, SharedData::GetAmbient(R)))) * Color::ReflectionNormalisationScale; # if defined(INTERIOR) float3 specularIrradiance = EnvTexture.SampleLevel(LinearSampler, R, level); diff --git a/package/Shaders/DistantTree.hlsl b/package/Shaders/DistantTree.hlsl index 3c338350a5..c77337527e 100644 --- a/package/Shaders/DistantTree.hlsl +++ b/package/Shaders/DistantTree.hlsl @@ -244,7 +244,7 @@ PS_OUTPUT main(PS_INPUT input) float3 ddy = ddy_coarse(input.WorldPosition.xyz); float3 normal = -normalize(cross(ddx, ddy)); - float3 directionalAmbientColor = max(0, Color::Ambient(mul(SharedData::DirectionalAmbient, float4(normal, 1.0)))); + float3 directionalAmbientColor = max(0, Color::Ambient(SharedData::GetAmbient(normal))); # if defined(IBL) float3 iblColor = 0; if (SharedData::iblSettings.EnableDiffuseIBL) { @@ -285,7 +285,7 @@ PS_OUTPUT main(PS_INPUT input) float3 ddy = ddy_coarse(input.WorldPosition.xyz); float3 normal = normalize(cross(ddx, ddy)); - float3 directionalAmbientColor = Color::Ambient(mul(SharedData::DirectionalAmbient, float4(normal, 1.0))); + float3 directionalAmbientColor = Color::Ambient(SharedData::GetAmbient(normal)); # if defined(IBL) float3 iblColor = 0; if (SharedData::iblSettings.EnableDiffuseIBL) { diff --git a/package/Shaders/Lighting.hlsl b/package/Shaders/Lighting.hlsl index 0e2587f52b..f3a06c1dff 100644 --- a/package/Shaders/Lighting.hlsl +++ b/package/Shaders/Lighting.hlsl @@ -2749,7 +2749,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) } # endif - float3 directionalAmbientColor = Color::Ambient(max(0, mul(DirectionalAmbient, float4(ambientNormal, 1.0)))); + float3 directionalAmbientColor = Color::Ambient(max(0, SharedData::GetAmbient(ambientNormal))); # if defined(IBL) if (SharedData::iblSettings.EnableDiffuseIBL) { diff --git a/package/Shaders/Particle.hlsl b/package/Shaders/Particle.hlsl index bc8074bc18..c35f68861c 100644 --- a/package/Shaders/Particle.hlsl +++ b/package/Shaders/Particle.hlsl @@ -297,7 +297,7 @@ PS_OUTPUT main(PS_INPUT input) float unusedDetailedShadow; float3 dirLightColor = SharedData::DirLightColor.xyz * ShadowSampling::GetLightingShadow(positionWS.xyz, eyeIndex, unusedDetailedShadow); - float3 ambientColor = max(0, mul(SharedData::DirectionalAmbient, float4(0, 0, 1, 1)).xyz); + float3 ambientColor = max(0, SharedData::GetAmbient(float3(0, 0, 1))); propertyColor += dirLightColor; propertyColor += ambientColor; diff --git a/package/Shaders/RunGrass.hlsl b/package/Shaders/RunGrass.hlsl index e85b3c917c..d03273f611 100644 --- a/package/Shaders/RunGrass.hlsl +++ b/package/Shaders/RunGrass.hlsl @@ -754,7 +754,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) diffuseColor.xyz = Color::IrradianceToGamma(diffuseColor.xyz); # else - float3 directionalAmbientColor = Color::Ambient(max(0, mul(SharedData::DirectionalAmbient, float4(normal, 1.0)))); + float3 directionalAmbientColor = Color::Ambient(max(0, SharedData::GetAmbient(normal))); # if defined(IBL) if (SharedData::iblSettings.EnableDiffuseIBL && (!SharedData::InInterior || SharedData::iblSettings.EnableInterior)) { @@ -960,7 +960,7 @@ PS_OUTPUT main(PS_INPUT input) } # endif - float3 directionalAmbientColor = Color::Ambient(max(0, mul(SharedData::DirectionalAmbient, float4(normal, 1.0)))); + float3 directionalAmbientColor = Color::Ambient(max(0, SharedData::GetAmbient(normal))); # if defined(IBL) if (SharedData::iblSettings.EnableDiffuseIBL && (!SharedData::InInterior || SharedData::iblSettings.EnableInterior)) { From c17f679204534c07be5fff581098b8342c6bc1d4 Mon Sep 17 00:00:00 2001 From: Jiaye Date: Fri, 27 Feb 2026 02:10:31 +0800 Subject: [PATCH 3/9] format --- package/Shaders/Common/SharedData.hlsli | 2 +- src/State.cpp | 16 +- src/Utils/SphericalHarmonics.cpp | 343 ++++++++++++------------ src/Utils/SphericalHarmonics.h | 100 ++++--- 4 files changed, 227 insertions(+), 234 deletions(-) diff --git a/package/Shaders/Common/SharedData.hlsli b/package/Shaders/Common/SharedData.hlsli index 867776818d..e4afcabe32 100644 --- a/package/Shaders/Common/SharedData.hlsli +++ b/package/Shaders/Common/SharedData.hlsli @@ -2,8 +2,8 @@ #define __SHARED_DATA_DEPENDENCY_HLSL__ #include "Common/FrameBuffer.hlsli" -#include "Common/VR.hlsli" #include "Common/Spherical Harmonics/SphericalHarmonics.hlsli" +#include "Common/VR.hlsli" namespace SharedData { diff --git a/src/State.cpp b/src/State.cpp index 589875617a..9794ce9441 100644 --- a/src/State.cpp +++ b/src/State.cpp @@ -19,9 +19,9 @@ #include "ShaderCache.h" #include "TruePBR.h" #include "Utils/FileSystem.h" +#include "Utils/SphericalHarmonics.h" #include "WeatherManager.h" #include "WeatherVariableRegistry.h" -#include "Utils/SphericalHarmonics.h" void State::Draw() { @@ -852,14 +852,14 @@ void State::UpdateSharedData([[maybe_unused]] bool a_inWorld, [[maybe_unused]] b // DALC to SH const auto& m = dalcTransform.rotate; - const auto& t = dalcTransform.translate; + const auto& t = dalcTransform.translate; float3 dalcColors[6]; - dalcColors[0] = float3{ m.entry[0][0] + t.x, m.entry[1][0] + t.y, m.entry[2][0] + t.z }; // +X - dalcColors[1] = float3{ -m.entry[0][0] + t.x, -m.entry[1][0] + t.y, -m.entry[2][0] + t.z }; // -X - dalcColors[2] = float3{ m.entry[0][1] + t.x, m.entry[1][1] + t.y, m.entry[2][1] + t.z }; // +Y - dalcColors[3] = float3{ -m.entry[0][1] + t.x, -m.entry[1][1] + t.y, -m.entry[2][1] + t.z }; // -Y - dalcColors[4] = float3{ m.entry[0][2] + t.x, m.entry[1][2] + t.y, m.entry[2][2] + t.z }; // +Z - dalcColors[5] = float3{ -m.entry[0][2] + t.x, -m.entry[1][2] + t.y, -m.entry[2][2] + t.z }; // -Z + dalcColors[0] = float3{ m.entry[0][0] + t.x, m.entry[1][0] + t.y, m.entry[2][0] + t.z }; // +X + dalcColors[1] = float3{ -m.entry[0][0] + t.x, -m.entry[1][0] + t.y, -m.entry[2][0] + t.z }; // -X + dalcColors[2] = float3{ m.entry[0][1] + t.x, m.entry[1][1] + t.y, m.entry[2][1] + t.z }; // +Y + dalcColors[3] = float3{ -m.entry[0][1] + t.x, -m.entry[1][1] + t.y, -m.entry[2][1] + t.z }; // -Y + dalcColors[4] = float3{ m.entry[0][2] + t.x, m.entry[1][2] + t.y, m.entry[2][2] + t.z }; // +Z + dalcColors[5] = float3{ -m.entry[0][2] + t.x, -m.entry[1][2] + t.y, -m.entry[2][2] + t.z }; // -Z SphericalHarmonics::SH2Color dalcSH = SphericalHarmonics::DALCToSH(dalcColors); data.AmbientSHR = { dalcSH.r.c0, dalcSH.r.c1[0], dalcSH.r.c1[1], dalcSH.r.c1[2] }; diff --git a/src/Utils/SphericalHarmonics.cpp b/src/Utils/SphericalHarmonics.cpp index f0f28e0264..295e6bbc87 100644 --- a/src/Utils/SphericalHarmonics.cpp +++ b/src/Utils/SphericalHarmonics.cpp @@ -4,282 +4,279 @@ using namespace SphericalHarmonics; SH2 SphericalHarmonics::Evaluate(float3 dir) { - SH2 result; - result.c0 = 0.28209479177387814347403972578039f; // L=0 , M= 0 - result.c1[0] = -0.48860251190291992158638462283836f * dir.y; // L=1 , M=-1 - result.c1[1] = 0.48860251190291992158638462283836f * dir.z; // L=1 , M= 0 - result.c1[2] = -0.48860251190291992158638462283836f * dir.x; // L=1 , M= 1 - return result; + SH2 result; + result.c0 = 0.28209479177387814347403972578039f; // L=0 , M= 0 + result.c1[0] = -0.48860251190291992158638462283836f * dir.y; // L=1 , M=-1 + result.c1[1] = 0.48860251190291992158638462283836f * dir.z; // L=1 , M= 0 + result.c1[2] = -0.48860251190291992158638462283836f * dir.x; // L=1 , M= 1 + return result; } float SphericalHarmonics::Dot(SH2 a, SH2 b) { - float4 aVec = float4(a.c0, a.c1[0], a.c1[1], a.c1[2]); - float4 bVec = float4(b.c0, b.c1[0], b.c1[1], b.c1[2]); - return aVec.Dot(bVec); + float4 aVec = float4(a.c0, a.c1[0], a.c1[1], a.c1[2]); + float4 bVec = float4(b.c0, b.c1[0], b.c1[1], b.c1[2]); + return aVec.Dot(bVec); } float3 SphericalHarmonics::Dot(SH2Color a, SH2Color b) { - return float3( - Dot(a.r, b.r), - Dot(a.g, b.g), - Dot(a.b, b.b) - ); + return float3( + Dot(a.r, b.r), + Dot(a.g, b.g), + Dot(a.b, b.b)); } float SphericalHarmonics::Unproject(float3 dir, SH2 sh) { - SH2 basis = Evaluate(dir); - return Dot(sh, basis); + SH2 basis = Evaluate(dir); + return Dot(sh, basis); } float3 SphericalHarmonics::Unproject(float3 dir, SH2Color sh) { - return float3( - Unproject(dir, sh.r), - Unproject(dir, sh.g), - Unproject(dir, sh.b) - ); + return float3( + Unproject(dir, sh.r), + Unproject(dir, sh.g), + Unproject(dir, sh.b)); } SH2 SphericalHarmonics::EvaluateCosineLobe(float3 dir) { - SH2 result; - result.c0 = 0.8862269254527580137f; // L=0 , M= 0 - result.c1[0] = -1.0233267079464884885f * dir.y; // L=1 , M=-1 - result.c1[1] = 1.0233267079464884885f * dir.z; // L=1 , M= 0 - result.c1[2] = -1.0233267079464884885f * dir.x; // L=1 , M= 1 - return result; + SH2 result; + result.c0 = 0.8862269254527580137f; // L=0 , M= 0 + result.c1[0] = -1.0233267079464884885f * dir.y; // L=1 , M=-1 + result.c1[1] = 1.0233267079464884885f * dir.z; // L=1 , M= 0 + result.c1[2] = -1.0233267079464884885f * dir.x; // L=1 , M= 1 + return result; } SH2 SphericalHarmonics::EvaluatePhaseHG(float3 dir, float g) { - SH2 result; - const float factor = 0.48860251190291992158638462283836f * g; - result.c0 = 0.28209479177387814347403972578039f; // L=0 , M= 0 - result.c1[0] = -factor * dir.y; // L=1 , M=-1 - result.c1[1] = factor * dir.z; // L=1 , M= 0 - result.c1[2] = -factor * dir.x; // L=1 , M= 1 - return result; + SH2 result; + const float factor = 0.48860251190291992158638462283836f * g; + result.c0 = 0.28209479177387814347403972578039f; // L=0 , M= 0 + result.c1[0] = -factor * dir.y; // L=1 , M=-1 + result.c1[1] = factor * dir.z; // L=1 , M= 0 + result.c1[2] = -factor * dir.x; // L=1 , M= 1 + return result; } SH2 SphericalHarmonics::Add(SH2 a, SH2 b) { - SH2 result; - result.c0 = a.c0 + b.c0; - result.c1[0] = a.c1[0] + b.c1[0]; - result.c1[1] = a.c1[1] + b.c1[1]; - result.c1[2] = a.c1[2] + b.c1[2]; - return result; + SH2 result; + result.c0 = a.c0 + b.c0; + result.c1[0] = a.c1[0] + b.c1[0]; + result.c1[1] = a.c1[1] + b.c1[1]; + result.c1[2] = a.c1[2] + b.c1[2]; + return result; } SH2Color SphericalHarmonics::Add(SH2Color a, SH2Color b) { - SH2Color result; - result.r = Add(a.r, b.r); - result.g = Add(a.g, b.g); - result.b = Add(a.b, b.b); - return result; + SH2Color result; + result.r = Add(a.r, b.r); + result.g = Add(a.g, b.g); + result.b = Add(a.b, b.b); + return result; } SH2 SphericalHarmonics::Scale(SH2 sh, float scale) { - SH2 result; - result.c0 = sh.c0 * scale; - result.c1[0] = sh.c1[0] * scale; - result.c1[1] = sh.c1[1] * scale; - result.c1[2] = sh.c1[2] * scale; - return result; + SH2 result; + result.c0 = sh.c0 * scale; + result.c1[0] = sh.c1[0] * scale; + result.c1[1] = sh.c1[1] * scale; + result.c1[2] = sh.c1[2] * scale; + return result; } SH2Color SphericalHarmonics::Scale(SH2Color sh, float scale) { - SH2Color result; - result.r = Scale(sh.r, scale); - result.g = Scale(sh.g, scale); - result.b = Scale(sh.b, scale); - return result; + SH2Color result; + result.r = Scale(sh.r, scale); + result.g = Scale(sh.g, scale); + result.b = Scale(sh.b, scale); + return result; } SH2 SphericalHarmonics::Rotate(SH2 sh, float3x3 rotMatrix) { - SH2 result; - result.c0 = sh.c0; - - float3 c1Vec = float3(sh.c1[0], sh.c1[1], sh.c1[2]); - DirectX::XMVECTOR v = XMLoadFloat3(&c1Vec); - v = DirectX::XMVector3Transform(v, XMLoadFloat3x3(&rotMatrix)); - DirectX::XMStoreFloat3(&c1Vec, v); - - result.c1[0] = c1Vec.x; - result.c1[1] = c1Vec.y; - result.c1[2] = c1Vec.z; - return result; + SH2 result; + result.c0 = sh.c0; + + float3 c1Vec = float3(sh.c1[0], sh.c1[1], sh.c1[2]); + DirectX::XMVECTOR v = XMLoadFloat3(&c1Vec); + v = DirectX::XMVector3Transform(v, XMLoadFloat3x3(&rotMatrix)); + DirectX::XMStoreFloat3(&c1Vec, v); + + result.c1[0] = c1Vec.x; + result.c1[1] = c1Vec.y; + result.c1[2] = c1Vec.z; + return result; } SH2Color SphericalHarmonics::Rotate(SH2Color sh, float3x3 rotMatrix) { - SH2Color result; - result.r = Rotate(sh.r, rotMatrix); - result.g = Rotate(sh.g, rotMatrix); - result.b = Rotate(sh.b, rotMatrix); - return result; + SH2Color result; + result.r = Rotate(sh.r, rotMatrix); + result.g = Rotate(sh.g, rotMatrix); + result.b = Rotate(sh.b, rotMatrix); + return result; } float SphericalHarmonics::FuncProductIntegral(SH2 shL, SH2 shR) { - return Dot(shL, shR); + return Dot(shL, shR); } float3 SphericalHarmonics::FuncProductIntegral(SH2Color shL, SH2Color shR) { - return float3( - FuncProductIntegral(shL.r, shR.r), - FuncProductIntegral(shL.g, shR.g), - FuncProductIntegral(shL.b, shR.b) - ); + return float3( + FuncProductIntegral(shL.r, shR.r), + FuncProductIntegral(shL.g, shR.g), + FuncProductIntegral(shL.b, shR.b)); } SH2 SphericalHarmonics::Product(SH2 shL, SH2 shR) { - const float factor = 1.0f / (2.0f * sqrt(3.14159265358979323846f)); - SH2 result; - result.c0 = factor * Dot(shL, shR); - result.c1[0] = factor * (shL.c1[0] * shR.c1[2] + shL.c1[2] * shR.c1[0]); - result.c1[1] = factor * (shL.c1[1] * shR.c1[2] + shL.c1[2] * shR.c1[1]); - result.c1[2] = factor * (shL.c1[2] * shR.c1[2] + shL.c1[2] * shR.c1[2]); - return result; + const float factor = 1.0f / (2.0f * sqrt(3.14159265358979323846f)); + SH2 result; + result.c0 = factor * Dot(shL, shR); + result.c1[0] = factor * (shL.c1[0] * shR.c1[2] + shL.c1[2] * shR.c1[0]); + result.c1[1] = factor * (shL.c1[1] * shR.c1[2] + shL.c1[2] * shR.c1[1]); + result.c1[2] = factor * (shL.c1[2] * shR.c1[2] + shL.c1[2] * shR.c1[2]); + return result; } SH2Color SphericalHarmonics::Product(SH2Color shL, SH2Color shR) { - SH2Color result; - result.r = Product(shL.r, shR.r); - result.g = Product(shL.g, shR.g); - result.b = Product(shL.b, shR.b); - return result; + SH2Color result; + result.r = Product(shL.r, shR.r); + result.g = Product(shL.g, shR.g); + result.b = Product(shL.b, shR.b); + return result; } SH2 SphericalHarmonics::HanningConvolution(SH2 sh, float w) { - SH2 result; - float invW = 1.0f / w; + SH2 result; + float invW = 1.0f / w; float factorBand1 = (1.0f + cos(3.14159265358979323846f * invW)) / 2.0f; - result.c0 = sh.c0; - result.c1[0] = sh.c1[0] * factorBand1; - result.c1[1] = sh.c1[1] * factorBand1; - result.c1[2] = sh.c1[2] * factorBand1; - return result; + result.c0 = sh.c0; + result.c1[0] = sh.c1[0] * factorBand1; + result.c1[1] = sh.c1[1] * factorBand1; + result.c1[2] = sh.c1[2] * factorBand1; + return result; } SH2Color SphericalHarmonics::HanningConvolution(SH2Color sh, float w) { - SH2Color result; - result.r = HanningConvolution(sh.r, w); - result.g = HanningConvolution(sh.g, w); - result.b = HanningConvolution(sh.b, w); - return result; + SH2Color result; + result.r = HanningConvolution(sh.r, w); + result.g = HanningConvolution(sh.g, w); + result.b = HanningConvolution(sh.b, w); + return result; } SH2 SphericalHarmonics::DiffuseConvolution(SH2 sh) { - SH2 result = sh; - result.c0 *= 3.14159265358979323846f; - result.c1[0] *= 2.0943951023931954923f; - result.c1[1] *= 2.0943951023931954923f; - result.c1[2] *= 2.0943951023931954923f; - return result; + SH2 result = sh; + result.c0 *= 3.14159265358979323846f; + result.c1[0] *= 2.0943951023931954923f; + result.c1[1] *= 2.0943951023931954923f; + result.c1[2] *= 2.0943951023931954923f; + return result; } SH2Color SphericalHarmonics::DiffuseConvolution(SH2Color sh) { - SH2Color result; - result.r = DiffuseConvolution(sh.r); - result.g = DiffuseConvolution(sh.g); - result.b = DiffuseConvolution(sh.b); - return result; + SH2Color result; + result.r = DiffuseConvolution(sh.r); + result.g = DiffuseConvolution(sh.g); + result.b = DiffuseConvolution(sh.b); + return result; } template -static T reflect(const T& i, const T& n) { - return i - n * (2.0f * i.Dot(n)); +static T reflect(const T& i, const T& n) +{ + return i - n * (2.0f * i.Dot(n)); } template -static T normalize(const T& v) { - return v * (1.0f / sqrtf(v.Dot(v))); +static T normalize(const T& v) +{ + return v * (1.0f / sqrtf(v.Dot(v))); } // Author: ProfJack // Constructs the SH of an approximate specular lobe SH2 SphericalHarmonics::FauxSpecularLobe(float3 N, float3 V, float roughness) { - // https://www.gdcvault.com/play/1026701/Fast-Denoising-With-Self-Stabilizing - // get dominant ggx reflection direction - float f = (1 - roughness) * (sqrt(1 - roughness) + roughness); - float3 R = reflect(-V, N); - float3 D = R * f + N * (1 - f); - float3 dominantDir = normalize(D); - - // lobe half angle - // credit: Olivier Therrien - float roughness2 = roughness * roughness; - float halfAngle = std::clamp(4.1679f * roughness2 * roughness2 - 9.0127f * roughness2 * roughness + 4.6161f * roughness2 + 1.7048f * roughness + 0.1f, 0.0f, 3.14159265358979323846f / 2.0f); - float lerpFactor = halfAngle / (3.14159265358979323846f / 2.0f); - SH2 directional = Evaluate(dominantDir); - SH2 cosineLobe = Scale(EvaluateCosineLobe(dominantDir), 1.0 / 3.14159265358979323846f); - return Add(Scale(directional, lerpFactor), Scale(cosineLobe, 1.0f - lerpFactor)); + // https://www.gdcvault.com/play/1026701/Fast-Denoising-With-Self-Stabilizing + // get dominant ggx reflection direction + float f = (1 - roughness) * (sqrt(1 - roughness) + roughness); + float3 R = reflect(-V, N); + float3 D = R * f + N * (1 - f); + float3 dominantDir = normalize(D); + + // lobe half angle + // credit: Olivier Therrien + float roughness2 = roughness * roughness; + float halfAngle = std::clamp(4.1679f * roughness2 * roughness2 - 9.0127f * roughness2 * roughness + 4.6161f * roughness2 + 1.7048f * roughness + 0.1f, 0.0f, 3.14159265358979323846f / 2.0f); + float lerpFactor = halfAngle / (3.14159265358979323846f / 2.0f); + SH2 directional = Evaluate(dominantDir); + SH2 cosineLobe = Scale(EvaluateCosineLobe(dominantDir), 1.0 / 3.14159265358979323846f); + return Add(Scale(directional, lerpFactor), Scale(cosineLobe, 1.0f - lerpFactor)); } float SphericalHarmonics::SHHallucinateZH3Irradiance(SH2 sh, float3 direction) { - float3 zonalAxis = normalize(float3(sh.c1[2], sh.c1[0], sh.c1[1])); - float ratio = 0.0; - ratio = abs(zonalAxis.Dot(float3(-sh.c1[2], -sh.c1[0], sh.c1[1]))); - ratio /= sh.c0; - float zonalL2Coeff = sh.c0 * (0.08f * ratio + 0.6f * ratio * ratio); // Curve-fit; Section 3.4.3 - float fZ = zonalAxis.Dot(direction); - float zhDir = sqrt(5.0f / (16.0f * 3.14159265358979323846f)) * (3.0f * fZ * fZ - 1.0f); - // Convolve inSH with the normalized cosine kernel (multiply the L1 band by the zonal scale 2/3), then dot with - // inSH(direction) for linear inSH (Equation 5). - float result = SphericalHarmonics::FuncProductIntegral(sh, SphericalHarmonics::EvaluateCosineLobe(direction)); - // Add irradiance from the ZH3 term. zonalL2Coeff is the ZH3 coefficient for a radiance signal, so we need to - // multiply by 1/4 (the L2 zonal scale for a normalized clamped cosine kernel) to evaluate irradiance. - result += 0.25f * zonalL2Coeff * zhDir; - return std::max(0.0f, result); + float3 zonalAxis = normalize(float3(sh.c1[2], sh.c1[0], sh.c1[1])); + float ratio = 0.0; + ratio = abs(zonalAxis.Dot(float3(-sh.c1[2], -sh.c1[0], sh.c1[1]))); + ratio /= sh.c0; + float zonalL2Coeff = sh.c0 * (0.08f * ratio + 0.6f * ratio * ratio); // Curve-fit; Section 3.4.3 + float fZ = zonalAxis.Dot(direction); + float zhDir = sqrt(5.0f / (16.0f * 3.14159265358979323846f)) * (3.0f * fZ * fZ - 1.0f); + // Convolve inSH with the normalized cosine kernel (multiply the L1 band by the zonal scale 2/3), then dot with + // inSH(direction) for linear inSH (Equation 5). + float result = SphericalHarmonics::FuncProductIntegral(sh, SphericalHarmonics::EvaluateCosineLobe(direction)); + // Add irradiance from the ZH3 term. zonalL2Coeff is the ZH3 coefficient for a radiance signal, so we need to + // multiply by 1/4 (the L2 zonal scale for a normalized clamped cosine kernel) to evaluate irradiance. + result += 0.25f * zonalL2Coeff * zhDir; + return std::max(0.0f, result); } float3 SphericalHarmonics::SHHallucinateZH3Irradiance(SH2Color sh, float3 direction) { - return float3( - SHHallucinateZH3Irradiance(sh.r, direction), - SHHallucinateZH3Irradiance(sh.g, direction), - SHHallucinateZH3Irradiance(sh.b, direction) - ); + return float3( + SHHallucinateZH3Irradiance(sh.r, direction), + SHHallucinateZH3Irradiance(sh.g, direction), + SHHallucinateZH3Irradiance(sh.b, direction)); } SH2Color SphericalHarmonics::DALCToSH(const float3 dalcColors[6]) { - SH2Color result; - - const float weight = (4.0f * 3.14159265358979323846f) / 6.0f; - - float3 dirs[6] = { - float3( 1, 0, 0), // X+ - float3(-1, 0, 0), // X- - float3( 0, 1, 0), // Y+ - float3( 0, -1, 0), // Y- - float3( 0, 0, 1), // Z+ - float3( 0, 0, -1) // Z- - }; - - for (int i = 0; i < 6; i++) - { - SH2 shBasis = Evaluate(dirs[i]); - result.r = Add(result.r, Scale(shBasis, dalcColors[i].x * weight)); - result.g = Add(result.g, Scale(shBasis, dalcColors[i].y * weight)); - result.b = Add(result.b, Scale(shBasis, dalcColors[i].z * weight)); - } - return result; + SH2Color result; + + const float weight = (4.0f * 3.14159265358979323846f) / 6.0f; + + float3 dirs[6] = { + float3(1, 0, 0), // X+ + float3(-1, 0, 0), // X- + float3(0, 1, 0), // Y+ + float3(0, -1, 0), // Y- + float3(0, 0, 1), // Z+ + float3(0, 0, -1) // Z- + }; + + for (int i = 0; i < 6; i++) { + SH2 shBasis = Evaluate(dirs[i]); + result.r = Add(result.r, Scale(shBasis, dalcColors[i].x * weight)); + result.g = Add(result.g, Scale(shBasis, dalcColors[i].y * weight)); + result.b = Add(result.b, Scale(shBasis, dalcColors[i].z * weight)); + } + return result; } \ No newline at end of file diff --git a/src/Utils/SphericalHarmonics.h b/src/Utils/SphericalHarmonics.h index 6f81eb2af9..becfad26fa 100644 --- a/src/Utils/SphericalHarmonics.h +++ b/src/Utils/SphericalHarmonics.h @@ -3,56 +3,52 @@ using float3x3 = DirectX::XMFLOAT3X3; namespace SphericalHarmonics { - struct SH2 - { - float c0; - float c1[3]; - - SH2() : c0(0.0f), c1{0.0f, 0.0f, 0.0f} {} - SH2(float _c0, float _c1_0, float _c1_1, float _c1_2) - : c0(_c0), c1{_c1_0, _c1_1, _c1_2} {} - }; - - struct SH2Color - { - SH2 r; - SH2 g; - SH2 b; - - SH2Color() : r(), g(), b() {} - - explicit SH2Color(const SH2& sh) : r(sh), g(sh), b(sh) {} - - SH2Color(SH2 _r, SH2 _g, SH2 _b) - : r(_r), g(_g), b(_b) {} - }; - - - - SH2 Evaluate(float3 dir); - float Dot(SH2 a, SH2 b); - float3 Dot(SH2Color a, SH2Color b); - float Unproject(float3 dir, SH2 sh); - float3 Unproject(float3 dir, SH2Color sh); - SH2 EvaluateCosineLobe(float3 dir); - SH2 EvaluatePhaseHG(float3 dir, float g); - SH2 Add(SH2 a, SH2 b); - SH2Color Add(SH2Color a, SH2Color b); - SH2 Scale(SH2 sh, float scale); - SH2Color Scale(SH2Color sh, float scale); - SH2 Rotate(SH2 sh, float3x3 rotMatrix); - SH2Color Rotate(SH2Color sh, float3x3 rotMatrix); - float FuncProductIntegral(SH2 shL, SH2 shR); - float3 FuncProductIntegral(SH2Color shL, SH2Color shR); - SH2 Product(SH2 shL, SH2 shR); - SH2Color Product(SH2Color shL, SH2Color shR); - SH2 HanningConvolution(SH2 sh, float w); - SH2Color HanningConvolution(SH2Color sh, float w); - SH2 DiffuseConvolution(SH2 sh); - SH2Color DiffuseConvolution(SH2Color sh); - SH2 FauxSpecularLobe(float3 N, float3 V, float roughness); - float SHHallucinateZH3Irradiance(SH2 sh, float3 direction); - float3 SHHallucinateZH3Irradiance(SH2Color sh, float3 direction); - - SH2Color DALCToSH(const float3 dalcColors[6]); + struct SH2 + { + float c0; + float c1[3]; + + SH2() : c0(0.0f), c1{ 0.0f, 0.0f, 0.0f } {} + SH2(float _c0, float _c1_0, float _c1_1, float _c1_2) : c0(_c0), c1{ _c1_0, _c1_1, _c1_2 } {} + }; + + struct SH2Color + { + SH2 r; + SH2 g; + SH2 b; + + SH2Color() : r(), g(), b() {} + + explicit SH2Color(const SH2& sh) : r(sh), g(sh), b(sh) {} + + SH2Color(SH2 _r, SH2 _g, SH2 _b) : r(_r), g(_g), b(_b) {} + }; + + SH2 Evaluate(float3 dir); + float Dot(SH2 a, SH2 b); + float3 Dot(SH2Color a, SH2Color b); + float Unproject(float3 dir, SH2 sh); + float3 Unproject(float3 dir, SH2Color sh); + SH2 EvaluateCosineLobe(float3 dir); + SH2 EvaluatePhaseHG(float3 dir, float g); + SH2 Add(SH2 a, SH2 b); + SH2Color Add(SH2Color a, SH2Color b); + SH2 Scale(SH2 sh, float scale); + SH2Color Scale(SH2Color sh, float scale); + SH2 Rotate(SH2 sh, float3x3 rotMatrix); + SH2Color Rotate(SH2Color sh, float3x3 rotMatrix); + float FuncProductIntegral(SH2 shL, SH2 shR); + float3 FuncProductIntegral(SH2Color shL, SH2Color shR); + SH2 Product(SH2 shL, SH2 shR); + SH2Color Product(SH2Color shL, SH2Color shR); + SH2 HanningConvolution(SH2 sh, float w); + SH2Color HanningConvolution(SH2Color sh, float w); + SH2 DiffuseConvolution(SH2 sh); + SH2Color DiffuseConvolution(SH2Color sh); + SH2 FauxSpecularLobe(float3 N, float3 V, float roughness); + float SHHallucinateZH3Irradiance(SH2 sh, float3 direction); + float3 SHHallucinateZH3Irradiance(SH2Color sh, float3 direction); + + SH2Color DALCToSH(const float3 dalcColors[6]); } \ No newline at end of file From a3e36aa98d22b8cafb01e83c331009e0309c37cd Mon Sep 17 00:00:00 2001 From: Jiaye Date: Sat, 28 Feb 2026 02:46:56 +0800 Subject: [PATCH 4/9] feat(ibl): revamped ibl --- .../DynamicCubemaps/DynamicCubemaps.hlsli | 278 ++++++++---------- features/IBL/Shaders/IBL/IBL.hlsli | 101 +++++-- package/Shaders/Common/ShadowSampling.hlsli | 17 +- package/Shaders/Common/SharedData.hlsli | 9 +- package/Shaders/DeferredCompositeCS.hlsl | 125 ++++---- package/Shaders/DistantTree.hlsl | 32 +- package/Shaders/Effect.hlsl | 2 +- package/Shaders/ISSAOComposite.hlsl | 2 +- package/Shaders/Lighting.hlsl | 29 +- package/Shaders/RunGrass.hlsl | 56 ++-- package/Shaders/Water.hlsl | 6 +- src/Features/IBL.cpp | 77 +++-- src/Features/IBL.h | 10 +- 13 files changed, 394 insertions(+), 350 deletions(-) diff --git a/features/Dynamic Cubemaps/Shaders/DynamicCubemaps/DynamicCubemaps.hlsli b/features/Dynamic Cubemaps/Shaders/DynamicCubemaps/DynamicCubemaps.hlsli index 3e06263bd7..0d374e6b71 100644 --- a/features/Dynamic Cubemaps/Shaders/DynamicCubemaps/DynamicCubemaps.hlsli +++ b/features/Dynamic Cubemaps/Shaders/DynamicCubemaps/DynamicCubemaps.hlsli @@ -56,90 +56,78 @@ namespace DynamicCubemaps # endif # if defined(SKYLIGHTING) - if (SharedData::InInterior) { -# if defined(IBL) - float3 iblColor = 0; - if (SharedData::iblSettings.EnableDiffuseIBL && SharedData::iblSettings.EnableInterior) { - directionalAmbientColorSpecular *= SharedData::iblSettings.DALCAmount; - iblColor += Color::Saturation(ImageBasedLighting::GetIBLColor(-R, 0), SharedData::iblSettings.IBLSaturation) * SharedData::iblSettings.DiffuseIBLScale; - float iblColorLuminance = Color::RGBToLuminance(Color::IrradianceToGamma(iblColor)); - directionalAmbientColorSpecular += iblColorLuminance; - } -# endif - float3 specularIrradiance = EnvTexture.SampleLevel(SampColorSampler, R, level).xyz; - - float specularIrradianceLuminance = Color::RGBToLuminance(EnvTexture.SampleLevel(SampColorSampler, R, 15)); - - specularIrradiance = (specularIrradiance / max(specularIrradianceLuminance, 0.001)) * directionalAmbientColorSpecular; - specularIrradiance = Color::IrradianceToLinear(specularIrradiance); - - finalIrradiance = specularIrradiance; - return finalIrradiance; + float skylightingSpecular = 1.0; + if (!SharedData::InInterior) { + sh2 specularLobe = SphericalHarmonics::FauxSpecularLobe(N, -V, roughness); + skylightingSpecular = SphericalHarmonics::FuncProductIntegral(skylighting, specularLobe); + skylightingSpecular = saturate(skylightingSpecular); + skylightingSpecular = Skylighting::mixSpecular(SharedData::skylightingSettings, skylightingSpecular); + } else { + skylightingSpecular = 0.0; } +# endif - sh2 specularLobe = SphericalHarmonics::FauxSpecularLobe(N, -V, roughness); - - float skylightingSpecular = SphericalHarmonics::FuncProductIntegral(skylighting, specularLobe); - skylightingSpecular = saturate(skylightingSpecular); - skylightingSpecular = Skylighting::mixSpecular(SharedData::skylightingSettings, skylightingSpecular); - -# if defined(IBL) - float3 iblColor = 0; +# if defined(IBL) if (SharedData::iblSettings.EnableDiffuseIBL) { - directionalAmbientColorSpecular *= SharedData::iblSettings.DALCAmount; - iblColor += Color::Saturation(ImageBasedLighting::GetIBLColor(-R, skylightingSpecular), SharedData::iblSettings.IBLSaturation) * SharedData::iblSettings.DiffuseIBLScale; - float iblColorLuminance = Color::RGBToLuminance(Color::IrradianceToGamma(iblColor)); - directionalAmbientColorSpecular += iblColorLuminance; - } + float3 envSample = EnvTexture.SampleLevel(SampColorSampler, R, level); + float3 fullSample = EnvReflectionsTexture.SampleLevel(SampColorSampler, R, level); + float3 envSpecular, skySpecular; + + if (SharedData::iblSettings.DALCMode == 2) { + // Mode 2: DALC-normalized env + sky overlay + float envLum = Color::RGBToLuminance(EnvTexture.SampleLevel(SampColorSampler, R, 15)); + envSpecular = Color::IrradianceToLinear((envSample / max(envLum, 0.001)) * directionalAmbientColorSpecular); + skySpecular = Color::IrradianceToLinear(max(0, fullSample - envSample)) * SharedData::iblSettings.SkyIBLScale; +# if defined(SKYLIGHTING) + skySpecular *= skylightingSpecular; # endif + } else { + // Mode 0/1: IBL ratio-based + float3 ratio = ImageBasedLighting::GetIBLRatio(); + envSpecular = Color::IrradianceToLinear(envSample * ratio) * SharedData::iblSettings.EnvIBLScale; + skySpecular = Color::IrradianceToLinear(max(0, fullSample - envSample)) * SharedData::iblSettings.SkyIBLScale; +# if defined(SKYLIGHTING) + skySpecular *= skylightingSpecular; +# endif + } - float3 specularIrradianceReflections = 0.0; - - if (skylightingSpecular > 0.0) { - specularIrradianceReflections = EnvReflectionsTexture.SampleLevel(SampColorSampler, R, level); - - float specularIrradianceReflectionsLuminance = Color::RGBToLuminance(EnvReflectionsTexture.SampleLevel(SampColorSampler, R, 15)); - - specularIrradianceReflections = (specularIrradianceReflections / max(specularIrradianceReflectionsLuminance, 0.001)) * directionalAmbientColorSpecular; - specularIrradianceReflections = Color::IrradianceToLinear(specularIrradianceReflections); - } - - float3 specularIrradiance = 0.0; - - if (skylightingSpecular < 1.0) { - specularIrradiance = EnvTexture.SampleLevel(SampColorSampler, R, level); - - float specularIrradianceLuminance = Color::RGBToLuminance(EnvTexture.SampleLevel(SampColorSampler, R, 15)); - - directionalAmbientColorSpecular = Color::IrradianceToLinear(directionalAmbientColorSpecular); - directionalAmbientColorSpecular *= skylightingSpecular; - directionalAmbientColorSpecular = Color::IrradianceToGamma(directionalAmbientColorSpecular); - - specularIrradiance = (specularIrradiance / max(specularIrradianceLuminance, 0.001)) * directionalAmbientColorSpecular; - specularIrradiance = Color::IrradianceToLinear(specularIrradiance); - } - - finalIrradiance = lerp(specularIrradiance, specularIrradianceReflections, skylightingSpecular); + finalIrradiance = envSpecular + skySpecular; + } else +# endif + { + // Fallback without IBL: normalize-by-luminance with DALC +# if defined(SKYLIGHTING) + if (SharedData::InInterior) { + float3 specularIrradiance = EnvTexture.SampleLevel(SampColorSampler, R, level); + float specularIrradianceLuminance = Color::RGBToLuminance(EnvTexture.SampleLevel(SampColorSampler, R, 15)); + specularIrradiance = (specularIrradiance / max(specularIrradianceLuminance, 0.001)) * directionalAmbientColorSpecular; + finalIrradiance = Color::IrradianceToLinear(specularIrradiance); + } else { + float3 specularIrradianceReflections = 0.0; + if (skylightingSpecular > 0.0) { + specularIrradianceReflections = EnvReflectionsTexture.SampleLevel(SampColorSampler, R, level); + float lum = Color::RGBToLuminance(EnvReflectionsTexture.SampleLevel(SampColorSampler, R, 15)); + specularIrradianceReflections = (specularIrradianceReflections / max(lum, 0.001)) * directionalAmbientColorSpecular; + specularIrradianceReflections = Color::IrradianceToLinear(specularIrradianceReflections); + } + float3 specularIrradiance = 0.0; + if (skylightingSpecular < 1.0) { + specularIrradiance = EnvTexture.SampleLevel(SampColorSampler, R, level); + float lum = Color::RGBToLuminance(EnvTexture.SampleLevel(SampColorSampler, R, 15)); + float dalcScaled = Color::IrradianceToGamma(Color::IrradianceToLinear(directionalAmbientColorSpecular) * skylightingSpecular); + specularIrradiance = (specularIrradiance / max(lum, 0.001)) * dalcScaled; + specularIrradiance = Color::IrradianceToLinear(specularIrradiance); + } + finalIrradiance = lerp(specularIrradiance, specularIrradianceReflections, skylightingSpecular); + } # else -# if defined(IBL) - float3 iblColor = 0; - if (SharedData::iblSettings.EnableDiffuseIBL) { - directionalAmbientColorSpecular *= SharedData::iblSettings.DALCAmount; - iblColor += Color::Saturation(ImageBasedLighting::GetIBLColor(-R), SharedData::iblSettings.IBLSaturation) * SharedData::iblSettings.DiffuseIBLScale; - float iblColorLuminance = Color::RGBToLuminance(Color::IrradianceToGamma(iblColor)); - directionalAmbientColorSpecular += iblColorLuminance; + float3 specularIrradiance = EnvReflectionsTexture.SampleLevel(SampColorSampler, R, level); + float specularIrradianceLuminance = Color::RGBToLuminance(EnvReflectionsTexture.SampleLevel(SampColorSampler, R, 15)); + specularIrradiance = (specularIrradiance / max(specularIrradianceLuminance, 0.001)) * directionalAmbientColorSpecular; + finalIrradiance = Color::IrradianceToLinear(specularIrradiance); +# endif } -# endif - float3 specularIrradiance = EnvReflectionsTexture.SampleLevel(SampColorSampler, R, level); - - float specularIrradianceLuminance = Color::RGBToLuminance(EnvReflectionsTexture.SampleLevel(SampColorSampler, R, 15)); - - specularIrradiance = (specularIrradiance / max(specularIrradianceLuminance, 0.001)) * directionalAmbientColorSpecular; - specularIrradiance = Color::IrradianceToLinear(specularIrradiance); - - finalIrradiance = specularIrradiance; -# endif return finalIrradiance; # endif } @@ -180,88 +168,78 @@ namespace DynamicCubemaps # endif # if defined(SKYLIGHTING) - if (SharedData::InInterior) { -# if defined(IBL) - float3 iblColor = 0; - if (SharedData::iblSettings.EnableDiffuseIBL && SharedData::iblSettings.EnableInterior) { - directionalAmbientColorSpecular *= SharedData::iblSettings.DALCAmount; - iblColor += Color::Saturation(ImageBasedLighting::GetIBLColor(-R, 0), SharedData::iblSettings.IBLSaturation) * SharedData::iblSettings.DiffuseIBLScale; - float iblColorLuminance = Color::RGBToLuminance(Color::IrradianceToGamma(iblColor)); - directionalAmbientColorSpecular += iblColorLuminance; - } -# endif - float3 specularIrradiance = EnvTexture.SampleLevel(SampColorSampler, R, level).xyz; - - float specularIrradianceLuminance = Color::RGBToLuminance(EnvTexture.SampleLevel(SampColorSampler, R, 15)); - - specularIrradiance = (specularIrradiance / max(specularIrradianceLuminance, 0.001)) * directionalAmbientColorSpecular; - specularIrradiance = Color::IrradianceToLinear(specularIrradiance); - - finalIrradiance = specularIrradiance; - return horizon * (F0 * specularBRDF.x + specularBRDF.y) * finalIrradiance; + float skylightingSpecular = 1.0; + if (!SharedData::InInterior) { + sh2 specularLobe = SphericalHarmonics::FauxSpecularLobe(N, -V, roughness); + skylightingSpecular = SphericalHarmonics::FuncProductIntegral(skylighting, specularLobe); + skylightingSpecular = saturate(skylightingSpecular); + skylightingSpecular = Skylighting::mixSpecular(SharedData::skylightingSettings, skylightingSpecular); + } else { + skylightingSpecular = 0.0; } +# endif - sh2 specularLobe = SphericalHarmonics::FauxSpecularLobe(N, -V, roughness); - - float skylightingSpecular = SphericalHarmonics::FuncProductIntegral(skylighting, specularLobe); - skylightingSpecular = saturate(skylightingSpecular); - skylightingSpecular = Skylighting::mixSpecular(SharedData::skylightingSettings, skylightingSpecular); - -# if defined(IBL) - float3 iblColor = 0; +# if defined(IBL) if (SharedData::iblSettings.EnableDiffuseIBL) { - directionalAmbientColorSpecular *= SharedData::iblSettings.DALCAmount; - iblColor += Color::Saturation(ImageBasedLighting::GetIBLColor(-R, skylightingSpecular), SharedData::iblSettings.IBLSaturation) * SharedData::iblSettings.DiffuseIBLScale; - float iblColorLuminance = Color::RGBToLuminance(Color::IrradianceToGamma(iblColor)); - directionalAmbientColorSpecular += iblColorLuminance; - } + float3 envSample = EnvTexture.SampleLevel(SampColorSampler, R, level); + float3 fullSample = EnvReflectionsTexture.SampleLevel(SampColorSampler, R, level); + float3 envSpecular, skySpecular; + + if (SharedData::iblSettings.DALCMode == 2) { + // Mode 2: DALC-normalized env + sky overlay + float envLum = Color::RGBToLuminance(EnvTexture.SampleLevel(SampColorSampler, R, 15)); + envSpecular = Color::IrradianceToLinear((envSample / max(envLum, 0.001)) * directionalAmbientColorSpecular); + skySpecular = Color::IrradianceToLinear(max(0, fullSample - envSample)) * SharedData::iblSettings.SkyIBLScale; +# if defined(SKYLIGHTING) + skySpecular *= skylightingSpecular; # endif + } else { + // Mode 0/1: IBL ratio-based + float3 ratio = ImageBasedLighting::GetIBLRatio(); + envSpecular = Color::IrradianceToLinear(envSample * ratio) * SharedData::iblSettings.EnvIBLScale; + skySpecular = Color::IrradianceToLinear(max(0, fullSample - envSample)) * SharedData::iblSettings.SkyIBLScale; +# if defined(SKYLIGHTING) + skySpecular *= skylightingSpecular; +# endif + } - directionalAmbientColorSpecular *= skylightingSpecular; - - float3 specularIrradiance = 1.0; - - if (skylightingSpecular < 1.0) { - specularIrradiance = EnvTexture.SampleLevel(SampColorSampler, R, level); - - float specularIrradianceLuminance = Color::RGBToLuminance(EnvTexture.SampleLevel(SampColorSampler, R, 15)); - - specularIrradiance = (specularIrradiance / max(specularIrradianceLuminance, 0.001)) * directionalAmbientColorSpecular; - specularIrradiance = Color::IrradianceToLinear(specularIrradiance); - } - - float3 specularIrradianceReflections = 1.0; - - if (skylightingSpecular > 0.0) { - specularIrradianceReflections = EnvReflectionsTexture.SampleLevel(SampColorSampler, R, level); - - float specularIrradianceReflectionsLuminance = Color::RGBToLuminance(EnvReflectionsTexture.SampleLevel(SampColorSampler, R, 15)); - - specularIrradianceReflections = (specularIrradianceReflections / max(specularIrradianceReflectionsLuminance, 0.001)) * directionalAmbientColorSpecular; - specularIrradianceReflections = Color::IrradianceToLinear(specularIrradianceReflections); - } - - finalIrradiance = lerp(specularIrradiance, specularIrradianceReflections, skylightingSpecular); + finalIrradiance = envSpecular + skySpecular; + } else +# endif + { + // Fallback without IBL: normalize-by-luminance with DALC +# if defined(SKYLIGHTING) + if (SharedData::InInterior) { + float3 specularIrradiance = EnvTexture.SampleLevel(SampColorSampler, R, level); + float specularIrradianceLuminance = Color::RGBToLuminance(EnvTexture.SampleLevel(SampColorSampler, R, 15)); + specularIrradiance = (specularIrradiance / max(specularIrradianceLuminance, 0.001)) * directionalAmbientColorSpecular; + finalIrradiance = Color::IrradianceToLinear(specularIrradiance); + } else { + float3 specularIrradiance = 1.0; + if (skylightingSpecular < 1.0) { + specularIrradiance = EnvTexture.SampleLevel(SampColorSampler, R, level); + float lum = Color::RGBToLuminance(EnvTexture.SampleLevel(SampColorSampler, R, 15)); + float dalcScaled = Color::IrradianceToGamma(Color::IrradianceToLinear(directionalAmbientColorSpecular) * skylightingSpecular); + specularIrradiance = (specularIrradiance / max(lum, 0.001)) * dalcScaled; + specularIrradiance = Color::IrradianceToLinear(specularIrradiance); + } + float3 specularIrradianceReflections = 1.0; + if (skylightingSpecular > 0.0) { + specularIrradianceReflections = EnvReflectionsTexture.SampleLevel(SampColorSampler, R, level); + float lum = Color::RGBToLuminance(EnvReflectionsTexture.SampleLevel(SampColorSampler, R, 15)); + specularIrradianceReflections = (specularIrradianceReflections / max(lum, 0.001)) * directionalAmbientColorSpecular; + specularIrradianceReflections = Color::IrradianceToLinear(specularIrradianceReflections); + } + finalIrradiance = lerp(specularIrradiance, specularIrradianceReflections, skylightingSpecular); + } # else -# if defined(IBL) - float3 iblColor = 0; - if (SharedData::iblSettings.EnableDiffuseIBL) { - directionalAmbientColorSpecular *= SharedData::iblSettings.DALCAmount; - iblColor += Color::Saturation(ImageBasedLighting::GetIBLColor(-R), SharedData::iblSettings.IBLSaturation) * SharedData::iblSettings.DiffuseIBLScale; - float iblColorLuminance = Color::RGBToLuminance(Color::IrradianceToGamma(iblColor)); - directionalAmbientColorSpecular += iblColorLuminance; + float3 specularIrradiance = EnvReflectionsTexture.SampleLevel(SampColorSampler, R, level); + float specularIrradianceLuminance = Color::RGBToLuminance(EnvReflectionsTexture.SampleLevel(SampColorSampler, R, 15)); + specularIrradiance = (specularIrradiance / max(specularIrradianceLuminance, 0.001)) * directionalAmbientColorSpecular; + finalIrradiance = Color::IrradianceToLinear(specularIrradiance); +# endif } -# endif - float3 specularIrradiance = EnvReflectionsTexture.SampleLevel(SampColorSampler, R, level); - - float specularIrradianceLuminance = Color::RGBToLuminance(EnvReflectionsTexture.SampleLevel(SampColorSampler, R, 15)); - - specularIrradiance = (specularIrradiance / max(specularIrradianceLuminance, 0.001)) * directionalAmbientColorSpecular; - specularIrradiance = Color::IrradianceToLinear(specularIrradiance); - - finalIrradiance = specularIrradiance; -# endif return horizon * (F0 * specularBRDF.x + specularBRDF.y) * finalIrradiance; # endif } diff --git a/features/IBL/Shaders/IBL/IBL.hlsli b/features/IBL/Shaders/IBL/IBL.hlsli index d095afd4b5..b21dc55de1 100644 --- a/features/IBL/Shaders/IBL/IBL.hlsli +++ b/features/IBL/Shaders/IBL/IBL.hlsli @@ -18,7 +18,9 @@ namespace ImageBasedLighting TextureCube StaticDiffuseIBLTexture : register(t78); TextureCube StaticSpecularIBLTexture : register(t79); #endif - float3 GetDiffuseIBL(float3 rayDir) + + /// Get Env IBL color from environment cubemap SH (without sky) + float3 GetEnvIBL(float3 rayDir) { sh2 shR = DiffuseIBLTexture.Load(int3(0, 0, 0)); sh2 shG = DiffuseIBLTexture.Load(int3(1, 0, 0)); @@ -29,37 +31,79 @@ namespace ImageBasedLighting return float3(colorR, colorG, colorB) / Math::PI; } - float3 GetSkyDiffuseIBL(float3 rayDir) + /// Get Sky-only IBL color: (full env+sky SH) - (env-only SH) + float3 GetSkyIBL(float3 rayDir) { - sh2 shR = DiffuseSkyIBLTexture.Load(int3(0, 0, 0)); - sh2 shG = DiffuseSkyIBLTexture.Load(int3(1, 0, 0)); - sh2 shB = DiffuseSkyIBLTexture.Load(int3(2, 0, 0)); - float colorR = SphericalHarmonics::SHHallucinateZH3Irradiance(shR, rayDir); - float colorG = SphericalHarmonics::SHHallucinateZH3Irradiance(shG, rayDir); - float colorB = SphericalHarmonics::SHHallucinateZH3Irradiance(shB, rayDir); - return float3(colorR, colorG, colorB) / Math::PI; + // Full SH (env + sky) + sh2 fullR = DiffuseSkyIBLTexture.Load(int3(0, 0, 0)); + sh2 fullG = DiffuseSkyIBLTexture.Load(int3(1, 0, 0)); + sh2 fullB = DiffuseSkyIBLTexture.Load(int3(2, 0, 0)); + + // Env-only SH + sh2 envR = DiffuseIBLTexture.Load(int3(0, 0, 0)); + sh2 envG = DiffuseIBLTexture.Load(int3(1, 0, 0)); + sh2 envB = DiffuseIBLTexture.Load(int3(2, 0, 0)); + + // Sky-only SH = full - env + sh2 skyR = fullR - envR; + sh2 skyG = fullG - envG; + sh2 skyB = fullB - envB; + + float colorR = SphericalHarmonics::SHHallucinateZH3Irradiance(skyR, rayDir); + float colorG = SphericalHarmonics::SHHallucinateZH3Irradiance(skyG, rayDir); + float colorB = SphericalHarmonics::SHHallucinateZH3Irradiance(skyB, rayDir); + return max(0, float3(colorR, colorG, colorB) / Math::PI); } -#if defined(SKYLIGHTING) && !defined(INTERIOR) - float3 GetIBLColor(float3 rayDir, float skylighting) -#else - float3 GetIBLColor(float3 rayDir) -#endif + /// Compute ratio between DALC and IBL for brightness/color matching. + /// Mode 0 (Luminance Ratio): scalar ratio from luminance, broadcast to float3 (loses DALC tint). + /// Mode 1 (Color Ratio): per-channel ratio, preserves DALC color tint. + /// DALCAmount interpolates between 1.0 (no matching) and the computed ratio. + float3 GetIBLRatio() { - float3 color = 0; - if (SharedData::InInterior) { - color = GetDiffuseIBL(rayDir); - } else -#if defined(SKYLIGHTING) - { - color = lerp(GetDiffuseIBL(rayDir), GetSkyDiffuseIBL(rayDir), skylighting); - } -#else - { - color = GetSkyDiffuseIBL(rayDir); + // 0th order DALC (DC term) + float3 dalc0 = Color::Ambient(SharedData::GetAmbient(0.f)); + + // 0th order IBL SH (DC term from env cubemap) + sh2 iblSHR = DiffuseIBLTexture.Load(int3(0, 0, 0)); + sh2 iblSHG = DiffuseIBLTexture.Load(int3(1, 0, 0)); + sh2 iblSHB = DiffuseIBLTexture.Load(int3(2, 0, 0)); + + float colorR = SphericalHarmonics::SHHallucinateZH3Irradiance(iblSHR, float3(0, 0, 0)); + float colorG = SphericalHarmonics::SHHallucinateZH3Irradiance(iblSHG, float3(0, 0, 0)); + float colorB = SphericalHarmonics::SHHallucinateZH3Irradiance(iblSHB, float3(0, 0, 0)); + float3 ibl0 = max(0, float3(colorR, colorG, colorB) / Math::PI); + + if (SharedData::iblSettings.DALCMode == 1) { + // Mode 1: per-channel ratio preserving DALC color tint + float3 ratio = dalc0 / max(ibl0, 0.001); + return lerp(1.0, ratio, SharedData::iblSettings.DALCAmount); + } else { + // Mode 0: scalar luminance ratio (default) + float dalcLum = Color::RGBToLuminance(dalc0); + float iblLum = Color::RGBToLuminance(ibl0); + float ratio = (iblLum > 0.001) ? (dalcLum / iblLum) : 1.0; + return lerp(1.0, ratio, SharedData::iblSettings.DALCAmount); } -#endif - return color; + } + + /// Get Env IBL color with settings applied (saturation, scale, ratio) + float3 GetEnvIBLColor(float3 rayDir) + { + float3 ratio = GetIBLRatio(); + return Color::Saturation(GetEnvIBL(rayDir), SharedData::iblSettings.EnvIBLSaturation) * SharedData::iblSettings.EnvIBLScale * ratio; + } + + /// Get Sky IBL color with settings applied (saturation, scale; no ratio) + float3 GetSkyIBLColor(float3 rayDir) + { + return Color::Saturation(GetSkyIBL(rayDir), SharedData::iblSettings.SkyIBLSaturation) * SharedData::iblSettings.SkyIBLScale; + } + + /// Get combined IBL color: Env IBL + Sky IBL (for contexts without skylighting) + float3 GetIBLColor(float3 rayDir) + { + return GetEnvIBLColor(rayDir) + GetSkyIBLColor(rayDir); } #if defined(LIGHTING) @@ -71,8 +115,7 @@ namespace ImageBasedLighting float3 GetFogIBLColor(float3 fogColor) { - float3 directionalAmbientColor = max(0, SharedData::GetAmbient(float3(0, 0, 0))).xyz; - float3 iblColor = directionalAmbientColor * SharedData::iblSettings.DALCAmount + Color::Saturation(GetSkyDiffuseIBL(float3(0, 0, 0)), SharedData::iblSettings.IBLSaturation) * SharedData::iblSettings.DiffuseIBLScale; + float3 iblColor = GetEnvIBLColor(float3(0, 0, 0)) + GetSkyIBLColor(float3(0, 0, 0)); if (SharedData::iblSettings.PreserveFogLuminance) { const float fogLuminance = Color::RGBToLuminance(fogColor); const float iblLuminance = Color::RGBToLuminance(iblColor); diff --git a/package/Shaders/Common/ShadowSampling.hlsli b/package/Shaders/Common/ShadowSampling.hlsli index ef2b9031c2..3d8ecbfa0a 100644 --- a/package/Shaders/Common/ShadowSampling.hlsli +++ b/package/Shaders/Common/ShadowSampling.hlsli @@ -110,14 +110,15 @@ namespace ShadowSampling float3 ambientColorAmb = max(0, SharedData::GetAmbient(float3(0, 0, 1))); #if defined(IBL) - if (SharedData::iblSettings.EnableDiffuseIBL && (!SharedData::InInterior || SharedData::iblSettings.EnableInterior)) { - ambientColorAmb *= SharedData::iblSettings.DALCAmount; -# if defined(SKYLIGHTING) && !defined(INTERIOR) - float3 iblColor = Color::Saturation(ImageBasedLighting::GetIBLColor(float3(0, 0, -1), skylightingDiffuse), SharedData::iblSettings.IBLSaturation) * SharedData::iblSettings.DiffuseIBLScale; -# else - float3 iblColor = Color::Saturation(ImageBasedLighting::GetIBLColor(float3(0, 0, -1)), SharedData::iblSettings.IBLSaturation) * SharedData::iblSettings.DiffuseIBLScale; -# endif - ambientColorAmb += Color::IrradianceToGamma(iblColor); + if (SharedData::iblSettings.EnableDiffuseIBL) { + if (SharedData::iblSettings.DALCMode == 2) { + // Mode 2: keep vanilla DALC, add sky IBL overlay + ambientColorAmb += Color::IrradianceToGamma(ImageBasedLighting::GetSkyIBLColor(float3(0, 0, -1))); + } else { + float3 envIBLColor = Color::IrradianceToGamma(ImageBasedLighting::GetEnvIBLColor(float3(0, 0, -1))); + float3 skyIBLColor = Color::IrradianceToGamma(ImageBasedLighting::GetSkyIBLColor(float3(0, 0, -1))); + ambientColorAmb = envIBLColor + skyIBLColor; + } } #endif diff --git a/package/Shaders/Common/SharedData.hlsli b/package/Shaders/Common/SharedData.hlsli index e4afcabe32..b4aaba4642 100644 --- a/package/Shaders/Common/SharedData.hlsli +++ b/package/Shaders/Common/SharedData.hlsli @@ -190,11 +190,14 @@ namespace SharedData uint EnableDiffuseIBL; uint PreserveFogLuminance; uint UseStaticIBL; - uint EnableInterior; - float DiffuseIBLScale; float DALCAmount; - float IBLSaturation; + float EnvIBLScale; + float SkyIBLScale; + float EnvIBLSaturation; + float SkyIBLSaturation; float FogAmount; + uint DALCMode; // 0: Luminance Ratio, 1: Color Ratio, 2: DALC + Sky + float2 pad0; }; struct ExtendedTranslucencySettings diff --git a/package/Shaders/DeferredCompositeCS.hlsl b/package/Shaders/DeferredCompositeCS.hlsl index 65b3deff76..7e89780d64 100644 --- a/package/Shaders/DeferredCompositeCS.hlsl +++ b/package/Shaders/DeferredCompositeCS.hlsl @@ -173,24 +173,7 @@ void SampleSSGISpecular(uint2 pixCoord, sh2 lobe, out float ao, out float3 il, i float directionalAmbientColorSpecular = Color::RGBToLuminance(Color::Ambient(max(0, SharedData::GetAmbient(R)))) * Color::ReflectionNormalisationScale; -# if defined(INTERIOR) - float3 specularIrradiance = EnvTexture.SampleLevel(LinearSampler, R, level); - - float specularIrradianceLuminance = Color::RGBToLuminance(EnvTexture.SampleLevel(LinearSampler, R, 15)); - -# if defined(IBL) - float3 iblColor = 0; - if (SharedData::iblSettings.EnableDiffuseIBL && SharedData::iblSettings.EnableInterior) { - directionalAmbientColorSpecular *= SharedData::iblSettings.DALCAmount; - iblColor += Color::Saturation(ImageBasedLighting::GetIBLColor(-R), SharedData::iblSettings.IBLSaturation) * SharedData::iblSettings.DiffuseIBLScale; - float iblColorLuminance = Color::RGBToLuminance(Color::IrradianceToGamma(iblColor)); - directionalAmbientColorSpecular += iblColorLuminance; - } -# endif - specularIrradiance = (specularIrradiance / max(specularIrradianceLuminance, 0.001)) * directionalAmbientColorSpecular; - - finalIrradiance = Color::IrradianceToLinear(specularIrradiance); -# elif defined(SKYLIGHTING) +# if defined(SKYLIGHTING) # if defined(VR) float3 positionMS = positionWS.xyz + FrameBuffer::CameraPosAdjust[eyeIndex].xyz - FrameBuffer::CameraPosAdjust[0].xyz; # else @@ -202,64 +185,70 @@ void SampleSSGISpecular(uint2 pixCoord, sh2 lobe, out float ao, out float3 il, i float skylightingSpecular = SphericalHarmonics::FuncProductIntegral(skylighting, specularLobe); skylightingSpecular = saturate(skylightingSpecular); skylightingSpecular = Skylighting::mixSpecular(SharedData::skylightingSettings, skylightingSpecular); +# endif -# if defined(IBL) - float3 iblColor = 0; +# if defined(IBL) if (SharedData::iblSettings.EnableDiffuseIBL) { - directionalAmbientColorSpecular *= SharedData::iblSettings.DALCAmount; - iblColor += Color::Saturation(ImageBasedLighting::GetIBLColor(-R, skylightingSpecular), SharedData::iblSettings.IBLSaturation) * SharedData::iblSettings.DiffuseIBLScale; - float iblColorLuminance = Color::RGBToLuminance(Color::IrradianceToGamma(iblColor)); - directionalAmbientColorSpecular += iblColorLuminance; - } + float3 envSample = EnvTexture.SampleLevel(LinearSampler, R, level); + float3 fullSample = EnvReflectionsTexture.SampleLevel(LinearSampler, R, level); + float3 envSpecular, skySpecular; + + if (SharedData::iblSettings.DALCMode == 2) { + // Mode 2: DALC-normalized env + sky overlay + float envLum = Color::RGBToLuminance(EnvTexture.SampleLevel(LinearSampler, R, 15)); + envSpecular = Color::IrradianceToLinear((envSample / max(envLum, 0.001)) * directionalAmbientColorSpecular); + skySpecular = Color::IrradianceToLinear(max(0, fullSample - envSample)) * SharedData::iblSettings.SkyIBLScale; +# if defined(SKYLIGHTING) + skySpecular *= skylightingSpecular; +# elif defined(INTERIOR) + skySpecular = 0; # endif + } else { + // Mode 0/1: IBL ratio-based + float3 ratio = ImageBasedLighting::GetIBLRatio(); + envSpecular = Color::IrradianceToLinear(envSample * ratio) * SharedData::iblSettings.EnvIBLScale; + skySpecular = Color::IrradianceToLinear(max(0, fullSample - envSample)) * SharedData::iblSettings.SkyIBLScale; +# if defined(SKYLIGHTING) + skySpecular *= skylightingSpecular; +# elif defined(INTERIOR) + skySpecular = 0; +# endif + } - float3 specularIrradianceReflections = 0.0; - - if (skylightingSpecular > 0.0) { - specularIrradianceReflections = EnvReflectionsTexture.SampleLevel(LinearSampler, R, level); - - float specularIrradianceLuminance = Color::RGBToLuminance(EnvReflectionsTexture.SampleLevel(LinearSampler, R, 15)); - - specularIrradianceReflections = (specularIrradianceReflections / max(specularIrradianceLuminance, 0.001)) * directionalAmbientColorSpecular; - - specularIrradianceReflections = Color::IrradianceToLinear(specularIrradianceReflections); - } - - float3 specularIrradiance = 0.0; - - if (skylightingSpecular < 1.0) { - specularIrradiance = EnvTexture.SampleLevel(LinearSampler, R, level); - + finalIrradiance = envSpecular + skySpecular; + } else +# endif + { + // Fallback without IBL: normalize-by-luminance with DALC +# if defined(INTERIOR) + float3 specularIrradiance = EnvTexture.SampleLevel(LinearSampler, R, level); float specularIrradianceLuminance = Color::RGBToLuminance(EnvTexture.SampleLevel(LinearSampler, R, 15)); - - directionalAmbientColorSpecular = Color::IrradianceToLinear(directionalAmbientColorSpecular); - directionalAmbientColorSpecular *= skylightingSpecular; - directionalAmbientColorSpecular = Color::IrradianceToGamma(directionalAmbientColorSpecular); - specularIrradiance = (specularIrradiance / max(specularIrradianceLuminance, 0.001)) * directionalAmbientColorSpecular; - - specularIrradiance = Color::IrradianceToLinear(specularIrradiance); - } - - finalIrradiance = lerp(specularIrradiance, specularIrradianceReflections, skylightingSpecular); + finalIrradiance = Color::IrradianceToLinear(specularIrradiance); +# elif defined(SKYLIGHTING) + float3 specularIrradianceReflections = 0.0; + if (skylightingSpecular > 0.0) { + specularIrradianceReflections = EnvReflectionsTexture.SampleLevel(LinearSampler, R, level); + float lum = Color::RGBToLuminance(EnvReflectionsTexture.SampleLevel(LinearSampler, R, 15)); + specularIrradianceReflections = (specularIrradianceReflections / max(lum, 0.001)) * directionalAmbientColorSpecular; + specularIrradianceReflections = Color::IrradianceToLinear(specularIrradianceReflections); + } + float3 specularIrradiance = 0.0; + if (skylightingSpecular < 1.0) { + specularIrradiance = EnvTexture.SampleLevel(LinearSampler, R, level); + float lum = Color::RGBToLuminance(EnvTexture.SampleLevel(LinearSampler, R, 15)); + float dalcScaled = Color::IrradianceToGamma(Color::IrradianceToLinear(directionalAmbientColorSpecular) * skylightingSpecular); + specularIrradiance = (specularIrradiance / max(lum, 0.001)) * dalcScaled; + specularIrradiance = Color::IrradianceToLinear(specularIrradiance); + } + finalIrradiance = lerp(specularIrradiance, specularIrradianceReflections, skylightingSpecular); # else -# if defined(IBL) - float3 iblColor = 0; - if (SharedData::iblSettings.EnableDiffuseIBL) { - directionalAmbientColorSpecular *= SharedData::iblSettings.DALCAmount; - iblColor += Color::Saturation(ImageBasedLighting::GetIBLColor(-R), SharedData::iblSettings.IBLSaturation) * SharedData::iblSettings.DiffuseIBLScale; - float iblColorLuminance = Color::RGBToLuminance(Color::IrradianceToGamma(iblColor)); - directionalAmbientColorSpecular += iblColorLuminance; - } -# endif - float3 specularIrradianceReflections = EnvReflectionsTexture.SampleLevel(LinearSampler, R, level); - - float specularIrradianceReflectionsLuminance = Color::RGBToLuminance(EnvReflectionsTexture.SampleLevel(LinearSampler, R, 15)); - - specularIrradianceReflections = (specularIrradianceReflections / max(specularIrradianceReflectionsLuminance, 0.001)) * directionalAmbientColorSpecular; - - finalIrradiance = Color::IrradianceToLinear(specularIrradianceReflections); + float3 specularIrradiance = EnvReflectionsTexture.SampleLevel(LinearSampler, R, level); + float specularIrradianceLuminance = Color::RGBToLuminance(EnvReflectionsTexture.SampleLevel(LinearSampler, R, 15)); + specularIrradiance = (specularIrradiance / max(specularIrradianceLuminance, 0.001)) * directionalAmbientColorSpecular; + finalIrradiance = Color::IrradianceToLinear(specularIrradiance); # endif + } # if defined(SSGI) float ssgiAo; diff --git a/package/Shaders/DistantTree.hlsl b/package/Shaders/DistantTree.hlsl index c77337527e..d9d155a82f 100644 --- a/package/Shaders/DistantTree.hlsl +++ b/package/Shaders/DistantTree.hlsl @@ -246,15 +246,15 @@ PS_OUTPUT main(PS_INPUT input) float3 directionalAmbientColor = max(0, Color::Ambient(SharedData::GetAmbient(normal))); # if defined(IBL) - float3 iblColor = 0; if (SharedData::iblSettings.EnableDiffuseIBL) { - directionalAmbientColor *= SharedData::iblSettings.DALCAmount; -# if defined(SKYLIGHTING) - iblColor += Color::Saturation(ImageBasedLighting::GetIBLColor(-normal, 1.0), SharedData::iblSettings.IBLSaturation) * SharedData::iblSettings.DiffuseIBLScale; -# else - iblColor += Color::Saturation(ImageBasedLighting::GetIBLColor(-normal), SharedData::iblSettings.IBLSaturation) * SharedData::iblSettings.DiffuseIBLScale; -# endif - directionalAmbientColor += Color::IrradianceToGamma(iblColor); + if (SharedData::iblSettings.DALCMode == 2) { + // Mode 2: keep vanilla DALC, add sky IBL overlay + directionalAmbientColor += Color::IrradianceToGamma(ImageBasedLighting::GetSkyIBLColor(-normal)); + } else { + float3 envIBLColor = Color::IrradianceToGamma(ImageBasedLighting::GetEnvIBLColor(-normal)); + float3 skyIBLColor = Color::IrradianceToGamma(ImageBasedLighting::GetSkyIBLColor(-normal)); + directionalAmbientColor = envIBLColor + skyIBLColor; + } } # endif diffuseColor += directionalAmbientColor; @@ -287,15 +287,15 @@ PS_OUTPUT main(PS_INPUT input) float3 directionalAmbientColor = Color::Ambient(SharedData::GetAmbient(normal)); # if defined(IBL) - float3 iblColor = 0; if (SharedData::iblSettings.EnableDiffuseIBL) { - directionalAmbientColor *= SharedData::iblSettings.DALCAmount; -# if defined(SKYLIGHTING) - iblColor += Color::Saturation(ImageBasedLighting::GetIBLColor(-normal, 1.0), SharedData::iblSettings.IBLSaturation) * SharedData::iblSettings.DiffuseIBLScale; -# else - iblColor += Color::Saturation(ImageBasedLighting::GetIBLColor(-normal), SharedData::iblSettings.IBLSaturation) * SharedData::iblSettings.DiffuseIBLScale; -# endif - directionalAmbientColor += Color::IrradianceToGamma(iblColor); + if (SharedData::iblSettings.DALCMode == 2) { + // Mode 2: keep vanilla DALC, add sky IBL overlay + directionalAmbientColor += Color::IrradianceToGamma(ImageBasedLighting::GetSkyIBLColor(-normal)); + } else { + float3 envIBLColor = Color::IrradianceToGamma(ImageBasedLighting::GetEnvIBLColor(-normal)); + float3 skyIBLColor = Color::IrradianceToGamma(ImageBasedLighting::GetSkyIBLColor(-normal)); + directionalAmbientColor = envIBLColor + skyIBLColor; + } } # endif diffuseColor += directionalAmbientColor; diff --git a/package/Shaders/Effect.hlsl b/package/Shaders/Effect.hlsl index f42f82188d..e7fb80101a 100644 --- a/package/Shaders/Effect.hlsl +++ b/package/Shaders/Effect.hlsl @@ -850,7 +850,7 @@ PS_OUTPUT main(PS_INPUT input) float fogFactor = Color::FogAlpha(input.FogParam.w); float3 fogColor = Color::Fog(input.FogParam.xyz); # if defined(IBL) - if (SharedData::iblSettings.EnableDiffuseIBL && !SharedData::InInterior) { + if (SharedData::iblSettings.EnableDiffuseIBL) { fogColor = ImageBasedLighting::GetFogIBLColor(fogColor); } # endif diff --git a/package/Shaders/ISSAOComposite.hlsl b/package/Shaders/ISSAOComposite.hlsl index 478e677b40..76fde19b23 100644 --- a/package/Shaders/ISSAOComposite.hlsl +++ b/package/Shaders/ISSAOComposite.hlsl @@ -181,7 +181,7 @@ PS_OUTPUT main(PS_INPUT input) float fogFactor = min(FogParam.w, pow(saturate(fogDistanceFactor * FogParam.y - FogParam.x), FogParam.z)); float3 fogColor = Color::Fog(lerp(FogNearColor.xyz, FogFarColor.xyz, fogFactor)); # if defined(IBL) - if (SharedData::iblSettings.EnableDiffuseIBL && !SharedData::InInterior) { + if (SharedData::iblSettings.EnableDiffuseIBL) { fogColor = ImageBasedLighting::GetFogIBLColor(fogColor); } # endif diff --git a/package/Shaders/Lighting.hlsl b/package/Shaders/Lighting.hlsl index f3a06c1dff..c0559cb627 100644 --- a/package/Shaders/Lighting.hlsl +++ b/package/Shaders/Lighting.hlsl @@ -2755,8 +2755,6 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) if (SharedData::iblSettings.EnableDiffuseIBL) { if (SharedData::iblSettings.UseStaticIBL && !inWorld && !inReflection) { directionalAmbientColor = ImageBasedLighting::GetStaticDiffuseIBL(ambientNormal, SampColorSampler); - } else if (!SharedData::InInterior || SharedData::iblSettings.EnableInterior) { - directionalAmbientColor *= SharedData::iblSettings.DALCAmount; } } # endif @@ -2774,16 +2772,19 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) # endif # if defined(IBL) - float3 iblColor = 0; + float3 envIBLColor = 0; if (SharedData::iblSettings.EnableDiffuseIBL) { - if ((!SharedData::InInterior || SharedData::iblSettings.EnableInterior) && !(SharedData::iblSettings.UseStaticIBL && !inWorld && !inReflection)) { -# if defined(SKYLIGHTING) - iblColor += Color::Saturation(ImageBasedLighting::GetIBLColor(-ambientNormal, skylightingDiffuse), SharedData::iblSettings.IBLSaturation) * SharedData::iblSettings.DiffuseIBLScale; -# else - iblColor += Color::Saturation(ImageBasedLighting::GetIBLColor(-ambientNormal), SharedData::iblSettings.IBLSaturation) * SharedData::iblSettings.DiffuseIBLScale; -# endif - iblColor = Color::IrradianceToGamma(iblColor); - directionalAmbientColor += iblColor; + if (!(SharedData::iblSettings.UseStaticIBL && !inWorld && !inReflection)) { + if (SharedData::iblSettings.DALCMode == 2) { + // Mode 2: keep vanilla DALC, add sky IBL overlay; save DALC in envIBLColor so skylighting dance protects it + envIBLColor = directionalAmbientColor; + directionalAmbientColor += Color::IrradianceToGamma(ImageBasedLighting::GetSkyIBLColor(-ambientNormal)); + } else { + // Mode 0/1: replace with IBL ratio + envIBLColor = Color::IrradianceToGamma(ImageBasedLighting::GetEnvIBLColor(-ambientNormal)); + float3 skyIBLColor = Color::IrradianceToGamma(ImageBasedLighting::GetSkyIBLColor(-ambientNormal)); + directionalAmbientColor = envIBLColor + skyIBLColor; + } } } # endif @@ -2969,7 +2970,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) float3 outputAlbedo = indirectLobeWeights.diffuse * vertexColor.xyz; # if defined(IBL) && defined(SKYLIGHTING) - directionalAmbientColor -= iblColor; + directionalAmbientColor -= envIBLColor; # endif directionalAmbientColor *= outputAlbedo; @@ -2979,7 +2980,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) # endif # if defined(IBL) && defined(SKYLIGHTING) - directionalAmbientColor += iblColor * outputAlbedo; + directionalAmbientColor += envIBLColor * outputAlbedo; # endif # if !defined(DEFERRED) @@ -2987,7 +2988,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) float3 fogColor = Color::Fog(input.FogParam.xyz); float fogFactor = Color::FogAlpha(input.FogParam.w); # if defined(IBL) - if (SharedData::iblSettings.EnableDiffuseIBL && !SharedData::InInterior) { + if (SharedData::iblSettings.EnableDiffuseIBL) { fogColor = ImageBasedLighting::GetFogIBLColor(fogColor); } # endif diff --git a/package/Shaders/RunGrass.hlsl b/package/Shaders/RunGrass.hlsl index d03273f611..043fbc6301 100644 --- a/package/Shaders/RunGrass.hlsl +++ b/package/Shaders/RunGrass.hlsl @@ -756,12 +756,6 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) float3 directionalAmbientColor = Color::Ambient(max(0, SharedData::GetAmbient(normal))); -# if defined(IBL) - if (SharedData::iblSettings.EnableDiffuseIBL && (!SharedData::InInterior || SharedData::iblSettings.EnableInterior)) { - directionalAmbientColor *= SharedData::iblSettings.DALCAmount; - } -# endif // IBL - # if defined(SKYLIGHTING) float skylightingDiffuse = 1.0; if (!SharedData::InInterior) { @@ -779,16 +773,16 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) # endif // SKYLIGHTING # if defined(IBL) - float3 iblColor = 0; + float3 envIBLColor = 0; if (SharedData::iblSettings.EnableDiffuseIBL) { - if (!SharedData::InInterior || SharedData::iblSettings.EnableInterior) { -# if defined(SKYLIGHTING) - iblColor += Color::Saturation(ImageBasedLighting::GetIBLColor(-normal, skylightingDiffuse), SharedData::iblSettings.IBLSaturation) * SharedData::iblSettings.DiffuseIBLScale; -# else - iblColor += Color::Saturation(ImageBasedLighting::GetIBLColor(-normal), SharedData::iblSettings.IBLSaturation) * SharedData::iblSettings.DiffuseIBLScale; -# endif - iblColor = Color::IrradianceToGamma(iblColor); - directionalAmbientColor += iblColor; + if (SharedData::iblSettings.DALCMode == 2) { + // Mode 2: keep vanilla DALC, add sky IBL overlay; save DALC in envIBLColor so skylighting dance protects it + envIBLColor = directionalAmbientColor; + directionalAmbientColor += Color::IrradianceToGamma(ImageBasedLighting::GetSkyIBLColor(-normal)); + } else { + envIBLColor = Color::IrradianceToGamma(ImageBasedLighting::GetEnvIBLColor(-normal)); + float3 skyIBLColor = Color::IrradianceToGamma(ImageBasedLighting::GetSkyIBLColor(-normal)); + directionalAmbientColor = envIBLColor + skyIBLColor; } } # endif @@ -796,7 +790,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) diffuseColor += directionalAmbientColor; # if defined(IBL) && defined(SKYLIGHTING) - directionalAmbientColor -= iblColor; + directionalAmbientColor -= envIBLColor; # endif diffuseColor *= albedo; diffuseColor += max(0, sss * subsurfaceColor * SharedData::grassLightingSettings.SubsurfaceScatteringAmount); @@ -808,7 +802,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) # endif # if defined(IBL) && defined(SKYLIGHTING) - directionalAmbientColor += iblColor * albedo; + directionalAmbientColor += envIBLColor * albedo; # endif specularColor += lightsSpecularColor; @@ -962,12 +956,6 @@ PS_OUTPUT main(PS_INPUT input) float3 directionalAmbientColor = Color::Ambient(max(0, SharedData::GetAmbient(normal))); -# if defined(IBL) - if (SharedData::iblSettings.EnableDiffuseIBL && (!SharedData::InInterior || SharedData::iblSettings.EnableInterior)) { - directionalAmbientColor *= SharedData::iblSettings.DALCAmount; - } -# endif // IBL - # if defined(SKYLIGHTING) float skylightingDiffuse = 1.0; if (!SharedData::InInterior) { @@ -985,16 +973,16 @@ PS_OUTPUT main(PS_INPUT input) # endif // SKYLIGHTING # if defined(IBL) - float3 iblColor = 0; + float3 envIBLColor = 0; if (SharedData::iblSettings.EnableDiffuseIBL) { - if (!SharedData::InInterior || SharedData::iblSettings.EnableInterior) { -# if defined(SKYLIGHTING) - iblColor += Color::Saturation(ImageBasedLighting::GetIBLColor(-normal, skylightingDiffuse), SharedData::iblSettings.IBLSaturation) * SharedData::iblSettings.DiffuseIBLScale; -# else - iblColor += Color::Saturation(ImageBasedLighting::GetIBLColor(-normal), SharedData::iblSettings.IBLSaturation) * SharedData::iblSettings.DiffuseIBLScale; -# endif - iblColor = Color::IrradianceToGamma(iblColor); - directionalAmbientColor += iblColor; + if (SharedData::iblSettings.DALCMode == 2) { + // Mode 2: keep vanilla DALC, add sky IBL overlay; save DALC in envIBLColor so skylighting dance protects it + envIBLColor = directionalAmbientColor; + directionalAmbientColor += Color::IrradianceToGamma(ImageBasedLighting::GetSkyIBLColor(-normal)); + } else { + envIBLColor = Color::IrradianceToGamma(ImageBasedLighting::GetEnvIBLColor(-normal)); + float3 skyIBLColor = Color::IrradianceToGamma(ImageBasedLighting::GetSkyIBLColor(-normal)); + directionalAmbientColor = envIBLColor + skyIBLColor; } } # endif @@ -1005,7 +993,7 @@ PS_OUTPUT main(PS_INPUT input) diffuseColor *= albedo; # if defined(IBL) && defined(SKYLIGHTING) - directionalAmbientColor -= iblColor; + directionalAmbientColor -= envIBLColor; # endif directionalAmbientColor *= albedo; @@ -1014,7 +1002,7 @@ PS_OUTPUT main(PS_INPUT input) # endif # if defined(IBL) && defined(SKYLIGHTING) - directionalAmbientColor += iblColor * albedo; + directionalAmbientColor += envIBLColor * albedo; # endif psout.Diffuse.xyz = diffuseColor; diff --git a/package/Shaders/Water.hlsl b/package/Shaders/Water.hlsl index 6bbe453f75..23a0369930 100644 --- a/package/Shaders/Water.hlsl +++ b/package/Shaders/Water.hlsl @@ -1267,7 +1267,7 @@ PS_OUTPUT main(PS_INPUT input) fogDistanceFactor = Color::FogAlpha(fogDistanceFactor); # if defined(IBL) - if (SharedData::iblSettings.EnableDiffuseIBL && !SharedData::InInterior) { + if (SharedData::iblSettings.EnableDiffuseIBL) { fogColor = ImageBasedLighting::GetFogIBLColor(fogColor); } # endif @@ -1305,7 +1305,7 @@ PS_OUTPUT main(PS_INPUT input) fogDistanceFactor = Color::FogAlpha(fogDistanceFactor); # if defined(IBL) - if (SharedData::iblSettings.EnableDiffuseIBL && !SharedData::InInterior) { + if (SharedData::iblSettings.EnableDiffuseIBL) { preFogColor = ImageBasedLighting::GetFogIBLColor(preFogColor); } # endif @@ -1330,7 +1330,7 @@ PS_OUTPUT main(PS_INPUT input) } # endif # if defined(IBL) - if (SharedData::iblSettings.EnableDiffuseIBL && !SharedData::InInterior) { + if (SharedData::iblSettings.EnableDiffuseIBL) { fogColor = ImageBasedLighting::GetFogIBLColor(fogColor); } # endif diff --git a/src/Features/IBL.cpp b/src/Features/IBL.cpp index 7d8095923e..15400d0a00 100644 --- a/src/Features/IBL.cpp +++ b/src/Features/IBL.cpp @@ -14,19 +14,38 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT( EnableDiffuseIBL, PreserveFogLuminance, UseStaticIBL, - EnableInterior, - DiffuseIBLScale, DALCAmount, - IBLSaturation, - FogAmount) + EnvIBLScale, + SkyIBLScale, + EnvIBLSaturation, + SkyIBLSaturation, + FogAmount, + DALCMode) void IBL::DrawSettings() { Util::WeatherUI::Checkbox("Enable Diffuse IBL", this, "EnableDiffuseIBL", (bool*)&settings.EnableDiffuseIBL); - Util::WeatherUI::SliderFloat("Diffuse IBL Scale", this, "DiffuseIBLScale", &settings.DiffuseIBLScale, 0.0f, 10.0f, "%.2f"); - Util::WeatherUI::SliderFloat("Diffuse IBL Saturation", this, "IBLSaturation", &settings.IBLSaturation, 0.0f, 2.0f, "%.2f"); + Util::WeatherUI::SliderFloat("Env IBL Scale", this, "EnvIBLScale", &settings.EnvIBLScale, 0.0f, 10.0f, "%.2f"); + Util::WeatherUI::SliderFloat("Sky IBL Scale", this, "SkyIBLScale", &settings.SkyIBLScale, 0.0f, 10.0f, "%.2f"); + Util::WeatherUI::SliderFloat("Env IBL Saturation", this, "EnvIBLSaturation", &settings.EnvIBLSaturation, 0.0f, 2.0f, "%.2f"); + Util::WeatherUI::SliderFloat("Sky IBL Saturation", this, "SkyIBLSaturation", &settings.SkyIBLSaturation, 0.0f, 2.0f, "%.2f"); Util::WeatherUI::SliderFloat("DALC Amount", this, "DALCAmount", &settings.DALCAmount, 0.0f, 1.0f, "%.2f"); - ImGui::Checkbox("Enable Interior", (bool*)&settings.EnableInterior); + if (auto _tt = Util::HoverTooltipWrapper()) { + ImGui::Text("Controls how much the IBL brightness is matched to the game's ambient light level."); + } + { + static const char* dalcModeNames[] = { "Luminance Ratio", "Color Ratio", "DALC + Sky" }; + int dalcMode = static_cast(settings.DALCMode); + if (ImGui::Combo("DALC Mode", &dalcMode, dalcModeNames, IM_ARRAYSIZE(dalcModeNames))) { + settings.DALCMode = static_cast(dalcMode); + } + if (auto _tt = Util::HoverTooltipWrapper()) { + ImGui::Text( + "Luminance Ratio: Scalar brightness ratio (loses DALC color tint).\n" + "Color Ratio: Per-channel ratio (preserves DALC color tint).\n" + "DALC + Sky: Use vanilla DALC as base, overlay sky IBL on top."); + } + } ImGui::Checkbox("Use Static IBL For Out-of-World Objects", (bool*)&settings.UseStaticIBL); if (auto _tt = Util::HoverTooltipWrapper()) { ImGui::Text("Enables the use of static IBL textures for objects that are not in the world (e.g. inventory items)."); @@ -65,31 +84,49 @@ void IBL::RegisterWeatherVariables() return factor > 0.5f ? to : from; // Switch at transition midpoint })); - // Register diffuse IBL scale - controls the overall intensity of diffuse IBL + // Register env IBL scale - controls the intensity of environment IBL registry->RegisterVariable(std::make_shared( - "DiffuseIBLScale", - "Diffuse IBL Scale", - "Controls the overall intensity of diffuse IBL lighting", - &settings.DiffuseIBLScale, + "EnvIBLScale", + "Env IBL Scale", + "Controls the intensity of environment IBL lighting", + &settings.EnvIBLScale, 1.0f, 0.0f, 10.0f)); - // Register IBL saturation - controls color saturation of IBL + // Register sky IBL scale - controls the intensity of sky IBL registry->RegisterVariable(std::make_shared( - "IBLSaturation", - "IBL Saturation", - "Controls the color saturation of IBL lighting", - &settings.IBLSaturation, + "SkyIBLScale", + "Sky IBL Scale", + "Controls the intensity of sky IBL lighting", + &settings.SkyIBLScale, + 1.0f, + 0.0f, 10.0f)); + + // Register env IBL saturation - controls color saturation of env IBL + registry->RegisterVariable(std::make_shared( + "EnvIBLSaturation", + "Env IBL Saturation", + "Controls the color saturation of environment IBL lighting", + &settings.EnvIBLSaturation, 1.0f, 0.0f, 2.0f)); - // Register DALC amount - controls mixing with Directional Ambient Light Color + // Register sky IBL saturation - controls color saturation of sky IBL + registry->RegisterVariable(std::make_shared( + "SkyIBLSaturation", + "Sky IBL Saturation", + "Controls the color saturation of sky IBL lighting", + &settings.SkyIBLSaturation, + 1.0f, + 0.0f, 2.0f)); + + // Register DALC amount - controls how much IBL brightness matches game ambient registry->RegisterVariable(std::make_shared( "DALCAmount", "DALC Amount", - "Amount of DALC (Directional Ambient Light Color) mixing", + "Controls how much IBL brightness is matched to game ambient light level", &settings.DALCAmount, - 0.33f, + 1.0f, 0.0f, 1.0f)); // Register fog amount - controls fog mixing diff --git a/src/Features/IBL.h b/src/Features/IBL.h index 9b5dee67f5..8add51ffd8 100644 --- a/src/Features/IBL.h +++ b/src/Features/IBL.h @@ -45,11 +45,15 @@ struct IBL : Feature uint EnableDiffuseIBL = 0; uint PreserveFogLuminance = 0; uint UseStaticIBL = 1; - uint EnableInterior = 0; - float DiffuseIBLScale = 1.0f; float DALCAmount = 1.0f; - float IBLSaturation = 1.0f; + float EnvIBLScale = 1.0f; + float SkyIBLScale = 1.0f; + float EnvIBLSaturation = 1.0f; + float SkyIBLSaturation = 1.0f; float FogAmount = 0.0f; + uint DALCMode = 0; // 0: Luminance Ratio, 1: Color Ratio, 2: DALC + Sky + float pad0 = 0.0f; + float pad1 = 0.0f; } settings; eastl::unique_ptr staticDiffuseIBLTexture = nullptr; From b9912fa4bfe38b7fd546d71f3b0b070b33f71237 Mon Sep 17 00:00:00 2001 From: Jiaye Date: Sat, 28 Feb 2026 14:38:29 +0800 Subject: [PATCH 5/9] use actual sky cubemap --- features/IBL/Shaders/IBL/IBL.hlsli | 26 +++++++------------------- src/Features/IBL.cpp | 7 ++++--- 2 files changed, 11 insertions(+), 22 deletions(-) diff --git a/features/IBL/Shaders/IBL/IBL.hlsli b/features/IBL/Shaders/IBL/IBL.hlsli index b21dc55de1..04e40fe11e 100644 --- a/features/IBL/Shaders/IBL/IBL.hlsli +++ b/features/IBL/Shaders/IBL/IBL.hlsli @@ -31,27 +31,15 @@ namespace ImageBasedLighting return float3(colorR, colorG, colorB) / Math::PI; } - /// Get Sky-only IBL color: (full env+sky SH) - (env-only SH) + /// Get Sky-only IBL color from game's native reflections cubemap SH float3 GetSkyIBL(float3 rayDir) { - // Full SH (env + sky) - sh2 fullR = DiffuseSkyIBLTexture.Load(int3(0, 0, 0)); - sh2 fullG = DiffuseSkyIBLTexture.Load(int3(1, 0, 0)); - sh2 fullB = DiffuseSkyIBLTexture.Load(int3(2, 0, 0)); - - // Env-only SH - sh2 envR = DiffuseIBLTexture.Load(int3(0, 0, 0)); - sh2 envG = DiffuseIBLTexture.Load(int3(1, 0, 0)); - sh2 envB = DiffuseIBLTexture.Load(int3(2, 0, 0)); - - // Sky-only SH = full - env - sh2 skyR = fullR - envR; - sh2 skyG = fullG - envG; - sh2 skyB = fullB - envB; - - float colorR = SphericalHarmonics::SHHallucinateZH3Irradiance(skyR, rayDir); - float colorG = SphericalHarmonics::SHHallucinateZH3Irradiance(skyG, rayDir); - float colorB = SphericalHarmonics::SHHallucinateZH3Irradiance(skyB, rayDir); + sh2 shR = DiffuseSkyIBLTexture.Load(int3(0, 0, 0)); + sh2 shG = DiffuseSkyIBLTexture.Load(int3(1, 0, 0)); + sh2 shB = DiffuseSkyIBLTexture.Load(int3(2, 0, 0)); + float colorR = SphericalHarmonics::SHHallucinateZH3Irradiance(shR, rayDir); + float colorG = SphericalHarmonics::SHHallucinateZH3Irradiance(shG, rayDir); + float colorB = SphericalHarmonics::SHHallucinateZH3Irradiance(shB, rayDir); return max(0, float3(colorR, colorG, colorB) / Math::PI); } diff --git a/src/Features/IBL.cpp b/src/Features/IBL.cpp index 15400d0a00..63ed6e710b 100644 --- a/src/Features/IBL.cpp +++ b/src/Features/IBL.cpp @@ -165,7 +165,6 @@ void IBL::Prepass() auto& dynamicCubemaps = globals::features::dynamicCubemaps; auto& envTexture = dynamicCubemaps.envTexture; - auto& envReflectionsTexture = dynamicCubemaps.envReflectionsTexture; // Unset PS shader resource { @@ -189,9 +188,11 @@ void IBL::Prepass() context->Dispatch(1, 1, 1); } - // IBL with sky + // IBL with sky (use game's native reflections cubemap directly) { - srvs.at(0) = (dynamicCubemaps.loaded && envReflectionsTexture) ? envReflectionsTexture->srv.get() : nullptr; + auto renderer = globals::game::renderer; + auto& reflections = renderer->GetRendererData().cubemapRenderTargets[RE::RENDER_TARGETS_CUBEMAP::kREFLECTIONS]; + srvs.at(0) = reflections.SRV; uavs.at(0) = diffuseSkyIBLTexture->uav.get(); context->CSSetShaderResources(0, (uint)srvs.size(), srvs.data()); From 403ad5dd3cf85aa8ed939551c8786309d2ed93f4 Mon Sep 17 00:00:00 2001 From: Jiaye Date: Sun, 1 Mar 2026 02:25:02 +0800 Subject: [PATCH 6/9] semantic renames --- .../DynamicCubemaps/DynamicCubemaps.hlsli | 8 +- features/IBL/Shaders/IBL/IBL.hlsli | 26 +++--- package/Shaders/Common/ShadowSampling.hlsli | 2 +- package/Shaders/Common/SharedData.hlsli | 2 +- package/Shaders/DeferredCompositeCS.hlsl | 2 +- package/Shaders/DistantTree.hlsl | 4 +- package/Shaders/Effect.hlsl | 2 +- package/Shaders/ISSAOComposite.hlsl | 2 +- package/Shaders/Lighting.hlsl | 6 +- package/Shaders/RunGrass.hlsl | 4 +- package/Shaders/Water.hlsl | 6 +- src/Deferred.cpp | 4 +- src/Features/IBL.cpp | 92 ++++++++++++------- src/Features/IBL.h | 17 ++-- 14 files changed, 101 insertions(+), 76 deletions(-) diff --git a/features/Dynamic Cubemaps/Shaders/DynamicCubemaps/DynamicCubemaps.hlsli b/features/Dynamic Cubemaps/Shaders/DynamicCubemaps/DynamicCubemaps.hlsli index 0d374e6b71..055e444660 100644 --- a/features/Dynamic Cubemaps/Shaders/DynamicCubemaps/DynamicCubemaps.hlsli +++ b/features/Dynamic Cubemaps/Shaders/DynamicCubemaps/DynamicCubemaps.hlsli @@ -48,7 +48,7 @@ namespace DynamicCubemaps # if defined(IBL) && defined(LIGHTING) const bool inWorld = (Permutation::ExtraShaderDescriptor & Permutation::ExtraFlags::InWorld); const bool inReflection = (Permutation::ExtraShaderDescriptor & Permutation::ExtraFlags::InReflection); - if (SharedData::iblSettings.EnableDiffuseIBL && SharedData::iblSettings.UseStaticIBL && !inWorld && !inReflection) { + if (SharedData::iblSettings.EnableIBL && SharedData::iblSettings.UseStaticIBL && !inWorld && !inReflection) { float3 specularIrradiance = ImageBasedLighting::StaticSpecularIBLTexture.SampleLevel(SampColorSampler, R.xzy, level).xyz; finalIrradiance = specularIrradiance; return finalIrradiance; @@ -68,7 +68,7 @@ namespace DynamicCubemaps # endif # if defined(IBL) - if (SharedData::iblSettings.EnableDiffuseIBL) { + if (SharedData::iblSettings.EnableIBL) { float3 envSample = EnvTexture.SampleLevel(SampColorSampler, R, level); float3 fullSample = EnvReflectionsTexture.SampleLevel(SampColorSampler, R, level); float3 envSpecular, skySpecular; @@ -160,7 +160,7 @@ namespace DynamicCubemaps # if defined(IBL) && defined(LIGHTING) const bool inWorld = (Permutation::ExtraShaderDescriptor & Permutation::ExtraFlags::InWorld); const bool inReflection = (Permutation::ExtraShaderDescriptor & Permutation::ExtraFlags::InReflection); - if (SharedData::iblSettings.EnableDiffuseIBL && SharedData::iblSettings.UseStaticIBL && !inWorld && !inReflection) { + if (SharedData::iblSettings.EnableIBL && SharedData::iblSettings.UseStaticIBL && !inWorld && !inReflection) { float3 specularIrradiance = ImageBasedLighting::StaticSpecularIBLTexture.SampleLevel(SampColorSampler, R.xzy, level).xyz; finalIrradiance += specularIrradiance; return horizon * (F0 * specularBRDF.x + specularBRDF.y) * finalIrradiance; @@ -180,7 +180,7 @@ namespace DynamicCubemaps # endif # if defined(IBL) - if (SharedData::iblSettings.EnableDiffuseIBL) { + if (SharedData::iblSettings.EnableIBL) { float3 envSample = EnvTexture.SampleLevel(SampColorSampler, R, level); float3 fullSample = EnvReflectionsTexture.SampleLevel(SampColorSampler, R, level); float3 envSpecular, skySpecular; diff --git a/features/IBL/Shaders/IBL/IBL.hlsli b/features/IBL/Shaders/IBL/IBL.hlsli index 04e40fe11e..b99cc4c035 100644 --- a/features/IBL/Shaders/IBL/IBL.hlsli +++ b/features/IBL/Shaders/IBL/IBL.hlsli @@ -10,11 +10,11 @@ namespace ImageBasedLighting { #if defined(IBL_DEFERRED) - Texture2D DiffuseIBLTexture : register(t14); - Texture2D DiffuseSkyIBLTexture : register(t15); + Texture2D EnvIBLTexture : register(t14); + Texture2D SkyIBLTexture : register(t15); #else - Texture2D DiffuseIBLTexture : register(t76); - Texture2D DiffuseSkyIBLTexture : register(t77); + Texture2D EnvIBLTexture : register(t76); + Texture2D SkyIBLTexture : register(t77); TextureCube StaticDiffuseIBLTexture : register(t78); TextureCube StaticSpecularIBLTexture : register(t79); #endif @@ -22,9 +22,9 @@ namespace ImageBasedLighting /// Get Env IBL color from environment cubemap SH (without sky) float3 GetEnvIBL(float3 rayDir) { - sh2 shR = DiffuseIBLTexture.Load(int3(0, 0, 0)); - sh2 shG = DiffuseIBLTexture.Load(int3(1, 0, 0)); - sh2 shB = DiffuseIBLTexture.Load(int3(2, 0, 0)); + sh2 shR = EnvIBLTexture.Load(int3(0, 0, 0)); + sh2 shG = EnvIBLTexture.Load(int3(1, 0, 0)); + sh2 shB = EnvIBLTexture.Load(int3(2, 0, 0)); float colorR = SphericalHarmonics::SHHallucinateZH3Irradiance(shR, rayDir); float colorG = SphericalHarmonics::SHHallucinateZH3Irradiance(shG, rayDir); float colorB = SphericalHarmonics::SHHallucinateZH3Irradiance(shB, rayDir); @@ -34,9 +34,9 @@ namespace ImageBasedLighting /// Get Sky-only IBL color from game's native reflections cubemap SH float3 GetSkyIBL(float3 rayDir) { - sh2 shR = DiffuseSkyIBLTexture.Load(int3(0, 0, 0)); - sh2 shG = DiffuseSkyIBLTexture.Load(int3(1, 0, 0)); - sh2 shB = DiffuseSkyIBLTexture.Load(int3(2, 0, 0)); + sh2 shR = SkyIBLTexture.Load(int3(0, 0, 0)); + sh2 shG = SkyIBLTexture.Load(int3(1, 0, 0)); + sh2 shB = SkyIBLTexture.Load(int3(2, 0, 0)); float colorR = SphericalHarmonics::SHHallucinateZH3Irradiance(shR, rayDir); float colorG = SphericalHarmonics::SHHallucinateZH3Irradiance(shG, rayDir); float colorB = SphericalHarmonics::SHHallucinateZH3Irradiance(shB, rayDir); @@ -53,9 +53,9 @@ namespace ImageBasedLighting float3 dalc0 = Color::Ambient(SharedData::GetAmbient(0.f)); // 0th order IBL SH (DC term from env cubemap) - sh2 iblSHR = DiffuseIBLTexture.Load(int3(0, 0, 0)); - sh2 iblSHG = DiffuseIBLTexture.Load(int3(1, 0, 0)); - sh2 iblSHB = DiffuseIBLTexture.Load(int3(2, 0, 0)); + sh2 iblSHR = EnvIBLTexture.Load(int3(0, 0, 0)); + sh2 iblSHG = EnvIBLTexture.Load(int3(1, 0, 0)); + sh2 iblSHB = EnvIBLTexture.Load(int3(2, 0, 0)); float colorR = SphericalHarmonics::SHHallucinateZH3Irradiance(iblSHR, float3(0, 0, 0)); float colorG = SphericalHarmonics::SHHallucinateZH3Irradiance(iblSHG, float3(0, 0, 0)); diff --git a/package/Shaders/Common/ShadowSampling.hlsli b/package/Shaders/Common/ShadowSampling.hlsli index 3d8ecbfa0a..94f9115a10 100644 --- a/package/Shaders/Common/ShadowSampling.hlsli +++ b/package/Shaders/Common/ShadowSampling.hlsli @@ -110,7 +110,7 @@ namespace ShadowSampling float3 ambientColorAmb = max(0, SharedData::GetAmbient(float3(0, 0, 1))); #if defined(IBL) - if (SharedData::iblSettings.EnableDiffuseIBL) { + if (SharedData::iblSettings.EnableIBL) { if (SharedData::iblSettings.DALCMode == 2) { // Mode 2: keep vanilla DALC, add sky IBL overlay ambientColorAmb += Color::IrradianceToGamma(ImageBasedLighting::GetSkyIBLColor(float3(0, 0, -1))); diff --git a/package/Shaders/Common/SharedData.hlsli b/package/Shaders/Common/SharedData.hlsli index b4aaba4642..4ea0d4d07c 100644 --- a/package/Shaders/Common/SharedData.hlsli +++ b/package/Shaders/Common/SharedData.hlsli @@ -187,7 +187,7 @@ namespace SharedData struct IBLSettings { - uint EnableDiffuseIBL; + uint EnableIBL; uint PreserveFogLuminance; uint UseStaticIBL; float DALCAmount; diff --git a/package/Shaders/DeferredCompositeCS.hlsl b/package/Shaders/DeferredCompositeCS.hlsl index 7e89780d64..8592419eea 100644 --- a/package/Shaders/DeferredCompositeCS.hlsl +++ b/package/Shaders/DeferredCompositeCS.hlsl @@ -188,7 +188,7 @@ void SampleSSGISpecular(uint2 pixCoord, sh2 lobe, out float ao, out float3 il, i # endif # if defined(IBL) - if (SharedData::iblSettings.EnableDiffuseIBL) { + if (SharedData::iblSettings.EnableIBL) { float3 envSample = EnvTexture.SampleLevel(LinearSampler, R, level); float3 fullSample = EnvReflectionsTexture.SampleLevel(LinearSampler, R, level); float3 envSpecular, skySpecular; diff --git a/package/Shaders/DistantTree.hlsl b/package/Shaders/DistantTree.hlsl index d9d155a82f..428dedc9c5 100644 --- a/package/Shaders/DistantTree.hlsl +++ b/package/Shaders/DistantTree.hlsl @@ -246,7 +246,7 @@ PS_OUTPUT main(PS_INPUT input) float3 directionalAmbientColor = max(0, Color::Ambient(SharedData::GetAmbient(normal))); # if defined(IBL) - if (SharedData::iblSettings.EnableDiffuseIBL) { + if (SharedData::iblSettings.EnableIBL) { if (SharedData::iblSettings.DALCMode == 2) { // Mode 2: keep vanilla DALC, add sky IBL overlay directionalAmbientColor += Color::IrradianceToGamma(ImageBasedLighting::GetSkyIBLColor(-normal)); @@ -287,7 +287,7 @@ PS_OUTPUT main(PS_INPUT input) float3 directionalAmbientColor = Color::Ambient(SharedData::GetAmbient(normal)); # if defined(IBL) - if (SharedData::iblSettings.EnableDiffuseIBL) { + if (SharedData::iblSettings.EnableIBL) { if (SharedData::iblSettings.DALCMode == 2) { // Mode 2: keep vanilla DALC, add sky IBL overlay directionalAmbientColor += Color::IrradianceToGamma(ImageBasedLighting::GetSkyIBLColor(-normal)); diff --git a/package/Shaders/Effect.hlsl b/package/Shaders/Effect.hlsl index e7fb80101a..09a8a5e450 100644 --- a/package/Shaders/Effect.hlsl +++ b/package/Shaders/Effect.hlsl @@ -850,7 +850,7 @@ PS_OUTPUT main(PS_INPUT input) float fogFactor = Color::FogAlpha(input.FogParam.w); float3 fogColor = Color::Fog(input.FogParam.xyz); # if defined(IBL) - if (SharedData::iblSettings.EnableDiffuseIBL) { + if (SharedData::iblSettings.EnableIBL) { fogColor = ImageBasedLighting::GetFogIBLColor(fogColor); } # endif diff --git a/package/Shaders/ISSAOComposite.hlsl b/package/Shaders/ISSAOComposite.hlsl index 76fde19b23..68e3d48bbf 100644 --- a/package/Shaders/ISSAOComposite.hlsl +++ b/package/Shaders/ISSAOComposite.hlsl @@ -181,7 +181,7 @@ PS_OUTPUT main(PS_INPUT input) float fogFactor = min(FogParam.w, pow(saturate(fogDistanceFactor * FogParam.y - FogParam.x), FogParam.z)); float3 fogColor = Color::Fog(lerp(FogNearColor.xyz, FogFarColor.xyz, fogFactor)); # if defined(IBL) - if (SharedData::iblSettings.EnableDiffuseIBL) { + if (SharedData::iblSettings.EnableIBL) { fogColor = ImageBasedLighting::GetFogIBLColor(fogColor); } # endif diff --git a/package/Shaders/Lighting.hlsl b/package/Shaders/Lighting.hlsl index c0559cb627..be2e702094 100644 --- a/package/Shaders/Lighting.hlsl +++ b/package/Shaders/Lighting.hlsl @@ -2752,7 +2752,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) float3 directionalAmbientColor = Color::Ambient(max(0, SharedData::GetAmbient(ambientNormal))); # if defined(IBL) - if (SharedData::iblSettings.EnableDiffuseIBL) { + if (SharedData::iblSettings.EnableIBL) { if (SharedData::iblSettings.UseStaticIBL && !inWorld && !inReflection) { directionalAmbientColor = ImageBasedLighting::GetStaticDiffuseIBL(ambientNormal, SampColorSampler); } @@ -2773,7 +2773,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) # if defined(IBL) float3 envIBLColor = 0; - if (SharedData::iblSettings.EnableDiffuseIBL) { + if (SharedData::iblSettings.EnableIBL) { if (!(SharedData::iblSettings.UseStaticIBL && !inWorld && !inReflection)) { if (SharedData::iblSettings.DALCMode == 2) { // Mode 2: keep vanilla DALC, add sky IBL overlay; save DALC in envIBLColor so skylighting dance protects it @@ -2988,7 +2988,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) float3 fogColor = Color::Fog(input.FogParam.xyz); float fogFactor = Color::FogAlpha(input.FogParam.w); # if defined(IBL) - if (SharedData::iblSettings.EnableDiffuseIBL) { + if (SharedData::iblSettings.EnableIBL) { fogColor = ImageBasedLighting::GetFogIBLColor(fogColor); } # endif diff --git a/package/Shaders/RunGrass.hlsl b/package/Shaders/RunGrass.hlsl index 043fbc6301..d3bc20cb94 100644 --- a/package/Shaders/RunGrass.hlsl +++ b/package/Shaders/RunGrass.hlsl @@ -774,7 +774,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) # if defined(IBL) float3 envIBLColor = 0; - if (SharedData::iblSettings.EnableDiffuseIBL) { + if (SharedData::iblSettings.EnableIBL) { if (SharedData::iblSettings.DALCMode == 2) { // Mode 2: keep vanilla DALC, add sky IBL overlay; save DALC in envIBLColor so skylighting dance protects it envIBLColor = directionalAmbientColor; @@ -974,7 +974,7 @@ PS_OUTPUT main(PS_INPUT input) # if defined(IBL) float3 envIBLColor = 0; - if (SharedData::iblSettings.EnableDiffuseIBL) { + if (SharedData::iblSettings.EnableIBL) { if (SharedData::iblSettings.DALCMode == 2) { // Mode 2: keep vanilla DALC, add sky IBL overlay; save DALC in envIBLColor so skylighting dance protects it envIBLColor = directionalAmbientColor; diff --git a/package/Shaders/Water.hlsl b/package/Shaders/Water.hlsl index 23a0369930..f1b722ad2a 100644 --- a/package/Shaders/Water.hlsl +++ b/package/Shaders/Water.hlsl @@ -1267,7 +1267,7 @@ PS_OUTPUT main(PS_INPUT input) fogDistanceFactor = Color::FogAlpha(fogDistanceFactor); # if defined(IBL) - if (SharedData::iblSettings.EnableDiffuseIBL) { + if (SharedData::iblSettings.EnableIBL) { fogColor = ImageBasedLighting::GetFogIBLColor(fogColor); } # endif @@ -1305,7 +1305,7 @@ PS_OUTPUT main(PS_INPUT input) fogDistanceFactor = Color::FogAlpha(fogDistanceFactor); # if defined(IBL) - if (SharedData::iblSettings.EnableDiffuseIBL) { + if (SharedData::iblSettings.EnableIBL) { preFogColor = ImageBasedLighting::GetFogIBLColor(preFogColor); } # endif @@ -1330,7 +1330,7 @@ PS_OUTPUT main(PS_INPUT input) } # endif # if defined(IBL) - if (SharedData::iblSettings.EnableDiffuseIBL) { + if (SharedData::iblSettings.EnableIBL) { fogColor = ImageBasedLighting::GetFogIBLColor(fogColor); } # endif diff --git a/src/Deferred.cpp b/src/Deferred.cpp index 699325c192..a0a322500b 100644 --- a/src/Deferred.cpp +++ b/src/Deferred.cpp @@ -354,8 +354,8 @@ void Deferred::DeferredPasses() ssgi_hq_spec ? nullptr : ssgi_y, ssgi_hq_spec ? nullptr : ssgi_cocg, ssgi_hq_spec ? ssgi_gi_spec : nullptr, - ibl.loaded ? ibl.diffuseIBLTexture->srv.get() : nullptr, - ibl.loaded ? ibl.diffuseSkyIBLTexture->srv.get() : nullptr, + ibl.loaded ? ibl.envIBLTexture->srv.get() : nullptr, + ibl.loaded ? ibl.skyIBLTexture->srv.get() : nullptr, }; if (dynamicCubemaps.loaded) diff --git a/src/Features/IBL.cpp b/src/Features/IBL.cpp index 63ed6e710b..11ece7727c 100644 --- a/src/Features/IBL.cpp +++ b/src/Features/IBL.cpp @@ -11,7 +11,7 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT( IBL::Settings, - EnableDiffuseIBL, + EnableIBL, PreserveFogLuminance, UseStaticIBL, DALCAmount, @@ -24,14 +24,31 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT( void IBL::DrawSettings() { - Util::WeatherUI::Checkbox("Enable Diffuse IBL", this, "EnableDiffuseIBL", (bool*)&settings.EnableDiffuseIBL); + Util::WeatherUI::Checkbox("Enable IBL", this, "EnableIBL", (bool*)&settings.EnableIBL); + if (auto _tt = Util::HoverTooltipWrapper()) { + ImGui::Text("Toggle IBL. When enabled, ambient lighting is derived from cubemap spherical harmonics instead of the vanilla system."); + } Util::WeatherUI::SliderFloat("Env IBL Scale", this, "EnvIBLScale", &settings.EnvIBLScale, 0.0f, 10.0f, "%.2f"); + if (auto _tt = Util::HoverTooltipWrapper()) { + ImGui::Text("Intensity multiplier for the environment IBL (from Dynamic Cubemaps).\nControls how strongly the surrounding environment contributes to ambient lighting."); + } Util::WeatherUI::SliderFloat("Sky IBL Scale", this, "SkyIBLScale", &settings.SkyIBLScale, 0.0f, 10.0f, "%.2f"); + if (auto _tt = Util::HoverTooltipWrapper()) { + ImGui::Text("Intensity multiplier for the sky IBL (from the game's native reflections cubemap).\nControls how strongly the sky contributes to ambient lighting."); + } Util::WeatherUI::SliderFloat("Env IBL Saturation", this, "EnvIBLSaturation", &settings.EnvIBLSaturation, 0.0f, 2.0f, "%.2f"); + if (auto _tt = Util::HoverTooltipWrapper()) { + ImGui::Text("Color saturation of the environment IBL.\nLower values produce more neutral ambient light; higher values produce more vivid color."); + } Util::WeatherUI::SliderFloat("Sky IBL Saturation", this, "SkyIBLSaturation", &settings.SkyIBLSaturation, 0.0f, 2.0f, "%.2f"); + if (auto _tt = Util::HoverTooltipWrapper()) { + ImGui::Text("Color saturation of the sky IBL.\nLower values produce more neutral ambient light; higher values produce more vivid color."); + } Util::WeatherUI::SliderFloat("DALC Amount", this, "DALCAmount", &settings.DALCAmount, 0.0f, 1.0f, "%.2f"); if (auto _tt = Util::HoverTooltipWrapper()) { - ImGui::Text("Controls how much the IBL brightness is matched to the game's ambient light level."); + ImGui::Text( + "Blends the IBL brightness toward the game's vanilla ambient (DALC) level.\n" + "0 = no matching (pure IBL brightness), 1 = fully matched to vanilla ambient."); } { static const char* dalcModeNames[] = { "Luminance Ratio", "Color Ratio", "DALC + Sky" }; @@ -41,17 +58,24 @@ void IBL::DrawSettings() } if (auto _tt = Util::HoverTooltipWrapper()) { ImGui::Text( - "Luminance Ratio: Scalar brightness ratio (loses DALC color tint).\n" + "How the DALC-to-IBL brightness ratio is computed:\n" + "Luminance Ratio: Scalar ratio from overall luminance (loses DALC color tint).\n" "Color Ratio: Per-channel ratio (preserves DALC color tint).\n" - "DALC + Sky: Use vanilla DALC as base, overlay sky IBL on top."); + "DALC + Sky: Uses vanilla DALC as base and overlays sky IBL on top."); } } ImGui::Checkbox("Use Static IBL For Out-of-World Objects", (bool*)&settings.UseStaticIBL); if (auto _tt = Util::HoverTooltipWrapper()) { - ImGui::Text("Enables the use of static IBL textures for objects that are not in the world (e.g. inventory items)."); + ImGui::Text("Uses pre-baked static IBL cubemap textures for objects rendered outside the game world (e.g. inventory items, loading screens)."); } Util::WeatherUI::SliderFloat("Fog Mix", this, "FogAmount", &settings.FogAmount, 0.0f, 1.0f, "%.2f"); + if (auto _tt = Util::HoverTooltipWrapper()) { + ImGui::Text("Blends the fog color toward the IBL ambient color.\n0 = vanilla fog, 1 = fog fully tinted by IBL."); + } ImGui::Checkbox("Preserve Fog Luminance", (bool*)&settings.PreserveFogLuminance); + if (auto _tt = Util::HoverTooltipWrapper()) { + ImGui::Text("When Fog Mix is active, rescales the IBL-tinted fog to keep the original fog brightness.\nPrevents fog from becoming too bright or too dark."); + } } void IBL::LoadSettings(json& o_json) @@ -73,67 +97,67 @@ void IBL::RegisterWeatherVariables() { auto* registry = WeatherVariables::GlobalWeatherRegistry::GetSingleton() ->GetOrCreateFeatureRegistry(GetShortName()); - // Register enable diffuse IBL toggle + // Toggle IBL for this weather (SH-based ambient replaces vanilla) registry->RegisterVariable(std::make_shared>( - "EnableDiffuseIBL", - "Enable Diffuse IBL", - "Enable or disable diffuse IBL for this weather", - (bool*)&settings.EnableDiffuseIBL, + "EnableIBL", + "Enable IBL", + "Enable or disable SH-based ambient lighting for this weather", + (bool*)&settings.EnableIBL, true, [](const bool& from, const bool& to, float factor) { return factor > 0.5f ? to : from; // Switch at transition midpoint })); - // Register env IBL scale - controls the intensity of environment IBL + // Intensity of environment IBL (from Dynamic Cubemaps) registry->RegisterVariable(std::make_shared( "EnvIBLScale", "Env IBL Scale", - "Controls the intensity of environment IBL lighting", + "Intensity of environment IBL from the Dynamic Cubemaps environment cubemap", &settings.EnvIBLScale, 1.0f, 0.0f, 10.0f)); - // Register sky IBL scale - controls the intensity of sky IBL + // Intensity of sky IBL (from the game's native reflections cubemap) registry->RegisterVariable(std::make_shared( "SkyIBLScale", "Sky IBL Scale", - "Controls the intensity of sky IBL lighting", + "Intensity of sky IBL from the game's native reflections cubemap", &settings.SkyIBLScale, 1.0f, 0.0f, 10.0f)); - // Register env IBL saturation - controls color saturation of env IBL + // Color saturation of environment IBL registry->RegisterVariable(std::make_shared( "EnvIBLSaturation", "Env IBL Saturation", - "Controls the color saturation of environment IBL lighting", + "Color saturation of the environment IBL ambient contribution", &settings.EnvIBLSaturation, 1.0f, 0.0f, 2.0f)); - // Register sky IBL saturation - controls color saturation of sky IBL + // Color saturation of sky IBL registry->RegisterVariable(std::make_shared( "SkyIBLSaturation", "Sky IBL Saturation", - "Controls the color saturation of sky IBL lighting", + "Color saturation of the sky IBL ambient contribution", &settings.SkyIBLSaturation, 1.0f, 0.0f, 2.0f)); - // Register DALC amount - controls how much IBL brightness matches game ambient + // How much IBL brightness is matched to vanilla ambient (DALC) registry->RegisterVariable(std::make_shared( "DALCAmount", "DALC Amount", - "Controls how much IBL brightness is matched to game ambient light level", + "Blend factor toward vanilla ambient brightness (0 = pure IBL, 1 = fully matched to DALC)", &settings.DALCAmount, 1.0f, 0.0f, 1.0f)); - // Register fog amount - controls fog mixing + // Fog color blending toward IBL ambient color registry->RegisterVariable(std::make_shared( "FogAmount", "Fog Mix", - "Amount of fog mixed into IBL", + "Blends fog color toward IBL ambient color (0 = vanilla fog, 1 = fully IBL-tinted)", &settings.FogAmount, 0.0f, 0.0f, 1.0f)); @@ -147,8 +171,8 @@ void IBL::ReflectionsPrepass() // Set PS shader resource { std::array srvs = { - diffuseIBLTexture->srv.get(), - diffuseSkyIBLTexture->srv.get(), + envIBLTexture->srv.get(), + skyIBLTexture->srv.get(), staticDiffuseIBLTexture->srv.get(), staticSpecularIBLTexture->srv.get() }; @@ -174,7 +198,7 @@ void IBL::Prepass() state->BeginPerfEvent("IBL"); std::array srvs = { (dynamicCubemaps.loaded && envTexture) ? envTexture->srv.get() : nullptr }; - std::array uavs = { diffuseIBLTexture->uav.get() }; + std::array uavs = { envIBLTexture->uav.get() }; std::array samplers = { Deferred::GetSingleton()->linearSampler }; // IBL @@ -193,7 +217,7 @@ void IBL::Prepass() auto renderer = globals::game::renderer; auto& reflections = renderer->GetRendererData().cubemapRenderTargets[RE::RENDER_TARGETS_CUBEMAP::kREFLECTIONS]; srvs.at(0) = reflections.SRV; - uavs.at(0) = diffuseSkyIBLTexture->uav.get(); + uavs.at(0) = skyIBLTexture->uav.get(); context->CSSetShaderResources(0, (uint)srvs.size(), srvs.data()); context->CSSetUnorderedAccessViews(0, (uint)uavs.size(), uavs.data(), nullptr); @@ -215,7 +239,7 @@ void IBL::Prepass() // Set PS shader resource { - ID3D11ShaderResourceView* views[2]{ diffuseIBLTexture->srv.get(), diffuseSkyIBLTexture->srv.get() }; + ID3D11ShaderResourceView* views[2]{ envIBLTexture->srv.get(), skyIBLTexture->srv.get() }; context->PSSetShaderResources(76, 2, views); } } @@ -250,12 +274,12 @@ void IBL::SetupResources() .Texture2D = { .MipSlice = 0 } }; - diffuseIBLTexture = new Texture2D(texDesc); - diffuseIBLTexture->CreateSRV(srvDesc); - diffuseIBLTexture->CreateUAV(uavDesc); - diffuseSkyIBLTexture = new Texture2D(texDesc); - diffuseSkyIBLTexture->CreateSRV(srvDesc); - diffuseSkyIBLTexture->CreateUAV(uavDesc); + envIBLTexture = new Texture2D(texDesc); + envIBLTexture->CreateSRV(srvDesc); + envIBLTexture->CreateUAV(uavDesc); + skyIBLTexture = new Texture2D(texDesc); + skyIBLTexture->CreateSRV(srvDesc); + skyIBLTexture->CreateUAV(uavDesc); } auto device = globals::d3d::device; diff --git a/src/Features/IBL.h b/src/Features/IBL.h index 8add51ffd8..71288fa299 100644 --- a/src/Features/IBL.h +++ b/src/Features/IBL.h @@ -14,18 +14,19 @@ struct IBL : Feature virtual std::pair> GetFeatureSummary() override { return { - "Image Based Lighting provides realistic diffuse ambient lighting for exteriors.", - { "Realistic diffuse ambient lighting from environment maps", - "Spherical harmonics-based ambient light calculation", - "Enhanced exterior ambient lighting quality", - "Configurable intensity and saturation, mixing with DALC" } + "Replaces the game's ambient lighting with physically-based IBL derived from cubemap spherical harmonics.", + { "Projects environment and sky cubemaps into spherical harmonics (SH) for irradiance", + "Dual IBL sources: environment cubemap (Dynamic Cubemaps) and Skyrim's native sky reflections cubemap", + "DALC brightness matching to keep IBL consistent with the game's ambient light levels", + "Configurable per-source intensity, saturation, fog mixing, and per-weather overrides", + "Static IBL fallback textures for out-of-world objects (e.g. inventory items)" } }; } bool HasShaderDefine(RE::BSShader::Type) override { return true; }; - Texture2D* diffuseIBLTexture = nullptr; - Texture2D* diffuseSkyIBLTexture = nullptr; + Texture2D* envIBLTexture = nullptr; + Texture2D* skyIBLTexture = nullptr; ID3D11ComputeShader* diffuseIBLCS = nullptr; virtual void RestoreDefaultSettings() override; @@ -42,7 +43,7 @@ struct IBL : Feature struct Settings { - uint EnableDiffuseIBL = 0; + uint EnableIBL = 0; uint PreserveFogLuminance = 0; uint UseStaticIBL = 1; float DALCAmount = 1.0f; From 42a51ad99d1b5a7387483f06741c6f35b15b95df Mon Sep 17 00:00:00 2001 From: Jiaye Date: Thu, 5 Mar 2026 14:15:56 +0800 Subject: [PATCH 7/9] ibl amount work on dalc + sky mode --- .../Shaders/DynamicCubemaps/DynamicCubemaps.hlsli | 8 ++++---- package/Shaders/Common/ShadowSampling.hlsli | 4 ++-- package/Shaders/DeferredCompositeCS.hlsl | 4 ++-- package/Shaders/DistantTree.hlsl | 8 ++++---- package/Shaders/Lighting.hlsl | 6 +++--- package/Shaders/RunGrass.hlsl | 12 ++++++------ src/Features/IBL.h | 2 +- 7 files changed, 22 insertions(+), 22 deletions(-) diff --git a/features/Dynamic Cubemaps/Shaders/DynamicCubemaps/DynamicCubemaps.hlsli b/features/Dynamic Cubemaps/Shaders/DynamicCubemaps/DynamicCubemaps.hlsli index 055e444660..af066bda40 100644 --- a/features/Dynamic Cubemaps/Shaders/DynamicCubemaps/DynamicCubemaps.hlsli +++ b/features/Dynamic Cubemaps/Shaders/DynamicCubemaps/DynamicCubemaps.hlsli @@ -74,9 +74,9 @@ namespace DynamicCubemaps float3 envSpecular, skySpecular; if (SharedData::iblSettings.DALCMode == 2) { - // Mode 2: DALC-normalized env + sky overlay + // Mode 2: DALC-normalized env scaled by DALCAmount + sky overlay float envLum = Color::RGBToLuminance(EnvTexture.SampleLevel(SampColorSampler, R, 15)); - envSpecular = Color::IrradianceToLinear((envSample / max(envLum, 0.001)) * directionalAmbientColorSpecular); + envSpecular = Color::IrradianceToLinear((envSample / max(envLum, 0.001)) * directionalAmbientColorSpecular) * SharedData::iblSettings.DALCAmount; skySpecular = Color::IrradianceToLinear(max(0, fullSample - envSample)) * SharedData::iblSettings.SkyIBLScale; # if defined(SKYLIGHTING) skySpecular *= skylightingSpecular; @@ -186,9 +186,9 @@ namespace DynamicCubemaps float3 envSpecular, skySpecular; if (SharedData::iblSettings.DALCMode == 2) { - // Mode 2: DALC-normalized env + sky overlay + // Mode 2: DALC-normalized env scaled by DALCAmount + sky overlay float envLum = Color::RGBToLuminance(EnvTexture.SampleLevel(SampColorSampler, R, 15)); - envSpecular = Color::IrradianceToLinear((envSample / max(envLum, 0.001)) * directionalAmbientColorSpecular); + envSpecular = Color::IrradianceToLinear((envSample / max(envLum, 0.001)) * directionalAmbientColorSpecular) * SharedData::iblSettings.DALCAmount; skySpecular = Color::IrradianceToLinear(max(0, fullSample - envSample)) * SharedData::iblSettings.SkyIBLScale; # if defined(SKYLIGHTING) skySpecular *= skylightingSpecular; diff --git a/package/Shaders/Common/ShadowSampling.hlsli b/package/Shaders/Common/ShadowSampling.hlsli index 94f9115a10..75728e0322 100644 --- a/package/Shaders/Common/ShadowSampling.hlsli +++ b/package/Shaders/Common/ShadowSampling.hlsli @@ -112,8 +112,8 @@ namespace ShadowSampling #if defined(IBL) if (SharedData::iblSettings.EnableIBL) { if (SharedData::iblSettings.DALCMode == 2) { - // Mode 2: keep vanilla DALC, add sky IBL overlay - ambientColorAmb += Color::IrradianceToGamma(ImageBasedLighting::GetSkyIBLColor(float3(0, 0, -1))); + // Mode 2: keep vanilla DALC scaled by DALCAmount, add sky IBL overlay + ambientColorAmb = ambientColorAmb * SharedData::iblSettings.DALCAmount + Color::IrradianceToGamma(ImageBasedLighting::GetSkyIBLColor(float3(0, 0, -1))); } else { float3 envIBLColor = Color::IrradianceToGamma(ImageBasedLighting::GetEnvIBLColor(float3(0, 0, -1))); float3 skyIBLColor = Color::IrradianceToGamma(ImageBasedLighting::GetSkyIBLColor(float3(0, 0, -1))); diff --git a/package/Shaders/DeferredCompositeCS.hlsl b/package/Shaders/DeferredCompositeCS.hlsl index 8592419eea..38008c4a13 100644 --- a/package/Shaders/DeferredCompositeCS.hlsl +++ b/package/Shaders/DeferredCompositeCS.hlsl @@ -194,9 +194,9 @@ void SampleSSGISpecular(uint2 pixCoord, sh2 lobe, out float ao, out float3 il, i float3 envSpecular, skySpecular; if (SharedData::iblSettings.DALCMode == 2) { - // Mode 2: DALC-normalized env + sky overlay + // Mode 2: DALC-normalized env scaled by DALCAmount + sky overlay float envLum = Color::RGBToLuminance(EnvTexture.SampleLevel(LinearSampler, R, 15)); - envSpecular = Color::IrradianceToLinear((envSample / max(envLum, 0.001)) * directionalAmbientColorSpecular); + envSpecular = Color::IrradianceToLinear((envSample / max(envLum, 0.001)) * directionalAmbientColorSpecular) * SharedData::iblSettings.DALCAmount; skySpecular = Color::IrradianceToLinear(max(0, fullSample - envSample)) * SharedData::iblSettings.SkyIBLScale; # if defined(SKYLIGHTING) skySpecular *= skylightingSpecular; diff --git a/package/Shaders/DistantTree.hlsl b/package/Shaders/DistantTree.hlsl index 428dedc9c5..cbd4608676 100644 --- a/package/Shaders/DistantTree.hlsl +++ b/package/Shaders/DistantTree.hlsl @@ -248,8 +248,8 @@ PS_OUTPUT main(PS_INPUT input) # if defined(IBL) if (SharedData::iblSettings.EnableIBL) { if (SharedData::iblSettings.DALCMode == 2) { - // Mode 2: keep vanilla DALC, add sky IBL overlay - directionalAmbientColor += Color::IrradianceToGamma(ImageBasedLighting::GetSkyIBLColor(-normal)); + // Mode 2: keep vanilla DALC scaled by DALCAmount, add sky IBL overlay + directionalAmbientColor = directionalAmbientColor * SharedData::iblSettings.DALCAmount + Color::IrradianceToGamma(ImageBasedLighting::GetSkyIBLColor(-normal)); } else { float3 envIBLColor = Color::IrradianceToGamma(ImageBasedLighting::GetEnvIBLColor(-normal)); float3 skyIBLColor = Color::IrradianceToGamma(ImageBasedLighting::GetSkyIBLColor(-normal)); @@ -289,8 +289,8 @@ PS_OUTPUT main(PS_INPUT input) # if defined(IBL) if (SharedData::iblSettings.EnableIBL) { if (SharedData::iblSettings.DALCMode == 2) { - // Mode 2: keep vanilla DALC, add sky IBL overlay - directionalAmbientColor += Color::IrradianceToGamma(ImageBasedLighting::GetSkyIBLColor(-normal)); + // Mode 2: keep vanilla DALC scaled by DALCAmount, add sky IBL overlay + directionalAmbientColor = directionalAmbientColor * SharedData::iblSettings.DALCAmount + Color::IrradianceToGamma(ImageBasedLighting::GetSkyIBLColor(-normal)); } else { float3 envIBLColor = Color::IrradianceToGamma(ImageBasedLighting::GetEnvIBLColor(-normal)); float3 skyIBLColor = Color::IrradianceToGamma(ImageBasedLighting::GetSkyIBLColor(-normal)); diff --git a/package/Shaders/Lighting.hlsl b/package/Shaders/Lighting.hlsl index be2e702094..70c55052a3 100644 --- a/package/Shaders/Lighting.hlsl +++ b/package/Shaders/Lighting.hlsl @@ -2776,9 +2776,9 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) if (SharedData::iblSettings.EnableIBL) { if (!(SharedData::iblSettings.UseStaticIBL && !inWorld && !inReflection)) { if (SharedData::iblSettings.DALCMode == 2) { - // Mode 2: keep vanilla DALC, add sky IBL overlay; save DALC in envIBLColor so skylighting dance protects it - envIBLColor = directionalAmbientColor; - directionalAmbientColor += Color::IrradianceToGamma(ImageBasedLighting::GetSkyIBLColor(-ambientNormal)); + // Mode 2: keep vanilla DALC scaled by DALCAmount, add sky IBL overlay + envIBLColor = directionalAmbientColor * SharedData::iblSettings.DALCAmount; + directionalAmbientColor = envIBLColor + Color::IrradianceToGamma(ImageBasedLighting::GetSkyIBLColor(-ambientNormal)); } else { // Mode 0/1: replace with IBL ratio envIBLColor = Color::IrradianceToGamma(ImageBasedLighting::GetEnvIBLColor(-ambientNormal)); diff --git a/package/Shaders/RunGrass.hlsl b/package/Shaders/RunGrass.hlsl index d3bc20cb94..c056898c26 100644 --- a/package/Shaders/RunGrass.hlsl +++ b/package/Shaders/RunGrass.hlsl @@ -776,9 +776,9 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) float3 envIBLColor = 0; if (SharedData::iblSettings.EnableIBL) { if (SharedData::iblSettings.DALCMode == 2) { - // Mode 2: keep vanilla DALC, add sky IBL overlay; save DALC in envIBLColor so skylighting dance protects it - envIBLColor = directionalAmbientColor; - directionalAmbientColor += Color::IrradianceToGamma(ImageBasedLighting::GetSkyIBLColor(-normal)); + // Mode 2: keep vanilla DALC scaled by DALCAmount, add sky IBL overlay + envIBLColor = directionalAmbientColor * SharedData::iblSettings.DALCAmount; + directionalAmbientColor = envIBLColor + Color::IrradianceToGamma(ImageBasedLighting::GetSkyIBLColor(-normal)); } else { envIBLColor = Color::IrradianceToGamma(ImageBasedLighting::GetEnvIBLColor(-normal)); float3 skyIBLColor = Color::IrradianceToGamma(ImageBasedLighting::GetSkyIBLColor(-normal)); @@ -976,9 +976,9 @@ PS_OUTPUT main(PS_INPUT input) float3 envIBLColor = 0; if (SharedData::iblSettings.EnableIBL) { if (SharedData::iblSettings.DALCMode == 2) { - // Mode 2: keep vanilla DALC, add sky IBL overlay; save DALC in envIBLColor so skylighting dance protects it - envIBLColor = directionalAmbientColor; - directionalAmbientColor += Color::IrradianceToGamma(ImageBasedLighting::GetSkyIBLColor(-normal)); + // Mode 2: keep vanilla DALC scaled by DALCAmount, add sky IBL overlay + envIBLColor = directionalAmbientColor * SharedData::iblSettings.DALCAmount; + directionalAmbientColor = envIBLColor + Color::IrradianceToGamma(ImageBasedLighting::GetSkyIBLColor(-normal)); } else { envIBLColor = Color::IrradianceToGamma(ImageBasedLighting::GetEnvIBLColor(-normal)); float3 skyIBLColor = Color::IrradianceToGamma(ImageBasedLighting::GetSkyIBLColor(-normal)); diff --git a/src/Features/IBL.h b/src/Features/IBL.h index 71288fa299..f0bad49de3 100644 --- a/src/Features/IBL.h +++ b/src/Features/IBL.h @@ -52,7 +52,7 @@ struct IBL : Feature float EnvIBLSaturation = 1.0f; float SkyIBLSaturation = 1.0f; float FogAmount = 0.0f; - uint DALCMode = 0; // 0: Luminance Ratio, 1: Color Ratio, 2: DALC + Sky + uint DALCMode = 2; // 0: Luminance Ratio, 1: Color Ratio, 2: DALC + Sky float pad0 = 0.0f; float pad1 = 0.0f; } settings; From 85aad884f70b65e826e5714c4dfded8f964f9178 Mon Sep 17 00:00:00 2001 From: jiayev Date: Sat, 14 Mar 2026 16:40:02 +0800 Subject: [PATCH 8/9] Add early return for non-positive weight in convolution --- src/Utils/SphericalHarmonics.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Utils/SphericalHarmonics.cpp b/src/Utils/SphericalHarmonics.cpp index 295e6bbc87..fa6476ed05 100644 --- a/src/Utils/SphericalHarmonics.cpp +++ b/src/Utils/SphericalHarmonics.cpp @@ -160,6 +160,8 @@ SH2Color SphericalHarmonics::Product(SH2Color shL, SH2Color shR) SH2 SphericalHarmonics::HanningConvolution(SH2 sh, float w) { + if (w <= 0) + return sh; SH2 result; float invW = 1.0f / w; float factorBand1 = (1.0f + cos(3.14159265358979323846f * invW)) / 2.0f; @@ -279,4 +281,4 @@ SH2Color SphericalHarmonics::DALCToSH(const float3 dalcColors[6]) result.b = Add(result.b, Scale(shBasis, dalcColors[i].z * weight)); } return result; -} \ No newline at end of file +} From a48bd4619a3b6a0c24c07ad0b069e6c06a62cf4a Mon Sep 17 00:00:00 2001 From: Jiaye Date: Sat, 14 Mar 2026 16:46:58 +0800 Subject: [PATCH 9/9] minor prevent --- src/Utils/SphericalHarmonics.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Utils/SphericalHarmonics.cpp b/src/Utils/SphericalHarmonics.cpp index fa6476ed05..80d819664f 100644 --- a/src/Utils/SphericalHarmonics.cpp +++ b/src/Utils/SphericalHarmonics.cpp @@ -209,7 +209,11 @@ static T reflect(const T& i, const T& n) template static T normalize(const T& v) { - return v * (1.0f / sqrtf(v.Dot(v))); + const float len2 = v.Dot(v); + if (len2 <= 1e-8f) { + return v; + } + return v * (1.0f / sqrtf(len2)); } // Author: ProfJack @@ -218,6 +222,7 @@ SH2 SphericalHarmonics::FauxSpecularLobe(float3 N, float3 V, float roughness) { // https://www.gdcvault.com/play/1026701/Fast-Denoising-With-Self-Stabilizing // get dominant ggx reflection direction + roughness = std::clamp(roughness, 0.0f, 1.0f); float f = (1 - roughness) * (sqrt(1 - roughness) + roughness); float3 R = reflect(-V, N); float3 D = R * f + N * (1 - f); @@ -238,7 +243,7 @@ float SphericalHarmonics::SHHallucinateZH3Irradiance(SH2 sh, float3 direction) float3 zonalAxis = normalize(float3(sh.c1[2], sh.c1[0], sh.c1[1])); float ratio = 0.0; ratio = abs(zonalAxis.Dot(float3(-sh.c1[2], -sh.c1[0], sh.c1[1]))); - ratio /= sh.c0; + ratio /= std::max(1e-8f, sh.c0); float zonalL2Coeff = sh.c0 * (0.08f * ratio + 0.6f * ratio * ratio); // Curve-fit; Section 3.4.3 float fZ = zonalAxis.Dot(direction); float zhDir = sqrt(5.0f / (16.0f * 3.14159265358979323846f)) * (3.0f * fZ * fZ - 1.0f);