@plasius/gpu-renderer 0.2.50 → 0.2.52
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.
- package/CHANGELOG.md +34 -0
- package/README.md +111 -910
- package/dist/index.cjs +7 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/wavefront-compute.js +2 -1
- package/src/wavefront-config.js +8 -0
package/README.md
CHANGED
|
@@ -1,26 +1,5 @@
|
|
|
1
1
|
# @plasius/gpu-renderer
|
|
2
2
|
|
|
3
|
-
## Native animated showcase surfaces
|
|
4
|
-
|
|
5
|
-
`createNativeSceneRenderer({ canvas })` draws interleaved world-space triangles
|
|
6
|
-
with depth testing, four-sample antialiasing, filtered directional shadows and
|
|
7
|
-
animated water with a planar reflection. Call `render({ vertices, camera, time,
|
|
8
|
-
wakes })` from the caller's animation loop and `destroy()` on teardown. Each vertex
|
|
9
|
-
has twelve floats: position XYZ, normal XYZ, linear colour RGB, roughness,
|
|
10
|
-
metalness, and procedural detail kind (0 plain, 1 wood, 2 mineral/cloth, 3 terrain).
|
|
11
|
-
`camera` supplies `eye`, `target` and optional vertical `fov`. At most four wakes
|
|
12
|
-
contain `[x, z, headingRadians]`; water can be disabled with `water: false`.
|
|
13
|
-
|
|
14
|
-
The input limit defaults to 300,000 vertices. Colour/depth targets are reused and
|
|
15
|
-
render dimensions are capped at 3840 per axis and a 1080p pixel budget. Snapshots identify `webgpu-raster`
|
|
16
|
-
and count submitted frames; they do not claim ray tracing, GPU completion or
|
|
17
|
-
photorealism. Native initialization failures reject; device loss changes
|
|
18
|
-
`available` and calls `onUnavailable`. The existing wavefront APIs are unchanged.
|
|
19
|
-
|
|
20
|
-
Enable only through the consumer's remote `gpu-demo.scene-fidelity.enabled`
|
|
21
|
-
decision and existing access capability. See [design](docs/design/native-showcase-surfaces.md)
|
|
22
|
-
and [ADR-0030](docs/adrs/adr-0030-native-showcase-surfaces.md) for rollout and limits.
|
|
23
|
-
|
|
24
3
|
[](https://www.npmjs.com/package/@plasius/gpu-renderer)
|
|
25
4
|
[](https://github.com/Plasius-LTD/gpu-renderer/actions/workflows/ci.yml)
|
|
26
5
|
[](https://codecov.io/gh/Plasius-LTD/gpu-renderer)
|
|
@@ -29,70 +8,11 @@ and [ADR-0030](docs/adrs/adr-0030-native-showcase-surfaces.md) for rollout and l
|
|
|
29
8
|
[](./SECURITY.md)
|
|
30
9
|
[](./CHANGELOG.md)
|
|
31
10
|
|
|
32
|
-
Framework-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
worker-driven app surfaces.
|
|
36
|
-
|
|
37
|
-
The package targets Node.js 24, consumes the stable `@plasius/gpu-shared` 1.x
|
|
38
|
-
runtime line (`^1.1.1`), and validates its development tooling against the
|
|
39
|
-
current ESLint 10 and TypeScript 7 baselines.
|
|
40
|
-
|
|
41
|
-
Apache-2.0. ESM + CJS builds.
|
|
42
|
-
|
|
43
|
-
## Privacy-Safe Feedback Diagnostics
|
|
44
|
-
|
|
45
|
-
Approved in-game viewers can explicitly convert a small set of current renderer
|
|
46
|
-
health observations into the closed
|
|
47
|
-
`@plasius/gpu-shared/feedback-diagnostics` packet:
|
|
48
|
-
|
|
49
|
-
```js
|
|
50
|
-
import { createFeedbackGameDiagnosticSnapshot } from "@plasius/gpu-renderer";
|
|
51
|
-
|
|
52
|
-
const diagnostics = createFeedbackGameDiagnosticSnapshot({
|
|
53
|
-
featureEnabled,
|
|
54
|
-
capabilityGranted,
|
|
55
|
-
consentConfirmed: true,
|
|
56
|
-
surfaceId: "site.gpu-demo",
|
|
57
|
-
renderer: "webgpu",
|
|
58
|
-
backend: "worker",
|
|
59
|
-
viewportWidth: 1920,
|
|
60
|
-
viewportHeight: 1080,
|
|
61
|
-
frameRate: 59.9,
|
|
62
|
-
frameTimeMs: 16.7,
|
|
63
|
-
featureIds: ["renderer.frame-loop"],
|
|
64
|
-
counters: [{ code: "frame-drop", count: 2 }],
|
|
65
|
-
errorCodes: [],
|
|
66
|
-
});
|
|
67
|
-
```
|
|
11
|
+
Framework-independent WebGPU rendering for browser applications. Render triangle
|
|
12
|
+
meshes with the wavefront path tracer, draw native raster surfaces, or use the
|
|
13
|
+
frame lifecycle and XR integration APIs in your own rendering pipeline.
|
|
68
14
|
|
|
69
|
-
|
|
70
|
-
remotely evaluated `feedback.game-diagnostics.enabled` flag, the
|
|
71
|
-
`feedback.game-diagnostics.attach` capability, and explicit consent are all
|
|
72
|
-
present. It derives trusted provenance and returns only coarse buckets and
|
|
73
|
-
closed codes.
|
|
74
|
-
|
|
75
|
-
This API is deliberately separate from general renderer snapshots. It accepts
|
|
76
|
-
no canvas, user-captured pixels, DOM, player object, URL, filename,
|
|
77
|
-
adapter/device detail, coordinate or raw warning, performs no automatic
|
|
78
|
-
diagnostic capture or network/storage operation, and never registers
|
|
79
|
-
`/player-system`. Disabling the flag preserves ordinary structured-only bug
|
|
80
|
-
reporting.
|
|
81
|
-
|
|
82
|
-
## Adaptive per-pixel implementation status
|
|
83
|
-
|
|
84
|
-
Per-pixel adaptive rendering is not yet exposed by the public renderer API.
|
|
85
|
-
The internal [metadata foundation](docs/design/adaptive-metadata.md) now provides
|
|
86
|
-
reflected requested/completed count storage and lazy, bounded allocation.
|
|
87
|
-
`@plasius/gpu-shader` validates the final initialization WGSL in development;
|
|
88
|
-
the reflector does not enter browser bundles. The normal `npm test` suite checks
|
|
89
|
-
generated byte constants, count packing, allocation admission and cleanup.
|
|
90
|
-
|
|
91
|
-
The fixed renderer and its whole-frame budget adjustment are unchanged. Actual
|
|
92
|
-
completed-count normalization, compact primary scheduling, foveation and shared
|
|
93
|
-
integration must land before a site comparison can claim per-pixel adaptation.
|
|
94
|
-
No image-quality, performance or net-memory-saving claim follows from the
|
|
95
|
-
metadata-only WebGPU execution fixture. See [ADR 0029](docs/adrs/adr-0029-reflected-adaptive-metadata-admission.md).
|
|
15
|
+
Apache-2.0 · ESM and CommonJS · TypeScript declarations included.
|
|
96
16
|
|
|
97
17
|
## Install
|
|
98
18
|
|
|
@@ -100,667 +20,149 @@ metadata-only WebGPU execution fixture. See [ADR 0029](docs/adrs/adr-0029-reflec
|
|
|
100
20
|
npm install @plasius/gpu-renderer
|
|
101
21
|
```
|
|
102
22
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
is no compatibility mode, fallback, waiver, or rollback to those renderers.
|
|
109
|
-
|
|
110
|
-
Run `npm run zero-three:source` before installing dependencies, or run
|
|
111
|
-
`npm run build && npm run zero-three` after `npm ci` for the installed graph,
|
|
112
|
-
built bundle, actual npm tarball, and generated CycloneDX evidence. The full
|
|
113
|
-
command writes digest-bound package/version evidence to
|
|
114
|
-
`release-artifacts/zero-three-evidence.json`. CI retains it, and production CD
|
|
115
|
-
passes it by immutable artifact ID, verifies it again, attaches it to the
|
|
116
|
-
GitHub release, and attests it.
|
|
117
|
-
|
|
118
|
-
## Usage
|
|
23
|
+
Use Node.js 24 or later for package tooling and a WebGPU-capable browser over
|
|
24
|
+
HTTPS or localhost for rendering. This is a browser GPU runtime; installing it
|
|
25
|
+
in Node does not provide a GPU device. A bundler resolves the package imports.
|
|
26
|
+
Initialization rejects when WebGPU or the requested resources are unavailable;
|
|
27
|
+
your application should present that error and an appropriate retry/fallback UI.
|
|
119
28
|
|
|
120
|
-
|
|
121
|
-
import { createGpuRenderer } from "@plasius/gpu-renderer";
|
|
29
|
+
## Quick start: render a mesh
|
|
122
30
|
|
|
123
|
-
|
|
124
|
-
canvas: document.querySelector("#scene"),
|
|
125
|
-
clearColor: "#102035",
|
|
126
|
-
});
|
|
31
|
+
Add a canvas to your page:
|
|
127
32
|
|
|
128
|
-
|
|
129
|
-
|
|
33
|
+
```html
|
|
34
|
+
<canvas id="scene" width="640" height="360" aria-label="Rendered triangle"></canvas>
|
|
130
35
|
```
|
|
131
36
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
`createCanonicalWavefrontMeshInputs` adapts decoded primitive geometry and image
|
|
135
|
-
samples from the canonical model pipeline to the renderer's existing
|
|
136
|
-
`WavefrontMeshInput` shape. It maps canonical material factors and standard
|
|
137
|
-
texture bindings, preserves supported material extension data, and rejects
|
|
138
|
-
missing primitive, material, or decoded texture references. It accepts triangle
|
|
139
|
-
primitives; parsing, resource fetching, image decoding, and other topologies stay
|
|
140
|
-
in the upstream conversion layer. Metallic-roughness and unlit workflows are
|
|
141
|
-
supported; workflows without an existing Wavefront mapping fail explicitly.
|
|
37
|
+
The following browser module renders a single emissive triangle:
|
|
142
38
|
|
|
143
39
|
```js
|
|
144
|
-
import {
|
|
145
|
-
|
|
146
|
-
const meshes = createCanonicalWavefrontMeshInputs({
|
|
147
|
-
document: canonicalDocument,
|
|
148
|
-
geometry: decodedPrimitives.map(({ primitiveId, positions, indices, normals, texcoords }) => ({
|
|
149
|
-
primitiveId,
|
|
150
|
-
positions,
|
|
151
|
-
indices,
|
|
152
|
-
normals,
|
|
153
|
-
texcoords,
|
|
154
|
-
})),
|
|
155
|
-
// ReadonlyMap<canonical texture id, decoded RGBA sample>
|
|
156
|
-
textures: decodedTextureSamples,
|
|
157
|
-
});
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
The canonical document uses types from `@plasius/gpu-model-core`. Geometry is
|
|
161
|
-
already renderer-ready, normals may be omitted to retain the renderer's existing
|
|
162
|
-
fallback, and texture samples are keyed by canonical texture ID. The consuming
|
|
163
|
-
surface remains responsible for evaluating the remote
|
|
164
|
-
`gpu.model.conversion.enabled` flag: when disabled or rolled back, keep the
|
|
165
|
-
existing rendering path active.
|
|
166
|
-
|
|
167
|
-
## Adaptive Frame Hooks
|
|
168
|
-
|
|
169
|
-
`@plasius/gpu-renderer` now exposes frame lifecycle hooks so the app can pass
|
|
170
|
-
negotiated frame targets from `@plasius/gpu-performance` and opt into renderer
|
|
171
|
-
frame sampling for `@plasius/gpu-debug`.
|
|
172
|
-
|
|
173
|
-
```js
|
|
174
|
-
import { createGpuRenderer, createRendererDebugHooks } from "@plasius/gpu-renderer";
|
|
175
|
-
|
|
176
|
-
const rendererDebugHooks = createRendererDebugHooks({
|
|
177
|
-
debugSession,
|
|
178
|
-
getTargetFrameTimeMs: () => governor.getSnapshot().targetFrameTimeMs,
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
const renderer = await createGpuRenderer({
|
|
182
|
-
canvas: "#scene",
|
|
183
|
-
frameIdFactory: ({ frame, xrActive }) => `scene.${xrActive ? "xr" : "flat"}.${frame}`,
|
|
184
|
-
...rendererDebugHooks,
|
|
185
|
-
});
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
## Worker DAG Manifests
|
|
189
|
-
|
|
190
|
-
The renderer also publishes worker-facing frame-stage manifests so
|
|
191
|
-
`@plasius/gpu-performance` and `@plasius/gpu-worker` can reason about renderer
|
|
192
|
-
work as a multi-root DAG instead of a flat queue.
|
|
193
|
-
|
|
194
|
-
```js
|
|
195
|
-
import { getRendererWorkerManifest } from "@plasius/gpu-renderer";
|
|
196
|
-
|
|
197
|
-
const realtimeManifest = getRendererWorkerManifest();
|
|
198
|
-
const xrManifest = getRendererWorkerManifest("xr");
|
|
199
|
-
|
|
200
|
-
console.log(realtimeManifest.jobs.map((job) => job.worker.jobType));
|
|
201
|
-
console.log(xrManifest.jobs.find((job) => job.key === "lateLatch"));
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
- `realtime` publishes `acquire`, `visibility`, `mainEncode`, `postProcess`,
|
|
205
|
-
and `submit`.
|
|
206
|
-
- `xr` publishes `acquire`, `visibility`, `lateLatch`, `mainEncode`, and
|
|
207
|
-
`submit`.
|
|
208
|
-
- Jobs include queue class, priority, dependencies, adaptive budget levels, and
|
|
209
|
-
debug metadata such as allocation tags.
|
|
210
|
-
|
|
211
|
-
## Ray-Tracing-First Planning
|
|
212
|
-
|
|
213
|
-
The renderer now publishes a stable-snapshot render plan for the premium
|
|
214
|
-
ray-tracing-first frame model.
|
|
215
|
-
|
|
216
|
-
```js
|
|
217
|
-
import { createRayTracingRenderPlan } from "@plasius/gpu-renderer";
|
|
218
|
-
|
|
219
|
-
const plan = createRayTracingRenderPlan({
|
|
220
|
-
snapshotId: "visual-snapshot-42",
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
console.log(plan.inputBoundary);
|
|
224
|
-
console.log(plan.renderStages.map((stage) => stage.key));
|
|
225
|
-
console.log(plan.representationBands);
|
|
226
|
-
console.log(plan.wavefront.queueLayout.strategy);
|
|
227
|
-
```
|
|
228
|
-
|
|
229
|
-
## Authored Material Transport
|
|
230
|
-
|
|
231
|
-
Wavefront materials support authored `KHR_materials_*` factors and extension
|
|
232
|
-
textures, bounded nested media, Beer-Lambert attenuation, and reference
|
|
233
|
-
transport helpers for reflected/transmitted branching and spectral dispersion.
|
|
234
|
-
GPU rays carry up to four medium identifiers and enqueue one selected
|
|
235
|
-
continuation per hit. Dielectric lobes are weighted by their selection
|
|
236
|
-
probability; total internal reflection retains unit throughput. The standalone
|
|
237
|
-
CPU reference helper can describe both branches, but the fixed GPU path never
|
|
238
|
-
enqueues an untracked sibling into its tile-sized queue. Physically qualified
|
|
239
|
-
nested media, rough dielectric transport and full spectral rendering remain
|
|
240
|
-
separate work. See [the Amber correction](docs/amber-transport-correction.md).
|
|
241
|
-
|
|
242
|
-
```js
|
|
243
|
-
import {
|
|
244
|
-
createMediumStack,
|
|
245
|
-
createTransportBranches,
|
|
246
|
-
createSpectralSamples,
|
|
247
|
-
} from "@plasius/gpu-renderer";
|
|
248
|
-
|
|
249
|
-
const stack = createMediumStack([1, 4]);
|
|
250
|
-
const branches = createTransportBranches({
|
|
251
|
-
mediumStack: stack,
|
|
252
|
-
mediumId: 7,
|
|
253
|
-
transmission: 1,
|
|
254
|
-
ior: 1.5,
|
|
255
|
-
});
|
|
256
|
-
const wavelengths = createSpectralSamples({ ior: 1.5, dispersion: 0.2 });
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
The plan makes the stable visual snapshot boundary explicit, publishes the
|
|
260
|
-
required RT-first stage ordering, and exposes representation-band plus
|
|
261
|
-
acceleration-structure update policy metadata for downstream lighting and
|
|
262
|
-
performance packages. It now also exposes the renderer-owned wavefront queue
|
|
263
|
-
model, versioned ray/hit/surface/material/medium/accumulation contracts, and
|
|
264
|
-
the termination policy for emissive/environment path completion.
|
|
265
|
-
|
|
266
|
-
## WebGPU Wavefront Compute Renderer
|
|
267
|
-
|
|
268
|
-
The package also exposes an executable WebGPU wavefront renderer for active-ray
|
|
269
|
-
debug validation scenes. It is compute-driven, tiled, and breadth-first by
|
|
270
|
-
bounce depth, so queue buffers are bounded by tile size instead of presentation
|
|
271
|
-
resolution. Renderer-owned GPU record sizes are part of the public compute
|
|
272
|
-
limits so ray, hit, triangle, BVH, and accumulation buffers stay aligned with
|
|
273
|
-
their WGSL layouts. Frame submission batching and dispatch diagnostics are kept
|
|
274
|
-
in dedicated runtime helpers so performance-facing integrations can consume
|
|
275
|
-
stable frame stats without inheriting the renderer's shader and pipeline
|
|
276
|
-
assembly internals.
|
|
277
|
-
|
|
278
|
-
## Animated Scene Renderer
|
|
279
|
-
|
|
280
|
-
`createAnimatedSceneRenderer` provides the renderer-owned v1 surface for the
|
|
281
|
-
GPU animation adventure demo. It accepts scripted beats, route points, props,
|
|
282
|
-
and a backward-compatible `lagged-follow` camera rig that now resolves through
|
|
283
|
-
the shared `@plasius/gpu-camera` editor, spectator, third-person, and
|
|
284
|
-
first-person rig primitives. The renderer exposes `start`, `resize`,
|
|
285
|
-
`getSnapshot`, `setCamera`, `setCameraViewMode`, `applyCameraControl`, and
|
|
286
|
-
`destroy` for host packages. Third-person distance is clamped by camera
|
|
287
|
-
constraints, first-person resolves from the head anchor, and head-look is
|
|
288
|
-
reported as transient post-animation intent instead of mutating clip data. It
|
|
289
|
-
is implemented inside `gpu-renderer` and does not route animation playback
|
|
290
|
-
through Three.js. When hosts provide
|
|
291
|
-
`modelAsset` and `clipAssets`, the v1 canvas renderer parses the Peasant Girl
|
|
292
|
-
GLB mesh, skin, inverse-bind matrices, and Mixamo-compatible clip channels,
|
|
293
|
-
then CPU-skins the active clip and draws model-derived geometry into the
|
|
294
|
-
adventure canvas. Snapshots expose `modelRenderable`, `fallbackProxyActive`,
|
|
295
|
-
`skinnedVertexCount`, `skinnedJointCount`, `activeClipRenderable`,
|
|
296
|
-
`cameraViewMode`, `cameraTransform`, `targetDistance`, `headLook`,
|
|
297
|
-
`characterVisible`, `characterGroundY`, and `propGroundAnchors` so hosts can
|
|
298
|
-
catch camera, scene grounding, and model-renderability regressions.
|
|
299
|
-
When hosts provide beat `movementRequirement` fields and clip
|
|
300
|
-
`movementProfile` metadata, character displacement is resolved per beat:
|
|
301
|
-
travel/jump beats move between route anchors, while stationary action beats
|
|
302
|
-
hold their current anchor unless the profile explicitly allows authored root
|
|
303
|
-
translation. Snapshots expose movement validation diagnostics so hosts can
|
|
304
|
-
detect mismatched animation motion before accepting playback.
|
|
305
|
-
|
|
306
|
-
`createProfessionalAnimatedSceneRenderer` is the fail-closed WebGPU entrypoint
|
|
307
|
-
for the professional Animation Adventure path. It requires a WebGPU canvas,
|
|
308
|
-
skinned GLB character metadata with UVs, normals, diffuse and normal textures,
|
|
309
|
-
and root-authored movement profiles for travel beats. It rejects the legacy
|
|
310
|
-
2D proxy path instead of silently falling back, renders through WebGPU, and
|
|
311
|
-
exposes `renderMode: "webgpu-pbr"`, texture counts, normal-map readiness,
|
|
312
|
-
root-motion policy, character position, camera position, and active clip
|
|
313
|
-
diagnostics for host validation. Repeated travel beats accumulate root-motion
|
|
314
|
-
distance from the clip duration and loop count, capped by the declared movement
|
|
315
|
-
requirement and route segment. The current surface establishes the WebGPU
|
|
316
|
-
lifecycle and validation boundary for the PBR animation path; shader-level
|
|
317
|
-
textured character and environment drawing builds on this boundary.
|
|
318
|
-
|
|
319
|
-
The production transport rollout remains gated by
|
|
320
|
-
`renderer.transport.physicalEstimator`. With the flag enabled, deferred
|
|
321
|
-
continuation vertices carry sanitized physical throughput segments instead of a
|
|
322
|
-
heuristic material-response tint, while the older fallback path remains the
|
|
323
|
-
rollback route during validation.
|
|
324
|
-
Low-SPP physical lighting hardening is separately controlled by the boolean
|
|
325
|
-
`renderer.transport.strictPhysicalLowSppLighting` flag, passed either as
|
|
326
|
-
`strictPhysicalLowSppLighting: true` or through `featureFlags`. When enabled,
|
|
327
|
-
the renderer disables terminal ambient rescue for max-depth/null-throughput
|
|
328
|
-
termination, samples procedural sunlight as an explicit shadow-tested
|
|
329
|
-
directional source, samples procedural sky over the visible hemisphere, and
|
|
330
|
-
selects emissive triangles by area-weighted emission power with matching MIS
|
|
331
|
-
PDFs. Use `denoise: false` when validating this strict path so remaining
|
|
332
|
-
variance is measured in the transport rather than hidden by filtering.
|
|
333
|
-
Strict validation can also enable the Product Studio transport experiment
|
|
334
|
-
matrix through independent boolean feature flags. The flags are composable:
|
|
335
|
-
none, one, several, or all may be requested at once. Flags that depend on strict
|
|
336
|
-
physical transport report as requested but no-op effectively when
|
|
337
|
-
`renderer.transport.strictPhysicalLowSppLighting` is disabled.
|
|
338
|
-
|
|
339
|
-
- `renderer.transport.stableSampleRouting.enabled`
|
|
340
|
-
- `renderer.transport.strictZeroOverflow.enabled`
|
|
341
|
-
- `renderer.transport.deferLowSppRussianRoulette.enabled`
|
|
342
|
-
- `renderer.transport.deterministicDirectLighting.enabled`
|
|
343
|
-
- `renderer.transport.sourceStableDirectLighting.enabled`
|
|
344
|
-
- `renderer.transport.deterministicLowSppIndirect.enabled`
|
|
345
|
-
- `renderer.environment.productStudioImportance.enabled`
|
|
346
|
-
- `renderer.diagnostics.productTransportTelemetry.enabled`
|
|
347
|
-
|
|
348
|
-
Renderer config, frame stats, and snapshots expose both the structured
|
|
349
|
-
`transportExperiments` state and the packed `transportExperimentFlags` bitfield
|
|
350
|
-
so Product Studio diagnostics can record the exact active set for each render.
|
|
351
|
-
`renderer.transport.sourceStableDirectLighting.enabled` is intended for
|
|
352
|
-
denoise-off Product Studio validation: in strict physical mode it enables the
|
|
353
|
-
deterministic direct-light path and removes direct-light sample dependence on
|
|
354
|
-
adjacent pixel ids and frame index so low-SPP source routing is stable without
|
|
355
|
-
adding ambient fill. Multi-bounce continuation rays that terminate on emissive
|
|
356
|
-
geometry are MIS-weighted against that direct emissive estimator, so rare
|
|
357
|
-
softbox hits remain physically valid without adding full unbalanced source
|
|
358
|
-
radiance as isolated stippled pixels.
|
|
359
|
-
`renderer.transport.deterministicLowSppIndirect.enabled` is also strict-mode
|
|
360
|
-
only. The flag remains in the public experiment matrix so Product Studio can
|
|
361
|
-
keep reporting requested and effective rollout state, but the strict shader path
|
|
362
|
-
does not inject cached indirect radiance or suppress physical continuation. Any
|
|
363
|
-
future low-SPP indirect stabilizer must be introduced as an auditable transport
|
|
364
|
-
source with measured PDFs, visibility, and validation against high-SPP reference
|
|
365
|
-
renders before it contributes radiance. Until then,
|
|
366
|
-
`transportContributions.cachedIndirectLuminance` should remain zero and
|
|
367
|
-
multi-bounce energy should come from direct explicit lighting, true terminal
|
|
368
|
-
emissive/environment hits, or stochastic BSDF continuation.
|
|
369
|
-
Set `presentationOutput: "linear"` for linear presented validation captures, or
|
|
370
|
-
omit it to keep the default tone-mapped presentation.
|
|
371
|
-
|
|
372
|
-
```js
|
|
373
|
-
import {
|
|
374
|
-
createWavefrontPathTracingComputeRenderer,
|
|
375
|
-
} from "@plasius/gpu-renderer";
|
|
40
|
+
import { createWavefrontPathTracingComputeRenderer } from "@plasius/gpu-renderer";
|
|
376
41
|
|
|
377
42
|
const renderer = await createWavefrontPathTracingComputeRenderer({
|
|
378
|
-
canvas:
|
|
379
|
-
width:
|
|
380
|
-
height:
|
|
381
|
-
maxDepth:
|
|
382
|
-
samplesPerPixel:
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
emission: [8, 7, 5, 1],
|
|
391
|
-
},
|
|
392
|
-
],
|
|
43
|
+
canvas: "#scene",
|
|
44
|
+
width: 640,
|
|
45
|
+
height: 360,
|
|
46
|
+
maxDepth: 2,
|
|
47
|
+
samplesPerPixel: 1,
|
|
48
|
+
meshes: [{
|
|
49
|
+
id: 1,
|
|
50
|
+
positions: [-1, -1, 0, 1, -1, 0, 0, 1, 0],
|
|
51
|
+
indices: [0, 1, 2],
|
|
52
|
+
normals: [0, 0, 1, 0, 0, 1, 0, 0, 1],
|
|
53
|
+
emission: [8, 7, 5, 1],
|
|
54
|
+
}],
|
|
393
55
|
});
|
|
394
56
|
|
|
395
|
-
renderer.
|
|
396
|
-
```
|
|
397
|
-
|
|
398
|
-
Existing consumers that still call `renderFrame(...)` or
|
|
399
|
-
`renderWavefrontPathTracingComputeFrame(...)` remain supported as compatibility
|
|
400
|
-
wrappers around the canonical mesh renderer.
|
|
401
|
-
|
|
402
|
-
Analytic scene objects remain available for debug fixtures:
|
|
403
|
-
|
|
404
|
-
```js
|
|
405
|
-
const debugRenderer = await createWavefrontPathTracingComputeRenderer({
|
|
406
|
-
canvas: document.querySelector("#debug-render"),
|
|
407
|
-
width: 1280,
|
|
408
|
-
height: 720,
|
|
409
|
-
maxDepth: 6,
|
|
410
|
-
sceneObjects: [
|
|
411
|
-
{
|
|
412
|
-
type: "sphere",
|
|
413
|
-
center: [0, 1.8, -0.5],
|
|
414
|
-
radius: 0.35,
|
|
415
|
-
emission: [8, 7, 5, 1],
|
|
416
|
-
materialKind: "emissive",
|
|
417
|
-
},
|
|
418
|
-
],
|
|
419
|
-
});
|
|
57
|
+
await renderer.renderFrame();
|
|
420
58
|
|
|
421
|
-
|
|
59
|
+
// Call this from your route or component cleanup.
|
|
60
|
+
export function dispose() {
|
|
61
|
+
renderer.destroy();
|
|
62
|
+
}
|
|
422
63
|
```
|
|
423
64
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
`
|
|
430
|
-
project-wide display-quality baseline for path-traced rendering, not a
|
|
431
|
-
Product-Studio-only requirement.
|
|
432
|
-
|
|
433
|
-
Mesh inputs are normalized into triangle records, packed into GPU buffers, and
|
|
434
|
-
uploaded as source buffers for tracing. Vertex normals are preserved for smooth
|
|
435
|
-
shading; when normals are absent the triangle geometric normal is used. The
|
|
436
|
-
display-quality path now defaults to `accelerationBuildMode: "cpu-upload"` so
|
|
437
|
-
CPU-built BVH nodes and triangle records are uploaded once and then reused by
|
|
438
|
-
the GPU tracing passes. Set `accelerationBuildMode: "gpu"` only when you
|
|
439
|
-
explicitly want the experimental GPU-side BVH assembly path for validation or
|
|
440
|
-
development. The `createWavefrontMeshAcceleration(...)` helper is therefore no
|
|
441
|
-
longer debug-only; it is the stable display-quality acceleration builder,
|
|
442
|
-
whereas GPU BVH construction remains available behind the explicit mode switch.
|
|
443
|
-
CPU-upload records still preserve the raw material factors plus atlas rects and
|
|
444
|
-
texture settings expected by the GPU hit-time samplers; they are not meant to
|
|
445
|
-
replace exact UV-driven sampling with CPU-baked triangle averages.
|
|
446
|
-
The GPU BVH path still uses Morton-style centroid keys to sort leaf references
|
|
447
|
-
before sorted leaves and level-concurrent internal nodes are materialized.
|
|
448
|
-
When mesh inputs also carry UVs plus decoded base-colour,
|
|
449
|
-
metallic-roughness, normal, occlusion, or emissive maps, the display-quality
|
|
450
|
-
path now packs them into GPU texture atlases and samples them at the resolved
|
|
451
|
-
hit UV inside the wavefront trace pass. Generic glTF-style material factors
|
|
452
|
-
such as clearcoat, sheen colour, specular colour, transmission, and IOR are
|
|
453
|
-
also preserved through the GPU records so demo validation does not need
|
|
454
|
-
model-name overrides. CPU-side texture work is limited to load-time decode and
|
|
455
|
-
atlas packing; per-hit shading stays on the GPU. Direct and terminal glossy
|
|
456
|
-
response now also samples reflection-aligned environment radiance so leather,
|
|
457
|
-
chrome, and other polished authored materials can read from the active
|
|
458
|
-
environment map or procedural sky instead of relying mostly on a sun-direction
|
|
459
|
-
proxy.
|
|
460
|
-
Authored participating-media inputs can also enter through the same mesh path.
|
|
461
|
-
A mesh may provide an explicit `medium` block or a glTF-style `material.volume`
|
|
462
|
-
block with `thickness`, `attenuationColor`, and `attenuationDistance`; the
|
|
463
|
-
renderer derives the medium-table entry, carries the active medium id on the
|
|
464
|
-
GPU ray, and applies Beer-Lambert transmittance to travelled segments on the
|
|
465
|
-
GPU. Thickness is now preserved in material packing for later shell-volume
|
|
466
|
-
work, but the current renderer still tracks only one active medium id per ray
|
|
467
|
-
and does not yet implement nested media, spectral dispersion, or a branching
|
|
468
|
-
reflection/refraction tree. Invalid medium ids fall back to the current ray
|
|
469
|
-
medium, and entering a second medium while one is already active preserves the
|
|
470
|
-
existing medium id until dedicated stack support is implemented.
|
|
471
|
-
|
|
472
|
-
`samplesPerPixel` controls how many GPU primary-ray samples are accumulated per
|
|
473
|
-
screen pixel within a single render. This multiplies dispatch work but does not
|
|
474
|
-
increase the tile queue memory footprint, so 720p/1080p/4K targets remain
|
|
475
|
-
bounded by `tileSize`. `maxDepth` is bounded at 32 for offline/reference
|
|
476
|
-
renders, allowing 20-bounce quality checks while keeping path-vertex memory
|
|
477
|
-
explicit and predictable. When `denoise` is enabled the renderer writes raw
|
|
478
|
-
linear radiance to an `rgba16float` texture first, then runs a two-stage
|
|
479
|
-
full-frame GPU denoise through an `rgba16float` scratch texture before final
|
|
480
|
-
tone mapping into the presented `rgba8unorm` output. Filtering in linear
|
|
481
|
-
radiance space lets the denoise pass cross tile boundaries without compressing
|
|
482
|
-
energy/detail before the final resolve. High-SPP wavefront sampling now routes
|
|
483
|
-
camera jitter, BSDF continuation, emissive-light selection, and environment
|
|
484
|
-
selection through named sample dimensions in
|
|
485
|
-
`src/wavefront-sampling-dimensions.js`, using low-discrepancy 2D pairs where
|
|
486
|
-
they improve convergence without adding CPU-side sample tables. The companion
|
|
487
|
-
`src/wavefront-denoise-validation.js` acceptance helper keeps denoise-off
|
|
488
|
-
validation explicit with structural-artifact, invalid-sample, baseline-noise,
|
|
489
|
-
and sheen/chrome/wood detail-retention thresholds so the
|
|
490
|
-
`renderer.denoise.highSppIndependence` rollout can stay reviewable and
|
|
491
|
-
rollbackable. The renderer also stores compact
|
|
492
|
-
emissive-triangle metadata in the existing BVH buffer tail and uses it to guide
|
|
493
|
-
diffuse continuation rays toward finite mesh light geometry. This is not a
|
|
494
|
-
separate shadow/direct-light pass: the active ray still has to hit emissive
|
|
495
|
-
geometry or miss into the environment before radiance is committed. Guided
|
|
496
|
-
emissive hits carry a bounded estimator weight so finite light guidance does not
|
|
497
|
-
over-expose low-sample renders before full material PDFs/MIS are implemented.
|
|
498
|
-
By default, `deferredPathResolve` records
|
|
499
|
-
per-bounce material responses in a tile-bounded path buffer and records the
|
|
500
|
-
terminal emissive/HDRI/environment source in the final path slot. The output
|
|
501
|
-
pass then resolves that recorded path backward and adds the weighted sample to
|
|
502
|
-
the pixel accumulation, so unresolved continuation light is still deferred until
|
|
503
|
-
a terminal source is known. Surface resolution may still add a small
|
|
504
|
-
shadow-tested direct-light term immediately when it has an explicit source and
|
|
505
|
-
visibility result, which keeps true occlusion shadows possible without falling
|
|
506
|
-
back to broad per-bounce ambient fill. Set `deferredPathResolve: false` only
|
|
507
|
-
for legacy forward-accumulation comparison.
|
|
508
|
-
When an `environmentMap` is provided, the wavefront trace shader samples it as
|
|
509
|
-
an equirectangular radiance source for environment misses and uses the same
|
|
510
|
-
mapped radiance for terminal residuals before falling back to static ambient.
|
|
511
|
-
The procedural horizon/zenith/sun model remains the fallback for callers that
|
|
512
|
-
have not supplied an HDRI/radiance texture. `environmentLighting.sunlitBaseline`
|
|
513
|
-
adds a time-of-day daylight floor to terminal and direct environment estimates,
|
|
514
|
-
so bright presets retain colour at the last collision without returning to a
|
|
515
|
-
whitewashed global ambient term. Extremely dark recorded bounce responses are
|
|
516
|
-
also remapped to a small scene-brightness-driven luminance floor so bright
|
|
517
|
-
low-sample scenes do not produce isolated black speckles when a valid terminal
|
|
518
|
-
source was already found. Awaited `renderFrame({ readStats: true })` results now
|
|
519
|
-
keep linear accumulation unclamped, sanitize only invalid or half-float-
|
|
520
|
-
overflow samples before presentation, and expose
|
|
521
|
-
`terminalRadiance.totalLuminance`,
|
|
522
|
-
`terminalRadiance.ambientResidualLuminance`, and
|
|
523
|
-
`terminalRadiance.ambientResidualShare` so validation harnesses can track how
|
|
524
|
-
much of the final resolved image came from terminal ambient residuals.
|
|
525
|
-
The same stats also expose `radianceDiagnostics.invalidSamples` and
|
|
526
|
-
`radianceDiagnostics.legacyClampEquivalentSamples`, so hardening scenes can
|
|
527
|
-
measure NaN/Inf cleanup and legacy-4.0-equivalent fireflies without
|
|
528
|
-
re-introducing a hidden estimator clamp.
|
|
529
|
-
When `strictPhysicalLowSppLighting` is enabled, termination stats also separate
|
|
530
|
-
`absorptionNull`, `russianRoulette`, and `strictMaxDepth` so validation can
|
|
531
|
-
distinguish physically explainable dark samples from legacy ambient fallback.
|
|
532
|
-
For static mesh scenes, the GPU acceleration build is submitted once and then
|
|
533
|
-
reused by subsequent frames. Per-frame tracing writes one dynamic uniform slot
|
|
534
|
-
per tile/sample or post-process pass and batches tile tracing, tile output,
|
|
535
|
-
optional denoise, and presentation into bounded command submissions controlled
|
|
536
|
-
by `maxFramePassesPerSubmission` to keep 4K/high-spp command buffers from
|
|
537
|
-
becoming oversized. `updateCamera(...)` can update the per-frame camera uniforms
|
|
538
|
-
without rebuilding scene buffers. `renderFrame(...)` also accepts an optional
|
|
539
|
-
`frameTimeBudgetMs` plus `minimumSamplesPerPixel`: when present, configured
|
|
540
|
-
`samplesPerPixel` becomes a ceiling instead of a hard requirement, the renderer
|
|
541
|
-
guarantees at least the minimum full-screen pass, and frame stats report both
|
|
542
|
-
configured `samplesPerPixel` and actual `renderedSamplesPerPixel` so realtime
|
|
543
|
-
callers can budget motion frames without overstating delivered quality.
|
|
544
|
-
Before requesting a WebGPU device, the renderer derives the largest scene
|
|
545
|
-
storage binding from the normalized scene, mesh, and BVH records. It requests
|
|
546
|
-
the smallest sufficient `maxStorageBufferBindingSize` (and `maxBufferSize` only
|
|
547
|
-
when the WebGPU default is also exceeded), preserves stricter caller limits,
|
|
548
|
-
and rejects unsupported adapters before device creation. Default-sized scenes
|
|
549
|
-
do not elevate either size limit. `config.memory` and snapshot memory telemetry
|
|
550
|
-
describe the actual persistent GPU buffer descriptors: placeholder records,
|
|
551
|
-
combined BVH/emissive storage, mesh source buffers, aligned frame/build uniform
|
|
552
|
-
buffers, counters, and dispatch arguments are each counted exactly once;
|
|
553
|
-
`materialTableBytes` is zero because material data is packed into mesh and
|
|
554
|
-
triangle records rather than a standalone GPU buffer.
|
|
555
|
-
For consumers that want to hand wavefront SPP adaptation to
|
|
556
|
-
`@plasius/gpu-performance`, `createWavefrontAdaptiveSamplingLevels(...)` exposes
|
|
557
|
-
a bounded low-to-high ladder of per-frame `samplesPerPixel`,
|
|
558
|
-
`frameTimeBudgetMs`, and `minimumSamplesPerPixel` configs that stay aligned
|
|
559
|
-
with the renderer's supported adaptive-sampling surface. Frame stats and
|
|
560
|
-
snapshots expose
|
|
561
|
-
`gpuParallelism` diagnostics with adapter compute limits, configured workgroup
|
|
562
|
-
size, direct compute dispatches, known workgroups/invocations, indirect dispatch
|
|
563
|
-
counts, and upper-bound indirect work estimates. WebGPU does not expose physical
|
|
564
|
-
GPU core counts, so `physicalCoreCount` remains `null`; use
|
|
565
|
-
`exposesMultiWorkgroupParallelism`, `largestDirectWorkgroupsPerDispatch`, and
|
|
566
|
-
`largestEstimatedIndirectWorkgroupsPerDispatch` to confirm the renderer is
|
|
567
|
-
issuing multi-workgroup GPU work. Awaited frame results also expose a
|
|
568
|
-
`transportGuardrails` summary with jobs/frame, jobs/s, jobs/submission, command
|
|
569
|
-
submissions, queue-overflow count, radiance diagnostics, total tracked memory
|
|
570
|
-
bytes, and device-loss status so validation harnesses can gate transport work
|
|
571
|
-
without scraping ad hoc metrics from multiple fields. Treat any sustained >10%
|
|
572
|
-
drop in jobs/submission or jobs/s versus the approved baseline as a
|
|
573
|
-
release-validation failure unless a linked ticket explicitly approves the
|
|
574
|
-
regression; `submission-batching` warns when the renderer falls back to roughly
|
|
575
|
-
one GPU job per submission despite a higher pass ceiling.
|
|
576
|
-
|
|
577
|
-
Awaited fixed-SPP renders can opt into exact ray and timing evidence:
|
|
65
|
+
Supply decoded triangle geometry and material/texture data through `meshes`.
|
|
66
|
+
The renderer does not fetch glTF files: use a loader such as
|
|
67
|
+
[`@plasius/gpu-shared`](https://www.npmjs.com/package/@plasius/gpu-shared), or adapt
|
|
68
|
+
canonical decoded assets with `createCanonicalWavefrontMeshInputs`.
|
|
69
|
+
For an interactive model viewer with loading, integrity checks, camera gestures
|
|
70
|
+
and quality controls, use the shared package's `model-inspection` entry.
|
|
578
71
|
|
|
579
|
-
|
|
580
|
-
const frame = await renderer.renderFrame({
|
|
581
|
-
readStats: true,
|
|
582
|
-
readOutputProbe: false,
|
|
583
|
-
});
|
|
584
|
-
|
|
585
|
-
console.log({
|
|
586
|
-
primaryRays: frame.primaryRays,
|
|
587
|
-
secondaryRays: frame.secondaryRays,
|
|
588
|
-
totalPathSegments: frame.totalPathSegments,
|
|
589
|
-
bounceHistogram: frame.rayCounts.bounceHistogram,
|
|
590
|
-
gpuTimeMs: frame.timings.totalGpuTimeMs,
|
|
591
|
-
renderJobTimeMs: frame.timings.totalRenderJobTimeMs,
|
|
592
|
-
timingSource: frame.timings.source,
|
|
593
|
-
});
|
|
594
|
-
```
|
|
72
|
+
## Choose a rendering API
|
|
595
73
|
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
it receives sky radiance; misses outside a portal fall back to the ambient
|
|
635
|
-
residual. This keeps interior rooms from treating the whole sky as visible from
|
|
636
|
-
every bounce.
|
|
637
|
-
Texture sampling, dynamic TLAS updates, higher-grade LBVH/SAH construction,
|
|
638
|
-
runtime execution behind the `@plasius/gpu-worker` lock-free queue, and broader
|
|
639
|
-
material lookup remain follow-up work.
|
|
640
|
-
|
|
641
|
-
## XR integration
|
|
74
|
+
| API | Purpose |
|
|
75
|
+
| --- | --- |
|
|
76
|
+
| `createWavefrontPathTracingComputeRenderer` | Tiled, mesh-BVH path tracing with material transport, environment lighting and optional progressive accumulation |
|
|
77
|
+
| `createNativeSceneRenderer` | Native raster triangles, authored surface maps, shadows and optional animated water |
|
|
78
|
+
| `createGpuRenderer` | Device/canvas setup, clear pass, frame hooks and XR binding for a host-owned pipeline |
|
|
79
|
+
| `createProfessionalAnimatedSceneRenderer` | The validated WebGPU animation scene path with skinned assets and root-motion requirements |
|
|
80
|
+
| `createAnimatedSceneRenderer` | The existing canvas animation-adventure surface |
|
|
81
|
+
| `createRayTracingRenderPlan` | Rendering plan metadata; it does not draw a scene |
|
|
82
|
+
|
|
83
|
+
All factories are exported from `@plasius/gpu-renderer`. Keep the import and GPU
|
|
84
|
+
initialization inside the route that needs them. The host owns loading/error UI,
|
|
85
|
+
visibility policy, accessible controls, and disposal when leaving the view.
|
|
86
|
+
|
|
87
|
+
## Progressive rendering
|
|
88
|
+
|
|
89
|
+
Set `progressive: true` when creating the wavefront renderer. Its creation-time
|
|
90
|
+
`samplesPerPixel` sets batch capacity; each `renderFrame({ samplesPerPixel })`
|
|
91
|
+
may submit a smaller batch. Await batches serially and stop scheduling when your
|
|
92
|
+
sample target is reached or the view is hidden. `renderFrame()` waits for GPU
|
|
93
|
+
completion by default.
|
|
94
|
+
|
|
95
|
+
Camera and scene changes reset accumulated radiance. `updateCamera()` preserves
|
|
96
|
+
the loaded meshes, materials and acceleration resources; do not call it between
|
|
97
|
+
refinement passes if the view has not changed. `resetAccumulation()` discards
|
|
98
|
+
history explicitly. Denoising runs after accumulation.
|
|
99
|
+
|
|
100
|
+
`getSnapshot().accumulatedSamplesPerPixel` counts **submitted** samples. If you
|
|
101
|
+
use synchronous `renderOnce()`, await `renderer.device.queue.onSubmittedWorkDone()`
|
|
102
|
+
before labelling that work complete. Public sampling is fixed per frame;
|
|
103
|
+
experimental per-pixel adaptive metadata does not enable adaptive rendering.
|
|
104
|
+
See the [wavefront guide](https://github.com/Plasius-LTD/gpu-renderer/blob/main/docs/guides/wavefront.md)
|
|
105
|
+
for memory, timing, transport flags and progressive behavior.
|
|
106
|
+
|
|
107
|
+
## Frame lifecycle without a scene
|
|
108
|
+
|
|
109
|
+
Use `createGpuRenderer` when your application owns the rendering pipeline. This
|
|
110
|
+
example clears the canvas and runs the frame lifecycle; it does not load or draw
|
|
111
|
+
3D models:
|
|
642
112
|
|
|
643
113
|
```js
|
|
644
|
-
import { createXrManager } from "@plasius/gpu-xr";
|
|
645
114
|
import { createGpuRenderer } from "@plasius/gpu-renderer";
|
|
646
115
|
|
|
647
|
-
const renderer = await createGpuRenderer({
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
renderer.bindXrManager(xr, {
|
|
651
|
-
onSessionStart: () => console.log("XR active"),
|
|
652
|
-
onSessionEnd: () => console.log("XR inactive"),
|
|
116
|
+
const renderer = await createGpuRenderer({
|
|
117
|
+
canvas: "#scene",
|
|
118
|
+
clearColor: "#102035",
|
|
653
119
|
});
|
|
120
|
+
renderer.resize(640, 360);
|
|
121
|
+
renderer.start();
|
|
122
|
+
|
|
123
|
+
export function dispose() {
|
|
124
|
+
renderer.destroy();
|
|
125
|
+
}
|
|
654
126
|
```
|
|
655
127
|
|
|
656
|
-
## API
|
|
657
|
-
|
|
658
|
-
- `supportsWebGpu(options)`
|
|
659
|
-
- `createGpuRenderer(options)`
|
|
660
|
-
- `createRendererDebugHooks(options)`
|
|
661
|
-
- `getRendererWorkerProfile(name?)`
|
|
662
|
-
- `getRendererWorkerManifest(name?)`
|
|
663
|
-
- `createRayTracingRenderPlan(options)`
|
|
664
|
-
- `createWavefrontPathTracingComputeRenderer(options)`
|
|
665
|
-
- `createWavefrontPathTracingComputeConfig(options)`
|
|
666
|
-
- `createWavefrontPathTracingComputeShaderSource(options?)`
|
|
667
|
-
- `renderWavefrontPathTracingComputeFrame(options)`
|
|
668
|
-
- `createWavefrontReferenceRay(config, options?)`
|
|
669
|
-
- `intersectWavefrontReferenceTriangle(ray, triangle, options?)`
|
|
670
|
-
- `traceWavefrontReferenceTriangles(config, ray, triangles, options?)`
|
|
671
|
-
- `normalizeWavefrontMesh(input)`
|
|
672
|
-
- `createWavefrontGpuMeshSource(meshes)`
|
|
673
|
-
- `createWavefrontBvhSortStages(itemCount)`
|
|
674
|
-
- `createWavefrontBvhBuildLevels(triangleCount)`
|
|
675
|
-
- `createWavefrontMeshAcceleration(meshes)`
|
|
676
|
-
- `normalizeWavefrontSceneObject(input)`
|
|
677
|
-
- `packWavefrontSceneObjects(sceneObjects, capacity?)`
|
|
678
|
-
- `packWavefrontTriangles(triangles, capacity?)`
|
|
679
|
-
- `packWavefrontBvhNodes(nodes, capacity?)`
|
|
680
|
-
- `rendererWavefrontComputeMode`
|
|
681
|
-
- `rendererWavefrontComputeWorkgroupSize`
|
|
682
|
-
- `rendererWavefrontComputeStatsStride`
|
|
683
|
-
- `bindRendererToXrManager(renderer, xrManager, options)`
|
|
684
|
-
- `defaultRendererClearColor`
|
|
685
|
-
- `rendererDebugOwner`
|
|
686
|
-
- `rendererWorkerQueueClass`
|
|
687
|
-
- `defaultRendererWorkerProfile`
|
|
688
|
-
- `rendererWorkerProfiles`
|
|
689
|
-
- `rendererWorkerProfileNames`
|
|
690
|
-
- `rendererWorkerManifests`
|
|
691
|
-
|
|
692
|
-
The reference helpers mirror the renderer WGSL camera and triangle-hit math in
|
|
693
|
-
deterministic JavaScript so tests and downstream tooling can validate primary
|
|
694
|
-
ray generation, barycentrics, nearest-hit selection, and environment misses
|
|
695
|
-
without standing up a WebGPU device.
|
|
696
|
-
|
|
697
|
-
## Demo
|
|
698
|
-
|
|
699
|
-
Run the demo server from the repo root:
|
|
128
|
+
## API and integration guides
|
|
700
129
|
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
130
|
+
- [Wavefront path tracing, materials and progressive refinement](https://github.com/Plasius-LTD/gpu-renderer/blob/main/docs/guides/wavefront.md).
|
|
131
|
+
- [Native surfaces, texture maps and clearcoat](https://github.com/Plasius-LTD/gpu-renderer/blob/main/docs/guides/native-surfaces.md).
|
|
132
|
+
- [Canonical ingestion, frame hooks, worker manifests, XR and diagnostics](https://github.com/Plasius-LTD/gpu-renderer/blob/main/docs/guides/integration.md). Snippets in this reference use host-provided decoded assets and services.
|
|
133
|
+
- [Animation scene contracts](https://github.com/Plasius-LTD/gpu-renderer/blob/main/docs/guides/animation.md).
|
|
134
|
+
- [Complete public TypeScript declarations](https://github.com/Plasius-LTD/gpu-renderer/blob/main/src/index.d.ts).
|
|
135
|
+
- [Crate material-study implementation notes](https://github.com/Plasius-LTD/gpu-renderer/blob/main/docs/guides/material-study-internals.md). Fixture helpers are internal; mount the study through `@plasius/gpu-shared/material-study`.
|
|
705
136
|
|
|
706
|
-
|
|
137
|
+
Experimental transport switches use the `featureFlags` options described in the
|
|
138
|
+
wavefront guide. The package consumes supplied decisions; it does not connect to
|
|
139
|
+
an account or remote flag service. Plasius production consumers also apply their
|
|
140
|
+
route capabilities and rollout policy before mounting a scene.
|
|
707
141
|
|
|
708
|
-
|
|
709
|
-
`@plasius/gpu-lighting` environment preset into the render. It reports the
|
|
710
|
-
active wavefront depth, tile count, triangle/BVH counts, lighting preset, probe
|
|
711
|
-
luminance, and hot buffer memory so it is clear whether the renderer is tracing
|
|
712
|
-
mesh paths rather than only showing planning metadata.
|
|
142
|
+
## Development
|
|
713
143
|
|
|
714
|
-
|
|
144
|
+
From a checkout, using the repository's Node.js version:
|
|
715
145
|
|
|
716
146
|
```sh
|
|
147
|
+
npm ci --legacy-peer-deps
|
|
717
148
|
npm run lint
|
|
718
149
|
npm run typecheck
|
|
719
150
|
npm run test:coverage
|
|
720
151
|
npm run build
|
|
721
152
|
npm run pack:check
|
|
153
|
+
npm run zero-three
|
|
722
154
|
```
|
|
723
155
|
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
`src/wavefront-frame-dispatcher.js`, and `src/wavefront-frame-stats.js`:
|
|
735
|
-
purpose-specific acceleration build, pass encoding, tile/sample dispatch, and
|
|
736
|
-
frame-stat policy helpers.
|
|
737
|
-
- `src/wavefront-bind-groups.js`, `src/wavefront-pipelines.js`,
|
|
738
|
-
`src/wavefront-gpu-synchronization.js`, and `src/wavefront-readbacks.js`:
|
|
739
|
-
WebGPU bind-group construction, pipeline construction, queue synchronization,
|
|
740
|
-
and readback/probe helpers.
|
|
741
|
-
- `src/wavefront-config.js`: wavefront camera, environment, portal, memory, and
|
|
742
|
-
scene-source configuration.
|
|
743
|
-
- `src/wavefront-scene-data.js`: compatibility facade for wavefront scene data
|
|
744
|
-
helpers.
|
|
745
|
-
- `src/wavefront-materials.js`, `src/wavefront-scene-normalizers.js`, and
|
|
746
|
-
`src/wavefront-mesh-sources.js`: material/medium normalization, scene/mesh
|
|
747
|
-
normalization, texture-atlas creation, BVH source generation, and GPU mesh
|
|
748
|
-
source packing.
|
|
749
|
-
- `src/wavefront-shaders.js` and `src/wavefront-shader-*.js`: WGSL assembly
|
|
750
|
-
and purpose-specific shader source sections for layout, materials, lighting,
|
|
751
|
-
BVH/intersection, and render kernels.
|
|
752
|
-
- `src/wavefront-gpu-resources.js`, `src/wavefront-packers.js`, and
|
|
753
|
-
`src/wavefront-runtime-support.js`: GPU resources, binary record packers, and
|
|
754
|
-
WebGPU runtime/pipeline support.
|
|
755
|
-
- `src/wavefront-reference.js` and `src/wavefront-sampling.js`: deterministic
|
|
756
|
-
reference transport and sampling helpers used by validation tests.
|
|
757
|
-
- `src/index.d.ts`: public API typings.
|
|
758
|
-
- `scripts/check-source-syntax.cjs`: syntax-checks every JavaScript source file
|
|
759
|
-
included under `src/`.
|
|
760
|
-
- `tests/package.test.js`: unit tests for renderer lifecycle behavior.
|
|
761
|
-
- `docs/design/worker-manifest-integration.md`: renderer frame-stage DAG model.
|
|
762
|
-
- `docs/adrs/*`: architecture decisions for renderer runtime design.
|
|
763
|
-
- `docs/tdrs/*`: technical direction for frame hook integration.
|
|
156
|
+
`npm run demo` requires Python 3 and serves the parent workspace. With the checkout
|
|
157
|
+
named `gpu-renderer`, visit `http://localhost:8000/gpu-renderer/demo/`.
|
|
158
|
+
See the [development guide](https://github.com/Plasius-LTD/gpu-renderer/blob/main/docs/guides/development.md)
|
|
159
|
+
for sibling-package requirements and source layout, and
|
|
160
|
+
[CONTRIBUTING.md](https://github.com/Plasius-LTD/gpu-renderer/blob/main/CONTRIBUTING.md).
|
|
161
|
+
|
|
162
|
+
Three.js and dependency paths reaching it are prohibited across runtime,
|
|
163
|
+
development tooling and published artifacts. `npm run zero-three:source` checks
|
|
164
|
+
source admission before installation; `npm run zero-three` validates the installed
|
|
165
|
+
and built package evidence.
|
|
764
166
|
|
|
765
167
|
<!-- BEGIN PLASIUS RELEASE INTEGRITY -->
|
|
766
168
|
## Release integrity
|
|
@@ -790,204 +192,3 @@ there is no npm write-token fallback. CD remains disabled until the npm trusted
|
|
|
790
192
|
publisher binding and protected-branch-only production environment are
|
|
791
193
|
independently verified.
|
|
792
194
|
<!-- END PLASIUS RELEASE INTEGRITY -->
|
|
793
|
-
|
|
794
|
-
### Authored materials in native surfaces
|
|
795
|
-
|
|
796
|
-
`createNativeSceneRenderer({ canvas, materials })` accepts up to 16 opaque
|
|
797
|
-
materials containing decoded RGBA8 `baseColor`, `normal`, and/or `orm` maps.
|
|
798
|
-
This matches images returned by the `@plasius/gpu-shared` glTF loader. Colour maps
|
|
799
|
-
are sRGB; normals and ORM (occlusion/roughness/metalness) are linear. Optional
|
|
800
|
-
`normalScale` defaults to 1. Maps must be power-of-two, up to 2048 pixels per axis,
|
|
801
|
-
with a 64 MiB combined decoded-source budget. Textures are uploaded once and mip
|
|
802
|
-
filtered; the renderer owns GPU disposal, while callers own image loading.
|
|
803
|
-
|
|
804
|
-
For textured frames, supply `texcoords` (two floats per vertex) and `surfaces`
|
|
805
|
-
containing `{ firstVertex, vertexCount, materialIndex }`. Ranges must cover the
|
|
806
|
-
whole triangle list in order, refer to the initialization-time materials, and
|
|
807
|
-
contain whole triangles. At most 256 ranges are accepted. Linear vertex colour
|
|
808
|
-
and roughness/metalness factors multiply the maps. Omit both new frame fields to
|
|
809
|
-
retain the existing procedural/untextured path. Main and reflection passes use
|
|
810
|
-
the same authored material; alpha/transmission/skinning are not added here.
|
|
811
|
-
|
|
812
|
-
See [material design](docs/design/native-textured-surfaces.md) and
|
|
813
|
-
[ADR-0031](docs/adrs/adr-0031-native-authored-materials.md). Rollout inherits
|
|
814
|
-
`gpu-demo.scene-fidelity.enabled`; access remains governed by the site's capability.
|
|
815
|
-
|
|
816
|
-
For varnished wood, set optional `clearcoat` and `clearcoatRoughness` (both 0–1).
|
|
817
|
-
The coating reflects neutral light using its own smooth geometric normal above
|
|
818
|
-
the coloured, normal-mapped wood; Fresnel layering attenuates the underlying
|
|
819
|
-
material instead of simply adding shine. Strength defaults to zero, preserving
|
|
820
|
-
uncoated callers. Coating roughness defaults to zero and is independent of the
|
|
821
|
-
base roughness map. Optional `clearcoatMap` uses linear RGBA8: red multiplies
|
|
822
|
-
`clearcoat` coverage and green multiplies `clearcoatRoughness`; blue and alpha
|
|
823
|
-
are ignored. Omission uses white, preserving the scalar-only finish. Use a
|
|
824
|
-
nonzero roughness factor (for example 1) to author roughness directly in green.
|
|
825
|
-
The map shares the surface UVs, mip filtering, memory budget and GPU lifetime of
|
|
826
|
-
other maps. Clearcoat normal maps remain unsupported.
|
|
827
|
-
|
|
828
|
-
For mixed objects, give uncoated parts their own surface ranges with
|
|
829
|
-
`clearcoat: 0`. This keeps rope and hardware uncoated even when they share the
|
|
830
|
-
wood's atlas. The source example [native-crate-finish.js](https://github.com/Plasius-LTD/gpu-shared/tree/main/src/material-study-helpers/native-crate-finish.js)
|
|
831
|
-
provides reviewed crate ranges and a deterministic wear preset combining existing
|
|
832
|
-
roughness/scuff detail with broad UV patches. Its 0–1 wear control changes coat
|
|
833
|
-
coverage and roughness without modifying the underlying colour or source asset.
|
|
834
|
-
See [spatial coating decision](docs/adrs/adr-0032-spatial-native-clearcoat.md) and
|
|
835
|
-
the [fixture/provenance record](docs/design/cc0-demo-asset-review.md). The example
|
|
836
|
-
helper is source-only; the published API is the generic material map.
|
|
837
|
-
|
|
838
|
-
For subtle rope fraying, the source-demo helper
|
|
839
|
-
[native-rope-fibres.js](https://github.com/Plasius-LTD/gpu-shared/tree/main/src/material-study-helpers/native-rope-fibres.js) exports `createRopeFibres`.
|
|
840
|
-
Pass the expanded native `vertices`, `texcoords` and explicit rope-only
|
|
841
|
-
`{ firstVertex, vertexCount }` ranges. It returns separate triangles/UVs and a
|
|
842
|
-
`fibreCount`; append them once with their own draw range. Generate in metre-scale
|
|
843
|
-
world coordinates after transforms. Sampling is deterministic and area-weighted,
|
|
844
|
-
with short tapered curves in small aligned tufts. `density` controls roots per
|
|
845
|
-
square metre; `maxFibres` caps them at 4,000 (default 1,600). `length` and `radius`
|
|
846
|
-
are in metres. The 21 triangles per strand use ordinary native multisampling,
|
|
847
|
-
depth and lighting; no animated fur or dedicated hair scattering is claimed.
|
|
848
|
-
|
|
849
|
-
Optional `fray` (0–1, default 0.08) promotes exactly
|
|
850
|
-
`round(fibreCount * fray)` strands to longer curled loose ends, reported as
|
|
851
|
-
`looseEndCount`. Selection is seeded and nested: raising the level keeps earlier
|
|
852
|
-
loose ends, roots and UVs in place. At 0, short fuzz remains. This is a visual
|
|
853
|
-
proportion of generated fibres, not lost rope strength.
|
|
854
|
-
|
|
855
|
-
Use `createRopeFibreMaterial(ropeMaterial)` for the added range: it keeps the
|
|
856
|
-
rope's colour map and zero clearcoat while omitting the core's baked occlusion
|
|
857
|
-
and normal creases from exposed fibres. Vertex roughness is 1 and metallic is 0.
|
|
858
|
-
The review fixture offers fibre on/off and both rope-detail views. See
|
|
859
|
-
[ADR-0033](docs/adrs/adr-0033-demo-rope-fibres.md). These helpers remain source-only
|
|
860
|
-
and do not add a renderer API, dependency or network request.
|
|
861
|
-
|
|
862
|
-
### Material ageing review wrapper
|
|
863
|
-
|
|
864
|
-
[material-ageing.js](https://github.com/Plasius-LTD/gpu-shared/tree/main/src/material-study-helpers/material-ageing.js) validates and freezes a serializable
|
|
865
|
-
version-1 profile. [native-crate-ageing.js](https://github.com/Plasius-LTD/gpu-shared/tree/main/src/material-study-helpers/native-crate-ageing.js) applies it
|
|
866
|
-
through the existing coating/fibre helpers:
|
|
867
|
-
|
|
868
|
-
```js
|
|
869
|
-
import { createCrateAgeing } from '@plasius/gpu-shared/material-study';
|
|
870
|
-
|
|
871
|
-
const ageing = createCrateAgeing({ ropeFray: 0.30, varnishWear: 0.90, seed: 7349 });
|
|
872
|
-
const { wood, rope, metal, fibres: fibreMaterial } = ageing.createMaterials(sourceMaps);
|
|
873
|
-
const fibreGeometry = ageing.createFibres({ vertices, texcoords, ranges: ropeRanges, heightOffset: -minY });
|
|
874
|
-
// Append fibreGeometry with fibreMaterial using the existing native draw contract.
|
|
875
|
-
```
|
|
876
|
-
|
|
877
|
-
Those values are also the review defaults. `varnishWear` is independent visual
|
|
878
|
-
intensity, not coating area removed or elapsed years. Broad scuff patches expose
|
|
879
|
-
wood while sheltered areas retain coating at intermediate wear. Above 70%, the
|
|
880
|
-
remaining islands smoothly erode; at 100%, every coverage texel and the scalar
|
|
881
|
-
coat strength are zero, matching varnish disabled. The original wood maps remain
|
|
882
|
-
unchanged, including their ordinary substrate light response. `seed` controls fibre placement and
|
|
883
|
-
loose-end selection; varnish uses its existing stable UV pattern. The crate adapter
|
|
884
|
-
uses a 4.5 mm fibre length scale and 0.16 mm root radius, with a 4,000-fibre budget;
|
|
885
|
-
loose ends are 1.3–2 times the length scale and taper to a point. Source images and
|
|
886
|
-
geometry remain unchanged; rope, added fibres and metal never acquire clearcoat.
|
|
887
|
-
|
|
888
|
-
Generate/copy geometry only when the fray channel changes; rebuild finish maps
|
|
889
|
-
only when coating settings change. Camera movement reuses both. The caller still
|
|
890
|
-
owns verified asset ranges, GPU resources and disposal. The local review includes
|
|
891
|
-
fresh/aged presets, fibre/coat toggles and both handle-detail views.
|
|
892
|
-
|
|
893
|
-
This is a tested source-demo wrapper, not a newly published package or a complete
|
|
894
|
-
damage simulator. [ADR-0034](docs/adrs/adr-0034-material-ageing-profile.md) records
|
|
895
|
-
the proposed `@plasius/material-ageing` boundary after a second asset establishes
|
|
896
|
-
the shared contract. Production adoption retains `gpu-demo.scene-fidelity.enabled`
|
|
897
|
-
and the existing GPU route capability; disabling the feature selects the existing
|
|
898
|
-
site fallback. No package publication or site deployment is implied by this study.
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
For structured nap, `createRopeFibres` accepts optional `guides`: at most eight
|
|
902
|
-
polylines of 2–65 finite 3D points, in the same metre-scale coordinates as the
|
|
903
|
-
input geometry. The nearest guide is projected onto the surface and given a
|
|
904
|
-
consistent helical lay. Without a usable projected guide, a finite tangent
|
|
905
|
-
fallback is used. Four nearby roots form a tuft within 1 mm of its anchor, with
|
|
906
|
-
small angular variation, shallow lift and tapered tips. Sampling remains stable
|
|
907
|
-
when fray changes; changing guides reauthors the groom.
|
|
908
|
-
|
|
909
|
-
[native-crate-groom.js](https://github.com/Plasius-LTD/gpu-shared/tree/main/src/material-study-helpers/native-crate-groom.js) supplies the hash-verified
|
|
910
|
-
fixture's loop/knot paths. Pass the same `heightOffset` used to ground its vertices
|
|
911
|
-
(default 0 means unmodified asset coordinates); other transforms require matching
|
|
912
|
-
transformed guide coordinates. The crate adapter adds 84,000 triangles: 90,578
|
|
913
|
-
triangles / 271,734 vertices total, within the native default budget. Its final
|
|
914
|
-
fibre arrays occupy 14,112,000 bytes. This deliberate denser nap replaces the
|
|
915
|
-
previous 1,600-strand loose-end study. See [ADR-0035](docs/adrs/adr-0035-groomed-rope-and-bare-coat-endpoint.md).
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
### In-scene material inspection camera
|
|
919
|
-
|
|
920
|
-
The shared [native-demo-camera.js](https://github.com/Plasius-LTD/gpu-shared/tree/main/src/material-study-helpers/native-demo-camera.js) adapter uses
|
|
921
|
-
released `@plasius/gpu-camera` state/control/projection APIs and the separate
|
|
922
|
-
`@plasius/gpu-camera-controls` pointer gesture runtime/browser normalizer. It maps DOM input to
|
|
923
|
-
orbit, camera-plane truck/pedestal, pan/tilt, dolly and optical zoom; camera math
|
|
924
|
-
is not duplicated in the renderer. The camera and fixture adapters now belong to the isolated gpu-shared material-study
|
|
925
|
-
entry; the renderer root does not load them.
|
|
926
|
-
|
|
927
|
-
```js
|
|
928
|
-
import { createNativeDemoCamera } from '@plasius/gpu-shared/material-study';
|
|
929
|
-
|
|
930
|
-
// Supply a focusable canvas with an accessible name and persistent
|
|
931
|
-
// aria-describedby instructions. The visual example is initially hidden,
|
|
932
|
-
// aria-hidden, pointer-events:none and contains no interactive controls.
|
|
933
|
-
const cameraControls = createNativeDemoCamera({
|
|
934
|
-
canvas,
|
|
935
|
-
camera: { eye: [-0.7, 0.5, 0.8], target: [0, 0.16, 0], fov: 42 },
|
|
936
|
-
onChange: () => draw(),
|
|
937
|
-
onHintChange: visible => { gestureExample.hidden = !visible; },
|
|
938
|
-
});
|
|
939
|
-
// Each draw uses camera: cameraControls.read(). Keep the controls alive across
|
|
940
|
-
// renderer/material rebuilds; defer drawing while the renderer is unavailable.
|
|
941
|
-
// At teardown (also cancels the hint timer):
|
|
942
|
-
cameraControls.destroy();
|
|
943
|
-
```
|
|
944
|
-
|
|
945
|
-
The material review uses direct manipulation with no persistent camera toolbar.
|
|
946
|
-
The optional `onHintChange` callback shows a brief example on canvas focus and
|
|
947
|
-
hides it after five seconds, on blur, Escape or disposal. Refocus or `?` replays
|
|
948
|
-
it; replay replaces the prior timer. The host owns visual presentation: show
|
|
949
|
-
mouse/touch or keyboard examples, preserve a screen-reader description without a
|
|
950
|
-
time limit, and respect reduced motion. Do not move the camera to demonstrate a
|
|
951
|
-
gesture or let the example intercept input. Keep material controls below the scene.
|
|
952
|
-
|
|
953
|
-
| Input | Camera action |
|
|
954
|
-
| --- | --- |
|
|
955
|
-
| Left-drag / one finger | Orbit, with the grabbed surface following the drag on both axes |
|
|
956
|
-
| Right/middle/Shift-drag | Truck / pedestal across the current view |
|
|
957
|
-
| Alt-drag | Pan / tilt in place |
|
|
958
|
-
| Scroll / touch pinch | Move closer or farther (dolly) |
|
|
959
|
-
| Shift-scroll | Change the lens (optical zoom) |
|
|
960
|
-
| Two fingers moving together | Truck / pedestal |
|
|
961
|
-
| Arrows / Shift-arrows | Selected keyboard mode / truck |
|
|
962
|
-
| 1–5 | Select orbit, truck, pan, dolly or lens zoom for keyboard arrows |
|
|
963
|
-
| +/− | Dolly (lens zoom in keyboard zoom mode) |
|
|
964
|
-
| Home | Reset view |
|
|
965
|
-
| ? / Escape | Replay / dismiss the brief guide |
|
|
966
|
-
|
|
967
|
-
Vertical orbit follows the grab direction for mouse, one-finger touch and keyboard
|
|
968
|
-
arrows. The adapter maps single-pointer orbit through gpu-camera; the shared
|
|
969
|
-
gesture runtime retains original pointer coordinates for multi-pointer truck/pinch
|
|
970
|
-
and stable transitions. Keyboard mode selection does not change mouse or touch gestures. The source-only
|
|
971
|
-
`setMode`, `getMode`, `move` and `reset` methods remain available to host code.
|
|
972
|
-
Browser Ctrl/Meta/Alt key shortcuts and keys outside the canvas are left alone.
|
|
973
|
-
Wheel input only belongs to a focused canvas; the surrounding page remains
|
|
974
|
-
scrollable. No inertia or animation loop is introduced. Distances, lens and travel
|
|
975
|
-
are bounded; pointer cancellation/blur/disposal release captures and listeners.
|
|
976
|
-
This is an inspection camera without scene collision detection or object picking.
|
|
977
|
-
|
|
978
|
-
Native material normal-map degeneracy checks now use normalized screen-space
|
|
979
|
-
position/UV gradients. Valid tiny derivatives in rope macro views no longer disable
|
|
980
|
-
normal maps. Mirrored UV handedness and geometric-normal fallback for degenerate
|
|
981
|
-
UVs are retained in the common direct/reflection shader. The shader contract tests
|
|
982
|
-
require browser rendering evidence as well; Node mocks do not execute WGSL.
|
|
983
|
-
See [ADR-0036](docs/adrs/adr-0036-in-scene-material-inspection.md).
|
|
984
|
-
|
|
985
|
-
### Shared material-study runtime
|
|
986
|
-
|
|
987
|
-
Production demo helpers and their regression tests now live in `@plasius/gpu-shared/material-study`. The renderer exposes `validateNativeSurfaceMaterials(materials)` so authoring and GPU creation use identical validation without allocation. See [ADR-0037](docs/adrs/adr-0037-shared-material-study-runtime.md).
|
|
988
|
-
|
|
989
|
-
## Progressive still-frame refinement
|
|
990
|
-
|
|
991
|
-
Create the wavefront renderer with `progressive: true` to retain linear radiance across frames. `samplesPerPixel` at creation is the maximum batch capacity; pass a smaller value to `renderOnce({ samplesPerPixel })` or `renderFrame({ samplesPerPixel })`. Submit batches serially. `renderFrame()` awaits completion by default; with `renderOnce()`, await `renderer.device.queue.onSubmittedWorkDone()` before reporting completed samples. `getSnapshot().accumulatedSamplesPerPixel` reports submitted history samples.
|
|
992
|
-
|
|
993
|
-
Camera or scene updates reset history automatically; `resetAccumulation()` also discards it without reallocating. Do not call `updateCamera()` between refinement passes unless the view changed. Progressive mode uses independent frame seeds, overriding static-frame stable sample routing. Denoising is applied after accumulation. Leave `progressive` unset for the existing independent-frame path. See [ADR 0038](docs/adrs/adr-0038-progressive-linear-radiance.md) for memory costs and host rollout responsibilities.
|