Skip to content

Commit 91f5484

Browse files
committed
Support adaptive raymarch step count
* Fix some issues with the way the camera frustum intersection checks are handled * Add debug mode to view step count
1 parent bb6970e commit 91f5484

File tree

13 files changed

+263
-97
lines changed

13 files changed

+263
-97
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ Adapted from [original implementation](https://github.com/n8python/goodGodRays)
2020
Or import from unpkg as a module:
2121

2222
```ts
23-
import { GodraysPass } from 'https://unpkg.com/three-good-godrays@0.10.0/build/three-good-godrays.esm.js';
23+
import { GodraysPass } from 'https://unpkg.com/three-good-godrays@0.11.0/build/three-good-godrays.esm.js';
2424
```
2525

2626
## Supported Three.JS Version

demo/src/BaseDemo.ts

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,18 @@ import { GodraysPass, GodraysPassParams, GodraysUpsampleQuality } from '../../sr
88

99
THREE.ColorManagement.enabled = true;
1010

11-
interface GodraysPassParamsState extends Omit<GodraysPassParams, 'color' | 'blur'> {
11+
interface GodraysPassParamsState
12+
extends Omit<GodraysPassParams, 'color' | 'blur' | 'adaptiveSteps' | 'debugSteps'> {
1213
color: number;
1314
enableBlur: boolean;
1415
blurVariance: number;
1516
blurKernelSize: KernelSize;
1617
upsampleQuality: GodraysUpsampleQuality;
18+
debugSteps: boolean;
19+
useAdaptiveSteps: boolean;
20+
stepSize: number;
21+
minSteps: number;
22+
maxSteps: number;
1723
}
1824

1925
export class BaseDemo extends Demo {
@@ -26,11 +32,16 @@ export class BaseDemo extends Demo {
2632
color: new THREE.Color(0xffffff).getHex(),
2733
raymarchSteps: 60,
2834
enableBlur: true,
29-
blurVariance: 0.1,
30-
blurKernelSize: KernelSize.SMALL,
35+
blurVariance: 0.125,
36+
blurKernelSize: KernelSize.MEDIUM,
3137
gammaCorrection: false,
3238
resolutionScale: 0.5,
3339
upsampleQuality: GodraysUpsampleQuality.HIGH,
40+
useAdaptiveSteps: false,
41+
stepSize: 3.5,
42+
minSteps: 8,
43+
maxSteps: 125,
44+
debugSteps: false,
3445
};
3546

3647
public composer: EffectComposer;
@@ -40,16 +51,35 @@ export class BaseDemo extends Demo {
4051
this.composer = composer;
4152
}
4253

43-
public onParamChange = (key: string, value: any) => {
44-
this.params[key] = value;
45-
46-
this.godraysPass.setParams({
47-
...this.params,
54+
public buildPassParams(): Partial<GodraysPassParams> {
55+
const passParams: Partial<GodraysPassParams> = {
56+
density: this.params.density,
57+
maxDensity: this.params.maxDensity,
58+
distanceAttenuation: this.params.distanceAttenuation,
4859
color: new THREE.Color(this.params.color),
4960
blur: this.params.enableBlur
5061
? { variance: this.params.blurVariance, kernelSize: this.params.blurKernelSize }
5162
: false,
52-
});
63+
gammaCorrection: this.params.gammaCorrection,
64+
resolutionScale: this.params.resolutionScale,
65+
upsampleQuality: this.params.upsampleQuality,
66+
debugSteps: this.params.debugSteps,
67+
};
68+
if (this.params.useAdaptiveSteps) {
69+
passParams.adaptiveSteps = {
70+
stepSize: this.params.stepSize,
71+
minSteps: this.params.minSteps,
72+
maxSteps: this.params.maxSteps,
73+
};
74+
} else {
75+
passParams.raymarchSteps = this.params.raymarchSteps;
76+
}
77+
return passParams;
78+
}
79+
80+
public onParamChange = (key: string, value: any) => {
81+
this.params[key] = value;
82+
this.godraysPass.setParams(this.buildPassParams());
5383
};
5484

5585
registerOptions(menu: GUI) {
@@ -69,6 +99,11 @@ export class BaseDemo extends Demo {
6999
.onChange(mkOnChange('upsampleQuality'));
70100
menu.add(this.params, 'resolutionScale', 0.1, 1, 0.05).onChange(mkOnChange('resolutionScale'));
71101
menu.add(this.params, 'raymarchSteps', 1, 200, 1).onChange(mkOnChange('raymarchSteps'));
102+
menu.add(this.params, 'debugSteps').onChange(mkOnChange('debugSteps'));
103+
menu.add(this.params, 'useAdaptiveSteps').onChange(mkOnChange('useAdaptiveSteps'));
104+
menu.add(this.params, 'stepSize', 0.1, 50, 0.1).onChange(mkOnChange('stepSize'));
105+
menu.add(this.params, 'minSteps', 1, 50, 1).onChange(mkOnChange('minSteps'));
106+
menu.add(this.params, 'maxSteps', 10, 300, 1).onChange(mkOnChange('maxSteps'));
72107
menu.add(this.params, 'enableBlur', true).onChange(mkOnChange('enableBlur'));
73108
menu.add(this.params, 'blurVariance', 0.001, 0.5, 0.001).onChange(mkOnChange('blurVariance'));
74109
menu

demo/src/dirlightDemo.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,12 @@ export default class DirlightDemo extends BaseDemo {
8282
this.controls.enableDamping = true;
8383
this.controls.dampingFactor = 0.1;
8484

85-
this.camera.position.set(-140, 110, -200);
85+
this.camera.position.set(-140, 140, -200);
8686
this.controls.target.set(0, 0, 0);
8787
this.controls.update();
8888

8989
const renderPass = new RenderPass(this.scene, this.camera);
90+
renderPass.renderToScreen = false;
9091
this.composer.addPass(renderPass);
9192

9293
const dirLight = new THREE.DirectionalLight(0xffffff, 0.3);
@@ -112,16 +113,19 @@ export default class DirlightDemo extends BaseDemo {
112113
// const dirLightCameraHelper = new THREE.CameraHelper(dirLight.shadow.camera);
113114
// this.scene.add(dirLightCameraHelper);
114115

115-
this.godraysPass = new GodraysPass(dirLight, this.camera as THREE.PerspectiveCamera, {
116-
...this.params,
117-
gammaCorrection: false,
118-
color: new THREE.Color(this.params.color),
119-
});
116+
this.params.useAdaptiveSteps = true;
117+
this.godraysPass = new GodraysPass(
118+
dirLight,
119+
this.camera as THREE.PerspectiveCamera,
120+
this.buildPassParams()
121+
);
122+
this.godraysPass.renderToScreen = false;
120123
this.composer.addPass(this.godraysPass);
121124

122125
const smaaEffect = new SMAAEffect();
123126
const smaaPass = new EffectPass(this.camera, smaaEffect);
124127
smaaPass.encodeOutput = false;
128+
smaaPass.renderToScreen = true;
125129
this.composer.addPass(smaaPass);
126130
}
127131

demo/src/pointlightDemo.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -109,11 +109,7 @@ export default class PointlightDemo extends BaseDemo {
109109
pointLight.position.copy(lightPos);
110110
this.scene.add(pointLight);
111111

112-
this.godraysPass = new GodraysPass(pointLight, this.camera as THREE.PerspectiveCamera, {
113-
...this.params,
114-
gammaCorrection: false,
115-
color: new THREE.Color(this.params.color),
116-
});
112+
this.godraysPass = new GodraysPass(pointLight, this.camera as THREE.PerspectiveCamera, this.buildPassParams());
117113
this.godraysPass.renderToScreen = false;
118114
this.composer.addPass(this.godraysPass);
119115

demo/src/sponzaDemo.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,7 @@ export class SponzaDemo extends BaseDemo {
6969
this.renderer.shadowMap.needsUpdate = true;
7070

7171
this.params.density = 0.11;
72-
this.godraysPass = new GodraysPass(pointLight, this.camera as THREE.PerspectiveCamera, {
73-
...this.params,
74-
gammaCorrection: false,
75-
color: new THREE.Color(this.params.color),
76-
});
72+
this.godraysPass = new GodraysPass(pointLight, this.camera as THREE.PerspectiveCamera, this.buildPassParams());
7773
this.godraysPass.renderToScreen = false;
7874
this.composer.addPass(this.godraysPass);
7975

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "three-good-godrays",
3-
"version": "0.10.0",
3+
"version": "0.11.0",
44
"description": "Screen-space raymarched godrays for three.js using the pmndrs postprocessing library",
55
"type": "module",
66
"main": "build/three-good-godrays.js",

src/bilateralFilter.frag

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ float normpdf(in float x, in float sigma) {
4141
void main() {
4242
ivec2 fragCoord = ivec2(vUv * resolution);
4343
vec4 centerSample = texelFetch(tInput, fragCoord, 0);
44+
45+
#if defined(DEBUG_STEPS)
46+
// Pass through heatmap colors unmodified — blurring would distort the visualization
47+
gl_FragColor = centerSample;
48+
#else
4449
float c = centerSample.r;
4550
float finalValue = 0.;
4651

@@ -56,4 +61,5 @@ void main() {
5661
}
5762

5863
gl_FragColor = vec4(vec3(finalValue / totalFactor), centerSample.a);
64+
#endif
5965
}

src/bilateralFilter.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,17 @@ export class BilateralFilterPass extends Pass implements Resizable, Disposable {
6262
this.material.defines.KSIZE_ENUM = params.kernelSize;
6363
}
6464

65+
public updateDebugSteps(enabled: boolean) {
66+
const has = this.material.defines.DEBUG_STEPS !== undefined;
67+
if (enabled && !has) {
68+
this.material.defines.DEBUG_STEPS = '';
69+
this.material.needsUpdate = true;
70+
} else if (!enabled && has) {
71+
delete this.material.defines.DEBUG_STEPS;
72+
this.material.needsUpdate = true;
73+
}
74+
}
75+
6576
public override dispose() {
6677
this.material.dispose();
6778
super.dispose();

src/compositor.frag

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,29 @@ void main() {
8484
float bestChoice = totalWeight > 0.0 ? totalIllum / totalWeight : 0.0;
8585

8686
vec3 diffuse = texture2D(sceneDiffuse, vUv).rgb;
87+
88+
#if defined(DEBUG_STEPS)
89+
// In debug mode, the godrays texture contains heatmap RGB — pass through directly
90+
vec3 totalColor = vec3(0.0);
91+
float totalColorWeight = 0.0;
92+
for (int y = -JBU_EXTENT; y <= 1 + JBU_EXTENT; y++) {
93+
for (int x = -JBU_EXTENT; x <= 1 + JBU_EXTENT; x++) {
94+
vec2 sampleUv = (base + vec2(float(x), float(y)) + 0.5) * texelSize;
95+
vec4 data = texture2D(godrays, sampleUv);
96+
float sampleDepth = data.a;
97+
vec2 d = vec2(float(x), float(y)) - f;
98+
float spatialW = exp(-dot(d, d) / (2.0 * JBU_SPATIAL_SIGMA * JBU_SPATIAL_SIGMA));
99+
float depthDiff = (sampleDepth - correctDepth) / max(correctDepth, 0.001);
100+
float depthW = exp(-0.5 * depthDiff * depthDiff / (JBU_DEPTH_SIGMA * JBU_DEPTH_SIGMA));
101+
float w = spatialW * depthW;
102+
totalColorWeight += w;
103+
totalColor += data.rgb * w;
104+
}
105+
}
106+
gl_FragColor = vec4(totalColorWeight > 0.0 ? totalColor / totalColorWeight : vec3(0.0), 1.0);
107+
#else
87108
gl_FragColor = vec4(mix(diffuse, color, bestChoice), 1.0);
109+
#endif
88110

89111
#include <dithering_fragment>
90112

src/compositorPass.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,18 @@ export class GodraysCompositorPass extends Pass {
9797
);
9898
}
9999

100+
public updateDebugSteps(enabled: boolean): void {
101+
const mat = this.fullscreenMaterial as GodraysCompositorMaterial;
102+
const has = mat.defines.DEBUG_STEPS !== undefined;
103+
if (enabled && !has) {
104+
mat.defines.DEBUG_STEPS = '';
105+
mat.needsUpdate = true;
106+
} else if (!enabled && has) {
107+
delete mat.defines.DEBUG_STEPS;
108+
mat.needsUpdate = true;
109+
}
110+
}
111+
100112
public updateUpsampleQuality(quality: GodraysUpsampleQuality): void {
101113
const mat = this.fullscreenMaterial as GodraysCompositorMaterial;
102114
const jbuExtent = quality >= 1 ? 1 : 0;

0 commit comments

Comments
 (0)