diff --git a/features/Cloud Shadows/Shaders/CloudShadows/CloudShadows.hlsli b/features/Cloud Shadows/Shaders/CloudShadows/CloudShadows.hlsli index 9bcad17a44..a999abc1f8 100644 --- a/features/Cloud Shadows/Shaders/CloudShadows/CloudShadows.hlsli +++ b/features/Cloud Shadows/Shaders/CloudShadows/CloudShadows.hlsli @@ -1,6 +1,7 @@ namespace CloudShadows { - TextureCube CloudShadowsTexture : register(t25); + TextureCube VolumetricCloudShadowsTexture : register(t25); + TextureCube CloudShadowsTexture : register(t26); const static float CloudHeight = (2e3f / 1.428e-2) * 0.25; const static float PlanetRadius = (6371e3f / 1.428e-2); diff --git a/package/Shaders/Common/SharedData.hlsli b/package/Shaders/Common/SharedData.hlsli index 6384330c2b..144a254860 100644 --- a/package/Shaders/Common/SharedData.hlsli +++ b/package/Shaders/Common/SharedData.hlsli @@ -141,7 +141,8 @@ namespace SharedData struct CloudShadowsSettings { float Opacity; - float3 pad0; + float InnerCloudShadowOpacity; + float2 pad0; }; struct LODBlendingSettings diff --git a/package/Shaders/Sky.hlsl b/package/Shaders/Sky.hlsl index 0a1731841d..f086425292 100644 --- a/package/Shaders/Sky.hlsl +++ b/package/Shaders/Sky.hlsl @@ -243,6 +243,36 @@ PS_OUTPUT main(PS_INPUT input) psout.Color.xyz = Color::Sky(input.Color.xyz) * baseColor.xyz + yyy; # endif +# if defined(CLOUD_SHADOWS) && defined(CLOUDS) + if (baseColor.w > 0.0 && SharedData::cloudShadowsSettings.InnerCloudShadowOpacity > 0.0) { + float3 viewDir = normalize(input.WorldPosition.xyz); + float rayStep = 1.0 / 32.0; + float rayPos = rayStep * 0.5; + float4 rayShadow = 0.0; + + float3 PoissonDisc[] = { + float3(0.460921f, 0.615192f, 0.887539f), + float3(0.757347f, 0.911008f, 0.189581f), + float3(0.548753f, 0.145482f, 0.0548723f), + float3(0.90051f, 0.157048f, 0.623493f) + }; + + [unroll] for (int i = 0; i < 4; i++) + { + float3 raySample = normalize(lerp(viewDir, SharedData::DirLightDirection.xyz, rayPos) + (PoissonDisc[i] * 2.0 - 1.0) * 0.01); + + if (raySample.z < 0.0) + rayShadow[i] += -raySample.z; + else + rayShadow[i] = max(rayShadow[i], CloudShadows::VolumetricCloudShadowsTexture.SampleLevel(SampBaseSampler, raySample, 0).x); + + rayPos += rayStep; + } + + psout.Color.xyz *= (1.0 - saturate(dot(rayShadow, 0.25)) * SharedData::cloudShadowsSettings.InnerCloudShadowOpacity); + } +# endif + # else psout.Color = float4(0, 0, 0, 1.0); # endif // OCCLUSION diff --git a/src/Features/CloudShadows.cpp b/src/Features/CloudShadows.cpp index 09b6eef40e..119fddd7e5 100644 --- a/src/Features/CloudShadows.cpp +++ b/src/Features/CloudShadows.cpp @@ -2,14 +2,19 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT( CloudShadows::Settings, - Opacity) + Opacity, + InnerCloudShadowOpacity) void CloudShadows::DrawSettings() { - ImGui::SliderFloat("Opacity", &settings.Opacity, 0.0f, 1.0f, "%.1f"); + ImGui::SliderFloat("Opacity", &settings.Opacity, 0.0f, 1.0f, "%.2f"); if (auto _tt = Util::HoverTooltipWrapper()) { - ImGui::Text( - "Higher values make cloud shadows darker."); + ImGui::Text("Controls how dark cloud shadows appear on terrain and objects."); + } + + ImGui::SliderFloat("Inner Cloud Shadow Opacity", &settings.InnerCloudShadowOpacity, 0.0f, 1.0f, "%.2f"); + if (auto _tt = Util::HoverTooltipWrapper()) { + ImGui::Text("Controls shadowing applied within the clouds themselves. Higher values make the underside of clouds darker."); } } @@ -118,6 +123,8 @@ void CloudShadows::ReflectionsPrepass() ID3D11ShaderResourceView* srv = texCubemapCloudOccCopy->srv.get(); context->PSSetShaderResources(25, 1, &srv); context->CSSetShaderResources(25, 1, &srv); + context->PSSetShaderResources(26, 1, &srv); + context->CSSetShaderResources(26, 1, &srv); } } @@ -129,9 +136,15 @@ void CloudShadows::EarlyPrepass() auto context = globals::d3d::context; + // t25 = VolumetricCloudShadowsTexture: use previous-frame copy so there is no D3D11 hazard + ID3D11ShaderResourceView* copySrv = texCubemapCloudOccCopy->srv.get(); + context->PSSetShaderResources(25, 1, ©Srv); + context->CSSetShaderResources(25, 1, ©Srv); + + // t26 = CloudShadowsTexture: live cubemap for terrain ground shadows. ID3D11ShaderResourceView* srv = texCubemapCloudOcc->srv.get(); - context->PSSetShaderResources(25, 1, &srv); - context->CSSetShaderResources(25, 1, &srv); + context->PSSetShaderResources(26, 1, &srv); + context->CSSetShaderResources(26, 1, &srv); } void CloudShadows::SetupResources() @@ -168,6 +181,12 @@ void CloudShadows::SetupResources() rtvDesc.Format = texDesc.Format; DX::ThrowIfFailed(device->CreateRenderTargetView(texCubemapCloudOccCopy->resource.get(), &rtvDesc, cubemapCloudOccCopyRTVs + i)); } + + // Clear copy cubemap so EarlyPrepass doesn't bind uninitialized data on the first frame. + auto context = globals::d3d::context; + float black[4] = { 0, 0, 0, 0 }; + for (int i = 0; i < 6; ++i) + context->ClearRenderTargetView(cubemapCloudOccCopyRTVs[i], black); } { D3D11_BLEND_DESC blendDesc = {}; diff --git a/src/Features/CloudShadows.h b/src/Features/CloudShadows.h index 3ab14cbfef..a54ddd8f55 100644 --- a/src/Features/CloudShadows.h +++ b/src/Features/CloudShadows.h @@ -9,8 +9,10 @@ struct CloudShadows : Feature struct alignas(16) Settings { float Opacity = 0.8f; - float pad[3]; + float InnerCloudShadowOpacity = 0.5f; + float pad[2]; }; + STATIC_ASSERT_ALIGNAS_16(Settings); Settings settings;