Add depth-of-field to WebP renders#248
Merged
Merged
Conversation
# 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
Contributor
There was a problem hiding this comment.
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_FLOORin 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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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-stopto world scale. Default0.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|, withfocal_real = (sensor_size / 2) / tan(fovY/2). The writer pre-bakesapertureScale = 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-stopis omitted,apertureScale = 0and renders are byte-identical to before.Equirect: all three new flags error out cleanly with
--projection equirect, matching the existing--fovguard.Bonus rasterizer fix: added gaussian-edge alpha compensation in
rasterize-binned.ts— subtractGAUSSIAN_FLOOR = exp(−SIGMA_CUTOFF²/2)fromexp(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.