reze-engine 0.57.6 → 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.
@@ -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 together. */
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"};\n` +
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
- if (c.a <= 0.0) { discard; }
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":"AA4BA,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;CACf,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,CAyZR"}
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() +
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reze-engine",
3
- "version": "0.57.6",
3
+ "version": "0.58.0",
4
4
  "description": "A lightweight WebGPU engine for real-time 3D MMD/PMX model rendering",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",