@driftengine/core 4.5.0 → 4.6.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/CHANGELOG.json +51 -0
- package/README.md +1 -1
- package/dist/geometry/boxCsg.d.ts +28 -0
- package/dist/geometry/boxCsg.js +139 -0
- package/dist/geometry/csg.d.ts +36 -0
- package/dist/geometry/csg.js +329 -0
- package/dist/geometry/meshBuilder.js +13 -0
- package/dist/geometry/meshConcat.js +1 -0
- package/dist/geometry/pixelFont.js +2 -0
- package/dist/geometry/solid.d.ts +61 -0
- package/dist/geometry/solid.js +152 -0
- package/dist/geometry/solidFlat.d.ts +35 -0
- package/dist/geometry/solidFlat.js +428 -0
- package/dist/geometry/solidHarness.d.ts +19 -0
- package/dist/geometry/solidHarness.js +66 -0
- package/dist/geometry/solidNormals.d.ts +17 -0
- package/dist/geometry/solidNormals.js +86 -0
- package/dist/geometry/solidRound.d.ts +41 -0
- package/dist/geometry/solidRound.js +323 -0
- package/dist/geometry/solidWriter.d.ts +16 -0
- package/dist/geometry/solidWriter.js +42 -0
- package/dist/index.d.ts +23 -2
- package/dist/index.js +13 -1
- package/dist/render/backend/api.d.ts +17 -3
- package/dist/render/backend/drawFog.d.ts +26 -0
- package/dist/render/backend/drawFog.js +30 -0
- package/dist/render/backend/materialChanges.d.ts +2 -1
- package/dist/render/backend/materialChanges.js +6 -1
- package/dist/render/backend/webgl2/instanced.d.ts +24 -5
- package/dist/render/backend/webgl2/instanced.js +72 -15
- package/dist/render/backend/webgl2/renderer.d.ts +122 -8
- package/dist/render/backend/webgl2/renderer.js +238 -121
- package/dist/render/backend/webgpu/buffers.d.ts +10 -0
- package/dist/render/backend/webgpu/buffers.js +34 -3
- package/dist/render/backend/webgpu/depthPass.js +5 -1
- package/dist/render/backend/webgpu/drawCommand.d.ts +5 -0
- package/dist/render/backend/webgpu/drawCommand.js +1 -0
- package/dist/render/backend/webgpu/flatPass.d.ts +4 -1
- package/dist/render/backend/webgpu/flatPass.js +38 -9
- package/dist/render/backend/webgpu/glassTintPass.js +2 -1
- package/dist/render/backend/webgpu/instanceCullPass.d.ts +34 -0
- package/dist/render/backend/webgpu/instanceCullPass.js +144 -0
- package/dist/render/backend/webgpu/instanced.d.ts +11 -1
- package/dist/render/backend/webgpu/instanced.js +21 -3
- package/dist/render/backend/webgpu/latePass.d.ts +6 -2
- package/dist/render/backend/webgpu/latePass.js +11 -4
- package/dist/render/backend/webgpu/postPass.d.ts +1 -1
- package/dist/render/backend/webgpu/renderer.d.ts +100 -10
- package/dist/render/backend/webgpu/renderer.js +462 -201
- package/dist/render/backend/webgpu/sdfTextPass.js +6 -1
- package/dist/render/backend/webgpu/shaders/instanceCull.wgsl.d.ts +18 -0
- package/dist/render/backend/webgpu/shaders/instanceCull.wgsl.js +59 -0
- package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +17 -3
- package/dist/render/backend/webgpu/surfaceTexturePass.js +77 -38
- package/dist/render/driftLight/bake.d.ts +7 -0
- package/dist/render/driftLight/bake.js +61 -56
- package/dist/render/driftLight/denseField.d.ts +46 -0
- package/dist/render/driftLight/denseField.js +109 -0
- package/dist/render/driftLight/lightField.d.ts +10 -5
- package/dist/render/driftLight/lightField.js +25 -19
- package/dist/render/driftLight/presence.d.ts +50 -0
- package/dist/render/driftLight/presence.js +43 -0
- package/dist/render/driftLight/uniforms.d.ts +3 -3
- package/dist/render/driftLight/uniforms.js +5 -6
- package/dist/render/driftLight/worldLightField.d.ts +41 -0
- package/dist/render/driftLight/worldLightField.js +66 -0
- package/dist/render/fixtureAtlas.d.ts +20 -0
- package/dist/render/fixtureAtlas.js +49 -0
- package/dist/render/instanceCull.d.ts +63 -0
- package/dist/render/instanceCull.js +160 -0
- package/dist/render/instances.d.ts +15 -0
- package/dist/render/lightBudget.d.ts +22 -33
- package/dist/render/lightBudget.js +24 -35
- package/dist/render/lightGrid.d.ts +42 -0
- package/dist/render/lightGrid.js +87 -0
- package/dist/render/mesh.d.ts +18 -24
- package/dist/render/mesh.js +51 -68
- package/dist/render/occlusion.d.ts +10 -0
- package/dist/render/occlusion.js +36 -7
- package/dist/render/pointLightSelection.d.ts +7 -0
- package/dist/render/pointLightSelection.js +29 -4
- package/dist/render/rainField.d.ts +13 -0
- package/dist/render/rainField.js +27 -5
- package/dist/render/recon/lateDepth.d.ts +11 -0
- package/dist/render/recon/lateDepth.js +70 -0
- package/dist/render/recon/resolve.d.ts +34 -4
- package/dist/render/recon/resolve.js +161 -20
- package/dist/render/rendererHarness.js +4 -0
- package/dist/render/sceneTarget.d.ts +2 -0
- package/dist/render/sceneTarget.js +2 -0
- package/dist/render/shaders/depth.js +12 -9
- package/dist/render/shaders/flat/driftLight.d.ts +1 -1
- package/dist/render/shaders/flat/driftLight.js +32 -7
- package/dist/render/shaders/flat/index.js +5 -3
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +75 -23
- package/dist/render/shaders/flat/preamble.js +75 -20
- package/dist/render/shaders/flat/surfaceEffects.d.ts +29 -0
- package/dist/render/shaders/flat/surfaceEffects.js +152 -0
- package/dist/render/shaders/generated/depth.wgsl.d.ts +8 -8
- package/dist/render/shaders/generated/depth.wgsl.js +8 -8
- package/dist/render/shaders/generated/flat.wgsl.d.ts +1320 -1320
- package/dist/render/shaders/generated/flat.wgsl.js +1359 -1345
- package/dist/render/shaders/generated/lightVolume.wgsl.js +18 -4
- package/dist/render/shaders/generated/rush.wgsl.d.ts +21 -16
- package/dist/render/shaders/generated/rush.wgsl.js +21 -16
- package/dist/render/shaders/generated/sdfText.wgsl.d.ts +2 -2
- package/dist/render/shaders/generated/sdfText.wgsl.js +2 -2
- package/dist/render/shaders/generated/text.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/text.wgsl.js +1 -1
- package/dist/render/shaders/gi/driftLight.wgsl.d.ts +1 -1
- package/dist/render/shaders/gi/driftLight.wgsl.js +27 -4
- package/dist/render/shaders/recon/lateDepth.wgsl.js +38 -13
- package/dist/render/shaders/recon/resolve.wgsl.d.ts +8 -7
- package/dist/render/shaders/recon/resolve.wgsl.js +161 -27
- package/dist/render/shaders/rush.js +11 -1
- package/dist/render/shaders/sdfText.d.ts +1 -1
- package/dist/render/shaders/sdfText.js +3 -2
- package/dist/render/shaders/text.d.ts +1 -1
- package/dist/render/shaders/text.js +18 -9
- package/dist/render/surfaceEffects.d.ts +83 -0
- package/dist/render/surfaceEffects.js +75 -0
- package/dist/render/surfaceScene.d.ts +8 -0
- package/dist/render/surfaceScene.js +8 -0
- package/dist/render/surfaceTexture.d.ts +18 -2
- package/dist/render/surfaceTexture.js +68 -15
- package/dist/render/temporalAa.d.ts +25 -6
- package/dist/render/temporalAa.js +45 -15
- package/dist/render/textureSource.d.ts +30 -0
- package/dist/render/textureSource.js +53 -0
- package/dist/render/vertexDefaults.js +2 -0
- package/dist/world/hlod.d.ts +104 -0
- package/dist/world/hlod.js +214 -0
- package/package.json +6 -6
- package/src/geometry/boxCsg.ts +186 -0
- package/src/geometry/csg.ts +384 -0
- package/src/geometry/meshBuilder.ts +15 -0
- package/src/geometry/meshConcat.ts +1 -0
- package/src/geometry/pixelFont.ts +2 -0
- package/src/geometry/solid.ts +189 -0
- package/src/geometry/solidFlat.ts +522 -0
- package/src/geometry/solidHarness.ts +83 -0
- package/src/geometry/solidNormals.ts +103 -0
- package/src/geometry/solidRound.ts +443 -0
- package/src/geometry/solidWriter.ts +58 -0
- package/src/index.ts +51 -1
- package/src/render/backend/api.ts +21 -2
- package/src/render/backend/drawFog.ts +31 -0
- package/src/render/backend/materialChanges.ts +9 -2
- package/src/render/backend/webgl2/instanced.ts +80 -14
- package/src/render/backend/webgl2/renderer.ts +300 -143
- package/src/render/backend/webgpu/buffers.ts +42 -3
- package/src/render/backend/webgpu/depthPass.ts +5 -1
- package/src/render/backend/webgpu/drawCommand.ts +6 -0
- package/src/render/backend/webgpu/flatPass.ts +41 -11
- package/src/render/backend/webgpu/glassTintPass.ts +2 -1
- package/src/render/backend/webgpu/instanceCullPass.ts +165 -0
- package/src/render/backend/webgpu/instanced.ts +21 -3
- package/src/render/backend/webgpu/latePass.ts +12 -3
- package/src/render/backend/webgpu/renderer.ts +540 -226
- package/src/render/backend/webgpu/sdfTextPass.ts +6 -1
- package/src/render/backend/webgpu/shaders/instanceCull.wgsl.ts +61 -0
- package/src/render/backend/webgpu/surfaceTexturePass.ts +90 -50
- package/src/render/driftLight/bake.ts +85 -62
- package/src/render/driftLight/denseField.ts +149 -0
- package/src/render/driftLight/lightField.ts +33 -18
- package/src/render/driftLight/presence.ts +69 -0
- package/src/render/driftLight/uniforms.ts +8 -9
- package/src/render/driftLight/worldLightField.ts +83 -0
- package/src/render/fixtureAtlas.ts +63 -0
- package/src/render/instanceCull.ts +205 -0
- package/src/render/instances.ts +16 -0
- package/src/render/lightBudget.ts +25 -36
- package/src/render/lightGrid.ts +130 -0
- package/src/render/mesh.ts +59 -72
- package/src/render/occlusion.ts +51 -7
- package/src/render/pointLightSelection.ts +72 -4
- package/src/render/rainField.ts +39 -5
- package/src/render/recon/lateDepth.ts +83 -0
- package/src/render/recon/resolve.ts +206 -20
- package/src/render/rendererHarness.ts +4 -0
- package/src/render/sceneTarget.ts +4 -0
- package/src/render/shaders/depth.ts +12 -9
- package/src/render/shaders/flat/driftLight.ts +32 -7
- package/src/render/shaders/flat/index.ts +5 -3
- package/src/render/shaders/flat/main.ts +75 -23
- package/src/render/shaders/flat/preamble.ts +75 -20
- package/src/render/shaders/flat/surfaceEffects.ts +152 -0
- package/src/render/shaders/generated/depth.wgsl.ts +8 -8
- package/src/render/shaders/generated/flat.wgsl.ts +1361 -1345
- package/src/render/shaders/generated/lightVolume.wgsl.ts +20 -4
- package/src/render/shaders/generated/rush.wgsl.ts +21 -16
- package/src/render/shaders/generated/sdfText.wgsl.ts +2 -2
- package/src/render/shaders/generated/text.wgsl.ts +1 -1
- package/src/render/shaders/gi/driftLight.wgsl.ts +27 -4
- package/src/render/shaders/recon/lateDepth.wgsl.ts +38 -13
- package/src/render/shaders/recon/resolve.wgsl.ts +161 -27
- package/src/render/shaders/rush.ts +11 -1
- package/src/render/shaders/sdfText.ts +3 -2
- package/src/render/shaders/text.ts +18 -9
- package/src/render/surfaceEffects.ts +132 -0
- package/src/render/surfaceScene.ts +16 -0
- package/src/render/surfaceTexture.ts +110 -19
- package/src/render/temporalAa.ts +78 -14
- package/src/render/textureSource.ts +72 -0
- package/src/render/vertexDefaults.ts +2 -0
- package/src/world/hlod.ts +290 -0
package/dist/render/mesh.js
CHANGED
|
@@ -49,14 +49,6 @@ const ATTR_JOINTS = 11;
|
|
|
49
49
|
* setting sway, watching nothing move, and having nothing to read about why.
|
|
50
50
|
*/
|
|
51
51
|
const ATTR_CHANNEL = 13;
|
|
52
|
-
/**
|
|
53
|
-
* The locations `attachInstances` claims, which a plain draw through the same array must not read.
|
|
54
|
-
*
|
|
55
|
-
* Eleven to fourteen are the model matrix's columns and fifteen is the tint. **Thirteen is also
|
|
56
|
-
* the channel**, and eleven and twelve are the skinning pair — which cannot collide, because
|
|
57
|
-
* `flatVert` refuses skinning with instancing. See `draw`.
|
|
58
|
-
*/
|
|
59
|
-
const INSTANCE_COLUMNS = [11, 12, 13, 14, 15];
|
|
60
52
|
const ATTR_WEIGHTS = 12;
|
|
61
53
|
/**
|
|
62
54
|
* One absent attribute's constant, at the width the table gave it.
|
|
@@ -84,6 +76,9 @@ function applyConstant(gl, location, value) {
|
|
|
84
76
|
export class Mesh {
|
|
85
77
|
vao;
|
|
86
78
|
buffers = [];
|
|
79
|
+
/** Every attribute bound from a buffer, so a batch can build a vertex array of its own. */
|
|
80
|
+
layout = [];
|
|
81
|
+
indexBuffer = null;
|
|
87
82
|
/**
|
|
88
83
|
* The position and normal buffers, kept so `update` can rewrite them.
|
|
89
84
|
*
|
|
@@ -93,6 +88,7 @@ export class Mesh {
|
|
|
93
88
|
deformable = null;
|
|
94
89
|
/** How many floats the position buffer holds, so an update of the wrong size is refused. */
|
|
95
90
|
positionFloats;
|
|
91
|
+
/** Indices one draw of it issues; the batch-size rule reads it (`cullsInstances`). */
|
|
96
92
|
indexCount;
|
|
97
93
|
/**
|
|
98
94
|
* The constant values this mesh's *absent* attributes are shaded with, re-applied on
|
|
@@ -211,7 +207,14 @@ export class Mesh {
|
|
|
211
207
|
gl.disableVertexAttribArray(ATTR_UV);
|
|
212
208
|
this.constants.push({ location: ATTR_UV, value: ABSENT_ATTRIBUTE['uvs'] });
|
|
213
209
|
}
|
|
210
|
+
else if (data.layers !== undefined) {
|
|
211
|
+
/* The texture-array layer rides as the third coordinate — every vertex location is spent —
|
|
212
|
+
so a layered mesh uploads (u, v, layer) packed. See `MeshData.layers`. */
|
|
213
|
+
this.attachAttribute(gl, ATTR_UV, packUvLayers(data.uvs, data.layers), 3);
|
|
214
|
+
}
|
|
214
215
|
else {
|
|
216
|
+
/* Two components; the shader's vec3 reads z as 0 by the GL rule for a missing component,
|
|
217
|
+
which is layer 0 — the only layer a plain texture has. */
|
|
215
218
|
this.attachAttribute(gl, ATTR_UV, data.uvs, 2);
|
|
216
219
|
}
|
|
217
220
|
/* Absent means "inherit albedo", supplied as the constant the shader tests for. */
|
|
@@ -297,6 +300,7 @@ export class Mesh {
|
|
|
297
300
|
throw new Error('createBuffer failed');
|
|
298
301
|
this.buffers.push(indexBuffer);
|
|
299
302
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
|
|
303
|
+
this.indexBuffer = indexBuffer;
|
|
300
304
|
this.allocate(gl, gl.ELEMENT_ARRAY_BUFFER, indexBuffer, data.indices, gl.STATIC_DRAW);
|
|
301
305
|
gl.bindVertexArray(null);
|
|
302
306
|
}
|
|
@@ -304,29 +308,11 @@ export class Mesh {
|
|
|
304
308
|
draw(gl) {
|
|
305
309
|
gl.bindVertexArray(this.vao);
|
|
306
310
|
/*
|
|
307
|
-
*
|
|
308
|
-
*
|
|
309
|
-
*
|
|
310
|
-
*
|
|
311
|
-
* of the absent-attribute constant. For a placement with no rotation that column is
|
|
312
|
-
* `(0, 0, 1, 0)`, so `aChannel.y` is zero, `vSkyDirect = aChannel.y` scales the directional
|
|
313
|
-
* term to nothing, and the mesh renders lit by the hemispheric ambient alone — correct
|
|
314
|
-
* normals, correct albedo, no error, no warning.
|
|
315
|
-
*
|
|
316
|
-
* Found on `demo/instancing.ts`, which exists to draw two ranks of one box the two ways and
|
|
317
|
-
* compare them: the `drawMesh` rank's top face read (51, 30, 21) against the `drawInstanced`
|
|
318
|
-
* rank's (218, 110, 59), and 51 is exactly the ambient. WebGPU has no array to share and drew
|
|
319
|
-
* them alike, which is what made it visible at all.
|
|
320
|
-
*
|
|
321
|
-
* Disabled here and put back by `drawInstances`, rather than given the batch an array of its
|
|
322
|
-
* own: that is the larger change this class's own note about two batches already contemplates,
|
|
323
|
-
* and it wants one for a different reason. Three calls on a mesh that has a batch and none on
|
|
324
|
-
* a mesh that does not.
|
|
311
|
+
* No instance columns to disable: a batch draws through a vertex array of its own
|
|
312
|
+
* (`createInstanceArray`), so this array never carries them. It used to, and location 13 — the
|
|
313
|
+
* channel — then read the third column of instance zero's matrix on a plain draw, which lit a
|
|
314
|
+
* mesh by ambient alone on this backend and not the other.
|
|
325
315
|
*/
|
|
326
|
-
if (this.instanced) {
|
|
327
|
-
for (const location of INSTANCE_COLUMNS)
|
|
328
|
-
gl.disableVertexAttribArray(location);
|
|
329
|
-
}
|
|
330
316
|
// The VAO restores the arrays; these it cannot. See `constants`.
|
|
331
317
|
for (const constant of this.constants) {
|
|
332
318
|
const value = constant.value;
|
|
@@ -335,32 +321,27 @@ export class Mesh {
|
|
|
335
321
|
gl.drawElements(gl.TRIANGLES, this.indexCount, gl.UNSIGNED_INT, 0);
|
|
336
322
|
}
|
|
337
323
|
/**
|
|
338
|
-
*
|
|
339
|
-
*
|
|
340
|
-
* **One batch per mesh, and the vertex array is why.** A `Mesh` owns exactly one, so a second
|
|
341
|
-
* batch would rebind locations 11 to 15 on the array the first still draws through — and what
|
|
342
|
-
* comes out is one batch's instances placed by the other's matrices, which reads as a transform
|
|
343
|
-
* bug in the consumer's own code rather than as a misuse of this.
|
|
324
|
+
* A vertex array for one instanced batch: every attribute this mesh binds from a buffer, its index
|
|
325
|
+
* buffer, and the batch's instance columns — the model matrix at 11 to 14, the tint at 15.
|
|
344
326
|
*
|
|
345
|
-
* **
|
|
346
|
-
*
|
|
327
|
+
* **A batch owns its own array**, which is what lets a mesh have more than one batch: a prop drawn
|
|
328
|
+
* once per region is one mesh and a batch per region. With one array a mesh had one batch, since a
|
|
329
|
+
* second would rebind the columns the first draws through and place one batch's instances by the
|
|
330
|
+
* other's matrices. The arrays share every buffer, so a batch costs its instances and nothing of
|
|
331
|
+
* the geometry. The caller deletes what this returns.
|
|
347
332
|
*/
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
333
|
+
createInstanceArray(gl, instances, stride) {
|
|
334
|
+
const vao = gl.createVertexArray();
|
|
335
|
+
if (vao === null)
|
|
336
|
+
throw new Error('createVertexArray failed');
|
|
337
|
+
gl.bindVertexArray(vao);
|
|
338
|
+
for (const attribute of this.layout) {
|
|
339
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, attribute.buffer);
|
|
340
|
+
gl.enableVertexAttribArray(attribute.location);
|
|
341
|
+
gl.vertexAttribPointer(attribute.location, attribute.size, gl.FLOAT, false, 0, 0);
|
|
352
342
|
}
|
|
353
|
-
this.
|
|
354
|
-
|
|
355
|
-
this.instanceStride = stride;
|
|
356
|
-
gl.bindVertexArray(this.vao);
|
|
357
|
-
this.bindInstanceColumns(gl, buffer);
|
|
358
|
-
gl.bindVertexArray(null);
|
|
359
|
-
}
|
|
360
|
-
/** The instance columns, bound. Called at attach and again after a plain draw disabled them. */
|
|
361
|
-
bindInstanceColumns(gl, buffer) {
|
|
362
|
-
const stride = this.instanceStride;
|
|
363
|
-
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
|
343
|
+
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
|
|
344
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, instances);
|
|
364
345
|
/* Four vec4 columns then a vec3 tint: a mat4 is not a vertex attribute in GLSL ES 300. */
|
|
365
346
|
for (let column = 0; column < 4; column += 1) {
|
|
366
347
|
const location = 11 + column;
|
|
@@ -371,22 +352,12 @@ export class Mesh {
|
|
|
371
352
|
gl.enableVertexAttribArray(15);
|
|
372
353
|
gl.vertexAttribPointer(15, 3, gl.FLOAT, false, stride, 64);
|
|
373
354
|
gl.vertexAttribDivisor(15, 1);
|
|
355
|
+
gl.bindVertexArray(null);
|
|
356
|
+
return vao;
|
|
374
357
|
}
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
instanced = false;
|
|
379
|
-
/**
|
|
380
|
-
* Draw `count` instances through the attributes `attachInstances` bound.
|
|
381
|
-
*
|
|
382
|
-
* The constants are re-applied exactly as `draw` does — a vertex array restores the arrays and
|
|
383
|
-
* not the constant attributes, and an absent optional attribute is a constant here.
|
|
384
|
-
*/
|
|
385
|
-
drawInstances(gl, count) {
|
|
386
|
-
gl.bindVertexArray(this.vao);
|
|
387
|
-
/* Put back what `draw` disabled, if a plain draw of this mesh came first. See its note. */
|
|
388
|
-
if (this.instanceBuffer !== null)
|
|
389
|
-
this.bindInstanceColumns(gl, this.instanceBuffer);
|
|
358
|
+
/** Draw `count` instances through a batch's own vertex array. See `createInstanceArray`. */
|
|
359
|
+
drawInstancesThrough(gl, vao, count) {
|
|
360
|
+
gl.bindVertexArray(vao);
|
|
390
361
|
for (const constant of this.constants) {
|
|
391
362
|
const value = constant.value;
|
|
392
363
|
applyConstant(gl, constant.location, value);
|
|
@@ -438,6 +409,7 @@ export class Mesh {
|
|
|
438
409
|
this.allocate(gl, gl.ARRAY_BUFFER, buffer, data, usage);
|
|
439
410
|
gl.enableVertexAttribArray(location);
|
|
440
411
|
gl.vertexAttribPointer(location, size, gl.FLOAT, false, 0, 0);
|
|
412
|
+
this.layout.push({ location, buffer, size });
|
|
441
413
|
return buffer;
|
|
442
414
|
}
|
|
443
415
|
/**
|
|
@@ -508,3 +480,14 @@ export function createMeshIncremental(gl, data, dynamic = false) {
|
|
|
508
480
|
const mesh = new Mesh(gl, data, dynamic, true);
|
|
509
481
|
return { mesh, upload: mesh.uploads(gl) };
|
|
510
482
|
}
|
|
483
|
+
/** Texture coordinates and layers interleaved as (u, v, layer), one row a vertex. */
|
|
484
|
+
export function packUvLayers(uvs, layers) {
|
|
485
|
+
const count = layers.length;
|
|
486
|
+
const packed = new Float32Array(count * 3);
|
|
487
|
+
for (let v = 0; v < count; v++) {
|
|
488
|
+
packed[v * 3] = uvs[v * 2];
|
|
489
|
+
packed[v * 3 + 1] = uvs[v * 2 + 1];
|
|
490
|
+
packed[v * 3 + 2] = layers[v];
|
|
491
|
+
}
|
|
492
|
+
return packed;
|
|
493
|
+
}
|
|
@@ -51,6 +51,16 @@ export declare class OcclusionBuffer {
|
|
|
51
51
|
* something hidden costs a draw call; culling something visible is a hole.
|
|
52
52
|
*/
|
|
53
53
|
occluded(bounds: Bounds, model: ReadonlyMat4): boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Whether a world-space box is entirely behind the declared occluders — the tighter question for
|
|
56
|
+
* something that is a box, where the cube around its sphere reaches far past it. **A flat region
|
|
57
|
+
* is the case it exists for**: a city block's sphere is half as deep underground as it is wide,
|
|
58
|
+
* nothing there occludes it, and the sphere test abstains for every block in the city. False
|
|
59
|
+
* whenever anything is uncertain, exactly as `occluded`.
|
|
60
|
+
*/
|
|
61
|
+
occludedBox(min: ArrayLike<number>, max: ArrayLike<number>): boolean;
|
|
62
|
+
/** The shared test: a world box's corners projected, its rectangle tested against the pyramid. */
|
|
63
|
+
private boxBehind;
|
|
54
64
|
/**
|
|
55
65
|
* A world point into buffer coordinates and depth, written into `CORNERS` at `slot`.
|
|
56
66
|
*
|
package/dist/render/occlusion.js
CHANGED
|
@@ -33,6 +33,8 @@
|
|
|
33
33
|
*/
|
|
34
34
|
/** What an unwritten texel holds: the far plane, which occludes nothing. */
|
|
35
35
|
const FAR = 1;
|
|
36
|
+
/** Texels a tested rectangle may span at the level it is read from. See `boxBehind`. */
|
|
37
|
+
const TEXELS_ACROSS = 8;
|
|
36
38
|
export class OcclusionBuffer {
|
|
37
39
|
/**
|
|
38
40
|
* The pyramid, level 0 first, each level a quarter of the one before.
|
|
@@ -169,15 +171,33 @@ export class OcclusionBuffer {
|
|
|
169
171
|
const sz = Math.sqrt(m8 * m8 + m9 * m9 + m10 * m10);
|
|
170
172
|
const radius = bounds.radius * Math.max(sx, sy, sz);
|
|
171
173
|
/* The cube that contains the sphere, which contains the object. Conservative twice over. */
|
|
174
|
+
return this.boxBehind(x - radius, y - radius, z - radius, x + radius, y + radius, z + radius);
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Whether a world-space box is entirely behind the declared occluders — the tighter question for
|
|
178
|
+
* something that is a box, where the cube around its sphere reaches far past it. **A flat region
|
|
179
|
+
* is the case it exists for**: a city block's sphere is half as deep underground as it is wide,
|
|
180
|
+
* nothing there occludes it, and the sphere test abstains for every block in the city. False
|
|
181
|
+
* whenever anything is uncertain, exactly as `occluded`.
|
|
182
|
+
*/
|
|
183
|
+
occludedBox(min, max) {
|
|
184
|
+
if (this.occluders === 0)
|
|
185
|
+
return false;
|
|
186
|
+
if (!this.built)
|
|
187
|
+
this.buildPyramid();
|
|
188
|
+
return this.boxBehind(min[0] ?? 0, min[1] ?? 0, min[2] ?? 0, max[0] ?? 0, max[1] ?? 0, max[2] ?? 0);
|
|
189
|
+
}
|
|
190
|
+
/** The shared test: a world box's corners projected, its rectangle tested against the pyramid. */
|
|
191
|
+
boxBehind(loX, loY, loZ, hiX, hiY, hiZ) {
|
|
172
192
|
let minX = Infinity;
|
|
173
193
|
let minY = Infinity;
|
|
174
194
|
let minZ = Infinity;
|
|
175
195
|
let maxX = -Infinity;
|
|
176
196
|
let maxY = -Infinity;
|
|
177
197
|
for (let corner = 0; corner < 8; corner++) {
|
|
178
|
-
const px = corner & 1 ?
|
|
179
|
-
const py = corner & 2 ?
|
|
180
|
-
const pz = corner & 4 ?
|
|
198
|
+
const px = corner & 1 ? hiX : loX;
|
|
199
|
+
const py = corner & 2 ? hiY : loY;
|
|
200
|
+
const pz = corner & 4 ? hiZ : loZ;
|
|
181
201
|
if (!this.project(px, py, pz, 0))
|
|
182
202
|
return false;
|
|
183
203
|
minX = Math.min(minX, CORNERS[0] ?? 0);
|
|
@@ -192,13 +212,22 @@ export class OcclusionBuffer {
|
|
|
192
212
|
if (minX < 0 || minY < 0 || maxX > width || maxY > height)
|
|
193
213
|
return false;
|
|
194
214
|
/*
|
|
195
|
-
* The coarsest level
|
|
196
|
-
* most
|
|
197
|
-
* repository's determinism gate refuses elsewhere; a shift loop says it in integers.
|
|
215
|
+
* The coarsest level at which the rectangle spans at most `TEXELS_ACROSS` texels, so the test
|
|
216
|
+
* reads at most that many plus one each way. `Math.log2` would say the same thing and is one of
|
|
217
|
+
* the calls this repository's determinism gate refuses elsewhere; a shift loop says it in integers.
|
|
218
|
+
*
|
|
219
|
+
* **Eight, not one, since 2026-09-30.** At one the level's texel was as wide as the rectangle, and
|
|
220
|
+
* two such texels aligned to the grid span up to twice it — so anything whose rectangle came near
|
|
221
|
+
* an occluder's edge, or the buffer's own unwritten border row, read unwritten texels and
|
|
222
|
+
* abstained. That was every city region behind a row of buildings, whose rectangle sits just
|
|
223
|
+
* under the skyline; measured on a box seven metres wide behind a wall filling the view, which
|
|
224
|
+
* one abstained at and eight culls. Reading up to 81 texels instead of 4 costs a few dozen
|
|
225
|
+
* compares and keeps the answer exactly as conservative: the maximum over every texel the
|
|
226
|
+
* rectangle touches.
|
|
198
227
|
*/
|
|
199
228
|
let level = 0;
|
|
200
229
|
let span = Math.max(maxX - minX, maxY - minY);
|
|
201
|
-
while (span >
|
|
230
|
+
while (span > TEXELS_ACROSS && level + 1 < this.levels.length) {
|
|
202
231
|
span *= 0.5;
|
|
203
232
|
level++;
|
|
204
233
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/** Allocation-free selection and packing of point lights for a fixed shader budget. */
|
|
2
|
+
import type { LightGrid } from './lightGrid.ts';
|
|
2
3
|
export declare const DEFAULT_POINT_LIGHT_VIEW_RANGE = 180;
|
|
3
4
|
export interface PointLightSource {
|
|
4
5
|
x: number;
|
|
@@ -189,3 +190,9 @@ export declare function createPointLightBuffer(capacity?: number): PointLightBuf
|
|
|
189
190
|
* makes the distance the *same arithmetic on the same operands*, not merely a close number.
|
|
190
191
|
*/
|
|
191
192
|
export declare function selectPointLights(sources: readonly PointLightSource[], x: number, y: number, z: number, out: PointLightBuffer, timeSeconds?: number, viewRange?: number, castX?: number, castY?: number, castZ?: number): void;
|
|
193
|
+
/**
|
|
194
|
+
* `selectPointLights` over a grid's sources, looking only at the lights near the eye: the same
|
|
195
|
+
* choice, tie for tie, for a world of thousands of fixed lights that the plain scan would visit
|
|
196
|
+
* every frame. See `lightGrid.ts` for why the result is the scan's exactly.
|
|
197
|
+
*/
|
|
198
|
+
export declare function selectGridLights(grid: LightGrid, x: number, y: number, z: number, out: PointLightBuffer, timeSeconds?: number, viewRange?: number, castX?: number, castY?: number, castZ?: number): void;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/** Allocation-free selection and packing of point lights for a fixed shader budget. */
|
|
2
2
|
import { POINT_LIGHT_COS_INNER, POINT_LIGHT_COS_OUTER } from './clusteredLights.js';
|
|
3
3
|
import { MAX_POINT_LIGHTS, POINT_SHADOW_POOL } from './lightBudget.js';
|
|
4
|
+
import { gatherLights } from './lightGrid.js';
|
|
4
5
|
export const DEFAULT_POINT_LIGHT_VIEW_RANGE = 180;
|
|
5
6
|
/**
|
|
6
7
|
* How far out a light fades to nothing, in metres before its cull distance.
|
|
@@ -38,8 +39,15 @@ const FADE_BAND_M = 40;
|
|
|
38
39
|
* while it is still too dim to show one.
|
|
39
40
|
*/
|
|
40
41
|
const CONTENTION_BAND_M = 3;
|
|
41
|
-
/**
|
|
42
|
-
|
|
42
|
+
/**
|
|
43
|
+
* Sort keys for the shadow list, which is ordered independently of the shaded one.
|
|
44
|
+
*
|
|
45
|
+
* **Float64, and ties broken by source index**, the shaded list's rule. These were single precision
|
|
46
|
+
* compared against a double, so which of two lamps exactly as far away came first depended on
|
|
47
|
+
* which way the distance happened to round — and on the order the lamps were met, which is what a
|
|
48
|
+
* grid changes. Now the list is the same whatever order its candidates arrive in.
|
|
49
|
+
*/
|
|
50
|
+
const shadowRank = new Float64Array(POINT_SHADOW_POOL);
|
|
43
51
|
/**
|
|
44
52
|
* Every light in range this frame, by source index and squared distance, before the nearest are
|
|
45
53
|
* chosen. Grown when a world offers more than it has held, and never shrunk, so a frame allocates
|
|
@@ -177,6 +185,19 @@ export function createPointLightBuffer(capacity = MAX_POINT_LIGHTS) {
|
|
|
177
185
|
* makes the distance the *same arithmetic on the same operands*, not merely a close number.
|
|
178
186
|
*/
|
|
179
187
|
export function selectPointLights(sources, x, y, z, out, timeSeconds = 0, viewRange = DEFAULT_POINT_LIGHT_VIEW_RANGE, castX = x, castY = y, castZ = z) {
|
|
188
|
+
choose(sources, null, sources.length, x, y, z, out, timeSeconds, viewRange, castX, castY, castZ);
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* `selectPointLights` over a grid's sources, looking only at the lights near the eye: the same
|
|
192
|
+
* choice, tie for tie, for a world of thousands of fixed lights that the plain scan would visit
|
|
193
|
+
* every frame. See `lightGrid.ts` for why the result is the scan's exactly.
|
|
194
|
+
*/
|
|
195
|
+
export function selectGridLights(grid, x, y, z, out, timeSeconds = 0, viewRange = DEFAULT_POINT_LIGHT_VIEW_RANGE, castX = x, castY = y, castZ = z) {
|
|
196
|
+
const count = gatherLights(grid, x, y, z, viewRange + grid.maxRadius);
|
|
197
|
+
choose(grid.sources, grid.gathered, count, x, y, z, out, timeSeconds, viewRange, castX, castY, castZ);
|
|
198
|
+
}
|
|
199
|
+
/** The choice itself, over every source or over the ones `visit` names, in source order. */
|
|
200
|
+
function choose(sources, visit, visitCount, x, y, z, out, timeSeconds, viewRange, castX, castY, castZ) {
|
|
180
201
|
out.count = 0;
|
|
181
202
|
out.shadowCount = 0;
|
|
182
203
|
/*
|
|
@@ -193,7 +214,8 @@ export function selectPointLights(sources, x, y, z, out, timeSeconds = 0, viewRa
|
|
|
193
214
|
*/
|
|
194
215
|
let cutSq = Infinity;
|
|
195
216
|
let candidateCount = 0;
|
|
196
|
-
for (let
|
|
217
|
+
for (let visited = 0; visited < visitCount; visited++) {
|
|
218
|
+
const sourceIndex = visit === null ? visited : visit[visited];
|
|
197
219
|
const light = sources[sourceIndex];
|
|
198
220
|
if (light === undefined)
|
|
199
221
|
continue;
|
|
@@ -265,7 +287,10 @@ export function selectPointLights(sources, x, y, z, out, timeSeconds = 0, viewRa
|
|
|
265
287
|
if (castDistanceSq >= light.radius * light.radius)
|
|
266
288
|
continue;
|
|
267
289
|
let shadowSlot = out.shadowCount;
|
|
268
|
-
while (shadowSlot > 0 &&
|
|
290
|
+
while (shadowSlot > 0 &&
|
|
291
|
+
((shadowRank[shadowSlot - 1] ?? 0) > castDistanceSq ||
|
|
292
|
+
((shadowRank[shadowSlot - 1] ?? 0) === castDistanceSq &&
|
|
293
|
+
(out.shadowIndex[shadowSlot - 1] ?? -1) > sourceIndex)))
|
|
269
294
|
shadowSlot--;
|
|
270
295
|
if (shadowSlot >= POINT_SHADOW_POOL)
|
|
271
296
|
continue;
|
|
@@ -49,6 +49,17 @@ export interface RainFieldOptions {
|
|
|
49
49
|
streakSec?: number;
|
|
50
50
|
/** The ground drops land on, and the roofs they do not fall through. */
|
|
51
51
|
ground?: GroundSurface;
|
|
52
|
+
/**
|
|
53
|
+
* How near the viewer a streak may pass and still be drawn, metres. Default 1.
|
|
54
|
+
*
|
|
55
|
+
* **A drop that close is out of any focus a camera holds**, and drawn sharp it was a bar across
|
|
56
|
+
* the frame: a streak is a world-space width, so a 6 mm drop 30 cm from the eye drew fourteen
|
|
57
|
+
* pixels thick and as long as the frame, from a city seen from the air in the rain. A streak
|
|
58
|
+
* whose nearest point is inside this distance is not drawn; the drop keeps falling, as a
|
|
59
|
+
* sheltered one does. What it gives up is rain on a lens or a visor, which this field never
|
|
60
|
+
* drew as such anyway. What would make it wrong is a view that wants drops in macro: pass 0.
|
|
61
|
+
*/
|
|
62
|
+
clearM?: number;
|
|
52
63
|
}
|
|
53
64
|
export declare class RainField {
|
|
54
65
|
readonly segments: LineSegments;
|
|
@@ -71,6 +82,8 @@ export declare class RainField {
|
|
|
71
82
|
*/
|
|
72
83
|
streakSec: number;
|
|
73
84
|
private readonly ground;
|
|
85
|
+
/** How near the viewer a streak may pass and still be drawn, squared. */
|
|
86
|
+
private readonly clear2;
|
|
74
87
|
/** Drops not drawn this update because something was over them. */
|
|
75
88
|
private sheltered;
|
|
76
89
|
/**
|
package/dist/render/rainField.js
CHANGED
|
@@ -41,6 +41,8 @@ export class RainField {
|
|
|
41
41
|
*/
|
|
42
42
|
streakSec;
|
|
43
43
|
ground;
|
|
44
|
+
/** How near the viewer a streak may pass and still be drawn, squared. */
|
|
45
|
+
clear2;
|
|
44
46
|
/** Drops not drawn this update because something was over them. */
|
|
45
47
|
sheltered = 0;
|
|
46
48
|
/**
|
|
@@ -68,6 +70,8 @@ export class RainField {
|
|
|
68
70
|
this.speedMps = options.speedMps ?? 9;
|
|
69
71
|
this.streakSec = options.streakSec ?? 0.04;
|
|
70
72
|
this.ground = options.ground ?? null;
|
|
73
|
+
const clear = options.clearM ?? 1;
|
|
74
|
+
this.clear2 = clear * clear;
|
|
71
75
|
this.segments = createLineSegments(options.count);
|
|
72
76
|
this.x = new Float32Array(options.count);
|
|
73
77
|
this.y = new Float32Array(options.count);
|
|
@@ -141,15 +145,33 @@ export class RainField {
|
|
|
141
145
|
this.sheltered++;
|
|
142
146
|
continue;
|
|
143
147
|
}
|
|
148
|
+
/* Where it was a shutter-time ago, which is the streak: down by its fall and back along the
|
|
149
|
+
wind, so a gust leans every drop at the same angle without anything trigonometric. */
|
|
150
|
+
const backX = -windX * this.streakSec;
|
|
151
|
+
const backY = this.speedMps * this.streakSec;
|
|
152
|
+
const backZ = -windZ * this.streakSec;
|
|
153
|
+
/* Too near the eye to be drawn (`clearM`): the streak's nearest point to the viewer. */
|
|
154
|
+
if (this.clear2 > 0) {
|
|
155
|
+
const ox = dropX - viewerX;
|
|
156
|
+
const oy = dropY - viewerY;
|
|
157
|
+
const oz = dropZ - viewerZ;
|
|
158
|
+
const length2 = backX * backX + backY * backY + backZ * backZ;
|
|
159
|
+
const along = length2 > 0
|
|
160
|
+
? Math.min(Math.max(-(ox * backX + oy * backY + oz * backZ) / length2, 0), 1)
|
|
161
|
+
: 0;
|
|
162
|
+
const nx = ox + backX * along;
|
|
163
|
+
const ny = oy + backY * along;
|
|
164
|
+
const nz = oz + backZ * along;
|
|
165
|
+
if (nx * nx + ny * ny + nz * nz < this.clear2)
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
144
168
|
const at = this.segments.count;
|
|
145
169
|
this.segments.from[at * 3] = dropX;
|
|
146
170
|
this.segments.from[at * 3 + 1] = dropY;
|
|
147
171
|
this.segments.from[at * 3 + 2] = dropZ;
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
this.segments.to[at * 3] =
|
|
151
|
-
this.segments.to[at * 3 + 1] = dropY + this.speedMps * this.streakSec;
|
|
152
|
-
this.segments.to[at * 3 + 2] = dropZ - windZ * this.streakSec;
|
|
172
|
+
this.segments.to[at * 3] = dropX + backX;
|
|
173
|
+
this.segments.to[at * 3 + 1] = dropY + backY;
|
|
174
|
+
this.segments.to[at * 3 + 2] = dropZ + backZ;
|
|
153
175
|
this.segments.count = at + 1;
|
|
154
176
|
}
|
|
155
177
|
this.primed = true;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where output pixel (`outX`, `outY`) — its centre, in pixels — falls among the render texels
|
|
3
|
+
* jittered by (`jitterX`, `jitterY`) render texels: into `out`, the cell's first texel and the
|
|
4
|
+
* fraction across it, `[x, y, fx, fy]`.
|
|
5
|
+
*/
|
|
6
|
+
export declare function lateDepthCell(outX: number, outY: number, renderWidth: number, renderHeight: number, outputWidth: number, outputHeight: number, jitterX: number, jitterY: number, out: Float64Array | Float32Array): void;
|
|
7
|
+
/**
|
|
8
|
+
* The depth to test against, from `taps` — a 4x4 block of render texels, row-major, starting one
|
|
9
|
+
* texel up and left of the cell's first — at (`fx`, `fy`) across the cell.
|
|
10
|
+
*/
|
|
11
|
+
export declare function lateDepthAt(taps: ArrayLike<number>, fx: number, fy: number): number;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The depth a reconstructing frame's late draws are tested against at an output pixel, taken from
|
|
3
|
+
* the render's jittered depth. `shaders/recon/lateDepth.wgsl.ts` is the same arithmetic on the device.
|
|
4
|
+
*
|
|
5
|
+
* **Each surface taken to the pixel, and the nearest of them.** A late draw — a glow, a pane, a
|
|
6
|
+
* caption — is drawn unjittered at the output size, and it has to meet the depth of what it lies on
|
|
7
|
+
* where *it* is. Each of the four render texels about the pixel, placed where the jitter put it,
|
|
8
|
+
* carries its surface on to the pixel's unjittered centre along that surface's own slope; the
|
|
9
|
+
* nearest of the four so carried stands, and a quarter of its slope is given back toward the far
|
|
10
|
+
* side so that a draw lying exactly on the surface is not decided by rounding. On a plane all four
|
|
11
|
+
* arrive at the plane's own depth at the pixel: exact, and the same whatever the jitter was.
|
|
12
|
+
*
|
|
13
|
+
* **A texel's slope is the smaller of its two differences along each axis, and none where they
|
|
14
|
+
* disagree in sign** — the minmod limiter. Beside an edge one difference is the jump, so it is the
|
|
15
|
+
* other, the surface's own, that is taken; a sliver one texel wide disagrees both ways and keeps its
|
|
16
|
+
* own depth, the nearest of the four as before. So a translucent draw still never comes through an
|
|
17
|
+
* opaque edge the render covered by more than the render's own texel.
|
|
18
|
+
*
|
|
19
|
+
* **Why this replaced the nearest of the four.** On a wall seen aslant the nearest of four neighbours
|
|
20
|
+
* stands up to a texel's step in front of the wall at the pixel, so a glow lying a few centimetres
|
|
21
|
+
* proud of it was judged behind the wall; and which neighbour was nearest moved with the jitter, so
|
|
22
|
+
* the glow was drawn one frame and not the next. Measured on a street of lit facades: every strip of
|
|
23
|
+
* glow on every tower flickered frame by frame, 12,415 pixels of one tower between two frames.
|
|
24
|
+
*
|
|
25
|
+
* What it gives up: a surface curving within a texel is carried on as a plane, which a quarter-step
|
|
26
|
+
* tolerance covers. What would make it wrong is depth that is not affine on a plane — a depth written
|
|
27
|
+
* from the fragment stage — which nothing drawn before the late pass writes.
|
|
28
|
+
*/
|
|
29
|
+
import { REVERSED_DEPTH } from '../depthConvention.js';
|
|
30
|
+
/** The share of a surface's slope given back toward the far side, for a draw lying on it. */
|
|
31
|
+
const TOLERANCE_SHARE = 0.25;
|
|
32
|
+
/** The smaller of two differences, or none where they disagree in sign. */
|
|
33
|
+
function minmod(a, b) {
|
|
34
|
+
if (a * b <= 0)
|
|
35
|
+
return 0;
|
|
36
|
+
return Math.abs(a) < Math.abs(b) ? a : b;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Where output pixel (`outX`, `outY`) — its centre, in pixels — falls among the render texels
|
|
40
|
+
* jittered by (`jitterX`, `jitterY`) render texels: into `out`, the cell's first texel and the
|
|
41
|
+
* fraction across it, `[x, y, fx, fy]`.
|
|
42
|
+
*/
|
|
43
|
+
export function lateDepthCell(outX, outY, renderWidth, renderHeight, outputWidth, outputHeight, jitterX, jitterY, out) {
|
|
44
|
+
/* A texel stands at its index plus a half less the jitter, as the resolve places it. */
|
|
45
|
+
const x = (outX * renderWidth) / outputWidth - 0.5 + jitterX;
|
|
46
|
+
const y = (outY * renderHeight) / outputHeight - 0.5 + jitterY;
|
|
47
|
+
out[0] = Math.floor(x);
|
|
48
|
+
out[1] = Math.floor(y);
|
|
49
|
+
out[2] = x - Math.floor(x);
|
|
50
|
+
out[3] = y - Math.floor(y);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The depth to test against, from `taps` — a 4x4 block of render texels, row-major, starting one
|
|
54
|
+
* texel up and left of the cell's first — at (`fx`, `fy`) across the cell.
|
|
55
|
+
*/
|
|
56
|
+
export function lateDepthAt(taps, fx, fy) {
|
|
57
|
+
const t = (x, y) => taps[(y + 1) * 4 + x + 1];
|
|
58
|
+
let depth = REVERSED_DEPTH ? -Infinity : Infinity;
|
|
59
|
+
for (let oy = 0; oy < 2; oy += 1) {
|
|
60
|
+
for (let ox = 0; ox < 2; ox += 1) {
|
|
61
|
+
const d = t(ox, oy);
|
|
62
|
+
const gx = minmod(t(ox + 1, oy) - d, d - t(ox - 1, oy));
|
|
63
|
+
const gy = minmod(t(ox, oy + 1) - d, d - t(ox, oy - 1));
|
|
64
|
+
const carried = d + gx * (fx - ox) + gy * (fy - oy);
|
|
65
|
+
const give = TOLERANCE_SHARE * Math.max(Math.abs(gx), Math.abs(gy));
|
|
66
|
+
depth = REVERSED_DEPTH ? Math.max(depth, carried - give) : Math.min(depth, carried + give);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return depth;
|
|
70
|
+
}
|
|
@@ -54,6 +54,12 @@ export interface ResolveFrame {
|
|
|
54
54
|
readonly inverseViewProj: ArrayLike<number>;
|
|
55
55
|
/** Last frame's view-projection and its inverse, unjittered. */
|
|
56
56
|
readonly previousViewProj: ArrayLike<number>;
|
|
57
|
+
/**
|
|
58
|
+
* Last frame's view-projection times this frame's inverse: where a texel's clip position was
|
|
59
|
+
* last frame. Derived from the two when absent; a caller holding its matrices at a lower precision
|
|
60
|
+
* than it multiplies at passes its own product, as the renderer does.
|
|
61
|
+
*/
|
|
62
|
+
readonly reprojection?: ArrayLike<number>;
|
|
57
63
|
readonly previousInverseViewProj: ArrayLike<number>;
|
|
58
64
|
readonly eye: ArrayLike<number>;
|
|
59
65
|
readonly previousEye: ArrayLike<number>;
|
|
@@ -62,6 +68,22 @@ export interface ResolveFrame {
|
|
|
62
68
|
readonly previousJitter: ArrayLike<number>;
|
|
63
69
|
readonly quality: ReconQuality;
|
|
64
70
|
readonly disocclusion: DisocclusionParams;
|
|
71
|
+
/**
|
|
72
|
+
* Last frame's flicker records, output size, four floats a pixel — the spread, this period's
|
|
73
|
+
* running sum, the last period's mean, and how many periods running repeated, as `Flicker` holds
|
|
74
|
+
* them — or absent for the resolve without its anti-flicker.
|
|
75
|
+
*/
|
|
76
|
+
readonly flicker?: ArrayLike<number> | null;
|
|
77
|
+
/**
|
|
78
|
+
* Last frame's depth swings, output size, one float a pixel: the largest miss, relative to the
|
|
79
|
+
* depth, of last frame's depth against the one this pixel's surface should have had, seen lately
|
|
80
|
+
* — held, and halving over a period. Carried with `flicker` and read only with it.
|
|
81
|
+
*/
|
|
82
|
+
readonly depthSwing?: ArrayLike<number> | null;
|
|
83
|
+
/** Whether this frame samples the first phase of the jitter's period. */
|
|
84
|
+
readonly periodStart?: boolean;
|
|
85
|
+
/** The jitter's period: the reconstruction's own phase count, not the temporal resolve's. */
|
|
86
|
+
readonly flickerPhases?: number;
|
|
65
87
|
}
|
|
66
88
|
/**
|
|
67
89
|
* How far a render sample's contribution reaches, in output pixels: a Gaussian's standard deviation.
|
|
@@ -78,13 +100,21 @@ export declare function reconstructionWeight(dx: number, dy: number): number;
|
|
|
78
100
|
* pixel away.
|
|
79
101
|
*/
|
|
80
102
|
export declare const CONFIDENT_WEIGHT = 1;
|
|
103
|
+
/**
|
|
104
|
+
* How many of its held depth swings a still pixel's depth test tolerates. The peak halves over a
|
|
105
|
+
* period, so the frame before the next once-a-period miss holds just over half of it: tolerating
|
|
106
|
+
* twice the peak passes that miss outright, and three leaves the same room again.
|
|
107
|
+
*/
|
|
108
|
+
export declare const DEPTH_SWING_WIDTH = 3;
|
|
81
109
|
/**
|
|
82
110
|
* One output pixel: the history's next colour, before sharpening, and the weight it has gathered —
|
|
83
|
-
* four values into `out`.
|
|
111
|
+
* four values into `out` — and, where the frame carries records, the pixel's next one into `record`.
|
|
84
112
|
*/
|
|
85
|
-
export declare function resolvePixel(frame: ResolveFrame, ox: number, oy: number, out: Float32Array): void;
|
|
113
|
+
export declare function resolvePixel(frame: ResolveFrame, ox: number, oy: number, out: Float32Array, record?: Float32Array, swingOut?: Float32Array): void;
|
|
86
114
|
/**
|
|
87
115
|
* The whole frame: the next history into `history`, four floats a pixel, and the picture shown into
|
|
88
|
-
* `shown`, three — the history sharpened against the four direct neighbours it has just been given
|
|
116
|
+
* `shown`, three — the history sharpened against the four direct neighbours it has just been given —
|
|
117
|
+
* and, where the frame carries records, the next ones into `records`, four floats a pixel, and the
|
|
118
|
+
* next depth swings into `swings`, one.
|
|
89
119
|
*/
|
|
90
|
-
export declare function resolveFrame(frame: ResolveFrame, history: Float32Array, shown: Float32Array): void;
|
|
120
|
+
export declare function resolveFrame(frame: ResolveFrame, history: Float32Array, shown: Float32Array, records?: Float32Array, swings?: Float32Array): void;
|