@multiplekex/shallot 0.2.3 → 0.2.5

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 (42) hide show
  1. package/package.json +1 -1
  2. package/src/extras/arrows/index.ts +3 -3
  3. package/src/extras/caustic.ts +37 -0
  4. package/src/extras/gradient/index.ts +63 -69
  5. package/src/extras/index.ts +3 -0
  6. package/src/extras/lines/index.ts +3 -3
  7. package/src/extras/skylab/index.ts +314 -0
  8. package/src/extras/text/font.ts +69 -14
  9. package/src/extras/text/index.ts +15 -7
  10. package/src/extras/text/sdf.ts +13 -2
  11. package/src/extras/water.ts +64 -0
  12. package/src/standard/defaults.ts +2 -0
  13. package/src/standard/index.ts +2 -0
  14. package/src/standard/raster/index.ts +517 -0
  15. package/src/standard/{render → raytracing}/bvh/blas.ts +3 -3
  16. package/src/standard/{render → raytracing}/bvh/tlas.ts +3 -0
  17. package/src/standard/{render → raytracing}/depth.ts +9 -9
  18. package/src/standard/raytracing/index.ts +380 -0
  19. package/src/standard/{render → raytracing}/instance.ts +3 -0
  20. package/src/standard/raytracing/shaders.ts +815 -0
  21. package/src/standard/{render → raytracing}/triangle.ts +1 -1
  22. package/src/standard/render/camera.ts +88 -80
  23. package/src/standard/render/index.ts +68 -208
  24. package/src/standard/render/indirect.ts +9 -10
  25. package/src/standard/render/mesh/index.ts +35 -166
  26. package/src/standard/render/overlay.ts +4 -4
  27. package/src/standard/render/pass.ts +1 -1
  28. package/src/standard/render/postprocess.ts +75 -50
  29. package/src/standard/render/scene.ts +28 -16
  30. package/src/standard/render/surface/compile.ts +6 -8
  31. package/src/standard/render/surface/noise.ts +15 -2
  32. package/src/standard/render/surface/shaders.ts +257 -0
  33. package/src/standard/render/surface/structs.ts +13 -6
  34. package/src/standard/render/forward/index.ts +0 -259
  35. package/src/standard/render/forward/raster.ts +0 -228
  36. package/src/standard/render/shaders.ts +0 -484
  37. package/src/standard/render/surface/wgsl.ts +0 -573
  38. /package/src/standard/{render → raytracing}/bvh/radix.ts +0 -0
  39. /package/src/standard/{render → raytracing}/bvh/structs.ts +0 -0
  40. /package/src/standard/{render → raytracing}/bvh/traverse.ts +0 -0
  41. /package/src/standard/{render → raytracing}/intersection.ts +0 -0
  42. /package/src/standard/{render → raytracing}/ray.ts +0 -0
@@ -1,5 +1,5 @@
1
1
  import { MAX_ENTITIES } from "../../../core";
2
- import { setTraits, type FieldAccessor } from "../../../core/component";
2
+ import { setTraits, createFieldProxy, type FieldProxy } from "../../../core/component";
3
3
  import { createEntityIdBuffer } from "../../compute";
4
4
  import { writeIndirect } from "../indirect";
5
5
  import { createBox } from "./box";
@@ -7,7 +7,7 @@ import { createSphere } from "./sphere";
7
7
  import { createPlane } from "./plane";
8
8
 
9
9
  export const MAX_SURFACES = 16;
10
- export const MAX_BATCH_SLOTS = 64;
10
+ export const MAX_BATCH_SLOTS = 256;
11
11
  const INVALID_SHAPE = 0xffffffff;
12
12
 
13
13
  const batchEntityIds = new Uint32Array(MAX_ENTITIES);
@@ -81,11 +81,7 @@ export const Volume = {
81
81
  HalfSpace: 1,
82
82
  } as const;
83
83
 
84
- interface ColorProxy extends Array<number>, FieldAccessor {}
85
-
86
- function colorProxy(): ColorProxy {
87
- const data = MeshColors.data;
88
-
84
+ function hexColorProxy(data: Float32Array): FieldProxy {
89
85
  function getValue(eid: number): number {
90
86
  const offset = eid * 4;
91
87
  const r = Math.round(data[offset] * 255);
@@ -101,67 +97,7 @@ function colorProxy(): ColorProxy {
101
97
  data[offset + 2] = (value & 0xff) / 255;
102
98
  }
103
99
 
104
- return new Proxy([] as unknown as ColorProxy, {
105
- get(_, prop) {
106
- if (prop === "get") return getValue;
107
- if (prop === "set") return setValue;
108
- const eid = Number(prop);
109
- if (Number.isNaN(eid)) return undefined;
110
- return getValue(eid);
111
- },
112
- set(_, prop, value) {
113
- const eid = Number(prop);
114
- if (Number.isNaN(eid)) return false;
115
- setValue(eid, value);
116
- return true;
117
- },
118
- });
119
- }
120
-
121
- interface ColorChannelProxy extends Array<number>, FieldAccessor {}
122
-
123
- function colorChannelProxy(channelIndex: number): ColorChannelProxy {
124
- const data = MeshColors.data;
125
-
126
- function getValue(eid: number): number {
127
- return data[eid * 4 + channelIndex];
128
- }
129
-
130
- function setValue(eid: number, value: number): void {
131
- data[eid * 4 + channelIndex] = value;
132
- }
133
-
134
- return new Proxy([] as unknown as ColorChannelProxy, {
135
- get(_, prop) {
136
- if (prop === "get") return getValue;
137
- if (prop === "set") return setValue;
138
- const eid = Number(prop);
139
- if (Number.isNaN(eid)) return undefined;
140
- return getValue(eid);
141
- },
142
- set(_, prop, value) {
143
- const eid = Number(prop);
144
- if (Number.isNaN(eid)) return false;
145
- setValue(eid, value);
146
- return true;
147
- },
148
- });
149
- }
150
-
151
- interface SizeProxy extends Array<number>, FieldAccessor {}
152
-
153
- function sizeProxy(component: number): SizeProxy {
154
- const data = MeshSizes.data;
155
-
156
- function getValue(eid: number): number {
157
- return data[eid * 4 + component];
158
- }
159
-
160
- function setValue(eid: number, value: number): void {
161
- data[eid * 4 + component] = value;
162
- }
163
-
164
- return new Proxy([] as unknown as SizeProxy, {
100
+ return new Proxy([] as unknown as FieldProxy, {
165
101
  get(_, prop) {
166
102
  if (prop === "get") return getValue;
167
103
  if (prop === "set") return setValue;
@@ -178,86 +114,19 @@ function sizeProxy(component: number): SizeProxy {
178
114
  });
179
115
  }
180
116
 
181
- interface PBRProxy extends Array<number>, FieldAccessor {}
182
-
183
- function pbrProxy(component: number, defaultValue: number): PBRProxy {
117
+ function roughnessProxy(): FieldProxy {
184
118
  const data = MeshPBR.data;
185
119
 
186
120
  function getValue(eid: number): number {
187
- const val = data[eid * 4 + component];
188
- return val === 0 && component === 0 ? defaultValue : val;
189
- }
190
-
191
- function setValue(eid: number, value: number): void {
192
- data[eid * 4 + component] = value;
193
- }
194
-
195
- return new Proxy([] as unknown as PBRProxy, {
196
- get(_, prop) {
197
- if (prop === "get") return getValue;
198
- if (prop === "set") return setValue;
199
- const eid = Number(prop);
200
- if (Number.isNaN(eid)) return undefined;
201
- return getValue(eid);
202
- },
203
- set(_, prop, value) {
204
- const eid = Number(prop);
205
- if (Number.isNaN(eid)) return false;
206
- setValue(eid, value);
207
- return true;
208
- },
209
- });
210
- }
211
-
212
- interface EmissionProxy extends Array<number>, FieldAccessor {}
213
-
214
- function emissionProxy(): EmissionProxy {
215
- const data = MeshEmission.data;
216
-
217
- function getValue(eid: number): number {
218
- const offset = eid * 4;
219
- const r = Math.round(data[offset] * 255);
220
- const g = Math.round(data[offset + 1] * 255);
221
- const b = Math.round(data[offset + 2] * 255);
222
- return (r << 16) | (g << 8) | b;
223
- }
224
-
225
- function setValue(eid: number, value: number): void {
226
- const offset = eid * 4;
227
- data[offset] = ((value >> 16) & 0xff) / 255;
228
- data[offset + 1] = ((value >> 8) & 0xff) / 255;
229
- data[offset + 2] = (value & 0xff) / 255;
230
- }
231
-
232
- return new Proxy([] as unknown as EmissionProxy, {
233
- get(_, prop) {
234
- if (prop === "get") return getValue;
235
- if (prop === "set") return setValue;
236
- const eid = Number(prop);
237
- if (Number.isNaN(eid)) return undefined;
238
- return getValue(eid);
239
- },
240
- set(_, prop, value) {
241
- const eid = Number(prop);
242
- if (Number.isNaN(eid)) return false;
243
- setValue(eid, value);
244
- return true;
245
- },
246
- });
247
- }
248
-
249
- function emissionIntensityProxy(): PBRProxy {
250
- const data = MeshEmission.data;
251
-
252
- function getValue(eid: number): number {
253
- return data[eid * 4 + 3];
121
+ const val = data[eid * 4];
122
+ return val === 0 ? 0.9 : val;
254
123
  }
255
124
 
256
125
  function setValue(eid: number, value: number): void {
257
- data[eid * 4 + 3] = value;
126
+ data[eid * 4] = value;
258
127
  }
259
128
 
260
- return new Proxy([] as unknown as PBRProxy, {
129
+ return new Proxy([] as unknown as FieldProxy, {
261
130
  get(_, prop) {
262
131
  if (prop === "get") return getValue;
263
132
  if (prop === "set") return setValue;
@@ -276,35 +145,35 @@ function emissionIntensityProxy(): PBRProxy {
276
145
 
277
146
  export const Mesh: {
278
147
  shape: Uint32Array;
279
- color: ColorProxy;
280
- colorR: ColorChannelProxy;
281
- colorG: ColorChannelProxy;
282
- colorB: ColorChannelProxy;
283
- opacity: ColorChannelProxy;
284
- sizeX: SizeProxy;
285
- sizeY: SizeProxy;
286
- sizeZ: SizeProxy;
287
- roughness: PBRProxy;
288
- metallic: PBRProxy;
289
- ior: PBRProxy;
290
- emission: EmissionProxy;
291
- emissionIntensity: PBRProxy;
148
+ color: FieldProxy;
149
+ colorR: FieldProxy;
150
+ colorG: FieldProxy;
151
+ colorB: FieldProxy;
152
+ opacity: FieldProxy;
153
+ sizeX: FieldProxy;
154
+ sizeY: FieldProxy;
155
+ sizeZ: FieldProxy;
156
+ roughness: FieldProxy;
157
+ metallic: FieldProxy;
158
+ ior: FieldProxy;
159
+ emission: FieldProxy;
160
+ emissionIntensity: FieldProxy;
292
161
  volume: Uint8Array;
293
162
  } = {
294
163
  shape: MeshShapes.data,
295
- color: colorProxy(),
296
- colorR: colorChannelProxy(0),
297
- colorG: colorChannelProxy(1),
298
- colorB: colorChannelProxy(2),
299
- opacity: colorChannelProxy(3),
300
- sizeX: sizeProxy(0),
301
- sizeY: sizeProxy(1),
302
- sizeZ: sizeProxy(2),
303
- roughness: pbrProxy(0, 0.9),
304
- metallic: pbrProxy(1, 0.0),
305
- ior: pbrProxy(2, 1.0),
306
- emission: emissionProxy(),
307
- emissionIntensity: emissionIntensityProxy(),
164
+ color: hexColorProxy(MeshColors.data),
165
+ colorR: createFieldProxy(MeshColors.data, 4, 0),
166
+ colorG: createFieldProxy(MeshColors.data, 4, 1),
167
+ colorB: createFieldProxy(MeshColors.data, 4, 2),
168
+ opacity: createFieldProxy(MeshColors.data, 4, 3),
169
+ sizeX: createFieldProxy(MeshSizes.data, 4, 0),
170
+ sizeY: createFieldProxy(MeshSizes.data, 4, 1),
171
+ sizeZ: createFieldProxy(MeshSizes.data, 4, 2),
172
+ roughness: roughnessProxy(),
173
+ metallic: createFieldProxy(MeshPBR.data, 4, 1),
174
+ ior: createFieldProxy(MeshPBR.data, 4, 2),
175
+ emission: hexColorProxy(MeshEmission.data),
176
+ emissionIntensity: createFieldProxy(MeshEmission.data, 4, 3),
308
177
  volume: MeshVolumes.data,
309
178
  };
310
179
 
@@ -12,7 +12,7 @@ export interface OverlayNodeConfig {
12
12
  export function createOverlayNode(config: OverlayNodeConfig): ComputeNode {
13
13
  return {
14
14
  id: "overlay",
15
- inputs: [{ id: "depth", access: "read" }],
15
+ inputs: [{ id: "z", access: "read" }],
16
16
  outputs: [
17
17
  { id: "color", access: "write" },
18
18
  { id: "mask", access: "write" },
@@ -21,7 +21,7 @@ export function createOverlayNode(config: OverlayNodeConfig): ComputeNode {
21
21
  execute(ctx: ExecutionContext) {
22
22
  const { device, encoder, format, context } = ctx;
23
23
  const targetView = ctx.getTextureView("color") ?? ctx.canvasView;
24
- const depthView = ctx.getTextureView("depth")!;
24
+ const zView = ctx.getTextureView("z")!;
25
25
  const maskView = ctx.getTextureView("mask")!;
26
26
  const eidView = ctx.getTextureView("eid")!;
27
27
 
@@ -32,7 +32,7 @@ export function createOverlayNode(config: OverlayNodeConfig): ComputeNode {
32
32
  width: context.canvas.width,
33
33
  height: context.canvas.height,
34
34
  sceneView: targetView,
35
- depthView,
35
+ zView,
36
36
  entityIdView: eidView,
37
37
  maskView,
38
38
  canvasView: ctx.canvasView,
@@ -60,7 +60,7 @@ export function createOverlayNode(config: OverlayNodeConfig): ComputeNode {
60
60
  },
61
61
  ],
62
62
  depthStencilAttachment: {
63
- view: depthView,
63
+ view: zView,
64
64
  depthLoadOp: "load" as const,
65
65
  depthStoreOp: "store" as const,
66
66
  },
@@ -17,7 +17,7 @@ export interface DrawContext {
17
17
  readonly height: number;
18
18
 
19
19
  readonly sceneView: GPUTextureView;
20
- readonly depthView: GPUTextureView;
20
+ readonly zView: GPUTextureView;
21
21
  readonly entityIdView: GPUTextureView;
22
22
  readonly maskView: GPUTextureView;
23
23
  readonly canvasView: GPUTextureView;
@@ -120,7 +120,6 @@ fn applyVignette(color: vec3f, uv: vec2f) -> vec3f {
120
120
 
121
121
  fn sampleBloom(uv: vec2f) -> vec3f {
122
122
  let texelSize = vec2f(uniforms.texelSizeX, uniforms.texelSizeY);
123
- // Normalize to reference height (1080p) for resolution-independent bloom
124
123
  let height = 1.0 / uniforms.texelSizeY;
125
124
  let resolutionScale = height / 1080.0;
126
125
  let spread = uniforms.bloomRadius * 4.0 * resolutionScale;
@@ -130,7 +129,6 @@ fn sampleBloom(uv: vec2f) -> vec3f {
130
129
  var bloom = vec3f(0.0);
131
130
  var totalWeight = 0.0;
132
131
 
133
- // 9x9 kernel with gaussian weights
134
132
  for (var y = -4; y <= 4; y++) {
135
133
  for (var x = -4; x <= 4; x++) {
136
134
  let offset = vec2f(f32(x), f32(y)) * texelSize * spread;
@@ -165,7 +163,6 @@ fn fragmentMain(input: VertexOutput) -> @location(0) vec4f {
165
163
  color = select(fxaaColor, color, maskValue >= 0.5);
166
164
  }
167
165
 
168
- // Bloom applied before tonemapping
169
166
  if (uniforms.flags & FLAG_BLOOM) != 0u {
170
167
  let bloom = sampleBloom(input.uv);
171
168
  color += bloom * uniforms.bloomIntensity;
@@ -251,6 +248,16 @@ export function createPostProcessNode(config: PostProcessConfig): ComputeNode {
251
248
  let linearSampler: GPUSampler | null = null;
252
249
  let nearestSampler: GPUSampler | null = null;
253
250
 
251
+ let uniformData: ArrayBuffer;
252
+ let uniformFloats: Float32Array;
253
+ let uniformUints: Uint32Array;
254
+
255
+ let mainBindGroup: GPUBindGroup | null = null;
256
+ let blitBindGroup: GPUBindGroup | null = null;
257
+ let cachedInputView: GPUTextureView | null = null;
258
+ let cachedSampler: GPUSampler | null = null;
259
+ let cachedMaskView: GPUTextureView | null = null;
260
+
254
261
  return {
255
262
  id: "postprocess",
256
263
  inputs: [
@@ -306,6 +313,10 @@ export function createPostProcessNode(config: PostProcessConfig): ComputeNode {
306
313
  magFilter: "nearest",
307
314
  minFilter: "nearest",
308
315
  });
316
+
317
+ uniformData = new ArrayBuffer(48);
318
+ uniformFloats = new Float32Array(uniformData);
319
+ uniformUints = new Uint32Array(uniformData);
309
320
  },
310
321
 
311
322
  execute(ctx: ExecutionContext) {
@@ -314,7 +325,7 @@ export function createPostProcessNode(config: PostProcessConfig): ComputeNode {
314
325
  const height = context.canvas.height;
315
326
  const colorView = ctx.getTextureView("color")!;
316
327
  const maskView = ctx.getTextureView("mask")!;
317
- const depthView = ctx.getTextureView("depth")!;
328
+ const zView = ctx.getTextureView("z")!;
318
329
  const eidView = ctx.getTextureView("eid")!;
319
330
  const pingAView = ctx.getTextureView("pingA")!;
320
331
  const pingBView = ctx.getTextureView("pingB")!;
@@ -337,7 +348,7 @@ export function createPostProcessNode(config: PostProcessConfig): ComputeNode {
337
348
  width,
338
349
  height,
339
350
  sceneView: colorView,
340
- depthView,
351
+ zView,
341
352
  entityIdView: eidView,
342
353
  maskView,
343
354
  canvasView,
@@ -368,7 +379,7 @@ export function createPostProcessNode(config: PostProcessConfig): ComputeNode {
368
379
  width,
369
380
  height,
370
381
  sceneView: colorView,
371
- depthView,
382
+ zView,
372
383
  entityIdView: eidView,
373
384
  maskView,
374
385
  canvasView,
@@ -390,32 +401,38 @@ export function createPostProcessNode(config: PostProcessConfig): ComputeNode {
390
401
  if (config.uniforms.bloomIntensity > 0) flags |= FLAG_BLOOM;
391
402
  if (config.uniforms.quantize > 0) flags |= FLAG_QUANTIZE;
392
403
 
393
- const data = new ArrayBuffer(48);
394
- const floats = new Float32Array(data);
395
- const uints = new Uint32Array(data);
396
- floats[0] = config.uniforms.exposure;
397
- floats[1] = config.uniforms.vignetteStrength;
398
- floats[2] = config.uniforms.vignetteInner;
399
- floats[3] = config.uniforms.vignetteOuter;
400
- floats[4] = 1.0 / width;
401
- floats[5] = 1.0 / height;
402
- uints[6] = flags;
403
- floats[7] = config.uniforms.bloomIntensity;
404
- floats[8] = config.uniforms.bloomThreshold;
405
- floats[9] = config.uniforms.bloomRadius;
406
- floats[10] = config.uniforms.quantize;
407
-
408
- device.queue.writeBuffer(uniformBuffer!, 0, data);
409
-
410
- const bindGroup = device.createBindGroup({
411
- layout: pipeline!.getBindGroupLayout(0),
412
- entries: [
413
- { binding: 0, resource: currentInput },
414
- { binding: 1, resource: sampler },
415
- { binding: 2, resource: { buffer: uniformBuffer! } },
416
- { binding: 3, resource: maskView },
417
- ],
418
- });
404
+ uniformFloats[0] = config.uniforms.exposure;
405
+ uniformFloats[1] = config.uniforms.vignetteStrength;
406
+ uniformFloats[2] = config.uniforms.vignetteInner;
407
+ uniformFloats[3] = config.uniforms.vignetteOuter;
408
+ uniformFloats[4] = 1.0 / width;
409
+ uniformFloats[5] = 1.0 / height;
410
+ uniformUints[6] = flags;
411
+ uniformFloats[7] = config.uniforms.bloomIntensity;
412
+ uniformFloats[8] = config.uniforms.bloomThreshold;
413
+ uniformFloats[9] = config.uniforms.bloomRadius;
414
+ uniformFloats[10] = config.uniforms.quantize;
415
+
416
+ device.queue.writeBuffer(uniformBuffer!, 0, uniformData);
417
+
418
+ if (
419
+ currentInput !== cachedInputView ||
420
+ sampler !== cachedSampler ||
421
+ maskView !== cachedMaskView
422
+ ) {
423
+ mainBindGroup = device.createBindGroup({
424
+ layout: pipeline!.getBindGroupLayout(0),
425
+ entries: [
426
+ { binding: 0, resource: currentInput },
427
+ { binding: 1, resource: sampler },
428
+ { binding: 2, resource: { buffer: uniformBuffer! } },
429
+ { binding: 3, resource: maskView },
430
+ ],
431
+ });
432
+ cachedInputView = currentInput;
433
+ cachedSampler = sampler;
434
+ cachedMaskView = maskView;
435
+ }
419
436
 
420
437
  const pass = encoder.beginRenderPass({
421
438
  colorAttachments: [
@@ -429,17 +446,21 @@ export function createPostProcessNode(config: PostProcessConfig): ComputeNode {
429
446
  });
430
447
 
431
448
  pass.setPipeline(pipeline!);
432
- pass.setBindGroup(0, bindGroup);
449
+ pass.setBindGroup(0, mainBindGroup!);
433
450
  pass.draw(3);
434
451
  pass.end();
435
452
  } else {
436
- const bindGroup = device.createBindGroup({
437
- layout: blitPipeline!.getBindGroupLayout(0),
438
- entries: [
439
- { binding: 0, resource: currentInput },
440
- { binding: 1, resource: sampler },
441
- ],
442
- });
453
+ if (currentInput !== cachedInputView || sampler !== cachedSampler) {
454
+ blitBindGroup = device.createBindGroup({
455
+ layout: blitPipeline!.getBindGroupLayout(0),
456
+ entries: [
457
+ { binding: 0, resource: currentInput },
458
+ { binding: 1, resource: sampler },
459
+ ],
460
+ });
461
+ cachedInputView = currentInput;
462
+ cachedSampler = sampler;
463
+ }
443
464
 
444
465
  const pass = encoder.beginRenderPass({
445
466
  colorAttachments: [
@@ -453,18 +474,22 @@ export function createPostProcessNode(config: PostProcessConfig): ComputeNode {
453
474
  });
454
475
 
455
476
  pass.setPipeline(blitPipeline!);
456
- pass.setBindGroup(0, bindGroup);
477
+ pass.setBindGroup(0, blitBindGroup!);
457
478
  pass.draw(3);
458
479
  pass.end();
459
480
  }
460
481
  } else {
461
- const bindGroup = device.createBindGroup({
462
- layout: blitPipeline!.getBindGroupLayout(0),
463
- entries: [
464
- { binding: 0, resource: currentInput },
465
- { binding: 1, resource: sampler },
466
- ],
467
- });
482
+ if (currentInput !== cachedInputView || sampler !== cachedSampler) {
483
+ blitBindGroup = device.createBindGroup({
484
+ layout: blitPipeline!.getBindGroupLayout(0),
485
+ entries: [
486
+ { binding: 0, resource: currentInput },
487
+ { binding: 1, resource: sampler },
488
+ ],
489
+ });
490
+ cachedInputView = currentInput;
491
+ cachedSampler = sampler;
492
+ }
468
493
 
469
494
  const pass = encoder.beginRenderPass({
470
495
  colorAttachments: [
@@ -478,7 +503,7 @@ export function createPostProcessNode(config: PostProcessConfig): ComputeNode {
478
503
  });
479
504
 
480
505
  pass.setPipeline(blitPipeline!);
481
- pass.setBindGroup(0, bindGroup);
506
+ pass.setBindGroup(0, blitBindGroup!);
482
507
  pass.draw(3);
483
508
  pass.end();
484
509
  }
@@ -492,7 +517,7 @@ export function createPostProcessNode(config: PostProcessConfig): ComputeNode {
492
517
  width,
493
518
  height,
494
519
  sceneView: colorView,
495
- depthView,
520
+ zView,
496
521
  entityIdView: eidView,
497
522
  maskView,
498
523
  canvasView,
@@ -1,6 +1,7 @@
1
- export const SCENE_UNIFORM_SIZE = 512;
2
- export const DEPTH_FORMAT: GPUTextureFormat = "depth24plus";
3
- export const LINEAR_DEPTH_FORMAT: GPUTextureFormat = "r32float";
1
+ export const SCENE_UNIFORM_SIZE = 352;
2
+ export const SKY_UNIFORM_SIZE = 176;
3
+ export const Z_FORMAT: GPUTextureFormat = "depth24plus";
4
+ export const DEPTH_FORMAT: GPUTextureFormat = "r32float";
4
5
  export const MASK_FORMAT: GPUTextureFormat = "r8unorm";
5
6
  export const EID_FORMAT: GPUTextureFormat = "r32uint";
6
7
  export const COLOR_FORMAT: GPUTextureFormat = "rgba8unorm";
@@ -13,6 +14,14 @@ export function createSceneBuffer(device: GPUDevice): GPUBuffer {
13
14
  });
14
15
  }
15
16
 
17
+ export function createSkyBuffer(device: GPUDevice): GPUBuffer {
18
+ return device.createBuffer({
19
+ label: "sky",
20
+ size: SKY_UNIFORM_SIZE,
21
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC,
22
+ });
23
+ }
24
+
16
25
  export function ensureTextures(
17
26
  device: GPUDevice,
18
27
  format: GPUTextureFormat,
@@ -25,9 +34,9 @@ export function ensureTextures(
25
34
  if (existing && existing.width === width && existing.height === height) return;
26
35
 
27
36
  existing?.destroy();
28
- textures.get("linear-depth")?.destroy();
29
- textures.get("eid")?.destroy();
30
37
  textures.get("depth")?.destroy();
38
+ textures.get("eid")?.destroy();
39
+ textures.get("z")?.destroy();
31
40
  textures.get("mask")?.destroy();
32
41
  textures.get("pingA")?.destroy();
33
42
  textures.get("pingB")?.destroy();
@@ -42,11 +51,14 @@ export function ensureTextures(
42
51
  GPUTextureUsage.TEXTURE_BINDING,
43
52
  });
44
53
 
45
- const linearDepth = device.createTexture({
46
- label: "linear-depth",
54
+ const depth = device.createTexture({
55
+ label: "depth",
47
56
  size: { width, height },
48
- format: LINEAR_DEPTH_FORMAT,
49
- usage: GPUTextureUsage.STORAGE_BINDING | GPUTextureUsage.TEXTURE_BINDING,
57
+ format: DEPTH_FORMAT,
58
+ usage:
59
+ GPUTextureUsage.STORAGE_BINDING |
60
+ GPUTextureUsage.RENDER_ATTACHMENT |
61
+ GPUTextureUsage.TEXTURE_BINDING,
50
62
  });
51
63
 
52
64
  const eid = device.createTexture({
@@ -59,10 +71,10 @@ export function ensureTextures(
59
71
  GPUTextureUsage.TEXTURE_BINDING,
60
72
  });
61
73
 
62
- const depth = device.createTexture({
63
- label: "depth",
74
+ const z = device.createTexture({
75
+ label: "z",
64
76
  size: { width, height },
65
- format: DEPTH_FORMAT,
77
+ format: Z_FORMAT,
66
78
  usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING,
67
79
  });
68
80
 
@@ -89,12 +101,12 @@ export function ensureTextures(
89
101
 
90
102
  textures.set("color", color);
91
103
  textureViews.set("color", color.createView());
92
- textures.set("linear-depth", linearDepth);
93
- textureViews.set("linear-depth", linearDepth.createView());
94
- textures.set("eid", eid);
95
- textureViews.set("eid", eid.createView());
96
104
  textures.set("depth", depth);
97
105
  textureViews.set("depth", depth.createView());
106
+ textures.set("eid", eid);
107
+ textureViews.set("eid", eid.createView());
108
+ textures.set("z", z);
109
+ textureViews.set("z", z.createView());
98
110
  textures.set("mask", mask);
99
111
  textureViews.set("mask", mask.createView());
100
112
  textures.set("pingA", pingA);
@@ -1,6 +1,6 @@
1
1
  import { WGSL_STRUCTS } from "./structs";
2
2
  import type { SurfaceData } from "./index";
3
- import { compileVertexBody, WGSL_LIGHTING_CALC } from "./wgsl";
3
+ import { compileVertexBody, WGSL_LIGHTING_CALC } from "./shaders";
4
4
 
5
5
  export function compileSurface(data: SurfaceData): string {
6
6
  const vertexTransform = compileVertexBody(data.vertex);
@@ -26,11 +26,10 @@ fn vs(input: VertexInput) -> VertexOutput {
26
26
  let baseWorldPos = (world * vec4<f32>(scaledPos, 1.0)).xyz;
27
27
  let worldNormal = normalize((world * vec4<f32>(input.normal, 0.0)).xyz);
28
28
  let finalWorldPos = userVertexTransform(baseWorldPos, worldNormal, eid);
29
- _ = shapes[eid]; // Keep binding alive
30
29
 
31
30
  var output: VertexOutput;
32
31
  output.position = scene.viewProj * vec4<f32>(finalWorldPos, 1.0);
33
- output.color = colors[eid];
32
+ output.color = data[eid].baseColor;
34
33
  output.worldNormal = worldNormal;
35
34
  output.entityId = eid;
36
35
  output.worldPos = finalWorldPos;
@@ -40,14 +39,13 @@ fn vs(input: VertexInput) -> VertexOutput {
40
39
  @fragment
41
40
  fn fs(input: VertexOutput) -> FragmentOutput {
42
41
  let eid = input.entityId;
43
- let pbrData = pbr[eid];
44
- let emissionData = emission[eid];
42
+ let d = data[eid];
45
43
 
46
44
  var surface: SurfaceData;
47
45
  surface.baseColor = input.color.rgb;
48
- surface.roughness = pbrData.x;
49
- surface.metallic = pbrData.y;
50
- surface.emission = emissionData.rgb * emissionData.a;
46
+ surface.roughness = d.pbr.x;
47
+ surface.metallic = d.pbr.y;
48
+ surface.emission = d.emission.rgb * d.emission.a;
51
49
  surface.normal = normalize(input.worldNormal);
52
50
  surface.worldPos = input.worldPos;
53
51