Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(VR): enable SSR #502

Merged
merged 48 commits into from
Sep 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
b798072
fix: fix detection of eyeIndex with imageshaders
alandtse Sep 6, 2024
c77d9e7
fix(VR): saturate uv.x before converting
alandtse Sep 6, 2024
1c69733
feat(VR): add additional helper functions
alandtse Sep 6, 2024
a5ff6e4
feat(VR): enable ssr
alandtse Sep 6, 2024
afc0fdb
chore: remove color lerping with reflectioncolor
alandtse Sep 6, 2024
1a1cf8a
feat: add Stochastic Sampling to SSR
alandtse Sep 7, 2024
0149fe2
feat: add Dynamic Step Size to SSR
alandtse Sep 7, 2024
f53e14a
feat: add Fallback Sampling to SSR
alandtse Sep 7, 2024
8c2d00a
fix: fix eyeindex for flat
alandtse Sep 8, 2024
789f1d9
fix(VR): disable clouds in SSR
alandtse Sep 8, 2024
caeeea4
fix: correct dynamic step size calculation
alandtse Sep 8, 2024
de7869b
fix: fix early exit
alandtse Sep 8, 2024
f434d8f
fix: remove fallback sampling
alandtse Sep 9, 2024
db465b6
perf: use binary search for refinement loop
alandtse Sep 9, 2024
1f7b1f1
feat: add jittered view direction
alandtse Sep 9, 2024
3214322
perf: restore linear search for initial raymarch
alandtse Sep 9, 2024
e54bafb
docs: add comments for fade calculations
alandtse Sep 9, 2024
3f3b731
feat(VR): add stereo-consistent SSR improvements
alandtse Sep 9, 2024
494a693
chore: add debug defines
alandtse Sep 10, 2024
9746c72
fix: fix linear stepping to use maxIterations value
alandtse Sep 10, 2024
946b452
fix(VR): set VR iteration defaults to avoid jaggies
alandtse Sep 10, 2024
c8bf25c
refactor: move VR init into dynamiccubemaps
alandtse Sep 10, 2024
3b60114
fix: respect RawSSRReflectionTex mask
alandtse Sep 10, 2024
5a6afd5
revert: "chore: remove color lerping with reflectioncolor"
alandtse Sep 10, 2024
fc97084
style: 🎨 apply clang-format changes
alandtse Sep 10, 2024
582adf8
chore: discard changes to src/XSEPlugin.cpp
alandtse Sep 10, 2024
6b21904
fix(VR): fix dynamic resolution and upscaler
alandtse Sep 11, 2024
158d016
perf(VR): exit early if in VR mask
alandtse Sep 11, 2024
dd958d3
perf: stop raymarch if outside of screen
alandtse Sep 11, 2024
6786138
chore: rename variables to indicate dynamic res uv
alandtse Sep 11, 2024
6f88e10
refactor: simplify ssr code
alandtse Sep 13, 2024
531f221
feat: add fog color to reflection and lean to blur
alandtse Sep 13, 2024
a788bed
fix: fix compiler warnings
alandtse Sep 14, 2024
1c7399d
feat: add hlsl-specific clear
alandtse Sep 16, 2024
00f9739
feat: enable settings for SSR
alandtse Sep 16, 2024
df1186e
refactor: consolidate VR functions
alandtse Sep 17, 2024
ff24fbf
fix: fix use of stereo consistent information
alandtse Sep 17, 2024
728725b
fix(VR): fix mismatched eyes for SSR
alandtse Sep 17, 2024
06af105
fix: fix compute shader compilation errors
alandtse Sep 17, 2024
eaff8ae
fix: fix fog application
alandtse Sep 17, 2024
5c21187
feat(VR): enable ssr as input for IS fog pass
alandtse Sep 17, 2024
5aeacb0
fix: fix clearing shader with prior diskcache
alandtse Sep 18, 2024
e2edb17
fix(VR): fix fade calculation
alandtse Sep 20, 2024
5e38cd3
fix(VR): blend eye colors to avoid inconsistencies
alandtse Sep 20, 2024
6109eeb
fix: fix fog application
alandtse Sep 21, 2024
5d01531
feat(VR): add option to completely disable SSR
alandtse Sep 22, 2024
fc03213
fix: remove eye blending between eyes
alandtse Sep 23, 2024
0be411c
refactor: use GetEyeIndexFromTexCoord
alandtse Sep 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 40 additions & 6 deletions package/Shaders/Common/FrameBuffer.hlsli
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
#ifndef __FRAMEBUFFER_DEPENDENCY_HLSL__
#define __FRAMEBUFFER_DEPENDENCY_HLSL__

cbuffer PerFrame : register(b12)
{
#if !defined(VR)
Expand Down Expand Up @@ -47,26 +50,39 @@ 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);
}

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;
Expand Down Expand Up @@ -99,3 +115,21 @@ 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;
}

/**
* @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)
{
float2 max = dynamicres ? DynamicResolutionParams1.xy : float2(1, 1);
return any(uv < float2(0, 0) || uv > max.xy);
}

#endif //__FRAMEBUFFER_DEPENDENCY_HLSL__
222 changes: 221 additions & 1 deletion package/Shaders/Common/VR.hlsli
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
#ifndef __VR_DEPENDENCY_HLSL__
#define __VR_DEPENDENCY_HLSL__
#ifdef VR
# ifndef COMPUTESHADER
# include "Common\Constants.hlsli"
# include "Common\FrameBuffer.hlsli"
# endif
cbuffer VRValues : register(b13)
{
float AlphaTestRefRS : packoffset(c0);
Expand All @@ -25,13 +29,26 @@ 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;
#endif
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.
Expand All @@ -53,6 +70,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.
Expand All @@ -66,7 +95,22 @@ 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;
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
Expand Down Expand Up @@ -102,6 +146,182 @@ 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 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.
*/
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.
*
* 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 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, [0-1]. Must not be dynamic resolution adjusted.
* @param[in] eyeIndex Index of the current eye (0 or 1).
* @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, out bool fromOtherEye)
{
fromOtherEye = false;
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, 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;
}

/**
* @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;
}

/**
* @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;
Expand Down
Loading
Loading