reze-engine 0.50.8 → 0.50.10

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.
@@ -19,6 +19,6 @@ export declare function commonFsOutWgsl(): string;
19
19
  * clumps of a few millimetres rather than as single-pixel snow, which at any
20
20
  * distance is just a fade.
21
21
  */
22
- export declare const DISSOLVE_WGSL = "\n/** Flake size, in object-space units \u2014 MMD's are roughly centimetres. */\nconst RZ_DISSOLVE_GRAIN: f32 = 0.55;\n/** How wide the glowing front is, in threshold units. */\nconst RZ_DISSOLVE_EDGE: f32 = 0.16;\n/** What the front burns with. Emission, added after lighting and after the\n * graph \u2014 a colour that ramps or takes a shadow reads as paint, not as heat.\n * Above 1 on purpose: it is meant to reach the bloom pyramid. */\nconst RZ_BURN_COLOR: vec3f = vec3f(0.55, 1.85, 2.60);\n\nfn rz_dissolve_hash(p: vec3f) -> f32 {\n let q = fract(p * 0.3183099 + vec3f(0.1, 0.2, 0.3));\n let r = q * 17.0 * (q + 34.0);\n return fract(r.x * r.y * r.z);\n}\n\n/** Value noise over that hash \u2014 smooth within a flake, uncorrelated between. */\nfn rz_dissolve_field(p: vec3f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let c000 = rz_dissolve_hash(i);\n let c100 = rz_dissolve_hash(i + vec3f(1.0, 0.0, 0.0));\n let c010 = rz_dissolve_hash(i + vec3f(0.0, 1.0, 0.0));\n let c110 = rz_dissolve_hash(i + vec3f(1.0, 1.0, 0.0));\n let c001 = rz_dissolve_hash(i + vec3f(0.0, 0.0, 1.0));\n let c101 = rz_dissolve_hash(i + vec3f(1.0, 0.0, 1.0));\n let c011 = rz_dissolve_hash(i + vec3f(0.0, 1.0, 1.0));\n let c111 = rz_dissolve_hash(i + vec3f(1.0, 1.0, 1.0));\n let x00 = mix(c000, c100, u.x);\n let x10 = mix(c010, c110, u.x);\n let x01 = mix(c001, c101, u.x);\n let x11 = mix(c011, c111, u.x);\n return mix(mix(x00, x10, u.y), mix(x01, x11, u.y), u.z);\n}\n\n/**\n * The threshold this fragment is measured against.\n *\n * Tilted by HEIGHT so the dissolve sweeps up the body instead of arriving\n * everywhere at once: a body that comes apart from the feet reads as something\n * happening TO her, and one that fades uniformly reads as an opacity slider.\n * The tilt is gentle \u2014 a third of the range \u2014 so the noise still decides which\n * flake goes when, and the sweep only decides roughly where it is.\n *\n * FEET FIRST, head last: the face is the last thing to go and the first thing\n * back, which is the order every disappearance in film is cut in. Object space\n * puts the floor at y = 0 on any PMX, so the tilt needs no rig measurement.\n */\nfn rz_dissolve_threshold(restPos: vec3f) -> f32 {\n let n = rz_dissolve_field(restPos / RZ_DISSOLVE_GRAIN);\n let low = 1.0 - clamp(restPos.y * 0.045, 0.0, 1.0);\n return clamp(n * 0.68 + low * 0.32, 0.0, 1.0);\n}\n";
22
+ export declare const DISSOLVE_WGSL = "\n/** Clump size, in object-space units \u2014 MMD's are roughly centimetres. */\nconst RZ_DISSOLVE_GRAIN: f32 = 0.42;\n/** Grit cell, as a fraction of a clump. See rz_dissolve_threshold: this is what\n * keeps a material SMALLER than a clump from having one threshold for all of\n * it. Seven millimetres or so on a normal model. */\nconst RZ_DISSOLVE_GRIT: f32 = 0.16;\n/** How wide the glowing front is, in threshold units. Thin: the front is a\n * rim, and a wide one lights whole limbs at once. */\nconst RZ_DISSOLVE_EDGE: f32 = 0.11;\n/** What the front burns with. Emission, added after lighting and after the\n * graph \u2014 a colour that ramps or takes a shadow reads as paint, not as heat.\n * Above 1 on purpose: it is meant to reach the bloom pyramid. Blue-violet,\n * the same light the motes leaving her are drawn and lit with. */\nconst RZ_BURN_COLOR: vec3f = vec3f(0.45, 0.62, 2.40);\n\nfn rz_dissolve_hash(p: vec3f) -> f32 {\n let q = fract(p * 0.3183099 + vec3f(0.1, 0.2, 0.3));\n let r = q * 17.0 * (q + 34.0);\n return fract(r.x * r.y * r.z);\n}\n\n/** Value noise over that hash \u2014 smooth within a flake, uncorrelated between. */\nfn rz_dissolve_field(p: vec3f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let c000 = rz_dissolve_hash(i);\n let c100 = rz_dissolve_hash(i + vec3f(1.0, 0.0, 0.0));\n let c010 = rz_dissolve_hash(i + vec3f(0.0, 1.0, 0.0));\n let c110 = rz_dissolve_hash(i + vec3f(1.0, 1.0, 0.0));\n let c001 = rz_dissolve_hash(i + vec3f(0.0, 0.0, 1.0));\n let c101 = rz_dissolve_hash(i + vec3f(1.0, 0.0, 1.0));\n let c011 = rz_dissolve_hash(i + vec3f(0.0, 1.0, 1.0));\n let c111 = rz_dissolve_hash(i + vec3f(1.0, 1.0, 1.0));\n let x00 = mix(c000, c100, u.x);\n let x10 = mix(c010, c110, u.x);\n let x01 = mix(c001, c101, u.x);\n let x11 = mix(c011, c111, u.x);\n return mix(mix(x00, x10, u.y), mix(x01, x11, u.y), u.z);\n}\n\n/**\n * The threshold this fragment is measured against.\n *\n * NOISE ALONE, so the whole body comes apart at once \u2014 a hand, a shoulder and\n * an ankle all thinning together rather than a line travelling up her. It was\n * tilted by height at first, on the argument that a sweep reads as something\n * happening TO her; what it actually reads as is a wipe, and a wipe has a\n * direction, an edge and a speed the rest of the effect knows nothing about.\n * Everywhere at once is also the honest match for what leaves: the motes are\n * shed from every limb in the same breath.\n *\n * TWO SCALES, and the second one is not decoration \u2014 it is the fix for a real\n * failure. Clumps alone meant every fragment of a material SMALLER than one\n * cell shared a single threshold: an eye highlight, a brow, a button, a hair\n * clip. All of it sat inside the glowing front at once, so the piece flashed\n * white as a whole and then popped out of existence \u2014 the two things a\n * disintegration must never do. The grit is a plain per-cell hash at a fraction\n * of the clump, so a part a few millimetres across still comes apart in pieces.\n *\n * Weighted toward the clumps, because they are the LOOK: flakes a centimetre\n * across, in an order fixed to the surface, so the same piece of sleeve goes at\n * the same moment every time she leaves. The grit only breaks ties.\n */\nfn rz_dissolve_threshold(restPos: vec3f) -> f32 {\n let clump = rz_dissolve_field(restPos / RZ_DISSOLVE_GRAIN);\n let grit = rz_dissolve_hash(floor(restPos / (RZ_DISSOLVE_GRAIN * RZ_DISSOLVE_GRIT)));\n return clamp(clump * 0.7 + grit * 0.3, 0.0, 1.0);\n}\n";
23
23
  export declare const COMMON_MATERIAL_PRELUDE_WGSL: string;
24
24
  //# sourceMappingURL=common.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../../src/shaders/materials/common.ts"],"names":[],"mappings":"AA2BA,eAAO,MAAM,oBAAoB,QAuHhC,CAAC;AAgIF,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAGD;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,aAAa,q5EAsDzB,CAAA;AAQD,eAAO,MAAM,4BAA4B,QAC0D,CAAA"}
1
+ {"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../../src/shaders/materials/common.ts"],"names":[],"mappings":"AA2BA,eAAO,MAAM,oBAAoB,QAuHhC,CAAC;AAgIF,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAGD;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,aAAa,mgHAsEzB,CAAA;AAQD,eAAO,MAAM,4BAA4B,QAC0D,CAAA"}
@@ -286,14 +286,20 @@ export function commonFsOutWgsl() {
286
286
  * distance is just a fade.
287
287
  */
288
288
  export const DISSOLVE_WGSL = /* wgsl */ `
289
- /** Flake size, in object-space units — MMD's are roughly centimetres. */
290
- const RZ_DISSOLVE_GRAIN: f32 = 0.55;
291
- /** How wide the glowing front is, in threshold units. */
292
- const RZ_DISSOLVE_EDGE: f32 = 0.16;
289
+ /** Clump size, in object-space units — MMD's are roughly centimetres. */
290
+ const RZ_DISSOLVE_GRAIN: f32 = 0.42;
291
+ /** Grit cell, as a fraction of a clump. See rz_dissolve_threshold: this is what
292
+ * keeps a material SMALLER than a clump from having one threshold for all of
293
+ * it. Seven millimetres or so on a normal model. */
294
+ const RZ_DISSOLVE_GRIT: f32 = 0.16;
295
+ /** How wide the glowing front is, in threshold units. Thin: the front is a
296
+ * rim, and a wide one lights whole limbs at once. */
297
+ const RZ_DISSOLVE_EDGE: f32 = 0.11;
293
298
  /** What the front burns with. Emission, added after lighting and after the
294
299
  * graph — a colour that ramps or takes a shadow reads as paint, not as heat.
295
- * Above 1 on purpose: it is meant to reach the bloom pyramid. */
296
- const RZ_BURN_COLOR: vec3f = vec3f(0.55, 1.85, 2.60);
300
+ * Above 1 on purpose: it is meant to reach the bloom pyramid. Blue-violet,
301
+ * the same light the motes leaving her are drawn and lit with. */
302
+ const RZ_BURN_COLOR: vec3f = vec3f(0.45, 0.62, 2.40);
297
303
 
298
304
  fn rz_dissolve_hash(p: vec3f) -> f32 {
299
305
  let q = fract(p * 0.3183099 + vec3f(0.1, 0.2, 0.3));
@@ -324,20 +330,30 @@ fn rz_dissolve_field(p: vec3f) -> f32 {
324
330
  /**
325
331
  * The threshold this fragment is measured against.
326
332
  *
327
- * Tilted by HEIGHT so the dissolve sweeps up the body instead of arriving
328
- * everywhere at once: a body that comes apart from the feet reads as something
329
- * happening TO her, and one that fades uniformly reads as an opacity slider.
330
- * The tilt is gentle a third of the range so the noise still decides which
331
- * flake goes when, and the sweep only decides roughly where it is.
333
+ * NOISE ALONE, so the whole body comes apart at once a hand, a shoulder and
334
+ * an ankle all thinning together rather than a line travelling up her. It was
335
+ * tilted by height at first, on the argument that a sweep reads as something
336
+ * happening TO her; what it actually reads as is a wipe, and a wipe has a
337
+ * direction, an edge and a speed the rest of the effect knows nothing about.
338
+ * Everywhere at once is also the honest match for what leaves: the motes are
339
+ * shed from every limb in the same breath.
332
340
  *
333
- * FEET FIRST, head last: the face is the last thing to go and the first thing
334
- * back, which is the order every disappearance in film is cut in. Object space
335
- * puts the floor at y = 0 on any PMX, so the tilt needs no rig measurement.
341
+ * TWO SCALES, and the second one is not decoration it is the fix for a real
342
+ * failure. Clumps alone meant every fragment of a material SMALLER than one
343
+ * cell shared a single threshold: an eye highlight, a brow, a button, a hair
344
+ * clip. All of it sat inside the glowing front at once, so the piece flashed
345
+ * white as a whole and then popped out of existence — the two things a
346
+ * disintegration must never do. The grit is a plain per-cell hash at a fraction
347
+ * of the clump, so a part a few millimetres across still comes apart in pieces.
348
+ *
349
+ * Weighted toward the clumps, because they are the LOOK: flakes a centimetre
350
+ * across, in an order fixed to the surface, so the same piece of sleeve goes at
351
+ * the same moment every time she leaves. The grit only breaks ties.
336
352
  */
337
353
  fn rz_dissolve_threshold(restPos: vec3f) -> f32 {
338
- let n = rz_dissolve_field(restPos / RZ_DISSOLVE_GRAIN);
339
- let low = 1.0 - clamp(restPos.y * 0.045, 0.0, 1.0);
340
- return clamp(n * 0.68 + low * 0.32, 0.0, 1.0);
354
+ let clump = rz_dissolve_field(restPos / RZ_DISSOLVE_GRAIN);
355
+ let grit = rz_dissolve_hash(floor(restPos / (RZ_DISSOLVE_GRAIN * RZ_DISSOLVE_GRIT)));
356
+ return clamp(clump * 0.7 + grit * 0.3, 0.0, 1.0);
341
357
  }
342
358
  `;
343
359
  // ─── Convenience: full shared prelude ───────────────────────────────
@@ -1,2 +1,2 @@
1
- export declare const SHADOW_DEPTH_SHADER_WGSL = "\n\n/** Flake size, in object-space units \u2014 MMD's are roughly centimetres. */\nconst RZ_DISSOLVE_GRAIN: f32 = 0.55;\n/** How wide the glowing front is, in threshold units. */\nconst RZ_DISSOLVE_EDGE: f32 = 0.16;\n/** What the front burns with. Emission, added after lighting and after the\n * graph \u2014 a colour that ramps or takes a shadow reads as paint, not as heat.\n * Above 1 on purpose: it is meant to reach the bloom pyramid. */\nconst RZ_BURN_COLOR: vec3f = vec3f(0.55, 1.85, 2.60);\n\nfn rz_dissolve_hash(p: vec3f) -> f32 {\n let q = fract(p * 0.3183099 + vec3f(0.1, 0.2, 0.3));\n let r = q * 17.0 * (q + 34.0);\n return fract(r.x * r.y * r.z);\n}\n\n/** Value noise over that hash \u2014 smooth within a flake, uncorrelated between. */\nfn rz_dissolve_field(p: vec3f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let c000 = rz_dissolve_hash(i);\n let c100 = rz_dissolve_hash(i + vec3f(1.0, 0.0, 0.0));\n let c010 = rz_dissolve_hash(i + vec3f(0.0, 1.0, 0.0));\n let c110 = rz_dissolve_hash(i + vec3f(1.0, 1.0, 0.0));\n let c001 = rz_dissolve_hash(i + vec3f(0.0, 0.0, 1.0));\n let c101 = rz_dissolve_hash(i + vec3f(1.0, 0.0, 1.0));\n let c011 = rz_dissolve_hash(i + vec3f(0.0, 1.0, 1.0));\n let c111 = rz_dissolve_hash(i + vec3f(1.0, 1.0, 1.0));\n let x00 = mix(c000, c100, u.x);\n let x10 = mix(c010, c110, u.x);\n let x01 = mix(c001, c101, u.x);\n let x11 = mix(c011, c111, u.x);\n return mix(mix(x00, x10, u.y), mix(x01, x11, u.y), u.z);\n}\n\n/**\n * The threshold this fragment is measured against.\n *\n * Tilted by HEIGHT so the dissolve sweeps up the body instead of arriving\n * everywhere at once: a body that comes apart from the feet reads as something\n * happening TO her, and one that fades uniformly reads as an opacity slider.\n * The tilt is gentle \u2014 a third of the range \u2014 so the noise still decides which\n * flake goes when, and the sweep only decides roughly where it is.\n *\n * FEET FIRST, head last: the face is the last thing to go and the first thing\n * back, which is the order every disappearance in film is cut in. Object space\n * puts the floor at y = 0 on any PMX, so the tilt needs no rig measurement.\n */\nfn rz_dissolve_threshold(restPos: vec3f) -> f32 {\n let n = rz_dissolve_field(restPos / RZ_DISSOLVE_GRAIN);\n let low = 1.0 - clamp(restPos.y * 0.045, 0.0, 1.0);\n return clamp(n * 0.68 + low * 0.32, 0.0, 1.0);\n}\n\nstruct LightVP { viewProj: mat4x4f, };\n@group(0) @binding(0) var<uniform> lp: LightVP;\n@group(0) @binding(1) var<storage, read> skinMats: array<mat4x4f>;\n@group(0) @binding(2) var texSampler: sampler;\n@group(1) @binding(0) var diffuseTexture: texture_2d<f32>;\n// The head of the material block and the one field at its tail \u2014 the same\n// reach the depth prepass takes, and for the same reason: a shadow cast by a\n// body that is no longer drawn is the tell that the vanishing is a trick.\nstruct MaterialDiffuse {\n diffuse: vec4f,\n _skip0: vec4f,\n _skip1: vec4f,\n _skip2: vec3f,\n dissolve: f32,\n};\n@group(1) @binding(1) var<uniform> material: MaterialDiffuse;\n\nstruct VSOut {\n @builtin(position) position: vec4f,\n @location(0) uv: vec2f,\n /** Bind-pose position, for the dissolve test \u2014 object space, as everywhere. */\n @location(1) restPos: vec3f,\n};\n\n@vertex fn vs(@location(0) position: vec3f, @location(1) normal: vec3f, @location(2) uv: vec2f,\n @location(3) joints0: vec4<u32>, @location(4) weights0: vec4<f32>) -> VSOut {\n let pos4 = vec4f(position, 1.0);\n let ws = weights0.x + weights0.y + weights0.z + weights0.w;\n let inv = select(1.0, 1.0 / ws, ws > 0.0001);\n let nw = select(vec4f(1.0,0.0,0.0,0.0), weights0 * inv, ws > 0.0001);\n var sp = vec4f(0.0);\n for (var i = 0u; i < 4u; i++) { sp += (skinMats[joints0[i]] * pos4) * nw[i]; }\n var out: VSOut;\n out.position = lp.viewProj * vec4f(sp.xyz, 1.0);\n out.uv = uv;\n out.restPos = position;\n return out;\n}\n\n@fragment fn fs(in: VSOut) {\n let alpha = textureSample(diffuseTexture, texSampler, in.uv).a * material.diffuse.a;\n if (alpha < 0.5) { discard; }\n // The dissolve, run as the colour pass runs it. A flake that is gone stops\n // casting: without this she leaves a whole shadow standing on the floor while\n // her body is in the air.\n if (material.dissolve < 0.9995 && rz_dissolve_threshold(in.restPos) > material.dissolve) { discard; }\n}\n";
1
+ export declare const SHADOW_DEPTH_SHADER_WGSL = "\n\n/** Clump size, in object-space units \u2014 MMD's are roughly centimetres. */\nconst RZ_DISSOLVE_GRAIN: f32 = 0.42;\n/** Grit cell, as a fraction of a clump. See rz_dissolve_threshold: this is what\n * keeps a material SMALLER than a clump from having one threshold for all of\n * it. Seven millimetres or so on a normal model. */\nconst RZ_DISSOLVE_GRIT: f32 = 0.16;\n/** How wide the glowing front is, in threshold units. Thin: the front is a\n * rim, and a wide one lights whole limbs at once. */\nconst RZ_DISSOLVE_EDGE: f32 = 0.11;\n/** What the front burns with. Emission, added after lighting and after the\n * graph \u2014 a colour that ramps or takes a shadow reads as paint, not as heat.\n * Above 1 on purpose: it is meant to reach the bloom pyramid. Blue-violet,\n * the same light the motes leaving her are drawn and lit with. */\nconst RZ_BURN_COLOR: vec3f = vec3f(0.45, 0.62, 2.40);\n\nfn rz_dissolve_hash(p: vec3f) -> f32 {\n let q = fract(p * 0.3183099 + vec3f(0.1, 0.2, 0.3));\n let r = q * 17.0 * (q + 34.0);\n return fract(r.x * r.y * r.z);\n}\n\n/** Value noise over that hash \u2014 smooth within a flake, uncorrelated between. */\nfn rz_dissolve_field(p: vec3f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let c000 = rz_dissolve_hash(i);\n let c100 = rz_dissolve_hash(i + vec3f(1.0, 0.0, 0.0));\n let c010 = rz_dissolve_hash(i + vec3f(0.0, 1.0, 0.0));\n let c110 = rz_dissolve_hash(i + vec3f(1.0, 1.0, 0.0));\n let c001 = rz_dissolve_hash(i + vec3f(0.0, 0.0, 1.0));\n let c101 = rz_dissolve_hash(i + vec3f(1.0, 0.0, 1.0));\n let c011 = rz_dissolve_hash(i + vec3f(0.0, 1.0, 1.0));\n let c111 = rz_dissolve_hash(i + vec3f(1.0, 1.0, 1.0));\n let x00 = mix(c000, c100, u.x);\n let x10 = mix(c010, c110, u.x);\n let x01 = mix(c001, c101, u.x);\n let x11 = mix(c011, c111, u.x);\n return mix(mix(x00, x10, u.y), mix(x01, x11, u.y), u.z);\n}\n\n/**\n * The threshold this fragment is measured against.\n *\n * NOISE ALONE, so the whole body comes apart at once \u2014 a hand, a shoulder and\n * an ankle all thinning together rather than a line travelling up her. It was\n * tilted by height at first, on the argument that a sweep reads as something\n * happening TO her; what it actually reads as is a wipe, and a wipe has a\n * direction, an edge and a speed the rest of the effect knows nothing about.\n * Everywhere at once is also the honest match for what leaves: the motes are\n * shed from every limb in the same breath.\n *\n * TWO SCALES, and the second one is not decoration \u2014 it is the fix for a real\n * failure. Clumps alone meant every fragment of a material SMALLER than one\n * cell shared a single threshold: an eye highlight, a brow, a button, a hair\n * clip. All of it sat inside the glowing front at once, so the piece flashed\n * white as a whole and then popped out of existence \u2014 the two things a\n * disintegration must never do. The grit is a plain per-cell hash at a fraction\n * of the clump, so a part a few millimetres across still comes apart in pieces.\n *\n * Weighted toward the clumps, because they are the LOOK: flakes a centimetre\n * across, in an order fixed to the surface, so the same piece of sleeve goes at\n * the same moment every time she leaves. The grit only breaks ties.\n */\nfn rz_dissolve_threshold(restPos: vec3f) -> f32 {\n let clump = rz_dissolve_field(restPos / RZ_DISSOLVE_GRAIN);\n let grit = rz_dissolve_hash(floor(restPos / (RZ_DISSOLVE_GRAIN * RZ_DISSOLVE_GRIT)));\n return clamp(clump * 0.7 + grit * 0.3, 0.0, 1.0);\n}\n\nstruct LightVP { viewProj: mat4x4f, };\n@group(0) @binding(0) var<uniform> lp: LightVP;\n@group(0) @binding(1) var<storage, read> skinMats: array<mat4x4f>;\n@group(0) @binding(2) var texSampler: sampler;\n@group(1) @binding(0) var diffuseTexture: texture_2d<f32>;\n// The head of the material block and the one field at its tail \u2014 the same\n// reach the depth prepass takes, and for the same reason: a shadow cast by a\n// body that is no longer drawn is the tell that the vanishing is a trick.\nstruct MaterialDiffuse {\n diffuse: vec4f,\n _skip0: vec4f,\n _skip1: vec4f,\n _skip2: vec3f,\n dissolve: f32,\n};\n@group(1) @binding(1) var<uniform> material: MaterialDiffuse;\n\nstruct VSOut {\n @builtin(position) position: vec4f,\n @location(0) uv: vec2f,\n /** Bind-pose position, for the dissolve test \u2014 object space, as everywhere. */\n @location(1) restPos: vec3f,\n};\n\n@vertex fn vs(@location(0) position: vec3f, @location(1) normal: vec3f, @location(2) uv: vec2f,\n @location(3) joints0: vec4<u32>, @location(4) weights0: vec4<f32>) -> VSOut {\n let pos4 = vec4f(position, 1.0);\n let ws = weights0.x + weights0.y + weights0.z + weights0.w;\n let inv = select(1.0, 1.0 / ws, ws > 0.0001);\n let nw = select(vec4f(1.0,0.0,0.0,0.0), weights0 * inv, ws > 0.0001);\n var sp = vec4f(0.0);\n for (var i = 0u; i < 4u; i++) { sp += (skinMats[joints0[i]] * pos4) * nw[i]; }\n var out: VSOut;\n out.position = lp.viewProj * vec4f(sp.xyz, 1.0);\n out.uv = uv;\n out.restPos = position;\n return out;\n}\n\n@fragment fn fs(in: VSOut) {\n let alpha = textureSample(diffuseTexture, texSampler, in.uv).a * material.diffuse.a;\n if (alpha < 0.5) { discard; }\n // The dissolve, run as the colour pass runs it. A flake that is gone stops\n // casting: without this she leaves a whole shadow standing on the floor while\n // her body is in the air.\n if (material.dissolve < 0.9995 && rz_dissolve_threshold(in.restPos) > material.dissolve) { discard; }\n}\n";
2
2
  //# sourceMappingURL=shadow.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"shadow.d.ts","sourceRoot":"","sources":["../../../src/shaders/passes/shadow.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,wBAAwB,y1IAiDpC,CAAA"}
1
+ {"version":3,"file":"shadow.d.ts","sourceRoot":"","sources":["../../../src/shaders/passes/shadow.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,wBAAwB,u8KAiDpC,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reze-engine",
3
- "version": "0.50.8",
3
+ "version": "0.50.10",
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",
@@ -297,14 +297,20 @@ export function commonFsOutWgsl(): string {
297
297
  * distance is just a fade.
298
298
  */
299
299
  export const DISSOLVE_WGSL = /* wgsl */ `
300
- /** Flake size, in object-space units — MMD's are roughly centimetres. */
301
- const RZ_DISSOLVE_GRAIN: f32 = 0.55;
302
- /** How wide the glowing front is, in threshold units. */
303
- const RZ_DISSOLVE_EDGE: f32 = 0.16;
300
+ /** Clump size, in object-space units — MMD's are roughly centimetres. */
301
+ const RZ_DISSOLVE_GRAIN: f32 = 0.42;
302
+ /** Grit cell, as a fraction of a clump. See rz_dissolve_threshold: this is what
303
+ * keeps a material SMALLER than a clump from having one threshold for all of
304
+ * it. Seven millimetres or so on a normal model. */
305
+ const RZ_DISSOLVE_GRIT: f32 = 0.16;
306
+ /** How wide the glowing front is, in threshold units. Thin: the front is a
307
+ * rim, and a wide one lights whole limbs at once. */
308
+ const RZ_DISSOLVE_EDGE: f32 = 0.11;
304
309
  /** What the front burns with. Emission, added after lighting and after the
305
310
  * graph — a colour that ramps or takes a shadow reads as paint, not as heat.
306
- * Above 1 on purpose: it is meant to reach the bloom pyramid. */
307
- const RZ_BURN_COLOR: vec3f = vec3f(0.55, 1.85, 2.60);
311
+ * Above 1 on purpose: it is meant to reach the bloom pyramid. Blue-violet,
312
+ * the same light the motes leaving her are drawn and lit with. */
313
+ const RZ_BURN_COLOR: vec3f = vec3f(0.45, 0.62, 2.40);
308
314
 
309
315
  fn rz_dissolve_hash(p: vec3f) -> f32 {
310
316
  let q = fract(p * 0.3183099 + vec3f(0.1, 0.2, 0.3));
@@ -335,20 +341,30 @@ fn rz_dissolve_field(p: vec3f) -> f32 {
335
341
  /**
336
342
  * The threshold this fragment is measured against.
337
343
  *
338
- * Tilted by HEIGHT so the dissolve sweeps up the body instead of arriving
339
- * everywhere at once: a body that comes apart from the feet reads as something
340
- * happening TO her, and one that fades uniformly reads as an opacity slider.
341
- * The tilt is gentle a third of the range so the noise still decides which
342
- * flake goes when, and the sweep only decides roughly where it is.
344
+ * NOISE ALONE, so the whole body comes apart at once a hand, a shoulder and
345
+ * an ankle all thinning together rather than a line travelling up her. It was
346
+ * tilted by height at first, on the argument that a sweep reads as something
347
+ * happening TO her; what it actually reads as is a wipe, and a wipe has a
348
+ * direction, an edge and a speed the rest of the effect knows nothing about.
349
+ * Everywhere at once is also the honest match for what leaves: the motes are
350
+ * shed from every limb in the same breath.
343
351
  *
344
- * FEET FIRST, head last: the face is the last thing to go and the first thing
345
- * back, which is the order every disappearance in film is cut in. Object space
346
- * puts the floor at y = 0 on any PMX, so the tilt needs no rig measurement.
352
+ * TWO SCALES, and the second one is not decoration it is the fix for a real
353
+ * failure. Clumps alone meant every fragment of a material SMALLER than one
354
+ * cell shared a single threshold: an eye highlight, a brow, a button, a hair
355
+ * clip. All of it sat inside the glowing front at once, so the piece flashed
356
+ * white as a whole and then popped out of existence — the two things a
357
+ * disintegration must never do. The grit is a plain per-cell hash at a fraction
358
+ * of the clump, so a part a few millimetres across still comes apart in pieces.
359
+ *
360
+ * Weighted toward the clumps, because they are the LOOK: flakes a centimetre
361
+ * across, in an order fixed to the surface, so the same piece of sleeve goes at
362
+ * the same moment every time she leaves. The grit only breaks ties.
347
363
  */
348
364
  fn rz_dissolve_threshold(restPos: vec3f) -> f32 {
349
- let n = rz_dissolve_field(restPos / RZ_DISSOLVE_GRAIN);
350
- let low = 1.0 - clamp(restPos.y * 0.045, 0.0, 1.0);
351
- return clamp(n * 0.68 + low * 0.32, 0.0, 1.0);
365
+ let clump = rz_dissolve_field(restPos / RZ_DISSOLVE_GRAIN);
366
+ let grit = rz_dissolve_hash(floor(restPos / (RZ_DISSOLVE_GRAIN * RZ_DISSOLVE_GRIT)));
367
+ return clamp(clump * 0.7 + grit * 0.3, 0.0, 1.0);
352
368
  }
353
369
  `
354
370