Skip to content

fix: fix vanilla vegetation normals direction#1469

Merged
doodlum merged 4 commits into
devfrom
veggie-fix
Sep 13, 2025
Merged

fix: fix vanilla vegetation normals direction#1469
doodlum merged 4 commits into
devfrom
veggie-fix

Conversation

@doodlum
Copy link
Copy Markdown
Collaborator

@doodlum doodlum commented Sep 12, 2025

Summary by CodeRabbit

  • Bug Fixes

    • Improved normal orientation for animated trees, grass, and distant foliage to reduce shading flips, dark/light popping, and banding when moving the camera or in VR, yielding more stable visuals.
  • New Features

    • Added view↔world space conversion in shaders to enable more consistent and accurate lighting for vegetation and similar geometry.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Sep 12, 2025

Walkthrough

Adds 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

Cohort / File(s) Summary
FrameBuffer utilities
package/Shaders/Common/FrameBuffer.hlsli
Added float3 ViewToWorld(float3 x, bool is_position = true, uint a_eyeIndex = 0) that multiplies (x, is_position) by CameraViewInverse[a_eyeIndex] and returns xyz.
Distant tree normal handling
package/Shaders/DistantTree.hlsl
Replaced direct world-space normalize(cross(ddx, ddy)) with flow: WorldToView(normal, is_position=false) → z = -abs(z) → ViewToWorld(...) → normalize; used for ambient/directional/IBL branches.
Lighting (TREE_ANIM, PS path)
package/Shaders/Lighting.hlsl
Under #if defined(TREE_ANIM) in the pixel shader, added same view-space clamp flow for the TBN third column (tbnTr[2]): WorldToView → clamp z to negative → ViewToWorld → normalize.
Grass normal handling
package/Shaders/RunGrass.hlsl
Replaced direct cross(ddx, ddy) normal with: WorldToView → z = -abs(z) → ViewToWorld → clamp world z non-negative → normalize before lighting.

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
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Suggested reviewers

  • Pentalimbed

Poem

I twitched my whiskers at normals askew,
Hopped into view space, made z less than two—no, zero,
Back to world space with a matrix hop,
Leaves settle, sunlight finds the top.
A carrot wink — shaders hum, I stop. 🥕✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title succinctly and accurately summarizes the primary change—fixing vanilla vegetation normal directions in the shaders—and directly matches the modifications described in the diff; it is concise and reviewer-friendly despite the duplicated "fix" token.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch veggie-fix

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown

Using provided base ref: 6513c53
Using base ref: 6513c53
Base commit date: 2025-09-13T00:07:58+01:00 (Saturday, September 13, 2025 12:07 AM)
No actionable suggestions for changed features.

pre-commit-ci Bot and others added 2 commits September 12, 2025 23:27
Automated formatting by clang-format, prettier, and other hooks.
See https://pre-commit.ci for details.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

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/ViewToWorldDir wrappers (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

📥 Commits

Reviewing files that changed from the base of the PR and between 6513c53 and b9abb14.

📒 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.hlsl
  • package/Shaders/DistantTree.hlsl
  • package/Shaders/RunGrass.hlsl
  • package/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.

Comment thread package/Shaders/DistantTree.hlsl
Comment thread package/Shaders/Lighting.hlsl
Comment thread package/Shaders/RunGrass.hlsl Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

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 = false for World↔View fixes the translation-on-normal issue.
  • Nit: .xyz swizzles are redundant for float3 and 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

📥 Commits

Reviewing files that changed from the base of the PR and between c010462 and efb64fd.

📒 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

@github-actions
Copy link
Copy Markdown

✅ A pre-release build is available for this PR:
Download

@doodlum doodlum merged commit 05f5c40 into dev Sep 13, 2025
17 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request Jan 19, 2026
@alandtse alandtse deleted the veggie-fix branch February 6, 2026 05:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant