@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,15 @@
|
|
|
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 { SKIN_BLUR_DEPTH_UNIT, SKIN_BLUR_TEXTURE_UNIT } from '../../lightBudget.js';
|
|
16
|
+
import { SKIN_BLUR_ALBEDO_UNIT, SKIN_BLUR_DEPTH_UNIT, SKIN_BLUR_TEXTURE_UNIT, } from '../../lightBudget.js';
|
|
16
17
|
import { blendKeeping } from '../../sceneCoverage.js';
|
|
17
18
|
import { compileProgram, uniformLocations } from '../../shader.js';
|
|
18
19
|
import { FULLSCREEN_VERT } from '../../shaders/fullscreen.js';
|
|
@@ -30,9 +31,13 @@ export class GlSkinScatter {
|
|
|
30
31
|
depthToViewZ = new Float32Array(4);
|
|
31
32
|
diffuse = null;
|
|
32
33
|
across = null;
|
|
34
|
+
albedo = null;
|
|
33
35
|
multisampled = null;
|
|
36
|
+
albedoMultisampled = null;
|
|
34
37
|
drawFramebuffer = null;
|
|
35
38
|
resolveFramebuffer = null;
|
|
39
|
+
albedoFramebuffer = null;
|
|
40
|
+
albedoResolveFramebuffer = null;
|
|
36
41
|
acrossFramebuffer = null;
|
|
37
42
|
width = 0;
|
|
38
43
|
height = 0;
|
|
@@ -41,6 +46,7 @@ export class GlSkinScatter {
|
|
|
41
46
|
/** Refused once and for good: a driver that will not render to half floats. */
|
|
42
47
|
unavailable = false;
|
|
43
48
|
cleared = false;
|
|
49
|
+
albedoCleared = false;
|
|
44
50
|
drawn = false;
|
|
45
51
|
constructor(gl) {
|
|
46
52
|
this.gl = gl;
|
|
@@ -102,6 +108,16 @@ export class GlSkinScatter {
|
|
|
102
108
|
}
|
|
103
109
|
this.drawn = true;
|
|
104
110
|
}
|
|
111
|
+
/** Bind the target for the same skin's colour half, cleared the first time a frame does. */
|
|
112
|
+
beginAlbedo() {
|
|
113
|
+
const { gl } = this;
|
|
114
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, this.albedoFramebuffer);
|
|
115
|
+
gl.viewport(0, 0, this.width, this.height);
|
|
116
|
+
if (!this.albedoCleared) {
|
|
117
|
+
gl.clearBufferfv(gl.COLOR, 0, CLEAR);
|
|
118
|
+
this.albedoCleared = true;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
105
121
|
/**
|
|
106
122
|
* Across into the second target, then down and added into the frame, which `scene` binds. `depth`
|
|
107
123
|
* is the frame's depth as a single-sampled texture, or null where none could be had — and then
|
|
@@ -110,9 +126,8 @@ export class GlSkinScatter {
|
|
|
110
126
|
spread(scene, depth, empty, projection, inverse) {
|
|
111
127
|
const { gl } = this;
|
|
112
128
|
if (this.multisampled !== null) {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
gl.blitFramebuffer(0, 0, this.width, this.height, 0, 0, this.width, this.height, gl.COLOR_BUFFER_BIT, gl.NEAREST);
|
|
129
|
+
this.resolve(this.drawFramebuffer, this.resolveFramebuffer);
|
|
130
|
+
this.resolve(this.albedoFramebuffer, this.albedoResolveFramebuffer);
|
|
116
131
|
}
|
|
117
132
|
gl.disable(gl.DEPTH_TEST);
|
|
118
133
|
gl.disable(gl.CULL_FACE);
|
|
@@ -123,6 +138,9 @@ export class GlSkinScatter {
|
|
|
123
138
|
const u = this.uniforms;
|
|
124
139
|
gl.uniform1i(u['uSkin'] ?? null, SKIN_BLUR_TEXTURE_UNIT);
|
|
125
140
|
gl.uniform1i(u['uDepth'] ?? null, SKIN_BLUR_DEPTH_UNIT);
|
|
141
|
+
gl.uniform1i(u['uAlbedo'] ?? null, SKIN_BLUR_ALBEDO_UNIT);
|
|
142
|
+
gl.activeTexture(gl.TEXTURE0 + SKIN_BLUR_ALBEDO_UNIT);
|
|
143
|
+
gl.bindTexture(gl.TEXTURE_2D, this.albedo ?? empty);
|
|
126
144
|
/* The four terms that carry a depth back to view-space metres: the occlusion blur's own. */
|
|
127
145
|
this.depthToViewZ[0] = inverse[10] ?? 0;
|
|
128
146
|
this.depthToViewZ[1] = inverse[14] ?? 0;
|
|
@@ -141,12 +159,15 @@ export class GlSkinScatter {
|
|
|
141
159
|
this.step[0] = 1 / this.width;
|
|
142
160
|
this.step[1] = 0;
|
|
143
161
|
gl.uniform2fv(u['uStep'] ?? null, this.step);
|
|
162
|
+
/* The light spreads on both axes; the colour is applied once, on the one adding into the frame. */
|
|
163
|
+
gl.uniform1f(u['uApplyAlbedo'] ?? null, 0);
|
|
144
164
|
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
|
145
165
|
scene.bind();
|
|
146
166
|
gl.bindTexture(gl.TEXTURE_2D, this.across);
|
|
147
167
|
this.step[0] = 0;
|
|
148
168
|
this.step[1] = 1 / this.height;
|
|
149
169
|
gl.uniform2fv(u['uStep'] ?? null, this.step);
|
|
170
|
+
gl.uniform1f(u['uApplyAlbedo'] ?? null, 1);
|
|
150
171
|
/* Added: its colour onto the frame's, the frame's alpha kept. */
|
|
151
172
|
gl.enable(gl.BLEND);
|
|
152
173
|
gl.blendEquation(gl.FUNC_ADD);
|
|
@@ -162,8 +183,16 @@ export class GlSkinScatter {
|
|
|
162
183
|
/** Spent at the frame's start: what was drawn into the target was last frame's. */
|
|
163
184
|
reset() {
|
|
164
185
|
this.cleared = false;
|
|
186
|
+
this.albedoCleared = false;
|
|
165
187
|
this.drawn = false;
|
|
166
188
|
}
|
|
189
|
+
/** A multisampled target into its single-sampled texture. */
|
|
190
|
+
resolve(from, to) {
|
|
191
|
+
const { gl } = this;
|
|
192
|
+
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, from);
|
|
193
|
+
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, to);
|
|
194
|
+
gl.blitFramebuffer(0, 0, this.width, this.height, 0, 0, this.width, this.height, gl.COLOR_BUFFER_BIT, gl.NEAREST);
|
|
195
|
+
}
|
|
167
196
|
dispose() {
|
|
168
197
|
const { gl } = this;
|
|
169
198
|
this.release();
|
|
@@ -181,36 +210,56 @@ export class GlSkinScatter {
|
|
|
181
210
|
this.sharedDepth = depthRenderbuffer ?? depthTexture;
|
|
182
211
|
this.diffuse = this.halfFloatTexture(width, height);
|
|
183
212
|
this.across = this.halfFloatTexture(width, height);
|
|
184
|
-
this.
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.diffuse, 0);
|
|
195
|
-
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, depthTexture, 0);
|
|
196
|
-
}
|
|
197
|
-
const drawReady = gl.checkFramebufferStatus(gl.FRAMEBUFFER) === gl.FRAMEBUFFER_COMPLETE;
|
|
198
|
-
if (this.multisampled !== null) {
|
|
199
|
-
this.resolveFramebuffer = gl.createFramebuffer();
|
|
200
|
-
gl.bindFramebuffer(gl.FRAMEBUFFER, this.resolveFramebuffer);
|
|
201
|
-
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.diffuse, 0);
|
|
202
|
-
}
|
|
213
|
+
this.albedo = this.halfFloatTexture(width, height);
|
|
214
|
+
const multisampled = samples > 1 && depthRenderbuffer !== null;
|
|
215
|
+
const diffuse = this.drawTarget(this.diffuse, multisampled, depthTexture, depthRenderbuffer);
|
|
216
|
+
this.drawFramebuffer = diffuse.framebuffer;
|
|
217
|
+
this.multisampled = diffuse.multisampled;
|
|
218
|
+
this.resolveFramebuffer = diffuse.resolve;
|
|
219
|
+
const albedo = this.drawTarget(this.albedo, multisampled, depthTexture, depthRenderbuffer);
|
|
220
|
+
this.albedoFramebuffer = albedo.framebuffer;
|
|
221
|
+
this.albedoMultisampled = albedo.multisampled;
|
|
222
|
+
this.albedoResolveFramebuffer = albedo.resolve;
|
|
203
223
|
this.acrossFramebuffer = gl.createFramebuffer();
|
|
204
224
|
gl.bindFramebuffer(gl.FRAMEBUFFER, this.acrossFramebuffer);
|
|
205
225
|
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.across, 0);
|
|
206
226
|
const acrossReady = gl.checkFramebufferStatus(gl.FRAMEBUFFER) === gl.FRAMEBUFFER_COMPLETE;
|
|
207
227
|
gl.bindRenderbuffer(gl.RENDERBUFFER, null);
|
|
208
|
-
if (
|
|
228
|
+
if (diffuse.ready && albedo.ready && acrossReady)
|
|
209
229
|
return true;
|
|
210
230
|
this.release();
|
|
211
231
|
this.refuse();
|
|
212
232
|
return false;
|
|
213
233
|
}
|
|
234
|
+
/**
|
|
235
|
+
* A framebuffer a skin half draws into against the frame's depth: the texture itself, or a
|
|
236
|
+
* multisampled renderbuffer and the framebuffer that resolves it into the texture.
|
|
237
|
+
*/
|
|
238
|
+
drawTarget(texture, multisampled, depthTexture, depthRenderbuffer) {
|
|
239
|
+
const { gl } = this;
|
|
240
|
+
const framebuffer = gl.createFramebuffer();
|
|
241
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
|
|
242
|
+
let samples = null;
|
|
243
|
+
if (multisampled && depthRenderbuffer !== null) {
|
|
244
|
+
samples = gl.createRenderbuffer();
|
|
245
|
+
gl.bindRenderbuffer(gl.RENDERBUFFER, samples);
|
|
246
|
+
gl.renderbufferStorageMultisample(gl.RENDERBUFFER, this.samples, gl.RGBA16F, this.width, this.height);
|
|
247
|
+
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, samples);
|
|
248
|
+
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthRenderbuffer);
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
|
|
252
|
+
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, depthTexture, 0);
|
|
253
|
+
}
|
|
254
|
+
const ready = gl.checkFramebufferStatus(gl.FRAMEBUFFER) === gl.FRAMEBUFFER_COMPLETE;
|
|
255
|
+
let resolve = null;
|
|
256
|
+
if (samples !== null) {
|
|
257
|
+
resolve = gl.createFramebuffer();
|
|
258
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, resolve);
|
|
259
|
+
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
|
|
260
|
+
}
|
|
261
|
+
return { framebuffer, multisampled: samples, resolve, ready };
|
|
262
|
+
}
|
|
214
263
|
refuse() {
|
|
215
264
|
if (this.unavailable)
|
|
216
265
|
return;
|
|
@@ -237,20 +286,33 @@ export class GlSkinScatter {
|
|
|
237
286
|
gl.deleteFramebuffer(this.drawFramebuffer);
|
|
238
287
|
if (this.resolveFramebuffer !== null)
|
|
239
288
|
gl.deleteFramebuffer(this.resolveFramebuffer);
|
|
289
|
+
if (this.albedoFramebuffer !== null)
|
|
290
|
+
gl.deleteFramebuffer(this.albedoFramebuffer);
|
|
291
|
+
if (this.albedoResolveFramebuffer !== null) {
|
|
292
|
+
gl.deleteFramebuffer(this.albedoResolveFramebuffer);
|
|
293
|
+
}
|
|
240
294
|
if (this.acrossFramebuffer !== null)
|
|
241
295
|
gl.deleteFramebuffer(this.acrossFramebuffer);
|
|
242
296
|
if (this.multisampled !== null)
|
|
243
297
|
gl.deleteRenderbuffer(this.multisampled);
|
|
298
|
+
if (this.albedoMultisampled !== null)
|
|
299
|
+
gl.deleteRenderbuffer(this.albedoMultisampled);
|
|
244
300
|
if (this.diffuse !== null)
|
|
245
301
|
gl.deleteTexture(this.diffuse);
|
|
246
302
|
if (this.across !== null)
|
|
247
303
|
gl.deleteTexture(this.across);
|
|
304
|
+
if (this.albedo !== null)
|
|
305
|
+
gl.deleteTexture(this.albedo);
|
|
248
306
|
this.drawFramebuffer = null;
|
|
249
307
|
this.resolveFramebuffer = null;
|
|
308
|
+
this.albedoFramebuffer = null;
|
|
309
|
+
this.albedoResolveFramebuffer = null;
|
|
250
310
|
this.acrossFramebuffer = null;
|
|
251
311
|
this.multisampled = null;
|
|
312
|
+
this.albedoMultisampled = null;
|
|
252
313
|
this.diffuse = null;
|
|
253
314
|
this.across = null;
|
|
315
|
+
this.albedo = null;
|
|
254
316
|
this.sharedDepth = null;
|
|
255
317
|
}
|
|
256
318
|
}
|
|
@@ -501,7 +501,8 @@ const INSTANCE_ATTRIBUTES = [
|
|
|
501
501
|
{ shaderLocation: 12, offset: 16, format: 'float32x4' },
|
|
502
502
|
{ shaderLocation: 13, offset: 32, format: 'float32x4' },
|
|
503
503
|
{ shaderLocation: 14, offset: 48, format: 'float32x4' },
|
|
504
|
-
|
|
504
|
+
/* The tint and, in w, the opacity. See `INSTANCE_FLOATS`. */
|
|
505
|
+
{ shaderLocation: 15, offset: 64, format: 'float32x4' },
|
|
505
506
|
];
|
|
506
507
|
/** The locations above, as the set the base layout must stand clear of on an instanced draw. */
|
|
507
508
|
const INSTANCE_LOCATIONS = new Set(INSTANCE_ATTRIBUTES.map((attribute) => attribute.shaderLocation));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CLUSTER_COUNT, LIGHT_TEXELS, MAX_CLUSTERED_LIGHTS, TABLE_HEIGHT, TABLE_WIDTH, writeLightRecord, } from '../../clusteredLights.js';
|
|
1
|
+
import { CLUSTER_COUNT, LIGHT_TEXELS, MAX_CLUSTERED_LIGHTS, TABLE_HEIGHT, TABLE_WIDTH, clusteredLightTotal, writeLightRecord, } from '../../clusteredLights.js';
|
|
2
2
|
import { MAX_POINT_LIGHTS } from '../../lightBudget.js';
|
|
3
3
|
import { CLUSTER_BIN_WGSL } from './shaders/clusterBin.wgsl.js';
|
|
4
4
|
/**
|
|
@@ -129,7 +129,8 @@ export class ClusterBinner {
|
|
|
129
129
|
return;
|
|
130
130
|
if (params === null || lightBuffer === null)
|
|
131
131
|
return;
|
|
132
|
-
|
|
132
|
+
/* The lamps and the clustered rectangles after them. See `ClusterLightSet.areas`. */
|
|
133
|
+
const count = clusteredLightTotal(frame.lights);
|
|
133
134
|
/*
|
|
134
135
|
* The records, by the function `buildLightClusters` writes them with, because the shader
|
|
135
136
|
* copies them into the table verbatim and the lit pass reads one layout on both backends.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { SurfaceModelKind } from '../../surfaceModel.ts';
|
|
2
|
-
import type
|
|
2
|
+
import { type SkinHalf } from '../../shaders/flat/models.ts';
|
|
3
3
|
import type { PipelineCache } from './pipelineCache.ts';
|
|
4
4
|
import type { ClothViews } from './clothTextures.ts';
|
|
5
5
|
/**
|
|
@@ -69,12 +69,14 @@ export declare function flatVertexKey(skinned: boolean, morphed: boolean, instan
|
|
|
69
69
|
/** Everything the generator recorded about one variant of the vertex stage. */
|
|
70
70
|
export declare function flatVertexBindings(skinned: boolean, morphed?: boolean, instanced?: boolean): StageBindings;
|
|
71
71
|
/**
|
|
72
|
-
* A vertex stage's constants, keyed by each override's **id** as the fragment's are: `SKIN_EIGHT`
|
|
73
|
-
* a skinned stage, nothing in any other. Every override a stage declares is set, and one this does
|
|
72
|
+
* A vertex stage's constants, keyed by each override's **id** as the fragment's are: `SKIN_EIGHT` and
|
|
73
|
+
* `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
|
|
74
74
|
* not know is refused here rather than defaulted. Shared with the shadow and glass-tint casters,
|
|
75
75
|
* whose skinned stages declare the same switch.
|
|
76
76
|
*/
|
|
77
|
-
export declare function vertexConstants(bindings: StageBindings, skinEight: boolean, cloth?: boolean
|
|
77
|
+
export declare function vertexConstants(bindings: StageBindings, skinEight: boolean, cloth?: boolean,
|
|
78
|
+
/** Whether an instanced stage reads its tint lane as a lightmap region. See `lightmap.ts`. */
|
|
79
|
+
lightmapRegions?: boolean): Record<string, number>;
|
|
78
80
|
export declare const FLAT_VERT_FIELDS: {
|
|
79
81
|
readonly uViewProj: {
|
|
80
82
|
readonly offset: 0;
|
|
@@ -233,7 +235,7 @@ skinEight?: boolean,
|
|
|
233
235
|
cloth?: boolean,
|
|
234
236
|
/** Which shading model its fragment stage shades by, null the standard. The key must carry it. */
|
|
235
237
|
model?: SurfaceModelKind | null,
|
|
236
|
-
/** Which half of a skin it draws. The key must carry it: `|ss` or `|
|
|
238
|
+
/** Which half of a skin it draws. The key must carry it: `|ss`, `|sd` or `|sa`. See `SkinHalf`. */
|
|
237
239
|
skinPass?: SkinHalf): GPURenderPipeline;
|
|
238
240
|
/**
|
|
239
241
|
* The same pipeline, compiled off the main thread.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { SURFACE_MODEL_SWITCH } from '../../surfaceModel.js';
|
|
2
|
+
import { SKIN_HALF_SWITCH } from '../../shaders/flat/models.js';
|
|
2
3
|
import { DEPTH_COMPARE, DEPTH_FORMAT, depthOffsetForLayer } from '../../depthConvention.js';
|
|
3
4
|
import { FLAT_BINDINGS, FLAT_FRAG_WGSL, FLAT_VERT_WGSL, } from '../../shaders/generated/flat.wgsl.js';
|
|
4
5
|
import { vertexBufferLayouts } from './buffers.js';
|
|
@@ -71,18 +72,22 @@ export function flatVertexBindings(skinned, morphed = false, instanced = false)
|
|
|
71
72
|
}
|
|
72
73
|
const PLAIN_VERTEX = flatVertexBindings(false);
|
|
73
74
|
/**
|
|
74
|
-
* A vertex stage's constants, keyed by each override's **id** as the fragment's are: `SKIN_EIGHT`
|
|
75
|
-
* a skinned stage, nothing in any other. Every override a stage declares is set, and one this does
|
|
75
|
+
* A vertex stage's constants, keyed by each override's **id** as the fragment's are: `SKIN_EIGHT` and
|
|
76
|
+
* `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
|
|
76
77
|
* not know is refused here rather than defaulted. Shared with the shadow and glass-tint casters,
|
|
77
78
|
* whose skinned stages declare the same switch.
|
|
78
79
|
*/
|
|
79
|
-
export function vertexConstants(bindings, skinEight, cloth = false
|
|
80
|
+
export function vertexConstants(bindings, skinEight, cloth = false,
|
|
81
|
+
/** Whether an instanced stage reads its tint lane as a lightmap region. See `lightmap.ts`. */
|
|
82
|
+
lightmapRegions = false) {
|
|
80
83
|
const constants = {};
|
|
81
84
|
for (const [name, id] of Object.entries(bindings.overrides ?? {})) {
|
|
82
85
|
if (name === 'SKIN_EIGHT')
|
|
83
86
|
constants[String(id)] = skinEight ? 1 : 0;
|
|
84
87
|
else if (name === 'CLOTH_BOUND')
|
|
85
88
|
constants[String(id)] = cloth ? 1 : 0;
|
|
89
|
+
else if (name === 'LIGHTMAP_REGIONS')
|
|
90
|
+
constants[String(id)] = lightmapRegions ? 1 : 0;
|
|
86
91
|
else
|
|
87
92
|
throw new Error(`flatPass: a vertex stage declares a switch "${name}" nothing sets`);
|
|
88
93
|
}
|
|
@@ -168,13 +173,9 @@ function flatFragmentConstants(variant, cache, model, skinPass = 'whole') {
|
|
|
168
173
|
constants[String(id)] = name === modelSwitch ? 1 : 0;
|
|
169
174
|
continue;
|
|
170
175
|
}
|
|
171
|
-
/* Skin's
|
|
172
|
-
if (name === 'SKIN_SCREEN' || name === 'SKIN_DIFFUSE') {
|
|
173
|
-
constants[String(id)] =
|
|
174
|
-
(name === 'SKIN_SCREEN' && skinPass === 'scene') ||
|
|
175
|
-
(name === 'SKIN_DIFFUSE' && skinPass === 'diffuse')
|
|
176
|
-
? 1
|
|
177
|
-
: 0;
|
|
176
|
+
/* Skin's halves under the screen-space blur, set from the key as a model's switch is. */
|
|
177
|
+
if (name === 'SKIN_SCREEN' || name === 'SKIN_DIFFUSE' || name === 'SKIN_ALBEDO') {
|
|
178
|
+
constants[String(id)] = skinPass !== 'whole' && name === SKIN_HALF_SWITCH[skinPass] ? 1 : 0;
|
|
178
179
|
continue;
|
|
179
180
|
}
|
|
180
181
|
if (!(name in cache.litSwitches)) {
|
|
@@ -479,7 +480,7 @@ skinEight = false,
|
|
|
479
480
|
cloth = false,
|
|
480
481
|
/** Which shading model its fragment stage shades by, null the standard. The key must carry it. */
|
|
481
482
|
model = null,
|
|
482
|
-
/** Which half of a skin it draws. The key must carry it: `|ss` or `|
|
|
483
|
+
/** Which half of a skin it draws. The key must carry it: `|ss`, `|sd` or `|sa`. See `SkinHalf`. */
|
|
483
484
|
skinPass = 'whole') {
|
|
484
485
|
return cache.get(key, () => flatDescriptor(cache, device, layout, variant, key, present, translucent, skinned, morphed, depthWrite, depthLayer, instanced, oit, doubleSided, alphaToCoverage, skinEight, cloth, model, skinPass), true);
|
|
485
486
|
}
|
|
@@ -563,7 +564,8 @@ doubleSided = false, alphaToCoverage = false, skinEight = false,
|
|
|
563
564
|
/** Whether its vertex stage places vertices by a cloth binding. See `clothBinding.ts`. */
|
|
564
565
|
cloth = false, model = null, skinPass = 'whole') {
|
|
565
566
|
const offset = depthOffsetForLayer(depthLayer);
|
|
566
|
-
|
|
567
|
+
/* The diffuse and colour halves are the same surface again, into the blur's targets. */
|
|
568
|
+
const diffuseAlone = skinPass === 'diffuse' || skinPass === 'albedo';
|
|
567
569
|
return {
|
|
568
570
|
label: key,
|
|
569
571
|
layout: device.createPipelineLayout({ bindGroupLayouts: [layout] }),
|
|
@@ -579,7 +581,7 @@ cloth = false, model = null, skinPass = 'whole') {
|
|
|
579
581
|
}),
|
|
580
582
|
entryPoint: 'main',
|
|
581
583
|
buffers: vertexBufferLayouts(present, instanced),
|
|
582
|
-
constants: vertexConstants(flatVertexBindings(skinned, morphed, instanced), skinEight, cloth),
|
|
584
|
+
constants: vertexConstants(flatVertexBindings(skinned, morphed, instanced), skinEight, cloth, model === 'lightmap'),
|
|
583
585
|
},
|
|
584
586
|
fragment: {
|
|
585
587
|
module: shaderModule(device, {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type ParticleMaterial } from '../../particleMaterial.ts';
|
|
2
2
|
import type { ParticleInstances } from '../../particlePool.ts';
|
|
3
|
-
import type
|
|
3
|
+
import { type ParticleBlend, type ParticleFacing } from '../../particleBatch.ts';
|
|
4
|
+
import { type ParticleSort } from '../../particleSort.ts';
|
|
4
5
|
import type { PipelineCache } from './pipelineCache.ts';
|
|
5
6
|
import type { UniformFields } from './scatterPass.ts';
|
|
6
7
|
/**
|
|
@@ -33,8 +34,22 @@ export interface GpuParticles {
|
|
|
33
34
|
readonly coreGain: number;
|
|
34
35
|
/** Whether the mote fragment stage mixes toward the medium. Ignored by the other materials. */
|
|
35
36
|
readonly fog: boolean;
|
|
37
|
+
/** A sprite's image view, or null for a procedural material. See `ParticleBatchOptions`. */
|
|
38
|
+
readonly texture: GPUTextureView | null;
|
|
39
|
+
readonly cells: readonly [number, number];
|
|
40
|
+
readonly blendCells: boolean;
|
|
41
|
+
readonly softDepth: number;
|
|
42
|
+
readonly cameraFade: number;
|
|
43
|
+
readonly cameraOffset: number;
|
|
44
|
+
/** Room to draw farthest first, where the batch asked. See `particleSort.ts`. */
|
|
45
|
+
readonly order: ParticleSort | null;
|
|
46
|
+
/** A sprite's bind group, which holds the frame's depth copy and is rebuilt when that moves. */
|
|
47
|
+
spriteGroup: GPUBindGroup | null;
|
|
48
|
+
spriteGroupDepth: GPUTextureView | null;
|
|
36
49
|
/** Reused every frame; see `packInstances`. */
|
|
37
50
|
readonly staging: Float32Array;
|
|
51
|
+
/** The sprite stream, two floats a particle, before it is woven into `staging`. */
|
|
52
|
+
readonly spriteStaging: Float32Array;
|
|
38
53
|
/**
|
|
39
54
|
* This batch's own uniform buffers, staging and bind group — never shared with another
|
|
40
55
|
* batch of the same material. See the note on `createGpuParticles` for why: two pools of one
|
|
@@ -49,7 +64,8 @@ export interface GpuParticles {
|
|
|
49
64
|
readonly fragFloats: Float32Array;
|
|
50
65
|
readonly fragInts: Int32Array;
|
|
51
66
|
readonly fields: UniformFields;
|
|
52
|
-
|
|
67
|
+
/** The procedural materials' group; a sprite's holds the depth copy, so it is `spriteGroup`. */
|
|
68
|
+
readonly bindGroup: GPUBindGroup | null;
|
|
53
69
|
/**
|
|
54
70
|
* The encoder this batch was last drawn into, by the renderer's count, or -1.
|
|
55
71
|
*
|
|
@@ -77,7 +93,7 @@ export interface GpuParticles {
|
|
|
77
93
|
* written last. `layout` still comes from the caller and is shared per material: it is
|
|
78
94
|
* structural, not data, so nothing about sharing it can go stale mid-frame.
|
|
79
95
|
*/
|
|
80
|
-
export declare function createGpuParticles(device: GPUDevice, capacity: number, material: ParticleMaterial, blend: ParticleBlend, stretchSec: number, facing: ParticleFacing, erosion: number, coreGain: number, fog: boolean, layout: GPUBindGroupLayout, fragFields: UniformFields, fragSize: number): GpuParticles;
|
|
96
|
+
export declare function createGpuParticles(device: GPUDevice, capacity: number, material: ParticleMaterial, blend: ParticleBlend, stretchSec: number, facing: ParticleFacing, erosion: number, coreGain: number, fog: boolean, layout: GPUBindGroupLayout, fragFields: UniformFields, fragSize: number, sprite?: SpriteOptions | null): GpuParticles;
|
|
81
97
|
/**
|
|
82
98
|
* Interleave the live prefix of eight streams into the batch's own staging array.
|
|
83
99
|
*
|
|
@@ -85,7 +101,24 @@ export declare function createGpuParticles(device: GPUDevice, capacity: number,
|
|
|
85
101
|
* into an array allocated with the batch — the frame loop may not allocate.
|
|
86
102
|
*/
|
|
87
103
|
export declare function packInstances(batch: GpuParticles, data: ParticleInstances): number;
|
|
88
|
-
|
|
104
|
+
/** The floats `packInstances` writes per particle, for the caller's upload. */
|
|
105
|
+
export declare const PARTICLE_INSTANCE_FLOATS = 16;
|
|
106
|
+
/** What a `'sprite'` batch carries beyond a procedural one. See `ParticleBatchOptions`. */
|
|
107
|
+
export interface SpriteOptions {
|
|
108
|
+
readonly texture: GPUTextureView | null;
|
|
109
|
+
readonly cells: readonly [number, number];
|
|
110
|
+
readonly blendCells: boolean;
|
|
111
|
+
readonly softDepth: number;
|
|
112
|
+
readonly cameraFade: number;
|
|
113
|
+
readonly cameraOffset: number;
|
|
114
|
+
readonly sort: boolean;
|
|
115
|
+
}
|
|
116
|
+
export declare function createParticleBindGroupLayout(device: GPUDevice, fragSize: number, sprite?: boolean): GPUBindGroupLayout;
|
|
117
|
+
/**
|
|
118
|
+
* A sprite batch's bind group: its uniforms, its image, and the frame's depth copy — or any
|
|
119
|
+
* single-sample float texture standing in, where the batch asks for no soft edge.
|
|
120
|
+
*/
|
|
121
|
+
export declare function createSpriteBindGroup(device: GPUDevice, layout: GPUBindGroupLayout, batch: GpuParticles, fragSize: number, depth: GPUTextureView, linear: GPUSampler, nearest: GPUSampler): GPUBindGroup;
|
|
89
122
|
export declare function createParticleBindGroup(device: GPUDevice, layout: GPUBindGroupLayout, perDraw: GPUBuffer, perFrame: GPUBuffer, fragSize: number): GPUBindGroup;
|
|
90
123
|
/**
|
|
91
124
|
* The pipeline for one material and blend.
|
|
@@ -2,6 +2,8 @@ import { DEPTH_COMPARE } from '../../depthConvention.js';
|
|
|
2
2
|
import { particleShaders } from '../../particleMaterial.js';
|
|
3
3
|
import { PARTICLE_BINDINGS } from '../../shaders/generated/particle.wgsl.js';
|
|
4
4
|
import { PARTICLE_BLADES, PARTICLE_INDICES, PARTICLE_VERTS } from '../../shaders/particle.js';
|
|
5
|
+
import { packSpriteStream } from '../../particleBatch.js';
|
|
6
|
+
import { createParticleSort } from '../../particleSort.js';
|
|
5
7
|
import { DEPTH_FORMAT } from './flatPass.js';
|
|
6
8
|
import { shaderModule } from './shaderModules.js';
|
|
7
9
|
import { SCENE_ALPHA_COVERS } from '../../sceneCoverage.js';
|
|
@@ -22,7 +24,9 @@ export function particleFragBindings(material) {
|
|
|
22
24
|
? PARTICLE_BINDINGS.PARTICLE_SPARK_FRAG
|
|
23
25
|
: material === 'mote'
|
|
24
26
|
? PARTICLE_BINDINGS.PARTICLE_MOTE_FRAG
|
|
25
|
-
:
|
|
27
|
+
: material === 'sprite'
|
|
28
|
+
? PARTICLE_BINDINGS.PARTICLE_SPRITE_FRAG
|
|
29
|
+
: PARTICLE_BINDINGS.PARTICLE_SMOKE_FRAG;
|
|
26
30
|
return { fields: stage.fields, size: stage.uniformSize };
|
|
27
31
|
}
|
|
28
32
|
const VISIBILITY_VERTEX = 0x1;
|
|
@@ -31,17 +35,17 @@ const USAGE_VERTEX = 0x0020 | 0x0008;
|
|
|
31
35
|
const USAGE_INDEX = 0x0010 | 0x0008;
|
|
32
36
|
const USAGE_UNIFORM_DST = 0x0040 | 0x0008;
|
|
33
37
|
/**
|
|
34
|
-
*
|
|
38
|
+
* Eleven attributes and eight slots, so the per-instance nine share one buffer.
|
|
35
39
|
*
|
|
36
|
-
* **`maxVertexBuffers` is eight and this shader declares
|
|
37
|
-
* interleave. The per-instance streams are the ones that do: the engine holds them as
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
+
* **`maxVertexBuffers` is eight and this shader declares eleven inputs**, so something has to
|
|
41
|
+
* interleave. The per-instance streams are the ones that do: the engine holds them as separate
|
|
42
|
+
* arrays and they are packed into one interleaved buffer per frame, which is a copy the upload
|
|
43
|
+
* was already paying per stream rather than new work.
|
|
40
44
|
*
|
|
41
45
|
* Floats per instance, in the order they are packed: position 3, size 1, spin 1, colour 3,
|
|
42
|
-
* alpha 1, age 1, seed 1, velocity 3.
|
|
46
|
+
* alpha 1, age 1, seed 1, velocity 3, and a sprite's frame and half-height 2.
|
|
43
47
|
*/
|
|
44
|
-
const INSTANCE_FLOATS =
|
|
48
|
+
const INSTANCE_FLOATS = 16;
|
|
45
49
|
const INSTANCE_ATTRIBUTES = [
|
|
46
50
|
{ shaderLocation: 2, offset: 0, format: 'float32x3' },
|
|
47
51
|
{ shaderLocation: 3, offset: 12, format: 'float32' },
|
|
@@ -51,6 +55,7 @@ const INSTANCE_ATTRIBUTES = [
|
|
|
51
55
|
{ shaderLocation: 7, offset: 36, format: 'float32' },
|
|
52
56
|
{ shaderLocation: 8, offset: 40, format: 'float32' },
|
|
53
57
|
{ shaderLocation: 9, offset: 44, format: 'float32x3' },
|
|
58
|
+
{ shaderLocation: 10, offset: 56, format: 'float32x2' },
|
|
54
59
|
];
|
|
55
60
|
export function particleVertexLayouts() {
|
|
56
61
|
return [
|
|
@@ -87,7 +92,7 @@ export function particleVertexLayouts() {
|
|
|
87
92
|
* written last. `layout` still comes from the caller and is shared per material: it is
|
|
88
93
|
* structural, not data, so nothing about sharing it can go stale mid-frame.
|
|
89
94
|
*/
|
|
90
|
-
export function createGpuParticles(device, capacity, material, blend, stretchSec, facing, erosion, coreGain, fog, layout, fragFields, fragSize) {
|
|
95
|
+
export function createGpuParticles(device, capacity, material, blend, stretchSec, facing, erosion, coreGain, fog, layout, fragFields, fragSize, sprite = null) {
|
|
91
96
|
/* The cross, built once: two blades of four corners, interleaved with the blade index. */
|
|
92
97
|
const corners = new Float32Array(PARTICLE_VERTS * 3);
|
|
93
98
|
const CORNERS = [
|
|
@@ -158,7 +163,17 @@ export function createGpuParticles(device, capacity, material, blend, stretchSec
|
|
|
158
163
|
erosion,
|
|
159
164
|
coreGain,
|
|
160
165
|
fog,
|
|
166
|
+
texture: sprite?.texture ?? null,
|
|
167
|
+
cells: sprite?.cells ?? [1, 1],
|
|
168
|
+
blendCells: sprite?.blendCells ?? false,
|
|
169
|
+
softDepth: sprite?.softDepth ?? 0,
|
|
170
|
+
cameraFade: sprite?.cameraFade ?? 0,
|
|
171
|
+
cameraOffset: sprite?.cameraOffset ?? 0,
|
|
172
|
+
order: sprite?.sort === true ? createParticleSort(capacity) : null,
|
|
173
|
+
spriteGroup: null,
|
|
174
|
+
spriteGroupDepth: null,
|
|
161
175
|
staging: new Float32Array(capacity * INSTANCE_FLOATS),
|
|
176
|
+
spriteStaging: new Float32Array(capacity * 2),
|
|
162
177
|
vertUniforms,
|
|
163
178
|
fragUniforms,
|
|
164
179
|
vertStaging,
|
|
@@ -167,7 +182,9 @@ export function createGpuParticles(device, capacity, material, blend, stretchSec
|
|
|
167
182
|
fragFloats: new Float32Array(fragStaging),
|
|
168
183
|
fragInts: new Int32Array(fragStaging),
|
|
169
184
|
fields: fragFields,
|
|
170
|
-
bindGroup:
|
|
185
|
+
bindGroup: material === 'sprite'
|
|
186
|
+
? null
|
|
187
|
+
: createParticleBindGroup(device, layout, vertUniforms, fragUniforms, fragSize),
|
|
171
188
|
dispose() {
|
|
172
189
|
cornerBuffer.destroy();
|
|
173
190
|
instances.destroy();
|
|
@@ -186,6 +203,8 @@ export function createGpuParticles(device, capacity, material, blend, stretchSec
|
|
|
186
203
|
export function packInstances(batch, data) {
|
|
187
204
|
const n = Math.min(data.count, batch.capacity);
|
|
188
205
|
const out = batch.staging;
|
|
206
|
+
/* The sprite's frame and half-height, two floats a particle, woven in below. */
|
|
207
|
+
packSpriteStream(data, n, batch.spriteStaging);
|
|
189
208
|
for (let i = 0; i < n; i++) {
|
|
190
209
|
const o = i * INSTANCE_FLOATS;
|
|
191
210
|
const p = i * 3;
|
|
@@ -203,12 +222,17 @@ export function packInstances(batch, data) {
|
|
|
203
222
|
out[o + 11] = data.velocities[p];
|
|
204
223
|
out[o + 12] = data.velocities[p + 1];
|
|
205
224
|
out[o + 13] = data.velocities[p + 2];
|
|
225
|
+
out[o + 14] = batch.spriteStaging[i * 2];
|
|
226
|
+
out[o + 15] = batch.spriteStaging[i * 2 + 1];
|
|
206
227
|
}
|
|
207
228
|
return n;
|
|
208
229
|
}
|
|
209
|
-
|
|
230
|
+
/** The floats `packInstances` writes per particle, for the caller's upload. */
|
|
231
|
+
export const PARTICLE_INSTANCE_FLOATS = INSTANCE_FLOATS;
|
|
232
|
+
export function createParticleBindGroupLayout(device, fragSize, sprite = false) {
|
|
233
|
+
const textures = PARTICLE_BINDINGS.PARTICLE_SPRITE_FRAG.textures;
|
|
210
234
|
return device.createBindGroupLayout({
|
|
211
|
-
label: 'particle.layout',
|
|
235
|
+
label: sprite ? 'particle.layout:sprite' : 'particle.layout',
|
|
212
236
|
entries: [
|
|
213
237
|
{
|
|
214
238
|
binding: VERT_BINDING,
|
|
@@ -220,6 +244,50 @@ export function createParticleBindGroupLayout(device, fragSize) {
|
|
|
220
244
|
visibility: VISIBILITY_FRAGMENT,
|
|
221
245
|
buffer: { type: 'uniform', minBindingSize: fragSize },
|
|
222
246
|
},
|
|
247
|
+
/* The image, filtered, and the frame's depth copy, read texel for texel. */
|
|
248
|
+
...(sprite
|
|
249
|
+
? [
|
|
250
|
+
{
|
|
251
|
+
binding: textures.uSprite.texture,
|
|
252
|
+
visibility: VISIBILITY_FRAGMENT,
|
|
253
|
+
texture: { sampleType: 'float', viewDimension: '2d-array' },
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
binding: textures.uSprite.sampler,
|
|
257
|
+
visibility: VISIBILITY_FRAGMENT,
|
|
258
|
+
sampler: { type: 'filtering' },
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
binding: textures.uDepth.texture,
|
|
262
|
+
visibility: VISIBILITY_FRAGMENT,
|
|
263
|
+
texture: { sampleType: 'unfilterable-float' },
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
binding: textures.uDepth.sampler,
|
|
267
|
+
visibility: VISIBILITY_FRAGMENT,
|
|
268
|
+
sampler: { type: 'non-filtering' },
|
|
269
|
+
},
|
|
270
|
+
]
|
|
271
|
+
: []),
|
|
272
|
+
],
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* A sprite batch's bind group: its uniforms, its image, and the frame's depth copy — or any
|
|
277
|
+
* single-sample float texture standing in, where the batch asks for no soft edge.
|
|
278
|
+
*/
|
|
279
|
+
export function createSpriteBindGroup(device, layout, batch, fragSize, depth, linear, nearest) {
|
|
280
|
+
const textures = PARTICLE_BINDINGS.PARTICLE_SPRITE_FRAG.textures;
|
|
281
|
+
return device.createBindGroup({
|
|
282
|
+
label: 'particle.bindGroup:sprite',
|
|
283
|
+
layout,
|
|
284
|
+
entries: [
|
|
285
|
+
{ binding: VERT_BINDING, resource: { buffer: batch.vertUniforms, size: PARTICLE_VERT_SIZE } },
|
|
286
|
+
{ binding: FRAG_BINDING, resource: { buffer: batch.fragUniforms, size: fragSize } },
|
|
287
|
+
{ binding: textures.uSprite.texture, resource: batch.texture },
|
|
288
|
+
{ binding: textures.uSprite.sampler, resource: linear },
|
|
289
|
+
{ binding: textures.uDepth.texture, resource: depth },
|
|
290
|
+
{ binding: textures.uDepth.sampler, resource: nearest },
|
|
223
291
|
],
|
|
224
292
|
});
|
|
225
293
|
}
|
|
@@ -180,7 +180,12 @@ export declare const PREMULTIPLIED_OVER_BLEND: GPUBlendState;
|
|
|
180
180
|
* the reflection's own and its alpha is the coverage this resolve reads.
|
|
181
181
|
*/
|
|
182
182
|
export declare const REFLECTION_OVER_BLEND: GPUBlendState;
|
|
183
|
-
|
|
183
|
+
/**
|
|
184
|
+
* The bloom pyramid's way back up: an octave added onto a level that the blend multiplies by that
|
|
185
|
+
* level's tint first — the blend constant, which white makes `one, one` exactly. Into the pyramid's
|
|
186
|
+
* own levels, never the scene. See `BloomResponse`.
|
|
187
|
+
*/
|
|
188
|
+
export declare const TINTED_ADD_BLEND: GPUBlendState;
|
|
184
189
|
/** A fullscreen pass over one or more inputs, writing to one colour target. */
|
|
185
190
|
export declare function postPipeline(cache: PipelineCache, device: GPUDevice, layout: GPUBindGroupLayout, key: string, fragment: string, format: GPUTextureFormat, blend?: GPUBlendState): GPURenderPipeline;
|
|
186
191
|
/**
|
|
@@ -252,8 +252,13 @@ export const REFLECTION_OVER_BLEND = {
|
|
|
252
252
|
color: { srcFactor: 'one', dstFactor: 'one-minus-src-alpha', operation: 'add' },
|
|
253
253
|
alpha: SCENE_ALPHA_KEEPS,
|
|
254
254
|
};
|
|
255
|
-
|
|
256
|
-
|
|
255
|
+
/**
|
|
256
|
+
* The bloom pyramid's way back up: an octave added onto a level that the blend multiplies by that
|
|
257
|
+
* level's tint first — the blend constant, which white makes `one, one` exactly. Into the pyramid's
|
|
258
|
+
* own levels, never the scene. See `BloomResponse`.
|
|
259
|
+
*/
|
|
260
|
+
export const TINTED_ADD_BLEND = {
|
|
261
|
+
color: { srcFactor: 'one', dstFactor: 'constant', operation: 'add' },
|
|
257
262
|
alpha: { srcFactor: 'one', dstFactor: 'one', operation: 'add' },
|
|
258
263
|
};
|
|
259
264
|
/** A fullscreen pass over one or more inputs, writing to one colour target. */
|