Skip to content

Add depth-of-field to WebP renders#248

Merged
slimbuck merged 11 commits into
playcanvas:mainfrom
slimbuck:defocus-dev
May 26, 2026
Merged

Add depth-of-field to WebP renders#248
slimbuck merged 11 commits into
playcanvas:mainfrom
slimbuck:defocus-dev

Conversation

@slimbuck
Copy link
Copy Markdown
Member

Adds physically-correct defocus blur to the WebP output path. Each gaussian's 2D covariance is dilated by its own circle-of-confusion in the project shader, with an energy-preserving alpha rescale so defocused foreground splats don't over-occlude what's behind them. Because gaussian splats are already gaussian blobs, the result is real bokeh without a depth buffer or post-process pass — one shader edit plus uniform plumbing.

New CLI flags (pinhole only):

  • --f-stop <N> — aperture as a photographic f-stop (e.g. 2.8, 5.6, 11). Default: disabled.
  • --focus-distance <z> — camera-space Z of the focus plane in world units. Defaults to the distance to --look-at.
  • --sensor-size <n> — vertical sensor height in world units. Calibrates --f-stop to world scale. Default 0.024 (35mm full-frame in meters); scale to suit non-meter scenes.

Math: thin-lens CoC, CoC_pixels = focal_real × focal_pixels / (N × focus) × |1 − focus/cz|, with focal_real = (sensor_size / 2) / tan(fovY/2). The writer pre-bakes apertureScale = focal_real × focal_pixels / (N × focus) so the shader stays a single multiply-and-subtract: coc = apertureScale × |1 − focus/cz|; cov00,cov11 += coc². When --f-stop is omitted, apertureScale = 0 and renders are byte-identical to before.

Equirect: all three new flags error out cleanly with --projection equirect, matching the existing --fov guard.

Bonus rasterizer fix: added gaussian-edge alpha compensation in rasterize-binned.ts — subtract GAUSSIAN_FLOOR = exp(−SIGMA_CUTOFF²/2) from exp(power) so each splat reaches alpha 0 exactly at the 3σ truncation radius instead of clipping at ~1.1%. This eliminates faint ring artifacts at splat boundaries (more visible with DoF, since dilation expands the truncated footprint) and matches the PlayCanvas engine. Render goldens regenerated.

slimbuck added 10 commits May 26, 2026 10:36
# Conflicts:
#	README.md
#	src/cli/index.ts
#	src/lib/gpu/shaders/chunks/constants.ts
#	src/lib/gpu/shaders/project.ts
#	src/lib/gpu/shaders/rasterize-binned.ts
#	src/lib/gpu/shaders/uniforms.ts
#	src/lib/render/camera.ts
#	src/lib/render/raster-pass.ts
#	src/lib/writers/write-image.ts
@slimbuck slimbuck requested a review from Copilot May 26, 2026 13:30
@slimbuck slimbuck self-assigned this May 26, 2026
@slimbuck slimbuck added the enhancement New feature or request label May 26, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds depth-of-field (defocus blur) support to the GPU rasterized WebP render path by plumbing thin-lens parameters from CLI/options into the camera/rasterizer uniforms and applying per-splat CoC-based covariance dilation in the project shader, plus an edge-alpha compensation tweak to remove faint ring artifacts at the Gaussian truncation boundary.

Changes:

  • Add new image-render options/CLI flags (--f-stop, --focus-distance, --sensor-size) and pass them through to the render camera/rasterizer uniforms.
  • Implement DoF in the projection shader by dilating each splat’s 2D covariance by its circle-of-confusion and rescaling alpha to preserve energy.
  • Fix splat-edge ring artifacts by subtracting GAUSSIAN_FLOOR in the binned rasterizer alpha evaluation.

Reviewed changes

Copilot reviewed 13 out of 15 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/lib/writers/write-image.ts Adds DoF-related writer options, validation/guards, and derives focusDistance + apertureScale for the camera.
src/lib/write.ts Wires new render options through the generic write pipeline into writeImage.
src/lib/types.ts Exposes new render option fields (renderFStop, renderFocusDistance, renderSensorSize).
src/lib/render/raster-pass.ts Passes focusDistance and apertureScale into the GPU rasterizer options/uniforms.
src/lib/render/config.ts Introduces GAUSSIAN_FLOOR constant derived from SIGMA_CUTOFF.
src/lib/render/camera.ts Extends RenderCamera with optional DoF parameters (focusDistance, apertureScale).
src/lib/gpu/shaders/uniforms.ts Extends the shared Uniforms struct + JS layout with DoF uniforms.
src/lib/gpu/shaders/project.ts Adds per-splat CoC dilation and energy-preserving alpha scaling (pinhole only).
src/lib/gpu/shaders/rasterize-binned.ts Applies GAUSSIAN_FLOOR subtraction to eliminate truncation-edge rings.
src/lib/gpu/shaders/chunks/constants.ts Injects GAUSSIAN_FLOOR into WGSL constants chunk.
src/lib/gpu/gpu-splat-rasterizer.ts Adds uniform buffer parameter plumbing for focusDistance/apertureScale.
src/cli/index.ts Adds CLI flags, parsing, validation, and help text for DoF controls.
README.md Documents new CLI flags and adds usage examples.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/lib/gpu/shaders/project.ts
Comment thread src/lib/writers/write-image.ts
@slimbuck slimbuck marked this pull request as ready for review May 26, 2026 13:52
@slimbuck slimbuck requested a review from a team May 26, 2026 13:53
@slimbuck slimbuck merged commit 958b004 into playcanvas:main May 26, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants