@multiplekex/shallot 0.2.4 → 0.3.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.
Files changed (62) hide show
  1. package/package.json +1 -1
  2. package/src/core/component.ts +1 -1
  3. package/src/core/index.ts +1 -13
  4. package/src/core/math.ts +186 -0
  5. package/src/core/state.ts +1 -1
  6. package/src/core/xml.ts +56 -41
  7. package/src/extras/arrows/index.ts +3 -3
  8. package/src/extras/caustic.ts +37 -0
  9. package/src/extras/gradient/index.ts +63 -69
  10. package/src/extras/index.ts +3 -0
  11. package/src/extras/lines/index.ts +3 -3
  12. package/src/extras/orbit/index.ts +1 -1
  13. package/src/extras/skylab/index.ts +314 -0
  14. package/src/extras/text/font.ts +69 -14
  15. package/src/extras/text/index.ts +17 -69
  16. package/src/extras/text/sdf.ts +13 -2
  17. package/src/extras/water/index.ts +119 -0
  18. package/src/standard/defaults.ts +2 -0
  19. package/src/standard/index.ts +2 -0
  20. package/src/standard/raster/batch.ts +149 -0
  21. package/src/standard/raster/forward.ts +832 -0
  22. package/src/standard/raster/index.ts +191 -0
  23. package/src/standard/raster/shadow.ts +408 -0
  24. package/src/standard/{render → raytracing}/bvh/blas.ts +336 -88
  25. package/src/standard/raytracing/bvh/radix.ts +473 -0
  26. package/src/standard/raytracing/bvh/refit.ts +711 -0
  27. package/src/standard/{render → raytracing}/bvh/structs.ts +0 -55
  28. package/src/standard/{render → raytracing}/bvh/tlas.ts +155 -140
  29. package/src/standard/{render → raytracing}/bvh/traverse.ts +72 -64
  30. package/src/standard/{render → raytracing}/depth.ts +9 -9
  31. package/src/standard/raytracing/index.ts +409 -0
  32. package/src/standard/{render → raytracing}/instance.ts +31 -16
  33. package/src/standard/{render → raytracing}/ray.ts +1 -1
  34. package/src/standard/raytracing/shaders.ts +798 -0
  35. package/src/standard/{render → raytracing}/triangle.ts +1 -1
  36. package/src/standard/render/camera.ts +96 -106
  37. package/src/standard/render/data.ts +1 -1
  38. package/src/standard/render/index.ts +136 -220
  39. package/src/standard/render/indirect.ts +9 -10
  40. package/src/standard/render/light.ts +2 -2
  41. package/src/standard/render/mesh.ts +404 -0
  42. package/src/standard/render/overlay.ts +8 -5
  43. package/src/standard/render/pass.ts +1 -1
  44. package/src/standard/render/postprocess.ts +263 -242
  45. package/src/standard/render/scene.ts +28 -16
  46. package/src/standard/render/surface/index.ts +81 -12
  47. package/src/standard/render/surface/shaders.ts +511 -0
  48. package/src/standard/render/surface/structs.ts +23 -6
  49. package/src/standard/tween/tween.ts +44 -115
  50. package/src/standard/render/bvh/radix.ts +0 -476
  51. package/src/standard/render/forward/index.ts +0 -259
  52. package/src/standard/render/forward/raster.ts +0 -228
  53. package/src/standard/render/mesh/box.ts +0 -20
  54. package/src/standard/render/mesh/index.ts +0 -446
  55. package/src/standard/render/mesh/plane.ts +0 -11
  56. package/src/standard/render/mesh/sphere.ts +0 -40
  57. package/src/standard/render/mesh/unified.ts +0 -96
  58. package/src/standard/render/shaders.ts +0 -484
  59. package/src/standard/render/surface/compile.ts +0 -67
  60. package/src/standard/render/surface/noise.ts +0 -45
  61. package/src/standard/render/surface/wgsl.ts +0 -573
  62. /package/src/standard/{render → raytracing}/intersection.ts +0 -0
@@ -1,259 +0,0 @@
1
- import type { ComputeNode, ExecutionContext } from "../../compute";
2
- import type { SurfaceData } from "../surface";
3
- import type { Batch } from "../mesh";
4
- import { COLOR_FORMAT } from "../scene";
5
- import { compileUberShader } from "../shaders";
6
- import { createRasterPipeline, createSkyPipeline } from "./raster";
7
-
8
- export interface ForwardConfig {
9
- scene: GPUBuffer;
10
- matrices: GPUBuffer;
11
- colors: GPUBuffer;
12
- sizes: GPUBuffer;
13
- pbr: GPUBuffer;
14
- emission: GPUBuffer;
15
- shapes: GPUBuffer;
16
- surfaces: GPUBuffer;
17
- data: GPUBuffer;
18
- getSurfaces: () => SurfaceData[];
19
- getRaytracing: () => boolean;
20
- getClearColor: () => { r: number; g: number; b: number };
21
- getSky: () => boolean;
22
- acquire?: (message?: string) => (() => void) | undefined;
23
- batches: () => (Batch | null)[];
24
- tlasNodes: GPUBuffer;
25
- tlasInstanceIds: GPUBuffer;
26
- blasNodes: GPUBuffer;
27
- blasTriIds: GPUBuffer;
28
- blasTriangles: GPUBuffer;
29
- blasMeta: GPUBuffer;
30
- instanceInverses: GPUBuffer;
31
- }
32
-
33
- export type ForwardNode = ComputeNode;
34
-
35
- export function createForwardNode(config: ForwardConfig): ForwardNode {
36
- let rasterPipeline: GPURenderPipeline | null = null;
37
- let rtPipeline: GPUComputePipeline | null = null;
38
- let rtCompiling = false;
39
- let skyPipeline: GPURenderPipeline | null = null;
40
-
41
- function executeRT(ctx: ExecutionContext): void {
42
- const { device, encoder } = ctx;
43
-
44
- const colorView = ctx.getTextureView("color");
45
- const linearDepthView = ctx.getTextureView("linear-depth");
46
- const eidView = ctx.getTextureView("eid");
47
-
48
- if (!colorView || !linearDepthView || !eidView || !rtPipeline) {
49
- return;
50
- }
51
-
52
- const bindGroup0 = device.createBindGroup({
53
- layout: rtPipeline.getBindGroupLayout(0),
54
- entries: [
55
- { binding: 0, resource: { buffer: config.scene } },
56
- { binding: 1, resource: { buffer: config.data } },
57
- { binding: 2, resource: colorView },
58
- { binding: 3, resource: linearDepthView },
59
- { binding: 4, resource: eidView },
60
- ],
61
- });
62
-
63
- const bindGroup1 = device.createBindGroup({
64
- layout: rtPipeline.getBindGroupLayout(1),
65
- entries: [
66
- { binding: 0, resource: { buffer: config.tlasNodes } },
67
- { binding: 1, resource: { buffer: config.tlasInstanceIds } },
68
- { binding: 2, resource: { buffer: config.blasNodes } },
69
- { binding: 3, resource: { buffer: config.blasTriIds } },
70
- { binding: 4, resource: { buffer: config.blasTriangles } },
71
- { binding: 5, resource: { buffer: config.blasMeta } },
72
- { binding: 6, resource: { buffer: config.instanceInverses } },
73
- ],
74
- });
75
-
76
- const colorTexture = ctx.getTexture("color");
77
- if (!colorTexture) return;
78
-
79
- const width = colorTexture.width;
80
- const height = colorTexture.height;
81
-
82
- const pass = encoder.beginComputePass();
83
- pass.setPipeline(rtPipeline);
84
- pass.setBindGroup(0, bindGroup0);
85
- pass.setBindGroup(1, bindGroup1);
86
- pass.dispatchWorkgroups(Math.ceil(width / 8), Math.ceil(height / 8));
87
- pass.end();
88
- }
89
-
90
- function executeRaster(ctx: ExecutionContext): void {
91
- const { device, encoder } = ctx;
92
-
93
- const colorView = ctx.getTextureView("color");
94
- const eidView = ctx.getTextureView("eid");
95
- const depthView = ctx.getTextureView("depth");
96
-
97
- if (!colorView || !eidView || !depthView || !rasterPipeline) {
98
- return;
99
- }
100
-
101
- const batches = config.batches();
102
- const colorTexture = ctx.getTexture("color");
103
- if (!colorTexture) return;
104
-
105
- const clearColor = config.getClearColor();
106
- const hasSky = config.getSky() && skyPipeline;
107
-
108
- if (hasSky) {
109
- const skyPass = encoder.beginRenderPass({
110
- colorAttachments: [
111
- {
112
- view: colorView as GPUTextureView,
113
- clearValue: { r: clearColor.r, g: clearColor.g, b: clearColor.b, a: 1 },
114
- loadOp: "clear" as const,
115
- storeOp: "store" as const,
116
- },
117
- ],
118
- depthStencilAttachment: {
119
- view: depthView as GPUTextureView,
120
- depthClearValue: 1.0,
121
- depthLoadOp: "clear" as const,
122
- depthStoreOp: "store" as const,
123
- },
124
- });
125
-
126
- const skyBindGroup = device.createBindGroup({
127
- layout: skyPipeline!.getBindGroupLayout(0),
128
- entries: [{ binding: 0, resource: { buffer: config.scene } }],
129
- });
130
-
131
- skyPass.setPipeline(skyPipeline!);
132
- skyPass.setBindGroup(0, skyBindGroup);
133
- skyPass.draw(3);
134
- skyPass.end();
135
- }
136
-
137
- const pass = encoder.beginRenderPass({
138
- colorAttachments: [
139
- {
140
- view: colorView as GPUTextureView,
141
- clearValue: { r: clearColor.r, g: clearColor.g, b: clearColor.b, a: 1 },
142
- loadOp: hasSky ? ("load" as const) : ("clear" as const),
143
- storeOp: "store" as const,
144
- },
145
- {
146
- view: eidView as GPUTextureView,
147
- clearValue: { r: 0, g: 0, b: 0, a: 0 },
148
- loadOp: "clear" as const,
149
- storeOp: "store" as const,
150
- },
151
- ],
152
- depthStencilAttachment: {
153
- view: depthView as GPUTextureView,
154
- depthClearValue: 1.0,
155
- depthLoadOp: hasSky ? ("load" as const) : ("clear" as const),
156
- depthStoreOp: "store" as const,
157
- },
158
- });
159
-
160
- pass.setPipeline(rasterPipeline);
161
-
162
- for (const batch of batches) {
163
- if (!batch || batch.count === 0) continue;
164
-
165
- const batchBindGroup = device.createBindGroup({
166
- layout: rasterPipeline.getBindGroupLayout(0),
167
- entries: [
168
- { binding: 0, resource: { buffer: config.scene } },
169
- { binding: 1, resource: { buffer: batch.entityIds } },
170
- { binding: 2, resource: { buffer: config.matrices } },
171
- { binding: 3, resource: { buffer: config.colors } },
172
- { binding: 4, resource: { buffer: config.sizes } },
173
- { binding: 5, resource: { buffer: config.pbr } },
174
- { binding: 6, resource: { buffer: config.emission } },
175
- { binding: 7, resource: { buffer: config.shapes } },
176
- { binding: 8, resource: { buffer: config.surfaces } },
177
- ],
178
- });
179
-
180
- pass.setBindGroup(0, batchBindGroup);
181
- pass.setVertexBuffer(0, batch.buffers.vertex);
182
- pass.setIndexBuffer(batch.buffers.index, "uint16");
183
- pass.drawIndexed(batch.buffers.indexCount, batch.count);
184
- }
185
-
186
- pass.end();
187
- }
188
-
189
- return {
190
- id: "forward",
191
- inputs: [{ id: "tlas-bvh-nodes", access: "read" as const }],
192
- outputs: [
193
- { id: "color", access: "write" },
194
- { id: "linear-depth", access: "write" },
195
- { id: "eid", access: "write" },
196
- ],
197
-
198
- async prepare(device: GPUDevice) {
199
- const surfaces = config.getSurfaces();
200
- rasterPipeline = await createRasterPipeline(device, surfaces, COLOR_FORMAT);
201
- skyPipeline = await createSkyPipeline(device, COLOR_FORMAT);
202
-
203
- if (config.getRaytracing()) {
204
- rtCompiling = true;
205
- const release = config.acquire?.("compiling shaders");
206
- createRTPipeline(device, surfaces)
207
- .then((p) => {
208
- rtPipeline = p;
209
- })
210
- .finally(() => {
211
- rtCompiling = false;
212
- release?.();
213
- });
214
- }
215
- },
216
-
217
- execute(ctx: ExecutionContext) {
218
- const wantRT = config.getRaytracing();
219
-
220
- if (wantRT && !rtPipeline && !rtCompiling) {
221
- rtCompiling = true;
222
- const release = config.acquire?.("compiling shaders");
223
- const surfaces = config.getSurfaces();
224
- createRTPipeline(ctx.device, surfaces)
225
- .then((p) => {
226
- rtPipeline = p;
227
- })
228
- .finally(() => {
229
- rtCompiling = false;
230
- release?.();
231
- });
232
- }
233
-
234
- if (wantRT && rtPipeline) {
235
- executeRT(ctx);
236
- } else {
237
- executeRaster(ctx);
238
- }
239
- },
240
- };
241
- }
242
-
243
- export function compileRTShader(surfaces: SurfaceData[]): string {
244
- return compileUberShader(surfaces);
245
- }
246
-
247
- export async function createRTPipeline(
248
- device: GPUDevice,
249
- surfaces: SurfaceData[]
250
- ): Promise<GPUComputePipeline> {
251
- const code = compileRTShader(surfaces);
252
- const module = device.createShaderModule({ code });
253
- return device.createComputePipelineAsync({
254
- layout: "auto",
255
- compute: { module, entryPoint: "main" },
256
- });
257
- }
258
-
259
- export { createRasterPipeline, compileRasterShader } from "./raster";
@@ -1,228 +0,0 @@
1
- import type { SurfaceData } from "../surface";
2
- import { SCENE_STRUCT_WGSL, WGSL_STRUCTS } from "../surface/structs";
3
- import { compileVertexBody, WGSL_LIGHTING_CALC, SKY_WGSL, SKY_DIR_WGSL } from "../surface/wgsl";
4
-
5
- function compileSurfaceVariant(id: number, data: SurfaceData): string {
6
- const vertexBody = compileVertexBody(data.vertex);
7
- const fragmentBody = data.fragment ?? "";
8
- const lit = data.lit !== false;
9
-
10
- return `
11
- fn userVertexTransform_${id}(worldPos: vec3<f32>, normal: vec3<f32>, eid: u32) -> vec3<f32> {
12
- ${vertexBody}
13
- }
14
-
15
- fn userFragment_${id}(surface: ptr<function, SurfaceData>, position: vec4<f32>) {
16
- ${fragmentBody}
17
- }
18
-
19
- fn applyLighting_${id}(surface: SurfaceData) -> vec3<f32> {
20
- ${
21
- lit
22
- ? `${WGSL_LIGHTING_CALC}
23
- return surface.baseColor * lighting + surface.emission;`
24
- : "return surface.baseColor;"
25
- }
26
- }
27
- `;
28
- }
29
-
30
- function compileDispatchFunctions(surfaceCount: number): string {
31
- const vertexCases = Array.from(
32
- { length: surfaceCount },
33
- (_, i) => ` case ${i}u: { return userVertexTransform_${i}(worldPos, normal, eid); }`
34
- ).join("\n");
35
-
36
- const fragmentCases = Array.from(
37
- { length: surfaceCount },
38
- (_, i) => ` case ${i}u: { userFragment_${i}(surface, position); }`
39
- ).join("\n");
40
-
41
- const lightingCases = Array.from(
42
- { length: surfaceCount },
43
- (_, i) => ` case ${i}u: { return applyLighting_${i}(surface); }`
44
- ).join("\n");
45
-
46
- return `
47
- fn dispatchVertexTransform(surfaceId: u32, worldPos: vec3<f32>, normal: vec3<f32>, eid: u32) -> vec3<f32> {
48
- switch surfaceId {
49
- ${vertexCases}
50
- default: { return userVertexTransform_0(worldPos, normal, eid); }
51
- }
52
- }
53
-
54
- fn dispatchFragment(surfaceId: u32, surface: ptr<function, SurfaceData>, position: vec4<f32>) {
55
- switch surfaceId {
56
- ${fragmentCases}
57
- default: { userFragment_0(surface, position); }
58
- }
59
- }
60
-
61
- fn dispatchLighting(surfaceId: u32, surface: SurfaceData) -> vec3<f32> {
62
- switch surfaceId {
63
- ${lightingCases}
64
- default: { return applyLighting_0(surface); }
65
- }
66
- }
67
- `;
68
- }
69
-
70
- export function compileRasterShader(surfaces: SurfaceData[]): string {
71
- const surfaceVariants = surfaces.map((s, i) => compileSurfaceVariant(i, s)).join("\n");
72
- const dispatchFunctions = compileDispatchFunctions(surfaces.length);
73
-
74
- return /* wgsl */ `
75
- ${WGSL_STRUCTS}
76
-
77
- @group(0) @binding(8) var<storage, read> surfaceIds: array<u32>;
78
-
79
- ${surfaceVariants}
80
- ${dispatchFunctions}
81
-
82
- @vertex
83
- fn vs(input: VertexInput) -> VertexOutput {
84
- let eid = entityIds[input.instance];
85
- let world = matrices[eid];
86
- let scaledPos = input.position * sizes[eid].xyz;
87
- let baseWorldPos = (world * vec4<f32>(scaledPos, 1.0)).xyz;
88
- let worldNormal = normalize((world * vec4<f32>(input.normal, 0.0)).xyz);
89
- let surfaceId = surfaceIds[eid] & 0xFFu;
90
- let finalWorldPos = dispatchVertexTransform(surfaceId, baseWorldPos, worldNormal, eid);
91
- _ = shapes[eid];
92
-
93
- var output: VertexOutput;
94
- output.position = scene.viewProj * vec4<f32>(finalWorldPos, 1.0);
95
- output.color = colors[eid];
96
- output.worldNormal = worldNormal;
97
- output.entityId = eid;
98
- output.worldPos = finalWorldPos;
99
- return output;
100
- }
101
-
102
- @fragment
103
- fn fs(input: VertexOutput) -> FragmentOutput {
104
- let eid = input.entityId;
105
- let pbrData = pbr[eid];
106
- let emissionData = emission[eid];
107
- let surfaceId = surfaceIds[eid] & 0xFFu;
108
-
109
- var surface: SurfaceData;
110
- surface.baseColor = input.color.rgb;
111
- surface.roughness = pbrData.x;
112
- surface.metallic = pbrData.y;
113
- surface.emission = emissionData.rgb * emissionData.a;
114
- surface.normal = normalize(input.worldNormal);
115
- surface.worldPos = input.worldPos;
116
-
117
- dispatchFragment(surfaceId, &surface, input.position);
118
-
119
- let litColor = dispatchLighting(surfaceId, surface);
120
-
121
- var output: FragmentOutput;
122
- output.color = vec4<f32>(litColor, input.color.a);
123
- output.entityId = input.entityId;
124
- return output;
125
- }
126
- `;
127
- }
128
-
129
- export async function createRasterPipeline(
130
- device: GPUDevice,
131
- surfaces: SurfaceData[],
132
- colorFormat: GPUTextureFormat
133
- ): Promise<GPURenderPipeline> {
134
- const code = compileRasterShader(surfaces);
135
- const module = device.createShaderModule({ code });
136
-
137
- return device.createRenderPipelineAsync({
138
- layout: "auto",
139
- vertex: {
140
- module,
141
- entryPoint: "vs",
142
- buffers: [
143
- {
144
- arrayStride: 24,
145
- attributes: [
146
- { shaderLocation: 0, offset: 0, format: "float32x3" },
147
- { shaderLocation: 1, offset: 12, format: "float32x3" },
148
- ],
149
- },
150
- ],
151
- },
152
- fragment: {
153
- module,
154
- entryPoint: "fs",
155
- targets: [{ format: colorFormat }, { format: "r32uint" }],
156
- },
157
- depthStencil: {
158
- format: "depth24plus",
159
- depthWriteEnabled: true,
160
- depthCompare: "less",
161
- },
162
- primitive: {
163
- topology: "triangle-list",
164
- cullMode: "back",
165
- },
166
- });
167
- }
168
-
169
- export function compileSkyShader(): string {
170
- return /* wgsl */ `
171
- ${SCENE_STRUCT_WGSL}
172
-
173
- @group(0) @binding(0) var<uniform> scene: Scene;
174
-
175
- ${SKY_DIR_WGSL}
176
- ${SKY_WGSL}
177
-
178
- struct VertexOutput {
179
- @builtin(position) position: vec4<f32>,
180
- @location(0) uv: vec2<f32>,
181
- }
182
-
183
- @vertex
184
- fn vs(@builtin(vertex_index) vertexIndex: u32) -> VertexOutput {
185
- var positions = array<vec2<f32>, 3>(
186
- vec2(-1.0, -1.0),
187
- vec2(3.0, -1.0),
188
- vec2(-1.0, 3.0)
189
- );
190
- var output: VertexOutput;
191
- output.position = vec4(positions[vertexIndex], 0.0, 1.0);
192
- output.uv = (positions[vertexIndex] + 1.0) * 0.5;
193
- output.uv.y = 1.0 - output.uv.y;
194
- return output;
195
- }
196
-
197
- @fragment
198
- fn fs(input: VertexOutput) -> @location(0) vec4<f32> {
199
- let dir = computeSkyDir(input.uv.x, input.uv.y);
200
- let color = sampleSky(dir);
201
- return vec4(color, 1.0);
202
- }
203
- `;
204
- }
205
-
206
- export async function createSkyPipeline(
207
- device: GPUDevice,
208
- colorFormat: GPUTextureFormat
209
- ): Promise<GPURenderPipeline> {
210
- const code = compileSkyShader();
211
- const module = device.createShaderModule({ code });
212
-
213
- return device.createRenderPipelineAsync({
214
- layout: "auto",
215
- vertex: { module, entryPoint: "vs" },
216
- fragment: {
217
- module,
218
- entryPoint: "fs",
219
- targets: [{ format: colorFormat }],
220
- },
221
- depthStencil: {
222
- format: "depth24plus",
223
- depthWriteEnabled: false,
224
- depthCompare: "always",
225
- },
226
- primitive: { topology: "triangle-list" },
227
- });
228
- }
@@ -1,20 +0,0 @@
1
- import type { MeshData } from "./index";
2
-
3
- export function createBox(): MeshData {
4
- const vertices = new Float32Array([
5
- -0.5, -0.5, 0.5, 0, 0, 1, 0.5, -0.5, 0.5, 0, 0, 1, 0.5, 0.5, 0.5, 0, 0, 1, -0.5, 0.5, 0.5,
6
- 0, 0, 1, 0.5, -0.5, -0.5, 0, 0, -1, -0.5, -0.5, -0.5, 0, 0, -1, -0.5, 0.5, -0.5, 0, 0, -1,
7
- 0.5, 0.5, -0.5, 0, 0, -1, -0.5, 0.5, 0.5, 0, 1, 0, 0.5, 0.5, 0.5, 0, 1, 0, 0.5, 0.5, -0.5,
8
- 0, 1, 0, -0.5, 0.5, -0.5, 0, 1, 0, -0.5, -0.5, -0.5, 0, -1, 0, 0.5, -0.5, -0.5, 0, -1, 0,
9
- 0.5, -0.5, 0.5, 0, -1, 0, -0.5, -0.5, 0.5, 0, -1, 0, 0.5, -0.5, 0.5, 1, 0, 0, 0.5, -0.5,
10
- -0.5, 1, 0, 0, 0.5, 0.5, -0.5, 1, 0, 0, 0.5, 0.5, 0.5, 1, 0, 0, -0.5, -0.5, -0.5, -1, 0, 0,
11
- -0.5, -0.5, 0.5, -1, 0, 0, -0.5, 0.5, 0.5, -1, 0, 0, -0.5, 0.5, -0.5, -1, 0, 0,
12
- ]);
13
-
14
- const indices = new Uint16Array([
15
- 0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7, 8, 9, 10, 8, 10, 11, 12, 13, 14, 12, 14, 15, 16, 17, 18,
16
- 16, 18, 19, 20, 21, 22, 20, 22, 23,
17
- ]);
18
-
19
- return { vertices, indices, vertexCount: 24, indexCount: 36 };
20
- }