@driftengine/core 4.1.4 → 4.1.5
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 +3796 -3770
- package/dist/render/backend/webgl2/renderer.d.ts +15 -0
- package/dist/render/backend/webgl2/renderer.js +28 -7
- package/dist/render/backend/webgpu/pointShadowPass.d.ts +4 -0
- package/dist/render/backend/webgpu/pointShadowPass.js +10 -0
- package/dist/render/backend/webgpu/renderer.js +7 -2
- package/dist/render/depthConvention.d.ts +19 -0
- package/dist/render/depthConvention.js +21 -0
- package/dist/render/livePointShadowSet.js +8 -0
- package/dist/render/pointShadowBudget.js +59 -5
- package/dist/render/pointShadowImage.d.ts +40 -0
- package/dist/render/pointShadowImage.js +20 -0
- package/dist/render/pointShadowMap.d.ts +4 -0
- package/dist/render/pointShadowMap.js +10 -0
- package/dist/render/pointShadowSystem.js +9 -0
- package/dist/render/renderQuality.d.ts +27 -4
- package/dist/render/renderQuality.js +1 -1
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +13 -5
- package/dist/render/shaders/flat/pointShadow.d.ts +1 -1
- package/dist/render/shaders/flat/pointShadow.js +42 -30
- package/dist/render/shaders/flat/preamble.js +26 -2
- package/dist/render/shaders/generated/flat.wgsl.d.ts +16 -16
- package/dist/render/shaders/generated/flat.wgsl.js +24 -24
- package/dist/render/shaders/sky.d.ts +10 -6
- package/dist/render/shaders/sky.js +35 -2
- package/package.json +6 -6
- package/src/render/backend/webgl2/renderer.ts +28 -7
- package/src/render/backend/webgpu/pointShadowPass.ts +13 -0
- package/src/render/backend/webgpu/renderer.ts +12 -2
- package/src/render/depthConvention.ts +22 -0
- package/src/render/livePointShadowSet.ts +7 -0
- package/src/render/pointShadowBudget.ts +58 -5
- package/src/render/pointShadowImage.ts +51 -0
- package/src/render/pointShadowMap.ts +13 -0
- package/src/render/pointShadowSystem.ts +8 -0
- package/src/render/renderQuality.ts +28 -5
- package/src/render/shaders/flat/main.ts +13 -5
- package/src/render/shaders/flat/pointShadow.ts +42 -30
- package/src/render/shaders/flat/preamble.ts +26 -2
- package/src/render/shaders/generated/flat.wgsl.ts +24 -24
- package/src/render/shaders/sky.ts +37 -2
|
@@ -15,7 +15,40 @@
|
|
|
15
15
|
import { MAX_SHADOW_FILTER_TAPS } from '../../renderQuality.ts';
|
|
16
16
|
import { OUTPUT_TRANSFORM_GLSL } from '../outputTransform.ts';
|
|
17
17
|
|
|
18
|
-
export const POINTSHADOW_GLSL =
|
|
18
|
+
export const POINTSHADOW_GLSL = `/**
|
|
19
|
+
* How much of one tap's occlusion survives its own penumbra.
|
|
20
|
+
*
|
|
21
|
+
* **How much of a shadow survives, as well as how soft it is.** A penumbra does not merely blur
|
|
22
|
+
* with distance from its caster, it *takes over*: far enough from the occluder, relative to how
|
|
23
|
+
* big the source is, every point on the receiver can see part of the flame and there is no umbra
|
|
24
|
+
* left at all. The shadow is gone, not blurry. Without it, two columns lit by a brazier threw
|
|
25
|
+
* full-strength edges fifteen metres across the terrace, and the gap between them read as a
|
|
26
|
+
* spotlight cone with straight sides rather than as a fire.
|
|
27
|
+
*
|
|
28
|
+
* **Per tap, and it was once for the whole filter, taken from the centre tap alone.** That is a
|
|
29
|
+
* blocker search of one sample standing in for twelve, and it is right wherever the taps agree —
|
|
30
|
+
* inside an umbra they all find the same occluder, so this returns exactly what the shared value
|
|
31
|
+
* did, and the umbra is unchanged to the bit. It is wrong exactly where they disagree, which is
|
|
32
|
+
* the silhouette: there the centre ray *misses*, so the search reports no occluder, the spread is
|
|
33
|
+
* zero and the fade is switched off — while the taps that do land on the caster then draw at full
|
|
34
|
+
* strength. What that paints is a hard one-texel line around a shadow whose interior has
|
|
35
|
+
* correctly faded to nothing.
|
|
36
|
+
*
|
|
37
|
+
* Reported as shadows "faded out but NOT shadow outlines, generating long lines around the
|
|
38
|
+
* world", and on a brazier as a bare square outline lying on the ground with no shadow inside it.
|
|
39
|
+
*
|
|
40
|
+
* **What it costs** is two multiplies and a divide per occluded tap, against a tap that has
|
|
41
|
+
* already paid for a texture fetch. **What would make it wrong** is a filter wide enough that one
|
|
42
|
+
* tap's occluder is not the occluder a neighbouring tap is estimating for, which is the same
|
|
43
|
+
* limit the shared value had and is what \`MAX_FILTER_RADIUS\` bounds.
|
|
44
|
+
*/
|
|
45
|
+
float tapStrength(float receiverDistance, float occluderDistance, float sourceRadius) {
|
|
46
|
+
float spread = max(receiverDistance - occluderDistance, 0.0) / max(occluderDistance, 0.05);
|
|
47
|
+
float penumbra = clamp(sourceRadius * spread * PENUMBRA_FADE, 0.0, 1.0);
|
|
48
|
+
return 1.0 - penumbra * penumbra;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
float pointShadow(
|
|
19
52
|
highp sampler2DArray maps,
|
|
20
53
|
float layer,
|
|
21
54
|
vec3 toFrag,
|
|
@@ -97,24 +130,6 @@ export const POINTSHADOW_GLSL = `float pointShadow(
|
|
|
97
130
|
*/
|
|
98
131
|
float radius = clamp(sourceRadius * spread, 0.01, MAX_FILTER_RADIUS) + 0.012;
|
|
99
132
|
|
|
100
|
-
/*
|
|
101
|
-
* How much of a shadow survives, as well as how soft it is.
|
|
102
|
-
*
|
|
103
|
-
* This is the half that was missing, and it is why widening the filter never
|
|
104
|
-
* fixed anything. A penumbra does not merely blur with distance from its
|
|
105
|
-
* caster — it *takes over*. Far enough from the occluder, relative to how big
|
|
106
|
-
* the source is, every point on the receiver can see part of the flame and
|
|
107
|
-
* there is no umbra left at all: the shadow is gone, not blurry.
|
|
108
|
-
*
|
|
109
|
-
* Without it, two columns lit by a brazier threw full-strength edges fifteen
|
|
110
|
-
* metres across the terrace, and the gap between them read as a spotlight cone
|
|
111
|
-
* with straight sides rather than as a fire. Three attempts in, the note that
|
|
112
|
-
* unlocked it was that the pool should still fade naturally at its rim — a
|
|
113
|
-
* description of the falloff, not of the blur.
|
|
114
|
-
*/
|
|
115
|
-
float penumbra = clamp(sourceRadius * spread * PENUMBRA_FADE, 0.0, 1.0);
|
|
116
|
-
float strength = 1.0 - penumbra * penumbra;
|
|
117
|
-
|
|
118
133
|
/*
|
|
119
134
|
* Taps are placed on a disk *perpendicular to the light ray*, turned by half a golden
|
|
120
135
|
* angle on every other column of pixels.
|
|
@@ -191,14 +206,13 @@ export const POINTSHADOW_GLSL = `float pointShadow(
|
|
|
191
206
|
if (dist - bias > storedDistance) {
|
|
192
207
|
// A point shadow cannot outlive the finite light that casts it, so its
|
|
193
208
|
// occlusion fades smoothly before the light's far plane.
|
|
194
|
-
|
|
209
|
+
float reach = shadowReach(max(dist - storedDistance, 0.0), far);
|
|
210
|
+
lit += 1.0 - reach * tapStrength(dist, storedDistance, sourceRadius);
|
|
195
211
|
} else {
|
|
196
212
|
lit += 1.0;
|
|
197
213
|
}
|
|
198
214
|
}
|
|
199
|
-
|
|
200
|
-
// Fade the whole shadow toward lit as the penumbra swallows it.
|
|
201
|
-
return mix(1.0, shaded, strength);
|
|
215
|
+
return lit / float(max(uShadowFilterTaps, 1));
|
|
202
216
|
}
|
|
203
217
|
|
|
204
218
|
/**
|
|
@@ -289,10 +303,6 @@ float areaShadow(
|
|
|
289
303
|
float widthX = min(halfX * spread, MAX_FILTER_RADIUS) + 0.012;
|
|
290
304
|
float widthY = min(halfY * spread, MAX_FILTER_RADIUS) + 0.012;
|
|
291
305
|
|
|
292
|
-
/* The narrower half extent, for the reason in the header: it is what keeps a strip's umbra. */
|
|
293
|
-
float penumbra = clamp(min(halfX, halfY) * spread * PENUMBRA_FADE, 0.0, 1.0);
|
|
294
|
-
float strength = 1.0 - penumbra * penumbra;
|
|
295
|
-
|
|
296
306
|
/*
|
|
297
307
|
* Turned inside the unit disk *before* the ellipse maps it, so the half-turn that separates
|
|
298
308
|
* the two halves of a pixel pair survives the anisotropy. The same two-pixel tile the round
|
|
@@ -318,13 +328,15 @@ float areaShadow(
|
|
|
318
328
|
float storedDistance = stored * far;
|
|
319
329
|
if (dist - bias > storedDistance) {
|
|
320
330
|
/* An area light's occlusion cannot outlive the map that carries it either. */
|
|
321
|
-
|
|
331
|
+
float reach = shadowReach(max(dist - storedDistance, 0.0), far);
|
|
332
|
+
/* The narrower half extent, for the reason in the header: it is what keeps a strip's
|
|
333
|
+
umbra. Per tap, for the reason \`tapStrength\` gives. */
|
|
334
|
+
lit += 1.0 - reach * tapStrength(dist, storedDistance, min(halfX, halfY));
|
|
322
335
|
} else {
|
|
323
336
|
lit += 1.0;
|
|
324
337
|
}
|
|
325
338
|
}
|
|
326
|
-
|
|
327
|
-
return mix(1.0, shaded, strength);
|
|
339
|
+
return lit / float(max(uShadowFilterTaps, 1));
|
|
328
340
|
}
|
|
329
341
|
#endif
|
|
330
342
|
|
|
@@ -803,7 +803,30 @@ uniform float uAreaLightTwoSided[MAX_AREA_LIGHTS];
|
|
|
803
803
|
#if POINT_SHADOWS
|
|
804
804
|
/** Which array layer holds this light's shadow, or -1 for none. */
|
|
805
805
|
uniform int uPointShadowLayer[MAX_LIGHTS];
|
|
806
|
-
|
|
806
|
+
/*
|
|
807
|
+
* **The projection each map was rendered under: \`xyz\` where from, \`w\` how far.**
|
|
808
|
+
*
|
|
809
|
+
* The origin is the half that is new and it is **not** the light's position. A light may drift
|
|
810
|
+
* \`pointShadowRebakeDistance\` from its image before the image is re-rendered, because
|
|
811
|
+
* re-baking six faces of the static world on every frame of a flicker is what made a brazier
|
|
812
|
+
* the most expensive object in a scene. The ray below is built from this point rather than from
|
|
813
|
+
* \`uLightPos\`, so it is the ray the picture was drawn along and the drift costs nothing.
|
|
814
|
+
*
|
|
815
|
+
* **It rides with the far plane because a row is a row.** A default-block \`float[N]\` spends a
|
|
816
|
+
* whole uniform vector per element and uses one of its four components, so the far plane was
|
|
817
|
+
* already paying for three floats it threw away — see \`uniformVectorBudget.ts\`. Declared as a
|
|
818
|
+
* separate \`vec3\` array this would have cost sixteen more rows on each of the two sets, and
|
|
819
|
+
* the ladder answers a 256-vector phone by halving \`MAX_LIGHTS\`: the measured price of a
|
|
820
|
+
* separate array was **eight lights becoming four**. Folded in, the whole fix is free.
|
|
821
|
+
*
|
|
822
|
+
* **What it gives up** is that the shadow stops tracking the flame *within* the tolerance — a
|
|
823
|
+
* flicker no longer nudges the shadow it casts, because the image it is read from did not move
|
|
824
|
+
* either. That is the trade the tolerance was always making, now made honestly instead of by
|
|
825
|
+
* reading a picture from the wrong place. **What would make it wrong** is a tolerance large
|
|
826
|
+
* enough that a shadow visibly lags its light, which is a reason to lower
|
|
827
|
+
* \`pointShadowRebakeDistance\` rather than to sample from a point the image cannot answer for.
|
|
828
|
+
*/
|
|
829
|
+
uniform vec4 uPointShadowProjection[MAX_LIGHTS];
|
|
807
830
|
uniform float uPointShadowNear[MAX_LIGHTS];
|
|
808
831
|
/** Emitter radius per light — drives how soft its shadows are. */
|
|
809
832
|
uniform float uPointShadowSize[MAX_LIGHTS];
|
|
@@ -834,7 +857,8 @@ uniform highp sampler2DArray uPointShadows; // wgsl:share shadow
|
|
|
834
857
|
* light owns at most one of the two at a time.
|
|
835
858
|
*/
|
|
836
859
|
uniform int uLivePointShadowLayer[MAX_LIGHTS];
|
|
837
|
-
|
|
860
|
+
/** The live pair's own projection, by the same rule. See \`uPointShadowProjection\`. */
|
|
861
|
+
uniform vec4 uLivePointShadowProjection[MAX_LIGHTS];
|
|
838
862
|
uniform float uLivePointShadowNear[MAX_LIGHTS];
|
|
839
863
|
uniform float uLivePointShadowSize[MAX_LIGHTS];
|
|
840
864
|
uniform float uLivePointShadowWeight[MAX_LIGHTS];
|