@driftengine/core 4.8.5 → 4.8.7

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 (214) hide show
  1. package/CHANGELOG.json +4392 -4250
  2. package/README.md +1 -1
  3. package/dist/index.d.ts +12 -4
  4. package/dist/index.js +12 -2
  5. package/dist/render/ambientHarmonics.d.ts +46 -0
  6. package/dist/render/ambientHarmonics.js +80 -0
  7. package/dist/render/anisotropicLobe.d.ts +10 -0
  8. package/dist/render/anisotropicLobe.js +19 -0
  9. package/dist/render/areaLights.d.ts +54 -0
  10. package/dist/render/areaLights.js +55 -0
  11. package/dist/render/backend/api.d.ts +43 -2
  12. package/dist/render/backend/webgl2/instanced.d.ts +2 -0
  13. package/dist/render/backend/webgl2/instanced.js +11 -2
  14. package/dist/render/backend/webgl2/renderer.d.ts +120 -11
  15. package/dist/render/backend/webgl2/renderer.js +356 -53
  16. package/dist/render/backend/webgl2/sceneCaptureTarget.d.ts +29 -0
  17. package/dist/render/backend/webgl2/sceneCaptureTarget.js +96 -0
  18. package/dist/render/backend/webgl2/skinScatter.d.ts +14 -0
  19. package/dist/render/backend/webgl2/skinScatter.js +88 -26
  20. package/dist/render/backend/webgpu/buffers.js +2 -1
  21. package/dist/render/backend/webgpu/clusterBinner.js +3 -2
  22. package/dist/render/backend/webgpu/flatPass.d.ts +7 -5
  23. package/dist/render/backend/webgpu/flatPass.js +15 -13
  24. package/dist/render/backend/webgpu/particlePass.d.ts +37 -4
  25. package/dist/render/backend/webgpu/particlePass.js +81 -12
  26. package/dist/render/backend/webgpu/postPass.d.ts +6 -1
  27. package/dist/render/backend/webgpu/postPass.js +7 -2
  28. package/dist/render/backend/webgpu/renderer.d.ts +93 -8
  29. package/dist/render/backend/webgpu/renderer.js +483 -93
  30. package/dist/render/backend/webgpu/sceneCaptureTarget.d.ts +24 -0
  31. package/dist/render/backend/webgpu/sceneCaptureTarget.js +59 -0
  32. package/dist/render/backend/webgpu/shaders/clusterBin.wgsl.d.ts +1 -1
  33. package/dist/render/backend/webgpu/shaders/clusterBin.wgsl.js +41 -1
  34. package/dist/render/backend/webgpu/skinPaletteRing.d.ts +40 -28
  35. package/dist/render/backend/webgpu/skinPaletteRing.js +69 -32
  36. package/dist/render/backend/webgpu/skinScatterPass.d.ts +10 -2
  37. package/dist/render/backend/webgpu/skinScatterPass.js +59 -9
  38. package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +5 -1
  39. package/dist/render/backend/webgpu/surfaceTexturePass.js +45 -5
  40. package/dist/render/backend/webgpu/waterPass.d.ts +1 -1
  41. package/dist/render/bloomChain.d.ts +44 -0
  42. package/dist/render/bloomChain.js +28 -0
  43. package/dist/render/bloomPass.d.ts +3 -1
  44. package/dist/render/bloomPass.js +16 -2
  45. package/dist/render/clusteredLights.d.ts +78 -6
  46. package/dist/render/clusteredLights.js +214 -13
  47. package/dist/render/driftLight/bake.d.ts +2 -0
  48. package/dist/render/driftLight/bake.js +1 -1
  49. package/dist/render/driftLight/falloff.d.ts +4 -2
  50. package/dist/render/driftLight/falloff.js +8 -2
  51. package/dist/render/fixtureAtlas.js +1 -1
  52. package/dist/render/gi/bounceLights.d.ts +3 -2
  53. package/dist/render/gi/bounceLights.js +8 -3
  54. package/dist/render/instanceCull.js +12 -0
  55. package/dist/render/instances.d.ts +47 -11
  56. package/dist/render/instances.js +24 -9
  57. package/dist/render/lightBudget.d.ts +38 -2
  58. package/dist/render/lightBudget.js +48 -5
  59. package/dist/render/lightmap.d.ts +116 -0
  60. package/dist/render/lightmap.js +140 -0
  61. package/dist/render/mesh.js +2 -1
  62. package/dist/render/particleBatch.d.ts +59 -3
  63. package/dist/render/particleBatch.js +53 -2
  64. package/dist/render/particleMaterial.d.ts +6 -1
  65. package/dist/render/particleMaterial.js +10 -2
  66. package/dist/render/particlePool.d.ts +32 -0
  67. package/dist/render/particlePool.js +9 -0
  68. package/dist/render/particleSort.d.ts +34 -0
  69. package/dist/render/particleSort.js +96 -0
  70. package/dist/render/pointLightSelection.d.ts +13 -0
  71. package/dist/render/pointLightSelection.js +3 -1
  72. package/dist/render/renderQuality.d.ts +8 -7
  73. package/dist/render/rendererHarness.js +3 -0
  74. package/dist/render/rgb9e5.d.ts +22 -0
  75. package/dist/render/rgb9e5.js +47 -0
  76. package/dist/render/sceneCapture.d.ts +29 -0
  77. package/dist/render/sceneCapture.js +36 -0
  78. package/dist/render/sceneTarget.d.ts +5 -0
  79. package/dist/render/sceneTarget.js +8 -1
  80. package/dist/render/screenSpaceReflection.d.ts +27 -1
  81. package/dist/render/screenSpaceReflection.js +9 -0
  82. package/dist/render/shaders/bloom.d.ts +8 -4
  83. package/dist/render/shaders/bloom.js +20 -3
  84. package/dist/render/shaders/bolt.d.ts +1 -1
  85. package/dist/render/shaders/bolt.js +7 -1
  86. package/dist/render/shaders/depth.js +11 -0
  87. package/dist/render/shaders/flat/anisotropic.d.ts +1 -1
  88. package/dist/render/shaders/flat/anisotropic.js +37 -3
  89. package/dist/render/shaders/flat/areaLight.d.ts +10 -0
  90. package/dist/render/shaders/flat/areaLight.js +142 -0
  91. package/dist/render/shaders/flat/glassTint.d.ts +3 -2
  92. package/dist/render/shaders/flat/glassTint.js +3 -1
  93. package/dist/render/shaders/flat/index.d.ts +5 -0
  94. package/dist/render/shaders/flat/index.js +46 -6
  95. package/dist/render/shaders/flat/lobes.d.ts +6 -0
  96. package/dist/render/shaders/flat/lobes.js +49 -43
  97. package/dist/render/shaders/flat/main.d.ts +1 -1
  98. package/dist/render/shaders/flat/main.js +158 -121
  99. package/dist/render/shaders/flat/models.d.ts +21 -6
  100. package/dist/render/shaders/flat/models.js +161 -17
  101. package/dist/render/shaders/flat/normalMap.d.ts +10 -0
  102. package/dist/render/shaders/flat/normalMap.js +25 -0
  103. package/dist/render/shaders/flat/preamble.js +84 -40
  104. package/dist/render/shaders/flat/skin.js +6 -5
  105. package/dist/render/shaders/generated/bloom.wgsl.d.ts +13 -3
  106. package/dist/render/shaders/generated/bloom.wgsl.js +13 -3
  107. package/dist/render/shaders/generated/bolt.wgsl.d.ts +1 -1
  108. package/dist/render/shaders/generated/bolt.wgsl.js +1 -1
  109. package/dist/render/shaders/generated/depth.wgsl.d.ts +4 -4
  110. package/dist/render/shaders/generated/depth.wgsl.js +4 -4
  111. package/dist/render/shaders/generated/flat.wgsl.d.ts +1647 -2044
  112. package/dist/render/shaders/generated/flat.wgsl.js +1685 -2082
  113. package/dist/render/shaders/generated/particle.wgsl.d.ts +115 -1
  114. package/dist/render/shaders/generated/particle.wgsl.js +115 -1
  115. package/dist/render/shaders/generated/skinBlur.wgsl.d.ts +12 -2
  116. package/dist/render/shaders/generated/skinBlur.wgsl.js +12 -2
  117. package/dist/render/shaders/generated/ssrTrace.wgsl.d.ts +11 -6
  118. package/dist/render/shaders/generated/ssrTrace.wgsl.js +11 -6
  119. package/dist/render/shaders/generated/water.wgsl.d.ts +23 -16
  120. package/dist/render/shaders/generated/water.wgsl.js +23 -16
  121. package/dist/render/shaders/gi/probeBake.wgsl.js +9 -5
  122. package/dist/render/shaders/particle.d.ts +17 -1
  123. package/dist/render/shaders/particle.js +133 -1
  124. package/dist/render/shaders/recon/motion.wgsl.d.ts +1 -1
  125. package/dist/render/shaders/recon/motion.wgsl.js +13 -2
  126. package/dist/render/shaders/skinBlur.js +25 -3
  127. package/dist/render/shaders/ssrTrace.js +20 -1
  128. package/dist/render/shaders/water.d.ts +1 -1
  129. package/dist/render/shaders/water.js +12 -3
  130. package/dist/render/skinBlur.d.ts +8 -5
  131. package/dist/render/skinBlur.js +8 -5
  132. package/dist/render/skinScatter.d.ts +3 -1
  133. package/dist/render/skinScatter.js +3 -1
  134. package/dist/render/ssrPass.js +1 -0
  135. package/dist/render/surfaceModel.d.ts +49 -8
  136. package/dist/render/surfaceModel.js +54 -8
  137. package/dist/render/surfaceTexture.d.ts +38 -1
  138. package/dist/render/surfaceTexture.js +37 -2
  139. package/package.json +6 -6
  140. package/src/index.ts +26 -3
  141. package/src/render/ambientHarmonics.ts +92 -0
  142. package/src/render/anisotropicLobe.ts +29 -0
  143. package/src/render/areaLights.ts +97 -0
  144. package/src/render/backend/api.ts +61 -1
  145. package/src/render/backend/webgl2/instanced.ts +12 -2
  146. package/src/render/backend/webgl2/renderer.ts +470 -59
  147. package/src/render/backend/webgl2/sceneCaptureTarget.ts +130 -0
  148. package/src/render/backend/webgl2/skinScatter.ts +120 -43
  149. package/src/render/backend/webgpu/buffers.ts +2 -1
  150. package/src/render/backend/webgpu/clusterBinner.ts +3 -1
  151. package/src/render/backend/webgpu/flatPass.ts +18 -13
  152. package/src/render/backend/webgpu/particlePass.ts +119 -12
  153. package/src/render/backend/webgpu/postPass.ts +7 -2
  154. package/src/render/backend/webgpu/renderer.ts +589 -113
  155. package/src/render/backend/webgpu/sceneCaptureTarget.ts +63 -0
  156. package/src/render/backend/webgpu/shaders/clusterBin.wgsl.ts +41 -1
  157. package/src/render/backend/webgpu/skinPaletteRing.ts +65 -33
  158. package/src/render/backend/webgpu/skinScatterPass.ts +59 -9
  159. package/src/render/backend/webgpu/surfaceTexturePass.ts +57 -6
  160. package/src/render/bloomChain.ts +69 -0
  161. package/src/render/bloomPass.ts +26 -2
  162. package/src/render/clusteredLights.ts +259 -12
  163. package/src/render/driftLight/bake.ts +3 -1
  164. package/src/render/driftLight/falloff.ts +13 -2
  165. package/src/render/fixtureAtlas.ts +1 -1
  166. package/src/render/gi/bounceLights.ts +12 -3
  167. package/src/render/instanceCull.ts +9 -0
  168. package/src/render/instances.ts +61 -12
  169. package/src/render/lightBudget.ts +88 -4
  170. package/src/render/lightmap.ts +243 -0
  171. package/src/render/mesh.ts +2 -1
  172. package/src/render/particleBatch.ts +95 -4
  173. package/src/render/particleMaterial.ts +11 -1
  174. package/src/render/particlePool.ts +36 -0
  175. package/src/render/particleSort.ts +133 -0
  176. package/src/render/pointLightSelection.ts +20 -1
  177. package/src/render/renderQuality.ts +8 -7
  178. package/src/render/rendererHarness.ts +3 -0
  179. package/src/render/rgb9e5.ts +52 -0
  180. package/src/render/sceneCapture.ts +56 -0
  181. package/src/render/sceneTarget.ts +18 -1
  182. package/src/render/screenSpaceReflection.ts +33 -1
  183. package/src/render/shaders/bloom.ts +20 -3
  184. package/src/render/shaders/bolt.ts +7 -1
  185. package/src/render/shaders/depth.ts +11 -0
  186. package/src/render/shaders/flat/anisotropic.ts +37 -3
  187. package/src/render/shaders/flat/areaLight.ts +142 -0
  188. package/src/render/shaders/flat/glassTint.ts +9 -2
  189. package/src/render/shaders/flat/index.ts +51 -6
  190. package/src/render/shaders/flat/lobes.ts +50 -43
  191. package/src/render/shaders/flat/main.ts +158 -121
  192. package/src/render/shaders/flat/models.ts +170 -21
  193. package/src/render/shaders/flat/normalMap.ts +25 -0
  194. package/src/render/shaders/flat/preamble.ts +84 -40
  195. package/src/render/shaders/flat/skin.ts +6 -5
  196. package/src/render/shaders/generated/bloom.wgsl.ts +13 -3
  197. package/src/render/shaders/generated/bolt.wgsl.ts +1 -1
  198. package/src/render/shaders/generated/depth.wgsl.ts +4 -4
  199. package/src/render/shaders/generated/flat.wgsl.ts +1685 -2082
  200. package/src/render/shaders/generated/particle.wgsl.ts +116 -1
  201. package/src/render/shaders/generated/skinBlur.wgsl.ts +12 -2
  202. package/src/render/shaders/generated/ssrTrace.wgsl.ts +11 -6
  203. package/src/render/shaders/generated/water.wgsl.ts +23 -16
  204. package/src/render/shaders/gi/probeBake.wgsl.ts +9 -5
  205. package/src/render/shaders/particle.ts +134 -1
  206. package/src/render/shaders/recon/motion.wgsl.ts +13 -2
  207. package/src/render/shaders/skinBlur.ts +25 -3
  208. package/src/render/shaders/ssrTrace.ts +20 -1
  209. package/src/render/shaders/water.ts +12 -3
  210. package/src/render/skinBlur.ts +8 -5
  211. package/src/render/skinScatter.ts +3 -1
  212. package/src/render/ssrPass.ts +1 -0
  213. package/src/render/surfaceModel.ts +82 -10
  214. package/src/render/surfaceTexture.ts +101 -3
@@ -3,7 +3,9 @@ import { particleShaders, type ParticleMaterial } from '../../particleMaterial.t
3
3
  import { PARTICLE_BINDINGS } from '../../shaders/generated/particle.wgsl.ts';
4
4
  import { PARTICLE_BLADES, PARTICLE_INDICES, PARTICLE_VERTS } from '../../shaders/particle.ts';
5
5
  import type { ParticleInstances } from '../../particlePool.ts';
6
- import type { ParticleBlend, ParticleFacing } from '../../particleBatch.ts';
6
+ import { DEVICE_PARTICLE_FLOATS } from '../../particlePool.ts';
7
+ import { packSpriteStream, type ParticleBlend, type ParticleFacing } from '../../particleBatch.ts';
8
+ import { createParticleSort, type ParticleSort } from '../../particleSort.ts';
7
9
  import { DEPTH_FORMAT } from './flatPass.ts';
8
10
  import type { PipelineCache } from './pipelineCache.ts';
9
11
  import type { UniformFields } from './scatterPass.ts';
@@ -34,7 +36,9 @@ export function particleFragBindings(material: ParticleMaterial): {
34
36
  ? PARTICLE_BINDINGS.PARTICLE_SPARK_FRAG
35
37
  : material === 'mote'
36
38
  ? PARTICLE_BINDINGS.PARTICLE_MOTE_FRAG
37
- : PARTICLE_BINDINGS.PARTICLE_SMOKE_FRAG;
39
+ : material === 'sprite'
40
+ ? PARTICLE_BINDINGS.PARTICLE_SPRITE_FRAG
41
+ : PARTICLE_BINDINGS.PARTICLE_SMOKE_FRAG;
38
42
  return { fields: stage.fields as UniformFields, size: stage.uniformSize };
39
43
  }
40
44
 
@@ -45,17 +49,17 @@ const USAGE_INDEX = 0x0010 | 0x0008;
45
49
  const USAGE_UNIFORM_DST = 0x0040 | 0x0008;
46
50
 
47
51
  /**
48
- * Ten attributes and eight slots, so the per-instance eight share one buffer.
52
+ * Eleven attributes and eight slots, so the per-instance nine share one buffer.
49
53
  *
50
- * **`maxVertexBuffers` is eight and this shader declares ten inputs**, so something has to
51
- * interleave. The per-instance streams are the ones that do: the engine holds them as eight
52
- * separate arrays and they are packed into one interleaved buffer per frame, which is a copy
53
- * the upload was already paying per stream rather than new work.
54
+ * **`maxVertexBuffers` is eight and this shader declares eleven inputs**, so something has to
55
+ * interleave. The per-instance streams are the ones that do: the engine holds them as separate
56
+ * arrays and they are packed into one interleaved buffer per frame, which is a copy the upload
57
+ * was already paying per stream rather than new work.
54
58
  *
55
59
  * Floats per instance, in the order they are packed: position 3, size 1, spin 1, colour 3,
56
- * alpha 1, age 1, seed 1, velocity 3.
60
+ * alpha 1, age 1, seed 1, velocity 3, and a sprite's frame and half-height 2.
57
61
  */
58
- const INSTANCE_FLOATS = 14;
62
+ const INSTANCE_FLOATS = DEVICE_PARTICLE_FLOATS;
59
63
 
60
64
  const INSTANCE_ATTRIBUTES: GPUVertexAttribute[] = [
61
65
  { shaderLocation: 2, offset: 0, format: 'float32x3' },
@@ -66,6 +70,7 @@ const INSTANCE_ATTRIBUTES: GPUVertexAttribute[] = [
66
70
  { shaderLocation: 7, offset: 36, format: 'float32' },
67
71
  { shaderLocation: 8, offset: 40, format: 'float32' },
68
72
  { shaderLocation: 9, offset: 44, format: 'float32x3' },
73
+ { shaderLocation: 10, offset: 56, format: 'float32x2' },
69
74
  ];
70
75
 
71
76
  export function particleVertexLayouts(): GPUVertexBufferLayout[] {
@@ -102,8 +107,22 @@ export interface GpuParticles {
102
107
  readonly coreGain: number;
103
108
  /** Whether the mote fragment stage mixes toward the medium. Ignored by the other materials. */
104
109
  readonly fog: boolean;
110
+ /** A sprite's image view, or null for a procedural material. See `ParticleBatchOptions`. */
111
+ readonly texture: GPUTextureView | null;
112
+ readonly cells: readonly [number, number];
113
+ readonly blendCells: boolean;
114
+ readonly softDepth: number;
115
+ readonly cameraFade: number;
116
+ readonly cameraOffset: number;
117
+ /** Room to draw farthest first, where the batch asked. See `particleSort.ts`. */
118
+ readonly order: ParticleSort | null;
119
+ /** A sprite's bind group, which holds the frame's depth copy and is rebuilt when that moves. */
120
+ spriteGroup: GPUBindGroup | null;
121
+ spriteGroupDepth: GPUTextureView | null;
105
122
  /** Reused every frame; see `packInstances`. */
106
123
  readonly staging: Float32Array;
124
+ /** The sprite stream, two floats a particle, before it is woven into `staging`. */
125
+ readonly spriteStaging: Float32Array;
107
126
  /**
108
127
  * This batch's own uniform buffers, staging and bind group — never shared with another
109
128
  * batch of the same material. See the note on `createGpuParticles` for why: two pools of one
@@ -118,7 +137,8 @@ export interface GpuParticles {
118
137
  readonly fragFloats: Float32Array;
119
138
  readonly fragInts: Int32Array;
120
139
  readonly fields: UniformFields;
121
- readonly bindGroup: GPUBindGroup;
140
+ /** The procedural materials' group; a sprite's holds the depth copy, so it is `spriteGroup`. */
141
+ readonly bindGroup: GPUBindGroup | null;
122
142
  /**
123
143
  * The encoder this batch was last drawn into, by the renderer's count, or -1.
124
144
  *
@@ -160,6 +180,7 @@ export function createGpuParticles(
160
180
  layout: GPUBindGroupLayout,
161
181
  fragFields: UniformFields,
162
182
  fragSize: number,
183
+ sprite: SpriteOptions | null = null,
163
184
  ): GpuParticles {
164
185
  /* The cross, built once: two blades of four corners, interleaved with the blade index. */
165
186
  const corners = new Float32Array(PARTICLE_VERTS * 3);
@@ -236,7 +257,17 @@ export function createGpuParticles(
236
257
  erosion,
237
258
  coreGain,
238
259
  fog,
260
+ texture: sprite?.texture ?? null,
261
+ cells: sprite?.cells ?? [1, 1],
262
+ blendCells: sprite?.blendCells ?? false,
263
+ softDepth: sprite?.softDepth ?? 0,
264
+ cameraFade: sprite?.cameraFade ?? 0,
265
+ cameraOffset: sprite?.cameraOffset ?? 0,
266
+ order: sprite?.sort === true ? createParticleSort(capacity) : null,
267
+ spriteGroup: null,
268
+ spriteGroupDepth: null,
239
269
  staging: new Float32Array(capacity * INSTANCE_FLOATS),
270
+ spriteStaging: new Float32Array(capacity * 2),
240
271
  vertUniforms,
241
272
  fragUniforms,
242
273
  vertStaging,
@@ -245,7 +276,10 @@ export function createGpuParticles(
245
276
  fragFloats: new Float32Array(fragStaging),
246
277
  fragInts: new Int32Array(fragStaging),
247
278
  fields: fragFields,
248
- bindGroup: createParticleBindGroup(device, layout, vertUniforms, fragUniforms, fragSize),
279
+ bindGroup:
280
+ material === 'sprite'
281
+ ? null
282
+ : createParticleBindGroup(device, layout, vertUniforms, fragUniforms, fragSize),
249
283
  dispose(): void {
250
284
  cornerBuffer.destroy();
251
285
  instances.destroy();
@@ -265,6 +299,8 @@ export function createGpuParticles(
265
299
  export function packInstances(batch: GpuParticles, data: ParticleInstances): number {
266
300
  const n = Math.min(data.count, batch.capacity);
267
301
  const out = batch.staging;
302
+ /* The sprite's frame and half-height, two floats a particle, woven in below. */
303
+ packSpriteStream(data, n, batch.spriteStaging);
268
304
  for (let i = 0; i < n; i++) {
269
305
  const o = i * INSTANCE_FLOATS;
270
306
  const p = i * 3;
@@ -282,16 +318,34 @@ export function packInstances(batch: GpuParticles, data: ParticleInstances): num
282
318
  out[o + 11] = data.velocities[p] as number;
283
319
  out[o + 12] = data.velocities[p + 1] as number;
284
320
  out[o + 13] = data.velocities[p + 2] as number;
321
+ out[o + 14] = batch.spriteStaging[i * 2] as number;
322
+ out[o + 15] = batch.spriteStaging[i * 2 + 1] as number;
285
323
  }
286
324
  return n;
287
325
  }
288
326
 
327
+ /** The floats `packInstances` writes per particle, for the caller's upload. */
328
+ export const PARTICLE_INSTANCE_FLOATS = INSTANCE_FLOATS;
329
+
330
+ /** What a `'sprite'` batch carries beyond a procedural one. See `ParticleBatchOptions`. */
331
+ export interface SpriteOptions {
332
+ readonly texture: GPUTextureView | null;
333
+ readonly cells: readonly [number, number];
334
+ readonly blendCells: boolean;
335
+ readonly softDepth: number;
336
+ readonly cameraFade: number;
337
+ readonly cameraOffset: number;
338
+ readonly sort: boolean;
339
+ }
340
+
289
341
  export function createParticleBindGroupLayout(
290
342
  device: GPUDevice,
291
343
  fragSize: number,
344
+ sprite = false,
292
345
  ): GPUBindGroupLayout {
346
+ const textures = PARTICLE_BINDINGS.PARTICLE_SPRITE_FRAG.textures;
293
347
  return device.createBindGroupLayout({
294
- label: 'particle.layout',
348
+ label: sprite ? 'particle.layout:sprite' : 'particle.layout',
295
349
  entries: [
296
350
  {
297
351
  binding: VERT_BINDING,
@@ -303,6 +357,59 @@ export function createParticleBindGroupLayout(
303
357
  visibility: VISIBILITY_FRAGMENT,
304
358
  buffer: { type: 'uniform', minBindingSize: fragSize },
305
359
  },
360
+ /* The image, filtered, and the frame's depth copy, read texel for texel. */
361
+ ...(sprite
362
+ ? [
363
+ {
364
+ binding: textures.uSprite.texture,
365
+ visibility: VISIBILITY_FRAGMENT,
366
+ texture: { sampleType: 'float' as const, viewDimension: '2d-array' as const },
367
+ },
368
+ {
369
+ binding: textures.uSprite.sampler,
370
+ visibility: VISIBILITY_FRAGMENT,
371
+ sampler: { type: 'filtering' as const },
372
+ },
373
+ {
374
+ binding: textures.uDepth.texture,
375
+ visibility: VISIBILITY_FRAGMENT,
376
+ texture: { sampleType: 'unfilterable-float' as const },
377
+ },
378
+ {
379
+ binding: textures.uDepth.sampler,
380
+ visibility: VISIBILITY_FRAGMENT,
381
+ sampler: { type: 'non-filtering' as const },
382
+ },
383
+ ]
384
+ : []),
385
+ ],
386
+ });
387
+ }
388
+
389
+ /**
390
+ * A sprite batch's bind group: its uniforms, its image, and the frame's depth copy — or any
391
+ * single-sample float texture standing in, where the batch asks for no soft edge.
392
+ */
393
+ export function createSpriteBindGroup(
394
+ device: GPUDevice,
395
+ layout: GPUBindGroupLayout,
396
+ batch: GpuParticles,
397
+ fragSize: number,
398
+ depth: GPUTextureView,
399
+ linear: GPUSampler,
400
+ nearest: GPUSampler,
401
+ ): GPUBindGroup {
402
+ const textures = PARTICLE_BINDINGS.PARTICLE_SPRITE_FRAG.textures;
403
+ return device.createBindGroup({
404
+ label: 'particle.bindGroup:sprite',
405
+ layout,
406
+ entries: [
407
+ { binding: VERT_BINDING, resource: { buffer: batch.vertUniforms, size: PARTICLE_VERT_SIZE } },
408
+ { binding: FRAG_BINDING, resource: { buffer: batch.fragUniforms, size: fragSize } },
409
+ { binding: textures.uSprite.texture, resource: batch.texture as GPUTextureView },
410
+ { binding: textures.uSprite.sampler, resource: linear },
411
+ { binding: textures.uDepth.texture, resource: depth },
412
+ { binding: textures.uDepth.sampler, resource: nearest },
306
413
  ],
307
414
  });
308
415
  }
@@ -331,8 +331,13 @@ export const REFLECTION_OVER_BLEND: GPUBlendState = {
331
331
  alpha: SCENE_ALPHA_KEEPS,
332
332
  };
333
333
 
334
- export const ADDITIVE_BLEND: GPUBlendState = {
335
- color: { srcFactor: 'one', dstFactor: 'one', operation: 'add' },
334
+ /**
335
+ * The bloom pyramid's way back up: an octave added onto a level that the blend multiplies by that
336
+ * level's tint first — the blend constant, which white makes `one, one` exactly. Into the pyramid's
337
+ * own levels, never the scene. See `BloomResponse`.
338
+ */
339
+ export const TINTED_ADD_BLEND: GPUBlendState = {
340
+ color: { srcFactor: 'one', dstFactor: 'constant', operation: 'add' },
336
341
  alpha: { srcFactor: 'one', dstFactor: 'one', operation: 'add' },
337
342
  };
338
343