reze-engine 0.55.1 → 0.55.3

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.
@@ -35,37 +35,54 @@ const SOFT_MAX_SPREAD = 14
35
35
  * `acc` must already be declared and zeroed; the caller reads it back
36
36
  * normalised, so there is no `/ 9.0` left at the call site.
37
37
  */
38
- function pcfWgsl(map: string, uv: string, texel: string, z: string, acc: string, pad: string): string {
38
+ function pcfWgsl(map: string, uv: string, texel: string, z: string, acc: string, pad: string, soft: boolean): string {
39
39
  const golden = 2.39996323
40
- return [
41
- `if (material.shadowSoftness <= 0.0) {`,
42
- ` let st = ${texel} * 2.0;`,
43
- ` for (var y = -1; y <= 1; y++) {`,
44
- ` for (var x = -1; x <= 1; x++) {`,
40
+ // ONE BRANCH IS COMPILED, NOT BOTH.
41
+ //
42
+ // A runtime `if` on the softness uniform was the first cut, and it costs a
43
+ // scene that never softens a shadow. Both bodies land in the module, the wide
44
+ // one needs more live registers than the narrow one, and occupancy on this
45
+ // draw is set by the worst of them — so the frame gets slower whether or not
46
+ // the branch is ever taken. That is expensive HERE in particular: this file
47
+ // already records that the ground is a full-coverage draw costing tens of
48
+ // millions of shadow fetches, and that bisection on a slow device pinned the
49
+ // whole cost to it.
50
+ //
51
+ // So the flag is the COMPILED VARIANT, which is the idiom the composite pass
52
+ // already uses for its effects. A scene at softness 0 gets byte-for-byte the
53
+ // shader it had before any of this existed, and cannot pay for a feature it
54
+ // is not using.
55
+ const sharp = [
56
+ `let st = ${texel} * 2.0;`,
57
+ `for (var y = -1; y <= 1; y++) {`,
58
+ ` for (var x = -1; x <= 1; x++) {`,
45
59
  // ...Level, not the implicit-derivative form: identical on a single-mip
46
60
  // shadow map, and legal inside a branch.
47
- ` ${acc} += textureSampleCompareLevel(${map}, shadowSampler, ${uv} + vec2f(f32(x), f32(y)) * st, ${z});`,
48
- ` }`,
61
+ ` ${acc} += textureSampleCompareLevel(${map}, shadowSampler, ${uv} + vec2f(f32(x), f32(y)) * st, ${z});`,
49
62
  ` }`,
50
- ` ${acc} *= ${1 / 9};`,
51
- `} else {`,
52
- ` let radius = ${texel} * 2.0 * (1.0 + material.shadowSoftness * ${SOFT_MAX_SPREAD}.0);`,
53
- ` for (var s = 0; s < ${SOFT_TAPS}; s++) {`,
54
- ` let fs = f32(s);`,
63
+ `}`,
64
+ `${acc} *= ${1 / 9};`,
65
+ ]
66
+ const wide = [
67
+ `let radius = ${texel} * 2.0 * (1.0 + material.shadowSoftness * ${SOFT_MAX_SPREAD}.0);`,
68
+ `for (var s = 0; s < ${SOFT_TAPS}; s++) {`,
69
+ ` let fs = f32(s);`,
55
70
  // sqrt of the index spaces the ring radii evenly by AREA; the golden angle
56
71
  // keeps successive taps from lining up into spokes.
57
- ` let r = sqrt((fs + 0.5) * ${1 / SOFT_TAPS});`,
58
- ` let a = fs * ${golden} + rot;`,
59
- ` ${acc} += textureSampleCompareLevel(${map}, shadowSampler, ${uv} + vec2f(cos(a), sin(a)) * (r * radius), ${z});`,
60
- ` }`,
61
- ` ${acc} *= ${1 / SOFT_TAPS};`,
72
+ ` let r = sqrt((fs + 0.5) * ${1 / SOFT_TAPS});`,
73
+ ` let a = fs * ${golden} + rot;`,
74
+ ` ${acc} += textureSampleCompareLevel(${map}, shadowSampler, ${uv} + vec2f(cos(a), sin(a)) * (r * radius), ${z});`,
62
75
  `}`,
76
+ `${acc} *= ${1 / SOFT_TAPS};`,
63
77
  ]
64
- .map((l) => pad + l)
65
- .join("\n")
78
+ return (soft ? wide : sharp).map((l) => pad + l).join("\n")
66
79
  }
67
80
 
68
- export function groundShaderWgsl(): string {
81
+ /**
82
+ * The ground's shader. `soft` selects the shadow-edge variant — see pcfWgsl for
83
+ * why this is a compiled flag and not a uniform the shader branches on.
84
+ */
85
+ export function groundShaderWgsl(soft = false): string {
69
86
  return /* wgsl */ `
70
87
  struct CameraUniforms { view: mat4x4f, projection: mat4x4f, viewPos: vec3f, _p: f32, };
71
88
  struct Light { direction: vec4f, color: vec4f, };
@@ -207,11 +224,15 @@ ${sceneFsOutWgsl()}@fragment fn fs(i: VO) -> FSOut {
207
224
  // The same reasoning the noise tint below already got, applied to the term
208
225
  // that costs a hundred times more.
209
226
  if (material.shadowStrength > 0.0 && shadowPossible) {
210
- // Per-pixel rotation for the soft disk, so its rings break up into fine noise
211
- // rather than banding. Interleaved gradient noise: a function of the pixel
212
- // alone, so a still camera gives a still shadow — the sharp path ignores it.
227
+ ${
228
+ soft
229
+ ? ` // Per-pixel rotation for the soft disk, so its rings break up into fine
230
+ // noise rather than banding. Interleaved gradient noise: a function of the
231
+ // pixel alone, so a still camera gives a still shadow.
213
232
  let ign = fract(52.9829189 * fract(dot(i.position.xy, vec2f(0.06711056, 0.00583715))));
214
- let rot = ign * 6.28318530718;
233
+ let rot = ign * 6.28318530718;`
234
+ : ""
235
+ }
215
236
  // The far cascade's taps, skipped entirely when nothing ever drew into it.
216
237
  //
217
238
  // This branch is the expensive one on a wide floor: it runs wherever the NEAR
@@ -227,7 +248,7 @@ ${sceneFsOutWgsl()}@fragment fn fs(i: VO) -> FSOut {
227
248
  let suv1_c = clamp(suv1, vec2f(0.02), vec2f(0.98));
228
249
  let compareZ1 = ndc1.z - 0.0035;
229
250
  var acc1 = 0.0;
230
- ${pcfWgsl("shadowMapFar", "suv1_c", `${1 / SHADOW_CASCADES[SHADOW_CASCADES.length - 1].mapSize}`, "compareZ1", "acc1", " ")}
251
+ ${pcfWgsl("shadowMapFar", "suv1_c", `${1 / SHADOW_CASCADES[SHADOW_CASCADES.length - 1].mapSize}`, "compareZ1", "acc1", " ", soft)}
231
252
  vis = mix(1.0, acc1, frustum1);
232
253
  }
233
254
  if (frustum > 0.0) {
@@ -235,7 +256,7 @@ ${pcfWgsl("shadowMapFar", "suv1_c", `${1 / SHADOW_CASCADES[SHADOW_CASCADES.lengt
235
256
  let suv_c = clamp(suv, vec2f(0.02), vec2f(0.98));
236
257
  let compareZ = ndc.z - 0.0035;
237
258
  var acc = 0.0;
238
- ${pcfWgsl("shadowMap", "suv_c", "material.pcfTexel", "compareZ", "acc", " ")}
259
+ ${pcfWgsl("shadowMap", "suv_c", "material.pcfTexel", "compareZ", "acc", " ", soft)}
239
260
  // The base is whatever the far cascade decided, so the near border blends
240
261
  // cascade to cascade rather than snapping to lit mid-floor.
241
262
  vis = mix(vis, acc, frustum);