fix: fix vanilla vegetation normals direction#1469
Conversation
WalkthroughAdds FrameBuffer::ViewToWorld and updates normal computation in DistantTree, Lighting (TREE_ANIM PS path), and RunGrass to transform normals to view space, clamp the view-space z to a negative magnitude, then convert back to world space before normalization and lighting (≤50 words). Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant PS as Pixel Shader
participant FB as FrameBuffer (World/View helpers)
Note over PS: Normal adjustment flow (DistantTree, RunGrass, TREE_ANIM PS)
PS->>PS: compute initial worldNormal (cross/TBN column)
PS->>FB: WorldToView(worldNormal, is_position=false)
FB-->>PS: viewNormal
PS->>PS: viewNormal.z = -abs(viewNormal.z)
PS->>FB: ViewToWorld(viewNormal, is_position=false)
FB-->>PS: adjustedWorldNormal
PS->>PS: worldNormal = normalize(adjustedWorldNormal)
PS->>PS: use worldNormal for ambient/directional/IBL lighting
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~30 minutes Suggested reviewers
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Automated formatting by clang-format, prettier, and other hooks. See https://pre-commit.ci for details.
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
package/Shaders/Common/FrameBuffer.hlsli (1)
115-120: Prevent future footguns with direction-specific helpers.Optional: add
WorldToViewDir/ViewToWorldDirwrappers (w=0) and use them for normals/tangents to make misuse harder.float3 ViewToWorld(float3 x, bool is_position = true, uint a_eyeIndex = 0) { float4 newPosition = float4(x, (float)is_position); return mul(CameraViewInverse[a_eyeIndex], newPosition).xyz; } + +// Optional helpers to avoid accidental translation of directions +float3 WorldToViewDir(float3 v, uint a_eyeIndex = 0) { + return mul(CameraView[a_eyeIndex], float4(v, 0.0)).xyz; +} +float3 ViewToWorldDir(float3 v, uint a_eyeIndex = 0) { + return mul(CameraViewInverse[a_eyeIndex], float4(v, 0.0)).xyz; +}
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
package/Shaders/Common/FrameBuffer.hlsli(1 hunks)package/Shaders/DistantTree.hlsl(1 hunks)package/Shaders/Lighting.hlsl(1 hunks)package/Shaders/RunGrass.hlsl(1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{cpp,cxx,cc,c,h,hpp,hxx,hlsl,hlsli,fx,fxh,py}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Do not include TODO/FIXME placeholders; provide complete, working solutions
Files:
package/Shaders/Lighting.hlslpackage/Shaders/DistantTree.hlslpackage/Shaders/RunGrass.hlslpackage/Shaders/Common/FrameBuffer.hlsli
🧠 Learnings (2)
📚 Learning: 2025-08-05T17:40:44.828Z
Learnt from: ThePagi
PR: doodlum/skyrim-community-shaders#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.
Applied to files:
package/Shaders/Lighting.hlsl
📚 Learning: 2025-08-03T18:37:19.690Z
Learnt from: jiayev
PR: doodlum/skyrim-community-shaders#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.
Applied to files:
package/Shaders/DistantTree.hlsl
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: Validate shader compilation (VR, .github/configs/shader-validation-vr.yaml)
- GitHub Check: Build plugin and addons
- GitHub Check: Validate shader compilation (Flatrim, .github/configs/shader-validation.yaml)
🔇 Additional comments (1)
package/Shaders/Common/FrameBuffer.hlsli (1)
115-120: ViewToWorld addition looks correct and mirrors WorldToView.Matrix and packing usage are consistent with existing conventions.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
package/Shaders/DistantTree.hlsl (1)
236-239: Correct w=0 transforms; small cleanups + parity check
- Good: Using
is_position = falsefor World↔View fixes the translation-on-normal issue.- Nit:
.xyzswizzles are redundant forfloat3and add noise.- Optional: You can normalize only once at the end; view/world transforms are rigid for direction vectors.
Option A — minimal cleanup (remove redundant swizzles):
- float3 normal = -normalize(cross(ddx, ddy)); - normal.xyz = normalize(FrameBuffer::WorldToView(normal.xyz, false, eyeIndex)); + float3 normal = -normalize(cross(ddx, ddy)); + normal = normalize(FrameBuffer::WorldToView(normal, false, eyeIndex)); normal.z = -abs(normal.z); - normal.xyz = normalize(FrameBuffer::ViewToWorld(normal.xyz, false, eyeIndex)); + normal = normalize(FrameBuffer::ViewToWorld(normal, false, eyeIndex));Option B — micro-opt (single final normalize):
- float3 normal = -normalize(cross(ddx, ddy)); - normal = normalize(FrameBuffer::WorldToView(normal, false, eyeIndex)); + float3 normal = -cross(ddx, ddy); + normal = FrameBuffer::WorldToView(normal, false, eyeIndex); normal.z = -abs(normal.z); - normal = normalize(FrameBuffer::ViewToWorld(normal, false, eyeIndex)); + normal = FrameBuffer::ViewToWorld(normal, false, eyeIndex); + normal = normalize(normal);Verification: The non-DEFERRED path (Lines 267–271) still uses
normalize(cross(ddx, ddy))without the view-space Z clamp. Confirm if the same correction is desired there to keep vegetation lighting consistent across paths.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
package/Shaders/DistantTree.hlsl(1 hunks)package/Shaders/Lighting.hlsl(1 hunks)package/Shaders/RunGrass.hlsl(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- package/Shaders/RunGrass.hlsl
- package/Shaders/Lighting.hlsl
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{cpp,cxx,cc,c,h,hpp,hxx,hlsl,hlsli,fx,fxh,py}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Do not include TODO/FIXME placeholders; provide complete, working solutions
Files:
package/Shaders/DistantTree.hlsl
🧠 Learnings (2)
📚 Learning: 2025-08-03T18:37:19.690Z
Learnt from: jiayev
PR: doodlum/skyrim-community-shaders#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.
Applied to files:
package/Shaders/DistantTree.hlsl
📚 Learning: 2025-08-05T17:40:44.828Z
Learnt from: ThePagi
PR: doodlum/skyrim-community-shaders#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.
Applied to files:
package/Shaders/DistantTree.hlsl
|
✅ A pre-release build is available for this PR: |
Summary by CodeRabbit
Bug Fixes
New Features