-
Notifications
You must be signed in to change notification settings - Fork 133
feat(vr): VR stereo reprojection quality improvements #1982
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
Changes from all commits
83769b0
e1fdc0c
bd1c480
b130bf5
19edb70
39d26c6
f58e0c6
e69a386
25a59f6
3a2bef3
a0d6e8c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| [Info] | ||
| Version = 1-0-0 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -20,11 +20,16 @@ namespace SharedData | |
| float Timer; | ||
| uint FrameCount; | ||
| uint FrameCountAlwaysActive; | ||
| bool InInterior; // If the area lacks a directional shadow light e.g. the sun or moon | ||
| bool InMapMenu; // If the world/local map is open (note that the renderer is still deferred here) | ||
| bool HideSky; // HideSky flag in WorldSpace, e.g. Blackreach | ||
| float MipBias; // Offset to mip level for TAA sharpness# | ||
| float pad0; | ||
| bool InInterior; // If the area lacks a directional shadow light e.g. the sun or moon | ||
| bool InMapMenu; // If the world/local map is open (note that the renderer is still deferred here) | ||
| bool HideSky; // HideSky flag in WorldSpace, e.g. Blackreach | ||
| float MipBias; // Offset to mip level for TAA sharpness | ||
| float VRMipBias; // Additional negative MIP bias for VR foliage sharpening (depth-scaled) | ||
| float VRMipBiasNearDist; // Game units: no VR MIP bias closer than this | ||
| float VRMipBiasFarDist; // Game units: full VR MIP bias beyond this | ||
| uint VRMipBiasMode; // 0=Off, 1=All Textures, 2=Distant Trees (TREE_ANIM) only | ||
| float VRAlphaTestThreshold; // Alpha test threshold for VR TREE_ANIM (0 = disabled) | ||
|
Comment on lines
+27
to
+31
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. VR settings should not be part of a flat cbuffer if possible. I forgot if we have our own cbuffer to pass through but we should make it conditional on being in VR.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed. The VR MIP bias fields (VRMipBias, VRMipBiasNearDist, VRMipBiasFarDist, VRMipBiasMode, VRAlphaTestThreshold) waste cbuffer space on flatrim. Action: Will move these to a VR-specific cbuffer or gate them so they don't occupy space in the shared SharedData cbuffer for SE/AE builds. |
||
| float2 pad0; | ||
| float4 AmbientSHR; | ||
| float4 AmbientSHG; | ||
| float4 AmbientSHB; | ||
|
|
@@ -52,7 +57,7 @@ namespace SharedData | |
| bool EnableShadows; | ||
| bool ExtendShadows; | ||
| bool EnableParallaxWarpingFix; | ||
| float1 pad0; | ||
| bool pad0; | ||
| }; | ||
|
|
||
| struct CubemapCreatorSettings | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,6 +19,10 @@ RWTexture2D<float4> NormalTAAMaskSpecularMaskRW : register(u1); | |
| RWTexture2D<float2> MotionVectorsRW : register(u2); | ||
| Texture2D<float> DepthTexture : register(t4); | ||
|
|
||
| #if defined(VR_STEREO_OPT) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Each define increases shader compile time. Another reason to consider whether this is truly different from VR's default define.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fair point. Could potentially reuse the existing VR define with a runtime cbuffer check instead of a compile-time define, to avoid doubling shader permutations. Will evaluate. |
||
| Texture2D<uint> StereoOptModeTexture : register(t16); | ||
| #endif | ||
|
|
||
| #if defined(DYNAMIC_CUBEMAPS) | ||
| Texture2D<float3> ReflectanceTexture : register(t5); | ||
| TextureCube<float3> EnvTexture : register(t6); | ||
|
|
@@ -92,6 +96,16 @@ void SampleSSGISpecular(uint2 pixCoord, sh2 lobe, inout float ao, out float3 il, | |
| uv *= FrameBuffer::DynamicResolutionParams2.xy; // adjust for dynamic res | ||
|
|
||
| uint eyeIndex = Stereo::GetEyeIndexFromTexCoord(uv); | ||
|
|
||
| #if defined(VR_STEREO_OPT) | ||
| if (eyeIndex == 1) { | ||
| uint mode = StereoOptModeTexture[uint2(dispatchID.xy)]; | ||
| if (mode == 2) { // MODE_MAIN — stencil-culled, no valid G-buffer | ||
| return; | ||
| } | ||
| } | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| #endif | ||
|
|
||
| uv = Stereo::ConvertFromStereoUV(uv, eyeIndex); | ||
|
|
||
| float3 normalGlossiness = NormalRoughnessTexture[dispatchID.xy]; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -203,8 +203,14 @@ PS_OUTPUT main(PS_INPUT input) | |
|
|
||
| float alpha = TexDiffuse.SampleBias(SampDiffuse, input.TexCoord.xy, SharedData::MipBias).w; | ||
|
|
||
| if ((alpha - AlphaTestRefRS) < 0) { | ||
| discard; | ||
| { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should've been a separate PR.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed. The DistantTree VR foliage changes (alpha threshold reduction, MIP bias) are separable from stereo optimizations. Will split to a dedicated VR foliage quality PR. |
||
| float alphaRef = AlphaTestRefRS; | ||
| # if defined(VR) | ||
| alphaRef -= eyeIndex * 0.1; | ||
| # endif | ||
| if ((alpha - alphaRef) < 0) { | ||
| discard; | ||
| } | ||
| } | ||
|
|
||
| psout.Diffuse.xyz = input.Depth.xxx / input.Depth.yyy; | ||
|
|
@@ -213,8 +219,14 @@ PS_OUTPUT main(PS_INPUT input) | |
| float4 baseColor = TexDiffuse.SampleBias(SampDiffuse, input.TexCoord.xy, SharedData::MipBias); | ||
| baseColor.xyz = Color::Diffuse(baseColor.xyz); | ||
|
|
||
| if ((baseColor.w - AlphaTestRefRS) < 0) { | ||
| discard; | ||
| { | ||
| float alphaRef = AlphaTestRefRS; | ||
| # if defined(VR) | ||
| alphaRef -= eyeIndex * 0.1; | ||
| # endif | ||
| if ((baseColor.w - alphaRef) < 0) { | ||
| discard; | ||
| } | ||
| } | ||
|
|
||
| # if defined(DEFERRED) | ||
|
|
||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this a stereo optimization or is this something else?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Two separate concerns mixed in here:
Action: Will split Lighting.hlsl changes — pixelOffset export stays with stereo opt PR, foliage alpha/MIP bias moves to a separate VR foliage PR along with DistantTree and RunGrass changes.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Feels like you have a VR foliage PR in here too.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes — there are VR foliage quality improvements mixed in (alpha threshold, MIP bias, fringe suppression). Will split these out to a separate VR foliage PR. The only Lighting.hlsl change that stays in this PR is the pixelOffset → Reflectance.w export for POM-aware stereo reprojection. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1780,7 +1780,19 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) | |
| rawRMAOS = blendedRMAOS; | ||
| # endif | ||
| # else // Non-landscape code | ||
| float4 rawBaseColor = TexColorSampler.SampleBias(SampColorSampler, diffuseUv, SharedData::MipBias); | ||
| // VR MIP bias: depth-gated sharpening for distant textures | ||
| // Mode 1 = All Textures, Mode 2 = Distant Trees (TREE_ANIM) only | ||
| float vrFoliageBias = 0; | ||
| # if defined(TREE_ANIM) | ||
| if (SharedData::VRMipBias < 0) { | ||
| # else | ||
| if (SharedData::VRMipBias < 0 && SharedData::VRMipBiasMode == 1) { | ||
| # endif | ||
| float linDepth = SharedData::GetScreenDepth(input.Position.z); | ||
| float t = saturate((linDepth - SharedData::VRMipBiasNearDist) / max(SharedData::VRMipBiasFarDist - SharedData::VRMipBiasNearDist, 1.0)); | ||
| vrFoliageBias = SharedData::VRMipBias * t; | ||
| } | ||
| float4 rawBaseColor = TexColorSampler.SampleBias(SampColorSampler, diffuseUv, SharedData::MipBias + vrFoliageBias); | ||
| baseColor = float4(Color::Diffuse(rawBaseColor.rgb), rawBaseColor.a); | ||
| float4 normalColor = TexNormalSampler.SampleBias(SampNormalSampler, uv, SharedData::MipBias); | ||
| normal = normalColor; | ||
|
|
@@ -3021,11 +3033,11 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) | |
| float alpha = baseColor.w; | ||
| # if defined(EMAT) && !defined(LANDSCAPE) | ||
| # if defined(PARALLAX) | ||
| alpha = TexColorSampler.SampleBias(SampColorSampler, uvOriginal, SharedData::MipBias).w; | ||
| alpha = TexColorSampler.SampleBias(SampColorSampler, uvOriginal, SharedData::MipBias + vrFoliageBias).w; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Based to have an ifdef to add this when VR is available so it doesn't pollute flatrim.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed. The vrFoliageBias MIP bias additions will be gated behind `#ifdef VR` and moved to the separate VR foliage PR. |
||
| # elif defined(TRUE_PBR) | ||
| [branch] if (PBRParallax) | ||
| { | ||
| alpha = TexColorSampler.SampleBias(SampColorSampler, uvOriginal, SharedData::MipBias).w; | ||
| alpha = TexColorSampler.SampleBias(SampColorSampler, uvOriginal, SharedData::MipBias + vrFoliageBias).w; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same re not polluting flat.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same — will VR-gate and move to foliage PR. |
||
| } | ||
| # endif | ||
| # endif | ||
|
|
@@ -3074,9 +3086,28 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) | |
| } | ||
| alpha = saturate(1.05 * alpha); | ||
| # endif // DEPTH_WRITE_DECALS | ||
| # if defined(TREE_ANIM) | ||
| // Fixed alpha floor — catch zombie texels with near-zero alpha | ||
| if (alpha < 0.1) { | ||
| discard; | ||
| } | ||
| if (alpha - AlphaTestRefRS < 0) { | ||
| discard; | ||
|
Comment on lines
+3089
to
+3095
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This branch hardcodes 🤖 Prompt for AI Agents
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is correct — VRAlphaTestThreshold is used in DistantTree.hlsl and RunGrass.hlsl but not in the Lighting.hlsl TREE_ANIM path. The Lighting TREE_ANIM path uses a different hardcoded approach (0.1 floor + fringe suppression). This will be addressed when we split the VR foliage changes to a separate PR — the threshold wiring in Lighting.hlsl will be part of that work.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Would you like me to open a follow-up GitHub issue to track the ✏️ Learnings added
🧠 Learnings used |
||
| } | ||
| // Suppress RGB fringe contamination from negative MIP bias. | ||
| // Low-alpha texels near the foliage boundary have bright padding bleeding into RGB. | ||
| // Alpha is a direct proxy for contamination — low alpha = more padding contribution. | ||
| // Scale correction by bias strength so close-range (no bias) textures are untouched. | ||
| if (vrFoliageBias < 0) { | ||
| float biasStrength = saturate(vrFoliageBias / min(SharedData::VRMipBias, -0.001)); | ||
| float fringeScale = 5.0; // higher = more aggressive fringe suppression | ||
| baseColor.rgb *= saturate(alpha * lerp(1.0, fringeScale, biasStrength)); | ||
| } | ||
| # else | ||
| if (alpha - AlphaTestRefRS < 0) { | ||
| discard; | ||
| } | ||
| # endif // TREE_ANIM | ||
| # endif // DO_ALPHA_TEST | ||
|
|
||
| # if defined(ANISOTROPIC_ALPHA) | ||
|
|
@@ -3166,7 +3197,12 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) | |
| } | ||
| # endif | ||
|
|
||
| psout.Reflectance = float4(indirectLobeWeights.specular, psout.Diffuse.w); | ||
| # if (defined(EMAT) || defined(TRUE_PBR)) && (defined(PARALLAX) || defined(LANDSCAPE)) | ||
| psout.Reflectance = float4(indirectLobeWeights.specular, | ||
| (pixelOffset > 0.0) ? saturate(pixelOffset) : 0.0); | ||
| # else | ||
| psout.Reflectance = float4(indirectLobeWeights.specular, 0.0); | ||
| # endif | ||
| psout.NormalGlossiness = float4(GBuffer::EncodeNormal(screenSpaceNormal), saturate(1.0 - material.Roughness), psout.Diffuse.w); | ||
|
|
||
| # if defined(SNOW) | ||
|
|
||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Break out please to another PR.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed. Will move RunGrass VR changes to the separate VR foliage PR. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| // AMD Contrast Adaptive Sharpening (CAS) - Sharpen-only for VR | ||
| // Based on AMD FidelityFX CAS (sharpen-only path) | ||
| // Reference: https://gpuopen.com/fidelityfx-cas/ | ||
| // | ||
| // Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. | ||
| // Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| // of this software and associated documentation files(the "Software"), to deal | ||
| // in the Software without restriction, including without limitation the rights | ||
| // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| // copies of the Software, and to permit persons to whom the Software is | ||
| // furnished to do so, subject to the following conditions: | ||
| // The above copyright notice and this permission notice shall be included in | ||
| // all copies or substantial portions of the Software. | ||
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
| // THE SOFTWARE. | ||
|
|
||
| // CASParams[0] = sharpness (0.0 = no sharpening, 1.0 = maximum sharpening) | ||
| StructuredBuffer<float> CASParams : register(t1); | ||
|
|
||
| Texture2D<float4> Source : register(t0); | ||
| RWTexture2D<float4> Dest : register(u0); | ||
|
|
||
| [numthreads(8, 8, 1)] void main(uint3 DTid : SV_DispatchThreadID) { | ||
| uint2 texDim; | ||
| Dest.GetDimensions(texDim.x, texDim.y); | ||
|
|
||
| if (DTid.x >= texDim.x || DTid.y >= texDim.y) | ||
| return; | ||
|
|
||
| float sharpness = CASParams[0]; | ||
|
|
||
| // Fetch 3x3 neighborhood | ||
| int2 sp = int2(DTid.xy); | ||
| float3 a = Source.Load(int3(sp + int2(-1, -1), 0)).rgb; | ||
| float3 b = Source.Load(int3(sp + int2(0, -1), 0)).rgb; | ||
| float3 c = Source.Load(int3(sp + int2(1, -1), 0)).rgb; | ||
| float3 d = Source.Load(int3(sp + int2(-1, 0), 0)).rgb; | ||
| float3 e = Source.Load(int3(sp, 0)).rgb; | ||
| float3 f = Source.Load(int3(sp + int2(1, 0), 0)).rgb; | ||
| float3 g = Source.Load(int3(sp + int2(-1, 1), 0)).rgb; | ||
| float3 h = Source.Load(int3(sp + int2(0, 1), 0)).rgb; | ||
| float3 i = Source.Load(int3(sp + int2(1, 1), 0)).rgb; | ||
|
|
||
| // Soft min/max of cross neighborhood | ||
| float3 mnRGB = min(min(min(d, e), min(f, b)), h); | ||
| float3 mxRGB = max(max(max(d, e), max(f, b)), h); | ||
|
|
||
| // Expand with diagonal neighbors for soft min/max | ||
| float3 mnRGB2 = min(min(a, c), min(g, i)); | ||
| float3 mxRGB2 = max(max(a, c), max(g, i)); | ||
| mnRGB += mnRGB2; | ||
| mxRGB += mxRGB2; | ||
|
|
||
| // Adaptive sharpening amount | ||
| float3 ampRGB = saturate(min(mnRGB, 2.0 - mxRGB) * rcp(max(mxRGB, 1e-4))); | ||
| ampRGB = rsqrt(ampRGB); | ||
|
|
||
| // Peak controls sharpening strength: | ||
| // sharpness 0.0 -> peak 8.0 (no sharpening) | ||
| // sharpness 1.0 -> peak 5.0 (maximum sharpening) | ||
| float peak = -3.0 * sharpness + 8.0; | ||
| float3 wRGB = -rcp(ampRGB * peak); | ||
| float3 rcpWeightRGB = rcp(4.0 * wRGB + 1.0); | ||
|
|
||
| // Apply sharpening filter | ||
| float3 outColor = saturate(((b + d) + (f + h)) * wRGB + e) * rcpWeightRGB; | ||
|
|
||
| Dest[DTid.xy] = float4(outColor, 1.0); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any situation where this wouldn't need to be installed for VR or disabled separately? Please note any CS features are likely to be demonetized by Nexus so there's minimal value to create a new feature to maintain a whole new Nexus page. If it will be part of the core, add it to the VR feature but you can have its files live in appropriate separate locations under VR to keep vr.cpp from becoming unwieldy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed — this should not be a separate feature. It will always be needed for VR and there's no reason to maintain a separate Nexus page.
Action: Will fold VRStereoOptimizations into the existing VR feature. Files will live under
features/VR/andsrc/Features/VR/to keep vr.cpp manageable, but settings and lifecycle will be part of the VR feature.