From cb9d38933e4601915906a0497254aa6c077dca1a Mon Sep 17 00:00:00 2001 From: Mikhail Gorobets Date: Tue, 14 May 2024 20:15:03 +0600 Subject: [PATCH] PBR: Fixed noise in IBL irradiance cube map (close #200) --- PBR/src/PBR_Renderer.cpp | 27 ++++++++++++-------- Shaders/PBR/private/ComputeIrradianceMap.psh | 22 +++++++++------- Shaders/PBR/private/PBR_PrecomputeCommon.fxh | 12 +++++++++ Shaders/PBR/private/PrefilterEnvMap.psh | 21 +++++++++------ 4 files changed, 55 insertions(+), 27 deletions(-) diff --git a/PBR/src/PBR_Renderer.cpp b/PBR/src/PBR_Renderer.cpp index be2a2f91..df0aec85 100644 --- a/PBR/src/PBR_Renderer.cpp +++ b/PBR/src/PBR_Renderer.cpp @@ -527,7 +527,12 @@ void PBR_Renderer::PrecomputeCubemaps(IDeviceContext* pCtx, float Roughness; float EnvMapWidth; float EnvMapHeight; - uint NumSamples; + float EnvMapMipCount; + + uint NumSamples; + uint Padding0; + uint Padding1; + uint Padding2; }; if (!m_PrecomputeEnvMapAttribsCB) @@ -687,10 +692,11 @@ void PBR_Renderer::PrecomputeCubemaps(IDeviceContext* pCtx, VERIFY_EXPR(mip == 0); { MapHelper Attribs{pCtx, m_PrecomputeEnvMapAttribsCB, MAP_WRITE, MAP_FLAG_DISCARD}; - Attribs->Rotation = Matrices[face]; - Attribs->EnvMapWidth = static_cast(pEnvironmentMap->GetTexture()->GetDesc().Width); - Attribs->EnvMapHeight = static_cast(pEnvironmentMap->GetTexture()->GetDesc().Height); - Attribs->NumSamples = NumDiffuseSamples; + Attribs->Rotation = Matrices[face]; + Attribs->EnvMapWidth = static_cast(pEnvironmentMap->GetTexture()->GetDesc().Width); + Attribs->EnvMapHeight = static_cast(pEnvironmentMap->GetTexture()->GetDesc().Height); + Attribs->EnvMapMipCount = static_cast(pEnvironmentMap->GetTexture()->GetDesc().MipLevels); + Attribs->NumSamples = NumDiffuseSamples; } DrawAttribs drawAttrs(4, DRAW_FLAG_VERIFY_ALL); pCtx->Draw(drawAttrs); @@ -706,11 +712,12 @@ void PBR_Renderer::PrecomputeCubemaps(IDeviceContext* pCtx, ProcessCubemapFaces(pCtx, pPrefilteredEnvMap, [&](ITextureView* pRTV, Uint32 mip, Uint32 face) { { MapHelper Attribs{pCtx, m_PrecomputeEnvMapAttribsCB, MAP_WRITE, MAP_FLAG_DISCARD}; - Attribs->Rotation = Matrices[face]; - Attribs->Roughness = static_cast(mip) / static_cast(pPrefilteredEnvMap->GetDesc().MipLevels - 1); - Attribs->EnvMapWidth = static_cast(pEnvironmentMap->GetTexture()->GetDesc().Width); - Attribs->EnvMapHeight = static_cast(pEnvironmentMap->GetTexture()->GetDesc().Height); - Attribs->NumSamples = NumSpecularSamples; + Attribs->Rotation = Matrices[face]; + Attribs->Roughness = static_cast(mip) / static_cast(pPrefilteredEnvMap->GetDesc().MipLevels - 1); + Attribs->EnvMapWidth = static_cast(pEnvironmentMap->GetTexture()->GetDesc().Width); + Attribs->EnvMapHeight = static_cast(pEnvironmentMap->GetTexture()->GetDesc().Height); + Attribs->EnvMapMipCount = static_cast(pEnvironmentMap->GetTexture()->GetDesc().MipLevels); + Attribs->NumSamples = NumSpecularSamples; } DrawAttribs drawAttrs(4, DRAW_FLAG_VERIFY_ALL); diff --git a/Shaders/PBR/private/ComputeIrradianceMap.psh b/Shaders/PBR/private/ComputeIrradianceMap.psh index 9d9ba162..d097438a 100644 --- a/Shaders/PBR/private/ComputeIrradianceMap.psh +++ b/Shaders/PBR/private/ComputeIrradianceMap.psh @@ -22,7 +22,12 @@ cbuffer FilterAttribs float g_Roughness; float g_EnvMapWidth; float g_EnvMapHeight; + float g_EnvMipCount; + uint g_NumSamples; + uint _Padding0; + uint _Padding1; + uint _Padding2; } float3 SampleEnvrionmentMap(float3 R, float MipLevel) @@ -47,7 +52,7 @@ float3 IrradianceMap(float3 N) // Importance sample the hemisphere with a cosine-weighted distribution float3 L = ImportanceSampleGGX(Xi, 1.0, N); - #if OPTIMIZE_SAMPLES +#if OPTIMIZE_SAMPLES // NdotL is equal to cos(theta) float NoL = max(dot(N, L), 0.0); @@ -57,18 +62,17 @@ float3 IrradianceMap(float3 N) // Solid angle of current smple float OmegaS = 1.0 / (float(g_NumSamples) * pdf); - float NumPixels = g_EnvMapWidth * g_EnvMapHeight; #if ENV_MAP_TYPE == ENV_MAP_TYPE_CUBE - NumPixels *= 6.0; -#endif - // Solid angle of 1 pixel across all cube faces - float OmegaP = 4.0 * PI / NumPixels; - - // Applying mip bias produces better results, especially for environments maps with + float OmegaP = ComputeCubeMapPixelSolidAngle(g_EnvMapWidth, g_EnvMapHeight); +#else + // Solid angle of 1 pixel on sphere + float OmegaP = ComputeSphereMapPixelSolidAngle(g_EnvMapWidth, g_EnvMapHeight, acos(L.y), 0.5); +#endif + // Applying mip bias produces better results, especially for environment maps with // very bright spots. float MipBias = 1.0; - float MipLevel = max(0.5 * log2(OmegaS / OmegaP) + MipBias, 0.0); + float MipLevel = clamp(0.5 * log2(OmegaS / max(OmegaP, 1e-10)) + MipBias, 0.0, g_EnvMipCount - 1.0); #else float MipLevel = 0.0; #endif diff --git a/Shaders/PBR/private/PBR_PrecomputeCommon.fxh b/Shaders/PBR/private/PBR_PrecomputeCommon.fxh index ed5b37c7..a4c2f1da 100644 --- a/Shaders/PBR/private/PBR_PrecomputeCommon.fxh +++ b/Shaders/PBR/private/PBR_PrecomputeCommon.fxh @@ -39,4 +39,16 @@ float3 ImportanceSampleGGX(float2 Xi, float PerceptualRoughness, float3 N) return TangentX * H.x + TangentY * H.y + N * H.z; } +float ComputeCubeMapPixelSolidAngle(float Width, float Height) +{ + return 4.0 * PI / (6.0 * Width * Height); +} + +float ComputeSphereMapPixelSolidAngle(float Width, float Height, float Theta, float Gamma) +{ + float dTheta = PI / Width; + float dPhi = 2.0 * PI / Height; + return dPhi * (cos(Theta - 0.5 * dTheta * Gamma) - cos(Theta + 0.5 * dTheta * Gamma)); +} + #endif // _PBR_PRECOMPUTE_COMMON_FXH_ diff --git a/Shaders/PBR/private/PrefilterEnvMap.psh b/Shaders/PBR/private/PrefilterEnvMap.psh index 8db59af4..a27a8813 100644 --- a/Shaders/PBR/private/PrefilterEnvMap.psh +++ b/Shaders/PBR/private/PrefilterEnvMap.psh @@ -19,7 +19,12 @@ cbuffer FilterAttribs float g_Roughness; float g_EnvMapWidth; float g_EnvMapHeight; + float g_EnvMipCount; + uint g_NumSamples; + uint _Padding0; + uint _Padding1; + uint _Padding2; } float3 SampleEnvrionmentMap(float3 R, float MipLevel) @@ -71,17 +76,17 @@ float3 PrefilterEnvMap( float PerceptualRoughness, float3 R ) // Solid angle of current smple float OmegaS = 1.0 / (float(g_NumSamples) * pdf); - float NumPixels = g_EnvMapWidth * g_EnvMapHeight; #if ENV_MAP_TYPE == ENV_MAP_TYPE_CUBE - NumPixels *= 6.0; -#endif - // Solid angle of 1 pixel across all cube faces - float OmegaP = 4.0 * PI / NumPixels; - // Applying mip bias produces better results, especially for environments maps with + float OmegaP = ComputeCubeMapPixelSolidAngle(g_EnvMapWidth, g_EnvMapHeight); +#else + // Solid angle of 1 pixel on sphere + float OmegaP = ComputeSphereMapPixelSolidAngle(g_EnvMapWidth, g_EnvMapHeight, acos(L.y), 1.0); +#endif + // Applying mip bias produces better results, especially for environment maps with // very bright spots. - float MipBias = 1.0; - float MipLevel = (AlphaRoughness == 0.0) ? 0.0 : max(0.5 * log2(OmegaS / OmegaP) + MipBias, 0.0); + float MipBias = 1.0; + float MipLevel = (AlphaRoughness == 0.0) ? 0.0 : clamp(0.5 * log2(OmegaS / max(OmegaP, 1e-10)) + MipBias, 0.0, g_EnvMipCount - 1.0); #else float MipLevel = 0.0; #endif