Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,7 @@ namespace ExtendedMaterials
float2 GetParallaxCoords(float distance, float2 coords, float mipLevel, float3 viewDir, float3x3 tbn, float noise, Texture2D<float4> tex, SamplerState texSampler, uint channel, DisplacementParams params, out float pixelOffset)
#endif
{
pixelOffset = 0;
float3 viewDirTS = normalize(mul(tbn, viewDir));
#if defined(LANDSCAPE)
viewDirTS.xy /= viewDirTS.z * 0.7 + 0.3 + params[0].FlattenAmount; // Fix for objects at extreme viewing angles
Expand Down Expand Up @@ -496,7 +497,7 @@ namespace ExtendedMaterials
#endif
nearBlendToFar *= nearBlendToFar;
float offset = (1.0 - parallaxAmount) * -maxHeight + minHeight;
pixelOffset = lerp(parallaxAmount * scale, 0, nearBlendToFar);
pixelOffset = saturate(lerp(parallaxAmount, 0.5, nearBlendToFar));
return lerp(viewDirTS.xy * offset + coords.xy, coords, nearBlendToFar);
}

Expand Down
Copy link
Copy Markdown
Collaborator

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.

Copy link
Copy Markdown
Contributor Author

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/ and src/Features/VR/ to keep vr.cpp manageable, but settings and lifecycle will be part of the VR feature.

Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[Info]
Version = 1-0-0
17 changes: 11 additions & 6 deletions package/Shaders/Common/SharedData.hlsli
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The 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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The 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;
Expand Down Expand Up @@ -52,7 +57,7 @@ namespace SharedData
bool EnableShadows;
bool ExtendShadows;
bool EnableParallaxWarpingFix;
float1 pad0;
bool pad0;
};

struct CubemapCreatorSettings
Expand Down
15 changes: 15 additions & 0 deletions package/Shaders/Common/VR.hlsli
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ cbuffer VRValues : register(b13)
float2 EyeOffsetScale : packoffset(c0.z);
float4 EyeClipEdge[2] : packoffset(c1);
}

#endif

namespace Stereo
Expand Down Expand Up @@ -626,6 +627,20 @@ namespace Stereo
vsout.VRPosition.z = clipPos.z;
vsout.VRPosition.w = clipPos.w;

// Sub-pixel diagonal jitter for Eye 1 stereo edge supersampling.
// Shifts Eye 1 rasterization by ~0.75px so alpha-tested edges (tree branches,
// fences) sample at slightly different positions than Eye 0, giving StereoBlend
// reprojection better edge detail to work with.
// Hardcoded NDC values: FrameBuffer/SharedData cbuffers are not reliably
// available in all vertex shader contexts (VSHADER section only includes
// FrameBuffer.hlsli, and BufferDim lives in SharedData). These constants
// give ~0.75px offset at the 6304x3088 SBS reference resolution and scale
// proportionally at other resolutions since NDC is resolution-relative.
if (a_eyeIndex == 1) {
static const float2 kJitterNDC = float2(1.68e-4, -3.44e-4);
vsout.VRPosition.xy += kJitterNDC * vsout.VRPosition.w;
}

vsout.ClipDistance = clipEdges.y;
vsout.CullDistance = clipEdges.x;
# endif // VR
Expand Down
14 changes: 14 additions & 0 deletions package/Shaders/DeferredCompositeCS.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ RWTexture2D<float4> NormalTAAMaskSpecularMaskRW : register(u1);
RWTexture2D<float2> MotionVectorsRW : register(u2);
Texture2D<float> DepthTexture : register(t4);

#if defined(VR_STEREO_OPT)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The 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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The 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);
Expand Down Expand Up @@ -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;
}
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
#endif

uv = Stereo::ConvertFromStereoUV(uv, eyeIndex);

float3 normalGlossiness = NormalRoughnessTexture[dispatchID.xy];
Expand Down
20 changes: 16 additions & 4 deletions package/Shaders/DistantTree.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -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;
{
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should've been a separate PR.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The 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;
Expand All @@ -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)
Expand Down
44 changes: 40 additions & 4 deletions package/Shaders/Lighting.hlsl
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a stereo optimization or is this something else?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two separate concerns mixed in here:

  1. pixelOffset export to Reflectance.w — This IS stereo optimization (feeds POM depth correction into StereoBlendCS)
  2. VR foliage alpha threshold / MIP bias — This is VR visual quality, not stereo optimization

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.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feels like you have a VR foliage PR in here too.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The 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
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The 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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The 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;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same re not polluting flat.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same — will VR-gate and move to foliage PR.

}
# endif
# endif
Expand Down Expand Up @@ -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
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot Mar 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

VRAlphaTestThreshold is still a no-op for TREE_ANIM.

This branch hardcodes 0.1 and AlphaTestRefRS, so the new SharedData::VRAlphaTestThreshold plumbing never participates in foliage rejection. The setting added by this PR will not affect tree alpha testing.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@package/Shaders/Lighting.hlsl` around lines 3089 - 3095, In the TREE_ANIM
branch the alpha test is hardcoded and ignores the new
SharedData::VRAlphaTestThreshold; update the two checks so they use the shared
threshold instead of 0.1 and AlphaTestRefRS — e.g. replace "if (alpha < 0.1)"
and "if (alpha - AlphaTestRefRS < 0)" with a single consistent comparison
against SharedData.VRAlphaTestThreshold (or use if (alpha < max(AlphaTestRefRS,
SharedData.VRAlphaTestThreshold)) to preserve the existing AlphaTestRefRS
floor), referencing the SharedData::VRAlphaTestThreshold symbol so foliage
rejection honors the new setting.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The 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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vrnord, understood — thanks for the clear explanation. The hardcoded 0.1 floor + fringe suppression in the TREE_ANIM path is intentionally decoupled from VRAlphaTestThreshold for now, with the wiring planned for a dedicated VR foliage PR.

Would you like me to open a follow-up GitHub issue to track the VRAlphaTestThreshold wiring into Lighting.hlsl's TREE_ANIM path so it doesn't get lost?


✏️ Learnings added
Learnt from: vrnord
Repo: doodlum/skyrim-community-shaders PR: 1982
File: package/Shaders/Lighting.hlsl:3089-3095
Timestamp: 2026-03-16T13:13:06.980Z
Learning: In `package/Shaders/Lighting.hlsl` (doodlum/skyrim-community-shaders), the `TREE_ANIM` alpha test path intentionally uses a hardcoded `0.1` floor check (`if (alpha < 0.1) discard;`) and `AlphaTestRefRS` rather than `SharedData::VRAlphaTestThreshold`. `VRAlphaTestThreshold` is wired into `DistantTree.hlsl` and `RunGrass.hlsl` only. The threshold wiring for `Lighting.hlsl`'s `TREE_ANIM` path is deferred to a separate VR foliage PR.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: jiayev
Repo: doodlum/skyrim-community-shaders PR: 1947
File: package/Shaders/DeferredCompositeCS.hlsl:234-243
Timestamp: 2026-03-14T08:35:37.608Z
Learning: In `package/Shaders/DeferredCompositeCS.hlsl` (doodlum/skyrim-community-shaders), the fallback non-IBL SKYLIGHTING specular path intentionally double-weights with `skylightingSpecular`: `dalcScaled` is computed as `IrradianceToGamma(IrradianceToLinear(directionalAmbientColorSpecular) * skylightingSpecular)`, and the final `finalIrradiance` is then `lerp(specularIrradiance, specularIrradianceReflections, skylightingSpecular)`. This is original engine logic preserved from before PR `#1947` and should not be flagged as a double-attenuation bug.

Learnt from: ThePagi
Repo: doodlum/skyrim-community-shaders PR: 1369
File: package/Shaders/Lighting.hlsl:0-0
Timestamp: 2025-08-05T17:40:44.828Z
Learning: In the skyrim-community-shaders repository, ultra trees (object LOD trees) are detected using a compound shader define condition `defined(DO_ALPHA_TEST) && defined(LOD_BLENDING) && defined(RIM_LIGHTING) && defined(SOFT_LIGHTING)` because "they have no define" according to the comment. The `ExtraFlags::IsTree` flag is used for different tree handling (AO removal in skylighting) and may not apply to ultra trees specifically. Before replacing the compound condition with `IsTree`, verification is needed to ensure the flag covers ultra trees.

Learnt from: jiayev
Repo: doodlum/skyrim-community-shaders PR: 0
File: :0-0
Timestamp: 2025-07-18T15:21:03.641Z
Learning: In the skyrim-community-shaders rendering pipeline, materials with alpha < 1 or alpha blending enabled are rendered in non-deferred mode rather than deferred mode. This means issues with dynamic cubemaps on transparent materials are actually non-deferred rendering issues.

Learnt from: jiayev
Repo: doodlum/skyrim-community-shaders PR: 0
File: :0-0
Timestamp: 2025-08-03T18:37:19.690Z
Learning: ISReflectionsRayTracing.hlsl and ISWorldMap.hlsl in the skyrim-community-shaders repository are image-space post-processing shaders that perform color sampling and blending operations that need proper linear color space handling for the linear lighting system. ISReflectionsRayTracing handles screen-space reflections and should use conditional Color::IrradianceToLinear/Gamma conversions similar to ISCompositeLensFlareVolumetricLighting.hlsl. ISWorldMap performs 7x7 color accumulation that should be done in linear space similar to the pattern used in ISSAOComposite.hlsl.

Learnt from: jiayev
Repo: doodlum/skyrim-community-shaders PR: 1947
File: features/Dynamic Cubemaps/Shaders/DynamicCubemaps/DynamicCubemaps.hlsli:107-115
Timestamp: 2026-03-14T08:34:58.304Z
Learning: In `features/Dynamic Cubemaps/Shaders/DynamicCubemaps/DynamicCubemaps.hlsli`, the pattern of scaling `directionalAmbientColorSpecular` by `skylightingSpecular` (via `dalcScaled`) before the final `lerp(specularIrradiance, specularIrradianceReflections, skylightingSpecular)` is intentional pre-existing behavior preserved from the original implementation. Do not flag this as double-attenuation; the combined attenuation is by design.

Learnt from: Dlizzio
Repo: doodlum/skyrim-community-shaders PR: 1966
File: package/Shaders/ISVolumetricLightingGenerateCS.hlsl:0-0
Timestamp: 2026-03-11T08:05:10.801Z
Learning: In doodlum/skyrim-community-shaders, the `sqrt()` applied to `CloudShadows::GetCloudShadowMult()` in `package/Shaders/ISVolumetricLightingGenerateCS.hlsl` is intentional. The maintainer (Dlizzio) confirmed it is used to increase cloud shadow strength at harsh angles for volumetric lighting, and should not be removed or replaced with a direct linear multiply.

Learnt from: alandtse
Repo: doodlum/skyrim-community-shaders PR: 1858
File: src/FrameAnnotations.cpp:1022-1024
Timestamp: 2026-02-21T22:01:15.534Z
Learning: In the skyrim-community-shaders repository, for the `REL::Relocate` function, when the VR offset is not explicitly provided as a third argument, it defaults to using the first parameter (SE offset) as the VR offset. This means `REL::Relocate(0x37f, 0)` will use `0x37f` for both SE and VR, with AE getting `0`.

Learnt from: CR
Repo: doodlum/skyrim-community-shaders PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-02-25T04:43:36.075Z
Learning: Applies to src/**/*.{h,hpp,cpp} : Ensure code changes work across SE/AE/VR Skyrim variants using runtime detection and `REL::RelocateMember()` patterns for compatibility

Learnt from: Dlizzio
Repo: doodlum/skyrim-community-shaders PR: 1950
File: package/Shaders/Lighting.hlsl:2008-2012
Timestamp: 2026-03-07T00:53:21.267Z
Learning: In doodlum/skyrim-community-shaders PR `#1950`, the maintainer (Dlizzio) confirmed that per-axis normal reorientation for triplanar-projected normals/detail in package/Shaders/Lighting.hlsl is not required for their use case; the current approach intentionally mixes samples without axis-specific swizzle/sign correction.

Learnt from: CR
Repo: doodlum/skyrim-community-shaders PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-02-25T04:43:36.075Z
Learning: Applies to src/Features/**/*.{h,hpp,cpp,hlsl} : Consider GPU workload and performance impact when implementing graphics features, with special attention to shader compilation and runtime performance

}
// 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)
Expand Down Expand Up @@ -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)
Expand Down
51 changes: 40 additions & 11 deletions package/Shaders/RunGrass.hlsl
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Break out please to another PR.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The 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
Expand Up @@ -480,22 +480,37 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace)
bool complex = abs(complexLength - 1.0) < SharedData::grassLightingSettings.ComplexGrassThreshold;
# endif // !TRUE_PBR

// VR MIP bias: depth-gated sharpening for distant textures
float vrGrassBias = 0;
if (SharedData::VRMipBias < 0 && SharedData::VRMipBiasMode == 1) {
float linDepth = SharedData::GetScreenDepth(input.HPosition.z);
float t = saturate((linDepth - SharedData::VRMipBiasNearDist) / max(SharedData::VRMipBiasFarDist - SharedData::VRMipBiasNearDist, 1.0));
vrGrassBias = SharedData::VRMipBias * t;
}

float4 baseColor;
# if !defined(TRUE_PBR)
if (complex) {
baseColor = TexBaseSampler.SampleBias(SampBaseSampler, float2(input.TexCoord.x, input.TexCoord.y * 0.5), SharedData::MipBias);
baseColor = TexBaseSampler.SampleBias(SampBaseSampler, float2(input.TexCoord.x, input.TexCoord.y * 0.5), SharedData::MipBias + vrGrassBias);
} else
# endif // !TRUE_PBR
{
baseColor = TexBaseSampler.SampleBias(SampBaseSampler, input.TexCoord.xy, SharedData::MipBias);
baseColor = TexBaseSampler.SampleBias(SampBaseSampler, input.TexCoord.xy, SharedData::MipBias + vrGrassBias);
}

baseColor.xyz = Color::Diffuse(baseColor.xyz);

# if defined(RENDER_DEPTH)
float diffuseAlpha = input.VertexColor.w * baseColor.w;
if ((diffuseAlpha - AlphaTestRefRS) < 0) {
discard;
{
float alphaRef = AlphaTestRefRS;
# if defined(VR)
uint convergenceEyeIndex = Stereo::GetEyeIndexPS(input.HPosition, VPOSOffset);
alphaRef -= convergenceEyeIndex * 0.1;
# endif
if ((diffuseAlpha - alphaRef) < 0) {
discard;
}
}
# endif // RENDER_DEPTH || DO_ALPHA_TEST

Expand All @@ -505,9 +520,9 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace)
psout.PS.w = diffuseAlpha;
# else
# if !defined(TRUE_PBR)
float4 specColor = complex ? TexBaseSampler.SampleBias(SampBaseSampler, float2(input.TexCoord.x, 0.5 + input.TexCoord.y * 0.5), SharedData::MipBias) : 1;
float4 specColor = complex ? TexBaseSampler.SampleBias(SampBaseSampler, float2(input.TexCoord.x, 0.5 + input.TexCoord.y * 0.5), SharedData::MipBias + vrGrassBias) : 1;
# else
float4 specColor = TexNormalSampler.SampleBias(SampNormalSampler, input.TexCoord.xy, SharedData::MipBias);
float4 specColor = TexNormalSampler.SampleBias(SampNormalSampler, input.TexCoord.xy, SharedData::MipBias + vrGrassBias);
# endif

uint eyeIndex = Stereo::GetEyeIndexPS(input.HPosition, VPOSOffset);
Expand Down Expand Up @@ -548,7 +563,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace)
# endif // !TRUE_PBR

# if defined(TRUE_PBR)
float4 rawRMAOS = TexRMAOSSampler.SampleBias(SampRMAOSSampler, input.TexCoord.xy, SharedData::MipBias) * float4(PBRParams1.x, 1, 1, PBRParams1.y);
float4 rawRMAOS = TexRMAOSSampler.SampleBias(SampRMAOSSampler, input.TexCoord.xy, SharedData::MipBias + vrGrassBias) * float4(PBRParams1.x, 1, 1, PBRParams1.y);

PBR::SurfaceProperties pbrSurfaceProperties = PBR::InitSurfaceProperties();

Expand Down Expand Up @@ -846,13 +861,27 @@ PS_OUTPUT main(PS_INPUT input)
{
PS_OUTPUT psout;

float4 baseColor = TexBaseSampler.SampleBias(SampBaseSampler, input.TexCoord.xy, SharedData::MipBias);
// VR MIP bias: depth-gated sharpening for distant textures
float vrGrassBias = 0;
if (SharedData::VRMipBias < 0 && SharedData::VRMipBiasMode == 1) {
float linDepth = SharedData::GetScreenDepth(input.HPosition.z);
float t = saturate((linDepth - SharedData::VRMipBiasNearDist) / max(SharedData::VRMipBiasFarDist - SharedData::VRMipBiasNearDist, 1.0));
vrGrassBias = SharedData::VRMipBias * t;
}

float4 baseColor = TexBaseSampler.SampleBias(SampBaseSampler, input.TexCoord.xy, SharedData::MipBias + vrGrassBias);

# if defined(RENDER_DEPTH)
float diffuseAlpha = input.VertexColor.w * baseColor.w;

if ((diffuseAlpha - AlphaTestRefRS) < 0) {
discard;
{
float alphaRef = AlphaTestRefRS;
# if defined(VR)
uint convergenceEyeIndex = Stereo::GetEyeIndexPS(input.HPosition, VPOSOffset);
alphaRef -= convergenceEyeIndex * 0.1;
# endif
if ((diffuseAlpha - alphaRef) < 0) {
discard;
}
}
# endif // RENDER_DEPTH || DO_ALPHA_TEST

Expand Down
74 changes: 74 additions & 0 deletions package/Shaders/VR/CASCS.hlsl
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);
}
Loading
Loading