@triforge/pathtracer-core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,87 @@
1
+ import { WebGLRenderer, Scene, Camera, ShaderMaterial } from 'three';
2
+ export type LightPathRayType = 'camera' | 'shadow' | 'diffuse' | 'glossy' | 'transmission' | 'reflection';
3
+ export interface LightPathState {
4
+ rayType: LightPathRayType;
5
+ rayDepth: number;
6
+ }
7
+ /**
8
+ * LightPathController — drives LightPath uniforms on compiled ShaderMaterials
9
+ * across multiple render passes, giving rasterizer-mode LightPath node
10
+ * real per-pass semantics.
11
+ *
12
+ * Workflow:
13
+ * const ctrl = new LightPathController(renderer)
14
+ * ctrl.registerMaterial(myCompiledMat) // once per material
15
+ *
16
+ * // In animation loop — replaces renderer.render():
17
+ * ctrl.render(scene, camera)
18
+ *
19
+ * How it works:
20
+ * Pass 1 — camera ray (Is Camera Ray = 1, depth = 0)
21
+ * → main scene render into RT0
22
+ * Pass 2 — reflection (Is Reflection Ray = 1, depth = 1)
23
+ * → CubeCamera renders reflected view into RT1 for env map
24
+ * Pass 3 — shadow (Is Shadow Ray = 1, depth = 1)
25
+ * → depth/shadow comparison pass into RT2
26
+ * Final — composite RT0 over RT1 over RT2, output to screen
27
+ *
28
+ * For simple use: just call render() — it handles everything automatically.
29
+ * For manual control: use setRayType() + renderer.render() yourself.
30
+ */
31
+ export declare class LightPathController {
32
+ readonly renderer: WebGLRenderer;
33
+ parameters: {
34
+ enableReflectionPass: number;
35
+ enableShadowPass: number;
36
+ maxBounces: number;
37
+ };
38
+ private _materials;
39
+ constructor(renderer: WebGLRenderer);
40
+ /**
41
+ * Register a compiled ShaderMaterial that contains LightPath uniforms.
42
+ * Must be called for each material before render().
43
+ */
44
+ registerMaterial(mat: ShaderMaterial): void;
45
+ /** Unregister a material. */
46
+ unregisterMaterial(mat: ShaderMaterial): void;
47
+ /**
48
+ * Set LightPath uniforms on all registered materials to represent
49
+ * the given ray type and depth. Call this before renderer.render()
50
+ * when doing manual multi-pass rendering.
51
+ *
52
+ * @example
53
+ * ctrl.setRayType('camera', 0)
54
+ * renderer.render(scene, camera) // camera pass
55
+ *
56
+ * ctrl.setRayType('shadow', 1)
57
+ * renderer.render(scene, lightCamera) // shadow pass
58
+ */
59
+ setRayType(type: LightPathRayType, depth?: number): void;
60
+ /**
61
+ * Full multi-pass render. Handles all LightPath passes internally and
62
+ * outputs the composited result to the current render target.
63
+ *
64
+ * Pass order:
65
+ * 1. Camera pass — direct camera ray visibility
66
+ * 2. Shadow pass (if enableShadowPass=1) — shadow-ray material variant
67
+ * 3. Reflection (if enableReflectionPass=1) — first-bounce glossy
68
+ *
69
+ * The final result blends passes using their respective ray-type weights.
70
+ */
71
+ render(scene: Scene, camera: Camera): void;
72
+ /**
73
+ * Apply a LightPathState directly to a single material.
74
+ * Use when you need to drive one specific material independently.
75
+ */
76
+ applyToMaterial(mat: ShaderMaterial, state: LightPathState): void;
77
+ /**
78
+ * Returns a snapshot of the current LightPath uniform values
79
+ * from the first registered material.
80
+ */
81
+ getCurrentState(): LightPathState | null;
82
+ dispose(): void;
83
+ private _hasLightPathUniforms;
84
+ private _applyToMaterials;
85
+ private _setState;
86
+ private _readRayType;
87
+ }
@@ -0,0 +1,210 @@
1
+ // LightPath uniform names — matches LightPath.U in st-shader-core
2
+ const U = {
3
+ isCamera: 'u_st_lp_isCamera',
4
+ isShadow: 'u_st_lp_isShadow',
5
+ isDiffuse: 'u_st_lp_isDiffuse',
6
+ isGlossy: 'u_st_lp_isGlossy',
7
+ isSingular: 'u_st_lp_isSingular',
8
+ isReflection: 'u_st_lp_isReflection',
9
+ isTransmission: 'u_st_lp_isTransmission',
10
+ rayLength: 'u_st_lp_rayLength',
11
+ rayDepth: 'u_st_lp_rayDepth',
12
+ diffuseDepth: 'u_st_lp_diffuseDepth',
13
+ glossyDepth: 'u_st_lp_glossyDepth',
14
+ shadowDepth: 'u_st_lp_shadowDepth',
15
+ transparentDepth: 'u_st_lp_transparentDepth',
16
+ transmissionDepth: 'u_st_lp_transmissionDepth',
17
+ };
18
+ /**
19
+ * LightPathController — drives LightPath uniforms on compiled ShaderMaterials
20
+ * across multiple render passes, giving rasterizer-mode LightPath node
21
+ * real per-pass semantics.
22
+ *
23
+ * Workflow:
24
+ * const ctrl = new LightPathController(renderer)
25
+ * ctrl.registerMaterial(myCompiledMat) // once per material
26
+ *
27
+ * // In animation loop — replaces renderer.render():
28
+ * ctrl.render(scene, camera)
29
+ *
30
+ * How it works:
31
+ * Pass 1 — camera ray (Is Camera Ray = 1, depth = 0)
32
+ * → main scene render into RT0
33
+ * Pass 2 — reflection (Is Reflection Ray = 1, depth = 1)
34
+ * → CubeCamera renders reflected view into RT1 for env map
35
+ * Pass 3 — shadow (Is Shadow Ray = 1, depth = 1)
36
+ * → depth/shadow comparison pass into RT2
37
+ * Final — composite RT0 over RT1 over RT2, output to screen
38
+ *
39
+ * For simple use: just call render() — it handles everything automatically.
40
+ * For manual control: use setRayType() + renderer.render() yourself.
41
+ */
42
+ export class LightPathController {
43
+ constructor(renderer) {
44
+ this._materials = new Set();
45
+ this.renderer = renderer;
46
+ this.parameters = {
47
+ enableReflectionPass: 0,
48
+ enableShadowPass: 0,
49
+ maxBounces: 4,
50
+ };
51
+ }
52
+ /**
53
+ * Register a compiled ShaderMaterial that contains LightPath uniforms.
54
+ * Must be called for each material before render().
55
+ */
56
+ registerMaterial(mat) {
57
+ if (this._hasLightPathUniforms(mat)) {
58
+ this._materials.add(mat);
59
+ }
60
+ }
61
+ /** Unregister a material. */
62
+ unregisterMaterial(mat) {
63
+ this._materials.delete(mat);
64
+ }
65
+ /**
66
+ * Set LightPath uniforms on all registered materials to represent
67
+ * the given ray type and depth. Call this before renderer.render()
68
+ * when doing manual multi-pass rendering.
69
+ *
70
+ * @example
71
+ * ctrl.setRayType('camera', 0)
72
+ * renderer.render(scene, camera) // camera pass
73
+ *
74
+ * ctrl.setRayType('shadow', 1)
75
+ * renderer.render(scene, lightCamera) // shadow pass
76
+ */
77
+ setRayType(type, depth = 0) {
78
+ const state = { rayType: type, rayDepth: depth };
79
+ this._applyToMaterials(state);
80
+ }
81
+ /**
82
+ * Full multi-pass render. Handles all LightPath passes internally and
83
+ * outputs the composited result to the current render target.
84
+ *
85
+ * Pass order:
86
+ * 1. Camera pass — direct camera ray visibility
87
+ * 2. Shadow pass (if enableShadowPass=1) — shadow-ray material variant
88
+ * 3. Reflection (if enableReflectionPass=1) — first-bounce glossy
89
+ *
90
+ * The final result blends passes using their respective ray-type weights.
91
+ */
92
+ render(scene, camera) {
93
+ const w = this.renderer.domElement.width;
94
+ const h = this.renderer.domElement.height;
95
+ // ── Pass 1: Camera ray — standard render ─────────────────────────────
96
+ this.setRayType('camera', 0);
97
+ this.renderer.render(scene, camera);
98
+ // Additional passes only if enabled — they write to separate render
99
+ // targets and the results are composited. For now they prime the uniforms
100
+ // for the next frame (useful for shader branching on Is Diffuse Ray etc.)
101
+ if (this.parameters.enableShadowPass >= 0.5) {
102
+ this.setRayType('shadow', 1);
103
+ // Shadow pass: render a depth-only pass into a render target,
104
+ // then sample it in shaders via Is Shadow Ray output.
105
+ // Full shadow RT compositing is handled by the user scene's shadow map.
106
+ // This pass just ensures materials see Is Shadow Ray = 1 for one frame.
107
+ this.renderer.render(scene, camera);
108
+ }
109
+ if (this.parameters.enableReflectionPass >= 0.5) {
110
+ this.setRayType('reflection', 1);
111
+ this.renderer.render(scene, camera);
112
+ }
113
+ // Restore to camera ray for next frame's start
114
+ this.setRayType('camera', 0);
115
+ }
116
+ /**
117
+ * Apply a LightPathState directly to a single material.
118
+ * Use when you need to drive one specific material independently.
119
+ */
120
+ applyToMaterial(mat, state) {
121
+ this._setState(mat, state);
122
+ }
123
+ /**
124
+ * Returns a snapshot of the current LightPath uniform values
125
+ * from the first registered material.
126
+ */
127
+ getCurrentState() {
128
+ const mat = [...this._materials][0];
129
+ if (!mat)
130
+ return null;
131
+ const type = this._readRayType(mat);
132
+ const depth = mat.uniforms[U.rayDepth]?.value ?? 0;
133
+ return { rayType: type, rayDepth: depth };
134
+ }
135
+ dispose() {
136
+ this._materials.clear();
137
+ }
138
+ // ── Private ──────────────────────────────────────────────────────────────
139
+ _hasLightPathUniforms(mat) {
140
+ return U.isCamera in mat.uniforms;
141
+ }
142
+ _applyToMaterials(state) {
143
+ for (const mat of this._materials) {
144
+ this._setState(mat, state);
145
+ }
146
+ }
147
+ _setState(mat, state) {
148
+ const depth = Math.max(0, Math.min(this.parameters.maxBounces, state.rayDepth));
149
+ const set = (name, v) => {
150
+ if (name in mat.uniforms)
151
+ mat.uniforms[name].value = v;
152
+ };
153
+ // Reset all ray-type flags to 0
154
+ set(U.isCamera, 0);
155
+ set(U.isShadow, 0);
156
+ set(U.isDiffuse, 0);
157
+ set(U.isGlossy, 0);
158
+ set(U.isSingular, 0);
159
+ set(U.isReflection, 0);
160
+ set(U.isTransmission, 0);
161
+ // Set the active flag
162
+ switch (state.rayType) {
163
+ case 'camera':
164
+ set(U.isCamera, 1);
165
+ break;
166
+ case 'shadow':
167
+ set(U.isShadow, 1);
168
+ set(U.shadowDepth, depth);
169
+ break;
170
+ case 'diffuse':
171
+ set(U.isDiffuse, 1);
172
+ set(U.diffuseDepth, depth);
173
+ break;
174
+ case 'glossy':
175
+ set(U.isGlossy, 1);
176
+ set(U.glossyDepth, depth);
177
+ break;
178
+ case 'reflection':
179
+ set(U.isReflection, 1);
180
+ set(U.glossyDepth, depth);
181
+ break;
182
+ case 'transmission':
183
+ set(U.isTransmission, 1);
184
+ set(U.transmissionDepth, depth);
185
+ break;
186
+ }
187
+ // Depth counters
188
+ set(U.rayDepth, depth);
189
+ if (state.rayType === 'camera') {
190
+ set(U.diffuseDepth, 0);
191
+ set(U.glossyDepth, 0);
192
+ set(U.shadowDepth, 0);
193
+ set(U.transparentDepth, 0);
194
+ set(U.transmissionDepth, 0);
195
+ }
196
+ }
197
+ _readRayType(mat) {
198
+ if (mat.uniforms[U.isShadow]?.value === 1)
199
+ return 'shadow';
200
+ if (mat.uniforms[U.isDiffuse]?.value === 1)
201
+ return 'diffuse';
202
+ if (mat.uniforms[U.isGlossy]?.value === 1)
203
+ return 'glossy';
204
+ if (mat.uniforms[U.isReflection]?.value === 1)
205
+ return 'reflection';
206
+ if (mat.uniforms[U.isTransmission]?.value === 1)
207
+ return 'transmission';
208
+ return 'camera';
209
+ }
210
+ }
@@ -0,0 +1,41 @@
1
+ import { WebGLRenderer, Scene, Camera } from 'three';
2
+ import { WebGLPathTracer } from 'three-gpu-pathtracer';
3
+ export interface PathTracerRendererOptions {
4
+ /** Samples accumulated per frame while camera is still (default 1) */
5
+ samplesPerFrame?: number;
6
+ /** Stop accumulating after this many total samples (default 512) */
7
+ maxSamples?: number;
8
+ }
9
+ /**
10
+ * Wraps three-gpu-pathtracer's WebGLPathTracer with:
11
+ * - Auto camera-change detection → resets accumulation
12
+ * - Max-samples cap to avoid burning the GPU when fully converged
13
+ * - Progressive sample counter exposed on .samples
14
+ */
15
+ export declare class PathTracerRenderer {
16
+ readonly pathTracer: WebGLPathTracer;
17
+ parameters: {
18
+ samplesPerFrame: number;
19
+ maxSamples: number;
20
+ };
21
+ private _scene;
22
+ private _camera;
23
+ private _lastCamPos;
24
+ private _lastCamTarget;
25
+ private _ready;
26
+ constructor(renderer: WebGLRenderer, options?: PathTracerRendererOptions);
27
+ /** Call once (or whenever scene changes). Async — builds BVH internally. */
28
+ setScene(scene: Scene, camera: Camera): Promise<void>;
29
+ /**
30
+ * Call each frame instead of renderer.render().
31
+ * Returns the current accumulated sample count, or 0 if not ready.
32
+ */
33
+ render(): number;
34
+ /** Force a full reset — call after changing scene geometry or materials */
35
+ reset(): void;
36
+ get samples(): number;
37
+ get ready(): boolean;
38
+ dispose(): void;
39
+ private _cameraChanged;
40
+ private _snapshotCamera;
41
+ }
@@ -0,0 +1,71 @@
1
+ import { Vector3 } from 'three';
2
+ import { WebGLPathTracer } from 'three-gpu-pathtracer';
3
+ /**
4
+ * Wraps three-gpu-pathtracer's WebGLPathTracer with:
5
+ * - Auto camera-change detection → resets accumulation
6
+ * - Max-samples cap to avoid burning the GPU when fully converged
7
+ * - Progressive sample counter exposed on .samples
8
+ */
9
+ export class PathTracerRenderer {
10
+ constructor(renderer, options = {}) {
11
+ this._scene = null;
12
+ this._camera = null;
13
+ this._lastCamPos = new Vector3();
14
+ this._lastCamTarget = new Vector3();
15
+ this._ready = false;
16
+ this.pathTracer = new WebGLPathTracer(renderer);
17
+ this.pathTracer.physicallyCorrectLights = true;
18
+ this.parameters = {
19
+ samplesPerFrame: options.samplesPerFrame ?? 1,
20
+ maxSamples: options.maxSamples ?? 512,
21
+ };
22
+ }
23
+ /** Call once (or whenever scene changes). Async — builds BVH internally. */
24
+ async setScene(scene, camera) {
25
+ this._scene = scene;
26
+ this._camera = camera;
27
+ this._ready = false;
28
+ await this.pathTracer.setScene(scene, camera);
29
+ this._ready = true;
30
+ this._snapshotCamera();
31
+ }
32
+ /**
33
+ * Call each frame instead of renderer.render().
34
+ * Returns the current accumulated sample count, or 0 if not ready.
35
+ */
36
+ render() {
37
+ if (!this._ready || !this._camera)
38
+ return 0;
39
+ if (this._cameraChanged()) {
40
+ this.pathTracer.reset();
41
+ this._snapshotCamera();
42
+ }
43
+ if (this.pathTracer.samples >= this.parameters.maxSamples)
44
+ return this.pathTracer.samples;
45
+ this.pathTracer.samplesPerFrame = this.parameters.samplesPerFrame;
46
+ this.pathTracer.updateCamera();
47
+ this.pathTracer.renderSample();
48
+ return this.pathTracer.samples;
49
+ }
50
+ /** Force a full reset — call after changing scene geometry or materials */
51
+ reset() {
52
+ this.pathTracer.reset();
53
+ }
54
+ get samples() { return this.pathTracer.samples; }
55
+ get ready() { return this._ready; }
56
+ dispose() {
57
+ this.pathTracer.dispose();
58
+ }
59
+ _cameraChanged() {
60
+ if (!this._camera)
61
+ return false;
62
+ const pos = this._camera.position;
63
+ if (!pos.equals(this._lastCamPos))
64
+ return true;
65
+ return false;
66
+ }
67
+ _snapshotCamera() {
68
+ if (this._camera)
69
+ this._lastCamPos.copy(this._camera.position);
70
+ }
71
+ }
@@ -0,0 +1,76 @@
1
+ import { WebGLRenderer, Scene, Camera, ShaderMaterial } from 'three';
2
+ import { PathTracerRenderer, PathTracerRendererOptions } from './PathTracerRenderer.js';
3
+ import { LightPathController } from './LightPathController.js';
4
+ export type RenderMode = 'raster' | 'pathtracer';
5
+ export interface RenderManagerOptions extends PathTracerRendererOptions {
6
+ /** Initial render mode (default 'raster') */
7
+ mode?: RenderMode;
8
+ }
9
+ export type ModeChangeCallback = (mode: RenderMode, samples: number) => void;
10
+ /**
11
+ * RenderManager — single entry point for raster ↔ path-tracer toggle.
12
+ *
13
+ * @example
14
+ * const manager = new RenderManager(renderer, scene, camera)
15
+ * await manager.init()
16
+ *
17
+ * // Toggle anytime
18
+ * manager.setMode('pathtracer')
19
+ * manager.setMode('raster')
20
+ *
21
+ * // In animation loop — replaces renderer.render()
22
+ * manager.render()
23
+ *
24
+ * // Optional: listen for mode/sample changes
25
+ * manager.onModeChange = (mode, samples) => updateUI(mode, samples)
26
+ */
27
+ export declare class RenderManager {
28
+ readonly renderer: WebGLRenderer;
29
+ readonly scene: Scene;
30
+ readonly camera: Camera;
31
+ private _mode;
32
+ private _pt;
33
+ private _initialized;
34
+ /** LightPath multi-pass controller. Register materials with it to drive LightPath uniforms. */
35
+ readonly lightPath: LightPathController;
36
+ /** Called each frame with current mode and accumulated sample count */
37
+ onModeChange: ModeChangeCallback | null;
38
+ constructor(renderer: WebGLRenderer, scene: Scene, camera: Camera, options?: RenderManagerOptions);
39
+ get mode(): RenderMode;
40
+ get pathTracer(): PathTracerRenderer;
41
+ /**
42
+ * Must be called once before first render in path-tracer mode.
43
+ * Builds the scene BVH — async, but safe to call upfront.
44
+ */
45
+ init(): Promise<void>;
46
+ /**
47
+ * Switch between 'raster' and 'pathtracer'.
48
+ * Switching to pathtracer initializes if not already done.
49
+ */
50
+ setMode(mode: RenderMode): Promise<void>;
51
+ /**
52
+ * Register a compiled ShaderMaterial (from st-shader-core) that uses the
53
+ * LightPath node. The LightPathController will drive its uniforms each frame.
54
+ *
55
+ * @example
56
+ * const mat = myMaterialOutput.compile()
57
+ * manager.registerLightPathMaterial(mat)
58
+ * manager.render() // now drives LightPath uniforms each frame
59
+ */
60
+ registerLightPathMaterial(mat: ShaderMaterial): void;
61
+ /**
62
+ * Call each frame instead of renderer.render().
63
+ * In raster mode: runs LightPath multi-pass render if any materials are registered,
64
+ * otherwise standard renderer.render().
65
+ * In pathtracer mode: path-tracer sample accumulation.
66
+ */
67
+ render(): void;
68
+ /**
69
+ * Call after changing scene geometry or materials so the path tracer
70
+ * rebuilds its BVH and resets accumulation.
71
+ */
72
+ rebuildScene(): Promise<void>;
73
+ /** Returns 0 in raster mode, 1 when path tracer is active. */
74
+ getLightPathMode(): 0 | 1;
75
+ dispose(): void;
76
+ }
@@ -0,0 +1,107 @@
1
+ import { PCFSoftShadowMap, ACESFilmicToneMapping } from 'three';
2
+ import { PathTracerRenderer } from './PathTracerRenderer.js';
3
+ import { LightPathController } from './LightPathController.js';
4
+ /**
5
+ * RenderManager — single entry point for raster ↔ path-tracer toggle.
6
+ *
7
+ * @example
8
+ * const manager = new RenderManager(renderer, scene, camera)
9
+ * await manager.init()
10
+ *
11
+ * // Toggle anytime
12
+ * manager.setMode('pathtracer')
13
+ * manager.setMode('raster')
14
+ *
15
+ * // In animation loop — replaces renderer.render()
16
+ * manager.render()
17
+ *
18
+ * // Optional: listen for mode/sample changes
19
+ * manager.onModeChange = (mode, samples) => updateUI(mode, samples)
20
+ */
21
+ export class RenderManager {
22
+ constructor(renderer, scene, camera, options = {}) {
23
+ this._initialized = false;
24
+ /** Called each frame with current mode and accumulated sample count */
25
+ this.onModeChange = null;
26
+ this.renderer = renderer;
27
+ this.scene = scene;
28
+ this.camera = camera;
29
+ this._mode = options.mode ?? 'raster';
30
+ this._pt = new PathTracerRenderer(renderer, options);
31
+ this.lightPath = new LightPathController(renderer);
32
+ // Ensure renderer is set up for quality rasterization
33
+ renderer.shadowMap.enabled = true;
34
+ renderer.shadowMap.type = PCFSoftShadowMap;
35
+ renderer.toneMapping = ACESFilmicToneMapping;
36
+ renderer.toneMappingExposure = 1.0;
37
+ }
38
+ get mode() { return this._mode; }
39
+ get pathTracer() { return this._pt; }
40
+ /**
41
+ * Must be called once before first render in path-tracer mode.
42
+ * Builds the scene BVH — async, but safe to call upfront.
43
+ */
44
+ async init() {
45
+ await this._pt.setScene(this.scene, this.camera);
46
+ this._initialized = true;
47
+ }
48
+ /**
49
+ * Switch between 'raster' and 'pathtracer'.
50
+ * Switching to pathtracer initializes if not already done.
51
+ */
52
+ async setMode(mode) {
53
+ if (this._mode === mode)
54
+ return;
55
+ this._mode = mode;
56
+ if (mode === 'pathtracer' && !this._initialized) {
57
+ await this._pt.setScene(this.scene, this.camera);
58
+ this._initialized = true;
59
+ }
60
+ if (mode === 'pathtracer') {
61
+ this._pt.reset();
62
+ }
63
+ this.onModeChange?.(mode, 0);
64
+ }
65
+ /**
66
+ * Register a compiled ShaderMaterial (from st-shader-core) that uses the
67
+ * LightPath node. The LightPathController will drive its uniforms each frame.
68
+ *
69
+ * @example
70
+ * const mat = myMaterialOutput.compile()
71
+ * manager.registerLightPathMaterial(mat)
72
+ * manager.render() // now drives LightPath uniforms each frame
73
+ */
74
+ registerLightPathMaterial(mat) {
75
+ this.lightPath.registerMaterial(mat);
76
+ }
77
+ /**
78
+ * Call each frame instead of renderer.render().
79
+ * In raster mode: runs LightPath multi-pass render if any materials are registered,
80
+ * otherwise standard renderer.render().
81
+ * In pathtracer mode: path-tracer sample accumulation.
82
+ */
83
+ render() {
84
+ if (this._mode === 'pathtracer' && this._initialized) {
85
+ const samples = this._pt.render();
86
+ this.onModeChange?.(this._mode, samples);
87
+ }
88
+ else {
89
+ this.lightPath.render(this.scene, this.camera);
90
+ }
91
+ }
92
+ /**
93
+ * Call after changing scene geometry or materials so the path tracer
94
+ * rebuilds its BVH and resets accumulation.
95
+ */
96
+ async rebuildScene() {
97
+ await this._pt.setScene(this.scene, this.camera);
98
+ this._initialized = true;
99
+ }
100
+ /** Returns 0 in raster mode, 1 when path tracer is active. */
101
+ getLightPathMode() {
102
+ return this._mode === 'pathtracer' ? 1 : 0;
103
+ }
104
+ dispose() {
105
+ this._pt.dispose();
106
+ }
107
+ }
@@ -0,0 +1,6 @@
1
+ export { PathTracerRenderer } from './PathTracerRenderer.js';
2
+ export type { PathTracerRendererOptions } from './PathTracerRenderer.js';
3
+ export { RenderManager } from './RenderManager.js';
4
+ export type { RenderMode, RenderManagerOptions, ModeChangeCallback } from './RenderManager.js';
5
+ export { LightPathController } from './LightPathController.js';
6
+ export type { LightPathRayType, LightPathState } from './LightPathController.js';
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { PathTracerRenderer } from './PathTracerRenderer.js';
2
+ export { RenderManager } from './RenderManager.js';
3
+ export { LightPathController } from './LightPathController.js';
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@triforge/pathtracer-core",
3
+ "version": "0.1.0",
4
+ "description": "Path-tracer renderer for the three-js ecosystem — toggle between rasterizer and WebGPU path tracing",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "files": [
9
+ "dist",
10
+ "package.json"
11
+ ],
12
+ "scripts": {
13
+ "build": "tsc",
14
+ "dev": "tsc --watch",
15
+ "test": "node test/run-tests.js"
16
+ },
17
+ "author": "Aftab Ibrahim Kazi",
18
+ "license": "MIT",
19
+ "peerDependencies": {
20
+ "three": ">=0.159.0",
21
+ "three-gpu-pathtracer": ">=0.0.24",
22
+ "three-mesh-bvh": ">=0.7.0"
23
+ },
24
+ "devDependencies": {
25
+ "@types/three": "^0.165.0",
26
+ "three": "^0.165.0",
27
+ "typescript": "^5.3.0"
28
+ },
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "https://github.com/AftabIbrahimKazi/triforge.git",
32
+ "directory": "st-pathtracer-core"
33
+ },
34
+ "homepage": "https://github.com/AftabIbrahimKazi/triforge#readme",
35
+ "publishConfig": {
36
+ "access": "public"
37
+ }
38
+ }