From dbab58da095ce4fb32b1c7269a26e6a77bfff1a6 Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Sun, 29 Sep 2024 17:39:14 -0700 Subject: [PATCH] feat(VR): enable volumetric lighting --- .../Shaders/Features/VolumetricLighting.ini | 2 + .../Shaders/ISApplyVolumetricLighting.hlsl | 22 +++-- .../ISVolumetricLightingGenerateCS.hlsl | 47 +++++++--- src/Feature.cpp | 4 +- src/Features/VolumetricLighting.cpp | 69 +++++++++++++++ src/Features/VolumetricLighting.h | 87 +++++++++++++++++++ 6 files changed, 212 insertions(+), 19 deletions(-) create mode 100644 features/Volumetric Lighting/Shaders/Features/VolumetricLighting.ini create mode 100644 src/Features/VolumetricLighting.cpp create mode 100644 src/Features/VolumetricLighting.h diff --git a/features/Volumetric Lighting/Shaders/Features/VolumetricLighting.ini b/features/Volumetric Lighting/Shaders/Features/VolumetricLighting.ini new file mode 100644 index 000000000..19f01444d --- /dev/null +++ b/features/Volumetric Lighting/Shaders/Features/VolumetricLighting.ini @@ -0,0 +1,2 @@ +[Info] +Version = 1-0-0 \ No newline at end of file diff --git a/package/Shaders/ISApplyVolumetricLighting.hlsl b/package/Shaders/ISApplyVolumetricLighting.hlsl index 72492b198..0288c97b0 100644 --- a/package/Shaders/ISApplyVolumetricLighting.hlsl +++ b/package/Shaders/ISApplyVolumetricLighting.hlsl @@ -1,5 +1,6 @@ #include "Common/DummyVSTexCoord.hlsl" #include "Common/FrameBuffer.hlsli" +#include "Common/VR.hlsli" typedef VS_OUTPUT PS_INPUT; @@ -34,8 +35,16 @@ PS_OUTPUT main(PS_INPUT input) { PS_OUTPUT psout; + uint eyeIndex = GetEyeIndexFromTexCoord(input.TexCoord); float2 screenPosition = GetDynamicResolutionAdjustedScreenPosition(input.TexCoord); float depth = DepthTex.Sample(DepthSampler, screenPosition).x; + +# ifdef VR + if (depth < 0.0001) { // not a valid location + psout.VL = 0.0; + return psout; + } +# endif float repartition = clamp(RepartitionTex.SampleLevel(RepartitionSampler, depth, 0).x, 0, 0.9999); float vl = g_IntensityX_TemporalY.x * VLTex.SampleLevel(VLSampler, float3(input.TexCoord, repartition), 0).x; @@ -45,17 +54,18 @@ PS_OUTPUT main(PS_INPUT input) if (0.001 < g_IntensityX_TemporalY.y) { float2 motionVector = MotionVectorsTex.Sample(MotionVectorsSampler, screenPosition).xy; - float2 previousTexCoord = input.TexCoord + motionVector; - float2 previousScreenPosition = GetPreviousDynamicResolutionAdjustedScreenPosition(previousTexCoord); + float2 previousTexCoord = ConvertFromStereoUV(input.TexCoord, eyeIndex) + motionVector; + float2 previousScreenPosition = GetPreviousDynamicResolutionAdjustedScreenPosition(ConvertToStereoUV(previousTexCoord, eyeIndex)); float previousVl = PreviousFrameTex.Sample(PreviousFrameSampler, previousScreenPosition).x; float previousDepth = PreviousDepthTex.Sample(PreviousDepthSampler, previousScreenPosition).x; float temporalContribution = g_IntensityX_TemporalY.y * (1 - smoothstep(0, 1, min(1, 100 * abs(depth - previousDepth)))); - float isValid = 0; - if (previousTexCoord.x >= 0 && previousTexCoord.x < 1 && previousTexCoord.y >= 0 && previousTexCoord.y < 1) { - isValid = 1; - } + float isValid = (previousTexCoord.x >= 0 && previousTexCoord.x < 1 && previousTexCoord.y >= 0 && previousTexCoord.y < 1 +# ifdef VR + && abs(previousDepth) > 0.0001 +# endif + ); psout.VL = lerp(adjustedVl, previousVl, temporalContribution * isValid); } else { psout.VL = adjustedVl; diff --git a/package/Shaders/ISVolumetricLightingGenerateCS.hlsl b/package/Shaders/ISVolumetricLightingGenerateCS.hlsl index 712dac5a5..95298cd64 100644 --- a/package/Shaders/ISVolumetricLightingGenerateCS.hlsl +++ b/package/Shaders/ISVolumetricLightingGenerateCS.hlsl @@ -1,4 +1,6 @@ #include "Common/Constants.hlsli" +#include "Common/Random.hlsli" +#include "Common/VR.hlsli" #if defined(CSHADER) SamplerState ShadowmapSampler : register(s0); @@ -16,21 +18,39 @@ RWTexture3D DensityCopyRW : register(u1); cbuffer PerTechnique : register(b0) { - float4x4 CameraViewProj : packoffset(c0); - float4x4 CameraViewProjInverse : packoffset(c4); - float4x3 ShadowMapProj[3] : packoffset(c8); +# ifndef VR + float4x4 CameraViewProj[1] : packoffset(c0); + float4x4 CameraViewProjInverse[1] : packoffset(c4); + float4x3 ShadowMapProj[1][3] : packoffset(c8); float3 EndSplitDistances : packoffset(c17.x); float ShadowMapCount : packoffset(c17.w); float EnableShadowCasting : packoffset(c18); float3 DirLightDirection : packoffset(c19); float3 TextureDimensions : packoffset(c20); - float3 WindInput : packoffset(c21); + float3 WindInput[1] : packoffset(c21); float InverseDensityScale : packoffset(c21.w); - float3 PosAdjust : packoffset(c22); + float3 PosAdjust[1] : packoffset(c22); float IterationIndex : packoffset(c22.w); float PhaseContribution : packoffset(c23.x); float PhaseScattering : packoffset(c23.y); float DensityContribution : packoffset(c23.z); +# else + float4x4 CameraViewProj[2] : packoffset(c0); + float4x4 CameraViewProjInverse[2] : packoffset(c8); + float4x3 ShadowMapProj[2][3] : packoffset(c16); + float3 EndSplitDistances : packoffset(c34.x); + float ShadowMapCount : packoffset(c34.w); + float EnableShadowCasting : packoffset(c35.x); + float3 DirLightDirection : packoffset(c36); + float3 TextureDimensions : packoffset(c37); + float3 WindInput[2] : packoffset(c38); + float InverseDensityScale : packoffset(c39.w); + float3 PosAdjust[2] : packoffset(c40); + float IterationIndex : packoffset(c41.w); + float PhaseContribution : packoffset(c42.x); + float PhaseScattering : packoffset(c42.y); + float DensityContribution : packoffset(c42.z); +# endif } [numthreads(32, 32, 1)] void main(uint3 dispatchID @@ -44,29 +64,32 @@ cbuffer PerTechnique : register(b0) { 1.000000, 1.000000, 0 }, { 1.000000, 1.000000, 1.000000 } }; - float3 depthUv = dispatchID.xyz / TextureDimensions.xyz + 0.001 * StepCoefficients[IterationIndex].xyz; + float3 normalizedCoordinates = dispatchID.xyz / TextureDimensions.xyz; + float2 uv = normalizedCoordinates.xy; + uint eyeIndex = GetEyeIndexFromTexCoord(uv); + float3 depthUv = ConvertFromStereoUV(normalizedCoordinates, eyeIndex) + 0.001 * StepCoefficients[IterationIndex].xyz; float depth = InverseRepartitionTex.SampleLevel(InverseRepartitionSampler, depthUv.z, 0).x; float4 positionCS = float4(2 * depthUv.x - 1, 1 - 2 * depthUv.y, depth, 1); - float4 positionWS = mul(transpose(CameraViewProjInverse), positionCS); + float4 positionWS = mul(transpose(CameraViewProjInverse[eyeIndex]), positionCS); positionWS /= positionWS.w; - float4 positionCSShifted = mul(transpose(CameraViewProj), positionWS); + float4 positionCSShifted = mul(transpose(CameraViewProj[eyeIndex]), positionWS); positionCSShifted /= positionCSShifted.w; float shadowMapDepth = positionCSShifted.z; float shadowContribution = 1; if (EndSplitDistances.z >= shadowMapDepth) { - float4x3 lightProjectionMatrix = ShadowMapProj[0]; + float4x3 lightProjectionMatrix = ShadowMapProj[eyeIndex][0]; float shadowMapThreshold = 0.01; float cascadeIndex = 0; if (2.5 < ShadowMapCount && EndSplitDistances.y < shadowMapDepth) { - lightProjectionMatrix = ShadowMapProj[2]; + lightProjectionMatrix = ShadowMapProj[eyeIndex][2]; shadowMapThreshold = 0; cascadeIndex = 2; } else if (EndSplitDistances.x < shadowMapDepth) { - lightProjectionMatrix = ShadowMapProj[1]; + lightProjectionMatrix = ShadowMapProj[eyeIndex][1]; shadowMapThreshold = 0; cascadeIndex = 1; } @@ -95,7 +118,7 @@ cbuffer PerTechnique : register(b0) } } - float3 noiseUv = 0.0125 * (InverseDensityScale * (positionWS.xyz + WindInput)); + float3 noiseUv = 0.0125 * (InverseDensityScale * (positionWS.xyz + WindInput[0])); float noise = NoiseTex.SampleLevel(NoiseSampler, noiseUv, 0).x; float densityFactor = noise * (1 - 0.75 * smoothstep(0, 1, saturate(2 * positionWS.z / 300))); float densityContribution = lerp(1, densityFactor, DensityContribution); diff --git a/src/Feature.cpp b/src/Feature.cpp index 5279bfb87..d5c853686 100644 --- a/src/Feature.cpp +++ b/src/Feature.cpp @@ -13,6 +13,7 @@ #include "Features/SubsurfaceScattering.h" #include "Features/TerrainBlending.h" #include "Features/TerrainShadows.h" +#include "Features/VolumetricLighting.h" #include "Features/WaterLighting.h" #include "Features/WetnessEffects.h" @@ -124,7 +125,8 @@ const std::vector& Feature::GetFeatureList() TerrainShadows::GetSingleton(), ScreenSpaceGI::GetSingleton(), Skylighting::GetSingleton(), - TerrainBlending::GetSingleton() + TerrainBlending::GetSingleton(), + VolumetricLighting::GetSingleton() }; static std::vector featuresVR(features); diff --git a/src/Features/VolumetricLighting.cpp b/src/Features/VolumetricLighting.cpp new file mode 100644 index 000000000..689966a98 --- /dev/null +++ b/src/Features/VolumetricLighting.cpp @@ -0,0 +1,69 @@ +#include "VolumetricLighting.h" +#include "State.h" +#include "Util.h" + +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT( + VolumetricLighting::Settings, + EnabledVL); + +void VolumetricLighting::DrawSettings() +{ + if (ImGui::TreeNodeEx("Settings", ImGuiTreeNodeFlags_DefaultOpen)) { + if (!State::GetSingleton()->isVR) { + RenderImGuiSettingsTree(VLSettings, "Skyrim Settings"); + } else { + if (ImGui::Checkbox("Enable Volumetric Lighting in VR", reinterpret_cast(&settings.EnabledVL))) { + if (auto _tt = Util::HoverTooltipWrapper()) { + ImGui::Text("Enable Volumetric Lighting in VR"); + } + SetBooleanSettings(hiddenVRSettings, GetName(), settings.EnabledVL); + } + if (settings.EnabledVL) { + RenderImGuiSettingsTree(VLSettings, "Skyrim Settings"); + RenderImGuiSettingsTree(hiddenVRSettings, "hiddenVR"); + } + } + ImGui::Spacing(); + ImGui::TreePop(); + } +} + +void VolumetricLighting::LoadSettings(json& o_json) +{ + settings = o_json; + LoadGameSettings(VLSettings); +} + +void VolumetricLighting::SaveSettings(json& o_json) +{ + o_json = settings; + SaveGameSettings(VLSettings); +} + +void VolumetricLighting::RestoreDefaultSettings() +{ + settings = {}; + ResetGameSettingsToDefaults(VLSettings); + if (State::GetSingleton()->isVR) { + ResetGameSettingsToDefaults(hiddenVRSettings); + } +} + +void VolumetricLighting::DataLoaded() +{ + // if (REL::Module::IsVR() && settings.EnabledVL) { + // EnableBooleanSettings(hiddenVRSettings, GetName()); + // enabledAtBoot = true; + // } +} + +void VolumetricLighting::PostPostLoad() +{ + if (REL::Module::IsVR() && settings.EnabledVL) { + EnableBooleanSettings(hiddenVRSettings, GetName()); + } +} + +void VolumetricLighting::Reset() +{ +} diff --git a/src/Features/VolumetricLighting.h b/src/Features/VolumetricLighting.h new file mode 100644 index 000000000..f2c4f8ca3 --- /dev/null +++ b/src/Features/VolumetricLighting.h @@ -0,0 +1,87 @@ +#pragma once + +#include "Buffer.h" +#include "Feature.h" + +struct VolumetricLighting : Feature +{ +public: + static VolumetricLighting* GetSingleton() + { + static VolumetricLighting singleton; + return &singleton; + } + + struct Settings + { + uint EnabledVL = true; + }; + + Settings settings; + + bool enabledAtBoot = false; + + virtual inline std::string GetName() override { return "Volumetric Lighting"; } + virtual inline std::string GetShortName() override { return "VolumetricLighting"; } + + virtual void Reset() override; + + virtual void SaveSettings(json&) override; + virtual void LoadSettings(json&) override; + virtual void RestoreDefaultSettings() override; + virtual void DrawSettings() override; + virtual void DataLoaded() override; + virtual void PostPostLoad() override; + + std::map VLSettings{ + { "bEnableVolumetricLighting:Display", { "Enable Volumetric Lighting (INI)", "Enables or disables volumetric lighting effects.", 0, true, false, true } }, + { "bVolumetricLightingDisableInterior:Display", { "Disable Interior Volumetric Lighting", "Disables volumetric lighting effects for interior spaces.", REL::Relocate(0, 0, 0x1ed4038), true, false, true } }, + { "bVolumetricLightingUpdateWeather:Display", { "Update Volumetric Lighting with Weather", "Updates volumetric lighting based on weather conditions.", 0, true, false, true } }, + { "bVolumetricLightingEnableTemporalAccumulation:Display", { "Enable Volumetric Lighting Temporal Accumulation", "Enables temporal accumulation for volumetric lighting effects.", 0, true, false, true } }, + { "iVolumetricLightingQuality:Display", { "Lighting Quality", "Adjusts the quality of volumetric lighting. [-1,-2] (off, low, mid, high).", REL::Relocate(0, 0, 0x1ed4030), 1, -1, 2 } }, + { "fVolumetricLightingCustomColorContribution:Display", { "Custom Color Contribution", "Controls the contribution of custom colors to volumetric lighting.", 0, 0.0f, 0.0f, 1.0f } }, + { "fVolumetricLightingDensityContribution:Display", { "Density Contribution", "Adjusts the density contribution of volumetric lighting.", 0, 0.3f, 0.0f, 1.0f } }, + { "fVolumetricLightingDensityScale:Display", { "Density Scale", "Scales the density of volumetric lighting effects.", 0, 300.0f, 0.0f, 1000.0f } }, + { "fVolumetricLightingIntensity:Display", { "Intensity", "Sets the intensity of volumetric lighting effects.", 0, 2.0f, 0.0f, 10.0f } }, + { "fVolumetricLightingPhaseContribution:Display", { "Phase Contribution", "Controls the contribution of phase to the volumetric lighting effects.", 0, 0.83f, 0.0f, 1.0f } }, + { "fVolumetricLightingPhaseScattering:Display", { "Phase Scattering", "Sets the phase scattering for volumetric lighting.", 0, 0.85f, 0.0f, 1.0f } }, + { "fVolumetricLightingRangeFactor:Display", { "Range Factor", "Adjusts the range factor for volumetric lighting effects.", 0, 40.0f, 0.0f, 100.0f } }, + { "fVolumetricLightingTemporalAccumulationFactor:Display", { "Temporal Accumulation Factor", "Controls the strength of temporal accumulation in volumetric lighting.", 0, 0.75f, 0.0f, 5.0f } }, + { "fVolumetricLightingWindFallingSpeed:Display", { "Wind Falling Speed", "Sets the speed at which wind affects falling particles in volumetric lighting.", 0, 0.3f, 0.0f, 1.0f } }, + { "fVolumetricLightingWindSpeedScale:Display", { "Wind Speed Scale", "Scales the wind speed effect on volumetric lighting.", 0, 15.0f, 0.0f, 50.0f } }, + // { "iVolumetricLightingNoiseTextureDepth:Display", { "Noise Texture Depth", "Sets the depth of the noise texture used for volumetric lighting.", 0, 32, 1, 128 } }, + // { "iVolumetricLightingNoiseTextureHeight:Display", { "Noise Texture Height", "Sets the height of the noise texture used for volumetric lighting.", 0, 32, 1, 128 } }, + // { "iVolumetricLightingNoiseTextureWidth:Display", { "Noise Texture Width", "Sets the width of the noise texture used for volumetric lighting.", 0, 32, 1, 128 } }, + // { "iVolumetricLightingTextureDepthHigh:Display", { "High Depth Texture", "Sets the depth for the high-quality volumetric lighting texture.", 0, 90, 1, 128 } }, + // { "iVolumetricLightingTextureDepthMedium:Display", { "Medium Depth Texture", "Sets the depth for the medium-quality volumetric lighting texture.", 0, 70, 1, 128 } }, + // { "iVolumetricLightingTextureDepthLow:Display", { "Low Depth Texture", "Sets the depth for the low-quality volumetric lighting texture.", 0, 50, 1, 128 } }, + // { "iVolumetricLightingTextureFormatHigh:Display", { "High Texture Format", "Sets the format for the high-quality volumetric lighting texture.", 0, 1, 0, 2 } }, + // { "iVolumetricLightingTextureFormatMedium:Display", { "Medium Texture Format", "Sets the format for the medium-quality volumetric lighting texture.", 0, 1, 0, 2 } }, + // { "iVolumetricLightingTextureFormatLow:Display", { "Low Texture Format", "Sets the format for the low-quality volumetric lighting texture.", 0, 0, 0, 2 } }, + // { "iVolumetricLightingTextureHeightHigh:Display", { "High Texture Height", "Sets the height for the high-quality volumetric lighting texture.", 0, 192, 1, 512 } }, + // { "iVolumetricLightingTextureHeightMedium:Display", { "Medium Texture Height", "Sets the height for the medium-quality volumetric lighting texture.", 0, 128, 1, 512 } }, + // { "iVolumetricLightingTextureHeightLow:Display", { "Low Texture Height", "Sets the height for the low-quality volumetric lighting texture.", 0, 96, 1, 512 } }, + // { "iVolumetricLightingTextureWidthHigh:Display", { "High Texture Width", "Sets the width for the high-quality volumetric lighting texture.", 0, 320, 1, 512 } }, + // { "iVolumetricLightingTextureWidthMedium:Display", { "Medium Texture Width", "Sets the width for the medium-quality volumetric lighting texture.", 0, 224, 1, 512 } }, + // { "iVolumetricLightingTextureWidthLow:Display", { "Low Texture Width", "Sets the width for the low-quality volumetric lighting texture.", 0, 160, 1, 512 } }, + }; + + std::map hiddenVRSettings{ + { "bEnableVolumetricLighting:Display", { "Enable Volumetric Lighting Shaders (INI) ", + "Enables volumetric lighting effects by creating shaders. " + "Needed at startup. ", + 0x1ed63d8, true, false, true } }, + { "bVolumetricLightingEnable:Display", { "Enable Volumetric Lighting (INI))", "Enables volumetric lighting. ", 0x3485360, true, false, true } }, + { "bVolumetricLightingUpdateWeather:Display", { "Enable Volumetric Lighting (Weather) (INI) ", + "Enables volumetric lighting for weather. " + "Only used during startup and used to set bVLWeatherUpdate.", + 0x3485361, true, false, true } }, + { "bVLWeatherUpdate", { "Enable Volumetric Lighting (Weather)", "Enables volumetric lighting for weather.", 0x3485363, true, false, true } }, + { "bVolumetricLightingEnabled_143232EF0", { "Enable Volumetric Lighting (Papyrus) ", + "Enables volumetric lighting. " + "This is the Papyrus command. ", + REL::Relocate(0x3232ef0, 0, 0x3485362), true, false, true } }, + }; + + virtual bool SupportsVR() override { return true; }; +};