Skip to content

bugfix(water): Fix river borders appearing darkened under shroud #2592

Open
afc-afc0 wants to merge 3 commits intoTheSuperHackers:mainfrom
afc-afc0:bugfix/river-borders-darkened-under-shroud
Open

bugfix(water): Fix river borders appearing darkened under shroud #2592
afc-afc0 wants to merge 3 commits intoTheSuperHackers:mainfrom
afc-afc0:bugfix/river-borders-darkened-under-shroud

Conversation

@afc-afc0
Copy link
Copy Markdown

Fixes #2364

Description River borders appear darkened when under shroud, creating visual artifacts that force map makers to place rocks and other objects to hide them.

Root Cause

River water rendering in drawRiverWater() applies the shroud as a separate multiplicative pass on the framebuffer after the
water is already alpha-blended onto the terrain. This causes double-darkening at transparent river edges:

  1. Terrain renders with its own shroud: pixel = terrain × shroud
  2. River alpha-blends on top: pixel = α × water + (1-α) × terrain × shroud
  3. River's multiplicative shroud pass darkens the entire framebuffer: pixel = shroud × (α × water + (1-α) × terrain × shroud)

The terrain contribution at river borders becomes terrain × shroud² instead of terrain × shroud — visibly darker than
surrounding terrain. This is only noticeable under shroud (when shroud < 1.0), since 1.0² = 1.0.

Note: Trapezoid water (lakes/ponds) avoids this by applying shroud in texture stage 3 of the main pass when pixel shaders are
available. River water cannot do this because stage 3 is already used for the river alpha edge texture.

Fix

Applies shroud per-vertex by looking up the shroud level at each river vertex's world position and multiplying it into the vertex
diffuse color before alpha blending. This ensures only the water's color contribution is shroud-modulated:

pixel = α × (water × shroud) + (1-α) × (terrain × shroud) = shroud × (α × water + (1-α) × terrain)

The separate multiplicative shroud pass is removed, which also eliminates one draw call per river.

Testing

Tested with the minimal reproduction map (Ariver.zip from #2364) — river borders no longer show dark outlines under shroud.

The river shroud was applied as a separate multiplicative pass on the
framebuffer, which double-darkened terrain showing through transparent
river edges. Apply shroud per-vertex in the diffuse color instead, so
only the water contribution is shrouded before alpha blending.
@greptile-apps
Copy link
Copy Markdown

greptile-apps bot commented Apr 12, 2026

Greptile Summary

This PR fixes river border double-darkening under shroud by replacing the post-draw multiplicative shroud pass with per-vertex shroud modulation. The new getRiverVertexDiffuse() helper looks up the shroud level at each vertex's world position and bakes it into the vertex diffuse colour before drawing, which eliminates the extra draw call and the shroud² darkening artefact at alpha-blended river edges.

The coordinate conversion (worldX / cellWidth) has been verified to be consistent with the engine's partition manager, which uses the same origin (lo.x = 0.0f). The vertex buffer is BUFFER_TYPE_DYNAMIC_DX8, so shroud values are refreshed every frame.

Confidence Score: 5/5

Safe to merge — the fix is correct, well-scoped, and removes a draw call as a bonus.

No P0/P1 issues found. The colour-channel ordering in GameMakeColor matches the original diffuse layout. The world-to-cell conversion (worldX / cellWidth) is consistent with the engine's partition manager coordinate system (lo.x = 0). The dynamic vertex buffer is rebuilt every frame so shroud changes are always reflected. The null guard for TheTerrainRenderObject is an improvement over the old code, which dereferenced it unconditionally.

No files require special attention.

Important Files Changed

Filename Overview
Core/GameEngineDevice/Source/W3DDevice/GameClient/Water/W3DWater.cpp Adds per-vertex shroud modulation for river water and removes the old multiplicative shroud draw pass; logic and colour-channel ordering are correct.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[drawRiverWater called] --> B[Compute shadeR/G/B and diffuse from lighting]
    B --> C{TheTerrainRenderObject not null?}
    C -->|Yes| D[shroud = getShroud]
    C -->|No| E[shroud = nullptr]
    D --> F[Build dynamic vertex buffer per frame]
    E --> F
    F --> G[For each river vertex]
    G --> H[getRiverVertexDiffuse]
    H --> I{shroud not null?}
    I -->|No| J[return original diffuse]
    I -->|Yes| K[cellX = worldX / cellWidth]
    K --> L[getShroudLevel returns 0-255]
    L --> M[shroudScale = level / 255.0f]
    M --> N[GameMakeColor with scaled RGB and original alpha]
    N --> O[vb-diffuse = modulated colour]
    J --> O
    O --> P{More vertices?}
    P -->|Yes| G
    P -->|No| Q[Single draw call for river triangles]
    Q --> R[Second multiplicative shroud pass REMOVED]
Loading

Reviews (3): Last reviewed commit: "Address PR Review" | Re-trigger Greptile

@DevGeniusCode DevGeniusCode added GUI For graphical user interface Major Severity: Minor < Major < Critical < Blocker Rendering Is Rendering related labels Apr 12, 2026
@stephanmeesters
Copy link
Copy Markdown

Did a little pre- post comparison, the results are interesting, this is using the map Winding River:

2026-04-13.00-23-21.mp4

You can try by adding this commit:
stephanmeesters@c63851d

Comment thread Core/GameEngineDevice/Source/W3DDevice/GameClient/Water/W3DWater.cpp Outdated
Comment thread Core/GameEngineDevice/Source/W3DDevice/GameClient/Water/W3DWater.cpp Outdated
Comment thread Core/GameEngineDevice/Source/W3DDevice/GameClient/Water/W3DWater.cpp Outdated
Comment thread Core/GameEngineDevice/Source/W3DDevice/GameClient/Water/W3DWater.cpp Outdated
Comment thread Core/GameEngineDevice/Source/W3DDevice/GameClient/Water/W3DWater.cpp Outdated
Comment thread Core/GameEngineDevice/Source/W3DDevice/GameClient/Water/W3DWater.cpp Outdated
Comment thread Core/GameEngineDevice/Source/W3DDevice/GameClient/Water/W3DWater.cpp Outdated
@xezon xezon added Minor Severity: Minor < Major < Critical < Blocker Gen Relates to Generals ZH Relates to Zero Hour and removed Major Severity: Minor < Major < Critical < Blocker labels Apr 14, 2026
@xezon xezon added Bug Something is not working right, typically is user facing and removed GUI For graphical user interface labels Apr 15, 2026
Copy link
Copy Markdown

@Mauller Mauller left a comment

Choose a reason for hiding this comment

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

Good to me too

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Bug Something is not working right, typically is user facing Gen Relates to Generals Minor Severity: Minor < Major < Critical < Blocker Rendering Is Rendering related ZH Relates to Zero Hour

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Rendering issue where river borders appear darkened under shroud

5 participants