Skip to content

Gaussian splats write the scene depth - #9175

Merged
mvaligursky merged 1 commit into
mainfrom
mv-gsplat-scene-depth
Aug 14, 2026
Merged

Gaussian splats write the scene depth#9175
mvaligursky merged 1 commit into
mainfrom
mv-gsplat-scene-depth

Conversation

@mvaligursky

@mvaligursky mvaligursky commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Makes gaussian splats a producer of the scene depth texture, so the effects consuming it work in a splat scene with no proxy geometry of any kind.

Until now the depth those effects sample came from the depth prepass, which renders opaque meshes only - in a splat scene it produces nothing, so volumetric fog flooded the frame, DOF blurred everything uniformly, and SSAO had no surfaces. The workaround was to ship a proxy mesh alongside the capture and keep it out of the forward pass, as gaussian-splatting/depth-of-field does today. Splats cannot be rendered into the prepass instead, as a single hard depth per pixel is not what a cloud of semi transparent gaussians has.

What makes this cheap is that the forward pass already composites the splats with premultiplied blending, which accumulates Σ d·α·T when the depth is written next to the color - a transmittance weighted depth, for the cost of one extra attachment write and no second pass over the splats. That is exactly the soft depth a raymarched effect wants, and #9174 put the scene pass MRT and the SCENE_TEXTURE_DEPTH define in place for it.

Changes

  • The forward gsplat fragment chunks, GLSL and WGSL, call writeSceneTextureDepth under SCENE_TEXTURE_DEPTH. Dithered splats render as opaque, so the fragments which survive the dither write full coverage instead of their alpha.
  • vLinearDepth, previously generated for the prepass only, is generated for that define as well - in all three vertex chunks, hybrid included.
  • GSplatQuadRenderer and GSplatHybridRenderer set sceneTexturesWrite on the forward material from GsplatParams.sceneDepthWrite each frame. That only selects the blend state - the write itself is driven by the define - so following the scene setting frame to frame needs no shader rebuild.

Notes

  • Off by default: Scene#gsplat.sceneDepthWrite gates it, and CameraFrame.isSplatSceneDepthSupported(device) reports whether the device can do it at all.
  • Verified on both backends. With the flag on, the depth reads 1.32 at the nearest splat and covers 12.7% of the frame matching the silhouette; with it off the same pixels read the cleared far value.
  • The demo exercising this is not in here, so this can land on its own - it follows separately.

The scene depth texture had no producer for gaussian splats, so effects
consuming it - volumetric fog, DOF, SSAO - had nothing to stop at in a
splat only scene, and needed a proxy mesh in the depth prepass to work
at all. The forward pass already composites the splats with premultiplied
blending, which accumulates a transmittance weighted depth for free once
the depth is written alongside the color, so this needs no second pass
over the splats.

The forward gsplat chunks now write vLinearDepth to the scene depth
attachment when SCENE_TEXTURE_DEPTH is defined, which the camera sets
when the depth is being produced in the scene pass. The linear depth
varying, previously generated for the prepass only, is generated for
that define as well - in all three vertex chunks, including the hybrid
one. Dithered splats render as opaque, so the fragments surviving the
dither write full coverage rather than their alpha.

Both renderers set `sceneTexturesWrite` on the forward material from
`GsplatParams.sceneDepthWrite` per frame. That only selects the blend
state, as the write itself is driven by the define, so following the
scene setting from frame to frame costs no shader rebuild.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Build size report

This PR changes the size of the minified bundles.

Bundle Minified Gzip Brotli
playcanvas.min.js 2365.1 KB (+0.9 KB, +0.04%) 607.4 KB (+0.2 KB, +0.03%) 471.8 KB (+0.2 KB, +0.04%)
playcanvas.min.mjs 2362.4 KB (+0.9 KB, +0.04%) 606.4 KB (+0.2 KB, +0.03%) 471.1 KB (+0.1 KB, +0.02%)

@mvaligursky mvaligursky added the area: graphics Graphics related issue label Aug 14, 2026
@mvaligursky
mvaligursky merged commit 5d38b1f into main Aug 14, 2026
10 checks passed
@mvaligursky
mvaligursky deleted the mv-gsplat-scene-depth branch August 14, 2026 14:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: graphics Graphics related issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant