From b7980727989beb4ce1b169492830c319b97c0c03 Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Thu, 5 Sep 2024 22:59:58 -0700 Subject: [PATCH 01/48] fix: fix detection of eyeIndex with imageshaders GetEyeIndexPS relies on VR buffers being present which may not occur for image shaders. --- package/Shaders/ISWaterBlend.hlsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/Shaders/ISWaterBlend.hlsl b/package/Shaders/ISWaterBlend.hlsl index 391c6250e..af1ef133f 100644 --- a/package/Shaders/ISWaterBlend.hlsl +++ b/package/Shaders/ISWaterBlend.hlsl @@ -32,7 +32,7 @@ cbuffer PerGeometry : register(b2) PS_OUTPUT main(PS_INPUT input) { PS_OUTPUT psout; - uint eyeIndex = GetEyeIndexPS(float4(input.TexCoord, 0, 0)); + uint eyeIndex = input.TexCoord >= 0.5; float2 adjustedScreenPosition = GetDynamicResolutionAdjustedScreenPosition(input.TexCoord); float waterMask = waterMaskTex.Sample(waterMaskSampler, adjustedScreenPosition).z; if (waterMask < 1e-4) { From c77d9e743211acc86a84a0877ed3a5ec48cabb2a Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Thu, 5 Sep 2024 23:01:02 -0700 Subject: [PATCH 02/48] fix(VR): saturate uv.x before converting --- package/Shaders/Common/VR.hlsli | 1 + 1 file changed, 1 insertion(+) diff --git a/package/Shaders/Common/VR.hlsli b/package/Shaders/Common/VR.hlsli index 07b89c314..874d7f6d0 100644 --- a/package/Shaders/Common/VR.hlsli +++ b/package/Shaders/Common/VR.hlsli @@ -25,6 +25,7 @@ float2 ConvertToStereoUV(float2 uv, uint a_eyeIndex, uint a_invertY = 0) { #ifdef VR // convert [0,1] to eye specific [0,.5] and [.5, 1] dependent on a_eyeIndex + uv.x = saturate(uv.x); uv.x = (uv.x + (float)a_eyeIndex) / 2; if (a_invertY) uv.y = 1 - uv.y; From 1c69733110e559e6377eadde0ec32d3a5b0cb9a5 Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Thu, 5 Sep 2024 23:01:18 -0700 Subject: [PATCH 03/48] feat(VR): add additional helper functions --- package/Shaders/Common/VR.hlsli | 43 +++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/package/Shaders/Common/VR.hlsli b/package/Shaders/Common/VR.hlsli index 874d7f6d0..b433afa66 100644 --- a/package/Shaders/Common/VR.hlsli +++ b/package/Shaders/Common/VR.hlsli @@ -33,6 +33,18 @@ float2 ConvertToStereoUV(float2 uv, uint a_eyeIndex, uint a_invertY = 0) return uv; } +float3 ConvertToStereoUV(float3 uv, uint a_eyeIndex, uint a_invertY = 0) +{ + uv.xy = ConvertToStereoUV(uv.xy, a_eyeIndex, a_invertY); + return uv; +} + +float4 ConvertToStereoUV(float4 uv, uint a_eyeIndex, uint a_invertY = 0) +{ + uv.xy = ConvertToStereoUV(uv.xy, a_eyeIndex, a_invertY); + return uv; +} + /** Converts from eye specific uv to general uv [0,1]. In VR, texture buffers include the left and right eye in the same buffer. @@ -54,6 +66,18 @@ float2 ConvertFromStereoUV(float2 uv, uint a_eyeIndex, uint a_invertY = 0) return uv; } +float3 ConvertFromStereoUV(float3 uv, uint a_eyeIndex, uint a_invertY = 0) +{ + uv.xy = ConvertFromStereoUV(uv.xy, a_eyeIndex, a_invertY); + return uv; +} + +float4 ConvertFromStereoUV(float4 uv, uint a_eyeIndex, uint a_invertY = 0) +{ + uv.xy = ConvertFromStereoUV(uv.xy, a_eyeIndex, a_invertY); + return uv; +} + /** Converts to the eye specific screenposition [0,Resolution]. In VR, texture buffers include the left and right eye in the same buffer. Flat only has a single camera for the entire width. @@ -66,8 +90,23 @@ This returns the adjusted value */ float2 ConvertToStereoSP(float2 screenPosition, uint a_eyeIndex, float2 a_resolution) { - screenPosition.x /= a_resolution.x; - return ConvertToStereoUV(screenPosition, a_eyeIndex) * a_resolution; + screenPosition.x /= a_resolution.x; + float2 stereoUV = ConvertToStereoUV(screenPosition, a_eyeIndex); + return stereoUV * a_resolution; +} + +float3 ConvertToStereoSP(float3 screenPosition, uint a_eyeIndex, float2 a_resolution) +{ + float2 xy = screenPosition.xy / a_resolution; + xy = ConvertToStereoUV(xy, a_eyeIndex); + return float3(xy * a_resolution, screenPosition.z); +} + +float4 ConvertToStereoSP(float4 screenPosition, uint a_eyeIndex, float2 a_resolution) +{ + float2 xy = screenPosition.xy / a_resolution; + xy = ConvertToStereoUV(xy, a_eyeIndex); + return float4(xy * a_resolution, screenPosition.zw); } #ifdef PSHADER From a5ff6e4424263cdb38e4c5f7283af64b6f65fc78 Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Thu, 5 Sep 2024 23:12:00 -0700 Subject: [PATCH 04/48] feat(VR): enable ssr This commit will be rearchitected once finalized. This includes some artifacts like black banding. --- package/Shaders/Common/FrameBuffer.hlsli | 15 +++++++++------ package/Shaders/ISReflectionsRayTracing.hlsl | 20 +++++++++++++++----- package/Shaders/Water.hlsl | 2 +- src/XSEPlugin.cpp | 15 ++++++++++++++- 4 files changed, 39 insertions(+), 13 deletions(-) diff --git a/package/Shaders/Common/FrameBuffer.hlsli b/package/Shaders/Common/FrameBuffer.hlsli index ced6aaa89..30314d263 100644 --- a/package/Shaders/Common/FrameBuffer.hlsli +++ b/package/Shaders/Common/FrameBuffer.hlsli @@ -47,17 +47,20 @@ cbuffer PerFrame : register(b12) #endif // !VR } -float2 GetDynamicResolutionAdjustedScreenPosition(float2 screenPosition) +float2 GetDynamicResolutionAdjustedScreenPosition(float2 screenPosition, uint stereo = 1) { float2 screenPositionDR = DynamicResolutionParams1.xy * screenPosition; float2 minValue = 0; float2 maxValue = float2(DynamicResolutionParams2.z, DynamicResolutionParams1.y); #if defined(VR) - bool isRight = screenPosition.x >= 0.5; - float minFactor = isRight ? 1 : 0; - minValue.x = 0.5 * (DynamicResolutionParams2.z * minFactor); - float maxFactor = isRight ? 2 : 1; - maxValue.x = 0.5 * (DynamicResolutionParams2.z * maxFactor); + // VR sometimes will clamp to stereouv + if (stereo) { + bool isRight = screenPosition.x >= 0.5; + float minFactor = isRight ? 1 : 0; + minValue.x = 0.5 * (DynamicResolutionParams2.z * minFactor); + float maxFactor = isRight ? 2 : 1; + maxValue.x = 0.5 * (DynamicResolutionParams2.z * maxFactor); + } #endif return clamp(screenPositionDR, minValue, maxValue); } diff --git a/package/Shaders/ISReflectionsRayTracing.hlsl b/package/Shaders/ISReflectionsRayTracing.hlsl index e42c4482e..8a50a889a 100644 --- a/package/Shaders/ISReflectionsRayTracing.hlsl +++ b/package/Shaders/ISReflectionsRayTracing.hlsl @@ -1,5 +1,7 @@ +#include "Common/Constants.hlsli" #include "Common/DummyVSTexCoord.hlsl" #include "Common/FrameBuffer.hlsli" +#include "Common/VR.hlsli" typedef VS_OUTPUT PS_INPUT; @@ -53,6 +55,7 @@ PS_OUTPUT main(PS_INPUT input) PS_OUTPUT psout; psout.Color = 0; + uint eyeIndex = input.TexCoord >= 0.5; float2 uvStart = input.TexCoord; float2 uvStartDR = GetDynamicResolutionAdjustedScreenPosition(uvStart); @@ -78,14 +81,15 @@ PS_OUTPUT main(PS_INPUT input) float4 normal = float4(lerp(decodedNormal, DefaultNormal, isDefaultNormal), 0); float3 uvDepthStart = float3(uvStart, depthStart); + uvDepthStart.xy = ConvertFromStereoUV(uvStart.xy, eyeIndex, 0); float3 vsStart = UVDepthToView(uvDepthStart); - float4 csStart = mul(CameraProjInverse[0], float4(vsStart, 1)); + float4 csStart = mul(CameraProjInverse[eyeIndex], float4(vsStart, 1)); csStart /= csStart.w; float4 viewDirection = float4(normalize(-csStart.xyz), 0); float4 reflectedDirection = reflect(-viewDirection, normal); float4 csFinish = csStart + reflectedDirection; - float4 vsFinish = mul(CameraProj[0], csFinish); + float4 vsFinish = mul(CameraProj[eyeIndex], csFinish); vsFinish.xyz /= vsFinish.w; float3 uvDepthFinish = ViewToUVDepth(vsFinish.xyz); @@ -94,6 +98,10 @@ PS_OUTPUT main(PS_INPUT input) float3 uvDepthFinishDR = uvDepthStart + deltaUvDepth * (SSRParams.x * rcp(length(deltaUvDepth.xy))); uvDepthFinishDR.xy = GetDynamicResolutionAdjustedScreenPosition(uvDepthFinishDR.xy); +# ifdef VR + uvStartDR.xy = GetDynamicResolutionAdjustedScreenPosition(uvDepthStart.xy); +# endif + float3 uvDepthStartDR = float3(uvStartDR, vsStart.z); float3 deltaUvDepthDR = uvDepthFinishDR - uvDepthStartDR; @@ -103,7 +111,8 @@ PS_OUTPUT main(PS_INPUT input) float iterationIndex = 1; for (; iterationIndex < 16; iterationIndex += 1) { float3 iterationUvDepthDR = uvDepthStartDR + (iterationIndex / 16) * deltaUvDepthDR; - float iterationDepth = DepthTex.SampleLevel(DepthSampler, iterationUvDepthDR.xy, 0).x; + float3 iterationUvDepthSample = ConvertToStereoUV(iterationUvDepthDR, eyeIndex); + float iterationDepth = DepthTex.SampleLevel(DepthSampler, iterationUvDepthSample.xy, 0).x; uvDepthPreResultDR = uvDepthResultDR; uvDepthResultDR = iterationUvDepthDR; if (iterationDepth < iterationUvDepthDR.z) { @@ -119,7 +128,8 @@ PS_OUTPUT main(PS_INPUT input) [unroll] for (; iterationIndex < 16; iterationIndex += 1) { uvDepthFinalDR = lerp(uvDepthPreResultDR, uvDepthResultDR, iterationIndex / 16); - float subIterationDepth = DepthTex.SampleLevel(DepthSampler, uvDepthFinalDR.xy, 0).x; + float3 uvDepthFinalSample = ConvertToStereoUV(uvDepthFinalDR, eyeIndex); + float subIterationDepth = DepthTex.SampleLevel(DepthSampler, uvDepthFinalSample.xy, 0).x; if (subIterationDepth < uvDepthFinalDR.z && uvDepthFinalDR.z < subIterationDepth + SSRParams.y) { break; } @@ -127,7 +137,7 @@ PS_OUTPUT main(PS_INPUT input) } float2 uvFinal = GetDynamicResolutionUnadjustedScreenPosition(uvDepthFinalDR.xy); - + uvFinal = ConvertToStereoUV(uvFinal, eyeIndex); float2 previousUvFinalDR = GetPreviousDynamicResolutionAdjustedScreenPosition(uvFinal); float3 alpha = AlphaTex.Sample(AlphaSampler, previousUvFinalDR).xyz; diff --git a/package/Shaders/Water.hlsl b/package/Shaders/Water.hlsl index fe62f3b82..563b26227 100644 --- a/package/Shaders/Water.hlsl +++ b/package/Shaders/Water.hlsl @@ -597,7 +597,7 @@ float3 GetWaterSpecularColor(PS_INPUT input, float3 normal, float3 viewDirection reflectionColor = ReflectionTex.SampleLevel(ReflectionSampler, reflectionNormal.xy / reflectionNormal.ww, 0).xyz; } -# if !defined(LOD) && NUM_SPECULAR_LIGHTS == 0 && !defined(VR) +# if !defined(LOD) && NUM_SPECULAR_LIGHTS == 0 if (PixelShaderDescriptor & _Cubemap) { float2 ssrReflectionUv = GetDynamicResolutionAdjustedScreenPosition((DynamicResolutionParams2.xy * input.HPosition.xy) * SSRParams.zw + SSRParams2.x * normal.xy); float4 ssrReflectionColor1 = SSRReflectionTex.Sample(SSRReflectionSampler, ssrReflectionUv); diff --git a/src/XSEPlugin.cpp b/src/XSEPlugin.cpp index f9027fcdc..7c55e0902 100644 --- a/src/XSEPlugin.cpp +++ b/src/XSEPlugin.cpp @@ -95,7 +95,20 @@ void MessageHandler(SKSE::MessagingInterface::Message* message) } } } - + if (REL::Module::IsVR()) { + std::map hiddenVRCubeMapSettings{ + { "bScreenSpaceReflectionEnabled:Display", 0x1ED5BC0 }, + }; + for (const auto& settingPair : hiddenVRCubeMapSettings) { + const auto& settingName = settingPair.first; + const auto address = REL::Offset{ settingPair.second }.address(); + bool* setting = reinterpret_cast(address); + if (!*setting) { + logger::info("[PostPostLoad] Changing {} from {} to {} to support Dynamic Cubemaps", settingName, *setting, true); + *setting = true; + } + } + } break; } case SKSE::MessagingInterface::kDataLoaded: From afc0fdbf9b07565072730fa94d8c90cc83de8c11 Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Thu, 5 Sep 2024 23:15:13 -0700 Subject: [PATCH 05/48] chore: remove color lerping with reflectioncolor This is for testing only as it removes any averaging of color with reflectioncolor. This is meant solely to make it easy to see the SSR effect. This will be reverted in a future commit. --- package/Shaders/Water.hlsl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package/Shaders/Water.hlsl b/package/Shaders/Water.hlsl index 563b26227..9af5985dc 100644 --- a/package/Shaders/Water.hlsl +++ b/package/Shaders/Water.hlsl @@ -609,9 +609,9 @@ float3 GetWaterSpecularColor(PS_INPUT input, float3 normal, float3 viewDirection } # endif - float3 finalReflectionColor = lerp(reflectionColor, finalSsrReflectionColor, ssrFraction); + float3 finalReflectionColor = lerp(finalSsrReflectionColor, finalSsrReflectionColor, ssrFraction); - return lerp(ReflectionColor.xyz, finalReflectionColor, VarAmounts.y); + return lerp(finalReflectionColor.xyz, finalReflectionColor, VarAmounts.y); } return ReflectionColor.xyz * VarAmounts.y; } From 1a1cf8a53c282cda8dc557169cd709aad35ec9e1 Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Fri, 6 Sep 2024 23:35:25 -0700 Subject: [PATCH 06/48] feat: add Stochastic Sampling to SSR --- package/Shaders/ISReflectionsRayTracing.hlsl | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/package/Shaders/ISReflectionsRayTracing.hlsl b/package/Shaders/ISReflectionsRayTracing.hlsl index 8a50a889a..4339c4f1b 100644 --- a/package/Shaders/ISReflectionsRayTracing.hlsl +++ b/package/Shaders/ISReflectionsRayTracing.hlsl @@ -50,6 +50,14 @@ float3 ViewToUVDepth(float3 view) return float3(0.5 * view.x + 0.5, 0.5 - 0.5 * view.y, view.z); } +// Simple hash function for generating a pseudo-random float +float hash(float2 p) +{ + p = frac(p * float2(0.1031, 0.1030)); // Random values for perturbation + p *= dot(p, p.xy + float2(33.33, 33.33)); // Mix values + return frac(p.x * p.y * float2(0.5, 0.5).x); +} + PS_OUTPUT main(PS_INPUT input) { PS_OUTPUT psout; @@ -88,6 +96,8 @@ PS_OUTPUT main(PS_INPUT input) csStart /= csStart.w; float4 viewDirection = float4(normalize(-csStart.xyz), 0); float4 reflectedDirection = reflect(-viewDirection, normal); + float2 jitter = hash(input.TexCoord) * SSRParams.xy; // Small offset + reflectedDirection.xy += jitter; float4 csFinish = csStart + reflectedDirection; float4 vsFinish = mul(CameraProj[eyeIndex], csFinish); vsFinish.xyz /= vsFinish.w; From 0149fe2216dbb68547b14f691bbed1a4fc3a0ce1 Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Fri, 6 Sep 2024 23:36:52 -0700 Subject: [PATCH 07/48] feat: add Dynamic Step Size to SSR --- package/Shaders/ISReflectionsRayTracing.hlsl | 22 ++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/package/Shaders/ISReflectionsRayTracing.hlsl b/package/Shaders/ISReflectionsRayTracing.hlsl index 4339c4f1b..88536b13e 100644 --- a/package/Shaders/ISReflectionsRayTracing.hlsl +++ b/package/Shaders/ISReflectionsRayTracing.hlsl @@ -118,9 +118,14 @@ PS_OUTPUT main(PS_INPUT input) float3 uvDepthPreResultDR = uvDepthStartDR; float3 uvDepthResultDR = float3(uvDepthStartDR.xy, depthStart); + float dynamicStepSize = length(deltaUvDepthDR.xy) * SSRParams.w; + dynamicStepSize = max(dynamicStepSize, 1e-3); + float iterationIndex = 1; - for (; iterationIndex < 16; iterationIndex += 1) { - float3 iterationUvDepthDR = uvDepthStartDR + (iterationIndex / 16) * deltaUvDepthDR; + const int maxIterations = 32; // Adjust based on performance/quality tradeoff + + for (; iterationIndex < maxIterations; iterationIndex++) { + float3 iterationUvDepthDR = uvDepthStartDR + (iterationIndex * dynamicStepSize) * deltaUvDepthDR; float3 iterationUvDepthSample = ConvertToStereoUV(iterationUvDepthDR, eyeIndex); float iterationDepth = DepthTex.SampleLevel(DepthSampler, iterationUvDepthSample.xy, 0).x; uvDepthPreResultDR = uvDepthResultDR; @@ -128,16 +133,21 @@ PS_OUTPUT main(PS_INPUT input) if (iterationDepth < iterationUvDepthDR.z) { break; } + // Reduce step size less aggressively if the scene is less complex + dynamicStepSize *= 0.9; } + // Handling the final result float3 uvDepthFinalDR = uvDepthResultDR; - if (iterationIndex < 16) { + if (iterationIndex < maxIterations) { + dynamicStepSize = length(deltaUvDepthDR.xy) * SSRParams.w; + dynamicStepSize = max(dynamicStepSize, 1e-3); iterationIndex = 0; uvDepthFinalDR = uvDepthPreResultDR; - [unroll] for (; iterationIndex < 16; iterationIndex += 1) + [unroll] for (; iterationIndex < maxIterations; iterationIndex++) { - uvDepthFinalDR = lerp(uvDepthPreResultDR, uvDepthResultDR, iterationIndex / 16); + uvDepthFinalDR = lerp(uvDepthPreResultDR, uvDepthResultDR, iterationIndex / float(maxIterations)); float3 uvDepthFinalSample = ConvertToStereoUV(uvDepthFinalDR, eyeIndex); float subIterationDepth = DepthTex.SampleLevel(DepthSampler, uvDepthFinalSample.xy, 0).x; if (subIterationDepth < uvDepthFinalDR.z && uvDepthFinalDR.z < subIterationDepth + SSRParams.y) { @@ -172,7 +182,7 @@ PS_OUTPUT main(PS_INPUT input) return psout; } - [branch] if (iterationIndex == 16) + [branch] if (iterationIndex == maxIterations) { return psout; } From f53e14a3a17c1101c9bf5611de2e50a4fc515a2c Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Fri, 6 Sep 2024 23:37:50 -0700 Subject: [PATCH 08/48] feat: add Fallback Sampling to SSR --- package/Shaders/Common/VR.hlsli | 34 ++++++++++---------- package/Shaders/ISReflectionsRayTracing.hlsl | 10 ++++-- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/package/Shaders/Common/VR.hlsli b/package/Shaders/Common/VR.hlsli index b433afa66..89358a736 100644 --- a/package/Shaders/Common/VR.hlsli +++ b/package/Shaders/Common/VR.hlsli @@ -35,14 +35,14 @@ float2 ConvertToStereoUV(float2 uv, uint a_eyeIndex, uint a_invertY = 0) float3 ConvertToStereoUV(float3 uv, uint a_eyeIndex, uint a_invertY = 0) { - uv.xy = ConvertToStereoUV(uv.xy, a_eyeIndex, a_invertY); - return uv; + uv.xy = ConvertToStereoUV(uv.xy, a_eyeIndex, a_invertY); + return uv; } float4 ConvertToStereoUV(float4 uv, uint a_eyeIndex, uint a_invertY = 0) { - uv.xy = ConvertToStereoUV(uv.xy, a_eyeIndex, a_invertY); - return uv; + uv.xy = ConvertToStereoUV(uv.xy, a_eyeIndex, a_invertY); + return uv; } /** @@ -68,14 +68,14 @@ float2 ConvertFromStereoUV(float2 uv, uint a_eyeIndex, uint a_invertY = 0) float3 ConvertFromStereoUV(float3 uv, uint a_eyeIndex, uint a_invertY = 0) { - uv.xy = ConvertFromStereoUV(uv.xy, a_eyeIndex, a_invertY); - return uv; + uv.xy = ConvertFromStereoUV(uv.xy, a_eyeIndex, a_invertY); + return uv; } float4 ConvertFromStereoUV(float4 uv, uint a_eyeIndex, uint a_invertY = 0) { - uv.xy = ConvertFromStereoUV(uv.xy, a_eyeIndex, a_invertY); - return uv; + uv.xy = ConvertFromStereoUV(uv.xy, a_eyeIndex, a_invertY); + return uv; } /** @@ -90,23 +90,23 @@ This returns the adjusted value */ float2 ConvertToStereoSP(float2 screenPosition, uint a_eyeIndex, float2 a_resolution) { - screenPosition.x /= a_resolution.x; - float2 stereoUV = ConvertToStereoUV(screenPosition, a_eyeIndex); - return stereoUV * a_resolution; + screenPosition.x /= a_resolution.x; + float2 stereoUV = ConvertToStereoUV(screenPosition, a_eyeIndex); + return stereoUV * a_resolution; } float3 ConvertToStereoSP(float3 screenPosition, uint a_eyeIndex, float2 a_resolution) { - float2 xy = screenPosition.xy / a_resolution; - xy = ConvertToStereoUV(xy, a_eyeIndex); - return float3(xy * a_resolution, screenPosition.z); + float2 xy = screenPosition.xy / a_resolution; + xy = ConvertToStereoUV(xy, a_eyeIndex); + return float3(xy * a_resolution, screenPosition.z); } float4 ConvertToStereoSP(float4 screenPosition, uint a_eyeIndex, float2 a_resolution) { - float2 xy = screenPosition.xy / a_resolution; - xy = ConvertToStereoUV(xy, a_eyeIndex); - return float4(xy * a_resolution, screenPosition.zw); + float2 xy = screenPosition.xy / a_resolution; + xy = ConvertToStereoUV(xy, a_eyeIndex); + return float4(xy * a_resolution, screenPosition.zw); } #ifdef PSHADER diff --git a/package/Shaders/ISReflectionsRayTracing.hlsl b/package/Shaders/ISReflectionsRayTracing.hlsl index 88536b13e..d5a896e14 100644 --- a/package/Shaders/ISReflectionsRayTracing.hlsl +++ b/package/Shaders/ISReflectionsRayTracing.hlsl @@ -184,10 +184,14 @@ PS_OUTPUT main(PS_INPUT input) [branch] if (iterationIndex == maxIterations) { - return psout; + float3 originalColor = ColorTex.Sample(ColorSampler, uvStartDR).rgb; + float fallbackBlendFactor = .25; + psout.Color.rgb = lerp(originalColor, ssrColor, fallbackBlendFactor); + } + else + { + psout.Color.rgb = ssrColor; } - - psout.Color.rgb = ssrColor; float2 deltaUv = uvFinal - uvStart; float ssrMarchingRadiusFadeFactor = 1 - length(deltaUv) * SSRParams.w; From 8c2d00ae944dc2ab60bf36b3755b0435dbabfa31 Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Sun, 8 Sep 2024 00:21:29 -0700 Subject: [PATCH 09/48] fix: fix eyeindex for flat --- package/Shaders/ISReflectionsRayTracing.hlsl | 4 ++++ package/Shaders/ISWaterBlend.hlsl | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/package/Shaders/ISReflectionsRayTracing.hlsl b/package/Shaders/ISReflectionsRayTracing.hlsl index d5a896e14..6db026ec7 100644 --- a/package/Shaders/ISReflectionsRayTracing.hlsl +++ b/package/Shaders/ISReflectionsRayTracing.hlsl @@ -63,7 +63,11 @@ PS_OUTPUT main(PS_INPUT input) PS_OUTPUT psout; psout.Color = 0; +# ifdef VR uint eyeIndex = input.TexCoord >= 0.5; +# else + uint eyeIndex = 0; +# endif float2 uvStart = input.TexCoord; float2 uvStartDR = GetDynamicResolutionAdjustedScreenPosition(uvStart); diff --git a/package/Shaders/ISWaterBlend.hlsl b/package/Shaders/ISWaterBlend.hlsl index af1ef133f..9f32b2d90 100644 --- a/package/Shaders/ISWaterBlend.hlsl +++ b/package/Shaders/ISWaterBlend.hlsl @@ -32,7 +32,11 @@ cbuffer PerGeometry : register(b2) PS_OUTPUT main(PS_INPUT input) { PS_OUTPUT psout; +# ifdef VR uint eyeIndex = input.TexCoord >= 0.5; +# else + uint eyeIndex = 0; +# endif float2 adjustedScreenPosition = GetDynamicResolutionAdjustedScreenPosition(input.TexCoord); float waterMask = waterMaskTex.Sample(waterMaskSampler, adjustedScreenPosition).z; if (waterMask < 1e-4) { From 789f1d972439a36fd94fe164f68d8f018a62a369 Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Sun, 8 Sep 2024 16:12:29 -0700 Subject: [PATCH 10/48] fix(VR): disable clouds in SSR Because alpha is based on the prior frame, there will be a lag for showing clouds. This is very obvious in VR. Hide clouds for now. --- package/Shaders/ISReflectionsRayTracing.hlsl | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/package/Shaders/ISReflectionsRayTracing.hlsl b/package/Shaders/ISReflectionsRayTracing.hlsl index 6db026ec7..a4b92dc97 100644 --- a/package/Shaders/ISReflectionsRayTracing.hlsl +++ b/package/Shaders/ISReflectionsRayTracing.hlsl @@ -168,6 +168,13 @@ PS_OUTPUT main(PS_INPUT input) float2 uvFinalDR = GetDynamicResolutionAdjustedScreenPosition(uvFinal); float3 color = ColorTex.Sample(ColorSampler, uvFinalDR).xyz; +# ifdef VR + const bool useAlpha = false; + // Because alpha is based on the prior frame, there will be a lag for showing clouds. + // This is very obvious in VR. Hide clouds for now. + alpha = useAlpha ? alpha : float3(0, 0, 0); +# endif + float3 ssrColor = SSRParams.z * alpha + color; [branch] if (isSsrDisabled) From caeeea4cfbf909e4d00f0cd59d888e84ae4156b9 Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Sun, 8 Sep 2024 16:31:24 -0700 Subject: [PATCH 11/48] fix: correct dynamic step size calculation --- package/Shaders/ISReflectionsRayTracing.hlsl | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/package/Shaders/ISReflectionsRayTracing.hlsl b/package/Shaders/ISReflectionsRayTracing.hlsl index a4b92dc97..a3fe642ea 100644 --- a/package/Shaders/ISReflectionsRayTracing.hlsl +++ b/package/Shaders/ISReflectionsRayTracing.hlsl @@ -129,15 +129,14 @@ PS_OUTPUT main(PS_INPUT input) const int maxIterations = 32; // Adjust based on performance/quality tradeoff for (; iterationIndex < maxIterations; iterationIndex++) { - float3 iterationUvDepthDR = uvDepthStartDR + (iterationIndex * dynamicStepSize) * deltaUvDepthDR; + float3 iterationUvDepthDR = uvDepthStartDR + (dynamicStepSize * deltaUvDepthDR); float3 iterationUvDepthSample = ConvertToStereoUV(iterationUvDepthDR, eyeIndex); float iterationDepth = DepthTex.SampleLevel(DepthSampler, iterationUvDepthSample.xy, 0).x; uvDepthPreResultDR = uvDepthResultDR; uvDepthResultDR = iterationUvDepthDR; - if (iterationDepth < iterationUvDepthDR.z) { + if (iterationDepth < iterationUvDepthDR.z) { // ray intersection detected break; } - // Reduce step size less aggressively if the scene is less complex dynamicStepSize *= 0.9; } @@ -145,8 +144,7 @@ PS_OUTPUT main(PS_INPUT input) float3 uvDepthFinalDR = uvDepthResultDR; if (iterationIndex < maxIterations) { - dynamicStepSize = length(deltaUvDepthDR.xy) * SSRParams.w; - dynamicStepSize = max(dynamicStepSize, 1e-3); + // refine the hit by searching between the start and hit boundary iterationIndex = 0; uvDepthFinalDR = uvDepthPreResultDR; [unroll] for (; iterationIndex < maxIterations; iterationIndex++) From de7869b27198c722d75aaea76fa94d9335ae6e9b Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Sun, 8 Sep 2024 16:32:03 -0700 Subject: [PATCH 12/48] fix: fix early exit --- package/Shaders/ISReflectionsRayTracing.hlsl | 1 + 1 file changed, 1 insertion(+) diff --git a/package/Shaders/ISReflectionsRayTracing.hlsl b/package/Shaders/ISReflectionsRayTracing.hlsl index a3fe642ea..dd687c8a4 100644 --- a/package/Shaders/ISReflectionsRayTracing.hlsl +++ b/package/Shaders/ISReflectionsRayTracing.hlsl @@ -196,6 +196,7 @@ PS_OUTPUT main(PS_INPUT input) float3 originalColor = ColorTex.Sample(ColorSampler, uvStartDR).rgb; float fallbackBlendFactor = .25; psout.Color.rgb = lerp(originalColor, ssrColor, fallbackBlendFactor); + return psout; } else { From f434d8f41c9148e0e7041c5767672985a8b93bb2 Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Mon, 9 Sep 2024 01:37:00 -0700 Subject: [PATCH 13/48] fix: remove fallback sampling In the image shader, the only available textures result in a transparent value because it is the scene without any water. The color represents the floor under the water which basically makes the water transparent. A fallback algorithm is properly better done in the water shader. --- package/Shaders/ISReflectionsRayTracing.hlsl | 3 --- 1 file changed, 3 deletions(-) diff --git a/package/Shaders/ISReflectionsRayTracing.hlsl b/package/Shaders/ISReflectionsRayTracing.hlsl index dd687c8a4..3c6afaf70 100644 --- a/package/Shaders/ISReflectionsRayTracing.hlsl +++ b/package/Shaders/ISReflectionsRayTracing.hlsl @@ -193,9 +193,6 @@ PS_OUTPUT main(PS_INPUT input) [branch] if (iterationIndex == maxIterations) { - float3 originalColor = ColorTex.Sample(ColorSampler, uvStartDR).rgb; - float fallbackBlendFactor = .25; - psout.Color.rgb = lerp(originalColor, ssrColor, fallbackBlendFactor); return psout; } else From db465b681d25e26d9a8820e8c57e6e39555368fd Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Mon, 9 Sep 2024 01:41:09 -0700 Subject: [PATCH 14/48] perf: use binary search for refinement loop Replaces the linear search with a binary search. This change makes the unroll likely unnecessary given the improved performance. --- package/Shaders/ISReflectionsRayTracing.hlsl | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/package/Shaders/ISReflectionsRayTracing.hlsl b/package/Shaders/ISReflectionsRayTracing.hlsl index 3c6afaf70..55c3e30ab 100644 --- a/package/Shaders/ISReflectionsRayTracing.hlsl +++ b/package/Shaders/ISReflectionsRayTracing.hlsl @@ -147,14 +147,21 @@ PS_OUTPUT main(PS_INPUT input) // refine the hit by searching between the start and hit boundary iterationIndex = 0; uvDepthFinalDR = uvDepthPreResultDR; - [unroll] for (; iterationIndex < maxIterations; iterationIndex++) + for (; iterationIndex < maxIterations; iterationIndex++) { - uvDepthFinalDR = lerp(uvDepthPreResultDR, uvDepthResultDR, iterationIndex / float(maxIterations)); + uvDepthFinalDR = (uvDepthPreResultDR + uvDepthResultDR) * 0.5; float3 uvDepthFinalSample = ConvertToStereoUV(uvDepthFinalDR, eyeIndex); float subIterationDepth = DepthTex.SampleLevel(DepthSampler, uvDepthFinalSample.xy, 0).x; if (subIterationDepth < uvDepthFinalDR.z && uvDepthFinalDR.z < subIterationDepth + SSRParams.y) { break; } + if (subIterationDepth < uvDepthFinalDR.z) { + // If intersection is closer, move towards uvDepthPreResultDR (lower half) + uvDepthResultDR = uvDepthFinalDR; + } else { + // Otherwise, move towards uvDepthResultDR (uppser half) + uvDepthPreResultDR = uvDepthFinalDR; + } } } From 1f7b1f12d65eea0ac9622642559e77a5f3019a36 Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Mon, 9 Sep 2024 01:43:35 -0700 Subject: [PATCH 15/48] feat: add jittered view direction --- package/Shaders/ISReflectionsRayTracing.hlsl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/package/Shaders/ISReflectionsRayTracing.hlsl b/package/Shaders/ISReflectionsRayTracing.hlsl index 55c3e30ab..67ff19e6d 100644 --- a/package/Shaders/ISReflectionsRayTracing.hlsl +++ b/package/Shaders/ISReflectionsRayTracing.hlsl @@ -99,9 +99,10 @@ PS_OUTPUT main(PS_INPUT input) float4 csStart = mul(CameraProjInverse[eyeIndex], float4(vsStart, 1)); csStart /= csStart.w; float4 viewDirection = float4(normalize(-csStart.xyz), 0); - float4 reflectedDirection = reflect(-viewDirection, normal); - float2 jitter = hash(input.TexCoord) * SSRParams.xy; // Small offset - reflectedDirection.xy += jitter; + // Apply jitter to view direction + float2 jitter = hash(input.TexCoord) * SSRParams.xy; + float4 jitteredViewDirection = float4(normalize(viewDirection.xyz + float3(jitter, 0.0)), 0.0); + float4 reflectedDirection = normalize(reflect(-jitteredViewDirection, normal)); float4 csFinish = csStart + reflectedDirection; float4 vsFinish = mul(CameraProj[eyeIndex], csFinish); vsFinish.xyz /= vsFinish.w; From 32143229c50b1227c61976053616915e94acc823 Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Mon, 9 Sep 2024 01:44:48 -0700 Subject: [PATCH 16/48] perf: restore linear search for initial raymarch With the addition of a binary search for refinement, there is less need for dynamic stepping during the initial raymarch. Also, the dynamic step size reducing by 0.9 could result in the final UV being less than `uvDepthStartDR + deltaUvDepthDR` which could cause unexpected behavior vs the original vanilla algorithm. --- package/Shaders/ISReflectionsRayTracing.hlsl | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/package/Shaders/ISReflectionsRayTracing.hlsl b/package/Shaders/ISReflectionsRayTracing.hlsl index 67ff19e6d..31315dfa0 100644 --- a/package/Shaders/ISReflectionsRayTracing.hlsl +++ b/package/Shaders/ISReflectionsRayTracing.hlsl @@ -123,14 +123,11 @@ PS_OUTPUT main(PS_INPUT input) float3 uvDepthPreResultDR = uvDepthStartDR; float3 uvDepthResultDR = float3(uvDepthStartDR.xy, depthStart); - float dynamicStepSize = length(deltaUvDepthDR.xy) * SSRParams.w; - dynamicStepSize = max(dynamicStepSize, 1e-3); - - float iterationIndex = 1; - const int maxIterations = 32; // Adjust based on performance/quality tradeoff + int iterationIndex = 1; + const int maxIterations = 16; // Adjust based on performance/quality tradeoff for (; iterationIndex < maxIterations; iterationIndex++) { - float3 iterationUvDepthDR = uvDepthStartDR + (dynamicStepSize * deltaUvDepthDR); + float3 iterationUvDepthDR = uvDepthStartDR + (iterationIndex / 16.0) * deltaUvDepthDR; float3 iterationUvDepthSample = ConvertToStereoUV(iterationUvDepthDR, eyeIndex); float iterationDepth = DepthTex.SampleLevel(DepthSampler, iterationUvDepthSample.xy, 0).x; uvDepthPreResultDR = uvDepthResultDR; @@ -138,7 +135,6 @@ PS_OUTPUT main(PS_INPUT input) if (iterationDepth < iterationUvDepthDR.z) { // ray intersection detected break; } - dynamicStepSize *= 0.9; } // Handling the final result From e54bafb7ea047bc96d796a95e4cc477d4cce8324 Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Mon, 9 Sep 2024 01:47:49 -0700 Subject: [PATCH 17/48] docs: add comments for fade calculations --- package/Shaders/ISReflectionsRayTracing.hlsl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/package/Shaders/ISReflectionsRayTracing.hlsl b/package/Shaders/ISReflectionsRayTracing.hlsl index 31315dfa0..57fcd6735 100644 --- a/package/Shaders/ISReflectionsRayTracing.hlsl +++ b/package/Shaders/ISReflectionsRayTracing.hlsl @@ -204,13 +204,18 @@ PS_OUTPUT main(PS_INPUT input) psout.Color.rgb = ssrColor; } + // Fade Calculations + + // SSR Marching Radius Fade Factor (based on ray distance) float2 deltaUv = uvFinal - uvStart; float ssrMarchingRadiusFadeFactor = 1 - length(deltaUv) * SSRParams.w; + // Screen Center Distance Fade Factor float2 uvResultScreenCenterOffset = uvFinal - 0.5; float centerDistance = min(1, 2 * length(uvResultScreenCenterOffset)); float centerDistanceFadeFactor = 1 - centerDistance * centerDistance; + // Final alpha calculation psout.Color.a = ssrPower * ssrMarchingRadiusFadeFactor * centerDistanceFadeFactor; # endif From 3f3b731e3dc9fb6c33933b13dab1f5e6043b69fc Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Mon, 9 Sep 2024 01:52:20 -0700 Subject: [PATCH 18/48] feat(VR): add stereo-consistent SSR improvements Add suggested improvements by Stereo-consistent Screen Space Reflection Wu, X., Xu, Y., & Wang, L. (2024). Stereo-consistent Screen Space Reflection. Computer Graphics Forum, 43(4). https://cuteloong.github.io/publications/scssr24/ --- package/Shaders/Common/FrameBuffer.hlsli | 26 ++++++++++++++ package/Shaders/Common/VR.hlsli | 36 ++++++++++++++++++++ package/Shaders/ISReflectionsRayTracing.hlsl | 17 ++++++--- 3 files changed, 74 insertions(+), 5 deletions(-) diff --git a/package/Shaders/Common/FrameBuffer.hlsli b/package/Shaders/Common/FrameBuffer.hlsli index 30314d263..38d5b4560 100644 --- a/package/Shaders/Common/FrameBuffer.hlsli +++ b/package/Shaders/Common/FrameBuffer.hlsli @@ -102,3 +102,29 @@ float2 ViewToUV(float3 x, bool is_position = true, uint a_eyeIndex = 0) float4 uv = mul(CameraProj[a_eyeIndex], newPosition); return (uv.xy / uv.w) * float2(0.5f, -0.5f) + 0.5f; } + +bool isOutsideFrame(float2 uv) +{ + bool result = uv.x < 0 || uv.x > 1 || uv.y < 0 || uv.y > 1; + return result; +} + +// Convert from mono UV of one eye to the corresponding mono UV of the other eye +float3 ConvertMonoUVToOtherEye(float3 monoUV, uint eyeIndex) +{ + // Step 1: Convert UV to Clip Space + float4 clipPos = float4(monoUV.xy * float2(2, -2) - float2(1, -1), monoUV.z, 1); + + // Step 2: Convert Clip Space to View Space for the current eye + float4 viewPosCurrentEye = mul(CameraProjInverse[eyeIndex], clipPos); + viewPosCurrentEye /= viewPosCurrentEye.w; + + // Step 3: Convert View Space to Clip Space for the other eye + float4 clipPosOtherEye = mul(CameraProj[1 - eyeIndex], viewPosCurrentEye); + clipPosOtherEye /= clipPosOtherEye.w; + + // Step 4: Convert Clip Space to UV + float3 monoUVOtherEye = float3((clipPosOtherEye.xy * 0.5f) + 0.5f, clipPosOtherEye.z); + + return monoUVOtherEye; +} \ No newline at end of file diff --git a/package/Shaders/Common/VR.hlsli b/package/Shaders/Common/VR.hlsli index 89358a736..5f54a6ad7 100644 --- a/package/Shaders/Common/VR.hlsli +++ b/package/Shaders/Common/VR.hlsli @@ -142,6 +142,42 @@ uint GetEyeIndexFromTexCoord(float2 texCoord) return 0; } +/** + * @brief Adjusts UV coordinates for VR stereo rendering when transitioning between eyes or handling boundary conditions. + * + * This is intended to be used in a raymarch to check the next uv coordinate. + * This function checks if the current UV coordinates are outside the frame. If so, it transitions the UV coordinates to the other eye + * and adjusts them if they are within the frame of the other eye. If the UV coordinates are outside the frame of both eyes, it will return + * the adjusted UV coordinates for the current eye. + * + * The function ensures that the UV coordinates are correctly adjusted for stereo rendering, taking into account the boundary conditions + * and preserving accurate reflections. + * Based on concepts from https://cuteloong.github.io/publications/scssr24/ + * Wu, X., Xu, Y., & Wang, L. (2024). Stereo-consistent Screen Space Reflection. Computer Graphics Forum, 43(4). + * + * We do not have a backface depth so we may be ray marching even though + * the ray is in an object. + * @param[in] monoUV Current UV coordinates with depth information. + * @param[in] eyeIndex Index of the current eye (0 or 1). + * + * @return Adjusted UV coordinates for stereo rendering. + */ +float3 ConvertStereoRayMarchUV(float3 monoUV, uint eyeIndex) +{ + float3 resultUV = ConvertToStereoUV(monoUV, eyeIndex); + +#ifdef VR + if (isOutsideFrame(resultUV.xy)) { + // Transition to the other eye + float3 otherEyeUV = ConvertMonoUVToOtherEye(resultUV, 1 - eyeIndex); + if (!isOutsideFrame(otherEyeUV.xy)) { + resultUV = ConvertToStereoUV(otherEyeUV, 1 - eyeIndex); + } + } +#endif + return resultUV; +} + struct VR_OUTPUT { float4 VRPosition; diff --git a/package/Shaders/ISReflectionsRayTracing.hlsl b/package/Shaders/ISReflectionsRayTracing.hlsl index 57fcd6735..1d2d186dd 100644 --- a/package/Shaders/ISReflectionsRayTracing.hlsl +++ b/package/Shaders/ISReflectionsRayTracing.hlsl @@ -124,11 +124,11 @@ PS_OUTPUT main(PS_INPUT input) float3 uvDepthResultDR = float3(uvDepthStartDR.xy, depthStart); int iterationIndex = 1; - const int maxIterations = 16; // Adjust based on performance/quality tradeoff + const int maxIterations = 16; // Adjust based on performance/quality tradeoff for (; iterationIndex < maxIterations; iterationIndex++) { float3 iterationUvDepthDR = uvDepthStartDR + (iterationIndex / 16.0) * deltaUvDepthDR; - float3 iterationUvDepthSample = ConvertToStereoUV(iterationUvDepthDR, eyeIndex); + float3 iterationUvDepthSample = ConvertStereoRayMarchUV(iterationUvDepthDR, eyeIndex); float iterationDepth = DepthTex.SampleLevel(DepthSampler, iterationUvDepthSample.xy, 0).x; uvDepthPreResultDR = uvDepthResultDR; uvDepthResultDR = iterationUvDepthDR; @@ -144,10 +144,9 @@ PS_OUTPUT main(PS_INPUT input) // refine the hit by searching between the start and hit boundary iterationIndex = 0; uvDepthFinalDR = uvDepthPreResultDR; - for (; iterationIndex < maxIterations; iterationIndex++) - { + for (; iterationIndex < maxIterations; iterationIndex++) { uvDepthFinalDR = (uvDepthPreResultDR + uvDepthResultDR) * 0.5; - float3 uvDepthFinalSample = ConvertToStereoUV(uvDepthFinalDR, eyeIndex); + float3 uvDepthFinalSample = ConvertStereoRayMarchUV(uvDepthFinalDR, eyeIndex); float subIterationDepth = DepthTex.SampleLevel(DepthSampler, uvDepthFinalSample.xy, 0).x; if (subIterationDepth < uvDepthFinalDR.z && uvDepthFinalDR.z < subIterationDepth + SSRParams.y) { break; @@ -213,6 +212,14 @@ PS_OUTPUT main(PS_INPUT input) // Screen Center Distance Fade Factor float2 uvResultScreenCenterOffset = uvFinal - 0.5; float centerDistance = min(1, 2 * length(uvResultScreenCenterOffset)); + +# ifdef VR + // Make VR fades consistent by taking the closer of the two eyes + // Based on concepts from https://cuteloong.github.io/publications/scssr24/ + float2 otherEyeUvResultScreenCenterOffset = ConvertMonoUVToOtherEye(uvDepthFinalDR, eyeIndex).xy - 0.5; + centerDistance = min(centerDistance, 2 * length(otherEyeUvResultScreenCenterOffset)); +# endif + float centerDistanceFadeFactor = 1 - centerDistance * centerDistance; // Final alpha calculation From 494a6933fc1f39cc28798dcd4a1ad7530b8d7f67 Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Mon, 9 Sep 2024 21:52:02 -0700 Subject: [PATCH 19/48] chore: add debug defines --- package/Shaders/ISReflectionsRayTracing.hlsl | 85 ++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/package/Shaders/ISReflectionsRayTracing.hlsl b/package/Shaders/ISReflectionsRayTracing.hlsl index 1d2d186dd..8f922cf74 100644 --- a/package/Shaders/ISReflectionsRayTracing.hlsl +++ b/package/Shaders/ISReflectionsRayTracing.hlsl @@ -137,6 +137,45 @@ PS_OUTPUT main(PS_INPUT input) } } +# ifdef DEBUG_SSR_RAYMARCH_ITERATIONS + // Visualize the number of raymarch iterations used for each pixel. This is for the initial + // attempt to find the intersection using linear search. + // Blue = 0, Red = max, should move through purple + float iterationColor = float(iterationIndex) / float(maxIterations); + psout.Color = float4(iterationColor, 0, 1 - iterationColor, 1); + return psout; +# endif + +# ifdef DEBUG_SSR_RAYMARCH_FIRST_HIT + // Visualize the position of the first hit for each pixel during raymarching. + // This shows where the initial intersection was found in the linear search. + // Red intensity represents the depth of the hit: + // Dark red = hit close to the camera (small number of iterations) + // Bright red = hit far from the camera (large number of iterations) + // Black = no hit found within max iterations + float hitDepth = (float)iterationIndex / (float)maxIterations; + psout.Color = float4(hitDepth, 0, 0, 1); + return psout; +# endif + +# ifdef DEBUG_SSR_RAYMARCH_DETAILED + // Visualize the start and end positions of the reflection ray + // Red channel: start position X + // Green channel: start position Y + // Blue channel: end position X + // Alpha channel: end position Y + + // Color interpretation: + // - Start position dominant (yellow/greenish-yellow): shorter ray or less travel + // - End position dominant (blue/purple): longer ray or more travel + // - Equal mix (brown/gray): medium-length ray + // - Brighter colors: stronger reflection or more direct hit + // - Darker colors: weaker reflection or more glancing hit + + psout.Color = float4(uvDepthStartDR.xy, uvDepthFinishDR.xy); + return psout; +# endif + // Handling the final result float3 uvDepthFinalDR = uvDepthResultDR; @@ -167,6 +206,52 @@ PS_OUTPUT main(PS_INPUT input) float3 alpha = AlphaTex.Sample(AlphaSampler, previousUvFinalDR).xyz; float2 uvFinalDR = GetDynamicResolutionAdjustedScreenPosition(uvFinal); + +# ifdef DEBUG_SSR_UV + // This helps identify whether the UV coordinates are being properly transformed, and whether they + // are behaving as expected across the screen when the camera pitch changes. + // When you run this, you should see a gradient across the screen, with colors changing smoothly from + // one corner of the screen to the other. + // Look for areas where the UVs become incorrect or discontinuous, especially as you tilt the camera + // downwards. If the UVs start to distort or shift, this can explain why reflections are moving in + // unexpected ways. + psout.Color = float4(uvFinalDR, 0, 1); + return psout; +# endif + +# ifdef DEBUG_SSR_DEPTH + // Sample depth at the current UV and return it as a grayscale value + // Helps determine if the depth values are sampled correctly at each UV position. + // You should see smooth gradients of depth across the screen. If the depth values + // suddenly shift, it could explain why reflections are appearing in the wrong places. + float depth = DepthTex.Sample(DepthSampler, uvFinalDR).x; + + // Output the depth as a grayscale color (depth values are expected to be between 0 and 1) + psout.Color = float4(depth, depth, depth, 1); + return psout; +# endif + +# ifdef DEBUG_SSR_REFINE_ITERATIONS + // Visualize the number of iterations used for each pixel in the refinement step + // This is the second for loop using binary search + // Blue = 0, Red = max, should move through purple + float iterationColor = float(iterationIndex) / float(maxIterations); + psout.Color = float4(iterationColor, 0, 1 - iterationColor, 1); + return psout; +# endif + +# ifdef DEBUG_SSR_REFINE_FIRST_HIT + // Visualize the position of the first hit for each pixel during refinement. + // This shows where the initial intersection was found in the binary search. + // Red intensity represents the depth of the hit: + // Dark red = hit close to the camera (small number of iterations) + // Bright red = hit far from the camera (large number of iterations) + // Black = no hit found within max iterations + float hitDepth = (float)iterationIndex / (float)maxIterations; + psout.Color = float4(hitDepth, 0, 0, 1); + return psout; +# endif + float3 color = ColorTex.Sample(ColorSampler, uvFinalDR).xyz; # ifdef VR From 9746c72b575d459f5ac2a7dbb0d9587f8cd90e5c Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Tue, 10 Sep 2024 00:51:17 -0700 Subject: [PATCH 20/48] fix: fix linear stepping to use maxIterations value --- package/Shaders/ISReflectionsRayTracing.hlsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/Shaders/ISReflectionsRayTracing.hlsl b/package/Shaders/ISReflectionsRayTracing.hlsl index 8f922cf74..86de89c45 100644 --- a/package/Shaders/ISReflectionsRayTracing.hlsl +++ b/package/Shaders/ISReflectionsRayTracing.hlsl @@ -127,7 +127,7 @@ PS_OUTPUT main(PS_INPUT input) const int maxIterations = 16; // Adjust based on performance/quality tradeoff for (; iterationIndex < maxIterations; iterationIndex++) { - float3 iterationUvDepthDR = uvDepthStartDR + (iterationIndex / 16.0) * deltaUvDepthDR; + float3 iterationUvDepthDR = uvDepthStartDR + (iterationIndex / (float)maxIterations) * deltaUvDepthDR; float3 iterationUvDepthSample = ConvertStereoRayMarchUV(iterationUvDepthDR, eyeIndex); float iterationDepth = DepthTex.SampleLevel(DepthSampler, iterationUvDepthSample.xy, 0).x; uvDepthPreResultDR = uvDepthResultDR; From 946b4521916e3bf3be5aed82ece638af3bfe7ffd Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Tue, 10 Sep 2024 00:51:42 -0700 Subject: [PATCH 21/48] fix(VR): set VR iteration defaults to avoid jaggies --- package/Shaders/ISReflectionsRayTracing.hlsl | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/package/Shaders/ISReflectionsRayTracing.hlsl b/package/Shaders/ISReflectionsRayTracing.hlsl index 86de89c45..3b82e990c 100644 --- a/package/Shaders/ISReflectionsRayTracing.hlsl +++ b/package/Shaders/ISReflectionsRayTracing.hlsl @@ -124,7 +124,13 @@ PS_OUTPUT main(PS_INPUT input) float3 uvDepthResultDR = float3(uvDepthStartDR.xy, depthStart); int iterationIndex = 1; - const int maxIterations = 16; // Adjust based on performance/quality tradeoff + const int maxIterations = +# ifndef VR + 16 +# else + 48 +# endif + ; // Adjust based on performance/quality tradeoff for (; iterationIndex < maxIterations; iterationIndex++) { float3 iterationUvDepthDR = uvDepthStartDR + (iterationIndex / (float)maxIterations) * deltaUvDepthDR; From c8bf25cac86b368353ae5fe62ad993497f3a49f7 Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Tue, 10 Sep 2024 01:07:17 -0700 Subject: [PATCH 22/48] refactor: move VR init into dynamiccubemaps --- src/Features/DynamicCubemaps.cpp | 18 ++++++++++++++++++ src/Features/DynamicCubemaps.h | 1 + src/XSEPlugin.cpp | 14 -------------- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/Features/DynamicCubemaps.cpp b/src/Features/DynamicCubemaps.cpp index 9f09d339d..a90892a29 100644 --- a/src/Features/DynamicCubemaps.cpp +++ b/src/Features/DynamicCubemaps.cpp @@ -115,6 +115,24 @@ void DynamicCubemaps::DataLoaded() MenuOpenCloseEventHandler::Register(); } +void DynamicCubemaps::PostPostLoad() +{ + if (REL::Module::IsVR()) { + std::map earlyhiddenVRCubeMapSettings{ + { "bScreenSpaceReflectionEnabled:Display", 0x1ED5BC0 }, + }; + for (const auto& settingPair : earlyhiddenVRCubeMapSettings) { + const auto& settingName = settingPair.first; + const auto address = REL::Offset{ settingPair.second }.address(); + bool* setting = reinterpret_cast(address); + if (!*setting) { + logger::info("[PostPostLoad] Changing {} from {} to {} to support Dynamic Cubemaps", settingName, *setting, true); + *setting = true; + } + } + } +} + RE::BSEventNotifyControl MenuOpenCloseEventHandler::ProcessEvent(const RE::MenuOpenCloseEvent* a_event, RE::BSTEventSource*) { // When entering a new cell, reset the capture diff --git a/src/Features/DynamicCubemaps.h b/src/Features/DynamicCubemaps.h index 78db8d1e5..7ee7da13a 100644 --- a/src/Features/DynamicCubemaps.h +++ b/src/Features/DynamicCubemaps.h @@ -98,6 +98,7 @@ struct DynamicCubemaps : Feature virtual void DrawSettings() override; virtual void DataLoaded() override; + virtual void PostPostLoad() override; std::map iniVRCubeMapSettings{ { "bAutoWaterSilhouetteReflections:Water", { "Auto Water Silhouette Reflections", "Automatically reflects silhouettes on water surfaces.", 0, true, false, true } }, diff --git a/src/XSEPlugin.cpp b/src/XSEPlugin.cpp index 7c55e0902..1889d7485 100644 --- a/src/XSEPlugin.cpp +++ b/src/XSEPlugin.cpp @@ -95,20 +95,6 @@ void MessageHandler(SKSE::MessagingInterface::Message* message) } } } - if (REL::Module::IsVR()) { - std::map hiddenVRCubeMapSettings{ - { "bScreenSpaceReflectionEnabled:Display", 0x1ED5BC0 }, - }; - for (const auto& settingPair : hiddenVRCubeMapSettings) { - const auto& settingName = settingPair.first; - const auto address = REL::Offset{ settingPair.second }.address(); - bool* setting = reinterpret_cast(address); - if (!*setting) { - logger::info("[PostPostLoad] Changing {} from {} to {} to support Dynamic Cubemaps", settingName, *setting, true); - *setting = true; - } - } - } break; } case SKSE::MessagingInterface::kDataLoaded: From 3b6011421b9c58ed50878c2825e01008067bcdb9 Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Tue, 10 Sep 2024 01:10:01 -0700 Subject: [PATCH 23/48] fix: respect RawSSRReflectionTex mask RawSSRReflectionTex uses 0 as a mask. The prior behavior would blend reflection data from this state which resulted in black areas in the reflection. The code now defaults to the cubemap reflection for masked data. --- package/Shaders/Water.hlsl | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/package/Shaders/Water.hlsl b/package/Shaders/Water.hlsl index 9af5985dc..77a587403 100644 --- a/package/Shaders/Water.hlsl +++ b/package/Shaders/Water.hlsl @@ -602,10 +602,16 @@ float3 GetWaterSpecularColor(PS_INPUT input, float3 normal, float3 viewDirection float2 ssrReflectionUv = GetDynamicResolutionAdjustedScreenPosition((DynamicResolutionParams2.xy * input.HPosition.xy) * SSRParams.zw + SSRParams2.x * normal.xy); float4 ssrReflectionColor1 = SSRReflectionTex.Sample(SSRReflectionSampler, ssrReflectionUv); float4 ssrReflectionColor2 = RawSSRReflectionTex.Sample(RawSSRReflectionSampler, ssrReflectionUv); - float4 ssrReflectionColor = lerp(ssrReflectionColor2, ssrReflectionColor1, SSRParams.y); - - finalSsrReflectionColor = max(0, ssrReflectionColor.xyz); - ssrFraction = saturate(ssrReflectionColor.w * SSRParams.x * distanceFactor); + float epsilon = 0.001; // Define a small tolerance value + if (dot(ssrReflectionColor2.xyz, ssrReflectionColor2.xyz) < epsilon * epsilon) { // check for bad ssr values and use only cubemap + finalSsrReflectionColor = reflectionColor.xyz; + ssrFraction = 0.f; + } else { + float4 ssrReflectionColor = lerp(ssrReflectionColor2, ssrReflectionColor1, SSRParams.y); + + finalSsrReflectionColor = max(0, ssrReflectionColor.xyz); + ssrFraction = saturate(ssrReflectionColor.w * SSRParams.x * distanceFactor); + } } # endif From 5a6afd5bbfea7ef0b93734601a7274400550ee9f Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Tue, 10 Sep 2024 01:15:14 -0700 Subject: [PATCH 24/48] revert: "chore: remove color lerping with reflectioncolor" This reverts commit 80acf0f30cf5549a440d2482e67225b26662a3d6. --- package/Shaders/Water.hlsl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package/Shaders/Water.hlsl b/package/Shaders/Water.hlsl index 77a587403..e26be2c7a 100644 --- a/package/Shaders/Water.hlsl +++ b/package/Shaders/Water.hlsl @@ -615,9 +615,9 @@ float3 GetWaterSpecularColor(PS_INPUT input, float3 normal, float3 viewDirection } # endif - float3 finalReflectionColor = lerp(finalSsrReflectionColor, finalSsrReflectionColor, ssrFraction); + float3 finalReflectionColor = lerp(reflectionColor, finalSsrReflectionColor, ssrFraction); - return lerp(finalReflectionColor.xyz, finalReflectionColor, VarAmounts.y); + return lerp(ReflectionColor.xyz, finalReflectionColor, VarAmounts.y); } return ReflectionColor.xyz * VarAmounts.y; } From fc97084491441f8f130a2790ba69b737cdc238c7 Mon Sep 17 00:00:00 2001 From: alandtse Date: Tue, 10 Sep 2024 08:11:59 +0000 Subject: [PATCH 25/48] =?UTF-8?q?style:=20=F0=9F=8E=A8=20apply=20clang-for?= =?UTF-8?q?mat=20changes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package/Shaders/Water.hlsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/Shaders/Water.hlsl b/package/Shaders/Water.hlsl index e26be2c7a..4923940f3 100644 --- a/package/Shaders/Water.hlsl +++ b/package/Shaders/Water.hlsl @@ -602,7 +602,7 @@ float3 GetWaterSpecularColor(PS_INPUT input, float3 normal, float3 viewDirection float2 ssrReflectionUv = GetDynamicResolutionAdjustedScreenPosition((DynamicResolutionParams2.xy * input.HPosition.xy) * SSRParams.zw + SSRParams2.x * normal.xy); float4 ssrReflectionColor1 = SSRReflectionTex.Sample(SSRReflectionSampler, ssrReflectionUv); float4 ssrReflectionColor2 = RawSSRReflectionTex.Sample(RawSSRReflectionSampler, ssrReflectionUv); - float epsilon = 0.001; // Define a small tolerance value + float epsilon = 0.001; // Define a small tolerance value if (dot(ssrReflectionColor2.xyz, ssrReflectionColor2.xyz) < epsilon * epsilon) { // check for bad ssr values and use only cubemap finalSsrReflectionColor = reflectionColor.xyz; ssrFraction = 0.f; From 582adf8222d5ffefa2cca50a7f396dd1b8b9dc85 Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Tue, 10 Sep 2024 01:24:46 -0700 Subject: [PATCH 26/48] chore: discard changes to src/XSEPlugin.cpp --- src/XSEPlugin.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/XSEPlugin.cpp b/src/XSEPlugin.cpp index 1889d7485..f9027fcdc 100644 --- a/src/XSEPlugin.cpp +++ b/src/XSEPlugin.cpp @@ -95,6 +95,7 @@ void MessageHandler(SKSE::MessagingInterface::Message* message) } } } + break; } case SKSE::MessagingInterface::kDataLoaded: From 6b21904b53943ee060e262f52053be1d7ab89341 Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Tue, 10 Sep 2024 23:20:29 -0700 Subject: [PATCH 27/48] fix(VR): fix dynamic resolution and upscaler --- package/Shaders/Common/FrameBuffer.hlsli | 10 ++++++++ package/Shaders/ISReflectionsRayTracing.hlsl | 25 ++++++++++++++++++-- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/package/Shaders/Common/FrameBuffer.hlsli b/package/Shaders/Common/FrameBuffer.hlsli index 38d5b4560..8a5d7c2d2 100644 --- a/package/Shaders/Common/FrameBuffer.hlsli +++ b/package/Shaders/Common/FrameBuffer.hlsli @@ -65,11 +65,21 @@ float2 GetDynamicResolutionAdjustedScreenPosition(float2 screenPosition, uint st return clamp(screenPositionDR, minValue, maxValue); } +float3 GetDynamicResolutionAdjustedScreenPosition(float3 screenPositionDR, uint stereo = 1) +{ + return float3(GetDynamicResolutionAdjustedScreenPosition(screenPositionDR.xy, stereo), screenPositionDR.z); +} + float2 GetDynamicResolutionUnadjustedScreenPosition(float2 screenPositionDR) { return screenPositionDR * DynamicResolutionParams2.xy; } +float3 GetDynamicResolutionUnadjustedScreenPosition(float3 screenPositionDR) +{ + return float3(GetDynamicResolutionUnadjustedScreenPosition(screenPositionDR.xy), screenPositionDR.z); +} + float2 GetPreviousDynamicResolutionAdjustedScreenPosition(float2 screenPosition) { float2 screenPositionDR = DynamicResolutionParams1.zw * screenPosition; diff --git a/package/Shaders/ISReflectionsRayTracing.hlsl b/package/Shaders/ISReflectionsRayTracing.hlsl index 3b82e990c..cd4850ebf 100644 --- a/package/Shaders/ISReflectionsRayTracing.hlsl +++ b/package/Shaders/ISReflectionsRayTracing.hlsl @@ -134,7 +134,17 @@ PS_OUTPUT main(PS_INPUT input) for (; iterationIndex < maxIterations; iterationIndex++) { float3 iterationUvDepthDR = uvDepthStartDR + (iterationIndex / (float)maxIterations) * deltaUvDepthDR; - float3 iterationUvDepthSample = ConvertStereoRayMarchUV(iterationUvDepthDR, eyeIndex); + float3 iterationUvDepthSample = +# ifdef VR + // Apply dynamic resolution adjustments and stereo UV conversions + GetDynamicResolutionAdjustedScreenPosition( + ConvertStereoRayMarchUV( + GetDynamicResolutionUnadjustedScreenPosition(iterationUvDepthDR), + eyeIndex)); +# else + // No VR adjustments, just use the raw UV coordinates + iterationUvDepthDR; +# endif float iterationDepth = DepthTex.SampleLevel(DepthSampler, iterationUvDepthSample.xy, 0).x; uvDepthPreResultDR = uvDepthResultDR; uvDepthResultDR = iterationUvDepthDR; @@ -191,7 +201,18 @@ PS_OUTPUT main(PS_INPUT input) uvDepthFinalDR = uvDepthPreResultDR; for (; iterationIndex < maxIterations; iterationIndex++) { uvDepthFinalDR = (uvDepthPreResultDR + uvDepthResultDR) * 0.5; - float3 uvDepthFinalSample = ConvertStereoRayMarchUV(uvDepthFinalDR, eyeIndex); + float3 uvDepthFinalSample = +# ifdef VR + // Apply dynamic resolution adjustments and stereo UV conversions + GetDynamicResolutionAdjustedScreenPosition( + ConvertStereoRayMarchUV( + GetDynamicResolutionUnadjustedScreenPosition(uvDepthFinalDR), + eyeIndex)); + +# else + // No VR adjustments, just use the raw UV coordinates + uvDepthFinalDR; +# endif float subIterationDepth = DepthTex.SampleLevel(DepthSampler, uvDepthFinalSample.xy, 0).x; if (subIterationDepth < uvDepthFinalDR.z && uvDepthFinalDR.z < subIterationDepth + SSRParams.y) { break; From 158d0160ecf43261e1d9f3db69804183f1e45dc8 Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Tue, 10 Sep 2024 23:20:58 -0700 Subject: [PATCH 28/48] perf(VR): exit early if in VR mask --- package/Shaders/ISReflectionsRayTracing.hlsl | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/package/Shaders/ISReflectionsRayTracing.hlsl b/package/Shaders/ISReflectionsRayTracing.hlsl index cd4850ebf..aaadb9bfa 100644 --- a/package/Shaders/ISReflectionsRayTracing.hlsl +++ b/package/Shaders/ISReflectionsRayTracing.hlsl @@ -75,6 +75,13 @@ PS_OUTPUT main(PS_INPUT input) float depthStart = DepthTex.SampleLevel(DepthSampler, uvStartDR, 0).x; +# ifdef VR + [branch] if (depthStart == 0.0f) // in VR mask + { + return psout; + } +# endif + bool isDefaultNormal = srcNormal.z >= 1e-5; float ssrPower = max(srcNormal.z >= 1e-5, srcNormal.w); bool isSsrDisabled = ssrPower < 1e-5; From dd958d3982b7982ef37262729aa84ae756a185cc Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Wed, 11 Sep 2024 01:46:36 -0700 Subject: [PATCH 29/48] perf: stop raymarch if outside of screen --- package/Shaders/Common/FrameBuffer.hlsli | 39 +++++++++++++++++--- package/Shaders/Common/VR.hlsli | 32 +++++++++------- package/Shaders/ISReflectionsRayTracing.hlsl | 20 ++++++++-- 3 files changed, 70 insertions(+), 21 deletions(-) diff --git a/package/Shaders/Common/FrameBuffer.hlsli b/package/Shaders/Common/FrameBuffer.hlsli index 8a5d7c2d2..f15dd2026 100644 --- a/package/Shaders/Common/FrameBuffer.hlsli +++ b/package/Shaders/Common/FrameBuffer.hlsli @@ -113,15 +113,40 @@ float2 ViewToUV(float3 x, bool is_position = true, uint a_eyeIndex = 0) return (uv.xy / uv.w) * float2(0.5f, -0.5f) + 0.5f; } -bool isOutsideFrame(float2 uv) +/** + * @brief Checks if the UV coordinates are outside the frame, considering dynamic resolution if specified. + * + * This function is used to determine whether the provided UV coordinates lie outside the valid range of [0,1]. + * If dynamic resolution is enabled, it adjusts the range according to dynamic resolution parameters. + * + * @param[in] uv The UV coordinates to check. + * @param[in] dynamicres Optional flag indicating whether dynamic resolution is applied. Default is false. + * @return True if the UV coordinates are outside the frame, false otherwise. + */ +bool isOutsideFrame(float2 uv, bool dynamicres = false) { - bool result = uv.x < 0 || uv.x > 1 || uv.y < 0 || uv.y > 1; - return result; + float2 max = dynamicres ? DynamicResolutionParams1.xy : float2(1, 1); + return any(uv < float2(0, 0) || uv > max.xy); } -// Convert from mono UV of one eye to the corresponding mono UV of the other eye -float3 ConvertMonoUVToOtherEye(float3 monoUV, uint eyeIndex) +/** + * @brief Converts mono UV coordinates from one eye to the corresponding mono UV coordinates of the other eye. + * + * This function is used to transition UV coordinates from one eye's perspective to the other eye in a stereo rendering setup. + * It works by converting the mono UV to clip space, transforming it into view space, and then reprojecting it into the other eye's + * clip space before converting back to UV coordinates. It also supports dynamic resolution. + * + * @param[in] monoUV The UV coordinates and depth value (Z component) for the current eye, in the range [0,1]. + * @param[in] eyeIndex Index of the current eye (0 or 1). + * @param[in] dynamicres Optional flag indicating whether dynamic resolution is applied. Default is false. + * @return UV coordinates adjusted to the other eye, with depth. + */ +float3 ConvertMonoUVToOtherEye(float3 monoUV, uint eyeIndex, bool dynamicres = false) { + // Convert from dynamic res to true UV space + if (dynamicres) + monoUV.xy *= DynamicResolutionParams2.xy; + // Step 1: Convert UV to Clip Space float4 clipPos = float4(monoUV.xy * float2(2, -2) - float2(1, -1), monoUV.z, 1); @@ -136,5 +161,9 @@ float3 ConvertMonoUVToOtherEye(float3 monoUV, uint eyeIndex) // Step 4: Convert Clip Space to UV float3 monoUVOtherEye = float3((clipPosOtherEye.xy * 0.5f) + 0.5f, clipPosOtherEye.z); + // Convert back to dynamic res space if necessary + if (dynamicres) + monoUVOtherEye.xy *= DynamicResolutionParams1.xy; + return monoUVOtherEye; } \ No newline at end of file diff --git a/package/Shaders/Common/VR.hlsli b/package/Shaders/Common/VR.hlsli index 5f54a6ad7..637bfd0e7 100644 --- a/package/Shaders/Common/VR.hlsli +++ b/package/Shaders/Common/VR.hlsli @@ -145,33 +145,39 @@ uint GetEyeIndexFromTexCoord(float2 texCoord) /** * @brief Adjusts UV coordinates for VR stereo rendering when transitioning between eyes or handling boundary conditions. * - * This is intended to be used in a raymarch to check the next uv coordinate. - * This function checks if the current UV coordinates are outside the frame. If so, it transitions the UV coordinates to the other eye - * and adjusts them if they are within the frame of the other eye. If the UV coordinates are outside the frame of both eyes, it will return - * the adjusted UV coordinates for the current eye. + * This function is used in raymarching to check the next UV coordinate. It checks if the current UV coordinates are outside + * the frame. If so, it transitions the UV coordinates to the other eye and adjusts them if they are within the frame of the other eye. + * If the UV coordinates are outside the frame of both eyes, it returns the adjusted UV coordinates for the current eye. * - * The function ensures that the UV coordinates are correctly adjusted for stereo rendering, taking into account the boundary conditions + * The function ensures that the UV coordinates are correctly adjusted for stereo rendering, taking into account boundary conditions * and preserving accurate reflections. * Based on concepts from https://cuteloong.github.io/publications/scssr24/ * Wu, X., Xu, Y., & Wang, L. (2024). Stereo-consistent Screen Space Reflection. Computer Graphics Forum, 43(4). * - * We do not have a backface depth so we may be ray marching even though - * the ray is in an object. - * @param[in] monoUV Current UV coordinates with depth information. + * We do not have a backface depth so we may be ray marching even though the ray is in an object. + + * @param[in] monoUV Current UV coordinates with depth information, [0-1]. Must not be dynamic resolution adjusted. * @param[in] eyeIndex Index of the current eye (0 or 1). - * - * @return Adjusted UV coordinates for stereo rendering. + * @param[out] fromOtherEye Boolean indicating if the result UV coordinates are from the other eye. + * + * @return Adjusted UV coordinates for stereo rendering, [0-1]. Must be dynamic resolution adjusted later. */ -float3 ConvertStereoRayMarchUV(float3 monoUV, uint eyeIndex) +float3 ConvertStereoRayMarchUV(float3 monoUV, uint eyeIndex, out bool fromOtherEye) { + fromOtherEye = false; + // Convert to stereo UV coordinates float3 resultUV = ConvertToStereoUV(monoUV, eyeIndex); #ifdef VR - if (isOutsideFrame(resultUV.xy)) { + // Check if the UV coordinates are outside the frame + if (isOutsideFrame(resultUV.xy, false)) { // Transition to the other eye float3 otherEyeUV = ConvertMonoUVToOtherEye(resultUV, 1 - eyeIndex); - if (!isOutsideFrame(otherEyeUV.xy)) { + + // Check if the other eye's UV coordinates are within the frame + if (!isOutsideFrame(otherEyeUV.xy, false)) { resultUV = ConvertToStereoUV(otherEyeUV, 1 - eyeIndex); + fromOtherEye = true; // Indicate that the result is from the other eye } } #endif diff --git a/package/Shaders/ISReflectionsRayTracing.hlsl b/package/Shaders/ISReflectionsRayTracing.hlsl index aaadb9bfa..768960a6d 100644 --- a/package/Shaders/ISReflectionsRayTracing.hlsl +++ b/package/Shaders/ISReflectionsRayTracing.hlsl @@ -131,6 +131,9 @@ PS_OUTPUT main(PS_INPUT input) float3 uvDepthResultDR = float3(uvDepthStartDR.xy, depthStart); int iterationIndex = 1; +# ifdef VR + bool fromOtherEye = false; +# endif const int maxIterations = # ifndef VR 16 @@ -147,7 +150,8 @@ PS_OUTPUT main(PS_INPUT input) GetDynamicResolutionAdjustedScreenPosition( ConvertStereoRayMarchUV( GetDynamicResolutionUnadjustedScreenPosition(iterationUvDepthDR), - eyeIndex)); + eyeIndex, + fromOtherEye)); # else // No VR adjustments, just use the raw UV coordinates iterationUvDepthDR; @@ -155,6 +159,16 @@ PS_OUTPUT main(PS_INPUT input) float iterationDepth = DepthTex.SampleLevel(DepthSampler, iterationUvDepthSample.xy, 0).x; uvDepthPreResultDR = uvDepthResultDR; uvDepthResultDR = iterationUvDepthDR; + if (isOutsideFrame(iterationUvDepthDR) +# ifdef VR + // In VR, it could be coming from the other eye + && !fromOtherEye || + (fromOtherEye && isOutsideFrame(ConvertMonoUVToOtherEye(iterationUvDepthDR, 1 - eyeIndex, true), true)) +# endif + ) { + // out of screen, no ray ssr possible + return psout; + } if (iterationDepth < iterationUvDepthDR.z) { // ray intersection detected break; } @@ -214,8 +228,8 @@ PS_OUTPUT main(PS_INPUT input) GetDynamicResolutionAdjustedScreenPosition( ConvertStereoRayMarchUV( GetDynamicResolutionUnadjustedScreenPosition(uvDepthFinalDR), - eyeIndex)); - + eyeIndex, + fromOtherEye)); # else // No VR adjustments, just use the raw UV coordinates uvDepthFinalDR; From 6786138061b547dfa7696e5754d6869374c0014a Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Wed, 11 Sep 2024 01:46:55 -0700 Subject: [PATCH 30/48] chore: rename variables to indicate dynamic res uv --- package/Shaders/ISReflectionsRayTracing.hlsl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package/Shaders/ISReflectionsRayTracing.hlsl b/package/Shaders/ISReflectionsRayTracing.hlsl index 768960a6d..eb907e72a 100644 --- a/package/Shaders/ISReflectionsRayTracing.hlsl +++ b/package/Shaders/ISReflectionsRayTracing.hlsl @@ -144,7 +144,7 @@ PS_OUTPUT main(PS_INPUT input) for (; iterationIndex < maxIterations; iterationIndex++) { float3 iterationUvDepthDR = uvDepthStartDR + (iterationIndex / (float)maxIterations) * deltaUvDepthDR; - float3 iterationUvDepthSample = + float3 iterationUvDepthSampleDR = # ifdef VR // Apply dynamic resolution adjustments and stereo UV conversions GetDynamicResolutionAdjustedScreenPosition( @@ -156,7 +156,7 @@ PS_OUTPUT main(PS_INPUT input) // No VR adjustments, just use the raw UV coordinates iterationUvDepthDR; # endif - float iterationDepth = DepthTex.SampleLevel(DepthSampler, iterationUvDepthSample.xy, 0).x; + float iterationDepth = DepthTex.SampleLevel(DepthSampler, iterationUvDepthSampleDR.xy, 0).x; uvDepthPreResultDR = uvDepthResultDR; uvDepthResultDR = iterationUvDepthDR; if (isOutsideFrame(iterationUvDepthDR) @@ -222,7 +222,7 @@ PS_OUTPUT main(PS_INPUT input) uvDepthFinalDR = uvDepthPreResultDR; for (; iterationIndex < maxIterations; iterationIndex++) { uvDepthFinalDR = (uvDepthPreResultDR + uvDepthResultDR) * 0.5; - float3 uvDepthFinalSample = + float3 uvDepthFinalSampleDR = # ifdef VR // Apply dynamic resolution adjustments and stereo UV conversions GetDynamicResolutionAdjustedScreenPosition( @@ -234,7 +234,7 @@ PS_OUTPUT main(PS_INPUT input) // No VR adjustments, just use the raw UV coordinates uvDepthFinalDR; # endif - float subIterationDepth = DepthTex.SampleLevel(DepthSampler, uvDepthFinalSample.xy, 0).x; + float subIterationDepth = DepthTex.SampleLevel(DepthSampler, uvDepthFinalSampleDR.xy, 0).x; if (subIterationDepth < uvDepthFinalDR.z && uvDepthFinalDR.z < subIterationDepth + SSRParams.y) { break; } From 6f88e109d6ab7e3fdee80369d801a8df091c5ba3 Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Thu, 12 Sep 2024 23:35:31 -0700 Subject: [PATCH 31/48] refactor: simplify ssr code --- package/Shaders/Water.hlsl | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/package/Shaders/Water.hlsl b/package/Shaders/Water.hlsl index 4923940f3..e786e28dc 100644 --- a/package/Shaders/Water.hlsl +++ b/package/Shaders/Water.hlsl @@ -379,6 +379,22 @@ cbuffer PerGeometry : register(b2) # endif //VR } +/** + * @brief Checks if the SSR reflection mask is invalid by testing if the reflection color is close to zero. + * + * This function evaluates whether the screen-space reflection (SSR) mask represents an invalid reflection by + * checking if the reflection color is essentially black (close to zero). It uses a small epsilon value to + * allow for floating point imprecision. + * + * @param[in] ssrColor The SSR reflection color sampled from a texture. + * @param[in] epsilon Small tolerance value used to determine if the color is close to zero. + * @return True if the SSR mask is considered invalid (color is close to zero), otherwise false. + */ +bool IsValidSSRMask(float4 ssrColor, float epsilon = 0.001) +{ + return !dot(ssrColor.xyz, ssrColor.xyz) < epsilon * epsilon; +} + # ifdef VR float GetStencil(float2 uv) { @@ -599,18 +615,20 @@ float3 GetWaterSpecularColor(PS_INPUT input, float3 normal, float3 viewDirection # if !defined(LOD) && NUM_SPECULAR_LIGHTS == 0 if (PixelShaderDescriptor & _Cubemap) { - float2 ssrReflectionUv = GetDynamicResolutionAdjustedScreenPosition((DynamicResolutionParams2.xy * input.HPosition.xy) * SSRParams.zw + SSRParams2.x * normal.xy); - float4 ssrReflectionColor1 = SSRReflectionTex.Sample(SSRReflectionSampler, ssrReflectionUv); - float4 ssrReflectionColor2 = RawSSRReflectionTex.Sample(RawSSRReflectionSampler, ssrReflectionUv); - float epsilon = 0.001; // Define a small tolerance value - if (dot(ssrReflectionColor2.xyz, ssrReflectionColor2.xyz) < epsilon * epsilon) { // check for bad ssr values and use only cubemap - finalSsrReflectionColor = reflectionColor.xyz; - ssrFraction = 0.f; - } else { - float4 ssrReflectionColor = lerp(ssrReflectionColor2, ssrReflectionColor1, SSRParams.y); + float2 ssrReflectionUv = (DynamicResolutionParams2.xy * input.HPosition.xy) * SSRParams.zw + SSRParams2.x * normal.xy; + float2 ssrReflectionUvDR = GetDynamicResolutionAdjustedScreenPosition(ssrReflectionUv); + float4 ssrReflectionColorBlurred = SSRReflectionTex.Sample(SSRReflectionSampler, ssrReflectionUvDR); + float4 ssrReflectionColorRaw = RawSSRReflectionTex.Sample(RawSSRReflectionSampler, ssrReflectionUvDR); + bool validSSRMask = IsValidSSRMask(ssrReflectionColorRaw); + if (validSSRMask) { + float4 ssrReflectionColor = lerp(ssrReflectionColorRaw, ssrReflectionColorBlurred, SSRParams.y); finalSsrReflectionColor = max(0, ssrReflectionColor.xyz); ssrFraction = saturate(ssrReflectionColor.w * SSRParams.x * distanceFactor); + } else { + // Use reflectionColor info only + finalSsrReflectionColor = reflectionColor.xyz; + ssrFraction = 0.f; } } # endif From 531f22137a492a24461988c768a8f66c2fb824ca Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Thu, 12 Sep 2024 23:36:57 -0700 Subject: [PATCH 32/48] feat: add fog color to reflection and lean to blur --- package/Shaders/Water.hlsl | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/package/Shaders/Water.hlsl b/package/Shaders/Water.hlsl index e786e28dc..afbc84c48 100644 --- a/package/Shaders/Water.hlsl +++ b/package/Shaders/Water.hlsl @@ -619,17 +619,25 @@ float3 GetWaterSpecularColor(PS_INPUT input, float3 normal, float3 viewDirection float2 ssrReflectionUvDR = GetDynamicResolutionAdjustedScreenPosition(ssrReflectionUv); float4 ssrReflectionColorBlurred = SSRReflectionTex.Sample(SSRReflectionSampler, ssrReflectionUvDR); float4 ssrReflectionColorRaw = RawSSRReflectionTex.Sample(RawSSRReflectionSampler, ssrReflectionUvDR); + + // calculate blur on reflection + float depth = DepthTex.Load(int3(ssrReflectionUvDR * BufferDim.xy, 0)).g; + float fogDensity = 1 - pow(saturate((-depth * FogParam.z + FogParam.z) / FogParam.w), FogNearColor.w); + float3 fogColor = lerp(FogNearColor.xyz, FogFarColor.xyz, fogDensity); + bool validSSRMask = IsValidSSRMask(ssrReflectionColorRaw); if (validSSRMask) { - float4 ssrReflectionColor = lerp(ssrReflectionColorRaw, ssrReflectionColorBlurred, SSRParams.y); + float effectiveBlurFactor = saturate(SSRParams.y * (1.0 + fogDensity)); + float4 ssrReflectionColor = lerp(ssrReflectionColorRaw, ssrReflectionColorBlurred, effectiveBlurFactor); finalSsrReflectionColor = max(0, ssrReflectionColor.xyz); ssrFraction = saturate(ssrReflectionColor.w * SSRParams.x * distanceFactor); } else { // Use reflectionColor info only finalSsrReflectionColor = reflectionColor.xyz; - ssrFraction = 0.f; + ssrFraction = 1.f; } + finalSsrReflectionColor = lerp(finalSsrReflectionColor, fogColor, fogDensity); } # endif From a788bedb54369eca91181de134e0955fb4f488e3 Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Sat, 14 Sep 2024 01:34:48 -0700 Subject: [PATCH 33/48] fix: fix compiler warnings --- package/Shaders/ISReflectionsRayTracing.hlsl | 6 +++--- package/Shaders/ISWaterBlend.hlsl | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package/Shaders/ISReflectionsRayTracing.hlsl b/package/Shaders/ISReflectionsRayTracing.hlsl index eb907e72a..615994725 100644 --- a/package/Shaders/ISReflectionsRayTracing.hlsl +++ b/package/Shaders/ISReflectionsRayTracing.hlsl @@ -64,7 +64,7 @@ PS_OUTPUT main(PS_INPUT input) psout.Color = 0; # ifdef VR - uint eyeIndex = input.TexCoord >= 0.5; + uint eyeIndex = input.TexCoord.x >= 0.5; # else uint eyeIndex = 0; # endif @@ -159,11 +159,11 @@ PS_OUTPUT main(PS_INPUT input) float iterationDepth = DepthTex.SampleLevel(DepthSampler, iterationUvDepthSampleDR.xy, 0).x; uvDepthPreResultDR = uvDepthResultDR; uvDepthResultDR = iterationUvDepthDR; - if (isOutsideFrame(iterationUvDepthDR) + if (isOutsideFrame(iterationUvDepthDR.xy) # ifdef VR // In VR, it could be coming from the other eye && !fromOtherEye || - (fromOtherEye && isOutsideFrame(ConvertMonoUVToOtherEye(iterationUvDepthDR, 1 - eyeIndex, true), true)) + (fromOtherEye && isOutsideFrame(ConvertMonoUVToOtherEye(iterationUvDepthDR, 1 - eyeIndex, true).xy, true)) # endif ) { // out of screen, no ray ssr possible diff --git a/package/Shaders/ISWaterBlend.hlsl b/package/Shaders/ISWaterBlend.hlsl index 9f32b2d90..6640738ea 100644 --- a/package/Shaders/ISWaterBlend.hlsl +++ b/package/Shaders/ISWaterBlend.hlsl @@ -33,7 +33,7 @@ PS_OUTPUT main(PS_INPUT input) { PS_OUTPUT psout; # ifdef VR - uint eyeIndex = input.TexCoord >= 0.5; + uint eyeIndex = input.TexCoord.x >= 0.5; # else uint eyeIndex = 0; # endif From 1c7399d06b2e5ec3c7ba544cf24a96b40e1b20c8 Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Sun, 15 Sep 2024 20:37:50 -0700 Subject: [PATCH 34/48] feat: add hlsl-specific clear Allow clearing of all shaders linked to a specific hlsl file. This is useful for DEFINE option passing for features. --- src/ShaderCache.cpp | 4 ++-- src/ShaderCache.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ShaderCache.cpp b/src/ShaderCache.cpp index 0793f14a8..d0910196d 100644 --- a/src/ShaderCache.cpp +++ b/src/ShaderCache.cpp @@ -1439,7 +1439,7 @@ namespace SIE logger::debug("Saved shader to {}", Util::WStringToString(diskPath)); } } - cache.AddCompletedShader(shaderClass, shader, descriptor, shaderBlob); + cache.AddCompletedShader(shaderClass, shader, descriptor, shaderBlob, pathString); return shaderBlob; } @@ -2014,7 +2014,7 @@ namespace SIE compilationSet.Clear(); } - bool ShaderCache::AddCompletedShader(ShaderClass shaderClass, const RE::BSShader& shader, uint32_t descriptor, ID3DBlob* a_blob) + bool ShaderCache::AddCompletedShader(ShaderClass shaderClass, const RE::BSShader& shader, uint32_t descriptor, ID3DBlob* a_blob, const std::string& a_path) { auto key = SIE::SShaderCache::GetShaderString(shaderClass, shader, descriptor, true); auto status = a_blob ? ShaderCompilationTask::Status::Completed : ShaderCompilationTask::Status::Failed; diff --git a/src/ShaderCache.h b/src/ShaderCache.h index 0e1d633da..78b4942c8 100644 --- a/src/ShaderCache.h +++ b/src/ShaderCache.h @@ -349,7 +349,7 @@ namespace SIE */ bool Clear(const std::string& a_path); - bool AddCompletedShader(ShaderClass shaderClass, const RE::BSShader& shader, uint32_t descriptor, ID3DBlob* a_blob); + bool AddCompletedShader(ShaderClass shaderClass, const RE::BSShader& shader, uint32_t descriptor, ID3DBlob* a_blob, const std::string& a_path = ""); ID3DBlob* GetCompletedShader(const std::string& a_key); ID3DBlob* GetCompletedShader(const SIE::ShaderCompilationTask& a_task); ID3DBlob* GetCompletedShader(ShaderClass shaderClass, const RE::BSShader& shader, uint32_t descriptor); From 00f973941d6bcec03a1af16fd08c069b7b528c96 Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Sun, 15 Sep 2024 20:46:55 -0700 Subject: [PATCH 35/48] feat: enable settings for SSR --- package/Shaders/ISReflectionsRayTracing.hlsl | 10 ++- src/Features/DynamicCubemaps.cpp | 79 +++++++++++++++++++- src/Features/DynamicCubemaps.h | 20 ++++- src/ShaderCache.cpp | 2 + 4 files changed, 104 insertions(+), 7 deletions(-) diff --git a/package/Shaders/ISReflectionsRayTracing.hlsl b/package/Shaders/ISReflectionsRayTracing.hlsl index 615994725..18626e6f4 100644 --- a/package/Shaders/ISReflectionsRayTracing.hlsl +++ b/package/Shaders/ISReflectionsRayTracing.hlsl @@ -63,6 +63,10 @@ PS_OUTPUT main(PS_INPUT input) PS_OUTPUT psout; psout.Color = 0; +# ifndef ENABLESSR + // Disable SSR raymarch + return psout; +# endif # ifdef VR uint eyeIndex = input.TexCoord.x >= 0.5; # else @@ -135,10 +139,10 @@ PS_OUTPUT main(PS_INPUT input) bool fromOtherEye = false; # endif const int maxIterations = -# ifndef VR - 16 +# ifdef MAX_ITERATIONS + MAX_ITERATIONS # else - 48 + 16 # endif ; // Adjust based on performance/quality tradeoff diff --git a/src/Features/DynamicCubemaps.cpp b/src/Features/DynamicCubemaps.cpp index a90892a29..9e1169700 100644 --- a/src/Features/DynamicCubemaps.cpp +++ b/src/Features/DynamicCubemaps.cpp @@ -1,4 +1,5 @@ #include "DynamicCubemaps.h" +#include "ShaderCache.h" #include "State.h" #include "Util.h" @@ -8,13 +9,50 @@ constexpr auto MIPLEVELS = 8; +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT( + DynamicCubemaps::Settings, + EnabledSSR, + EnabledCreator, + MaxIterations); + +std::vector> DynamicCubemaps::GetShaderDefineOptions() +{ + std::vector> result; + maxIterationsString = std::to_string(settings.MaxIterations); + if (settings.EnabledSSR) { + result.push_back({ "ENABLESSR", "" }); + } + + result.push_back({ "MAX_ITERATIONS", maxIterationsString }); + + return result; +} + void DynamicCubemaps::DrawSettings() { if (ImGui::TreeNodeEx("Settings", ImGuiTreeNodeFlags_DefaultOpen)) { + if (ImGui::TreeNodeEx("Screen Space Reflections", ImGuiTreeNodeFlags_DefaultOpen)) { + recompileFlag |= ImGui::Checkbox("Enable Screen Space Reflections", reinterpret_cast(&settings.EnabledSSR)); + if (auto _tt = Util::HoverTooltipWrapper()) { + ImGui::Text("Enable Screen Space Reflections on Water"); + } + if (settings.EnabledSSR) { + recompileFlag |= ImGui::SliderInt("Max Iterations", reinterpret_cast(&settings.MaxIterations), 1, 128); + if (auto _tt = Util::HoverTooltipWrapper()) { + ImGui::Text( + "The maximum iterations to ray march. " + "Higher values result in better quality but lower performance."); + } + RenderImGuiSettingsTree(SSRSettings, "Skyrim SSR"); + ImGui::TreePop(); + } + ImGui::TreePop(); + } + if (ImGui::TreeNodeEx("Dynamic Cubemap Creator", ImGuiTreeNodeFlags_DefaultOpen)) { ImGui::Text("You must enable creator mode by adding the shader define CREATOR"); - ImGui::Checkbox("Enable Creator", reinterpret_cast(&settings.Enabled)); - if (settings.Enabled) { + ImGui::Checkbox("Enable Creator", reinterpret_cast(&settings.EnabledCreator)); + if (settings.EnabledCreator) { ImGui::ColorEdit3("Color", reinterpret_cast(&settings.CubemapColor)); ImGui::SliderFloat("Roughness", &settings.CubemapColor.w, 0.0f, 1.0f, "%.2f"); if (ImGui::Button("Export")) { @@ -105,6 +143,36 @@ void DynamicCubemaps::DrawSettings() } } +void DynamicCubemaps::LoadSettings(json& o_json) +{ + settings = o_json; + LoadGameSettings(SSRSettings); + if (REL::Module::IsVR()) { + LoadGameSettings(iniVRCubeMapSettings); + } + recompileFlag = true; +} + +void DynamicCubemaps::SaveSettings(json& o_json) +{ + o_json = settings; + SaveGameSettings(SSRSettings); + if (REL::Module::IsVR()) { + SaveGameSettings(iniVRCubeMapSettings); + } +} + +void DynamicCubemaps::RestoreDefaultSettings() +{ + settings = {}; + ResetGameSettingsToDefaults(SSRSettings); + if (REL::Module::IsVR()) { + ResetGameSettingsToDefaults(iniVRCubeMapSettings); + ResetGameSettingsToDefaults(hiddenVRCubeMapSettings); + } + recompileFlag = true; +} + void DynamicCubemaps::DataLoaded() { if (REL::Module::IsVR()) { @@ -367,6 +435,13 @@ void DynamicCubemaps::Irradiance(bool a_reflections) void DynamicCubemaps::UpdateCubemap() { TracyD3D11Zone(State::GetSingleton()->tracyCtx, "Cubemap Update"); + if (recompileFlag) { + auto& shaderCache = SIE::ShaderCache::Instance(); + if (!shaderCache.Clear("Data//Shaders//ISReflectionsRayTracing.hlsl")) + // if can't find specific hlsl file cache, clear all image space files + shaderCache.Clear(RE::BSShader::Types::ImageSpace); + recompileFlag = false; + } switch (nextTask) { case NextTask::kInferrence: diff --git a/src/Features/DynamicCubemaps.h b/src/Features/DynamicCubemaps.h index 7ee7da13a..a06755891 100644 --- a/src/Features/DynamicCubemaps.h +++ b/src/Features/DynamicCubemaps.h @@ -61,6 +61,7 @@ struct DynamicCubemaps : Feature bool activeReflections = false; bool resetCapture = true; + bool recompileFlag = false; enum class NextTask { @@ -77,12 +78,15 @@ struct DynamicCubemaps : Feature struct Settings { - uint Enabled = false; - uint pad0[3]{}; + uint EnabledCreator = false; + uint EnabledSSR = true; + uint MaxIterations = static_cast(REL::Relocate(16, 16, 48)); + uint pad0{}; float4 CubemapColor{ 1.0f, 1.0f, 1.0f, 0.0f }; }; Settings settings; + std::string maxIterationsString = ""; // required to avoid string going out of scope for defines void UpdateCubemap(); @@ -91,15 +95,27 @@ struct DynamicCubemaps : Feature virtual inline std::string GetName() override { return "Dynamic Cubemaps"; } virtual inline std::string GetShortName() override { return "DynamicCubemaps"; } virtual inline std::string_view GetShaderDefineName() override { return "DYNAMIC_CUBEMAPS"; } + virtual inline std::vector> GetShaderDefineOptions() override; + bool HasShaderDefine(RE::BSShader::Type) override { return true; }; virtual void SetupResources() override; 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 SSRSettings{ + { "fWaterSSRNormalPerturbationScale:Display", { "Water Normal Perturbation Scale", "Controls the scale of normal perturbations for Screen Space Reflections (SSR) on water surfaces.", 0, 0.05f, 0.f, 1.f } }, + { "fWaterSSRBlurAmount:Display", { "Water SSR Blur Amount", "Defines the amount of blur applied to Screen Space Reflections on water surfaces.", 0, 0.3f, 0.f, 1.f } }, + { "fWaterSSRIntensity:Display", { "Water SSR Intensity", "Adjusts the intensity or strength of Screen Space Reflections on water.", 0, 1.3f, 0.f, 5.f } }, + { "bDownSampleNormalSSR:Display", { "Down Sample Normal SSR", "Enables or disables downsampling of normals for SSR to improve performance.", 0, true, false, true } } + }; + std::map iniVRCubeMapSettings{ { "bAutoWaterSilhouetteReflections:Water", { "Auto Water Silhouette Reflections", "Automatically reflects silhouettes on water surfaces.", 0, true, false, true } }, { "bForceHighDetailReflections:Water", { "Force High Detail Reflections", "Forces the use of high-detail reflections on water surfaces.", 0, true, false, true } } diff --git a/src/ShaderCache.cpp b/src/ShaderCache.cpp index d0910196d..5eb62303d 100644 --- a/src/ShaderCache.cpp +++ b/src/ShaderCache.cpp @@ -1308,6 +1308,8 @@ namespace SIE std::string::size_type pos = a_key.find(':'); if (pos != std::string::npos) type = a_key.substr(0, pos); + if (type.starts_with("IS") || type == "ReflectionsRayTracing") + type = "ImageSpace"; // fix type for image space shaders return type; } From df1186ee083970740105280bc11d37975cd3f4fb Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Tue, 17 Sep 2024 01:16:36 -0700 Subject: [PATCH 36/48] refactor: consolidate VR functions --- package/Shaders/Common/FrameBuffer.hlsli | 42 +++--------------------- package/Shaders/Common/VR.hlsli | 42 ++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 38 deletions(-) diff --git a/package/Shaders/Common/FrameBuffer.hlsli b/package/Shaders/Common/FrameBuffer.hlsli index f15dd2026..d4384650c 100644 --- a/package/Shaders/Common/FrameBuffer.hlsli +++ b/package/Shaders/Common/FrameBuffer.hlsli @@ -1,3 +1,6 @@ +#ifndef __FRAMEBUFFER_DEPENDENCY_HLSL__ +#define __FRAMEBUFFER_DEPENDENCY_HLSL__ + cbuffer PerFrame : register(b12) { #if !defined(VR) @@ -129,41 +132,4 @@ bool isOutsideFrame(float2 uv, bool dynamicres = false) return any(uv < float2(0, 0) || uv > max.xy); } -/** - * @brief Converts mono UV coordinates from one eye to the corresponding mono UV coordinates of the other eye. - * - * This function is used to transition UV coordinates from one eye's perspective to the other eye in a stereo rendering setup. - * It works by converting the mono UV to clip space, transforming it into view space, and then reprojecting it into the other eye's - * clip space before converting back to UV coordinates. It also supports dynamic resolution. - * - * @param[in] monoUV The UV coordinates and depth value (Z component) for the current eye, in the range [0,1]. - * @param[in] eyeIndex Index of the current eye (0 or 1). - * @param[in] dynamicres Optional flag indicating whether dynamic resolution is applied. Default is false. - * @return UV coordinates adjusted to the other eye, with depth. - */ -float3 ConvertMonoUVToOtherEye(float3 monoUV, uint eyeIndex, bool dynamicres = false) -{ - // Convert from dynamic res to true UV space - if (dynamicres) - monoUV.xy *= DynamicResolutionParams2.xy; - - // Step 1: Convert UV to Clip Space - float4 clipPos = float4(monoUV.xy * float2(2, -2) - float2(1, -1), monoUV.z, 1); - - // Step 2: Convert Clip Space to View Space for the current eye - float4 viewPosCurrentEye = mul(CameraProjInverse[eyeIndex], clipPos); - viewPosCurrentEye /= viewPosCurrentEye.w; - - // Step 3: Convert View Space to Clip Space for the other eye - float4 clipPosOtherEye = mul(CameraProj[1 - eyeIndex], viewPosCurrentEye); - clipPosOtherEye /= clipPosOtherEye.w; - - // Step 4: Convert Clip Space to UV - float3 monoUVOtherEye = float3((clipPosOtherEye.xy * 0.5f) + 0.5f, clipPosOtherEye.z); - - // Convert back to dynamic res space if necessary - if (dynamicres) - monoUVOtherEye.xy *= DynamicResolutionParams1.xy; - - return monoUVOtherEye; -} \ No newline at end of file +#endif //__FRAMEBUFFER_DEPENDENCY_HLSL__ \ No newline at end of file diff --git a/package/Shaders/Common/VR.hlsli b/package/Shaders/Common/VR.hlsli index 637bfd0e7..54d74f425 100644 --- a/package/Shaders/Common/VR.hlsli +++ b/package/Shaders/Common/VR.hlsli @@ -1,6 +1,8 @@ #ifndef __VR_DEPENDENCY_HLSL__ #define __VR_DEPENDENCY_HLSL__ #ifdef VR +# include "Common\Constants.hlsli" +# include "Common\FrameBuffer.hlsli" cbuffer VRValues : register(b13) { float AlphaTestRefRS : packoffset(c0); @@ -142,6 +144,46 @@ uint GetEyeIndexFromTexCoord(float2 texCoord) return 0; } +/** + * @brief Converts mono UV coordinates from one eye to the corresponding mono UV coordinates of the other eye. + * + * This function is used to transition UV coordinates from one eye's perspective to the other eye in a stereo rendering setup. + * It works by converting the mono UV to clip space, transforming it into view space, and then reprojecting it into the other eye's + * clip space before converting back to UV coordinates. It also supports dynamic resolution. + * + * @param[in] monoUV The UV coordinates and depth value (Z component) for the current eye, in the range [0,1]. + * @param[in] eyeIndex Index of the current eye (0 or 1). + * @param[in] dynamicres Optional flag indicating whether dynamic resolution is applied. Default is false. + * @return UV coordinates adjusted to the other eye, with depth. + */ +float3 ConvertMonoUVToOtherEye(float3 monoUV, uint eyeIndex, bool dynamicres = false) +{ + // Convert from dynamic res to true UV space + if (dynamicres) + monoUV.xy *= DynamicResolutionParams2.xy; + + // Step 1: Convert UV to Clip Space + float4 clipPos = float4(monoUV.xy * float2(2, -2) - float2(1, -1), monoUV.z, 1); + + // Step 2: Convert Clip Space to View Space for the current eye + float4 viewPosCurrentEye = mul(CameraProjInverse[eyeIndex], clipPos); + viewPosCurrentEye /= viewPosCurrentEye.w; + + // Step 3: Convert View Space to Clip Space for the other eye + float4 clipPosOtherEye = mul(CameraProj[1 - eyeIndex], viewPosCurrentEye); + clipPosOtherEye /= clipPosOtherEye.w; + + // Step 4: Convert Clip Space to UV + float3 monoUVOtherEye = float3((clipPosOtherEye.xy * 0.5f) + 0.5f, clipPosOtherEye.z); + + // Convert back to dynamic res space if necessary + if (dynamicres) + monoUVOtherEye.xy *= DynamicResolutionParams1.xy; + + return monoUVOtherEye; +} + + /** * @brief Adjusts UV coordinates for VR stereo rendering when transitioning between eyes or handling boundary conditions. * From ff24fbf8b957286fa68232d59a4e176d639401aa Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Tue, 17 Sep 2024 01:17:34 -0700 Subject: [PATCH 37/48] fix: fix use of stereo consistent information --- package/Shaders/Common/VR.hlsli | 11 +++++------ package/Shaders/ISReflectionsRayTracing.hlsl | 6 +++--- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/package/Shaders/Common/VR.hlsli b/package/Shaders/Common/VR.hlsli index 54d74f425..1758407c0 100644 --- a/package/Shaders/Common/VR.hlsli +++ b/package/Shaders/Common/VR.hlsli @@ -152,7 +152,7 @@ uint GetEyeIndexFromTexCoord(float2 texCoord) * clip space before converting back to UV coordinates. It also supports dynamic resolution. * * @param[in] monoUV The UV coordinates and depth value (Z component) for the current eye, in the range [0,1]. - * @param[in] eyeIndex Index of the current eye (0 or 1). + * @param[in] eyeIndex Index of the source/current eye (0 or 1). * @param[in] dynamicres Optional flag indicating whether dynamic resolution is applied. Default is false. * @return UV coordinates adjusted to the other eye, with depth. */ @@ -183,7 +183,6 @@ float3 ConvertMonoUVToOtherEye(float3 monoUV, uint eyeIndex, bool dynamicres = f return monoUVOtherEye; } - /** * @brief Adjusts UV coordinates for VR stereo rendering when transitioning between eyes or handling boundary conditions. * @@ -207,20 +206,20 @@ float3 ConvertMonoUVToOtherEye(float3 monoUV, uint eyeIndex, bool dynamicres = f float3 ConvertStereoRayMarchUV(float3 monoUV, uint eyeIndex, out bool fromOtherEye) { fromOtherEye = false; - // Convert to stereo UV coordinates - float3 resultUV = ConvertToStereoUV(monoUV, eyeIndex); - + float3 resultUV = monoUV; #ifdef VR // Check if the UV coordinates are outside the frame if (isOutsideFrame(resultUV.xy, false)) { // Transition to the other eye - float3 otherEyeUV = ConvertMonoUVToOtherEye(resultUV, 1 - eyeIndex); + float3 otherEyeUV = ConvertMonoUVToOtherEye(resultUV, eyeIndex); // Check if the other eye's UV coordinates are within the frame if (!isOutsideFrame(otherEyeUV.xy, false)) { resultUV = ConvertToStereoUV(otherEyeUV, 1 - eyeIndex); fromOtherEye = true; // Indicate that the result is from the other eye } + } else { + resultUV = ConvertToStereoUV(resultUV, eyeIndex); } #endif return resultUV; diff --git a/package/Shaders/ISReflectionsRayTracing.hlsl b/package/Shaders/ISReflectionsRayTracing.hlsl index 18626e6f4..4e84776a0 100644 --- a/package/Shaders/ISReflectionsRayTracing.hlsl +++ b/package/Shaders/ISReflectionsRayTracing.hlsl @@ -163,11 +163,11 @@ PS_OUTPUT main(PS_INPUT input) float iterationDepth = DepthTex.SampleLevel(DepthSampler, iterationUvDepthSampleDR.xy, 0).x; uvDepthPreResultDR = uvDepthResultDR; uvDepthResultDR = iterationUvDepthDR; - if (isOutsideFrame(iterationUvDepthDR.xy) + if (isOutsideFrame(iterationUvDepthDR.xy, true) # ifdef VR // In VR, it could be coming from the other eye && !fromOtherEye || - (fromOtherEye && isOutsideFrame(ConvertMonoUVToOtherEye(iterationUvDepthDR, 1 - eyeIndex, true).xy, true)) + (fromOtherEye && isOutsideFrame(ConvertMonoUVToOtherEye(iterationUvDepthDR, eyeIndex, true).xy, true)) # endif ) { // out of screen, no ray ssr possible @@ -353,7 +353,7 @@ PS_OUTPUT main(PS_INPUT input) # ifdef VR // Make VR fades consistent by taking the closer of the two eyes // Based on concepts from https://cuteloong.github.io/publications/scssr24/ - float2 otherEyeUvResultScreenCenterOffset = ConvertMonoUVToOtherEye(uvDepthFinalDR, eyeIndex).xy - 0.5; + float2 otherEyeUvResultScreenCenterOffset = ConvertMonoUVToOtherEye(uvDepthFinalDR, eyeIndex, true).xy - 0.5; centerDistance = min(centerDistance, 2 * length(otherEyeUvResultScreenCenterOffset)); # endif From 728725b73cc9c9177650358cfc45c735a7d110cb Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Tue, 17 Sep 2024 01:17:57 -0700 Subject: [PATCH 38/48] fix(VR): fix mismatched eyes for SSR Uses new VR function that allows conversion from the coordinates from one eye to the other. This allows us to detect if one eye has calculated a reflection and to use that data instead. --- package/Shaders/Common/VR.hlsli | 30 ++++++++++++++++++++++++++++++ package/Shaders/Water.hlsl | 14 ++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/package/Shaders/Common/VR.hlsli b/package/Shaders/Common/VR.hlsli index 1758407c0..db67cf85e 100644 --- a/package/Shaders/Common/VR.hlsli +++ b/package/Shaders/Common/VR.hlsli @@ -225,6 +225,36 @@ float3 ConvertStereoRayMarchUV(float3 monoUV, uint eyeIndex, out bool fromOtherE return resultUV; } +/** + * @brief Converts stereo UV coordinates from one eye to the corresponding stereo UV coordinates of the other eye. + * + * This function is used to transition UV coordinates from one eye's perspective to the other eye in a stereo rendering setup. + * It works by converting the stereo UV to mono UV, then to clip space, transforming it into view space, and then reprojecting it into the other eye's + * clip space before converting back to stereo UV coordinates. It also supports dynamic resolution. + * + * @param[in] stereoUV The UV coordinates and depth value (Z component) for the current eye, in the range [0,1]. + * @param[in] eyeIndex Index of the current eye (0 or 1). + * @param[in] dynamicres Optional flag indicating whether dynamic resolution is applied. Default is false. + * @return UV coordinates adjusted to the other eye, with depth. + */ +float3 ConvertStereoUVToOtherEyeStereoUV(float3 stereoUV, uint eyeIndex, bool dynamicres = false) +{ + // Convert from dynamic res to true UV space + if (dynamicres) + stereoUV.xy *= DynamicResolutionParams2.xy; + + stereoUV.xy = ConvertFromStereoUV(stereoUV.xy, eyeIndex, true); // for some reason, the uv.y needs to be inverted before conversion? + // Swap eyes + stereoUV.xyz = ConvertMonoUVToOtherEye(stereoUV.xyz, eyeIndex); + + stereoUV.xy = ConvertToStereoUV(stereoUV.xy, 1 - eyeIndex, false); + + // Convert back to dynamic res space if necessary + if (dynamicres) + stereoUV.xy *= DynamicResolutionParams1.xy; + return stereoUV; +} + struct VR_OUTPUT { float4 VRPosition; diff --git a/package/Shaders/Water.hlsl b/package/Shaders/Water.hlsl index afbc84c48..372feb571 100644 --- a/package/Shaders/Water.hlsl +++ b/package/Shaders/Water.hlsl @@ -626,6 +626,20 @@ float3 GetWaterSpecularColor(PS_INPUT input, float3 normal, float3 viewDirection float3 fogColor = lerp(FogNearColor.xyz, FogFarColor.xyz, fogDensity); bool validSSRMask = IsValidSSRMask(ssrReflectionColorRaw); + +# ifdef VR + if (!validSSRMask) { + // Check the other eye's mask to see if we have better information to use + float3 otherEyeUV = ConvertStereoUVToOtherEyeStereoUV(float3(ssrReflectionUv, input.HPosition.z), a_eyeIndex, false); + float2 otherEyeUVDR = GetDynamicResolutionAdjustedScreenPosition(otherEyeUV.xy); + ssrReflectionColorRaw = RawSSRReflectionTex.Sample(RawSSRReflectionSampler, otherEyeUVDR); + validSSRMask = IsValidSSRMask(ssrReflectionColorRaw); + if (validSSRMask && !isOutsideFrame(otherEyeUV.xy)) { + ssrReflectionColorBlurred = SSRReflectionTex.Sample(SSRReflectionSampler, otherEyeUVDR); + } + } +# endif + if (validSSRMask) { float effectiveBlurFactor = saturate(SSRParams.y * (1.0 + fogDensity)); float4 ssrReflectionColor = lerp(ssrReflectionColorRaw, ssrReflectionColorBlurred, effectiveBlurFactor); From 06af105c7211db5a0c137ff158c74e07fc8890f8 Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Tue, 17 Sep 2024 01:25:17 -0700 Subject: [PATCH 39/48] fix: fix compute shader compilation errors --- package/Shaders/Common/VR.hlsli | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/package/Shaders/Common/VR.hlsli b/package/Shaders/Common/VR.hlsli index db67cf85e..224131340 100644 --- a/package/Shaders/Common/VR.hlsli +++ b/package/Shaders/Common/VR.hlsli @@ -1,8 +1,10 @@ #ifndef __VR_DEPENDENCY_HLSL__ #define __VR_DEPENDENCY_HLSL__ #ifdef VR -# include "Common\Constants.hlsli" -# include "Common\FrameBuffer.hlsli" +# ifndef COMPUTESHADER +# include "Common\Constants.hlsli" +# include "Common\FrameBuffer.hlsli" +# endif cbuffer VRValues : register(b13) { float AlphaTestRefRS : packoffset(c0); From eaff8ae151e7109c7105993faf404d475f4f1fcc Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Tue, 17 Sep 2024 01:27:22 -0700 Subject: [PATCH 40/48] fix: fix fog application Fixes bug where the fog was being applied less when it was more foggy. --- package/Shaders/Water.hlsl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package/Shaders/Water.hlsl b/package/Shaders/Water.hlsl index 372feb571..d2e4dc6aa 100644 --- a/package/Shaders/Water.hlsl +++ b/package/Shaders/Water.hlsl @@ -621,7 +621,7 @@ float3 GetWaterSpecularColor(PS_INPUT input, float3 normal, float3 viewDirection float4 ssrReflectionColorRaw = RawSSRReflectionTex.Sample(RawSSRReflectionSampler, ssrReflectionUvDR); // calculate blur on reflection - float depth = DepthTex.Load(int3(ssrReflectionUvDR * BufferDim.xy, 0)).g; + float depth = DepthTex.Load(int3(ssrReflectionUvDR * BufferDim.xy, 0)); float fogDensity = 1 - pow(saturate((-depth * FogParam.z + FogParam.z) / FogParam.w), FogNearColor.w); float3 fogColor = lerp(FogNearColor.xyz, FogFarColor.xyz, fogDensity); @@ -651,7 +651,7 @@ float3 GetWaterSpecularColor(PS_INPUT input, float3 normal, float3 viewDirection finalSsrReflectionColor = reflectionColor.xyz; ssrFraction = 1.f; } - finalSsrReflectionColor = lerp(finalSsrReflectionColor, fogColor, fogDensity); + finalSsrReflectionColor = lerp(fogColor, finalSsrReflectionColor, fogDensity); } # endif From 5c21187b3d5c610c7062a262616568db85bd6e2d Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Tue, 17 Sep 2024 02:26:37 -0700 Subject: [PATCH 41/48] feat(VR): enable ssr as input for IS fog pass --- package/Shaders/ISSAOComposite.hlsl | 2 -- 1 file changed, 2 deletions(-) diff --git a/package/Shaders/ISSAOComposite.hlsl b/package/Shaders/ISSAOComposite.hlsl index ab195a091..10570b280 100644 --- a/package/Shaders/ISSAOComposite.hlsl +++ b/package/Shaders/ISSAOComposite.hlsl @@ -134,7 +134,6 @@ PS_OUTPUT main(PS_INPUT input) float4 composedColor = sourceColor; -# if !defined(VR) if (0.5 < SSRParams.z) { float2 ssrMask = NormalsSSRMaskTex.SampleLevel(NormalsSSRMaskSampler, screenPosition, 0).zw; float4 ssr = SSRSourceTex.Sample(SSRSourceSampler, screenPosition); @@ -145,7 +144,6 @@ PS_OUTPUT main(PS_INPUT input) } composedColor.xyz += ssrInput; } -# endif float snowMask = 0; # if !defined(VR) From 5aeacb0d8bef12384910351ce44eccf221143822 Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Tue, 17 Sep 2024 20:49:01 -0700 Subject: [PATCH 42/48] fix: fix clearing shader with prior diskcache Fixes a bug where a prior saved diskcache shaders could not be cleared by filename. --- src/ShaderCache.cpp | 4 ++-- src/ShaderCache.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ShaderCache.cpp b/src/ShaderCache.cpp index 5eb62303d..b94b3472e 100644 --- a/src/ShaderCache.cpp +++ b/src/ShaderCache.cpp @@ -1441,7 +1441,7 @@ namespace SIE logger::debug("Saved shader to {}", Util::WStringToString(diskPath)); } } - cache.AddCompletedShader(shaderClass, shader, descriptor, shaderBlob, pathString); + cache.AddCompletedShader(shaderClass, shader, descriptor, shaderBlob); return shaderBlob; } @@ -2016,7 +2016,7 @@ namespace SIE compilationSet.Clear(); } - bool ShaderCache::AddCompletedShader(ShaderClass shaderClass, const RE::BSShader& shader, uint32_t descriptor, ID3DBlob* a_blob, const std::string& a_path) + bool ShaderCache::AddCompletedShader(ShaderClass shaderClass, const RE::BSShader& shader, uint32_t descriptor, ID3DBlob* a_blob) { auto key = SIE::SShaderCache::GetShaderString(shaderClass, shader, descriptor, true); auto status = a_blob ? ShaderCompilationTask::Status::Completed : ShaderCompilationTask::Status::Failed; diff --git a/src/ShaderCache.h b/src/ShaderCache.h index 78b4942c8..0e1d633da 100644 --- a/src/ShaderCache.h +++ b/src/ShaderCache.h @@ -349,7 +349,7 @@ namespace SIE */ bool Clear(const std::string& a_path); - bool AddCompletedShader(ShaderClass shaderClass, const RE::BSShader& shader, uint32_t descriptor, ID3DBlob* a_blob, const std::string& a_path = ""); + bool AddCompletedShader(ShaderClass shaderClass, const RE::BSShader& shader, uint32_t descriptor, ID3DBlob* a_blob); ID3DBlob* GetCompletedShader(const std::string& a_key); ID3DBlob* GetCompletedShader(const SIE::ShaderCompilationTask& a_task); ID3DBlob* GetCompletedShader(ShaderClass shaderClass, const RE::BSShader& shader, uint32_t descriptor); From e2edb17c0bfb19b2b90a644694bf1228c59b7067 Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Thu, 19 Sep 2024 22:33:32 -0700 Subject: [PATCH 43/48] fix(VR): fix fade calculation Fade calculation should be in monoUV so it's relative for both each eye. This fixes an issue where eyes would have drastically different alpha components. --- package/Shaders/ISReflectionsRayTracing.hlsl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/package/Shaders/ISReflectionsRayTracing.hlsl b/package/Shaders/ISReflectionsRayTracing.hlsl index 4e84776a0..2d5be8f08 100644 --- a/package/Shaders/ISReflectionsRayTracing.hlsl +++ b/package/Shaders/ISReflectionsRayTracing.hlsl @@ -311,6 +311,10 @@ PS_OUTPUT main(PS_INPUT input) // Because alpha is based on the prior frame, there will be a lag for showing clouds. // This is very obvious in VR. Hide clouds for now. alpha = useAlpha ? alpha : float3(0, 0, 0); + + // for fade calculation from eye center, need to adjust to monoUV + uvFinal = ConvertFromStereoUV(uvFinal, eyeIndex); + uvStart = ConvertFromStereoUV(uvStart, eyeIndex); # endif float3 ssrColor = SSRParams.z * alpha + color; @@ -353,7 +357,7 @@ PS_OUTPUT main(PS_INPUT input) # ifdef VR // Make VR fades consistent by taking the closer of the two eyes // Based on concepts from https://cuteloong.github.io/publications/scssr24/ - float2 otherEyeUvResultScreenCenterOffset = ConvertMonoUVToOtherEye(uvDepthFinalDR, eyeIndex, true).xy - 0.5; + float2 otherEyeUvResultScreenCenterOffset = ConvertMonoUVToOtherEye(GetDynamicResolutionUnadjustedScreenPosition(uvDepthFinalDR), eyeIndex).xy - 0.5; centerDistance = min(centerDistance, 2 * length(otherEyeUvResultScreenCenterOffset)); # endif From 5e38cd30ae387c0c4bcf503b28a7cb5d49daf74d Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Fri, 20 Sep 2024 00:06:26 -0700 Subject: [PATCH 44/48] fix(VR): blend eye colors to avoid inconsistencies --- package/Shaders/Common/VR.hlsli | 65 +++++++++++++++++++++++++++++++++ package/Shaders/Water.hlsl | 36 +++++------------- 2 files changed, 74 insertions(+), 27 deletions(-) diff --git a/package/Shaders/Common/VR.hlsli b/package/Shaders/Common/VR.hlsli index 224131340..3837d3077 100644 --- a/package/Shaders/Common/VR.hlsli +++ b/package/Shaders/Common/VR.hlsli @@ -257,6 +257,71 @@ float3 ConvertStereoUVToOtherEyeStereoUV(float3 stereoUV, uint eyeIndex, bool dy 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. + * + * This function checks the validity of the colors based on their UV coordinates and + * alpha values. It blends the colors while ensuring proper handling of transparency. + * + * @param uv1 UV coordinates for the first eye. + * @param color1 Color from the first eye. + * @param uv2 UV coordinates for the second eye. + * @param color2 Color from the second eye. + * @param dynamicres Whether the uvs have dynamic resolution applied + * @return Blended color, including the maximum alpha from both inputs. + */ +float4 BlendEyeColors( + float3 uv1, + float4 color1, + float3 uv2, + float4 color2, + bool dynamicres = false) +{ + // Check validity for color1 + bool validColor1 = IsNonZeroColor(color1) && !isOutsideFrame(uv1.xy, dynamicres); + // Check validity for color2 + bool validColor2 = IsNonZeroColor(color2) && !isOutsideFrame(uv2.xy, dynamicres); + + // Calculate alpha values + float alpha1 = validColor1 ? color1.a : 0.0f; + float alpha2 = validColor2 ? color2.a : 0.0f; + + // Total alpha + float totalAlpha = alpha1 + alpha2; + + // Blend based on higher alpha + float4 blendedColor = (validColor1 ? color1 * (alpha1 / max(totalAlpha, 1e-5)) : float4(0, 0, 0, 0)) + + (validColor2 ? color2 * (alpha2 / max(totalAlpha, 1e-5)) : float4(0, 0, 0, 0)); + + // Final alpha determination + blendedColor.a = max(color1.a, color2.a); + + return blendedColor; +} + +float4 BlendEyeColors(float2 uv1, float4 color1, float2 uv2, float4 color2, bool dynamicres = false) +{ + return BlendEyeColors(float3(uv1, 0), color1, float3(uv2, 0), color2, dynamicres); +} + struct VR_OUTPUT { float4 VRPosition; diff --git a/package/Shaders/Water.hlsl b/package/Shaders/Water.hlsl index d2e4dc6aa..74d81110c 100644 --- a/package/Shaders/Water.hlsl +++ b/package/Shaders/Water.hlsl @@ -379,22 +379,6 @@ cbuffer PerGeometry : register(b2) # endif //VR } -/** - * @brief Checks if the SSR reflection mask is invalid by testing if the reflection color is close to zero. - * - * This function evaluates whether the screen-space reflection (SSR) mask represents an invalid reflection by - * checking if the reflection color is essentially black (close to zero). It uses a small epsilon value to - * allow for floating point imprecision. - * - * @param[in] ssrColor The SSR reflection color sampled from a texture. - * @param[in] epsilon Small tolerance value used to determine if the color is close to zero. - * @return True if the SSR mask is considered invalid (color is close to zero), otherwise false. - */ -bool IsValidSSRMask(float4 ssrColor, float epsilon = 0.001) -{ - return !dot(ssrColor.xyz, ssrColor.xyz) < epsilon * epsilon; -} - # ifdef VR float GetStencil(float2 uv) { @@ -625,19 +609,17 @@ float3 GetWaterSpecularColor(PS_INPUT input, float3 normal, float3 viewDirection float fogDensity = 1 - pow(saturate((-depth * FogParam.z + FogParam.z) / FogParam.w), FogNearColor.w); float3 fogColor = lerp(FogNearColor.xyz, FogFarColor.xyz, fogDensity); - bool validSSRMask = IsValidSSRMask(ssrReflectionColorRaw); + bool validSSRMask = IsNonZeroColor(ssrReflectionColorRaw); # ifdef VR - if (!validSSRMask) { - // Check the other eye's mask to see if we have better information to use - float3 otherEyeUV = ConvertStereoUVToOtherEyeStereoUV(float3(ssrReflectionUv, input.HPosition.z), a_eyeIndex, false); - float2 otherEyeUVDR = GetDynamicResolutionAdjustedScreenPosition(otherEyeUV.xy); - ssrReflectionColorRaw = RawSSRReflectionTex.Sample(RawSSRReflectionSampler, otherEyeUVDR); - validSSRMask = IsValidSSRMask(ssrReflectionColorRaw); - if (validSSRMask && !isOutsideFrame(otherEyeUV.xy)) { - ssrReflectionColorBlurred = SSRReflectionTex.Sample(SSRReflectionSampler, otherEyeUVDR); - } - } + float3 otherEyeUV = ConvertStereoUVToOtherEyeStereoUV(float3(ssrReflectionUv, input.HPosition.z), a_eyeIndex, false); + float2 otherEyeUVDR = GetDynamicResolutionAdjustedScreenPosition(otherEyeUV.xy); + + float4 otherEyeSSRColorRaw = RawSSRReflectionTex.Sample(RawSSRReflectionSampler, otherEyeUVDR); + float4 otherEyeSSRColorBlurred = SSRReflectionTex.Sample(SSRReflectionSampler, otherEyeUVDR); + + ssrReflectionColorRaw = BlendEyeColors(ssrReflectionUvDR, ssrReflectionColorRaw, otherEyeUVDR, otherEyeSSRColorRaw, true); + ssrReflectionColorBlurred = BlendEyeColors(ssrReflectionUvDR, ssrReflectionColorBlurred, otherEyeUVDR, otherEyeSSRColorBlurred, true); # endif if (validSSRMask) { From 6109eeb56c6365d3072ea92f48b666602206c0dd Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Sat, 21 Sep 2024 02:41:09 -0700 Subject: [PATCH 45/48] fix: fix fog application Fixes blue colors near edges when nearly underwater due to incorrect depth detection. --- package/Shaders/Common/VR.hlsli | 2 +- package/Shaders/Water.hlsl | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/package/Shaders/Common/VR.hlsli b/package/Shaders/Common/VR.hlsli index 3837d3077..2c0d66076 100644 --- a/package/Shaders/Common/VR.hlsli +++ b/package/Shaders/Common/VR.hlsli @@ -272,7 +272,7 @@ float3 ConvertStereoUVToOtherEyeStereoUV(float3 stereoUV, uint eyeIndex, bool dy */ bool IsNonZeroColor(float4 ssrColor, float epsilon = 0.001) { - return !dot(ssrColor.xyz, ssrColor.xyz) < epsilon * epsilon; + return dot(ssrColor.xyz, ssrColor.xyz) > epsilon * epsilon; } /** diff --git a/package/Shaders/Water.hlsl b/package/Shaders/Water.hlsl index 74d81110c..94adbabbe 100644 --- a/package/Shaders/Water.hlsl +++ b/package/Shaders/Water.hlsl @@ -604,9 +604,9 @@ float3 GetWaterSpecularColor(PS_INPUT input, float3 normal, float3 viewDirection float4 ssrReflectionColorBlurred = SSRReflectionTex.Sample(SSRReflectionSampler, ssrReflectionUvDR); float4 ssrReflectionColorRaw = RawSSRReflectionTex.Sample(RawSSRReflectionSampler, ssrReflectionUvDR); - // calculate blur on reflection + // calculate fog on reflection float depth = DepthTex.Load(int3(ssrReflectionUvDR * BufferDim.xy, 0)); - float fogDensity = 1 - pow(saturate((-depth * FogParam.z + FogParam.z) / FogParam.w), FogNearColor.w); + float fogDensity = depth == 0 ? 0.f : pow(saturate((-depth * FogParam.z + FogParam.z) / FogParam.w), FogNearColor.w); float3 fogColor = lerp(FogNearColor.xyz, FogFarColor.xyz, fogDensity); bool validSSRMask = IsNonZeroColor(ssrReflectionColorRaw); @@ -623,6 +623,7 @@ float3 GetWaterSpecularColor(PS_INPUT input, float3 normal, float3 viewDirection # endif if (validSSRMask) { + // calculate blur on reflection float effectiveBlurFactor = saturate(SSRParams.y * (1.0 + fogDensity)); float4 ssrReflectionColor = lerp(ssrReflectionColorRaw, ssrReflectionColorBlurred, effectiveBlurFactor); @@ -633,7 +634,7 @@ float3 GetWaterSpecularColor(PS_INPUT input, float3 normal, float3 viewDirection finalSsrReflectionColor = reflectionColor.xyz; ssrFraction = 1.f; } - finalSsrReflectionColor = lerp(fogColor, finalSsrReflectionColor, fogDensity); + finalSsrReflectionColor = lerp(finalSsrReflectionColor, fogColor, fogDensity); } # endif From 5d01531cd7859d360290af583a480084ab03e90e Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Sat, 21 Sep 2024 17:14:31 -0700 Subject: [PATCH 46/48] feat(VR): add option to completely disable SSR Because VR requires a hidden setting, once enabled on boot, it will persist. Now, if SSR is disabled on boot, it will completely disable the SSR path for maximum performance. However, a restart is necessary to reenable. --- src/Features/DynamicCubemaps.cpp | 10 +++++++++- src/Features/DynamicCubemaps.h | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Features/DynamicCubemaps.cpp b/src/Features/DynamicCubemaps.cpp index 9e1169700..ca56000b8 100644 --- a/src/Features/DynamicCubemaps.cpp +++ b/src/Features/DynamicCubemaps.cpp @@ -35,6 +35,13 @@ void DynamicCubemaps::DrawSettings() recompileFlag |= ImGui::Checkbox("Enable Screen Space Reflections", reinterpret_cast(&settings.EnabledSSR)); if (auto _tt = Util::HoverTooltipWrapper()) { ImGui::Text("Enable Screen Space Reflections on Water"); + if (REL::Module::IsVR() && !enabledAtBoot) { + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.0f, 0.0f, 1.0f)); + ImGui::Text( + "A restart is required to enable in VR. " + "Save Settings after enabling and restart the game."); + ImGui::PopStyleColor(); + } } if (settings.EnabledSSR) { recompileFlag |= ImGui::SliderInt("Max Iterations", reinterpret_cast(&settings.MaxIterations), 1, 128); @@ -185,7 +192,7 @@ void DynamicCubemaps::DataLoaded() void DynamicCubemaps::PostPostLoad() { - if (REL::Module::IsVR()) { + if (REL::Module::IsVR() && settings.EnabledSSR) { std::map earlyhiddenVRCubeMapSettings{ { "bScreenSpaceReflectionEnabled:Display", 0x1ED5BC0 }, }; @@ -198,6 +205,7 @@ void DynamicCubemaps::PostPostLoad() *setting = true; } } + enabledAtBoot = true; } } diff --git a/src/Features/DynamicCubemaps.h b/src/Features/DynamicCubemaps.h index a06755891..b661850fd 100644 --- a/src/Features/DynamicCubemaps.h +++ b/src/Features/DynamicCubemaps.h @@ -87,7 +87,7 @@ struct DynamicCubemaps : Feature Settings settings; std::string maxIterationsString = ""; // required to avoid string going out of scope for defines - + bool enabledAtBoot = false; void UpdateCubemap(); void PostDeferred(); From fc0321393f5e6d8af6ebd0b6694792df815a60dd Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Sun, 22 Sep 2024 23:27:07 -0700 Subject: [PATCH 47/48] fix: remove eye blending between eyes Some HMDs appear to have bad transformations so would experience duplication. --- package/Shaders/Water.hlsl | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/package/Shaders/Water.hlsl b/package/Shaders/Water.hlsl index 94adbabbe..9714e08ed 100644 --- a/package/Shaders/Water.hlsl +++ b/package/Shaders/Water.hlsl @@ -611,17 +611,6 @@ float3 GetWaterSpecularColor(PS_INPUT input, float3 normal, float3 viewDirection bool validSSRMask = IsNonZeroColor(ssrReflectionColorRaw); -# ifdef VR - float3 otherEyeUV = ConvertStereoUVToOtherEyeStereoUV(float3(ssrReflectionUv, input.HPosition.z), a_eyeIndex, false); - float2 otherEyeUVDR = GetDynamicResolutionAdjustedScreenPosition(otherEyeUV.xy); - - float4 otherEyeSSRColorRaw = RawSSRReflectionTex.Sample(RawSSRReflectionSampler, otherEyeUVDR); - float4 otherEyeSSRColorBlurred = SSRReflectionTex.Sample(SSRReflectionSampler, otherEyeUVDR); - - ssrReflectionColorRaw = BlendEyeColors(ssrReflectionUvDR, ssrReflectionColorRaw, otherEyeUVDR, otherEyeSSRColorRaw, true); - ssrReflectionColorBlurred = BlendEyeColors(ssrReflectionUvDR, ssrReflectionColorBlurred, otherEyeUVDR, otherEyeSSRColorBlurred, true); -# endif - if (validSSRMask) { // calculate blur on reflection float effectiveBlurFactor = saturate(SSRParams.y * (1.0 + fogDensity)); From 0be411c57c38fb713e8a34ab57c4668f7fcad0d0 Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Wed, 25 Sep 2024 18:53:09 -0700 Subject: [PATCH 48/48] refactor: use GetEyeIndexFromTexCoord --- package/Shaders/ISReflectionsRayTracing.hlsl | 6 +----- package/Shaders/ISWaterBlend.hlsl | 6 +----- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/package/Shaders/ISReflectionsRayTracing.hlsl b/package/Shaders/ISReflectionsRayTracing.hlsl index 2d5be8f08..a9c874912 100644 --- a/package/Shaders/ISReflectionsRayTracing.hlsl +++ b/package/Shaders/ISReflectionsRayTracing.hlsl @@ -67,11 +67,7 @@ PS_OUTPUT main(PS_INPUT input) // Disable SSR raymarch return psout; # endif -# ifdef VR - uint eyeIndex = input.TexCoord.x >= 0.5; -# else - uint eyeIndex = 0; -# endif + uint eyeIndex = GetEyeIndexFromTexCoord(input.TexCoord); float2 uvStart = input.TexCoord; float2 uvStartDR = GetDynamicResolutionAdjustedScreenPosition(uvStart); diff --git a/package/Shaders/ISWaterBlend.hlsl b/package/Shaders/ISWaterBlend.hlsl index 6640738ea..ade7fcb86 100644 --- a/package/Shaders/ISWaterBlend.hlsl +++ b/package/Shaders/ISWaterBlend.hlsl @@ -32,11 +32,7 @@ cbuffer PerGeometry : register(b2) PS_OUTPUT main(PS_INPUT input) { PS_OUTPUT psout; -# ifdef VR - uint eyeIndex = input.TexCoord.x >= 0.5; -# else - uint eyeIndex = 0; -# endif + uint eyeIndex = GetEyeIndexFromTexCoord(input.TexCoord); float2 adjustedScreenPosition = GetDynamicResolutionAdjustedScreenPosition(input.TexCoord); float waterMask = waterMaskTex.Sample(waterMaskSampler, adjustedScreenPosition).z; if (waterMask < 1e-4) {