@driftengine/core 4.8.4 → 4.8.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.json +4332 -4250
- package/dist/build/shaderComments.js +0 -0
- package/dist/index.d.ts +10 -2
- package/dist/index.js +11 -1
- package/dist/render/ambientHarmonics.d.ts +46 -0
- package/dist/render/ambientHarmonics.js +80 -0
- package/dist/render/areaLights.d.ts +19 -0
- package/dist/render/areaLights.js +20 -0
- package/dist/render/backend/api.d.ts +13 -2
- package/dist/render/backend/webgl2/instanced.d.ts +2 -0
- package/dist/render/backend/webgl2/instanced.js +11 -2
- package/dist/render/backend/webgl2/renderer.d.ts +74 -1
- package/dist/render/backend/webgl2/renderer.js +229 -37
- package/dist/render/backend/webgl2/skinScatter.d.ts +14 -0
- package/dist/render/backend/webgl2/skinScatter.js +88 -26
- package/dist/render/backend/webgpu/buffers.js +2 -1
- package/dist/render/backend/webgpu/clusterBinner.js +3 -2
- package/dist/render/backend/webgpu/flatPass.d.ts +7 -5
- package/dist/render/backend/webgpu/flatPass.js +15 -13
- package/dist/render/backend/webgpu/particlePass.d.ts +37 -4
- package/dist/render/backend/webgpu/particlePass.js +80 -12
- package/dist/render/backend/webgpu/postPass.d.ts +6 -1
- package/dist/render/backend/webgpu/postPass.js +7 -2
- package/dist/render/backend/webgpu/renderer.d.ts +58 -7
- package/dist/render/backend/webgpu/renderer.js +329 -78
- package/dist/render/backend/webgpu/shaders/clusterBin.wgsl.d.ts +1 -1
- package/dist/render/backend/webgpu/shaders/clusterBin.wgsl.js +41 -1
- package/dist/render/backend/webgpu/skinPaletteRing.d.ts +40 -28
- package/dist/render/backend/webgpu/skinPaletteRing.js +69 -32
- package/dist/render/backend/webgpu/skinScatterPass.d.ts +10 -2
- package/dist/render/backend/webgpu/skinScatterPass.js +59 -9
- package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +2 -1
- package/dist/render/backend/webgpu/surfaceTexturePass.js +18 -4
- package/dist/render/bloomChain.d.ts +44 -0
- package/dist/render/bloomChain.js +28 -0
- package/dist/render/bloomPass.d.ts +3 -1
- package/dist/render/bloomPass.js +16 -2
- package/dist/render/clusteredLights.d.ts +55 -1
- package/dist/render/clusteredLights.js +182 -8
- package/dist/render/fixtureAtlas.js +1 -1
- package/dist/render/instanceCull.js +7 -0
- package/dist/render/instances.d.ts +32 -10
- package/dist/render/instances.js +17 -8
- package/dist/render/lightBudget.d.ts +26 -1
- package/dist/render/lightBudget.js +26 -3
- package/dist/render/lightmap.d.ts +102 -0
- package/dist/render/lightmap.js +118 -0
- package/dist/render/mesh.js +2 -1
- package/dist/render/particleBatch.d.ts +59 -3
- package/dist/render/particleBatch.js +53 -2
- package/dist/render/particleMaterial.d.ts +6 -1
- package/dist/render/particleMaterial.js +10 -2
- package/dist/render/particlePool.d.ts +11 -0
- package/dist/render/particlePool.js +2 -0
- package/dist/render/particleSort.d.ts +34 -0
- package/dist/render/particleSort.js +96 -0
- package/dist/render/sceneTarget.d.ts +5 -0
- package/dist/render/sceneTarget.js +8 -1
- package/dist/render/shaders/bloom.d.ts +8 -4
- package/dist/render/shaders/bloom.js +20 -3
- package/dist/render/shaders/flat/areaLight.d.ts +10 -0
- package/dist/render/shaders/flat/areaLight.js +96 -0
- package/dist/render/shaders/flat/index.js +30 -4
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +107 -105
- package/dist/render/shaders/flat/models.d.ts +16 -4
- package/dist/render/shaders/flat/models.js +64 -4
- package/dist/render/shaders/flat/normalMap.d.ts +10 -0
- package/dist/render/shaders/flat/normalMap.js +25 -0
- package/dist/render/shaders/flat/preamble.js +74 -37
- package/dist/render/shaders/flat/skin.js +6 -5
- package/dist/render/shaders/generated/bloom.wgsl.d.ts +13 -3
- package/dist/render/shaders/generated/bloom.wgsl.js +13 -3
- package/dist/render/shaders/generated/flat.wgsl.d.ts +1631 -2044
- package/dist/render/shaders/generated/flat.wgsl.js +1669 -2082
- package/dist/render/shaders/generated/particle.wgsl.d.ts +115 -1
- package/dist/render/shaders/generated/particle.wgsl.js +115 -1
- package/dist/render/shaders/generated/skinBlur.wgsl.d.ts +12 -2
- package/dist/render/shaders/generated/skinBlur.wgsl.js +12 -2
- package/dist/render/shaders/particle.d.ts +17 -1
- package/dist/render/shaders/particle.js +133 -1
- package/dist/render/shaders/skinBlur.js +25 -3
- package/dist/render/skinBlur.d.ts +8 -5
- package/dist/render/skinBlur.js +8 -5
- package/dist/render/surfaceModel.d.ts +21 -1
- package/dist/render/surfaceModel.js +22 -0
- package/dist/render/surfaceTexture.d.ts +14 -1
- package/dist/render/surfaceTexture.js +15 -2
- package/package.json +6 -6
- package/src/build/shaderComments.ts +0 -0
- package/src/index.ts +20 -1
- package/src/render/ambientHarmonics.ts +92 -0
- package/src/render/areaLights.ts +39 -0
- package/src/render/backend/api.ts +14 -1
- package/src/render/backend/webgl2/instanced.ts +12 -2
- package/src/render/backend/webgl2/renderer.ts +292 -35
- package/src/render/backend/webgl2/skinScatter.ts +120 -43
- package/src/render/backend/webgpu/buffers.ts +2 -1
- package/src/render/backend/webgpu/clusterBinner.ts +3 -1
- package/src/render/backend/webgpu/flatPass.ts +18 -13
- package/src/render/backend/webgpu/particlePass.ts +118 -12
- package/src/render/backend/webgpu/postPass.ts +7 -2
- package/src/render/backend/webgpu/renderer.ts +396 -95
- package/src/render/backend/webgpu/shaders/clusterBin.wgsl.ts +41 -1
- package/src/render/backend/webgpu/skinPaletteRing.ts +65 -33
- package/src/render/backend/webgpu/skinScatterPass.ts +59 -9
- package/src/render/backend/webgpu/surfaceTexturePass.ts +26 -5
- package/src/render/bloomChain.ts +69 -0
- package/src/render/bloomPass.ts +26 -2
- package/src/render/clusteredLights.ts +221 -7
- package/src/render/fixtureAtlas.ts +1 -1
- package/src/render/instanceCull.ts +5 -0
- package/src/render/instances.ts +40 -11
- package/src/render/lightBudget.ts +48 -2
- package/src/render/lightmap.ts +202 -0
- package/src/render/mesh.ts +2 -1
- package/src/render/particleBatch.ts +95 -4
- package/src/render/particleMaterial.ts +11 -1
- package/src/render/particlePool.ts +13 -0
- package/src/render/particleSort.ts +133 -0
- package/src/render/sceneTarget.ts +18 -1
- package/src/render/shaders/bloom.ts +20 -3
- package/src/render/shaders/flat/areaLight.ts +96 -0
- package/src/render/shaders/flat/index.ts +30 -4
- package/src/render/shaders/flat/main.ts +107 -105
- package/src/render/shaders/flat/models.ts +73 -8
- package/src/render/shaders/flat/normalMap.ts +25 -0
- package/src/render/shaders/flat/preamble.ts +74 -37
- package/src/render/shaders/flat/skin.ts +6 -5
- package/src/render/shaders/generated/bloom.wgsl.ts +13 -3
- package/src/render/shaders/generated/flat.wgsl.ts +1669 -2082
- package/src/render/shaders/generated/particle.wgsl.ts +116 -1
- package/src/render/shaders/generated/skinBlur.wgsl.ts +12 -2
- package/src/render/shaders/particle.ts +134 -1
- package/src/render/shaders/skinBlur.ts +25 -3
- package/src/render/skinBlur.ts +8 -5
- package/src/render/surfaceModel.ts +36 -1
- package/src/render/surfaceTexture.ts +41 -3
|
@@ -5,14 +5,19 @@
|
|
|
5
5
|
*
|
|
6
6
|
* **Drawn as it goes, not kept**: this backend issues a draw when it is asked for, and the state a
|
|
7
7
|
* skin draw reads — its joint palette, its uniforms — is rewritten by the draw after it. So the
|
|
8
|
-
* renderer draws each skin
|
|
9
|
-
* this target,
|
|
8
|
+
* renderer draws each skin three times in place: the frame's half, then at once the diffuse's light
|
|
9
|
+
* alone into this target and its colour alone into the other, both sharing the frame's depth and
|
|
10
|
+
* testing for equality against it. The blur spreads the light and multiplies it by the colour.
|
|
10
11
|
*
|
|
11
12
|
* **The units it reads are its own** — `SKIN_BLUR_TEXTURE_UNIT` and the one after — past every
|
|
12
13
|
* unit a lit program binds, because it runs in the middle of a frame and the draws after it must
|
|
13
14
|
* find their textures where they left them.
|
|
14
15
|
*/
|
|
15
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
SKIN_BLUR_ALBEDO_UNIT,
|
|
18
|
+
SKIN_BLUR_DEPTH_UNIT,
|
|
19
|
+
SKIN_BLUR_TEXTURE_UNIT,
|
|
20
|
+
} from '../../lightBudget.ts';
|
|
16
21
|
import { blendKeeping } from '../../sceneCoverage.ts';
|
|
17
22
|
import type { SceneTarget } from '../../sceneTarget.ts';
|
|
18
23
|
import { compileProgram, uniformLocations } from '../../shader.ts';
|
|
@@ -33,9 +38,13 @@ export class GlSkinScatter {
|
|
|
33
38
|
private readonly depthToViewZ = new Float32Array(4);
|
|
34
39
|
private diffuse: WebGLTexture | null = null;
|
|
35
40
|
private across: WebGLTexture | null = null;
|
|
41
|
+
private albedo: WebGLTexture | null = null;
|
|
36
42
|
private multisampled: WebGLRenderbuffer | null = null;
|
|
43
|
+
private albedoMultisampled: WebGLRenderbuffer | null = null;
|
|
37
44
|
private drawFramebuffer: WebGLFramebuffer | null = null;
|
|
38
45
|
private resolveFramebuffer: WebGLFramebuffer | null = null;
|
|
46
|
+
private albedoFramebuffer: WebGLFramebuffer | null = null;
|
|
47
|
+
private albedoResolveFramebuffer: WebGLFramebuffer | null = null;
|
|
39
48
|
private acrossFramebuffer: WebGLFramebuffer | null = null;
|
|
40
49
|
private width = 0;
|
|
41
50
|
private height = 0;
|
|
@@ -44,6 +53,7 @@ export class GlSkinScatter {
|
|
|
44
53
|
/** Refused once and for good: a driver that will not render to half floats. */
|
|
45
54
|
private unavailable = false;
|
|
46
55
|
private cleared = false;
|
|
56
|
+
private albedoCleared = false;
|
|
47
57
|
private drawn = false;
|
|
48
58
|
|
|
49
59
|
constructor(gl: WebGL2RenderingContext) {
|
|
@@ -115,6 +125,17 @@ export class GlSkinScatter {
|
|
|
115
125
|
this.drawn = true;
|
|
116
126
|
}
|
|
117
127
|
|
|
128
|
+
/** Bind the target for the same skin's colour half, cleared the first time a frame does. */
|
|
129
|
+
beginAlbedo(): void {
|
|
130
|
+
const { gl } = this;
|
|
131
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, this.albedoFramebuffer);
|
|
132
|
+
gl.viewport(0, 0, this.width, this.height);
|
|
133
|
+
if (!this.albedoCleared) {
|
|
134
|
+
gl.clearBufferfv(gl.COLOR, 0, CLEAR);
|
|
135
|
+
this.albedoCleared = true;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
118
139
|
/**
|
|
119
140
|
* Across into the second target, then down and added into the frame, which `scene` binds. `depth`
|
|
120
141
|
* is the frame's depth as a single-sampled texture, or null where none could be had — and then
|
|
@@ -129,20 +150,8 @@ export class GlSkinScatter {
|
|
|
129
150
|
): void {
|
|
130
151
|
const { gl } = this;
|
|
131
152
|
if (this.multisampled !== null) {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
gl.blitFramebuffer(
|
|
135
|
-
0,
|
|
136
|
-
0,
|
|
137
|
-
this.width,
|
|
138
|
-
this.height,
|
|
139
|
-
0,
|
|
140
|
-
0,
|
|
141
|
-
this.width,
|
|
142
|
-
this.height,
|
|
143
|
-
gl.COLOR_BUFFER_BIT,
|
|
144
|
-
gl.NEAREST,
|
|
145
|
-
);
|
|
153
|
+
this.resolve(this.drawFramebuffer, this.resolveFramebuffer);
|
|
154
|
+
this.resolve(this.albedoFramebuffer, this.albedoResolveFramebuffer);
|
|
146
155
|
}
|
|
147
156
|
gl.disable(gl.DEPTH_TEST);
|
|
148
157
|
gl.disable(gl.CULL_FACE);
|
|
@@ -153,6 +162,9 @@ export class GlSkinScatter {
|
|
|
153
162
|
const u = this.uniforms;
|
|
154
163
|
gl.uniform1i(u['uSkin'] ?? null, SKIN_BLUR_TEXTURE_UNIT);
|
|
155
164
|
gl.uniform1i(u['uDepth'] ?? null, SKIN_BLUR_DEPTH_UNIT);
|
|
165
|
+
gl.uniform1i(u['uAlbedo'] ?? null, SKIN_BLUR_ALBEDO_UNIT);
|
|
166
|
+
gl.activeTexture(gl.TEXTURE0 + SKIN_BLUR_ALBEDO_UNIT);
|
|
167
|
+
gl.bindTexture(gl.TEXTURE_2D, this.albedo ?? empty);
|
|
156
168
|
/* The four terms that carry a depth back to view-space metres: the occlusion blur's own. */
|
|
157
169
|
this.depthToViewZ[0] = (inverse[10] as number) ?? 0;
|
|
158
170
|
this.depthToViewZ[1] = (inverse[14] as number) ?? 0;
|
|
@@ -175,6 +187,8 @@ export class GlSkinScatter {
|
|
|
175
187
|
this.step[0] = 1 / this.width;
|
|
176
188
|
this.step[1] = 0;
|
|
177
189
|
gl.uniform2fv(u['uStep'] ?? null, this.step);
|
|
190
|
+
/* The light spreads on both axes; the colour is applied once, on the one adding into the frame. */
|
|
191
|
+
gl.uniform1f(u['uApplyAlbedo'] ?? null, 0);
|
|
178
192
|
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
|
179
193
|
|
|
180
194
|
scene.bind();
|
|
@@ -182,6 +196,7 @@ export class GlSkinScatter {
|
|
|
182
196
|
this.step[0] = 0;
|
|
183
197
|
this.step[1] = 1 / this.height;
|
|
184
198
|
gl.uniform2fv(u['uStep'] ?? null, this.step);
|
|
199
|
+
gl.uniform1f(u['uApplyAlbedo'] ?? null, 1);
|
|
185
200
|
/* Added: its colour onto the frame's, the frame's alpha kept. */
|
|
186
201
|
gl.enable(gl.BLEND);
|
|
187
202
|
gl.blendEquation(gl.FUNC_ADD);
|
|
@@ -199,9 +214,29 @@ export class GlSkinScatter {
|
|
|
199
214
|
/** Spent at the frame's start: what was drawn into the target was last frame's. */
|
|
200
215
|
reset(): void {
|
|
201
216
|
this.cleared = false;
|
|
217
|
+
this.albedoCleared = false;
|
|
202
218
|
this.drawn = false;
|
|
203
219
|
}
|
|
204
220
|
|
|
221
|
+
/** A multisampled target into its single-sampled texture. */
|
|
222
|
+
private resolve(from: WebGLFramebuffer | null, to: WebGLFramebuffer | null): void {
|
|
223
|
+
const { gl } = this;
|
|
224
|
+
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, from);
|
|
225
|
+
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, to);
|
|
226
|
+
gl.blitFramebuffer(
|
|
227
|
+
0,
|
|
228
|
+
0,
|
|
229
|
+
this.width,
|
|
230
|
+
this.height,
|
|
231
|
+
0,
|
|
232
|
+
0,
|
|
233
|
+
this.width,
|
|
234
|
+
this.height,
|
|
235
|
+
gl.COLOR_BUFFER_BIT,
|
|
236
|
+
gl.NEAREST,
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
|
|
205
240
|
dispose(): void {
|
|
206
241
|
const { gl } = this;
|
|
207
242
|
this.release();
|
|
@@ -226,19 +261,59 @@ export class GlSkinScatter {
|
|
|
226
261
|
this.sharedDepth = depthRenderbuffer ?? depthTexture;
|
|
227
262
|
this.diffuse = this.halfFloatTexture(width, height);
|
|
228
263
|
this.across = this.halfFloatTexture(width, height);
|
|
264
|
+
this.albedo = this.halfFloatTexture(width, height);
|
|
229
265
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
266
|
+
const multisampled = samples > 1 && depthRenderbuffer !== null;
|
|
267
|
+
const diffuse = this.drawTarget(this.diffuse, multisampled, depthTexture, depthRenderbuffer);
|
|
268
|
+
this.drawFramebuffer = diffuse.framebuffer;
|
|
269
|
+
this.multisampled = diffuse.multisampled;
|
|
270
|
+
this.resolveFramebuffer = diffuse.resolve;
|
|
271
|
+
const albedo = this.drawTarget(this.albedo, multisampled, depthTexture, depthRenderbuffer);
|
|
272
|
+
this.albedoFramebuffer = albedo.framebuffer;
|
|
273
|
+
this.albedoMultisampled = albedo.multisampled;
|
|
274
|
+
this.albedoResolveFramebuffer = albedo.resolve;
|
|
275
|
+
|
|
276
|
+
this.acrossFramebuffer = gl.createFramebuffer();
|
|
277
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, this.acrossFramebuffer);
|
|
278
|
+
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.across, 0);
|
|
279
|
+
const acrossReady = gl.checkFramebufferStatus(gl.FRAMEBUFFER) === gl.FRAMEBUFFER_COMPLETE;
|
|
280
|
+
gl.bindRenderbuffer(gl.RENDERBUFFER, null);
|
|
281
|
+
if (diffuse.ready && albedo.ready && acrossReady) return true;
|
|
282
|
+
this.release();
|
|
283
|
+
this.refuse();
|
|
284
|
+
return false;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* A framebuffer a skin half draws into against the frame's depth: the texture itself, or a
|
|
289
|
+
* multisampled renderbuffer and the framebuffer that resolves it into the texture.
|
|
290
|
+
*/
|
|
291
|
+
private drawTarget(
|
|
292
|
+
texture: WebGLTexture | null,
|
|
293
|
+
multisampled: boolean,
|
|
294
|
+
depthTexture: WebGLTexture | null,
|
|
295
|
+
depthRenderbuffer: WebGLRenderbuffer | null,
|
|
296
|
+
): {
|
|
297
|
+
framebuffer: WebGLFramebuffer | null;
|
|
298
|
+
multisampled: WebGLRenderbuffer | null;
|
|
299
|
+
resolve: WebGLFramebuffer | null;
|
|
300
|
+
ready: boolean;
|
|
301
|
+
} {
|
|
302
|
+
const { gl } = this;
|
|
303
|
+
const framebuffer = gl.createFramebuffer();
|
|
304
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
|
|
305
|
+
let samples: WebGLRenderbuffer | null = null;
|
|
306
|
+
if (multisampled && depthRenderbuffer !== null) {
|
|
307
|
+
samples = gl.createRenderbuffer();
|
|
308
|
+
gl.bindRenderbuffer(gl.RENDERBUFFER, samples);
|
|
309
|
+
gl.renderbufferStorageMultisample(
|
|
239
310
|
gl.RENDERBUFFER,
|
|
240
|
-
this.
|
|
311
|
+
this.samples,
|
|
312
|
+
gl.RGBA16F,
|
|
313
|
+
this.width,
|
|
314
|
+
this.height,
|
|
241
315
|
);
|
|
316
|
+
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, samples);
|
|
242
317
|
gl.framebufferRenderbuffer(
|
|
243
318
|
gl.FRAMEBUFFER,
|
|
244
319
|
gl.DEPTH_ATTACHMENT,
|
|
@@ -246,25 +321,17 @@ export class GlSkinScatter {
|
|
|
246
321
|
depthRenderbuffer,
|
|
247
322
|
);
|
|
248
323
|
} else {
|
|
249
|
-
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D,
|
|
324
|
+
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
|
|
250
325
|
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, depthTexture, 0);
|
|
251
326
|
}
|
|
252
|
-
const
|
|
253
|
-
|
|
254
|
-
if (
|
|
255
|
-
|
|
256
|
-
gl.bindFramebuffer(gl.FRAMEBUFFER,
|
|
257
|
-
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D,
|
|
327
|
+
const ready = gl.checkFramebufferStatus(gl.FRAMEBUFFER) === gl.FRAMEBUFFER_COMPLETE;
|
|
328
|
+
let resolve: WebGLFramebuffer | null = null;
|
|
329
|
+
if (samples !== null) {
|
|
330
|
+
resolve = gl.createFramebuffer();
|
|
331
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, resolve);
|
|
332
|
+
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
|
|
258
333
|
}
|
|
259
|
-
|
|
260
|
-
gl.bindFramebuffer(gl.FRAMEBUFFER, this.acrossFramebuffer);
|
|
261
|
-
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.across, 0);
|
|
262
|
-
const acrossReady = gl.checkFramebufferStatus(gl.FRAMEBUFFER) === gl.FRAMEBUFFER_COMPLETE;
|
|
263
|
-
gl.bindRenderbuffer(gl.RENDERBUFFER, null);
|
|
264
|
-
if (drawReady && acrossReady) return true;
|
|
265
|
-
this.release();
|
|
266
|
-
this.refuse();
|
|
267
|
-
return false;
|
|
334
|
+
return { framebuffer, multisampled: samples, resolve, ready };
|
|
268
335
|
}
|
|
269
336
|
|
|
270
337
|
private refuse(): void {
|
|
@@ -294,16 +361,26 @@ export class GlSkinScatter {
|
|
|
294
361
|
const { gl } = this;
|
|
295
362
|
if (this.drawFramebuffer !== null) gl.deleteFramebuffer(this.drawFramebuffer);
|
|
296
363
|
if (this.resolveFramebuffer !== null) gl.deleteFramebuffer(this.resolveFramebuffer);
|
|
364
|
+
if (this.albedoFramebuffer !== null) gl.deleteFramebuffer(this.albedoFramebuffer);
|
|
365
|
+
if (this.albedoResolveFramebuffer !== null) {
|
|
366
|
+
gl.deleteFramebuffer(this.albedoResolveFramebuffer);
|
|
367
|
+
}
|
|
297
368
|
if (this.acrossFramebuffer !== null) gl.deleteFramebuffer(this.acrossFramebuffer);
|
|
298
369
|
if (this.multisampled !== null) gl.deleteRenderbuffer(this.multisampled);
|
|
370
|
+
if (this.albedoMultisampled !== null) gl.deleteRenderbuffer(this.albedoMultisampled);
|
|
299
371
|
if (this.diffuse !== null) gl.deleteTexture(this.diffuse);
|
|
300
372
|
if (this.across !== null) gl.deleteTexture(this.across);
|
|
373
|
+
if (this.albedo !== null) gl.deleteTexture(this.albedo);
|
|
301
374
|
this.drawFramebuffer = null;
|
|
302
375
|
this.resolveFramebuffer = null;
|
|
376
|
+
this.albedoFramebuffer = null;
|
|
377
|
+
this.albedoResolveFramebuffer = null;
|
|
303
378
|
this.acrossFramebuffer = null;
|
|
304
379
|
this.multisampled = null;
|
|
380
|
+
this.albedoMultisampled = null;
|
|
305
381
|
this.diffuse = null;
|
|
306
382
|
this.across = null;
|
|
383
|
+
this.albedo = null;
|
|
307
384
|
this.sharedDepth = null;
|
|
308
385
|
}
|
|
309
386
|
}
|
|
@@ -766,7 +766,8 @@ const INSTANCE_ATTRIBUTES: GPUVertexAttribute[] = [
|
|
|
766
766
|
{ shaderLocation: 12, offset: 16, format: 'float32x4' },
|
|
767
767
|
{ shaderLocation: 13, offset: 32, format: 'float32x4' },
|
|
768
768
|
{ shaderLocation: 14, offset: 48, format: 'float32x4' },
|
|
769
|
-
|
|
769
|
+
/* The tint and, in w, the opacity. See `INSTANCE_FLOATS`. */
|
|
770
|
+
{ shaderLocation: 15, offset: 64, format: 'float32x4' },
|
|
770
771
|
];
|
|
771
772
|
|
|
772
773
|
/** The locations above, as the set the base layout must stand clear of on an instanced draw. */
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
TABLE_HEIGHT,
|
|
6
6
|
TABLE_WIDTH,
|
|
7
7
|
type ClusterLightSet,
|
|
8
|
+
clusteredLightTotal,
|
|
8
9
|
writeLightRecord,
|
|
9
10
|
} from '../../clusteredLights.ts';
|
|
10
11
|
import { MAX_POINT_LIGHTS } from '../../lightBudget.ts';
|
|
@@ -167,7 +168,8 @@ export class ClusterBinner {
|
|
|
167
168
|
if (frame === null || device === null || pipeline === null || group === null) return;
|
|
168
169
|
if (params === null || lightBuffer === null) return;
|
|
169
170
|
|
|
170
|
-
|
|
171
|
+
/* The lamps and the clustered rectangles after them. See `ClusterLightSet.areas`. */
|
|
172
|
+
const count = clusteredLightTotal(frame.lights);
|
|
171
173
|
|
|
172
174
|
/*
|
|
173
175
|
* The records, by the function `buildLightClusters` writes them with, because the shader
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { SURFACE_MODEL_SWITCH } from '../../surfaceModel.ts';
|
|
2
2
|
import type { SurfaceModelKind } from '../../surfaceModel.ts';
|
|
3
|
-
import type
|
|
3
|
+
import { SKIN_HALF_SWITCH, type SkinHalf } from '../../shaders/flat/models.ts';
|
|
4
4
|
import { DEPTH_COMPARE, DEPTH_FORMAT, depthOffsetForLayer } from '../../depthConvention.ts';
|
|
5
5
|
import {
|
|
6
6
|
FLAT_BINDINGS,
|
|
@@ -153,8 +153,8 @@ export function flatVertexBindings(
|
|
|
153
153
|
const PLAIN_VERTEX = flatVertexBindings(false);
|
|
154
154
|
|
|
155
155
|
/**
|
|
156
|
-
* A vertex stage's constants, keyed by each override's **id** as the fragment's are: `SKIN_EIGHT`
|
|
157
|
-
* a skinned stage, nothing in any other. Every override a stage declares is set, and one this does
|
|
156
|
+
* A vertex stage's constants, keyed by each override's **id** as the fragment's are: `SKIN_EIGHT` and
|
|
157
|
+
* `CLOTH_BOUND` in a skinned stage, `LIGHTMAP_REGIONS` in an instanced one, nothing in any other. Every override a stage declares is set, and one this does
|
|
158
158
|
* not know is refused here rather than defaulted. Shared with the shadow and glass-tint casters,
|
|
159
159
|
* whose skinned stages declare the same switch.
|
|
160
160
|
*/
|
|
@@ -162,11 +162,14 @@ export function vertexConstants(
|
|
|
162
162
|
bindings: StageBindings,
|
|
163
163
|
skinEight: boolean,
|
|
164
164
|
cloth = false,
|
|
165
|
+
/** Whether an instanced stage reads its tint lane as a lightmap region. See `lightmap.ts`. */
|
|
166
|
+
lightmapRegions = false,
|
|
165
167
|
): Record<string, number> {
|
|
166
168
|
const constants: Record<string, number> = {};
|
|
167
169
|
for (const [name, id] of Object.entries(bindings.overrides ?? {})) {
|
|
168
170
|
if (name === 'SKIN_EIGHT') constants[String(id)] = skinEight ? 1 : 0;
|
|
169
171
|
else if (name === 'CLOTH_BOUND') constants[String(id)] = cloth ? 1 : 0;
|
|
172
|
+
else if (name === 'LIGHTMAP_REGIONS') constants[String(id)] = lightmapRegions ? 1 : 0;
|
|
170
173
|
else throw new Error(`flatPass: a vertex stage declares a switch "${name}" nothing sets`);
|
|
171
174
|
}
|
|
172
175
|
return constants;
|
|
@@ -263,13 +266,9 @@ function flatFragmentConstants(
|
|
|
263
266
|
constants[String(id)] = name === modelSwitch ? 1 : 0;
|
|
264
267
|
continue;
|
|
265
268
|
}
|
|
266
|
-
/* Skin's
|
|
267
|
-
if (name === 'SKIN_SCREEN' || name === 'SKIN_DIFFUSE') {
|
|
268
|
-
constants[String(id)] =
|
|
269
|
-
(name === 'SKIN_SCREEN' && skinPass === 'scene') ||
|
|
270
|
-
(name === 'SKIN_DIFFUSE' && skinPass === 'diffuse')
|
|
271
|
-
? 1
|
|
272
|
-
: 0;
|
|
269
|
+
/* Skin's halves under the screen-space blur, set from the key as a model's switch is. */
|
|
270
|
+
if (name === 'SKIN_SCREEN' || name === 'SKIN_DIFFUSE' || name === 'SKIN_ALBEDO') {
|
|
271
|
+
constants[String(id)] = skinPass !== 'whole' && name === SKIN_HALF_SWITCH[skinPass] ? 1 : 0;
|
|
273
272
|
continue;
|
|
274
273
|
}
|
|
275
274
|
if (!(name in cache.litSwitches)) {
|
|
@@ -608,7 +607,7 @@ export function flatPipeline(
|
|
|
608
607
|
cloth = false,
|
|
609
608
|
/** Which shading model its fragment stage shades by, null the standard. The key must carry it. */
|
|
610
609
|
model: SurfaceModelKind | null = null,
|
|
611
|
-
/** Which half of a skin it draws. The key must carry it: `|ss` or `|
|
|
610
|
+
/** Which half of a skin it draws. The key must carry it: `|ss`, `|sd` or `|sa`. See `SkinHalf`. */
|
|
612
611
|
skinPass: SkinHalf = 'whole',
|
|
613
612
|
): GPURenderPipeline {
|
|
614
613
|
return cache.get(
|
|
@@ -768,7 +767,8 @@ function flatDescriptor(
|
|
|
768
767
|
skinPass: SkinHalf = 'whole',
|
|
769
768
|
): GPURenderPipelineDescriptor {
|
|
770
769
|
const offset = depthOffsetForLayer(depthLayer);
|
|
771
|
-
|
|
770
|
+
/* The diffuse and colour halves are the same surface again, into the blur's targets. */
|
|
771
|
+
const diffuseAlone = skinPass === 'diffuse' || skinPass === 'albedo';
|
|
772
772
|
return {
|
|
773
773
|
label: key,
|
|
774
774
|
layout: device.createPipelineLayout({ bindGroupLayouts: [layout] }),
|
|
@@ -784,7 +784,12 @@ function flatDescriptor(
|
|
|
784
784
|
}),
|
|
785
785
|
entryPoint: 'main',
|
|
786
786
|
buffers: vertexBufferLayouts(present, instanced),
|
|
787
|
-
constants: vertexConstants(
|
|
787
|
+
constants: vertexConstants(
|
|
788
|
+
flatVertexBindings(skinned, morphed, instanced),
|
|
789
|
+
skinEight,
|
|
790
|
+
cloth,
|
|
791
|
+
model === 'lightmap',
|
|
792
|
+
),
|
|
788
793
|
},
|
|
789
794
|
fragment: {
|
|
790
795
|
module: shaderModule(device, {
|
|
@@ -3,7 +3,8 @@ 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
|
|
6
|
+
import { packSpriteStream, type ParticleBlend, type ParticleFacing } from '../../particleBatch.ts';
|
|
7
|
+
import { createParticleSort, type ParticleSort } from '../../particleSort.ts';
|
|
7
8
|
import { DEPTH_FORMAT } from './flatPass.ts';
|
|
8
9
|
import type { PipelineCache } from './pipelineCache.ts';
|
|
9
10
|
import type { UniformFields } from './scatterPass.ts';
|
|
@@ -34,7 +35,9 @@ export function particleFragBindings(material: ParticleMaterial): {
|
|
|
34
35
|
? PARTICLE_BINDINGS.PARTICLE_SPARK_FRAG
|
|
35
36
|
: material === 'mote'
|
|
36
37
|
? PARTICLE_BINDINGS.PARTICLE_MOTE_FRAG
|
|
37
|
-
:
|
|
38
|
+
: material === 'sprite'
|
|
39
|
+
? PARTICLE_BINDINGS.PARTICLE_SPRITE_FRAG
|
|
40
|
+
: PARTICLE_BINDINGS.PARTICLE_SMOKE_FRAG;
|
|
38
41
|
return { fields: stage.fields as UniformFields, size: stage.uniformSize };
|
|
39
42
|
}
|
|
40
43
|
|
|
@@ -45,17 +48,17 @@ const USAGE_INDEX = 0x0010 | 0x0008;
|
|
|
45
48
|
const USAGE_UNIFORM_DST = 0x0040 | 0x0008;
|
|
46
49
|
|
|
47
50
|
/**
|
|
48
|
-
*
|
|
51
|
+
* Eleven attributes and eight slots, so the per-instance nine share one buffer.
|
|
49
52
|
*
|
|
50
|
-
* **`maxVertexBuffers` is eight and this shader declares
|
|
51
|
-
* interleave. The per-instance streams are the ones that do: the engine holds them as
|
|
52
|
-
*
|
|
53
|
-
*
|
|
53
|
+
* **`maxVertexBuffers` is eight and this shader declares eleven inputs**, so something has to
|
|
54
|
+
* interleave. The per-instance streams are the ones that do: the engine holds them as separate
|
|
55
|
+
* arrays and they are packed into one interleaved buffer per frame, which is a copy the upload
|
|
56
|
+
* was already paying per stream rather than new work.
|
|
54
57
|
*
|
|
55
58
|
* 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.
|
|
59
|
+
* alpha 1, age 1, seed 1, velocity 3, and a sprite's frame and half-height 2.
|
|
57
60
|
*/
|
|
58
|
-
const INSTANCE_FLOATS =
|
|
61
|
+
const INSTANCE_FLOATS = 16;
|
|
59
62
|
|
|
60
63
|
const INSTANCE_ATTRIBUTES: GPUVertexAttribute[] = [
|
|
61
64
|
{ shaderLocation: 2, offset: 0, format: 'float32x3' },
|
|
@@ -66,6 +69,7 @@ const INSTANCE_ATTRIBUTES: GPUVertexAttribute[] = [
|
|
|
66
69
|
{ shaderLocation: 7, offset: 36, format: 'float32' },
|
|
67
70
|
{ shaderLocation: 8, offset: 40, format: 'float32' },
|
|
68
71
|
{ shaderLocation: 9, offset: 44, format: 'float32x3' },
|
|
72
|
+
{ shaderLocation: 10, offset: 56, format: 'float32x2' },
|
|
69
73
|
];
|
|
70
74
|
|
|
71
75
|
export function particleVertexLayouts(): GPUVertexBufferLayout[] {
|
|
@@ -102,8 +106,22 @@ export interface GpuParticles {
|
|
|
102
106
|
readonly coreGain: number;
|
|
103
107
|
/** Whether the mote fragment stage mixes toward the medium. Ignored by the other materials. */
|
|
104
108
|
readonly fog: boolean;
|
|
109
|
+
/** A sprite's image view, or null for a procedural material. See `ParticleBatchOptions`. */
|
|
110
|
+
readonly texture: GPUTextureView | null;
|
|
111
|
+
readonly cells: readonly [number, number];
|
|
112
|
+
readonly blendCells: boolean;
|
|
113
|
+
readonly softDepth: number;
|
|
114
|
+
readonly cameraFade: number;
|
|
115
|
+
readonly cameraOffset: number;
|
|
116
|
+
/** Room to draw farthest first, where the batch asked. See `particleSort.ts`. */
|
|
117
|
+
readonly order: ParticleSort | null;
|
|
118
|
+
/** A sprite's bind group, which holds the frame's depth copy and is rebuilt when that moves. */
|
|
119
|
+
spriteGroup: GPUBindGroup | null;
|
|
120
|
+
spriteGroupDepth: GPUTextureView | null;
|
|
105
121
|
/** Reused every frame; see `packInstances`. */
|
|
106
122
|
readonly staging: Float32Array;
|
|
123
|
+
/** The sprite stream, two floats a particle, before it is woven into `staging`. */
|
|
124
|
+
readonly spriteStaging: Float32Array;
|
|
107
125
|
/**
|
|
108
126
|
* This batch's own uniform buffers, staging and bind group — never shared with another
|
|
109
127
|
* batch of the same material. See the note on `createGpuParticles` for why: two pools of one
|
|
@@ -118,7 +136,8 @@ export interface GpuParticles {
|
|
|
118
136
|
readonly fragFloats: Float32Array;
|
|
119
137
|
readonly fragInts: Int32Array;
|
|
120
138
|
readonly fields: UniformFields;
|
|
121
|
-
|
|
139
|
+
/** The procedural materials' group; a sprite's holds the depth copy, so it is `spriteGroup`. */
|
|
140
|
+
readonly bindGroup: GPUBindGroup | null;
|
|
122
141
|
/**
|
|
123
142
|
* The encoder this batch was last drawn into, by the renderer's count, or -1.
|
|
124
143
|
*
|
|
@@ -160,6 +179,7 @@ export function createGpuParticles(
|
|
|
160
179
|
layout: GPUBindGroupLayout,
|
|
161
180
|
fragFields: UniformFields,
|
|
162
181
|
fragSize: number,
|
|
182
|
+
sprite: SpriteOptions | null = null,
|
|
163
183
|
): GpuParticles {
|
|
164
184
|
/* The cross, built once: two blades of four corners, interleaved with the blade index. */
|
|
165
185
|
const corners = new Float32Array(PARTICLE_VERTS * 3);
|
|
@@ -236,7 +256,17 @@ export function createGpuParticles(
|
|
|
236
256
|
erosion,
|
|
237
257
|
coreGain,
|
|
238
258
|
fog,
|
|
259
|
+
texture: sprite?.texture ?? null,
|
|
260
|
+
cells: sprite?.cells ?? [1, 1],
|
|
261
|
+
blendCells: sprite?.blendCells ?? false,
|
|
262
|
+
softDepth: sprite?.softDepth ?? 0,
|
|
263
|
+
cameraFade: sprite?.cameraFade ?? 0,
|
|
264
|
+
cameraOffset: sprite?.cameraOffset ?? 0,
|
|
265
|
+
order: sprite?.sort === true ? createParticleSort(capacity) : null,
|
|
266
|
+
spriteGroup: null,
|
|
267
|
+
spriteGroupDepth: null,
|
|
239
268
|
staging: new Float32Array(capacity * INSTANCE_FLOATS),
|
|
269
|
+
spriteStaging: new Float32Array(capacity * 2),
|
|
240
270
|
vertUniforms,
|
|
241
271
|
fragUniforms,
|
|
242
272
|
vertStaging,
|
|
@@ -245,7 +275,10 @@ export function createGpuParticles(
|
|
|
245
275
|
fragFloats: new Float32Array(fragStaging),
|
|
246
276
|
fragInts: new Int32Array(fragStaging),
|
|
247
277
|
fields: fragFields,
|
|
248
|
-
bindGroup:
|
|
278
|
+
bindGroup:
|
|
279
|
+
material === 'sprite'
|
|
280
|
+
? null
|
|
281
|
+
: createParticleBindGroup(device, layout, vertUniforms, fragUniforms, fragSize),
|
|
249
282
|
dispose(): void {
|
|
250
283
|
cornerBuffer.destroy();
|
|
251
284
|
instances.destroy();
|
|
@@ -265,6 +298,8 @@ export function createGpuParticles(
|
|
|
265
298
|
export function packInstances(batch: GpuParticles, data: ParticleInstances): number {
|
|
266
299
|
const n = Math.min(data.count, batch.capacity);
|
|
267
300
|
const out = batch.staging;
|
|
301
|
+
/* The sprite's frame and half-height, two floats a particle, woven in below. */
|
|
302
|
+
packSpriteStream(data, n, batch.spriteStaging);
|
|
268
303
|
for (let i = 0; i < n; i++) {
|
|
269
304
|
const o = i * INSTANCE_FLOATS;
|
|
270
305
|
const p = i * 3;
|
|
@@ -282,16 +317,34 @@ export function packInstances(batch: GpuParticles, data: ParticleInstances): num
|
|
|
282
317
|
out[o + 11] = data.velocities[p] as number;
|
|
283
318
|
out[o + 12] = data.velocities[p + 1] as number;
|
|
284
319
|
out[o + 13] = data.velocities[p + 2] as number;
|
|
320
|
+
out[o + 14] = batch.spriteStaging[i * 2] as number;
|
|
321
|
+
out[o + 15] = batch.spriteStaging[i * 2 + 1] as number;
|
|
285
322
|
}
|
|
286
323
|
return n;
|
|
287
324
|
}
|
|
288
325
|
|
|
326
|
+
/** The floats `packInstances` writes per particle, for the caller's upload. */
|
|
327
|
+
export const PARTICLE_INSTANCE_FLOATS = INSTANCE_FLOATS;
|
|
328
|
+
|
|
329
|
+
/** What a `'sprite'` batch carries beyond a procedural one. See `ParticleBatchOptions`. */
|
|
330
|
+
export interface SpriteOptions {
|
|
331
|
+
readonly texture: GPUTextureView | null;
|
|
332
|
+
readonly cells: readonly [number, number];
|
|
333
|
+
readonly blendCells: boolean;
|
|
334
|
+
readonly softDepth: number;
|
|
335
|
+
readonly cameraFade: number;
|
|
336
|
+
readonly cameraOffset: number;
|
|
337
|
+
readonly sort: boolean;
|
|
338
|
+
}
|
|
339
|
+
|
|
289
340
|
export function createParticleBindGroupLayout(
|
|
290
341
|
device: GPUDevice,
|
|
291
342
|
fragSize: number,
|
|
343
|
+
sprite = false,
|
|
292
344
|
): GPUBindGroupLayout {
|
|
345
|
+
const textures = PARTICLE_BINDINGS.PARTICLE_SPRITE_FRAG.textures;
|
|
293
346
|
return device.createBindGroupLayout({
|
|
294
|
-
label: 'particle.layout',
|
|
347
|
+
label: sprite ? 'particle.layout:sprite' : 'particle.layout',
|
|
295
348
|
entries: [
|
|
296
349
|
{
|
|
297
350
|
binding: VERT_BINDING,
|
|
@@ -303,6 +356,59 @@ export function createParticleBindGroupLayout(
|
|
|
303
356
|
visibility: VISIBILITY_FRAGMENT,
|
|
304
357
|
buffer: { type: 'uniform', minBindingSize: fragSize },
|
|
305
358
|
},
|
|
359
|
+
/* The image, filtered, and the frame's depth copy, read texel for texel. */
|
|
360
|
+
...(sprite
|
|
361
|
+
? [
|
|
362
|
+
{
|
|
363
|
+
binding: textures.uSprite.texture,
|
|
364
|
+
visibility: VISIBILITY_FRAGMENT,
|
|
365
|
+
texture: { sampleType: 'float' as const, viewDimension: '2d-array' as const },
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
binding: textures.uSprite.sampler,
|
|
369
|
+
visibility: VISIBILITY_FRAGMENT,
|
|
370
|
+
sampler: { type: 'filtering' as const },
|
|
371
|
+
},
|
|
372
|
+
{
|
|
373
|
+
binding: textures.uDepth.texture,
|
|
374
|
+
visibility: VISIBILITY_FRAGMENT,
|
|
375
|
+
texture: { sampleType: 'unfilterable-float' as const },
|
|
376
|
+
},
|
|
377
|
+
{
|
|
378
|
+
binding: textures.uDepth.sampler,
|
|
379
|
+
visibility: VISIBILITY_FRAGMENT,
|
|
380
|
+
sampler: { type: 'non-filtering' as const },
|
|
381
|
+
},
|
|
382
|
+
]
|
|
383
|
+
: []),
|
|
384
|
+
],
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* A sprite batch's bind group: its uniforms, its image, and the frame's depth copy — or any
|
|
390
|
+
* single-sample float texture standing in, where the batch asks for no soft edge.
|
|
391
|
+
*/
|
|
392
|
+
export function createSpriteBindGroup(
|
|
393
|
+
device: GPUDevice,
|
|
394
|
+
layout: GPUBindGroupLayout,
|
|
395
|
+
batch: GpuParticles,
|
|
396
|
+
fragSize: number,
|
|
397
|
+
depth: GPUTextureView,
|
|
398
|
+
linear: GPUSampler,
|
|
399
|
+
nearest: GPUSampler,
|
|
400
|
+
): GPUBindGroup {
|
|
401
|
+
const textures = PARTICLE_BINDINGS.PARTICLE_SPRITE_FRAG.textures;
|
|
402
|
+
return device.createBindGroup({
|
|
403
|
+
label: 'particle.bindGroup:sprite',
|
|
404
|
+
layout,
|
|
405
|
+
entries: [
|
|
406
|
+
{ binding: VERT_BINDING, resource: { buffer: batch.vertUniforms, size: PARTICLE_VERT_SIZE } },
|
|
407
|
+
{ binding: FRAG_BINDING, resource: { buffer: batch.fragUniforms, size: fragSize } },
|
|
408
|
+
{ binding: textures.uSprite.texture, resource: batch.texture as GPUTextureView },
|
|
409
|
+
{ binding: textures.uSprite.sampler, resource: linear },
|
|
410
|
+
{ binding: textures.uDepth.texture, resource: depth },
|
|
411
|
+
{ binding: textures.uDepth.sampler, resource: nearest },
|
|
306
412
|
],
|
|
307
413
|
});
|
|
308
414
|
}
|
|
@@ -331,8 +331,13 @@ export const REFLECTION_OVER_BLEND: GPUBlendState = {
|
|
|
331
331
|
alpha: SCENE_ALPHA_KEEPS,
|
|
332
332
|
};
|
|
333
333
|
|
|
334
|
-
|
|
335
|
-
|
|
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
|
|