Skip to content

Fix shadow rendering for cockpits when raytracing is active - #7685

Open
The-E wants to merge 2 commits into
scp-fs2open:masterfrom
The-E:fix/vulkan-rt-cockpit-shadow-parity
Open

Fix shadow rendering for cockpits when raytracing is active#7685
The-E wants to merge 2 commits into
scp-fs2open:masterfrom
The-E:fix/vulkan-rt-cockpit-shadow-parity

Conversation

@The-E

@The-E The-E commented Aug 4, 2026

Copy link
Copy Markdown
Member

Summary

This branch fixes two problems with player cockpit rendering under Vulkan:

  1. Raytraced (RT) shadows on the cockpit showed as solid green.
  2. The ship hull could show through the cockpit model, because the two shared one depth buffer.

Fix 1: RT shadows on cockpits

The shadow acceleration structure (TLAS) did not include the cockpit model. It also
did not include OBJ_RAW_POF and OBJ_PROP objects. The shadow ray for the cockpit
pass used the wrong ray origin. In combination, these problems meant that cockpits were
not receiving shadows.

Changes:

  • Add the cockpit model to the TLAS gather. Add OBJ_RAW_POF and OBJ_PROP objects
    too. This matches the object set that the rasterized shadow path already uses.
  • Give each TLAS instance a ray-cull mask. Tag the viewer's own ship hull with a
    dedicated mask bit. Shadow rays exclude this bit outside the cockpit's own shading
    pass, so the hull does not shadow itself. ship_render_player_ship_casts_shadow_on_cockpit()
    still allows the hull to cast a shadow onto the cockpit during that pass.
  • Add a world-space offset to the shadow uniform block. The cockpit render pass uses
    a camera-relative frame, not true world space. The offset corrects the ray origin
    before the shader traces the shadow ray. shadow_cascade_params_bind() now takes
    this offset as a parameter, so the hull draw and the cockpit draw can each supply
    their own value.
  • Add shadow_cascade_params_bind_deferred(). This function removes duplicate
    offset-selection code from the OpenGL and Vulkan deferred-lighting passes.
  • Add ship_cockpit_render_offset(). This function computes the cockpit's rotated
    and swayed position in one place. The render path, the rasterized shadow path, and
    the TLAS path all call this function. The three paths cannot drift apart.

Fix 2: cockpit depth in Vulkan

OpenGL swaps the depth buffer before it draws the cockpit. The swap keeps the ship
hull's depth values away from the cockpit draw. The Vulkan backend did not do this
swap. The hull's depth values stayed in the depth buffer, so the hull could block
the view of the cockpit.

Changes:

  • Add a backup depth image to VulkanPostProcessor.
  • Add saveSceneDepth(). This function copies the scene depth into the backup image,
    then clears the live depth buffer for the cockpit draw.
  • Add restoreSceneDepth(). This function copies the backup image back after the
    cockpit draw. Later passes that read scene depth, for example the lightshaft pass,
    then read the correct scene depth.

Known gaps

  • The deferred lighting pass shades the whole G-buffer in one draw call. The pass
    can apply only one world-space offset per draw. This is correct for the common
    case. It is not correct when Cockpit_shares_coordinate_space is true, or when a
    ship has no cockpit model. This case needs a per-pixel offset, or the offset must
    move into the G-buffer data itself. This branch does not fix this case.
  • The cockpit's detail-box check in the TLAS walk uses Eye_position. The
    rasterized path uses a cockpit-relative eye position instead. The TLAS walk skips
    the detail-box check for the cockpit model, rather than compute the correct eye
    position.

Testing

  • ninja code and ninja Freespace2 build with no errors.
  • Confirmed in-game: player cockpit shadows now render correctly.

@wookieejedi wookieejedi added cleanup A modification or rewrite of code to make it more understandable or easier to maintain. graphics A feature or issue related to graphics (2d and 3d) labels Aug 4, 2026
@The-E
The-E force-pushed the fix/vulkan-rt-cockpit-shadow-parity branch from 840954d to 0181d81 Compare August 15, 2026 08:27
The-E and others added 2 commits August 15, 2026 12:57
The raytraced shadow TLAS included only ships, asteroids, and debris.
It now also includes the player's cockpit model and OBJ_RAW_POF /
OBJ_PROP objects, matching the object set the rasterized shadow path
already covers.

The viewer's own hull was self-shadowing the cockpit as solid green.
Each TLAS instance now carries a ray-cull mask, and the viewer hull
gets a dedicated mask bit so shadow rays can exclude it outside the
cockpit's own shading pass.

The cockpit render pass uses a camera-relative view frame, not true
world space, so a ray reconstructed from it needs a world-space
correction before tracing against the TLAS. shadow_cascade_params_bind()
now takes this offset (and a self-shadow flag) per bind, so the hull and
cockpit draws inside ship_render_player_ship() can each supply their own.

Also:
- Extract ship_player_cockpit_model_would_render() and
  ship_cockpit_render_offset() so the render, shadow, and TLAS paths
  share one definition of "is the cockpit rendering" and "where is it".
- Add shadow_cascade_params_bind_deferred() to remove the duplicated
  offset/count selection between the GL and Vulkan deferred-lighting
  backends.
- Bind the shadow-cascade uniform buffer with a full replace instead of
  an offset-append: this buffer is now bound several times per frame
  with different content, and an offset-append only bump-allocates on
  its first call per frame on Vulkan's streaming buffer.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The cockpit model renders around the camera, inside the ship hull the
scene pass already drew. OpenGL isolates the cockpit's depth from the
scene's by swapping the depth attachment to a separate texture for the
cockpit pass. The Vulkan backend left gr_post_process_save_zbuffer()
and gr_post_process_restore_zbuffer() as no-ops, so the cockpit shared
the scene's depth buffer and the hull incorrectly occluded it.

A Vulkan framebuffer owns its attachments, so it cannot swap them like
OpenGL does. Instead, VulkanPostProcessor::saveSceneDepth() copies the
scene depth into a new backup image and the live depth buffer is
cleared for the cockpit; restoreSceneDepth() copies the backup back so
later passes that sample scene depth (for example lightshafts) see the
scene, not the cockpit. VulkanRenderer::saveSceneDepth()/
restoreSceneDepth() end and resume the active render pass around each
copy.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@The-E
The-E force-pushed the fix/vulkan-rt-cockpit-shadow-parity branch from 0181d81 to 580c076 Compare August 15, 2026 11:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cleanup A modification or rewrite of code to make it more understandable or easier to maintain. graphics A feature or issue related to graphics (2d and 3d)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants