@driftengine/core 4.8.5 → 4.8.7
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 +4392 -4250
- package/README.md +1 -1
- package/dist/index.d.ts +12 -4
- package/dist/index.js +12 -2
- package/dist/render/ambientHarmonics.d.ts +46 -0
- package/dist/render/ambientHarmonics.js +80 -0
- package/dist/render/anisotropicLobe.d.ts +10 -0
- package/dist/render/anisotropicLobe.js +19 -0
- package/dist/render/areaLights.d.ts +54 -0
- package/dist/render/areaLights.js +55 -0
- package/dist/render/backend/api.d.ts +43 -2
- package/dist/render/backend/webgl2/instanced.d.ts +2 -0
- package/dist/render/backend/webgl2/instanced.js +11 -2
- package/dist/render/backend/webgl2/renderer.d.ts +120 -11
- package/dist/render/backend/webgl2/renderer.js +356 -53
- package/dist/render/backend/webgl2/sceneCaptureTarget.d.ts +29 -0
- package/dist/render/backend/webgl2/sceneCaptureTarget.js +96 -0
- package/dist/render/backend/webgl2/skinScatter.d.ts +14 -0
- package/dist/render/backend/webgl2/skinScatter.js +88 -26
- package/dist/render/backend/webgpu/buffers.js +2 -1
- package/dist/render/backend/webgpu/clusterBinner.js +3 -2
- package/dist/render/backend/webgpu/flatPass.d.ts +7 -5
- package/dist/render/backend/webgpu/flatPass.js +15 -13
- package/dist/render/backend/webgpu/particlePass.d.ts +37 -4
- package/dist/render/backend/webgpu/particlePass.js +81 -12
- package/dist/render/backend/webgpu/postPass.d.ts +6 -1
- package/dist/render/backend/webgpu/postPass.js +7 -2
- package/dist/render/backend/webgpu/renderer.d.ts +93 -8
- package/dist/render/backend/webgpu/renderer.js +483 -93
- package/dist/render/backend/webgpu/sceneCaptureTarget.d.ts +24 -0
- package/dist/render/backend/webgpu/sceneCaptureTarget.js +59 -0
- package/dist/render/backend/webgpu/shaders/clusterBin.wgsl.d.ts +1 -1
- package/dist/render/backend/webgpu/shaders/clusterBin.wgsl.js +41 -1
- package/dist/render/backend/webgpu/skinPaletteRing.d.ts +40 -28
- package/dist/render/backend/webgpu/skinPaletteRing.js +69 -32
- package/dist/render/backend/webgpu/skinScatterPass.d.ts +10 -2
- package/dist/render/backend/webgpu/skinScatterPass.js +59 -9
- package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +5 -1
- package/dist/render/backend/webgpu/surfaceTexturePass.js +45 -5
- package/dist/render/backend/webgpu/waterPass.d.ts +1 -1
- package/dist/render/bloomChain.d.ts +44 -0
- package/dist/render/bloomChain.js +28 -0
- package/dist/render/bloomPass.d.ts +3 -1
- package/dist/render/bloomPass.js +16 -2
- package/dist/render/clusteredLights.d.ts +78 -6
- package/dist/render/clusteredLights.js +214 -13
- package/dist/render/driftLight/bake.d.ts +2 -0
- package/dist/render/driftLight/bake.js +1 -1
- package/dist/render/driftLight/falloff.d.ts +4 -2
- package/dist/render/driftLight/falloff.js +8 -2
- package/dist/render/fixtureAtlas.js +1 -1
- package/dist/render/gi/bounceLights.d.ts +3 -2
- package/dist/render/gi/bounceLights.js +8 -3
- package/dist/render/instanceCull.js +12 -0
- package/dist/render/instances.d.ts +47 -11
- package/dist/render/instances.js +24 -9
- package/dist/render/lightBudget.d.ts +38 -2
- package/dist/render/lightBudget.js +48 -5
- package/dist/render/lightmap.d.ts +116 -0
- package/dist/render/lightmap.js +140 -0
- package/dist/render/mesh.js +2 -1
- package/dist/render/particleBatch.d.ts +59 -3
- package/dist/render/particleBatch.js +53 -2
- package/dist/render/particleMaterial.d.ts +6 -1
- package/dist/render/particleMaterial.js +10 -2
- package/dist/render/particlePool.d.ts +32 -0
- package/dist/render/particlePool.js +9 -0
- package/dist/render/particleSort.d.ts +34 -0
- package/dist/render/particleSort.js +96 -0
- package/dist/render/pointLightSelection.d.ts +13 -0
- package/dist/render/pointLightSelection.js +3 -1
- package/dist/render/renderQuality.d.ts +8 -7
- package/dist/render/rendererHarness.js +3 -0
- package/dist/render/rgb9e5.d.ts +22 -0
- package/dist/render/rgb9e5.js +47 -0
- package/dist/render/sceneCapture.d.ts +29 -0
- package/dist/render/sceneCapture.js +36 -0
- package/dist/render/sceneTarget.d.ts +5 -0
- package/dist/render/sceneTarget.js +8 -1
- package/dist/render/screenSpaceReflection.d.ts +27 -1
- package/dist/render/screenSpaceReflection.js +9 -0
- package/dist/render/shaders/bloom.d.ts +8 -4
- package/dist/render/shaders/bloom.js +20 -3
- package/dist/render/shaders/bolt.d.ts +1 -1
- package/dist/render/shaders/bolt.js +7 -1
- package/dist/render/shaders/depth.js +11 -0
- package/dist/render/shaders/flat/anisotropic.d.ts +1 -1
- package/dist/render/shaders/flat/anisotropic.js +37 -3
- package/dist/render/shaders/flat/areaLight.d.ts +10 -0
- package/dist/render/shaders/flat/areaLight.js +142 -0
- package/dist/render/shaders/flat/glassTint.d.ts +3 -2
- package/dist/render/shaders/flat/glassTint.js +3 -1
- package/dist/render/shaders/flat/index.d.ts +5 -0
- package/dist/render/shaders/flat/index.js +46 -6
- package/dist/render/shaders/flat/lobes.d.ts +6 -0
- package/dist/render/shaders/flat/lobes.js +49 -43
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +158 -121
- package/dist/render/shaders/flat/models.d.ts +21 -6
- package/dist/render/shaders/flat/models.js +161 -17
- package/dist/render/shaders/flat/normalMap.d.ts +10 -0
- package/dist/render/shaders/flat/normalMap.js +25 -0
- package/dist/render/shaders/flat/preamble.js +84 -40
- package/dist/render/shaders/flat/skin.js +6 -5
- package/dist/render/shaders/generated/bloom.wgsl.d.ts +13 -3
- package/dist/render/shaders/generated/bloom.wgsl.js +13 -3
- package/dist/render/shaders/generated/bolt.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/bolt.wgsl.js +1 -1
- package/dist/render/shaders/generated/depth.wgsl.d.ts +4 -4
- package/dist/render/shaders/generated/depth.wgsl.js +4 -4
- package/dist/render/shaders/generated/flat.wgsl.d.ts +1647 -2044
- package/dist/render/shaders/generated/flat.wgsl.js +1685 -2082
- package/dist/render/shaders/generated/particle.wgsl.d.ts +115 -1
- package/dist/render/shaders/generated/particle.wgsl.js +115 -1
- package/dist/render/shaders/generated/skinBlur.wgsl.d.ts +12 -2
- package/dist/render/shaders/generated/skinBlur.wgsl.js +12 -2
- package/dist/render/shaders/generated/ssrTrace.wgsl.d.ts +11 -6
- package/dist/render/shaders/generated/ssrTrace.wgsl.js +11 -6
- package/dist/render/shaders/generated/water.wgsl.d.ts +23 -16
- package/dist/render/shaders/generated/water.wgsl.js +23 -16
- package/dist/render/shaders/gi/probeBake.wgsl.js +9 -5
- package/dist/render/shaders/particle.d.ts +17 -1
- package/dist/render/shaders/particle.js +133 -1
- package/dist/render/shaders/recon/motion.wgsl.d.ts +1 -1
- package/dist/render/shaders/recon/motion.wgsl.js +13 -2
- package/dist/render/shaders/skinBlur.js +25 -3
- package/dist/render/shaders/ssrTrace.js +20 -1
- package/dist/render/shaders/water.d.ts +1 -1
- package/dist/render/shaders/water.js +12 -3
- package/dist/render/skinBlur.d.ts +8 -5
- package/dist/render/skinBlur.js +8 -5
- package/dist/render/skinScatter.d.ts +3 -1
- package/dist/render/skinScatter.js +3 -1
- package/dist/render/ssrPass.js +1 -0
- package/dist/render/surfaceModel.d.ts +49 -8
- package/dist/render/surfaceModel.js +54 -8
- package/dist/render/surfaceTexture.d.ts +38 -1
- package/dist/render/surfaceTexture.js +37 -2
- package/package.json +6 -6
- package/src/index.ts +26 -3
- package/src/render/ambientHarmonics.ts +92 -0
- package/src/render/anisotropicLobe.ts +29 -0
- package/src/render/areaLights.ts +97 -0
- package/src/render/backend/api.ts +61 -1
- package/src/render/backend/webgl2/instanced.ts +12 -2
- package/src/render/backend/webgl2/renderer.ts +470 -59
- package/src/render/backend/webgl2/sceneCaptureTarget.ts +130 -0
- package/src/render/backend/webgl2/skinScatter.ts +120 -43
- package/src/render/backend/webgpu/buffers.ts +2 -1
- package/src/render/backend/webgpu/clusterBinner.ts +3 -1
- package/src/render/backend/webgpu/flatPass.ts +18 -13
- package/src/render/backend/webgpu/particlePass.ts +119 -12
- package/src/render/backend/webgpu/postPass.ts +7 -2
- package/src/render/backend/webgpu/renderer.ts +589 -113
- package/src/render/backend/webgpu/sceneCaptureTarget.ts +63 -0
- package/src/render/backend/webgpu/shaders/clusterBin.wgsl.ts +41 -1
- package/src/render/backend/webgpu/skinPaletteRing.ts +65 -33
- package/src/render/backend/webgpu/skinScatterPass.ts +59 -9
- package/src/render/backend/webgpu/surfaceTexturePass.ts +57 -6
- package/src/render/bloomChain.ts +69 -0
- package/src/render/bloomPass.ts +26 -2
- package/src/render/clusteredLights.ts +259 -12
- package/src/render/driftLight/bake.ts +3 -1
- package/src/render/driftLight/falloff.ts +13 -2
- package/src/render/fixtureAtlas.ts +1 -1
- package/src/render/gi/bounceLights.ts +12 -3
- package/src/render/instanceCull.ts +9 -0
- package/src/render/instances.ts +61 -12
- package/src/render/lightBudget.ts +88 -4
- package/src/render/lightmap.ts +243 -0
- package/src/render/mesh.ts +2 -1
- package/src/render/particleBatch.ts +95 -4
- package/src/render/particleMaterial.ts +11 -1
- package/src/render/particlePool.ts +36 -0
- package/src/render/particleSort.ts +133 -0
- package/src/render/pointLightSelection.ts +20 -1
- package/src/render/renderQuality.ts +8 -7
- package/src/render/rendererHarness.ts +3 -0
- package/src/render/rgb9e5.ts +52 -0
- package/src/render/sceneCapture.ts +56 -0
- package/src/render/sceneTarget.ts +18 -1
- package/src/render/screenSpaceReflection.ts +33 -1
- package/src/render/shaders/bloom.ts +20 -3
- package/src/render/shaders/bolt.ts +7 -1
- package/src/render/shaders/depth.ts +11 -0
- package/src/render/shaders/flat/anisotropic.ts +37 -3
- package/src/render/shaders/flat/areaLight.ts +142 -0
- package/src/render/shaders/flat/glassTint.ts +9 -2
- package/src/render/shaders/flat/index.ts +51 -6
- package/src/render/shaders/flat/lobes.ts +50 -43
- package/src/render/shaders/flat/main.ts +158 -121
- package/src/render/shaders/flat/models.ts +170 -21
- package/src/render/shaders/flat/normalMap.ts +25 -0
- package/src/render/shaders/flat/preamble.ts +84 -40
- package/src/render/shaders/flat/skin.ts +6 -5
- package/src/render/shaders/generated/bloom.wgsl.ts +13 -3
- package/src/render/shaders/generated/bolt.wgsl.ts +1 -1
- package/src/render/shaders/generated/depth.wgsl.ts +4 -4
- package/src/render/shaders/generated/flat.wgsl.ts +1685 -2082
- package/src/render/shaders/generated/particle.wgsl.ts +116 -1
- package/src/render/shaders/generated/skinBlur.wgsl.ts +12 -2
- package/src/render/shaders/generated/ssrTrace.wgsl.ts +11 -6
- package/src/render/shaders/generated/water.wgsl.ts +23 -16
- package/src/render/shaders/gi/probeBake.wgsl.ts +9 -5
- package/src/render/shaders/particle.ts +134 -1
- package/src/render/shaders/recon/motion.wgsl.ts +13 -2
- package/src/render/shaders/skinBlur.ts +25 -3
- package/src/render/shaders/ssrTrace.ts +20 -1
- package/src/render/shaders/water.ts +12 -3
- package/src/render/skinBlur.ts +8 -5
- package/src/render/skinScatter.ts +3 -1
- package/src/render/ssrPass.ts +1 -0
- package/src/render/surfaceModel.ts +82 -10
- package/src/render/surfaceTexture.ts +101 -3
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { SurfaceTextureOptions } from '../../surfaceTexture.ts';
|
|
2
|
+
import type { LightmapTexels } from '../../lightmap.ts';
|
|
3
|
+
import type { SceneCaptureTexels } from '../../sceneCapture.ts';
|
|
2
4
|
import type { CompressedTextureFormat, SurfaceSource } from '../../compressedSource.ts';
|
|
3
5
|
import type { PipelineCache } from './pipelineCache.ts';
|
|
4
6
|
/** How many levels an image of this size has, counting the one it arrives with. */
|
|
@@ -62,11 +64,13 @@ export declare class GpuSurfaceTexture {
|
|
|
62
64
|
effectsView: GPUTextureView | null;
|
|
63
65
|
/** The block format a compressed texture holds, or null for one uploaded from images. */
|
|
64
66
|
private readonly blockFormat;
|
|
65
|
-
constructor(device: GPUDevice, pipelines: PipelineCache, source: SurfaceSource | readonly SurfaceSource[], options?: SurfaceTextureOptions, compressed?: readonly CompressedTextureFormat[]);
|
|
67
|
+
constructor(device: GPUDevice, pipelines: PipelineCache, source: SurfaceSource | readonly SurfaceSource[] | LightmapTexels | SceneCaptureTexels, options?: SurfaceTextureOptions, compressed?: readonly CompressedTextureFormat[]);
|
|
66
68
|
/** A view of every layer, as the shaders' `texture_2d_array` bindings read it. */
|
|
67
69
|
private arrayView;
|
|
68
70
|
/** The view every binding of this image holds. A new one when `update` changes the size. */
|
|
69
71
|
get view(): GPUTextureView;
|
|
72
|
+
/** The only layer as a plain 2D view, which a scene capture's pass resolves into. */
|
|
73
|
+
layerView(): GPUTextureView | null;
|
|
70
74
|
/**
|
|
71
75
|
* Replace the pixels, keeping the handle and its sampler.
|
|
72
76
|
*
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { SURFACE_EFFECT_TEXELS, packSurfaceEffects } from '../../surfaceEffects.js';
|
|
2
2
|
import { toHalfFloats } from '../../halfFloat.js';
|
|
3
|
+
import { isLightmapTexels } from '../../lightmap.js';
|
|
4
|
+
import { isSceneCaptureTexels } from '../../sceneCapture.js';
|
|
3
5
|
import { layerSize, refuseArrayUpdate, sourceSize } from '../../textureSource.js';
|
|
4
6
|
import { compressedLayers, planBlocks, refuseBlockUpdate } from '../../compressedSource.js';
|
|
5
7
|
import { createCompressedTexture } from './compressedUpload.js';
|
|
@@ -155,13 +157,46 @@ export class GpuSurfaceTexture {
|
|
|
155
157
|
constructor(device, pipelines, source, options = {}, compressed = []) {
|
|
156
158
|
this.device = device;
|
|
157
159
|
this.pipelines = pipelines;
|
|
158
|
-
|
|
160
|
+
/* A scene capture's target: one empty layer the world is drawn into. See `sceneCapture.ts`. */
|
|
161
|
+
const capture = isSceneCaptureTexels(source) ? source : null;
|
|
162
|
+
/* A lightmap's page: two layers of bytes, uploaded as they are. See `lightmap.ts`. */
|
|
163
|
+
const baked = isLightmapTexels(source) ? source : null;
|
|
164
|
+
const listed = (baked !== null || capture !== null ? [] : Array.isArray(source) ? source : [source]);
|
|
159
165
|
const blocks = compressedLayers(listed);
|
|
160
166
|
const srgb = (options.colorSpace ?? 'linear') === 'srgb';
|
|
161
167
|
/* `SRGB8_ALPHA8`'s equivalent. Decoded in the sampler, before filtering, which is the only
|
|
162
|
-
place it is correct — `surfaceTexture.ts` makes the argument in full.
|
|
163
|
-
|
|
164
|
-
|
|
168
|
+
place it is correct — `surfaceTexture.ts` makes the argument in full. A capture takes the
|
|
169
|
+
world's own format, which its pass resolves into. */
|
|
170
|
+
this.format =
|
|
171
|
+
capture !== null
|
|
172
|
+
? pipelines.format
|
|
173
|
+
: srgb && baked === null
|
|
174
|
+
? 'rgba8unorm-srgb'
|
|
175
|
+
: 'rgba8unorm';
|
|
176
|
+
if (capture !== null) {
|
|
177
|
+
this.layers = 1;
|
|
178
|
+
this.blockFormat = null;
|
|
179
|
+
this.mipmapped = false;
|
|
180
|
+
this.width = capture.width;
|
|
181
|
+
this.height = capture.height;
|
|
182
|
+
this.levels = 1;
|
|
183
|
+
this.texture = this.allocate();
|
|
184
|
+
}
|
|
185
|
+
else if (baked !== null) {
|
|
186
|
+
/* No chain: a page is read at its own resolution, and a level of it would blend regions. */
|
|
187
|
+
this.layers = baked.layers.length;
|
|
188
|
+
this.blockFormat = null;
|
|
189
|
+
this.mipmapped = false;
|
|
190
|
+
this.width = baked.width;
|
|
191
|
+
this.height = baked.height;
|
|
192
|
+
this.levels = 1;
|
|
193
|
+
this.texture = this.allocate();
|
|
194
|
+
/* A layer at a time, so a page's arrays are uploaded where they lie rather than joined. */
|
|
195
|
+
for (let layer = 0; layer < this.layers; layer++) {
|
|
196
|
+
device.queue.writeTexture({ texture: this.texture, origin: [0, 0, layer] }, baked.layers[layer], { bytesPerRow: baked.width * 4, rowsPerImage: baked.height }, { width: baked.width, height: baked.height, depthOrArrayLayers: 1 });
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
else if (blocks !== null) {
|
|
165
200
|
/* Refused before anything is allocated; the stored chain, or level 0 alone where no chain
|
|
166
201
|
was asked for. See `compressedUpload.ts`. */
|
|
167
202
|
const plan = planBlocks(blocks, srgb, compressed);
|
|
@@ -216,8 +251,9 @@ export class GpuSurfaceTexture {
|
|
|
216
251
|
? Math.max(1, Math.floor(options.anisotropy ?? DEFAULT_ANISOTROPY))
|
|
217
252
|
: 1,
|
|
218
253
|
});
|
|
219
|
-
if (blocks === null)
|
|
254
|
+
if (blocks === null && baked === null && capture === null) {
|
|
220
255
|
this.upload(listed);
|
|
256
|
+
}
|
|
221
257
|
/* Half floats, as `renderer.ts` uploads it: filterable, so the ordinary float layout takes it,
|
|
222
258
|
and read by `textureLoad` alone, so the filter never runs. */
|
|
223
259
|
const effects = options.effects ?? [];
|
|
@@ -241,6 +277,10 @@ export class GpuSurfaceTexture {
|
|
|
241
277
|
get view() {
|
|
242
278
|
return this.current;
|
|
243
279
|
}
|
|
280
|
+
/** The only layer as a plain 2D view, which a scene capture's pass resolves into. */
|
|
281
|
+
layerView() {
|
|
282
|
+
return (this.texture?.createView({ dimension: '2d', baseArrayLayer: 0, arrayLayerCount: 1 }) ?? null);
|
|
283
|
+
}
|
|
244
284
|
/**
|
|
245
285
|
* Replace the pixels, keeping the handle and its sampler.
|
|
246
286
|
*
|
|
@@ -11,7 +11,7 @@ import type { UniformFields } from './scatterPass.ts';
|
|
|
11
11
|
export declare const WATER_VERT_FIELDS: UniformFields;
|
|
12
12
|
export declare const WATER_VERT_SIZE: 208;
|
|
13
13
|
export declare const WATER_FRAG_FIELDS: UniformFields;
|
|
14
|
-
export declare const WATER_FRAG_SIZE:
|
|
14
|
+
export declare const WATER_FRAG_SIZE: 1504;
|
|
15
15
|
/** The two meshes and the numbers the grid decided, behind the surface's opaque handle. */
|
|
16
16
|
export interface GpuWater {
|
|
17
17
|
/** The ocean: near sheet and far skirt in one buffer. */
|
|
@@ -57,6 +57,50 @@ export declare function bloomProfileWarning(quality: {
|
|
|
57
57
|
readonly hdrScene: boolean;
|
|
58
58
|
readonly bloomThreshold: number;
|
|
59
59
|
}): string | null;
|
|
60
|
+
/**
|
|
61
|
+
* How a frame's bloom answers the light it is given, beside the strength and threshold `setBloom`
|
|
62
|
+
* already takes. Absent, or a field absent, is the response every frame had before 4.8.6.
|
|
63
|
+
*
|
|
64
|
+
* **Why it exists: one band over the colour less the threshold washes a stage built for another
|
|
65
|
+
* engine.** Unreal's standard bloom keeps `saturate((L − threshold) / 2)` of a colour rather than
|
|
66
|
+
* the colour less the threshold, and spreads it over six bands, each in a tint of its own. A
|
|
67
|
+
* stage authored against that — a threshold of 0.1 and tints summing below one — came out white
|
|
68
|
+
* here, because subtracting so low a threshold keeps nearly every light in the frame at full
|
|
69
|
+
* strength. These are the two things that differ, as numbers.
|
|
70
|
+
*/
|
|
71
|
+
export interface BloomResponse {
|
|
72
|
+
/**
|
|
73
|
+
* Scene units over which a colour comes in, from none of it at the threshold to all of it this
|
|
74
|
+
* far past. **0, the default, subtracts instead**: a colour keeps what it has past the threshold,
|
|
75
|
+
* the response since bloom shipped. Unreal's is 2 after exposure, so a frame exposed by `e` that
|
|
76
|
+
* wants it passes `2 / e`, as it passes the threshold over the exposure.
|
|
77
|
+
*
|
|
78
|
+
* Brightness is still the largest channel rather than luminance, for the reason
|
|
79
|
+
* `shaders/bloom.ts` gives; on a neutral colour the two are equal.
|
|
80
|
+
*/
|
|
81
|
+
readonly ramp?: number;
|
|
82
|
+
/**
|
|
83
|
+
* Each level's colour, three numbers a level, finest first: the halo at a few pixels first and
|
|
84
|
+
* at a twentieth of the frame last. **White, the default, is the identity**; a level not named
|
|
85
|
+
* takes white. Not clamped above one, which is how a tint carries a strength past the 0..1 the
|
|
86
|
+
* dial gives — a stage asking for twice the light names tints twice as bright.
|
|
87
|
+
*
|
|
88
|
+
* At most `BLOOM_LEVELS` levels. A frame too small for that many uses the first it has room for.
|
|
89
|
+
*/
|
|
90
|
+
readonly tints?: ArrayLike<number>;
|
|
91
|
+
}
|
|
92
|
+
/** Floats `resolveBloomResponse` writes: the ramp, then a tint a level. */
|
|
93
|
+
export declare const BLOOM_RESPONSE_FLOATS: number;
|
|
94
|
+
/**
|
|
95
|
+
* A response as both backends bind it, into a caller-owned `out` of `BLOOM_RESPONSE_FLOATS`: the
|
|
96
|
+
* ramp, then `BLOOM_LEVELS` tints, white where none was named. Refuses a tint list that is not
|
|
97
|
+
* whole colours or names more levels than the pyramid has, naming the length — a caller error with
|
|
98
|
+
* one correct outcome. A ramp that is not a positive number is the subtraction; a tint channel
|
|
99
|
+
* that is not a number or is below zero is zero.
|
|
100
|
+
*/
|
|
101
|
+
export declare function resolveBloomResponse(response: BloomResponse | null | undefined, out: Float32Array): void;
|
|
102
|
+
/** The response every frame had before 4.8.6: the subtraction, every level white. */
|
|
103
|
+
export declare function defaultBloomResponse(): Float32Array;
|
|
60
104
|
/** One level of the pyramid: level 0 is half the frame and each one after it half again. */
|
|
61
105
|
export interface BloomLevelSize {
|
|
62
106
|
readonly width: number;
|
|
@@ -67,6 +67,34 @@ export function bloomProfileWarning(quality) {
|
|
|
67
67
|
}
|
|
68
68
|
return null;
|
|
69
69
|
}
|
|
70
|
+
/** Floats `resolveBloomResponse` writes: the ramp, then a tint a level. */
|
|
71
|
+
export const BLOOM_RESPONSE_FLOATS = 1 + BLOOM_LEVELS * 3;
|
|
72
|
+
/**
|
|
73
|
+
* A response as both backends bind it, into a caller-owned `out` of `BLOOM_RESPONSE_FLOATS`: the
|
|
74
|
+
* ramp, then `BLOOM_LEVELS` tints, white where none was named. Refuses a tint list that is not
|
|
75
|
+
* whole colours or names more levels than the pyramid has, naming the length — a caller error with
|
|
76
|
+
* one correct outcome. A ramp that is not a positive number is the subtraction; a tint channel
|
|
77
|
+
* that is not a number or is below zero is zero.
|
|
78
|
+
*/
|
|
79
|
+
export function resolveBloomResponse(response, out) {
|
|
80
|
+
const ramp = response?.ramp ?? 0;
|
|
81
|
+
out[0] = Number.isFinite(ramp) && ramp > 0 ? ramp : 0;
|
|
82
|
+
const tints = response?.tints;
|
|
83
|
+
if (tints !== undefined && (tints.length % 3 !== 0 || tints.length > BLOOM_LEVELS * 3)) {
|
|
84
|
+
throw new Error(`setBloom: ${tints.length} tint values is not up to ${BLOOM_LEVELS} whole colours, three ` +
|
|
85
|
+
'numbers a level, finest first');
|
|
86
|
+
}
|
|
87
|
+
for (let i = 0; i < BLOOM_LEVELS * 3; i++) {
|
|
88
|
+
const value = tints !== undefined && i < tints.length ? tints[i] : 1;
|
|
89
|
+
out[1 + i] = Number.isFinite(value) && value > 0 ? value : 0;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/** The response every frame had before 4.8.6: the subtraction, every level white. */
|
|
93
|
+
export function defaultBloomResponse() {
|
|
94
|
+
const out = new Float32Array(BLOOM_RESPONSE_FLOATS);
|
|
95
|
+
resolveBloomResponse(null, out);
|
|
96
|
+
return out;
|
|
97
|
+
}
|
|
70
98
|
/**
|
|
71
99
|
* The pyramid for a frame of this size, largest first, or an empty list where none would fit.
|
|
72
100
|
*
|
|
@@ -46,7 +46,9 @@ export declare class BloomPass {
|
|
|
46
46
|
* `threshold` is in scene units, so it only separates a light from white paint on a target
|
|
47
47
|
* that kept the range. The caller owns that decision and the warning that goes with it.
|
|
48
48
|
*/
|
|
49
|
-
run(scene: WebGLTexture, width: number, height: number, threshold: number
|
|
49
|
+
run(scene: WebGLTexture, width: number, height: number, threshold: number,
|
|
50
|
+
/** The ramp, then each level's tint: `resolveBloomResponse`'s floats. */
|
|
51
|
+
response: Float32Array): WebGLTexture | null;
|
|
50
52
|
/** One source texel in UV, which is what the thirteen taps are laid out in. */
|
|
51
53
|
private uploadTexel;
|
|
52
54
|
private release;
|
package/dist/render/bloomPass.js
CHANGED
|
@@ -130,7 +130,9 @@ export class BloomPass {
|
|
|
130
130
|
* `threshold` is in scene units, so it only separates a light from white paint on a target
|
|
131
131
|
* that kept the range. The caller owns that decision and the warning that goes with it.
|
|
132
132
|
*/
|
|
133
|
-
run(scene, width, height, threshold
|
|
133
|
+
run(scene, width, height, threshold,
|
|
134
|
+
/** The ramp, then each level's tint: `resolveBloomResponse`'s floats. */
|
|
135
|
+
response) {
|
|
134
136
|
if (!this.ensureSize(width, height))
|
|
135
137
|
return null;
|
|
136
138
|
const { gl } = this;
|
|
@@ -153,6 +155,7 @@ export class BloomPass {
|
|
|
153
155
|
gl.bindTexture(gl.TEXTURE_2D, scene);
|
|
154
156
|
gl.uniform1i(this.prefilterUniforms['uSource'] ?? null, 0);
|
|
155
157
|
gl.uniform1f(this.prefilterUniforms['uThreshold'] ?? null, threshold);
|
|
158
|
+
gl.uniform1f(this.prefilterUniforms['uRamp'] ?? null, response[0] ?? 0);
|
|
156
159
|
this.uploadTexel(this.prefilterUniforms, width, height);
|
|
157
160
|
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
|
158
161
|
/* Down the chain. Each level reads the one above it, which was written a draw ago. */
|
|
@@ -174,8 +177,13 @@ export class BloomPass {
|
|
|
174
177
|
* every octave at once. Additive blending rather than a second target to ping-pong through:
|
|
175
178
|
* a level is only ever read while a *different* level is bound, so there is no feedback.
|
|
176
179
|
*/
|
|
180
|
+
/*
|
|
181
|
+
* Each level's tint rides the blend: the level being added onto is multiplied by its own as the
|
|
182
|
+
* octave below is added, and the deepest by `uScale` on the first draw, the only one reading it
|
|
183
|
+
* before anything has been added to it. White everywhere is `ONE, ONE` exactly.
|
|
184
|
+
*/
|
|
177
185
|
gl.enable(gl.BLEND);
|
|
178
|
-
gl.blendFunc(gl.ONE, gl.
|
|
186
|
+
gl.blendFunc(gl.ONE, gl.CONSTANT_COLOR);
|
|
179
187
|
gl.useProgram(this.upsampleProgram);
|
|
180
188
|
gl.uniform1i(this.upsampleUniforms['uSource'] ?? null, 0);
|
|
181
189
|
gl.uniform1f(this.upsampleUniforms['uRadius'] ?? null, BLOOM_FILTER_RADIUS_UV);
|
|
@@ -184,11 +192,17 @@ export class BloomPass {
|
|
|
184
192
|
const source = levels[index];
|
|
185
193
|
if (target === undefined || source === undefined)
|
|
186
194
|
break;
|
|
195
|
+
const deepest = index === levels.length - 1;
|
|
196
|
+
const into = 1 + (index - 1) * 3;
|
|
197
|
+
const from = 1 + index * 3;
|
|
198
|
+
gl.blendColor(response[into] ?? 1, response[into + 1] ?? 1, response[into + 2] ?? 1, 1);
|
|
199
|
+
gl.uniform3f(this.upsampleUniforms['uScale'] ?? null, deepest ? (response[from] ?? 1) : 1, deepest ? (response[from + 1] ?? 1) : 1, deepest ? (response[from + 2] ?? 1) : 1);
|
|
187
200
|
gl.bindFramebuffer(gl.FRAMEBUFFER, target.framebuffer);
|
|
188
201
|
gl.viewport(0, 0, target.width, target.height);
|
|
189
202
|
gl.bindTexture(gl.TEXTURE_2D, source.texture);
|
|
190
203
|
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
|
191
204
|
}
|
|
205
|
+
gl.blendColor(0, 0, 0, 0);
|
|
192
206
|
gl.disable(gl.BLEND);
|
|
193
207
|
gl.bindVertexArray(null);
|
|
194
208
|
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { AreaLightBuffer } from './areaLights.ts';
|
|
1
2
|
/**
|
|
2
3
|
* The froxel table: what it looks like in memory, and the CPU binner that fills it.
|
|
3
4
|
*
|
|
@@ -84,7 +85,7 @@ export declare const MAX_CLUSTERED_LIGHTS = 320;
|
|
|
84
85
|
* it cannot. That is what lets a light index the shadow uniforms by a number read from a texture,
|
|
85
86
|
* and it is the same fact the octahedral array texture was built on.
|
|
86
87
|
*/
|
|
87
|
-
export declare const LIGHT_TEXELS =
|
|
88
|
+
export declare const LIGHT_TEXELS = 6;
|
|
88
89
|
/**
|
|
89
90
|
* Where each field of a light's record sits, as an offset in `uint`s from `lightBase`.
|
|
90
91
|
*
|
|
@@ -114,7 +115,11 @@ export declare const LIGHT_TEXELS = 5;
|
|
|
114
115
|
* **Reordered 2026-10-02 so a plain light's fields are the first eight**, which is what lets the
|
|
115
116
|
* shader stop after two texels. Nine values are needed by every light and eight fit in two
|
|
116
117
|
* texels, so the emitter size and the weight share slot 7 as two halves; see `packSizeAndWeight`.
|
|
117
|
-
*
|
|
118
|
+
*
|
|
119
|
+
* **A sixth texel arrived in 4.8.7 for a light's own falloff exponent**, the fifth's last slot
|
|
120
|
+
* having gone to the channels: 6 KB more of a table near 300, read only for a light whose record
|
|
121
|
+
* carries a fixture, which a light with an exponent does. Slots 21 and 22 are a rectangle's barn
|
|
122
|
+
* doors and spare on a lamp; slot 23 is spare.
|
|
118
123
|
*/
|
|
119
124
|
export declare const LIGHT_RECORD: {
|
|
120
125
|
readonly positionX: 0;
|
|
@@ -139,11 +144,36 @@ export declare const LIGHT_RECORD: {
|
|
|
139
144
|
readonly iesAxisX: 16;
|
|
140
145
|
readonly iesAxisY: 17;
|
|
141
146
|
readonly iesAxisZ: 18;
|
|
147
|
+
/**
|
|
148
|
+
* The light's lighting channels, a mask (`PointLightSet.lightChannels`), in the slot that was
|
|
149
|
+
* spare. A light naming channels other than the default is read as carrying a fixture, so its
|
|
150
|
+
* last texels are fetched; a plain light's are not, and the shader takes the default for it.
|
|
151
|
+
*/
|
|
152
|
+
readonly channels: 19;
|
|
153
|
+
/** The light's own falloff exponent, or 0 for the frame's falloff. `PointLightSource.falloffExponent`. */
|
|
154
|
+
readonly falloffExponent: 20;
|
|
155
|
+
/** A rectangle's barn doors: the cosine of their angle from its normal, then their length in metres. */
|
|
156
|
+
readonly barnDoorCos: 21;
|
|
157
|
+
readonly barnDoorLength: 22;
|
|
142
158
|
};
|
|
159
|
+
/** The lighting channel every light and every surface is on unless it says otherwise. */
|
|
160
|
+
export declare const DEFAULT_LIGHT_CHANNELS = 1;
|
|
161
|
+
/**
|
|
162
|
+
* A light's channels as the shader compares them: a whole mask from 1 to 255, or the default where
|
|
163
|
+
* none was named or what was named is not one. The froxel record and the uniform path both write
|
|
164
|
+
* this, so a light named badly is on channel 1 in both rather than on two different guesses.
|
|
165
|
+
*/
|
|
166
|
+
export declare function lightChannelsOf(value: number | undefined): number;
|
|
167
|
+
/**
|
|
168
|
+
* A light's own falloff exponent as the shader reads it: a positive finite number, or 0 for the
|
|
169
|
+
* frame's falloff where none was named or what was named is not one. The record and the uniform
|
|
170
|
+
* path both write this, so a light named badly falls off by the frame's rule in both.
|
|
171
|
+
*/
|
|
172
|
+
export declare function lightFalloffExponentOf(value: number | undefined): number;
|
|
143
173
|
/** Texels the lit pass reads for a light whose record carries no fixture. */
|
|
144
174
|
export declare const PLAIN_LIGHT_TEXELS = 2;
|
|
145
175
|
/**
|
|
146
|
-
* The bit of `sizeAndWeight` that says the record's last
|
|
176
|
+
* The bit of `sizeAndWeight` that says the record's last four texels hold something: the sign of
|
|
147
177
|
* the emitter size's half, which a size never needs.
|
|
148
178
|
*/
|
|
149
179
|
export declare const LIGHT_FIXTURE_FLAG = 32768;
|
|
@@ -270,6 +300,18 @@ export interface ClusterLightSet {
|
|
|
270
300
|
readonly cookies?: Float32Array;
|
|
271
301
|
/** One per light: an index into the IES atlas, or −1. */
|
|
272
302
|
readonly iesProfiles?: Float32Array;
|
|
303
|
+
/** One per light: its lighting channels. See `PointLightSet.lightChannels`. */
|
|
304
|
+
readonly channels?: Float32Array;
|
|
305
|
+
/** One per light: its own falloff exponent, or 0. See `PointLightSource.falloffExponent`. */
|
|
306
|
+
readonly falloffExponents?: Float32Array;
|
|
307
|
+
/**
|
|
308
|
+
* The rectangles shaded through the table, after the `count` point lights: those of `areas` from
|
|
309
|
+
* `areaFrom` on, `areaCount` of them. The ones before `areaFrom` are the fixed arm's, which is
|
|
310
|
+
* where a rectangle's shadow lives. Absent, there are none.
|
|
311
|
+
*/
|
|
312
|
+
readonly areas?: AreaLightBuffer | null;
|
|
313
|
+
readonly areaFrom?: number;
|
|
314
|
+
readonly areaCount?: number;
|
|
273
315
|
}
|
|
274
316
|
/** Where `fillClusterLightSet` reads a frame's lights from: the arrays `Environment` carries. */
|
|
275
317
|
export interface ClusterLightArrays {
|
|
@@ -284,6 +326,9 @@ export interface ClusterLightArrays {
|
|
|
284
326
|
readonly lightIesProfiles?: Float32Array;
|
|
285
327
|
readonly lightIesAxes?: Float32Array;
|
|
286
328
|
readonly lightCookies?: Float32Array;
|
|
329
|
+
readonly lightChannels?: Float32Array;
|
|
330
|
+
readonly lightFalloffExponents?: Float32Array;
|
|
331
|
+
readonly areaLights?: AreaLightBuffer | null;
|
|
287
332
|
}
|
|
288
333
|
/** A `ClusterLightSet` a renderer refills every frame. */
|
|
289
334
|
export type ClusterLightScratch = {
|
|
@@ -305,10 +350,12 @@ export declare function createClusterLightSet(): ClusterLightScratch;
|
|
|
305
350
|
* radius in its place, an emitter as wide as the light reaches, which widened every lamp's
|
|
306
351
|
* highlight on a specular surface on the clustered arm alone.
|
|
307
352
|
*/
|
|
308
|
-
export declare function fillClusterLightSet(env: ClusterLightArrays, out: ClusterLightScratch
|
|
353
|
+
export declare function fillClusterLightSet(env: ClusterLightArrays, out: ClusterLightScratch,
|
|
354
|
+
/** How many rectangles the fixed arm shades; the rest of the buffer is clustered. */
|
|
355
|
+
areaFrom?: number): ClusterLightSet;
|
|
309
356
|
/**
|
|
310
|
-
* Whether a light's record needs its last
|
|
311
|
-
* or a
|
|
357
|
+
* Whether a light's record needs its last four texels: it holds a shadow slot, a cone, a profile,
|
|
358
|
+
* a cookie, channels of its own or a falloff exponent of its own. Everything else is a plain light, read in `PLAIN_LIGHT_TEXELS`.
|
|
312
359
|
*
|
|
313
360
|
* **`shadowSlots` is zero where the lit shader has no point shadows**, and both binners are handed
|
|
314
361
|
* it that way: a slot is only read under `POINT_SHADOWS`, so a light holding one in a build without
|
|
@@ -343,6 +390,8 @@ export declare function clusteredMode(lights: ClusterLightSet, count: number, sh
|
|
|
343
390
|
* is a light without a map, which is what a scene with two hundred lamps has anyway.
|
|
344
391
|
*/
|
|
345
392
|
export declare function writeLightRecord(lights: ClusterLightSet, light: number, shadowSlots: number, out: Uint32Array, at: number): void;
|
|
393
|
+
/** How many lights the table carries: the point lights and the clustered rectangles after them. */
|
|
394
|
+
export declare function clusteredLightTotal(lights: ClusterLightSet): number;
|
|
346
395
|
/**
|
|
347
396
|
* Fill the table with this frame's light assignment, and answer how many lights it carried.
|
|
348
397
|
*
|
|
@@ -389,3 +438,26 @@ export declare function writeLightRecord(lights: ClusterLightSet, light: number,
|
|
|
389
438
|
* Fills a caller-owned table and allocates nothing.
|
|
390
439
|
*/
|
|
391
440
|
export declare function buildLightClusters(lights: ClusterLightSet, view: Float32Array, near: number, far: number, tanHalfFovY: number, aspect: number, table: Uint32Array, shadowSlots: number): number;
|
|
441
|
+
/**
|
|
442
|
+
* How far a froxel lies outside a spot's cone, in metres, or a negative number where the cone
|
|
443
|
+
* reaches it: positive is a froxel the light cannot shade. Everything in view space with depth
|
|
444
|
+
* positive, as the binner holds it; `axis` is unit length and `cosA`, `sinA` are the outer cone's.
|
|
445
|
+
*
|
|
446
|
+
* **The froxel's bounding sphere against the infinite cone**, after Wronski's cone test for
|
|
447
|
+
* clustered spots. With `V` from the apex to the sphere's centre at an angle `θ` from the axis and
|
|
448
|
+
* `A` the cone's half-angle, the centre's distance from the cone is `|V| sin(θ − A)` — which is
|
|
449
|
+
* `cosA |V⊥| − (V·axis) sinA` — while `θ − A` is under a right angle, and `|V|` past it, where the
|
|
450
|
+
* nearest point of the cone is its apex. Inside the cone it is negative. So the gap is exact for
|
|
451
|
+
* the sphere, and the sphere holds the froxel: the test can only keep a light, never drop one the
|
|
452
|
+
* shader would draw. The apex case is what drops a froxel behind a spot whose sphere reaches it,
|
|
453
|
+
* which the formula alone underestimates.
|
|
454
|
+
*
|
|
455
|
+
* **What it gives up** is tightness at the near end of the frustum, where a froxel is a long thin
|
|
456
|
+
* box whose sphere is much larger than it: a spot grazing such a froxel is kept. **What would make
|
|
457
|
+
* it wrong** is a profile showing those near froxels hold the cost; testing the box's corners is
|
|
458
|
+
* the tight version, at eight dot products a pair.
|
|
459
|
+
*
|
|
460
|
+
* Exported so the conformance page can tell a disagreement at the cone's edge from a real one; the
|
|
461
|
+
* GPU binner writes the same arithmetic in WGSL.
|
|
462
|
+
*/
|
|
463
|
+
export declare function coneBoxGap(px: number, py: number, pz: number, axisX: number, axisY: number, axisZ: number, cosA: number, sinA: number, box: Float32Array): number;
|