diff --git a/examples/depth-of-field/index.html b/examples/depth-of-field/index.html
new file mode 100644
index 0000000..e7fd745
--- /dev/null
+++ b/examples/depth-of-field/index.html
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
Spark • Depth of Field
+
+
+
+
+
+
+
+
+
diff --git a/index.html b/index.html
index 908f5ca..c241b3a 100644
--- a/index.html
+++ b/index.html
@@ -145,6 +145,7 @@
Examples
WebXR
GLSL Shaders
Debug Coloring
+
Depth of Field
Editor
Viewer
diff --git a/src/SparkRenderer.ts b/src/SparkRenderer.ts
index 7a94baf..8ecc99b 100644
--- a/src/SparkRenderer.ts
+++ b/src/SparkRenderer.ts
@@ -115,6 +115,10 @@ export type SparkRendererOptions = {
// to correctly account for "blurring" when anti-aliasing. Typically 0.3
// (equivalent to approx 0.5 pixel radius) in scenes trained with anti-aliasing.
blurAmount?: number;
+ // Depth-of-field distance to focal plane
+ focalDistance?: number;
+ // Full-width angle of aperture opening (in radians), default 0.0 to disable
+ apertureAngle?: number;
// Modulate Gaussian kernel falloff. 0 means "no falloff, flat shading",
// while 1 is the normal Gaussian kernel. (default: 1.0)
falloff?: number;
@@ -140,6 +144,8 @@ export class SparkRenderer extends THREE.Mesh {
enable2DGS: boolean;
preBlurAmount: number;
blurAmount: number;
+ focalDistance: number;
+ apertureAngle: number;
falloff: number;
clipXY: number;
@@ -241,6 +247,8 @@ export class SparkRenderer extends THREE.Mesh {
this.enable2DGS = options.enable2DGS ?? true;
this.preBlurAmount = options.preBlurAmount ?? 0.0;
this.blurAmount = options.blurAmount ?? 0.3;
+ this.focalDistance = options.focalDistance ?? 0.0;
+ this.apertureAngle = options.apertureAngle ?? 0.0;
this.falloff = options.falloff ?? 1.0;
this.clipXY = options.clipXY ?? 1.4;
@@ -287,6 +295,10 @@ export class SparkRenderer extends THREE.Mesh {
preBlurAmount: { value: 0.0 },
// Add to 2D splat covariance diagonal and adjust opacity (anti-aliasing)
blurAmount: { value: 0.3 },
+ // Depth-of-field distance to focal plane
+ focalDistance: { value: 0.0 },
+ // Full-width angle of aperture opening (in radians)
+ apertureAngle: { value: 0.0 },
// Modulate Gaussian kernal falloff. 0 means "no falloff, flat shading",
// 1 is normal e^-x^2 falloff.
falloff: { value: 1.0 },
@@ -424,6 +436,8 @@ export class SparkRenderer extends THREE.Mesh {
this.uniforms.enable2DGS.value = this.enable2DGS;
this.uniforms.preBlurAmount.value = this.preBlurAmount;
this.uniforms.blurAmount.value = this.blurAmount;
+ this.uniforms.focalDistance.value = this.focalDistance;
+ this.uniforms.apertureAngle.value = this.apertureAngle;
this.uniforms.falloff.value = this.falloff;
this.uniforms.clipXY.value = this.clipXY;
diff --git a/src/shaders/splatVertex.glsl b/src/shaders/splatVertex.glsl
index 00a009b..c9e9893 100644
--- a/src/shaders/splatVertex.glsl
+++ b/src/shaders/splatVertex.glsl
@@ -22,6 +22,8 @@ uniform bool debugFlag;
uniform bool enable2DGS;
uniform float blurAmount;
uniform float preBlurAmount;
+uniform float focalDistance;
+uniform float apertureAngle;
uniform float clipXY;
uniform usampler2DArray packedSplats;
@@ -135,10 +137,21 @@ void main() {
a += preBlurAmount;
d += preBlurAmount;
+ float fullBlurAmount = blurAmount;
+ if ((focalDistance > 0.0) && (apertureAngle > 0.0)) {
+ float focusRadius = MAX_PIXEL_RADIUS;
+ if (viewCenter.z < 0.0) {
+ float focusBlur = abs((-viewCenter.z - focalDistance) / viewCenter.z);
+ float apertureRadius = focal.x * tan(0.5 * apertureAngle);
+ focusRadius = focusBlur * apertureRadius;
+ }
+ fullBlurAmount = clamp(sqr(focusRadius), blurAmount, sqr(MAX_PIXEL_RADIUS));
+ }
+
// Do convolution with a 0.5-pixel Gaussian for anti-aliasing: sqrt(0.3) ~= 0.5
float detOrig = a * d - b * b;
- a += blurAmount;
- d += blurAmount;
+ a += fullBlurAmount;
+ d += fullBlurAmount;
float det = a * d - b * b;
// Compute anti-aliasing intensity scaling factor