@driftengine/core 4.5.0 → 4.6.1
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 +62 -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 +249 -128
- package/dist/render/backend/webgpu/buffers.d.ts +17 -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/motionPass.js +2 -1
- 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 +478 -214
- 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 +23 -24
- package/dist/render/mesh.js +56 -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 +311 -150
- package/src/render/backend/webgpu/buffers.ts +49 -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/motionPass.ts +2 -1
- package/src/render/backend/webgpu/renderer.ts +556 -239
- 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 +64 -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
|
@@ -1,4 +1,16 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* A GPU image the caller supplies, sampled by the flat shader as surface colour — or several, as
|
|
3
|
+
* the layers of one array.
|
|
4
|
+
*
|
|
5
|
+
* **Every surface texture is an array; a plain image is an array of one.** The lit pass has three of
|
|
6
|
+
* WebGL2's guaranteed sixteen texture units free, so a second set of array samplers beside the 2D
|
|
7
|
+
* ones was never affordable. Making the one set of samplers arrays costs no unit, and a one-layer
|
|
8
|
+
* array samples exactly as a 2D texture does. A mesh names its layer per vertex (`MeshData.layers`),
|
|
9
|
+
* so a merged block wearing forty facades is one draw.
|
|
10
|
+
*/
|
|
11
|
+
import { isSourceList, layerSize, refuseArrayUpdate, sourceSize } from './textureSource.ts';
|
|
12
|
+
import { SURFACE_EFFECT_TEXELS, packSurfaceEffects } from './surfaceEffects.ts';
|
|
13
|
+
import type { SurfaceLayerEffect } from './surfaceEffects.ts';
|
|
2
14
|
|
|
3
15
|
/**
|
|
4
16
|
* How a texture behaves past its edges and between its texels.
|
|
@@ -103,6 +115,12 @@ export interface SurfaceMaterial<Texture = SurfaceTexture> {
|
|
|
103
115
|
}
|
|
104
116
|
|
|
105
117
|
export interface SurfaceTextureOptions {
|
|
118
|
+
/**
|
|
119
|
+
* What each layer does beyond its picture — rooms behind windows, windows lit by night, wear,
|
|
120
|
+
* animation, staying dry — one entry a layer, `undefined` for a layer with none. Read only where
|
|
121
|
+
* this texture is a material's albedo. See `surfaceEffects.ts`.
|
|
122
|
+
*/
|
|
123
|
+
effects?: readonly (SurfaceLayerEffect | undefined)[];
|
|
106
124
|
/**
|
|
107
125
|
* `repeat` tiles the image, `clamp` stretches its edge texels.
|
|
108
126
|
*
|
|
@@ -189,11 +207,23 @@ export class SurfaceTexture {
|
|
|
189
207
|
/** Whether the sampler decodes sRGB on fetch. See `colorSpace`. */
|
|
190
208
|
private readonly srgb: boolean;
|
|
191
209
|
|
|
210
|
+
/** How many images the array holds: 1 for `createSurfaceTexture`, the list's length otherwise. */
|
|
211
|
+
readonly layers: number;
|
|
212
|
+
/** The per-layer effects table, or null for a texture given none. See `surfaceEffects.ts`. */
|
|
213
|
+
private effectsTable: WebGLTexture | null = null;
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* One image, or an array of images that must share one size (`layerSize` refuses otherwise).
|
|
217
|
+
* Uploaded unflipped either way; see the constructor body for why.
|
|
218
|
+
*/
|
|
192
219
|
constructor(
|
|
193
220
|
gl: WebGL2RenderingContext,
|
|
194
|
-
source: TexImageSource,
|
|
221
|
+
source: TexImageSource | readonly TexImageSource[],
|
|
195
222
|
options: SurfaceTextureOptions = {},
|
|
196
223
|
) {
|
|
224
|
+
const sources: readonly TexImageSource[] = isSourceList(source) ? source : [source];
|
|
225
|
+
const { width, height } = layerSize(sources);
|
|
226
|
+
this.layers = sources.length;
|
|
197
227
|
const texture = gl.createTexture();
|
|
198
228
|
if (texture === null) throw new Error('SurfaceTexture: createTexture failed');
|
|
199
229
|
this.texture = texture;
|
|
@@ -202,7 +232,7 @@ export class SurfaceTexture {
|
|
|
202
232
|
|
|
203
233
|
const wrap = (options.wrap ?? 'repeat') === 'repeat' ? gl.REPEAT : gl.CLAMP_TO_EDGE;
|
|
204
234
|
|
|
205
|
-
gl.bindTexture(gl.
|
|
235
|
+
gl.bindTexture(gl.TEXTURE_2D_ARRAY, texture);
|
|
206
236
|
/*
|
|
207
237
|
* **Not flipped, and this used to be, which is the bug it is written down for.**
|
|
208
238
|
*
|
|
@@ -224,24 +254,45 @@ export class SurfaceTexture {
|
|
|
224
254
|
*
|
|
225
255
|
* A caller that was mirroring its own canvas to cancel this must stop.
|
|
226
256
|
*/
|
|
227
|
-
|
|
228
|
-
|
|
257
|
+
/* Allocated mutable rather than with texStorage3D, so `update` can hand a one-layer texture a
|
|
258
|
+
new size, as texImage2D always could. Each layer is then written in place. */
|
|
259
|
+
gl.texImage3D(
|
|
260
|
+
gl.TEXTURE_2D_ARRAY,
|
|
229
261
|
0,
|
|
230
262
|
this.srgb ? gl.SRGB8_ALPHA8 : gl.RGBA,
|
|
263
|
+
width,
|
|
264
|
+
height,
|
|
265
|
+
sources.length,
|
|
266
|
+
0,
|
|
231
267
|
gl.RGBA,
|
|
232
268
|
gl.UNSIGNED_BYTE,
|
|
233
|
-
|
|
269
|
+
null,
|
|
234
270
|
);
|
|
271
|
+
for (let layer = 0; layer < sources.length; layer++) {
|
|
272
|
+
gl.texSubImage3D(
|
|
273
|
+
gl.TEXTURE_2D_ARRAY,
|
|
274
|
+
0,
|
|
275
|
+
0,
|
|
276
|
+
0,
|
|
277
|
+
layer,
|
|
278
|
+
width,
|
|
279
|
+
height,
|
|
280
|
+
1,
|
|
281
|
+
gl.RGBA,
|
|
282
|
+
gl.UNSIGNED_BYTE,
|
|
283
|
+
sources[layer] as TexImageSource,
|
|
284
|
+
);
|
|
285
|
+
}
|
|
235
286
|
|
|
236
|
-
gl.texParameteri(gl.
|
|
237
|
-
gl.texParameteri(gl.
|
|
287
|
+
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_WRAP_S, wrap);
|
|
288
|
+
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_WRAP_T, wrap);
|
|
238
289
|
/* Nearest where the caller said their pixels are the subject; see `filter`. The mip chain is
|
|
239
290
|
still blended between levels, because that is minification and this option is about
|
|
240
291
|
magnification. */
|
|
241
292
|
const nearest = options.filter === 'nearest';
|
|
242
|
-
gl.texParameteri(gl.
|
|
293
|
+
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MAG_FILTER, nearest ? gl.NEAREST : gl.LINEAR);
|
|
243
294
|
gl.texParameteri(
|
|
244
|
-
gl.
|
|
295
|
+
gl.TEXTURE_2D_ARRAY,
|
|
245
296
|
gl.TEXTURE_MIN_FILTER,
|
|
246
297
|
this.mipmapped
|
|
247
298
|
? nearest
|
|
@@ -252,9 +303,41 @@ export class SurfaceTexture {
|
|
|
252
303
|
: gl.LINEAR,
|
|
253
304
|
);
|
|
254
305
|
|
|
255
|
-
if (this.mipmapped) gl.generateMipmap(gl.
|
|
306
|
+
if (this.mipmapped) gl.generateMipmap(gl.TEXTURE_2D_ARRAY);
|
|
256
307
|
applyAnisotropy(gl, options.anisotropy ?? DEFAULT_ANISOTROPY);
|
|
257
|
-
gl.bindTexture(gl.
|
|
308
|
+
gl.bindTexture(gl.TEXTURE_2D_ARRAY, null);
|
|
309
|
+
|
|
310
|
+
/* Half floats, nearest, clamped: a table read by texelFetch, whose every whole number is under
|
|
311
|
+
what a half holds exactly. */
|
|
312
|
+
const effects = options.effects ?? [];
|
|
313
|
+
if (effects.length > 0) {
|
|
314
|
+
const table = gl.createTexture();
|
|
315
|
+
if (table === null) throw new Error('SurfaceTexture: createTexture failed');
|
|
316
|
+
gl.bindTexture(gl.TEXTURE_2D, table);
|
|
317
|
+
gl.texImage2D(
|
|
318
|
+
gl.TEXTURE_2D,
|
|
319
|
+
0,
|
|
320
|
+
gl.RGBA16F,
|
|
321
|
+
SURFACE_EFFECT_TEXELS,
|
|
322
|
+
effects.length,
|
|
323
|
+
0,
|
|
324
|
+
gl.RGBA,
|
|
325
|
+
gl.FLOAT,
|
|
326
|
+
packSurfaceEffects(effects),
|
|
327
|
+
);
|
|
328
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
|
329
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
|
330
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
331
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
332
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
333
|
+
this.effectsTable = table;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/** Bind the effects table to `unit`, or `fallback` for a texture given none. */
|
|
338
|
+
bindEffects(gl: WebGL2RenderingContext, unit: number, fallback: WebGLTexture): void {
|
|
339
|
+
gl.activeTexture(gl.TEXTURE0 + unit);
|
|
340
|
+
gl.bindTexture(gl.TEXTURE_2D, this.effectsTable ?? fallback);
|
|
258
341
|
}
|
|
259
342
|
|
|
260
343
|
/**
|
|
@@ -270,30 +353,38 @@ export class SurfaceTexture {
|
|
|
270
353
|
*/
|
|
271
354
|
update(gl: WebGL2RenderingContext, source: TexImageSource): void {
|
|
272
355
|
if (this.texture === null) return;
|
|
273
|
-
|
|
356
|
+
refuseArrayUpdate(this.layers);
|
|
357
|
+
const { width, height } = sourceSize(source);
|
|
358
|
+
gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.texture);
|
|
274
359
|
/* Unflipped, matching the constructor. See it for why, and for what it cost. */
|
|
275
|
-
gl.
|
|
276
|
-
gl.
|
|
360
|
+
gl.texImage3D(
|
|
361
|
+
gl.TEXTURE_2D_ARRAY,
|
|
277
362
|
0,
|
|
278
363
|
this.srgb ? gl.SRGB8_ALPHA8 : gl.RGBA,
|
|
364
|
+
width,
|
|
365
|
+
height,
|
|
366
|
+
1,
|
|
367
|
+
0,
|
|
279
368
|
gl.RGBA,
|
|
280
369
|
gl.UNSIGNED_BYTE,
|
|
281
370
|
source,
|
|
282
371
|
);
|
|
283
|
-
if (this.mipmapped) gl.generateMipmap(gl.
|
|
284
|
-
gl.bindTexture(gl.
|
|
372
|
+
if (this.mipmapped) gl.generateMipmap(gl.TEXTURE_2D_ARRAY);
|
|
373
|
+
gl.bindTexture(gl.TEXTURE_2D_ARRAY, null);
|
|
285
374
|
}
|
|
286
375
|
|
|
287
376
|
/** Bind to a unit for sampling. Engine-internal: the renderer owns unit assignment. */
|
|
288
377
|
bind(gl: WebGL2RenderingContext, unit: number): void {
|
|
289
378
|
gl.activeTexture(gl.TEXTURE0 + unit);
|
|
290
|
-
gl.bindTexture(gl.
|
|
379
|
+
gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.texture);
|
|
291
380
|
}
|
|
292
381
|
|
|
293
382
|
dispose(gl: WebGL2RenderingContext): void {
|
|
294
383
|
if (this.texture === null) return;
|
|
295
384
|
gl.deleteTexture(this.texture);
|
|
296
385
|
this.texture = null;
|
|
386
|
+
if (this.effectsTable !== null) gl.deleteTexture(this.effectsTable);
|
|
387
|
+
this.effectsTable = null;
|
|
297
388
|
}
|
|
298
389
|
}
|
|
299
390
|
|
|
@@ -311,5 +402,5 @@ function applyAnisotropy(gl: WebGL2RenderingContext, requested: number): void {
|
|
|
311
402
|
gl.getExtension('WEBKIT_EXT_texture_filter_anisotropic');
|
|
312
403
|
if (ext === null) return;
|
|
313
404
|
const max = gl.getParameter(ext.MAX_TEXTURE_MAX_ANISOTROPY_EXT) as number;
|
|
314
|
-
gl.texParameterf(gl.
|
|
405
|
+
gl.texParameterf(gl.TEXTURE_2D_ARRAY, ext.TEXTURE_MAX_ANISOTROPY_EXT, Math.min(requested, max));
|
|
315
406
|
}
|
package/src/render/temporalAa.ts
CHANGED
|
@@ -204,6 +204,49 @@ export const FLICKER_MEMORY = 1 / JITTER_PERIOD;
|
|
|
204
204
|
*/
|
|
205
205
|
export const FLICKER_WIDTH = 6;
|
|
206
206
|
|
|
207
|
+
/**
|
|
208
|
+
* The two numbers the proof takes from the jitter's period: how much of a period one frame is, and
|
|
209
|
+
* how many spreads a proven pixel's box widens by. This resolve's are `FLICKER_MEMORY` and
|
|
210
|
+
* `FLICKER_WIDTH`; a reconstruction jitters along a longer sequence and needs its own.
|
|
211
|
+
*/
|
|
212
|
+
export interface FlickerRule {
|
|
213
|
+
readonly memory: number;
|
|
214
|
+
readonly width: number;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/** The temporal resolve's own: eight phases. */
|
|
218
|
+
export const TEMPORAL_FLICKER: FlickerRule = { memory: FLICKER_MEMORY, width: FLICKER_WIDTH };
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* The running spread of the hardest thread — one caught in a single phase of `phases` — just before
|
|
222
|
+
* its catch, as a share of its contrast: the steady state `FLICKER_WIDTH`'s comment solves for eight
|
|
223
|
+
* phases, solved for any. With m one over the period and r = (1 − m)^(p − 1), the seven, or p − 1,
|
|
224
|
+
* misses that wear it down: a = (m + ((p − 1)m² − m)·r) / (1 − (1 − m)·r).
|
|
225
|
+
*/
|
|
226
|
+
function hardestSpread(phases: number): number {
|
|
227
|
+
const m = 1 / phases;
|
|
228
|
+
const r = (1 - m) ** (phases - 1);
|
|
229
|
+
return (m + ((phases - 1) * m * m - m) * r) / (1 - (1 - m) * r);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/** How many of those spreads the hardest thread's catch lands from its period's mean. */
|
|
233
|
+
function widthNeeded(phases: number): number {
|
|
234
|
+
return (1 - 1 / phases) / hardestSpread(phases);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* The rule for a jitter of `phases` positions. **The width keeps the room `FLICKER_WIDTH` leaves over
|
|
239
|
+
* what eight phases need** — 4.8 — in proportion: a longer period wears the spread down further
|
|
240
|
+
* between catches, so over eighteen phases the hardest thread needs eleven spreads, and a box that
|
|
241
|
+
* reached six reset the count of exactly the pixels this is for.
|
|
242
|
+
*/
|
|
243
|
+
export function flickerRule(phases: number): FlickerRule {
|
|
244
|
+
return {
|
|
245
|
+
memory: 1 / phases,
|
|
246
|
+
width: (FLICKER_WIDTH * widthNeeded(phases)) / widthNeeded(JITTER_PERIOD),
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
207
250
|
/**
|
|
208
251
|
* How far apart two periods' means may be, relative to the larger, and still repeat: fully below
|
|
209
252
|
* this, not at all at twice it. Above an eight-bit record's rounding and a lamp's slow breathing; far
|
|
@@ -292,9 +335,13 @@ export interface Flicker {
|
|
|
292
335
|
* record of another surface is no record**, so the resolve starts one over wherever last frame's
|
|
293
336
|
* depth says the surface is not the one it followed, before this frame widens by anything.
|
|
294
337
|
*/
|
|
295
|
-
export function freshFlicker(
|
|
338
|
+
export function freshFlicker(
|
|
339
|
+
out: Flicker,
|
|
340
|
+
sample: number,
|
|
341
|
+
rule: FlickerRule = TEMPORAL_FLICKER,
|
|
342
|
+
): Flicker {
|
|
296
343
|
out.spread = 0;
|
|
297
|
-
out.sum = sample *
|
|
344
|
+
out.sum = sample * rule.memory;
|
|
298
345
|
out.mean = sample;
|
|
299
346
|
out.repeated = 0;
|
|
300
347
|
return out;
|
|
@@ -320,23 +367,38 @@ export function stillness(motionPixels: number): number {
|
|
|
320
367
|
* lands within the reach of the box it would widen to. A sample beyond that reach is something
|
|
321
368
|
* arriving, and it disqualifies the frame it lands in as well as the count it carries out.
|
|
322
369
|
*/
|
|
323
|
-
export function proven(
|
|
324
|
-
|
|
370
|
+
export function proven(
|
|
371
|
+
record: Flicker,
|
|
372
|
+
sample: number,
|
|
373
|
+
rule: FlickerRule = TEMPORAL_FLICKER,
|
|
374
|
+
): boolean {
|
|
375
|
+
return record.repeated > 0.75 && within(record, sample, rule);
|
|
325
376
|
}
|
|
326
377
|
|
|
327
378
|
/** Whether this frame's sample lands inside the reach of the box the record would widen to. */
|
|
328
|
-
function within(record: Flicker, sample: number): boolean {
|
|
329
|
-
return Math.abs(sample - record.mean) <=
|
|
379
|
+
function within(record: Flicker, sample: number, rule: FlickerRule): boolean {
|
|
380
|
+
return Math.abs(sample - record.mean) <= rule.width * record.spread + 1 / 255;
|
|
330
381
|
}
|
|
331
382
|
|
|
332
383
|
/** How far a pixel's box widens this frame: by its spread, once proven, and only while still. */
|
|
333
|
-
export function flickerWidening(
|
|
334
|
-
|
|
384
|
+
export function flickerWidening(
|
|
385
|
+
record: Flicker,
|
|
386
|
+
sample: number,
|
|
387
|
+
still: number,
|
|
388
|
+
rule: FlickerRule = TEMPORAL_FLICKER,
|
|
389
|
+
): number {
|
|
390
|
+
return proven(record, sample, rule) ? rule.width * record.spread * still : 0;
|
|
335
391
|
}
|
|
336
392
|
|
|
337
393
|
/** How much of the history this frame keeps: the base blend, or more once proven. */
|
|
338
|
-
export function flickerBlend(
|
|
339
|
-
|
|
394
|
+
export function flickerBlend(
|
|
395
|
+
base: number,
|
|
396
|
+
record: Flicker,
|
|
397
|
+
sample: number,
|
|
398
|
+
still: number,
|
|
399
|
+
rule: FlickerRule = TEMPORAL_FLICKER,
|
|
400
|
+
): number {
|
|
401
|
+
return proven(record, sample, rule) ? base + (FLICKER_BLEND - base) * still : base;
|
|
340
402
|
}
|
|
341
403
|
|
|
342
404
|
/** Whether two periods' means are the same, one to zero. */
|
|
@@ -349,7 +411,8 @@ export function repeats(mean: number, previous: number): number {
|
|
|
349
411
|
* The record carried out, from the one carried in and this frame's sample in luma. `periodStart`
|
|
350
412
|
* is the first frame of the jitter's period, where the period just gathered is compared with the
|
|
351
413
|
* one before. Forgotten in proportion to motion — a moving pixel's record would be a record of
|
|
352
|
-
* somewhere else.
|
|
414
|
+
* somewhere else. `rule` is this resolve's eight phases unless a caller jitters along another
|
|
415
|
+
* sequence, as a reconstruction does.
|
|
353
416
|
*/
|
|
354
417
|
export function nextFlicker(
|
|
355
418
|
out: Flicker,
|
|
@@ -357,17 +420,18 @@ export function nextFlicker(
|
|
|
357
420
|
sample: number,
|
|
358
421
|
still: number,
|
|
359
422
|
periodStart: boolean,
|
|
423
|
+
rule: FlickerRule = TEMPORAL_FLICKER,
|
|
360
424
|
): Flicker {
|
|
361
425
|
const apart = Math.abs(sample - previous.mean);
|
|
362
426
|
let { sum, mean, repeated } = previous;
|
|
363
|
-
if (apart >
|
|
427
|
+
if (apart > rule.width * previous.spread + 1 / 255) repeated = 0;
|
|
364
428
|
if (periodStart) {
|
|
365
429
|
repeated = repeats(sum, mean) > 0.5 ? Math.min(1, repeated + 0.5) : 0;
|
|
366
430
|
mean = sum;
|
|
367
431
|
sum = 0;
|
|
368
432
|
}
|
|
369
|
-
out.spread = previous.spread + (apart - previous.spread) *
|
|
370
|
-
out.sum = sum + sample *
|
|
433
|
+
out.spread = previous.spread + (apart - previous.spread) * rule.memory;
|
|
434
|
+
out.sum = sum + sample * rule.memory;
|
|
371
435
|
out.mean = mean;
|
|
372
436
|
out.repeated = repeated * still;
|
|
373
437
|
return out;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The size of an image a caller hands over, and the one rule a texture array adds to it: every
|
|
3
|
+
* layer the same size.
|
|
4
|
+
*
|
|
5
|
+
* Backend-neutral, because both backends upload the same `TexImageSource` and must agree on its
|
|
6
|
+
* size, and on refusing an array whose layers disagree, before either touches a device.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export interface SourceSize {
|
|
10
|
+
readonly width: number;
|
|
11
|
+
readonly height: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Width and height of any `TexImageSource`: a video by its frame, everything else by its own size. */
|
|
15
|
+
export function sourceSize(source: TexImageSource): SourceSize {
|
|
16
|
+
const candidate = source as {
|
|
17
|
+
width?: number;
|
|
18
|
+
height?: number;
|
|
19
|
+
videoWidth?: number;
|
|
20
|
+
videoHeight?: number;
|
|
21
|
+
};
|
|
22
|
+
const width = candidate.videoWidth ?? candidate.width ?? 0;
|
|
23
|
+
const height = candidate.videoHeight ?? candidate.height ?? 0;
|
|
24
|
+
return { width: Math.max(1, width), height: Math.max(1, height) };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The one size every layer of an array shares, or a refusal naming the first that differs.
|
|
29
|
+
*
|
|
30
|
+
* **Refused rather than resized**, because an array layer has no size of its own — the device
|
|
31
|
+
* allocates one size for all of them — and quietly scaling a 128-pixel sign up to a 512 array, or a
|
|
32
|
+
* facade down, is a decision about quality the caller should make where they can see it. Sorting
|
|
33
|
+
* images into arrays by size is theirs; this makes sure they did.
|
|
34
|
+
*/
|
|
35
|
+
export function layerSize(sources: readonly TexImageSource[]): SourceSize {
|
|
36
|
+
const first = sources[0];
|
|
37
|
+
if (first === undefined) {
|
|
38
|
+
throw new Error('createSurfaceTextureArray: an array needs at least one image');
|
|
39
|
+
}
|
|
40
|
+
const size = sourceSize(first);
|
|
41
|
+
for (let layer = 1; layer < sources.length; layer++) {
|
|
42
|
+
const next = sourceSize(sources[layer] as TexImageSource);
|
|
43
|
+
if (next.width !== size.width || next.height !== size.height) {
|
|
44
|
+
throw new Error(
|
|
45
|
+
`createSurfaceTextureArray: layer ${layer} is ${next.width}×${next.height} and layer 0 is ` +
|
|
46
|
+
`${size.width}×${size.height}. Every layer of an array is one size on the device; group ` +
|
|
47
|
+
'images into arrays by size, or scale them to one, before building the array.',
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return size;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Whether a constructor argument is a list of images rather than one. */
|
|
55
|
+
export function isSourceList(
|
|
56
|
+
source: TexImageSource | readonly TexImageSource[],
|
|
57
|
+
): source is readonly TexImageSource[] {
|
|
58
|
+
return Array.isArray(source);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* `update` replaces the one image of a plain texture. An array's layers are its identity — a
|
|
63
|
+
* block's facades by index — and replacing "the image" of a forty-layer array has no single
|
|
64
|
+
* meaning, so it is refused rather than guessed at.
|
|
65
|
+
*/
|
|
66
|
+
export function refuseArrayUpdate(layers: number): void {
|
|
67
|
+
if (layers === 1) return;
|
|
68
|
+
throw new Error(
|
|
69
|
+
`updateSurfaceTexture: this texture is an array of ${layers} layers, and update replaces the ` +
|
|
70
|
+
'one image of a plain texture. Build a new array for new layers.',
|
|
71
|
+
);
|
|
72
|
+
}
|