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/Common/VR.hlsli b/package/Shaders/Common/VR.hlsli index 5f00abf3c..ed7feee3f 100644 --- a/package/Shaders/Common/VR.hlsli +++ b/package/Shaders/Common/VR.hlsli @@ -128,6 +128,24 @@ namespace Stereo return 0; } + /** + * @brief Converts UV coordinates from the range [0, 1] to normalized screen space [-1, 1]. + * + * This function takes texture coordinates and transforms them into a normalized + * coordinate system centered at the origin. This is useful for various graphical + * calculations, especially in shaders that require symmetry around the center. + * + * @param uv The input UV coordinates in the range [0, 1]. + * @return float2 Normalized screen space coordinates in the range [-1, 1]. + */ + float2 ConvertUVToNormalizedScreenSpace(float2 uv) + { + float2 normalizedCoord; + normalizedCoord.x = 2.0 * (-0.5 + abs(2.0 * (uv.x - 0.5))); // Convert UV.x + normalizedCoord.y = 2.0 * uv.y - 1.0; // Convert UV.y + return normalizedCoord; + } + #ifdef PSHADER /** Gets the eyeIndex for PSHADER @@ -147,6 +165,25 @@ namespace Stereo return eyeIndex; } + /** + * @brief Checks if the color is non zero by testing if the color is greater than 0 by epsilon. + * + * This function check is a color is non black. It uses a small epsilon value to allow for + * floating point imprecision. + * + * For screen-space reflection (SSR), this acts as a mask and checks for an invalid reflection by + * checking if the reflection color is essentially black (close to zero). + * + * @param[in] ssrColor The color to check. + * @param[in] epsilon Small tolerance value used to determine if the color is close to zero. + * @return True if color is non zero, otherwise false. + */ + bool IsNonZeroColor(float4 ssrColor, float epsilon = 0.001) + { + return dot(ssrColor.xyz, ssrColor.xyz) > epsilon * epsilon; + } + +# ifdef VR /** * @brief Converts mono UV coordinates from one eye to the corresponding mono UV coordinates of the other eye. * @@ -210,7 +247,7 @@ namespace Stereo { fromOtherEye = false; float3 resultUV = monoUV; -# ifdef VR +# ifdef VR // Check if the UV coordinates are outside the frame if (FrameBuffer::isOutsideFrame(resultUV.xy, false)) { // Transition to the other eye @@ -224,7 +261,7 @@ namespace Stereo } else { resultUV = ConvertToStereoUV(resultUV, eyeIndex); } -# endif +# endif return resultUV; } @@ -258,24 +295,6 @@ namespace Stereo return stereoUV; } - /** - * @brief Checks if the color is non zero by testing if the color is greater than 0 by epsilon. - * - * This function check is a color is non black. It uses a small epsilon value to allow for - * floating point imprecision. - * - * For screen-space reflection (SSR), this acts as a mask and checks for an invalid reflection by - * checking if the reflection color is essentially black (close to zero). - * - * @param[in] ssrColor The color to check. - * @param[in] epsilon Small tolerance value used to determine if the color is close to zero. - * @return True if color is non zero, otherwise false. - */ - bool IsNonZeroColor(float4 ssrColor, float epsilon = 0.001) - { - return dot(ssrColor.xyz, ssrColor.xyz) > epsilon * epsilon; - } - /** * @brief Blends color data from two eyes based on their UV coordinates and validity. * @@ -322,7 +341,8 @@ namespace Stereo { return BlendEyeColors(float3(uv1, 0), color1, float3(uv2, 0), color2, dynamicres); } -#endif // PSHADER +# endif // VR +#endif // PSHADER #ifdef VSHADER struct VR_OUTPUT diff --git a/package/Shaders/ISApplyVolumetricLighting.hlsl b/package/Shaders/ISApplyVolumetricLighting.hlsl index d7d8caeb9..bc87deb34 100644 --- a/package/Shaders/ISApplyVolumetricLighting.hlsl +++ b/package/Shaders/ISApplyVolumetricLighting.hlsl @@ -36,6 +36,13 @@ PS_OUTPUT main(PS_INPUT input) float2 screenPosition = FrameBuffer::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; @@ -48,14 +55,25 @@ PS_OUTPUT main(PS_INPUT input) float2 previousTexCoord = input.TexCoord + motionVector; float2 previousScreenPosition = FrameBuffer::GetPreviousDynamicResolutionAdjustedScreenPosition(previousTexCoord); float previousVl = PreviousFrameTex.Sample(PreviousFrameSampler, previousScreenPosition).x; - float previousDepth = PreviousDepthTex.Sample(PreviousDepthSampler, previousScreenPosition).x; + float previousDepth = PreviousDepthTex.Sample(PreviousDepthSampler, +# ifndef VR + previousScreenPosition +# else + // In VR with dynamic resolution enabled, there's a bug with the depth stencil. + // The depth stencil from ISDepthBufferCopy is actually full size and not scaled. + // Thus there's never a need to scale it down. + previousTexCoord +# endif + ) + .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/ISCopy.hlsl b/package/Shaders/ISCopy.hlsl index 8ea2dc65f..99513da82 100644 --- a/package/Shaders/ISCopy.hlsl +++ b/package/Shaders/ISCopy.hlsl @@ -28,7 +28,7 @@ PS_OUTPUT main(PS_INPUT input) { PS_OUTPUT psout; -# if !defined(DISABLE_DYNAMIC) +# if !defined(DISABLE_DYNAMIC) || (defined(DEPTHBUFFER_COPY) && defined(DEPTHBUFFER_4X_DOWNSAMPLE)) float2 screenPosition = FrameBuffer::GetDynamicResolutionAdjustedScreenPosition(input.TexCoord); # else float2 screenPosition = input.TexCoord; diff --git a/package/Shaders/ISFullScreenVR.hlsl b/package/Shaders/ISFullScreenVR.hlsl new file mode 100644 index 000000000..404fde27a --- /dev/null +++ b/package/Shaders/ISFullScreenVR.hlsl @@ -0,0 +1,69 @@ +#include "Common/DummyVSTexCoord.hlsl" +#include "Common/VR.hlsli" + +typedef VS_OUTPUT PS_INPUT; + +struct PS_OUTPUT +{ + float4 Color : SV_Target0; // Final color output for the pixel shader. +}; + +#if defined(PSHADER) +SamplerState ImageSampler : register(s0); // Sampler state for texture sampling. +Texture2D ImageTex : register(t0); // Texture to sample colors from. + +cbuffer PerGeometry : register(b2) +{ + float4 FullScreenColor; // Color applied to the final output, used for tinting or blending effects. + float4 Params0; // General parameters; may include scaling or offset values. + float4 Params1; // Length parameters for scaling or thresholding; Params1.z represents `g_flTime`. + float4 UpsampleParams; // Dynamic resolution parameters: + // - UpsampleParams.x: fDynamicResolutionWidthRatio + // - UpsampleParams.y: fDynamicResolutionHeightRatio + // - UpsampleParams.z: fDynamicResolutionPreviousWidthRatio + // - UpsampleParams.w: fDynamicResolutionPreviousHeightRatio +}; + +// Function to generate noise using Valve's ScreenSpaceDither method. +// References: +// - https://blog.frost.kiwi/GLSL-noise-and-radial-gradient/ +// - https://media.steampowered.com/apps/valve/2015/Alex_Vlachos_Advanced_VR_Rendering_GDC2015.pdf +float3 ScreenSpaceDither(float2 vScreenPos) +{ + // Iestyn's RGB dither (7 asm instructions) from Portal 2 X360, + // slightly modified for VR applications. + float3 vDither = dot(float2(171.0, 231.0), vScreenPos.xy + Params1.zz).xxx; + vDither.rgb = frac(vDither.rgb / float3(103.0, 71.0, 97.0)) - float3(0.5, 0.5, 0.5); + return (vDither.rgb / 255.0) * 0.375; // Normalize dither values to a suitable range. +} + +PS_OUTPUT main(PS_INPUT input) +{ + PS_OUTPUT psout; + + float2 uv = input.TexCoord; // Get the UV coordinates from input. + + // Convert UV to normalized screen space [-1, 1]. + float2 normalizedScreenCoord = Stereo::ConvertUVToNormalizedScreenSpace(uv); + + // Calculate the length of the normalized screen coordinates. + float normalizedLength = saturate(Params1.x * (length(normalizedScreenCoord) - Params1.y) * Params0.x); + + // Upsample and clamp texture coordinates based on dynamic resolution. + float2 uvScaled = min(UpsampleParams.zw, UpsampleParams.xy * uv.xy); // Clamp UVs to prevent overflow. + float3 sampledColor = ImageTex.Sample(ImageSampler, uvScaled).xyz; // Sample color from the texture. + + // Manipulate the sampled color based on the normalized length. + float3 finalColor = sampledColor * (1.0 + normalizedLength); // Scale sampled color. + + // Generate noise to apply to the final color. + float3 noise = ScreenSpaceDither(input.Position.xy); + finalColor += Params0.yyy * noise * Params1.www; // Adjust final color with noise. + + // Final color manipulation: blend final color with FullScreenColor. + psout.Color.xyz = lerp(finalColor, FullScreenColor.xyz, FullScreenColor.www); // Blend based on the alpha component. + psout.Color.w = 1.0; // Set alpha to full opacity. + + return psout; // Return the pixel shader output. +} +#endif diff --git a/package/Shaders/ISVolumetricLightingGenerateCS.hlsl b/package/Shaders/ISVolumetricLightingGenerateCS.hlsl index 712dac5a5..8909a5954 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 = Stereo::GetEyeIndexFromTexCoord(uv); + float3 depthUv = Stereo::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[eyeIndex])); 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..1b7730d20 --- /dev/null +++ b/src/Features/VolumetricLighting.cpp @@ -0,0 +1,80 @@ +#include "VolumetricLighting.h" +#include "ShaderCache.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() +{ + auto& shaderCache = SIE::ShaderCache::Instance(); + const static auto address = REL::Offset{ 0x1ec6b88 }.address(); + bool& bDepthBufferCulling = *reinterpret_cast(address); + + if (REL::Module::IsVR() && bDepthBufferCulling && shaderCache.IsDiskCache()) { + // clear cache to fix bug caused by bDepthBufferCulling + logger::info("Force clearing cache due to bDepthBufferCulling"); + shaderCache.Clear(); + } +} + +void VolumetricLighting::PostPostLoad() +{ + if (REL::Module::IsVR()) { + if (settings.EnabledVL) + EnableBooleanSettings(hiddenVRSettings, GetName()); + auto address = REL::RelocationID(100475, 0).address() + 0x45b; // AE not needed, VR only hook + logger::info("[{}] Hooking CopyResource at {:x}", GetName(), address); + REL::safe_fill(address, REL::NOP, 7); + stl::write_thunk_call(address); + } +} + +void VolumetricLighting::Reset() +{ +} \ No newline at end of file diff --git a/src/Features/VolumetricLighting.h b/src/Features/VolumetricLighting.h new file mode 100644 index 000000000..25369189e --- /dev/null +++ b/src/Features/VolumetricLighting.h @@ -0,0 +1,111 @@ +#pragma once + +#include "Buffer.h" +#include "Feature.h" +#include "Util.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 VL Shaders (INI) ", + "Enables volumetric lighting effects by creating shaders. " + "Needed at startup. ", + 0x1ed63d8, true, false, true } }, + { "bVolumetricLightingEnable:Display", { "Enable VL (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 VL (Weather)", "Enables volumetric lighting for weather.", 0x3485363, true, false, true } }, + { "bVolumetricLightingEnabled_143232EF0", { "Enable VL (Papyrus) ", + "Enables volumetric lighting. " + "This is the Papyrus command. ", + REL::Relocate(0x3232ef0, 0, 0x3485362), true, false, true } }, + }; + + virtual bool SupportsVR() override { return true; }; + + // hooks + + struct CopyResource + { + static void thunk(ID3D11DeviceContext* a_this, ID3D11Resource* a_renderTarget, ID3D11Resource* a_renderTargetSource) + { + // In VR with dynamic resolution enabled, there's a bug with the depth stencil. + // The depth stencil passed to IsFullScreenVR is scaled down incorrectly. + // The fix is to stop a CopyResource from replacing kMAIN_COPY with kMAIN after + // ISApplyVolumetricLighting because it clobbers a properly scaled kMAIN_COPY. + // The kMAIN_COPY does not appear to be used in the remaining frame after + // ISApplyVolumetricLighting except for IsFullScreenVR. + // But, the copy might have to be done manually later after IsFullScreenVR if + // used in the next frame. + + auto* singleton = GetSingleton(); + if (singleton && !(Util::IsDynamicResolution() && singleton->settings.EnabledVL)) { + a_this->CopyResource(a_renderTarget, a_renderTargetSource); + } + } + static inline REL::Relocation func; + }; +}; diff --git a/src/ShaderCache.cpp b/src/ShaderCache.cpp index 0ed427110..36ed7c00d 100644 --- a/src/ShaderCache.cpp +++ b/src/ShaderCache.cpp @@ -1647,6 +1647,11 @@ namespace SIE { "BSImagespaceShaderVolumetricLightingRaymarchCS", 256 }, { "BSImagespaceShaderVolumetricLightingGenerateCS", 257 }, + { "BSImagespaceShaderCopyDepthBuffer", 98 }, + { "BSImagespaceShaderCopyDepthBuffer", 99 }, + { "BSImagespaceShaderCopyDepthBuffer", 100 }, + { "BSImagespaceShaderISFullScreenVR", 129 }, + }; auto it = descriptors.find(imagespaceShader.name); diff --git a/src/XSEPlugin.cpp b/src/XSEPlugin.cpp index a50d2eb87..843cf2ab2 100644 --- a/src/XSEPlugin.cpp +++ b/src/XSEPlugin.cpp @@ -145,7 +145,7 @@ bool Load() } if (REL::Module::IsVR()) { - REL::IDDatabase::get().IsVRAddressLibraryAtLeastVersion("0.146.0", true); + REL::IDDatabase::get().IsVRAddressLibraryAtLeastVersion("0.155.0", true); } auto messaging = SKSE::GetMessagingInterface();