reze-engine 0.57.5 → 0.58.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/engine.d.ts +23 -0
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +187 -77
- package/dist/shaders/directives.d.ts +18 -1
- package/dist/shaders/directives.d.ts.map +1 -1
- package/dist/shaders/directives.js +22 -3
- package/dist/shaders/materials/common.js +4 -4
- package/dist/shaders/passes/particles.d.ts +18 -2
- package/dist/shaders/passes/particles.d.ts.map +1 -1
- package/dist/shaders/passes/particles.js +70 -4
- package/dist/shaders/passes/trails.d.ts +3 -0
- package/dist/shaders/passes/trails.d.ts.map +1 -1
- package/dist/shaders/passes/trails.js +5 -0
- package/package.json +1 -1
- package/src/engine.ts +226 -91
- package/src/shaders/directives.ts +37 -4
- package/src/shaders/materials/common.ts +4 -4
- package/src/shaders/passes/particles.ts +85 -5
- package/src/shaders/passes/trails.ts +8 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { castDistanceStub } from "./cast-distance";
|
|
2
|
+
import { gridReadApi } from "./grid";
|
|
2
3
|
import { RZ_LIGHT_STRUCT_WGSL } from "../lights";
|
|
3
4
|
import { audioApi } from "../audio-api";
|
|
4
5
|
import { lyricsApi } from "../lyrics-api";
|
|
@@ -91,12 +92,14 @@ fn rzProject(p: vec3f) -> vec3f {
|
|
|
91
92
|
}
|
|
92
93
|
fn rzCamPos() -> vec3f { return cam.camPos; }
|
|
93
94
|
`;
|
|
94
|
-
/** Does the source define the particle contract? All three are required
|
|
95
|
+
/** Does the source define the particle contract? All three are required
|
|
96
|
+
* together; `cover` is optional and only means anything to a cutout. */
|
|
95
97
|
export function particleEntryPoints(wgsl) {
|
|
96
98
|
return {
|
|
97
99
|
init: /\bfn\s+particleInit\s*\(/.test(wgsl),
|
|
98
100
|
step: /\bfn\s+particleStep\s*\(/.test(wgsl),
|
|
99
101
|
shade: /\bfn\s+particleShade\s*\(/.test(wgsl),
|
|
102
|
+
cover: /\bfn\s+particleCover\s*\(/.test(wgsl),
|
|
100
103
|
};
|
|
101
104
|
}
|
|
102
105
|
/**
|
|
@@ -123,6 +126,11 @@ ${src.paramsDecl}
|
|
|
123
126
|
audioApi(0, 4) +
|
|
124
127
|
midiApi(0, 5) +
|
|
125
128
|
lyricsApi(0, 6) +
|
|
129
|
+
// Always, even with no grid — then it samples a 1x1 of zeroes. Same reason
|
|
130
|
+
// composite compiles it unconditionally, plus one this module owns: an
|
|
131
|
+
// effect's whole file is spliced here, so a `gridStep` written for the grid
|
|
132
|
+
// pass has to RESOLVE here too, and the stubs that let it are in here.
|
|
133
|
+
gridReadApi(0, 8, 9, src.gridSize) +
|
|
126
134
|
PRELUDE +
|
|
127
135
|
// Stubbed: this module cannot read an attachment the scene pass writes — see
|
|
128
136
|
// id-api.ts. The author's whole file compiles here, so the names must exist.
|
|
@@ -168,7 +176,13 @@ fn main(@builtin(global_invocation_id) gid: vec3u) {
|
|
|
168
176
|
*/
|
|
169
177
|
export function buildParticleRenderShader(src, cast) {
|
|
170
178
|
return (`override BLOOM: bool = ${src.bloom ? "true" : "false"};
|
|
171
|
-
override ADDITIVE: bool = ${src.blend === "additive" ? "true" : "false"}
|
|
179
|
+
override ADDITIVE: bool = ${src.blend === "additive" ? "true" : "false"};
|
|
180
|
+
override CUTOUT: bool = ${src.blend === "cutout" ? "true" : "false"};
|
|
181
|
+
// A cutout with a prepass: the depth is already written and this pass draws at
|
|
182
|
+
// depthCompare equal, so it neither discards nor masks — the depth test IS the
|
|
183
|
+
// coverage, per sample, and a shader with no discard keeps the hardware's
|
|
184
|
+
// early rejection.
|
|
185
|
+
override PREPASSED: bool = ${src.blend === "cutout" && src.cover ? "true" : "false"};\n` +
|
|
172
186
|
PARTICLE_STRUCT_WGSL +
|
|
173
187
|
CAMERA_STRUCT +
|
|
174
188
|
PARTICLE_UNIFORMS +
|
|
@@ -183,6 +197,11 @@ ${src.paramsDecl}
|
|
|
183
197
|
audioApi(0, 4) +
|
|
184
198
|
midiApi(0, 5) +
|
|
185
199
|
lyricsApi(0, 6) +
|
|
200
|
+
// Always, even with no grid — then it samples a 1x1 of zeroes. Same reason
|
|
201
|
+
// composite compiles it unconditionally, plus one this module owns: an
|
|
202
|
+
// effect's whole file is spliced here, so a `gridStep` written for the grid
|
|
203
|
+
// pass has to RESOLVE here too, and the stubs that let it are in here.
|
|
204
|
+
gridReadApi(0, 8, 9, src.gridSize) +
|
|
186
205
|
PRELUDE +
|
|
187
206
|
// Stubbed: this module cannot read an attachment the scene pass writes — see
|
|
188
207
|
// id-api.ts. The author's whole file compiles here, so the names must exist.
|
|
@@ -239,6 +258,12 @@ fn vs(@builtin(vertex_index) vi: u32, @builtin(instance_index) ii: u32) -> VSOut
|
|
|
239
258
|
|
|
240
259
|
struct FSOut {
|
|
241
260
|
@location(0) color: vec4f,
|
|
261
|
+
// Which of the pixel's MSAA samples this fragment covers. Alpha-to-coverage
|
|
262
|
+
// by hand: the hardware kind reads alpha off the first colour target, and
|
|
263
|
+
// the HDR target is rg11b10, which has no alpha to read. Every particle
|
|
264
|
+
// writes it; a blended one writes all four, which is what not declaring it
|
|
265
|
+
// would have meant.
|
|
266
|
+
@builtin(sample_mask) samples: u32,
|
|
242
267
|
// The scene's aux target: (bloom mask, coverage). Materials write it, so a
|
|
243
268
|
// particle that skipped it would punch a hole in the mask of whatever it drew
|
|
244
269
|
// over.
|
|
@@ -266,8 +291,25 @@ fn fs(in: VSOut) -> FSOut {
|
|
|
266
291
|
c.a *= pu.weight;
|
|
267
292
|
// Weight 0 therefore discards every fragment, so an effect faded out costs
|
|
268
293
|
// nothing past the vertex stage even on the frame the draw is still issued.
|
|
269
|
-
|
|
294
|
+
// Not after a prepass: that pass already decided coverage into depth, and
|
|
295
|
+
// a discard here would cost this pass the early rejection it exists for.
|
|
296
|
+
if (!PREPASSED && c.a <= 0.0) { discard; }
|
|
270
297
|
var out: FSOut;
|
|
298
|
+
out.samples = 0xFu;
|
|
299
|
+
if (PREPASSED) {
|
|
300
|
+
c.a = 1.0;
|
|
301
|
+
} else if (CUTOUT) {
|
|
302
|
+
// Alpha becomes coverage: 0..4 of the pixel's samples, and the fragment is
|
|
303
|
+
// then opaque wherever it lands. The pattern is turned by pixel position
|
|
304
|
+
// so a half-covered edge is not the same two samples on every pixel along
|
|
305
|
+
// it, which is what makes a2c edges look dithered rather than stepped.
|
|
306
|
+
let n = u32(clamp(c.a, 0.0, 1.0) * 4.0 + 0.5);
|
|
307
|
+
if (n == 0u) { discard; }
|
|
308
|
+
let low = (1u << n) - 1u;
|
|
309
|
+
let turn = (u32(in.clip.x) + u32(in.clip.y)) & 3u;
|
|
310
|
+
out.samples = ((low << turn) | (low >> (4u - turn))) & 0xFu;
|
|
311
|
+
c.a = 1.0;
|
|
312
|
+
}
|
|
271
313
|
// PREMULTIPLIED: the scene's colour target blends with srcFactor \"one\", so a
|
|
272
314
|
// straight-alpha fragment would come out over-bright wherever it is
|
|
273
315
|
// translucent — which is most of a soft particle.
|
|
@@ -288,5 +330,29 @@ fn fs(in: VSOut) -> FSOut {
|
|
|
288
330
|
out.mask = vec4f(mg.x, mg.y, 0.0, c.a);
|
|
289
331
|
${sceneIdPadWgsl("out")} return out;
|
|
290
332
|
}
|
|
291
|
-
`
|
|
333
|
+
` +
|
|
334
|
+
(src.blend === "cutout" && src.cover
|
|
335
|
+
? /* wgsl */ `
|
|
336
|
+
/**
|
|
337
|
+
* The prepass: the shape to depth, nothing to colour. Every output is still
|
|
338
|
+
* declared — the pipeline takes the colour targets at writeMask 0 — and the
|
|
339
|
+
* sample mask is exactly what the single-pass cutout would have written, so
|
|
340
|
+
* the shading pass's equal test lands on the same samples.
|
|
341
|
+
*/
|
|
342
|
+
@fragment
|
|
343
|
+
fn fsDepth(in: VSOut) -> FSOut {
|
|
344
|
+
let p = particles[in.id];
|
|
345
|
+
let a = particleCover(p, in.uv) * pu.weight;
|
|
346
|
+
let n = u32(clamp(a, 0.0, 1.0) * 4.0 + 0.5);
|
|
347
|
+
if (n == 0u) { discard; }
|
|
348
|
+
let low = (1u << n) - 1u;
|
|
349
|
+
let turn = (u32(in.clip.x) + u32(in.clip.y)) & 3u;
|
|
350
|
+
var out: FSOut;
|
|
351
|
+
out.samples = ((low << turn) | (low >> (4u - turn))) & 0xFu;
|
|
352
|
+
out.color = vec4f(0.0);
|
|
353
|
+
out.mask = vec4f(0.0);
|
|
354
|
+
${sceneIdPadWgsl("out")} return out;
|
|
355
|
+
}
|
|
356
|
+
`
|
|
357
|
+
: ""));
|
|
292
358
|
}
|
|
@@ -11,6 +11,9 @@ type TrailSource = {
|
|
|
11
11
|
/** Additive, like most glowing ribbons, or straight alpha. */
|
|
12
12
|
blend: "alpha" | "additive";
|
|
13
13
|
bloom: boolean;
|
|
14
|
+
/** The side of this effect's grid, or 0 when it declared none. See the note
|
|
15
|
+
* on the same field in particles.ts: a ribbon reads the grid, never steps it. */
|
|
16
|
+
gridSize: number;
|
|
14
17
|
};
|
|
15
18
|
/**
|
|
16
19
|
* Sub-segments drawn between each pair of recorded samples.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trails.d.ts","sourceRoot":"","sources":["../../../src/shaders/passes/trails.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"trails.d.ts","sourceRoot":"","sources":["../../../src/shaders/passes/trails.ts"],"names":[],"mappings":"AA6BA,KAAK,WAAW,GAAG;IACjB,kCAAkC;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ,yDAAyD;IACzD,KAAK,EAAE,MAAM,CAAA;IACb;oEACgE;IAChE,WAAW,EAAE,MAAM,EAAE,CAAA;IACrB,6CAA6C;IAC7C,UAAU,EAAE,MAAM,CAAA;IAClB,8DAA8D;IAC9D,KAAK,EAAE,OAAO,GAAG,UAAU,CAAA;IAC3B,KAAK,EAAE,OAAO,CAAA;IACd;sFACkF;IAClF,QAAQ,EAAE,MAAM,CAAA;CACjB,CAAA;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,kBAAkB,IAAI,CAAA;AAEnC,6EAA6E;AAC7E,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAKjF;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,WAAW,EAChB,IAAI,EAAE;IACJ,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,EAAE,CAAA;IACf;;;;kFAI8E;IAC9E,SAAS,EAAE,OAAO,CAAA;CACnB,GACA,MAAM,CA6ZR"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { castDistanceStub } from "./cast-distance";
|
|
2
|
+
import { gridReadApi } from "./grid";
|
|
2
3
|
import { RZ_LIGHT_STRUCT_WGSL } from "../lights";
|
|
3
4
|
import { audioApi } from "../audio-api";
|
|
4
5
|
import { lyricsApi } from "../lyrics-api";
|
|
@@ -240,6 +241,10 @@ fn rzTurnRadius(a: vec3f, b: vec3f, c: vec3f) -> f32 {
|
|
|
240
241
|
audioApi(0, 4) +
|
|
241
242
|
midiApi(0, 5) +
|
|
242
243
|
lyricsApi(0, 6) +
|
|
244
|
+
// Always, with or without a grid — and required either way, because an
|
|
245
|
+
// effect's whole file is spliced here and a `gridStep` written for the grid
|
|
246
|
+
// pass has to resolve in this module too.
|
|
247
|
+
gridReadApi(0, 8, 9, src.gridSize) +
|
|
243
248
|
// Stubbed: this module cannot read an attachment the scene pass writes — see
|
|
244
249
|
// id-api.ts. The author's whole file compiles here, so the names must exist.
|
|
245
250
|
idApi(false, 0, 0) + castDistanceStub() +
|