@yali-engine/render 0.0.0-stage → 0.4.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.
- package/dist/bvh.d.ts +66 -0
- package/dist/bvh.js +493 -0
- package/dist/bvh.js.map +1 -0
- package/dist/camera.d.ts +34 -0
- package/dist/camera.js +66 -0
- package/dist/camera.js.map +1 -0
- package/dist/cluster-build.d.ts +46 -0
- package/dist/cluster-build.js +179 -0
- package/dist/cluster-build.js.map +1 -0
- package/dist/cluster-pages.d.ts +70 -0
- package/dist/cluster-pages.js +203 -0
- package/dist/cluster-pages.js.map +1 -0
- package/dist/cluster-stream.d.ts +83 -0
- package/dist/cluster-stream.js +226 -0
- package/dist/cluster-stream.js.map +1 -0
- package/dist/culled-draws.d.ts +30 -0
- package/dist/culled-draws.js +103 -0
- package/dist/culled-draws.js.map +1 -0
- package/dist/debug-lines.d.ts +51 -0
- package/dist/debug-lines.js +158 -0
- package/dist/debug-lines.js.map +1 -0
- package/dist/device-profile.d.ts +100 -0
- package/dist/device-profile.js +131 -0
- package/dist/device-profile.js.map +1 -0
- package/dist/device.d.ts +39 -0
- package/dist/device.js +79 -0
- package/dist/device.js.map +1 -0
- package/dist/environment.d.ts +60 -0
- package/dist/environment.js +204 -0
- package/dist/environment.js.map +1 -0
- package/dist/extract.d.ts +74 -0
- package/dist/extract.js +194 -0
- package/dist/extract.js.map +1 -0
- package/dist/frame-graph.d.ts +112 -0
- package/dist/frame-graph.js +226 -0
- package/dist/frame-graph.js.map +1 -0
- package/dist/gpu-health.d.ts +74 -0
- package/dist/gpu-health.js +106 -0
- package/dist/gpu-health.js.map +1 -0
- package/dist/gpu-timer.d.ts +26 -0
- package/dist/gpu-timer.js +93 -0
- package/dist/gpu-timer.js.map +1 -0
- package/dist/headless.d.ts +28 -0
- package/dist/headless.js +91 -0
- package/dist/headless.js.map +1 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.js +36 -0
- package/dist/index.js.map +1 -0
- package/dist/lights.d.ts +81 -0
- package/dist/lights.js +247 -0
- package/dist/lights.js.map +1 -0
- package/dist/matrix.d.ts +6 -0
- package/dist/matrix.js +59 -0
- package/dist/matrix.js.map +1 -0
- package/dist/meshes.d.ts +411 -0
- package/dist/meshes.js +545 -0
- package/dist/meshes.js.map +1 -0
- package/dist/meshlets-decoder.d.ts +20 -0
- package/dist/meshlets-decoder.js +47 -0
- package/dist/meshlets-decoder.js.map +1 -0
- package/dist/meshlets.d.ts +186 -0
- package/dist/meshlets.js +1040 -0
- package/dist/meshlets.js.map +1 -0
- package/dist/occlusion.d.ts +51 -0
- package/dist/occlusion.js +188 -0
- package/dist/occlusion.js.map +1 -0
- package/dist/pick-decoder.d.ts +19 -0
- package/dist/pick-decoder.js +45 -0
- package/dist/pick-decoder.js.map +1 -0
- package/dist/pick.d.ts +86 -0
- package/dist/pick.js +452 -0
- package/dist/pick.js.map +1 -0
- package/dist/pipeline-cache.d.ts +53 -0
- package/dist/pipeline-cache.js +96 -0
- package/dist/pipeline-cache.js.map +1 -0
- package/dist/post-options.d.ts +109 -0
- package/dist/post-options.js +23 -0
- package/dist/post-options.js.map +1 -0
- package/dist/post-shaders.gen.d.ts +40 -0
- package/dist/post-shaders.gen.js +43 -0
- package/dist/post-shaders.gen.js.map +1 -0
- package/dist/post.d.ts +101 -0
- package/dist/post.js +555 -0
- package/dist/post.js.map +1 -0
- package/dist/render-plugin.d.ts +239 -0
- package/dist/render-plugin.js +633 -0
- package/dist/render-plugin.js.map +1 -0
- package/dist/scene.d.ts +406 -0
- package/dist/scene.js +2005 -0
- package/dist/scene.js.map +1 -0
- package/dist/shaders.gen.d.ts +40 -0
- package/dist/shaders.gen.js +43 -0
- package/dist/shaders.gen.js.map +1 -0
- package/dist/shadows.d.ts +104 -0
- package/dist/shadows.js +227 -0
- package/dist/shadows.js.map +1 -0
- package/dist/staging-ring.d.ts +41 -0
- package/dist/staging-ring.js +125 -0
- package/dist/staging-ring.js.map +1 -0
- package/dist/textures.d.ts +165 -0
- package/dist/textures.js +361 -0
- package/dist/textures.js.map +1 -0
- package/dist/tonemap.d.ts +45 -0
- package/dist/tonemap.js +141 -0
- package/dist/tonemap.js.map +1 -0
- package/dist/viewport.d.ts +78 -0
- package/dist/viewport.js +130 -0
- package/dist/viewport.js.map +1 -0
- package/dist/webgl2/plugin.d.ts +40 -0
- package/dist/webgl2/plugin.js +190 -0
- package/dist/webgl2/plugin.js.map +1 -0
- package/dist/webgl2/program.d.ts +27 -0
- package/dist/webgl2/program.js +74 -0
- package/dist/webgl2/program.js.map +1 -0
- package/dist/webgl2/renderer.d.ts +128 -0
- package/dist/webgl2/renderer.js +976 -0
- package/dist/webgl2/renderer.js.map +1 -0
- package/dist/webgl2/shaders.glsl.gen.d.ts +27 -0
- package/dist/webgl2/shaders.glsl.gen.js +20 -0
- package/dist/webgl2/shaders.glsl.gen.js.map +1 -0
- package/dist/webgl2/textures.d.ts +11 -0
- package/dist/webgl2/textures.js +82 -0
- package/dist/webgl2/textures.js.map +1 -0
- package/dist/webgl2/webgl2.d.ts +1 -0
- package/dist/webgl2/webgl2.js +4 -0
- package/dist/webgl2/webgl2.js.map +1 -0
- package/dist/worker-mode.d.ts +85 -0
- package/dist/worker-mode.js +204 -0
- package/dist/worker-mode.js.map +1 -0
- package/package.json +47 -3
- package/README.md +0 -3
package/dist/headless.js
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { YaliError } from '@yali-engine/core';
|
|
2
|
+
import { OrbitCamera } from "./camera.js";
|
|
3
|
+
import { InstanceExtractor } from "./extract.js";
|
|
4
|
+
import { MaterialStore, MeshStore, registerMeshComponent } from "./meshes.js";
|
|
5
|
+
import { registerLightComponent } from "./lights.js";
|
|
6
|
+
import { TextureStore } from "./textures.js";
|
|
7
|
+
/**
|
|
8
|
+
* The render plugin for a run without a GPU (DESIGN.md §19 headless mode; ADR-0041): it
|
|
9
|
+
* registers as `@yali-engine/render`, so game plugins that depend on it build unchanged, and
|
|
10
|
+
* provides a `render` resource with what game code uses: the camera (updated each frame,
|
|
11
|
+
* as the real plugin does), the mesh and material stores, `render.Mesh`, and the counters.
|
|
12
|
+
* It runs the same extract as the real plugin and checksums the records instead of
|
|
13
|
+
* drawing them. GPU members (`gpu`, `scene`, `graph`, `timer`, `pipelines`, `health`,
|
|
14
|
+
* `profile`, `viewport`) are absent; `capture()` rejects with `NO_GPU`.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* const app = App.create({ core, seed: 1, frames: manual, clock: fixed, plugins: [headlessRenderPlugin(), game] });
|
|
18
|
+
* await app.init();
|
|
19
|
+
* for (let i = 0; i < 120; i++) app.step(1 / 60);
|
|
20
|
+
* app.resource<RenderContext & { stats: HeadlessFrameStats }>('render').stats.checksum;
|
|
21
|
+
*/
|
|
22
|
+
export function headlessRenderPlugin(options = {}) {
|
|
23
|
+
return {
|
|
24
|
+
name: '@yali-engine/render',
|
|
25
|
+
version: '0.4.0',
|
|
26
|
+
build(b) {
|
|
27
|
+
const camera = new OrbitCamera();
|
|
28
|
+
const stats = { frames: 0, instances: 0, checksum: 0 };
|
|
29
|
+
let Mesh = null;
|
|
30
|
+
let Light = null;
|
|
31
|
+
let extractor = null;
|
|
32
|
+
try {
|
|
33
|
+
Mesh = registerMeshComponent(b.app.world);
|
|
34
|
+
Light = registerLightComponent(b.app.world);
|
|
35
|
+
extractor = new InstanceExtractor(b.app.world, Mesh);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// no world, no meshes
|
|
39
|
+
}
|
|
40
|
+
const meshes = new MeshStore();
|
|
41
|
+
const ctx = {
|
|
42
|
+
meshes,
|
|
43
|
+
materials: new MaterialStore(),
|
|
44
|
+
// No device: textures are kept (and counted) but never uploaded.
|
|
45
|
+
textures: new TextureStore({ family: 'rgba8', size: 256 }),
|
|
46
|
+
Mesh,
|
|
47
|
+
Light,
|
|
48
|
+
camera,
|
|
49
|
+
tier: 't0',
|
|
50
|
+
recoveries: 0,
|
|
51
|
+
submitMs: 0,
|
|
52
|
+
uploadMs: 0,
|
|
53
|
+
maxFramesInFlight: 0,
|
|
54
|
+
framesInFlight: 0,
|
|
55
|
+
skippedFrames: 0,
|
|
56
|
+
stats,
|
|
57
|
+
capture() {
|
|
58
|
+
return Promise.reject(new YaliError('NO_GPU', 'A headless run has no GPU frame to capture.', 'Capture in a browser (the golden-image script), or compare the headless world hash and instance checksum.'));
|
|
59
|
+
},
|
|
60
|
+
simulateDeviceLoss() { },
|
|
61
|
+
};
|
|
62
|
+
b.provide('render', ctx);
|
|
63
|
+
const aspect = options.aspect ?? 16 / 9;
|
|
64
|
+
b.system({ name: 'render.camera', stage: 'PostUpdate', run: () => camera.update(aspect) });
|
|
65
|
+
b.system({ name: 'render.extract', stage: 'Extract', run: () => void extractor?.run(camera.eye, meshes.entries.length) });
|
|
66
|
+
b.onDispose(() => extractor?.dispose());
|
|
67
|
+
b.system({
|
|
68
|
+
name: 'render.submit',
|
|
69
|
+
stage: 'Submit',
|
|
70
|
+
run: () => {
|
|
71
|
+
stats.frames++;
|
|
72
|
+
if (!extractor)
|
|
73
|
+
return;
|
|
74
|
+
const heap = extractor.world.core.heap.u8;
|
|
75
|
+
const { address, count, cap } = extractor;
|
|
76
|
+
// The transform region, then the static region (ADR-0055), byte by byte: no view,
|
|
77
|
+
// no allocation.
|
|
78
|
+
let h = 0x811c9dc5;
|
|
79
|
+
for (let i = address, end = address + count * 32; i < end; i++)
|
|
80
|
+
h = Math.imul(h ^ heap[i], 0x01000193) >>> 0;
|
|
81
|
+
for (let i = address + cap * 32, end = i + count * 16; i < end; i++)
|
|
82
|
+
h = Math.imul(h ^ heap[i], 0x01000193) >>> 0;
|
|
83
|
+
stats.instances = count;
|
|
84
|
+
stats.checksum = h;
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
b.counter('render.instances', () => stats.instances);
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=headless.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"headless.js","sourceRoot":"","sources":["../src/headless.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/// <reference types="@webgpu/types" preserve="true" />
|
|
2
|
+
/**
|
|
3
|
+
* `@yali-engine/render`: the WebGPU render backend (DESIGN.md §11–§14).
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* // Most apps use the plugin: every entity with `render.Mesh` and a GlobalTransform is drawn.
|
|
7
|
+
* const app = App.create({ core, plugins: [renderPlugin({ canvas }), transformsPlugin(), game] });
|
|
8
|
+
* // in the game: world.insert(e, render.Mesh!, { mesh: CUBE_MESH, material: 0, color: rgba8(1, 0.5, 0.2) });
|
|
9
|
+
*/
|
|
10
|
+
export { OrbitCamera } from './camera.ts';
|
|
11
|
+
export { DEBUG_VERTEX_BYTES, DebugLines } from './debug-lines.ts';
|
|
12
|
+
export { type Backend, type Gpu, acquireGpu } from './device.ts';
|
|
13
|
+
export { type ComputePassDef, FrameGraph, type FrameContext, type PassDef, type PassTimer, type RenderPassDef, type TransientTexture, } from './frame-graph.ts';
|
|
14
|
+
export { DEVICE_FILTER, type DeviceFilterRule, type DisableableFeature, type DeviceProfile, type Tier, collectDeviceProfile, selectTier, } from './device-profile.ts';
|
|
15
|
+
export { type GpuDiagEvent, GpuHealth, type GpuHealthState, isRecoverableAttachmentMismatch } from './gpu-health.ts';
|
|
16
|
+
export { GpuTimer } from './gpu-timer.ts';
|
|
17
|
+
export { type CachedComputeDesc, type CachedRenderDesc, type LinkedShader, PipelineCache, type PipelineCacheStats } from './pipeline-cache.ts';
|
|
18
|
+
export { type EnvironmentControls, type FrameCapture, HDR_FORMAT, type RenderBackend, type RenderContext, type RenderPluginOptions, renderPlugin, type ShadowControls, type ToneControls, } from './render-plugin.ts';
|
|
19
|
+
export { type ToneCurve, ToneMapper } from './tonemap.ts';
|
|
20
|
+
export { type BloomOptions, type GradingOptions, gradingDefaults, type PostControls, type PostOptions, type PostPasses, whiteBalance } from './post-options.ts';
|
|
21
|
+
export { halton, invert4 } from './matrix.ts';
|
|
22
|
+
export { Environment, projectSh9, type SkyOptions, skyRadiance } from './environment.ts';
|
|
23
|
+
export { ClusteredLights, LIGHT_KIND, MAX_LIGHTS, registerLightComponent } from './lights.ts';
|
|
24
|
+
export { MAX_CASCADES, type ShadowOptions, SunShadows } from './shadows.ts';
|
|
25
|
+
export { type HeadlessFrameStats, headlessRenderPlugin } from './headless.ts';
|
|
26
|
+
export { CUBE_MESH, type LodRange, MAX_LODS, MATERIAL_WORDS, MAX_MATERIALS, MAX_SHADERS, type MaterialData, type MaterialShader, MaterialStore, MESH_VERTEX_FLOATS, type MeshData, type MeshEntry, type MeshLod, type MeshState, type PrebuiltClusters, type StreamedClusters, MeshStore, DRAW_CLASSES, drawClassOf, isSkinnedClass, packSkin, RIGID_CLASSES, SKIN_STREAM_BYTES, SKINNED_CLASS, registerMeshComponent, rgba8, type TextureSource, unpackRgba8, } from './meshes.ts';
|
|
27
|
+
export { type ColorSpace, FAMILY_FEATURE, packTextureSlot, pickFamily, type TextureData, type TextureFamily, type TextureBackend, TextureRef, TEXTURE_SLOT, TextureStore, webgpuTextureBackend, } from './textures.ts';
|
|
28
|
+
export { type ExtractedInstances, InstanceExtractor } from './extract.ts';
|
|
29
|
+
export { StagingRing } from './staging-ring.ts';
|
|
30
|
+
export { computeRenderSize, Viewport, type ViewportOptions, type ViewportSize } from './viewport.ts';
|
|
31
|
+
export { CULLED_BYTES, INSTANCE_BYTES, type LightingOptions, PALETTE_WIDTH, type PaletteSource, type PaletteView, type SceneControls, SceneRenderer } from './scene.ts';
|
|
32
|
+
export { connectRenderWorker, type ForwardedInput, hostRenderWorker, type RenderWorkerHost, type RenderWorkerLink } from './worker-mode.ts';
|
|
33
|
+
export { buildBvh, intersectAll, intersectBvh, type MeshBvh, refitBvh, type TriangleHit } from './bvh.ts';
|
|
34
|
+
export { Picker, type PickHit, type PickOptions } from './pick.ts';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// The WebGPU types this package's API uses, for consumers (kept in the emitted .d.ts).
|
|
2
|
+
/// <reference types="@webgpu/types" preserve="true" />
|
|
3
|
+
/**
|
|
4
|
+
* `@yali-engine/render`: the WebGPU render backend (DESIGN.md §11–§14).
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* // Most apps use the plugin: every entity with `render.Mesh` and a GlobalTransform is drawn.
|
|
8
|
+
* const app = App.create({ core, plugins: [renderPlugin({ canvas }), transformsPlugin(), game] });
|
|
9
|
+
* // in the game: world.insert(e, render.Mesh!, { mesh: CUBE_MESH, material: 0, color: rgba8(1, 0.5, 0.2) });
|
|
10
|
+
*/
|
|
11
|
+
export { OrbitCamera } from "./camera.js";
|
|
12
|
+
export { DEBUG_VERTEX_BYTES, DebugLines } from "./debug-lines.js";
|
|
13
|
+
export { acquireGpu } from "./device.js";
|
|
14
|
+
export { FrameGraph, } from "./frame-graph.js";
|
|
15
|
+
export { DEVICE_FILTER, collectDeviceProfile, selectTier, } from "./device-profile.js";
|
|
16
|
+
export { GpuHealth, isRecoverableAttachmentMismatch } from "./gpu-health.js";
|
|
17
|
+
export { GpuTimer } from "./gpu-timer.js";
|
|
18
|
+
export { PipelineCache } from "./pipeline-cache.js";
|
|
19
|
+
export { HDR_FORMAT, renderPlugin, } from "./render-plugin.js";
|
|
20
|
+
export { ToneMapper } from "./tonemap.js";
|
|
21
|
+
export { gradingDefaults, whiteBalance } from "./post-options.js";
|
|
22
|
+
export { halton, invert4 } from "./matrix.js";
|
|
23
|
+
export { Environment, projectSh9, skyRadiance } from "./environment.js";
|
|
24
|
+
export { ClusteredLights, LIGHT_KIND, MAX_LIGHTS, registerLightComponent } from "./lights.js";
|
|
25
|
+
export { MAX_CASCADES, SunShadows } from "./shadows.js";
|
|
26
|
+
export { headlessRenderPlugin } from "./headless.js";
|
|
27
|
+
export { CUBE_MESH, MAX_LODS, MATERIAL_WORDS, MAX_MATERIALS, MAX_SHADERS, MaterialStore, MESH_VERTEX_FLOATS, MeshStore, DRAW_CLASSES, drawClassOf, isSkinnedClass, packSkin, RIGID_CLASSES, SKIN_STREAM_BYTES, SKINNED_CLASS, registerMeshComponent, rgba8, unpackRgba8, } from "./meshes.js";
|
|
28
|
+
export { FAMILY_FEATURE, packTextureSlot, pickFamily, TextureRef, TEXTURE_SLOT, TextureStore, webgpuTextureBackend, } from "./textures.js";
|
|
29
|
+
export { InstanceExtractor } from "./extract.js";
|
|
30
|
+
export { StagingRing } from "./staging-ring.js";
|
|
31
|
+
export { computeRenderSize, Viewport } from "./viewport.js";
|
|
32
|
+
export { CULLED_BYTES, INSTANCE_BYTES, PALETTE_WIDTH, SceneRenderer } from "./scene.js";
|
|
33
|
+
export { connectRenderWorker, hostRenderWorker } from "./worker-mode.js";
|
|
34
|
+
export { buildBvh, intersectAll, intersectBvh, refitBvh } from "./bvh.js";
|
|
35
|
+
export { Picker } from "./pick.js";
|
|
36
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,uFAAuF;AACvF,uDAAuD;AACvD;;;;;;;GAOG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAClE,OAAO,EAA0B,UAAU,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,EAEL,UAAU,GAMX,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,aAAa,EAKb,oBAAoB,EACpB,UAAU,GACX,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAqB,SAAS,EAAuB,+BAA+B,EAAE,MAAM,iBAAiB,CAAC;AACrH,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAoE,aAAa,EAA2B,MAAM,qBAAqB,CAAC;AAC/I,OAAO,EAGL,UAAU,EAIV,YAAY,GAGb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAkB,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,EAA0C,eAAe,EAAwD,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAChK,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,UAAU,EAAmB,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACzF,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,UAAU,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAC9F,OAAO,EAAE,YAAY,EAAsB,UAAU,EAAE,MAAM,cAAc,CAAC;AAC5E,OAAO,EAA2B,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAC9E,OAAO,EACL,SAAS,EAET,QAAQ,EACR,cAAc,EACd,aAAa,EACb,WAAW,EAGX,aAAa,EACb,kBAAkB,EAOlB,SAAS,EACT,YAAY,EACZ,WAAW,EACX,cAAc,EACd,QAAQ,EACR,aAAa,EACb,iBAAiB,EACjB,aAAa,EACb,qBAAqB,EACrB,KAAK,EAEL,WAAW,GACZ,MAAM,aAAa,CAAC;AACrB,OAAO,EAEL,cAAc,EACd,eAAe,EACf,UAAU,EAIV,UAAU,EACV,YAAY,EACZ,YAAY,EACZ,oBAAoB,GACrB,MAAM,eAAe,CAAC;AACvB,OAAO,EAA2B,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAC1E,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,QAAQ,EAA2C,MAAM,eAAe,CAAC;AACrG,OAAO,EAAE,YAAY,EAAE,cAAc,EAAwB,aAAa,EAA4D,aAAa,EAAE,MAAM,YAAY,CAAC;AACxK,OAAO,EAAE,mBAAmB,EAAuB,gBAAgB,EAAgD,MAAM,kBAAkB,CAAC;AAC5I,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,YAAY,EAAgB,QAAQ,EAAoB,MAAM,UAAU,CAAC;AAC1G,OAAO,EAAE,MAAM,EAAkC,MAAM,WAAW,CAAC"}
|
package/dist/lights.d.ts
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { type Component, type World } from '@yali-engine/core';
|
|
2
|
+
import type { OrbitCamera } from './camera.ts';
|
|
3
|
+
import type { FrameGraph } from './frame-graph.ts';
|
|
4
|
+
import type { PipelineCache } from './pipeline-cache.ts';
|
|
5
|
+
/** Light kinds of `render.Light` (`kind`). */
|
|
6
|
+
export declare const LIGHT_KIND: {
|
|
7
|
+
readonly point: 0;
|
|
8
|
+
readonly spot: 1;
|
|
9
|
+
};
|
|
10
|
+
/** Most lights drawn (the uniform array, `shaders/scene/lights.wesl` `MAX_LIGHTS`). */
|
|
11
|
+
export declare const MAX_LIGHTS = 256;
|
|
12
|
+
/** Clusters in the froxel grid (rows of the bins texture). */
|
|
13
|
+
export declare const CLUSTERS: number;
|
|
14
|
+
/** Texels per cluster row: its count, then up to 63 light indices. */
|
|
15
|
+
export declare const CLUSTER_ROW = 64;
|
|
16
|
+
/**
|
|
17
|
+
* The `render.Light` component (M2 increment 6, ADR-0067): a point or spot light at its
|
|
18
|
+
* entity's `GlobalTransform` (a spot points along the entity's −Z). `color` is packed linear
|
|
19
|
+
* RGBA8 (`rgba8`), `intensity` the radiance at 1 m, `range` where it fades to zero, the cone
|
|
20
|
+
* angles in radians (spot only).
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* const e = world.spawn([...transforms.bundle, render.Light!]);
|
|
24
|
+
* world.insert(e, Transform, { t: [0, 3, 0], r: [0, 0, 0, 1], s: 1 });
|
|
25
|
+
* world.insert(e, render.Light!, { kind: LIGHT_KIND.point, color: rgba8(1, 0.6, 0.3), intensity: 40, range: 12 });
|
|
26
|
+
*/
|
|
27
|
+
export declare function registerLightComponent(world: World): Component;
|
|
28
|
+
/**
|
|
29
|
+
* The backend-neutral half of clustered lighting (ADR-0067): every `render.Light` packed
|
|
30
|
+
* camera-relative into the shaders' `Lights` array (`lights`, 16 floats each), and the
|
|
31
|
+
* view the clusters are built for (`params`, the shaders' `ClusterParams`). WebGPU bins on
|
|
32
|
+
* the GPU (`ClusteredLights`), WebGL2 on the CPU (`binLights`).
|
|
33
|
+
*/
|
|
34
|
+
export declare class LightPacker {
|
|
35
|
+
#private;
|
|
36
|
+
/** Lights extracted this frame (at most `MAX_LIGHTS`; the rest are dropped and counted). */
|
|
37
|
+
count: number;
|
|
38
|
+
/** Lights over the limit last frame. */
|
|
39
|
+
dropped: number;
|
|
40
|
+
readonly lights: Float32Array<ArrayBuffer>;
|
|
41
|
+
readonly params: Float32Array<ArrayBuffer>;
|
|
42
|
+
constructor();
|
|
43
|
+
/** Packs every `render.Light` relative to `camera`'s eye, and the cluster parameters for a `width × height` target. */
|
|
44
|
+
pack(world: World | null, Light: Component | null, camera: OrbitCamera, width: number, height: number): void;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Bins the packed lights into the froxel grid on the CPU, into `bins` (`CLUSTERS` rows of
|
|
48
|
+
* `CLUSTER_ROW` words: the count, then the indices), for backends without compute (WebGL2).
|
|
49
|
+
* Exactly `shaders/scene/cluster.wesl`'s rows (the same sphere–box test, indices ascending,
|
|
50
|
+
* at most 63), but separably: the boxes' bounds are tabulated per slice and per tile column
|
|
51
|
+
* and row, and a light visits only the slices its depth reaches, the columns within its
|
|
52
|
+
* radius there, and the rows within what is left, not all 3,456 clusters. `counts` is
|
|
53
|
+
* scratch of `CLUSTERS` words.
|
|
54
|
+
*/
|
|
55
|
+
export declare function binLights(lights: Float32Array, params: Float32Array, bins: Uint32Array, counts: Uint32Array): void;
|
|
56
|
+
/**
|
|
57
|
+
* Clustered forward lighting (ADR-0067): extracts every `render.Light` each frame (camera-
|
|
58
|
+
* relative, into a 16 KiB uniform array), and a compute pass bins them into a 16×9×24 froxel
|
|
59
|
+
* grid stored in an r32uint texture, which the draw's fragment stage reads with `textureLoad`
|
|
60
|
+
* (compatibility mode may allow it no storage buffers).
|
|
61
|
+
*/
|
|
62
|
+
export declare class ClusteredLights {
|
|
63
|
+
#private;
|
|
64
|
+
readonly device: GPUDevice;
|
|
65
|
+
readonly lightsBuffer: GPUBuffer;
|
|
66
|
+
readonly paramsBuffer: GPUBuffer;
|
|
67
|
+
readonly bins: GPUTexture;
|
|
68
|
+
constructor(device: GPUDevice);
|
|
69
|
+
/** Lights extracted this frame (at most `MAX_LIGHTS`; the rest are dropped and counted). */
|
|
70
|
+
get count(): number;
|
|
71
|
+
/** Lights over the limit last frame. */
|
|
72
|
+
get dropped(): number;
|
|
73
|
+
/** This frame's packed lights and cluster parameters (what the binning pass reads). */
|
|
74
|
+
get packed(): LightPacker;
|
|
75
|
+
init(cache: PipelineCache): Promise<void>;
|
|
76
|
+
/** Packs every `render.Light` relative to `camera`'s eye and uploads lights and cluster parameters. */
|
|
77
|
+
update(world: World | null, Light: Component | null, camera: OrbitCamera, width: number, height: number): void;
|
|
78
|
+
/** Adds `scene.lights`, the binning pass (skipped when there are no lights). */
|
|
79
|
+
addPass(graph: FrameGraph): void;
|
|
80
|
+
destroy(): void;
|
|
81
|
+
}
|
package/dist/lights.js
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import { registerTransforms, YaliError } from '@yali-engine/core';
|
|
2
|
+
import { sceneCluster } from "./shaders.gen.js";
|
|
3
|
+
/** Light kinds of `render.Light` (`kind`). */
|
|
4
|
+
export const LIGHT_KIND = { point: 0, spot: 1 };
|
|
5
|
+
/** Most lights drawn (the uniform array, `shaders/scene/lights.wesl` `MAX_LIGHTS`). */
|
|
6
|
+
export const MAX_LIGHTS = 256;
|
|
7
|
+
const GRID = { x: 16, y: 9, z: 24 };
|
|
8
|
+
/** Clusters in the froxel grid (rows of the bins texture). */
|
|
9
|
+
export const CLUSTERS = GRID.x * GRID.y * GRID.z;
|
|
10
|
+
/** Texels per cluster row: its count, then up to 63 light indices. */
|
|
11
|
+
export const CLUSTER_ROW = 64;
|
|
12
|
+
const ROW = CLUSTER_ROW;
|
|
13
|
+
/** The last depth slice's end (metres): lights farther than this from the camera are not binned. */
|
|
14
|
+
const FAR = 500;
|
|
15
|
+
/**
|
|
16
|
+
* The `render.Light` component (M2 increment 6, ADR-0067): a point or spot light at its
|
|
17
|
+
* entity's `GlobalTransform` (a spot points along the entity's −Z). `color` is packed linear
|
|
18
|
+
* RGBA8 (`rgba8`), `intensity` the radiance at 1 m, `range` where it fades to zero, the cone
|
|
19
|
+
* angles in radians (spot only).
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* const e = world.spawn([...transforms.bundle, render.Light!]);
|
|
23
|
+
* world.insert(e, Transform, { t: [0, 3, 0], r: [0, 0, 0, 1], s: 1 });
|
|
24
|
+
* world.insert(e, render.Light!, { kind: LIGHT_KIND.point, color: rgba8(1, 0.6, 0.3), intensity: 40, range: 12 });
|
|
25
|
+
*/
|
|
26
|
+
export function registerLightComponent(world) {
|
|
27
|
+
return world.registerComponent({ name: 'render.Light', fields: { kind: 'u32', color: 'u32', intensity: 'f32', range: 'f32', inner: 'f32', outer: 'f32' } });
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The backend-neutral half of clustered lighting (ADR-0067): every `render.Light` packed
|
|
31
|
+
* camera-relative into the shaders' `Lights` array (`lights`, 16 floats each), and the
|
|
32
|
+
* view the clusters are built for (`params`, the shaders' `ClusterParams`). WebGPU bins on
|
|
33
|
+
* the GPU (`ClusteredLights`), WebGL2 on the CPU (`binLights`).
|
|
34
|
+
*/
|
|
35
|
+
export class LightPacker {
|
|
36
|
+
/** Lights extracted this frame (at most `MAX_LIGHTS`; the rest are dropped and counted). */
|
|
37
|
+
count = 0;
|
|
38
|
+
/** Lights over the limit last frame. */
|
|
39
|
+
dropped = 0;
|
|
40
|
+
lights = new Float32Array(MAX_LIGHTS * 16);
|
|
41
|
+
params = new Float32Array(20);
|
|
42
|
+
#query = null;
|
|
43
|
+
#eye = new Float64Array(3);
|
|
44
|
+
#packChunk;
|
|
45
|
+
constructor() {
|
|
46
|
+
// One function made once: the per-frame pass allocates nothing.
|
|
47
|
+
this.#packChunk = (c) => {
|
|
48
|
+
const t = c.f64(0), r = c.f32(1), kind = c.u32(2), color = c.u32(3), intensity = c.f32(4), range = c.f32(5), inner = c.f32(6), outer = c.f32(7);
|
|
49
|
+
const out = this.lights;
|
|
50
|
+
const [ex, ey, ez] = this.#eye;
|
|
51
|
+
for (let i = 0; i < c.length; i++) {
|
|
52
|
+
if (this.count >= MAX_LIGHTS) {
|
|
53
|
+
this.dropped++;
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
const o = this.count * 16;
|
|
57
|
+
const col = color[i];
|
|
58
|
+
const k = intensity[i];
|
|
59
|
+
out[o] = t[i * 3] - ex;
|
|
60
|
+
out[o + 1] = t[i * 3 + 1] - ey;
|
|
61
|
+
out[o + 2] = t[i * 3 + 2] - ez;
|
|
62
|
+
out[o + 3] = Math.max(1e-3, range[i]);
|
|
63
|
+
out[o + 4] = ((col & 255) / 255) * k;
|
|
64
|
+
out[o + 5] = (((col >>> 8) & 255) / 255) * k;
|
|
65
|
+
out[o + 6] = (((col >>> 16) & 255) / 255) * k;
|
|
66
|
+
out[o + 7] = kind[i];
|
|
67
|
+
// A spot's direction: the rotation applied to −Z.
|
|
68
|
+
const qx = r[i * 4], qy = r[i * 4 + 1], qz = r[i * 4 + 2], qw = r[i * 4 + 3];
|
|
69
|
+
out[o + 8] = -2 * (qx * qz + qw * qy);
|
|
70
|
+
out[o + 9] = -2 * (qy * qz - qw * qx);
|
|
71
|
+
out[o + 10] = -(1 - 2 * (qx * qx + qy * qy));
|
|
72
|
+
out[o + 11] = Math.cos(outer[i]);
|
|
73
|
+
out[o + 12] = Math.cos(Math.min(inner[i], outer[i]));
|
|
74
|
+
this.count++;
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
/** Packs every `render.Light` relative to `camera`'s eye, and the cluster parameters for a `width × height` target. */
|
|
79
|
+
pack(world, Light, camera, width, height) {
|
|
80
|
+
this.count = 0;
|
|
81
|
+
this.dropped = 0;
|
|
82
|
+
if (world && Light) {
|
|
83
|
+
if (!this.#query) {
|
|
84
|
+
const { GlobalTransform } = registerTransforms(world);
|
|
85
|
+
this.#query = world.query({
|
|
86
|
+
with: [Light, GlobalTransform],
|
|
87
|
+
columns: [[GlobalTransform, 't'], [GlobalTransform, 'r'], [Light, 'kind'], [Light, 'color'], [Light, 'intensity'], [Light, 'range'], [Light, 'inner'], [Light, 'outer']],
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
this.#eye.set(camera.eye);
|
|
91
|
+
this.#query.forEachChunk(this.#packChunk);
|
|
92
|
+
}
|
|
93
|
+
const v = camera.view;
|
|
94
|
+
const tanY = Math.tan(camera.fovY / 2);
|
|
95
|
+
this.params.set([v[0], v[4], v[8], 0, v[1], v[5], v[9], 0, v[2], v[6], v[10], 0, tanY * (width / Math.max(1, height)), tanY, camera.near, FAR, width, height, this.count, Math.log2(FAR / camera.near)]);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/** Per-call tables of `binLights`: each slice's and each (slice, tile column / row)'s box bounds. */
|
|
99
|
+
const BIN_TABLES = { z: new Float32Array(GRID.z * 2), x: new Float32Array(GRID.z * GRID.x * 2), y: new Float32Array(GRID.z * GRID.y * 2), cols: new Int32Array(GRID.x), dx2: new Float32Array(GRID.x) };
|
|
100
|
+
/**
|
|
101
|
+
* Bins the packed lights into the froxel grid on the CPU, into `bins` (`CLUSTERS` rows of
|
|
102
|
+
* `CLUSTER_ROW` words: the count, then the indices), for backends without compute (WebGL2).
|
|
103
|
+
* Exactly `shaders/scene/cluster.wesl`'s rows (the same sphere–box test, indices ascending,
|
|
104
|
+
* at most 63), but separably: the boxes' bounds are tabulated per slice and per tile column
|
|
105
|
+
* and row, and a light visits only the slices its depth reaches, the columns within its
|
|
106
|
+
* radius there, and the rows within what is left, not all 3,456 clusters. `counts` is
|
|
107
|
+
* scratch of `CLUSTERS` words.
|
|
108
|
+
*/
|
|
109
|
+
export function binLights(lights, params, bins, counts) {
|
|
110
|
+
const [vx0, vx1, vx2, , vy0, vy1, vy2, , vz0, vz1, vz2, , px, py, near, , , , total, logRange] = params;
|
|
111
|
+
const T = BIN_TABLES;
|
|
112
|
+
for (let tz = 0; tz < GRID.z; tz++) {
|
|
113
|
+
const z0 = near * 2 ** ((logRange * tz) / GRID.z);
|
|
114
|
+
const z1 = near * 2 ** ((logRange * (tz + 1)) / GRID.z);
|
|
115
|
+
T.z[tz * 2] = -z1;
|
|
116
|
+
T.z[tz * 2 + 1] = -z0;
|
|
117
|
+
for (let tx = 0; tx < GRID.x; tx++) {
|
|
118
|
+
const a = (-1 + (2 * tx) / GRID.x) * px, b = (-1 + (2 * (tx + 1)) / GRID.x) * px;
|
|
119
|
+
T.x[(tz * GRID.x + tx) * 2] = Math.min(a * z0, a * z1, b * z0, b * z1);
|
|
120
|
+
T.x[(tz * GRID.x + tx) * 2 + 1] = Math.max(a * z0, a * z1, b * z0, b * z1);
|
|
121
|
+
}
|
|
122
|
+
for (let ty = 0; ty < GRID.y; ty++) {
|
|
123
|
+
// Rows run from the top (NDC y = 1), as fragment coordinates do.
|
|
124
|
+
const b = (1 - (2 * ty) / GRID.y) * py, a = (1 - (2 * (ty + 1)) / GRID.y) * py;
|
|
125
|
+
T.y[(tz * GRID.y + ty) * 2] = Math.min(a * z0, a * z1, b * z0, b * z1);
|
|
126
|
+
T.y[(tz * GRID.y + ty) * 2 + 1] = Math.max(a * z0, a * z1, b * z0, b * z1);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
counts.fill(0, 0, CLUSTERS);
|
|
130
|
+
const n = total | 0;
|
|
131
|
+
for (let l = 0; l < n; l++) {
|
|
132
|
+
const o = l * 16;
|
|
133
|
+
const wx = lights[o], wy = lights[o + 1], wz = lights[o + 2], r = lights[o + 3];
|
|
134
|
+
const x = vx0 * wx + vx1 * wy + vx2 * wz;
|
|
135
|
+
const y = vy0 * wx + vy1 * wy + vy2 * wz;
|
|
136
|
+
const z = vz0 * wx + vz1 * wy + vz2 * wz;
|
|
137
|
+
const r2 = r * r;
|
|
138
|
+
for (let tz = 0; tz < GRID.z; tz++) {
|
|
139
|
+
const dz = z - Math.min(Math.max(z, T.z[tz * 2]), T.z[tz * 2 + 1]);
|
|
140
|
+
const left = r2 - dz * dz;
|
|
141
|
+
if (left < 0)
|
|
142
|
+
continue;
|
|
143
|
+
let cols = 0;
|
|
144
|
+
for (let tx = 0; tx < GRID.x; tx++) {
|
|
145
|
+
const k = (tz * GRID.x + tx) * 2;
|
|
146
|
+
const dx = x - Math.min(Math.max(x, T.x[k]), T.x[k + 1]);
|
|
147
|
+
if (dx * dx <= left) {
|
|
148
|
+
T.cols[cols] = tx;
|
|
149
|
+
T.dx2[cols++] = dx * dx;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (cols === 0)
|
|
153
|
+
continue;
|
|
154
|
+
for (let ty = 0; ty < GRID.y; ty++) {
|
|
155
|
+
const k = (tz * GRID.y + ty) * 2;
|
|
156
|
+
const dy = y - Math.min(Math.max(y, T.y[k]), T.y[k + 1]);
|
|
157
|
+
const rest = left - dy * dy;
|
|
158
|
+
if (rest < 0)
|
|
159
|
+
continue;
|
|
160
|
+
for (let c = 0; c < cols; c++) {
|
|
161
|
+
// The same sum the shader tests, so a light on a box's edge goes the same way.
|
|
162
|
+
if (T.dx2[c] + dy * dy + dz * dz > r2)
|
|
163
|
+
continue;
|
|
164
|
+
const i = (tz * GRID.y + ty) * GRID.x + T.cols[c];
|
|
165
|
+
if (counts[i] < CLUSTER_ROW - 1)
|
|
166
|
+
bins[i * CLUSTER_ROW + ++counts[i]] = l;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
for (let i = 0; i < CLUSTERS; i++)
|
|
172
|
+
bins[i * CLUSTER_ROW] = counts[i];
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Clustered forward lighting (ADR-0067): extracts every `render.Light` each frame (camera-
|
|
176
|
+
* relative, into a 16 KiB uniform array), and a compute pass bins them into a 16×9×24 froxel
|
|
177
|
+
* grid stored in an r32uint texture, which the draw's fragment stage reads with `textureLoad`
|
|
178
|
+
* (compatibility mode may allow it no storage buffers).
|
|
179
|
+
*/
|
|
180
|
+
export class ClusteredLights {
|
|
181
|
+
device;
|
|
182
|
+
lightsBuffer;
|
|
183
|
+
paramsBuffer;
|
|
184
|
+
bins;
|
|
185
|
+
#packer = new LightPacker();
|
|
186
|
+
#pipeline;
|
|
187
|
+
#group = null;
|
|
188
|
+
constructor(device) {
|
|
189
|
+
this.device = device;
|
|
190
|
+
this.lightsBuffer = device.createBuffer({ label: 'yali:lights', size: this.#packer.lights.byteLength, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
|
|
191
|
+
this.paramsBuffer = device.createBuffer({ label: 'yali:lights:params', size: this.#packer.params.byteLength, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
|
|
192
|
+
this.bins = device.createTexture({ label: 'yali:lights:bins', size: [ROW, CLUSTERS], format: 'r32uint', usage: GPUTextureUsage.STORAGE_BINDING | GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_SRC });
|
|
193
|
+
}
|
|
194
|
+
/** Lights extracted this frame (at most `MAX_LIGHTS`; the rest are dropped and counted). */
|
|
195
|
+
get count() {
|
|
196
|
+
return this.#packer.count;
|
|
197
|
+
}
|
|
198
|
+
/** Lights over the limit last frame. */
|
|
199
|
+
get dropped() {
|
|
200
|
+
return this.#packer.dropped;
|
|
201
|
+
}
|
|
202
|
+
/** This frame's packed lights and cluster parameters (what the binning pass reads). */
|
|
203
|
+
get packed() {
|
|
204
|
+
return this.#packer;
|
|
205
|
+
}
|
|
206
|
+
async init(cache) {
|
|
207
|
+
this.#pipeline = await cache.compute({ label: 'yali:lights:cluster', compute: { shader: sceneCluster, entryPoint: 'build' } });
|
|
208
|
+
this.#group = this.device.createBindGroup({
|
|
209
|
+
label: 'yali:lights:cluster',
|
|
210
|
+
layout: this.#pipeline.getBindGroupLayout(0),
|
|
211
|
+
entries: [
|
|
212
|
+
{ binding: 0, resource: { buffer: this.lightsBuffer } },
|
|
213
|
+
{ binding: 1, resource: { buffer: this.paramsBuffer } },
|
|
214
|
+
{ binding: 2, resource: this.bins.createView() },
|
|
215
|
+
],
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
/** Packs every `render.Light` relative to `camera`'s eye and uploads lights and cluster parameters. */
|
|
219
|
+
update(world, Light, camera, width, height) {
|
|
220
|
+
const k = this.#packer;
|
|
221
|
+
k.pack(world, Light, camera, width, height);
|
|
222
|
+
if (k.count > 0)
|
|
223
|
+
this.device.queue.writeBuffer(this.lightsBuffer, 0, k.lights, 0, k.count * 16);
|
|
224
|
+
this.device.queue.writeBuffer(this.paramsBuffer, 0, k.params);
|
|
225
|
+
}
|
|
226
|
+
/** Adds `scene.lights`, the binning pass (skipped when there are no lights). */
|
|
227
|
+
addPass(graph) {
|
|
228
|
+
graph.addPass({
|
|
229
|
+
name: 'scene.lights',
|
|
230
|
+
kind: 'compute',
|
|
231
|
+
writes: ['scene.lights'],
|
|
232
|
+
execute: (pass) => {
|
|
233
|
+
if (!this.#group)
|
|
234
|
+
throw new YaliError('LIGHTS_NOT_READY', 'The light-binning pass ran before init.', 'Await ClusteredLights.init (the render plugin does).');
|
|
235
|
+
pass.setPipeline(this.#pipeline);
|
|
236
|
+
pass.setBindGroup(0, this.#group);
|
|
237
|
+
pass.dispatchWorkgroups(Math.ceil(CLUSTERS / 64));
|
|
238
|
+
},
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
destroy() {
|
|
242
|
+
this.lightsBuffer.destroy();
|
|
243
|
+
this.paramsBuffer.destroy();
|
|
244
|
+
this.bins.destroy();
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
//# sourceMappingURL=lights.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lights.js","sourceRoot":"","sources":["../src/lights.ts"],"names":[],"mappings":"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"}
|
package/dist/matrix.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Halton's low-discrepancy sequence, base `b`, element `i` (1-based), in [0, 1). */
|
|
2
|
+
export declare function halton(i: number, b: number): number;
|
|
3
|
+
/** `a × b`, column-major 4×4. */
|
|
4
|
+
export declare function mul4(a: ArrayLike<number>, b: ArrayLike<number>, out: Float64Array): Float64Array;
|
|
5
|
+
/** The inverse of a column-major 4×4 (Gauss-Jordan with partial pivoting); false if singular. */
|
|
6
|
+
export declare function invert4(m: ArrayLike<number>, out: Float64Array): boolean;
|
package/dist/matrix.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// f64 4×4 helpers the CPU side of TAA and picking share (column-major, as the camera's).
|
|
2
|
+
/** Halton's low-discrepancy sequence, base `b`, element `i` (1-based), in [0, 1). */
|
|
3
|
+
export function halton(i, b) {
|
|
4
|
+
let f = 1;
|
|
5
|
+
let r = 0;
|
|
6
|
+
for (let k = i; k > 0; k = Math.floor(k / b)) {
|
|
7
|
+
f /= b;
|
|
8
|
+
r += f * (k % b);
|
|
9
|
+
}
|
|
10
|
+
return r;
|
|
11
|
+
}
|
|
12
|
+
/** `a × b`, column-major 4×4. */
|
|
13
|
+
export function mul4(a, b, out) {
|
|
14
|
+
for (let c = 0; c < 4; c++) {
|
|
15
|
+
for (let r = 0; r < 4; r++) {
|
|
16
|
+
let s = 0;
|
|
17
|
+
for (let k = 0; k < 4; k++)
|
|
18
|
+
s += a[k * 4 + r] * b[c * 4 + k];
|
|
19
|
+
out[c * 4 + r] = s;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return out;
|
|
23
|
+
}
|
|
24
|
+
/** The inverse of a column-major 4×4 (Gauss-Jordan with partial pivoting); false if singular. */
|
|
25
|
+
export function invert4(m, out) {
|
|
26
|
+
const a = new Float64Array(32);
|
|
27
|
+
for (let r = 0; r < 4; r++) {
|
|
28
|
+
for (let c = 0; c < 4; c++)
|
|
29
|
+
a[r * 8 + c] = m[c * 4 + r];
|
|
30
|
+
a[r * 8 + 4 + r] = 1;
|
|
31
|
+
}
|
|
32
|
+
for (let c = 0; c < 4; c++) {
|
|
33
|
+
let p = c;
|
|
34
|
+
for (let r = c + 1; r < 4; r++)
|
|
35
|
+
if (Math.abs(a[r * 8 + c]) > Math.abs(a[p * 8 + c]))
|
|
36
|
+
p = r;
|
|
37
|
+
if (Math.abs(a[p * 8 + c]) < 1e-300)
|
|
38
|
+
return false;
|
|
39
|
+
if (p !== c)
|
|
40
|
+
for (let k = 0; k < 8; k++)
|
|
41
|
+
[a[c * 8 + k], a[p * 8 + k]] = [a[p * 8 + k], a[c * 8 + k]];
|
|
42
|
+
const inv = 1 / a[c * 8 + c];
|
|
43
|
+
for (let k = 0; k < 8; k++)
|
|
44
|
+
a[c * 8 + k] *= inv;
|
|
45
|
+
for (let r = 0; r < 4; r++) {
|
|
46
|
+
if (r === c)
|
|
47
|
+
continue;
|
|
48
|
+
const f = a[r * 8 + c];
|
|
49
|
+
if (f !== 0)
|
|
50
|
+
for (let k = 0; k < 8; k++)
|
|
51
|
+
a[r * 8 + k] -= f * a[c * 8 + k];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
for (let r = 0; r < 4; r++)
|
|
55
|
+
for (let c = 0; c < 4; c++)
|
|
56
|
+
out[c * 4 + r] = a[r * 8 + 4 + c];
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=matrix.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"matrix.js","sourceRoot":"","sources":["../src/matrix.ts"],"names":[],"mappings":"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"}
|