@driftengine/core 4.8.6 → 4.9.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 +132 -0
- package/README.md +1 -1
- package/dist/index.d.ts +7 -2
- package/dist/index.js +8 -1
- package/dist/render/anisotropicLobe.d.ts +10 -0
- package/dist/render/anisotropicLobe.js +19 -0
- package/dist/render/areaLights.d.ts +35 -0
- package/dist/render/areaLights.js +35 -0
- package/dist/render/atmosphere.d.ts +13 -0
- package/dist/render/atmosphere.js +9 -1
- package/dist/render/backend/api.d.ts +41 -3
- package/dist/render/backend/createRenderer.d.ts +6 -0
- package/dist/render/backend/createRenderer.js +4 -1
- package/dist/render/backend/webgl2/renderer.d.ts +140 -16
- package/dist/render/backend/webgl2/renderer.js +436 -44
- package/dist/render/backend/webgl2/sceneCaptureTarget.d.ts +29 -0
- package/dist/render/backend/webgl2/sceneCaptureTarget.js +96 -0
- package/dist/render/backend/webgpu/device.d.ts +7 -1
- package/dist/render/backend/webgpu/device.js +31 -3
- package/dist/render/backend/webgpu/flatPass.d.ts +16 -1
- package/dist/render/backend/webgpu/flatPass.js +58 -26
- package/dist/render/backend/webgpu/particlePass.js +2 -1
- package/dist/render/backend/webgpu/postPass.d.ts +51 -9
- package/dist/render/backend/webgpu/postPass.js +16 -2
- package/dist/render/backend/webgpu/reflectionSurfacePass.d.ts +30 -0
- package/dist/render/backend/webgpu/reflectionSurfacePass.js +100 -0
- package/dist/render/backend/webgpu/renderer.d.ts +166 -3
- package/dist/render/backend/webgpu/renderer.js +738 -83
- 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/surfaceTexturePass.d.ts +4 -1
- package/dist/render/backend/webgpu/surfaceTexturePass.js +34 -8
- package/dist/render/backend/webgpu/waterPass.d.ts +1 -1
- package/dist/render/clusteredLights.d.ts +23 -5
- package/dist/render/clusteredLights.js +35 -8
- package/dist/render/displayRange.d.ts +31 -0
- package/dist/render/displayRange.js +47 -0
- 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/filmicCurve.d.ts +74 -0
- package/dist/render/filmicCurve.js +203 -0
- package/dist/render/fog.d.ts +4 -1
- package/dist/render/fog.js +12 -2
- package/dist/render/frame/resources.d.ts +2 -2
- package/dist/render/frame/resources.js +6 -0
- package/dist/render/frameReflections.d.ts +40 -0
- package/dist/render/frameReflections.js +48 -0
- package/dist/render/gi/bounceLights.d.ts +3 -2
- package/dist/render/gi/bounceLights.js +8 -3
- package/dist/render/instanceCull.js +5 -0
- package/dist/render/instances.d.ts +19 -5
- package/dist/render/instances.js +9 -3
- package/dist/render/lightBudget.d.ts +12 -1
- package/dist/render/lightBudget.js +23 -3
- package/dist/render/lightmap.d.ts +23 -9
- package/dist/render/lightmap.js +40 -18
- package/dist/render/particlePool.d.ts +21 -0
- package/dist/render/particlePool.js +7 -0
- package/dist/render/pass.d.ts +49 -0
- package/dist/render/pointLightSelection.d.ts +13 -0
- package/dist/render/pointLightSelection.js +3 -1
- package/dist/render/reflectionSurfaceTargets.d.ts +28 -0
- package/dist/render/reflectionSurfaceTargets.js +99 -0
- package/dist/render/renderQuality.d.ts +47 -15
- package/dist/render/renderQuality.js +4 -0
- 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 +24 -1
- package/dist/render/screenSpaceReflection.d.ts +32 -1
- package/dist/render/screenSpaceReflection.js +14 -0
- package/dist/render/shaders/arcane.d.ts +1 -1
- package/dist/render/shaders/bolt.d.ts +1 -1
- package/dist/render/shaders/bolt.js +7 -1
- package/dist/render/shaders/caustics.d.ts +1 -1
- package/dist/render/shaders/depth.js +11 -0
- package/dist/render/shaders/film.d.ts +1 -1
- package/dist/render/shaders/filmLook.js +2 -1
- package/dist/render/shaders/fire.d.ts +1 -1
- 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 +1 -1
- package/dist/render/shaders/flat/areaLight.js +46 -0
- package/dist/render/shaders/flat/glassTint.d.ts +3 -2
- package/dist/render/shaders/flat/glassTint.js +5 -1
- package/dist/render/shaders/flat/index.d.ts +15 -0
- package/dist/render/shaders/flat/index.js +22 -2
- 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 +85 -22
- package/dist/render/shaders/flat/models.d.ts +5 -2
- package/dist/render/shaders/flat/models.js +100 -16
- package/dist/render/shaders/flat/overlay.d.ts +16 -0
- package/dist/render/shaders/flat/overlay.js +117 -0
- package/dist/render/shaders/flat/pointShadow.js +1 -1
- package/dist/render/shaders/flat/preamble.js +10 -3
- package/dist/render/shaders/flat/reflectionSurface.d.ts +21 -0
- package/dist/render/shaders/flat/reflectionSurface.js +61 -0
- package/dist/render/shaders/fog.d.ts +1 -1
- package/dist/render/shaders/fog.js +9 -2
- package/dist/render/shaders/fringe.d.ts +29 -0
- package/dist/render/shaders/fringe.js +55 -0
- package/dist/render/shaders/generated/arcane.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/arcane.wgsl.js +1 -1
- 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/caustics.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/caustics.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/film.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/film.wgsl.js +1 -1
- package/dist/render/shaders/generated/fire.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/fire.wgsl.js +1 -1
- package/dist/render/shaders/generated/flat.wgsl.d.ts +192 -16
- package/dist/render/shaders/generated/flat.wgsl.js +230 -54
- package/dist/render/shaders/generated/line.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/line.wgsl.js +1 -1
- package/dist/render/shaders/generated/particle.wgsl.d.ts +4 -4
- package/dist/render/shaders/generated/particle.wgsl.js +4 -4
- package/dist/render/shaders/generated/rush.wgsl.d.ts +56 -26
- package/dist/render/shaders/generated/rush.wgsl.js +56 -26
- package/dist/render/shaders/generated/scatter.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/scatter.wgsl.js +1 -1
- package/dist/render/shaders/generated/smoke.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/smoke.wgsl.js +1 -1
- package/dist/render/shaders/generated/ssrResolve.wgsl.d.ts +29 -0
- package/dist/render/shaders/generated/ssrResolve.wgsl.js +29 -0
- package/dist/render/shaders/generated/ssrTrace.wgsl.d.ts +32 -7
- package/dist/render/shaders/generated/ssrTrace.wgsl.js +32 -7
- 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/gpudriven/fog.wgsl.d.ts +2 -2
- package/dist/render/shaders/gpudriven/fog.wgsl.js +11 -3
- package/dist/render/shaders/line.d.ts +1 -1
- package/dist/render/shaders/particle.d.ts +3 -3
- 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/rush.js +54 -11
- package/dist/render/shaders/scatter.d.ts +1 -1
- package/dist/render/shaders/smoke.d.ts +1 -1
- package/dist/render/shaders/ssrResolve.d.ts +17 -0
- package/dist/render/shaders/ssrResolve.js +51 -0
- package/dist/render/shaders/ssrTrace.js +46 -1
- package/dist/render/shaders/water.d.ts +1 -1
- package/dist/render/shaders/water.js +12 -3
- package/dist/render/skinScatter.d.ts +3 -1
- package/dist/render/skinScatter.js +3 -1
- package/dist/render/ssrPass.d.ts +16 -0
- package/dist/render/ssrPass.js +107 -1
- package/dist/render/surfaceModel.d.ts +29 -8
- package/dist/render/surfaceModel.js +33 -9
- package/dist/render/surfaceOverlay.d.ts +132 -0
- package/dist/render/surfaceOverlay.js +85 -0
- package/dist/render/surfaceTexture.d.ts +44 -1
- package/dist/render/surfaceTexture.js +96 -5
- package/dist/render/temporalAa.d.ts +14 -0
- package/dist/render/temporalAa.js +20 -0
- package/dist/render/vertexDefaults.d.ts +17 -0
- package/dist/render/vertexDefaults.js +24 -0
- package/package.json +6 -6
- package/src/index.ts +22 -2
- package/src/render/anisotropicLobe.ts +29 -0
- package/src/render/areaLights.ts +58 -0
- package/src/render/atmosphere.ts +22 -1
- package/src/render/backend/api.ts +58 -2
- package/src/render/backend/createRenderer.ts +18 -1
- package/src/render/backend/webgl2/renderer.ts +548 -50
- package/src/render/backend/webgl2/sceneCaptureTarget.ts +130 -0
- package/src/render/backend/webgpu/device.ts +42 -3
- package/src/render/backend/webgpu/flatPass.ts +63 -26
- package/src/render/backend/webgpu/particlePass.ts +2 -1
- package/src/render/backend/webgpu/postPass.ts +18 -0
- package/src/render/backend/webgpu/reflectionSurfacePass.ts +114 -0
- package/src/render/backend/webgpu/renderer.ts +927 -100
- package/src/render/backend/webgpu/sceneCaptureTarget.ts +63 -0
- package/src/render/backend/webgpu/surfaceTexturePass.ts +44 -14
- package/src/render/clusteredLights.ts +41 -8
- package/src/render/displayRange.ts +61 -0
- package/src/render/driftLight/bake.ts +3 -1
- package/src/render/driftLight/falloff.ts +13 -2
- package/src/render/filmicCurve.ts +229 -0
- package/src/render/fog.ts +19 -3
- package/src/render/frame/resources.ts +6 -0
- package/src/render/frameReflections.ts +72 -0
- package/src/render/gi/bounceLights.ts +12 -3
- package/src/render/instanceCull.ts +4 -0
- package/src/render/instances.ts +26 -6
- package/src/render/lightBudget.ts +40 -2
- package/src/render/lightmap.ts +64 -23
- package/src/render/particlePool.ts +23 -0
- package/src/render/pass.ts +53 -1
- package/src/render/pointLightSelection.ts +20 -1
- package/src/render/reflectionSurfaceTargets.ts +105 -0
- package/src/render/renderQuality.ts +52 -15
- 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 +30 -2
- package/src/render/screenSpaceReflection.ts +38 -1
- package/src/render/shaders/bolt.ts +7 -1
- package/src/render/shaders/depth.ts +11 -0
- package/src/render/shaders/filmLook.ts +2 -1
- package/src/render/shaders/flat/anisotropic.ts +37 -3
- package/src/render/shaders/flat/areaLight.ts +46 -0
- package/src/render/shaders/flat/glassTint.ts +13 -2
- package/src/render/shaders/flat/index.ts +37 -2
- package/src/render/shaders/flat/lobes.ts +50 -43
- package/src/render/shaders/flat/main.ts +85 -22
- package/src/render/shaders/flat/models.ts +100 -16
- package/src/render/shaders/flat/overlay.ts +117 -0
- package/src/render/shaders/flat/pointShadow.ts +1 -1
- package/src/render/shaders/flat/preamble.ts +10 -3
- package/src/render/shaders/flat/reflectionSurface.ts +61 -0
- package/src/render/shaders/fog.ts +9 -2
- package/src/render/shaders/fringe.ts +59 -0
- package/src/render/shaders/generated/arcane.wgsl.ts +1 -1
- package/src/render/shaders/generated/bolt.wgsl.ts +1 -1
- package/src/render/shaders/generated/caustics.wgsl.ts +1 -1
- package/src/render/shaders/generated/depth.wgsl.ts +4 -4
- package/src/render/shaders/generated/film.wgsl.ts +1 -1
- package/src/render/shaders/generated/fire.wgsl.ts +1 -1
- package/src/render/shaders/generated/flat.wgsl.ts +230 -54
- package/src/render/shaders/generated/line.wgsl.ts +1 -1
- package/src/render/shaders/generated/particle.wgsl.ts +4 -4
- package/src/render/shaders/generated/rush.wgsl.ts +56 -26
- package/src/render/shaders/generated/scatter.wgsl.ts +1 -1
- package/src/render/shaders/generated/smoke.wgsl.ts +1 -1
- package/src/render/shaders/generated/ssrResolve.wgsl.ts +30 -0
- package/src/render/shaders/generated/ssrTrace.wgsl.ts +32 -7
- package/src/render/shaders/generated/water.wgsl.ts +23 -16
- package/src/render/shaders/gi/probeBake.wgsl.ts +9 -5
- package/src/render/shaders/gpudriven/fog.wgsl.ts +11 -3
- package/src/render/shaders/recon/motion.wgsl.ts +13 -2
- package/src/render/shaders/rush.ts +54 -11
- package/src/render/shaders/ssrResolve.ts +52 -0
- package/src/render/shaders/ssrTrace.ts +46 -1
- package/src/render/shaders/water.ts +12 -3
- package/src/render/skinScatter.ts +3 -1
- package/src/render/ssrPass.ts +139 -1
- package/src/render/surfaceModel.ts +47 -10
- package/src/render/surfaceOverlay.ts +195 -0
- package/src/render/surfaceTexture.ts +136 -9
- package/src/render/temporalAa.ts +20 -0
- package/src/render/vertexDefaults.ts +28 -0
|
@@ -3,6 +3,7 @@ import { compressedLayers, planBlocks, refuseBlockUpdate } from './compressedSou
|
|
|
3
3
|
import { uploadCompressedArray } from './glCompressed.js';
|
|
4
4
|
import { SURFACE_EFFECT_TEXELS, packSurfaceEffects } from './surfaceEffects.js';
|
|
5
5
|
import { isLightmapTexels } from './lightmap.js';
|
|
6
|
+
import { isSceneCaptureTexels } from './sceneCapture.js';
|
|
6
7
|
const DEFAULT_ANISOTROPY = 4;
|
|
7
8
|
/**
|
|
8
9
|
* An image uploaded once and bound per draw.
|
|
@@ -24,15 +25,24 @@ export class SurfaceTexture {
|
|
|
24
25
|
effectsTable = null;
|
|
25
26
|
/** The block format a compressed texture holds, or null for one uploaded from images. */
|
|
26
27
|
blockFormat;
|
|
28
|
+
/** Level 0's size, which `flatLayer` copies; kept as `update` changes it. */
|
|
29
|
+
width = 0;
|
|
30
|
+
height = 0;
|
|
31
|
+
/** The texture's storage format, which `flatLayer`'s copy matches so the copy converts nothing. */
|
|
32
|
+
storage;
|
|
33
|
+
/** Layer 0 as a plain 2D texture, made by `flatLayer` and dropped when the pixels change. */
|
|
34
|
+
flat = null;
|
|
27
35
|
/**
|
|
28
36
|
* One image, or an array of images that must share one size (`layerSize` refuses otherwise).
|
|
29
37
|
* Uploaded unflipped either way; see the constructor body for why. Or blocks, uploaded as they are
|
|
30
38
|
* where `compressed` — the formats this context samples — has theirs; see `compressedSource.ts`.
|
|
31
39
|
*/
|
|
32
40
|
constructor(gl, source, options = {}, compressed = []) {
|
|
33
|
-
/* A
|
|
41
|
+
/* A scene capture's target: one empty layer the renderer draws into. See `sceneCapture.ts`. */
|
|
42
|
+
const capture = isSceneCaptureTexels(source) ? source : null;
|
|
43
|
+
/* A lightmap's page: two layers of bytes, uploaded as they are. See `lightmap.ts`. */
|
|
34
44
|
const baked = isLightmapTexels(source) ? source : null;
|
|
35
|
-
const listed = (baked !== null ? [] : Array.isArray(source) ? source : [source]);
|
|
45
|
+
const listed = (baked !== null || capture !== null ? [] : Array.isArray(source) ? source : [source]);
|
|
36
46
|
const blocks = compressedLayers(listed);
|
|
37
47
|
this.srgb = (options.colorSpace ?? 'linear') === 'srgb';
|
|
38
48
|
/* Refused before anything is allocated, so a refusal leaves no texture behind. */
|
|
@@ -43,21 +53,40 @@ export class SurfaceTexture {
|
|
|
43
53
|
this.texture = texture;
|
|
44
54
|
const wrap = (options.wrap ?? 'repeat') === 'repeat' ? gl.REPEAT : gl.CLAMP_TO_EDGE;
|
|
45
55
|
gl.bindTexture(gl.TEXTURE_2D_ARRAY, texture);
|
|
46
|
-
if (
|
|
56
|
+
if (capture !== null) {
|
|
57
|
+
/* Immutable storage, one level: a framebuffer attachment, and radiance read linear. */
|
|
58
|
+
this.layers = 1;
|
|
59
|
+
this.blockFormat = null;
|
|
60
|
+
this.mipmapped = false;
|
|
61
|
+
this.width = capture.width;
|
|
62
|
+
this.height = capture.height;
|
|
63
|
+
this.storage = capture.float ? gl.RGBA16F : gl.RGBA8;
|
|
64
|
+
gl.texStorage3D(gl.TEXTURE_2D_ARRAY, 1, capture.float ? gl.RGBA16F : gl.RGBA8, capture.width, capture.height, 1);
|
|
65
|
+
}
|
|
66
|
+
else if (baked !== null) {
|
|
47
67
|
/* No chain: a page is read at its own resolution, and a level of it would blend regions. */
|
|
48
|
-
this.layers = baked.layers;
|
|
68
|
+
this.layers = baked.layers.length;
|
|
49
69
|
this.blockFormat = null;
|
|
50
70
|
this.mipmapped = false;
|
|
71
|
+
this.width = baked.width;
|
|
72
|
+
this.height = baked.height;
|
|
73
|
+
this.storage = gl.RGBA8;
|
|
51
74
|
/* A typed array refuses either flag set, so both are put down here rather than assumed. */
|
|
52
75
|
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
|
|
53
76
|
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
|
|
54
|
-
|
|
77
|
+
/* Bytes, and read by `texelFetch` alone: the shader decodes and filters. See `lightmap.ts`. */
|
|
78
|
+
gl.texImage3D(gl.TEXTURE_2D_ARRAY, 0, gl.RGBA8, baked.width, baked.height, this.layers, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
|
79
|
+
/* A layer at a time, so a page's arrays are uploaded where they lie rather than joined. */
|
|
80
|
+
for (let layer = 0; layer < this.layers; layer++) {
|
|
81
|
+
gl.texSubImage3D(gl.TEXTURE_2D_ARRAY, 0, 0, 0, layer, baked.width, baked.height, 1, gl.RGBA, gl.UNSIGNED_BYTE, baked.layers[layer]);
|
|
82
|
+
}
|
|
55
83
|
}
|
|
56
84
|
else if (blocks !== null && plan !== null) {
|
|
57
85
|
/* The stored chain, or level 0 alone where no chain was asked for. Nothing is generated:
|
|
58
86
|
`generateMipmap` cannot write a compressed format. */
|
|
59
87
|
this.layers = blocks.length;
|
|
60
88
|
this.blockFormat = blocks[0].format;
|
|
89
|
+
this.storage = gl.NONE;
|
|
61
90
|
this.mipmapped = (options.mipmap ?? true) && plan.levels > 1;
|
|
62
91
|
uploadCompressedArray(gl, plan.name, blocks, this.mipmapped ? plan.levels : 1);
|
|
63
92
|
}
|
|
@@ -66,6 +95,10 @@ export class SurfaceTexture {
|
|
|
66
95
|
this.layers = sources.length;
|
|
67
96
|
this.blockFormat = null;
|
|
68
97
|
this.mipmapped = options.mipmap ?? true;
|
|
98
|
+
const { width, height } = layerSize(sources);
|
|
99
|
+
this.width = width;
|
|
100
|
+
this.height = height;
|
|
101
|
+
this.storage = this.srgb ? gl.SRGB8_ALPHA8 : gl.RGBA8;
|
|
69
102
|
uploadImages(gl, sources, this.srgb);
|
|
70
103
|
}
|
|
71
104
|
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_WRAP_S, wrap);
|
|
@@ -128,6 +161,9 @@ export class SurfaceTexture {
|
|
|
128
161
|
refuseArrayUpdate(this.layers);
|
|
129
162
|
refuseBlockUpdate(this.blockFormat);
|
|
130
163
|
const { width, height } = sourceSize(source);
|
|
164
|
+
this.width = width;
|
|
165
|
+
this.height = height;
|
|
166
|
+
this.dropFlat(gl);
|
|
131
167
|
gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.texture);
|
|
132
168
|
/* Unflipped, matching the constructor. See it for why, and for what it cost. */
|
|
133
169
|
gl.texImage3D(gl.TEXTURE_2D_ARRAY, 0, this.srgb ? gl.SRGB8_ALPHA8 : gl.RGBA, width, height, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, source);
|
|
@@ -135,14 +171,69 @@ export class SurfaceTexture {
|
|
|
135
171
|
gl.generateMipmap(gl.TEXTURE_2D_ARRAY);
|
|
136
172
|
gl.bindTexture(gl.TEXTURE_2D_ARRAY, null);
|
|
137
173
|
}
|
|
174
|
+
/**
|
|
175
|
+
* Attach the only layer as the bound framebuffer's colour: a scene capture's target, drawn into
|
|
176
|
+
* by `captureScene`. Engine-internal, as `bind` is.
|
|
177
|
+
*/
|
|
178
|
+
attachColor(gl) {
|
|
179
|
+
gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, this.texture, 0, 0);
|
|
180
|
+
}
|
|
138
181
|
/** Bind to a unit for sampling. Engine-internal: the renderer owns unit assignment. */
|
|
139
182
|
bind(gl, unit) {
|
|
140
183
|
gl.activeTexture(gl.TEXTURE0 + unit);
|
|
141
184
|
gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.texture);
|
|
142
185
|
}
|
|
186
|
+
/**
|
|
187
|
+
* Layer 0 as a plain 2D texture: what a `sampler2D` can be handed, which an array cannot. Copied
|
|
188
|
+
* the first time it is asked for, by a framebuffer blit into storage of the same format, and kept
|
|
189
|
+
* until `update` or `dispose`. Engine-internal: a surface overlay's atlas is bound where the
|
|
190
|
+
* lit stage's one 2D image slot is (`surfaceOverlay.ts`).
|
|
191
|
+
*
|
|
192
|
+
* **Null for blocks**, which no framebuffer can read; the caller says so. What it costs is a copy
|
|
193
|
+
* of level 0 in memory beside the array, and the bindings of both framebuffer targets, which are
|
|
194
|
+
* read back before the blit and put back after it — a sync query, paid once per image.
|
|
195
|
+
*/
|
|
196
|
+
flatLayer(gl) {
|
|
197
|
+
if (this.texture === null || this.blockFormat !== null)
|
|
198
|
+
return null;
|
|
199
|
+
if (this.flat !== null)
|
|
200
|
+
return this.flat;
|
|
201
|
+
const flat = gl.createTexture();
|
|
202
|
+
const read = gl.createFramebuffer();
|
|
203
|
+
const draw = gl.createFramebuffer();
|
|
204
|
+
if (flat === null || read === null || draw === null)
|
|
205
|
+
return null;
|
|
206
|
+
gl.bindTexture(gl.TEXTURE_2D, flat);
|
|
207
|
+
gl.texStorage2D(gl.TEXTURE_2D, 1, this.storage, this.width, this.height);
|
|
208
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
|
209
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
|
210
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
211
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
212
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
213
|
+
const heldRead = gl.getParameter(gl.READ_FRAMEBUFFER_BINDING);
|
|
214
|
+
const heldDraw = gl.getParameter(gl.DRAW_FRAMEBUFFER_BINDING);
|
|
215
|
+
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, read);
|
|
216
|
+
gl.framebufferTextureLayer(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, this.texture, 0, 0);
|
|
217
|
+
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, draw);
|
|
218
|
+
gl.framebufferTexture2D(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, flat, 0);
|
|
219
|
+
const [w, h] = [this.width, this.height];
|
|
220
|
+
gl.blitFramebuffer(0, 0, w, h, 0, 0, w, h, gl.COLOR_BUFFER_BIT, gl.NEAREST);
|
|
221
|
+
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, heldRead);
|
|
222
|
+
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, heldDraw);
|
|
223
|
+
gl.deleteFramebuffer(read);
|
|
224
|
+
gl.deleteFramebuffer(draw);
|
|
225
|
+
this.flat = flat;
|
|
226
|
+
return flat;
|
|
227
|
+
}
|
|
228
|
+
dropFlat(gl) {
|
|
229
|
+
if (this.flat !== null)
|
|
230
|
+
gl.deleteTexture(this.flat);
|
|
231
|
+
this.flat = null;
|
|
232
|
+
}
|
|
143
233
|
dispose(gl) {
|
|
144
234
|
if (this.texture === null)
|
|
145
235
|
return;
|
|
236
|
+
this.dropFlat(gl);
|
|
146
237
|
gl.deleteTexture(this.texture);
|
|
147
238
|
this.texture = null;
|
|
148
239
|
if (this.effectsTable !== null)
|
|
@@ -248,12 +248,26 @@ export declare class TemporalHistory {
|
|
|
248
248
|
private picture;
|
|
249
249
|
private width;
|
|
250
250
|
private height;
|
|
251
|
+
/** Whether this frame of the renderer's has opened one of the sequence, and what it answered. */
|
|
252
|
+
private opened;
|
|
253
|
+
private answer;
|
|
254
|
+
/**
|
|
255
|
+
* A frame of the renderer's began: the next `openFrame` opens a frame of the sequence. Called at
|
|
256
|
+
* `beginFrame`, by both backends.
|
|
257
|
+
*/
|
|
258
|
+
nextFrame(): void;
|
|
251
259
|
/**
|
|
252
260
|
* Open a frame at this target size, and answer whether the history may be sampled.
|
|
253
261
|
*
|
|
254
262
|
* **The jitter advances whether or not it may be.** Holding the sequence still on the frames a
|
|
255
263
|
* resize or a cut discarded would sample the same position twice running, which is one sample
|
|
256
264
|
* and not two.
|
|
265
|
+
*
|
|
266
|
+
* **Once a frame, however many times it is asked.** A renderer asks where the scene's camera
|
|
267
|
+
* arrives, and a frame binds that camera again after a scene capture or anything else that bound
|
|
268
|
+
* another; each asking opened a frame of its own, so the sequence stepped twice in such a frame
|
|
269
|
+
* and the edges of the picture swam. Asked again, it gives the answer it gave. What it gives up:
|
|
270
|
+
* a size changed between two asks in one frame is seen at the next frame, not at once.
|
|
257
271
|
*/
|
|
258
272
|
openFrame(width: number, height: number): boolean;
|
|
259
273
|
/**
|
|
@@ -355,20 +355,40 @@ export class TemporalHistory {
|
|
|
355
355
|
picture = false;
|
|
356
356
|
width = 0;
|
|
357
357
|
height = 0;
|
|
358
|
+
/** Whether this frame of the renderer's has opened one of the sequence, and what it answered. */
|
|
359
|
+
opened = false;
|
|
360
|
+
answer = false;
|
|
361
|
+
/**
|
|
362
|
+
* A frame of the renderer's began: the next `openFrame` opens a frame of the sequence. Called at
|
|
363
|
+
* `beginFrame`, by both backends.
|
|
364
|
+
*/
|
|
365
|
+
nextFrame() {
|
|
366
|
+
this.opened = false;
|
|
367
|
+
}
|
|
358
368
|
/**
|
|
359
369
|
* Open a frame at this target size, and answer whether the history may be sampled.
|
|
360
370
|
*
|
|
361
371
|
* **The jitter advances whether or not it may be.** Holding the sequence still on the frames a
|
|
362
372
|
* resize or a cut discarded would sample the same position twice running, which is one sample
|
|
363
373
|
* and not two.
|
|
374
|
+
*
|
|
375
|
+
* **Once a frame, however many times it is asked.** A renderer asks where the scene's camera
|
|
376
|
+
* arrives, and a frame binds that camera again after a scene capture or anything else that bound
|
|
377
|
+
* another; each asking opened a frame of its own, so the sequence stepped twice in such a frame
|
|
378
|
+
* and the edges of the picture swam. Asked again, it gives the answer it gave. What it gives up:
|
|
379
|
+
* a size changed between two asks in one frame is seen at the next frame, not at once.
|
|
364
380
|
*/
|
|
365
381
|
openFrame(width, height) {
|
|
382
|
+
if (this.opened)
|
|
383
|
+
return this.answer;
|
|
384
|
+
this.opened = true;
|
|
366
385
|
this.frameIndex++;
|
|
367
386
|
const usable = this.picture && width === this.width && height === this.height;
|
|
368
387
|
if (!usable)
|
|
369
388
|
this.picture = false;
|
|
370
389
|
this.width = width;
|
|
371
390
|
this.height = height;
|
|
391
|
+
this.answer = usable;
|
|
372
392
|
return usable;
|
|
373
393
|
}
|
|
374
394
|
/**
|
|
@@ -38,6 +38,12 @@ export declare const ABSENT_ATTRIBUTE: Readonly<Record<string, readonly number[]
|
|
|
38
38
|
* value that produces a picture. `flat.ts` reads `0` as none, `1` as sRGB and `2` as ACES.
|
|
39
39
|
*/
|
|
40
40
|
export declare const OUTPUT_TRANSFORM_CODE: Readonly<Record<string, number>>;
|
|
41
|
+
/**
|
|
42
|
+
* The code a **forward** pass grades with: the transform's own, except `filmic`, which only the
|
|
43
|
+
* composite carries the constants for, and which a pass grading itself therefore draws as `aces`.
|
|
44
|
+
* Both backends' `gradeCode` read this, so neither can hand a forward stage a code it lacks.
|
|
45
|
+
*/
|
|
46
|
+
export declare function forwardTransformCode(transform: string): number;
|
|
41
47
|
/**
|
|
42
48
|
* How far the speed rush reaches, and how far a motion-blurred pixel may travel, both in UV.
|
|
43
49
|
*
|
|
@@ -46,3 +52,14 @@ export declare const OUTPUT_TRANSFORM_CODE: Readonly<Record<string, number>>;
|
|
|
46
52
|
*/
|
|
47
53
|
export declare const RUSH_REACH_UV = 0.012;
|
|
48
54
|
export declare const MOTION_BLUR_MAX_UV = 0.03;
|
|
55
|
+
/**
|
|
56
|
+
* A longest smear as `setCameraMotionBlur` takes it: a share of the frame, above zero and at most
|
|
57
|
+
* half, and the default where the number is not one. Shared, so both backends cap one way.
|
|
58
|
+
*/
|
|
59
|
+
export declare function clampMotionShare(share: number): number;
|
|
60
|
+
/**
|
|
61
|
+
* Said once where a draw names where it was last frame and the frame blurs by motion, but keeps no
|
|
62
|
+
* motion target to blur that draw by: each object's own motion is drawn only under a
|
|
63
|
+
* reconstruction on WebGPU, and anywhere else the camera's motion alone smears the frame.
|
|
64
|
+
*/
|
|
65
|
+
export declare const OBJECT_BLUR_UNKEPT: string;
|
|
@@ -110,7 +110,16 @@ export const OUTPUT_TRANSFORM_CODE = {
|
|
|
110
110
|
srgb: 1,
|
|
111
111
|
aces: 2,
|
|
112
112
|
shoulder: 3,
|
|
113
|
+
filmic: 4,
|
|
113
114
|
};
|
|
115
|
+
/**
|
|
116
|
+
* The code a **forward** pass grades with: the transform's own, except `filmic`, which only the
|
|
117
|
+
* composite carries the constants for, and which a pass grading itself therefore draws as `aces`.
|
|
118
|
+
* Both backends' `gradeCode` read this, so neither can hand a forward stage a code it lacks.
|
|
119
|
+
*/
|
|
120
|
+
export function forwardTransformCode(transform) {
|
|
121
|
+
return transform === 'filmic' ? 2 : (OUTPUT_TRANSFORM_CODE[transform] ?? 0);
|
|
122
|
+
}
|
|
114
123
|
/**
|
|
115
124
|
* How far the speed rush reaches, and how far a motion-blurred pixel may travel, both in UV.
|
|
116
125
|
*
|
|
@@ -119,3 +128,18 @@ export const OUTPUT_TRANSFORM_CODE = {
|
|
|
119
128
|
*/
|
|
120
129
|
export const RUSH_REACH_UV = 0.012;
|
|
121
130
|
export const MOTION_BLUR_MAX_UV = 0.03;
|
|
131
|
+
/**
|
|
132
|
+
* A longest smear as `setCameraMotionBlur` takes it: a share of the frame, above zero and at most
|
|
133
|
+
* half, and the default where the number is not one. Shared, so both backends cap one way.
|
|
134
|
+
*/
|
|
135
|
+
export function clampMotionShare(share) {
|
|
136
|
+
return Number.isFinite(share) && share > 0 ? Math.min(share, 0.5) : MOTION_BLUR_MAX_UV;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Said once where a draw names where it was last frame and the frame blurs by motion, but keeps no
|
|
140
|
+
* motion target to blur that draw by: each object's own motion is drawn only under a
|
|
141
|
+
* reconstruction on WebGPU, and anywhere else the camera's motion alone smears the frame.
|
|
142
|
+
*/
|
|
143
|
+
export const OBJECT_BLUR_UNKEPT = "[driftengine] motion blur follows each draw's own motion only under a reconstruction " +
|
|
144
|
+
"(DriftTR, WebGPU), which draws the motion target it reads; here the camera's motion alone " +
|
|
145
|
+
'blurs the frame, and a moving object is smeared only as the camera moved past it.';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@driftengine/core",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.9.0",
|
|
4
4
|
"description": "The runtime of DriftEngine, a WebGPU game engine with a WebGL2 fallback: renderer, loop, input, cameras, scene graph and physics",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
],
|
|
30
30
|
"sideEffects": false,
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@driftengine/drft": "4.
|
|
33
|
-
"@driftengine/physics": "4.
|
|
32
|
+
"@driftengine/drft": "4.9.0",
|
|
33
|
+
"@driftengine/physics": "4.9.0",
|
|
34
34
|
"gl-matrix": "^3.4.3"
|
|
35
35
|
},
|
|
36
36
|
"author": "Drift Technologies",
|
|
@@ -67,8 +67,8 @@
|
|
|
67
67
|
"prepack": "node ../../scripts/build.mjs @driftengine/core"
|
|
68
68
|
},
|
|
69
69
|
"devDependencies": {
|
|
70
|
-
"@driftengine/nav": "4.
|
|
71
|
-
"@driftengine/network": "4.
|
|
72
|
-
"@driftengine/texture": "4.
|
|
70
|
+
"@driftengine/nav": "4.9.0",
|
|
71
|
+
"@driftengine/network": "4.9.0",
|
|
72
|
+
"@driftengine/texture": "4.9.0"
|
|
73
73
|
}
|
|
74
74
|
}
|
package/src/index.ts
CHANGED
|
@@ -454,6 +454,8 @@ export type { ReflectionOptions, ReflectiveSample } from './render/gi/reflection
|
|
|
454
454
|
*/
|
|
455
455
|
export { GRADE_PLACEHOLDER_SIZE, MAX_GRADE_SIZE, identityGradeLut } from './render/colourGrade.ts';
|
|
456
456
|
export type { ColourGradeLut } from './render/colourGrade.ts';
|
|
457
|
+
export { DEFAULT_FILMIC_CURVE } from './render/filmicCurve.ts';
|
|
458
|
+
export type { FilmicCurve } from './render/filmicCurve.ts';
|
|
457
459
|
/**
|
|
458
460
|
* How bloom answers past its threshold — a ramp in place of the subtraction, and a tint for each
|
|
459
461
|
* of the pyramid's `BLOOM_LEVELS` octaves — the third argument of `setBloom`.
|
|
@@ -513,6 +515,20 @@ export type {
|
|
|
513
515
|
SurfaceModel,
|
|
514
516
|
SurfaceModelKind,
|
|
515
517
|
} from './render/surfaceModel.ts';
|
|
518
|
+
/*
|
|
519
|
+
* What a draw lays over its surface beyond its material — a rim of light, a dissolve, wrinkle
|
|
520
|
+
* normals by region — set per draw with `setSurfaceOverlay`, its images regions of one atlas.
|
|
521
|
+
*/
|
|
522
|
+
/* Every opaque lit surface reflecting the frame by its own material: `screenSpaceReflections`. */
|
|
523
|
+
export { DEFAULT_FRAME_REFLECTIONS } from './render/frameReflections.ts';
|
|
524
|
+
export type { FrameReflections } from './render/frameReflections.ts';
|
|
525
|
+
export type {
|
|
526
|
+
OverlayRegion,
|
|
527
|
+
SurfaceDissolve,
|
|
528
|
+
SurfaceOverlay,
|
|
529
|
+
SurfaceRim,
|
|
530
|
+
SurfaceWrinkle,
|
|
531
|
+
} from './render/surfaceOverlay.ts';
|
|
516
532
|
/*
|
|
517
533
|
* A baked lightmap: the page a consumer decoded, which `createLightmap` uploads as a lightmapped
|
|
518
534
|
* material's `modelMap`, and the region of it a material or an instance reads.
|
|
@@ -1065,8 +1081,12 @@ export {
|
|
|
1065
1081
|
INSTANCE_FLOATS,
|
|
1066
1082
|
INSTANCE_STRIDE,
|
|
1067
1083
|
} from './render/instances.ts';
|
|
1068
|
-
export { ParticlePool } from './render/particlePool.ts';
|
|
1069
|
-
export type {
|
|
1084
|
+
export { ParticlePool, DEVICE_PARTICLE_FLOATS } from './render/particlePool.ts';
|
|
1085
|
+
export type {
|
|
1086
|
+
DeviceParticles,
|
|
1087
|
+
ParticlePoolOptions,
|
|
1088
|
+
ParticleInstances,
|
|
1089
|
+
} from './render/particlePool.ts';
|
|
1070
1090
|
export { ParticleBatch } from './render/particleBatch.ts';
|
|
1071
1091
|
export type {
|
|
1072
1092
|
ParticleBatchOptions,
|
|
@@ -39,3 +39,32 @@ export function anisotropicLobe(
|
|
|
39
39
|
const d = x * x + y * y + nh * nh;
|
|
40
40
|
return 1 / Math.max(d * d, 1e-8);
|
|
41
41
|
}
|
|
42
|
+
|
|
43
|
+
/** Three components in the lobe's frame: along the direction, across it, along the normal. */
|
|
44
|
+
export type FrameVector = readonly [number, number, number];
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The physical form of the same lobe, `π · D · Vis · N·L`, for a surface whose highlight is GGX's
|
|
48
|
+
* own (`SurfaceMaterial.physicalSpecular`): the stretched distribution normalised as a BRDF's, and
|
|
49
|
+
* Smith's height-correlated masking stretched with it (Heitz 2014, as Filament writes it), Fresnel
|
|
50
|
+
* left to the caller. The half vector, the light and the view are each in the lobe's frame. At
|
|
51
|
+
* strength 0 it is isotropic GGX, the term skin's and the eye's highlights are.
|
|
52
|
+
*/
|
|
53
|
+
export function anisotropicPhysicalLobe(
|
|
54
|
+
half: FrameVector,
|
|
55
|
+
light: FrameVector,
|
|
56
|
+
view: FrameVector,
|
|
57
|
+
alphaT: number,
|
|
58
|
+
alphaB: number,
|
|
59
|
+
): number {
|
|
60
|
+
const x = half[0] / alphaT;
|
|
61
|
+
const y = half[1] / alphaB;
|
|
62
|
+
const d = x * x + y * y + half[2] * half[2];
|
|
63
|
+
const distribution = 1 / (Math.PI * alphaT * alphaB * d * d);
|
|
64
|
+
const ndl = light[2];
|
|
65
|
+
const ndv = view[2];
|
|
66
|
+
const lambdaView = ndl * Math.hypot(alphaT * view[0], alphaB * view[1], ndv);
|
|
67
|
+
const lambdaLight = ndv * Math.hypot(alphaT * light[0], alphaB * light[1], ndl);
|
|
68
|
+
const visibility = 0.5 / Math.max(lambdaView + lambdaLight, 1e-5);
|
|
69
|
+
return Math.PI * distribution * visibility * ndl;
|
|
70
|
+
}
|
package/src/render/areaLights.ts
CHANGED
|
@@ -97,6 +97,22 @@ export interface AreaLightSource {
|
|
|
97
97
|
* of a scene unit, and never inside the rectangle itself. Unreal's `AttenuationRadius`, in metres.
|
|
98
98
|
*/
|
|
99
99
|
range?: number;
|
|
100
|
+
/**
|
|
101
|
+
* Barn doors: a flap hinged at each of the rectangle's four edges, standing this many degrees from
|
|
102
|
+
* its normal. 90, or absent, folds them flat and they hide nothing; smaller closes them, so the
|
|
103
|
+
* light narrows to the opening they leave — a rectangle at 50° throws a beam rather than filling
|
|
104
|
+
* its hemisphere. Unreal's `BarnDoorAngle`, whose default of 88° is nearly flat.
|
|
105
|
+
*
|
|
106
|
+
* What a fragment sees past the doors is the part of the rectangle no door's tip hides, one axis
|
|
107
|
+
* at a time, and the rectangle then shades as that smaller rectangle, its highlight included.
|
|
108
|
+
* Its shadow map is still baked from the whole rectangle's centre. **What it gives up**: each
|
|
109
|
+
* door is taken as endless along its hinge, so the two axes clip apart and the corner where two
|
|
110
|
+
* doors meet hides a square rather than the mitre two real flaps make. One-sided: a two-sided
|
|
111
|
+
* rectangle's back face has none.
|
|
112
|
+
*/
|
|
113
|
+
barnDoorAngle?: number;
|
|
114
|
+
/** The barn doors' length in metres. Absent, 0.2: Unreal's default of 20 cm. */
|
|
115
|
+
barnDoorLength?: number;
|
|
100
116
|
}
|
|
101
117
|
|
|
102
118
|
/** The irradiance a derived `range` ends at, in scene units. See `AreaLightSource.range`. */
|
|
@@ -113,6 +129,11 @@ export interface AreaLightBuffer {
|
|
|
113
129
|
readonly twoSided: Float32Array;
|
|
114
130
|
/** One a rectangle: how far its light reaches. See `AreaLightSource.range`. */
|
|
115
131
|
readonly ranges?: Float32Array;
|
|
132
|
+
/**
|
|
133
|
+
* Two a rectangle: the cosine of its barn doors' angle from its normal, then their length in
|
|
134
|
+
* metres — 0 and 0 for none. See `AreaLightSource.barnDoorAngle`.
|
|
135
|
+
*/
|
|
136
|
+
readonly barnDoors?: Float32Array;
|
|
116
137
|
}
|
|
117
138
|
|
|
118
139
|
export function createAreaLightBuffer(capacity: number = MAX_AREA_LIGHTS): AreaLightBuffer {
|
|
@@ -125,9 +146,45 @@ export function createAreaLightBuffer(capacity: number = MAX_AREA_LIGHTS): AreaL
|
|
|
125
146
|
sizes: new Float32Array(capacity * 2),
|
|
126
147
|
twoSided: new Float32Array(capacity),
|
|
127
148
|
ranges: new Float32Array(capacity),
|
|
149
|
+
barnDoors: new Float32Array(capacity * 2),
|
|
128
150
|
};
|
|
129
151
|
}
|
|
130
152
|
|
|
153
|
+
/** Unreal's default barn door length, in metres. See `AreaLightSource.barnDoorLength`. */
|
|
154
|
+
export const DEFAULT_BARN_DOOR_LENGTH = 0.2;
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* A rectangle's barn doors as the shader reads them, into `out[at]` and `out[at + 1]`: the cosine of
|
|
158
|
+
* their angle and their length. An angle not between 0 and 90 degrees, or a length not above zero,
|
|
159
|
+
* is no doors at all, written as 0 and 0 — which the shader reads as "nothing hidden".
|
|
160
|
+
*/
|
|
161
|
+
export function barnDoorsOf(light: AreaLightSource, out: Float32Array, at: number): void {
|
|
162
|
+
const angle = light.barnDoorAngle;
|
|
163
|
+
const length = light.barnDoorLength ?? DEFAULT_BARN_DOOR_LENGTH;
|
|
164
|
+
if (angle === undefined || !(angle >= 0 && angle < 90) || !(length > 0)) {
|
|
165
|
+
out[at] = 0;
|
|
166
|
+
out[at + 1] = 0;
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
out[at] = Math.cos((angle * Math.PI) / 180);
|
|
170
|
+
out[at + 1] = length;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* The fixed arm's half extents and barn doors, four a rectangle as `uAreaLightSize` holds them: the
|
|
175
|
+
* two half extents, then the doors' cosine and length. Into a target the binder owns, for the first
|
|
176
|
+
* `MAX_AREA_LIGHTS` alone, which are all the fixed arm reads.
|
|
177
|
+
*/
|
|
178
|
+
export function packAreaSizesAndDoors(lights: AreaLightBuffer, out: Float32Array): void {
|
|
179
|
+
const doors = lights.barnDoors;
|
|
180
|
+
for (let a = 0; a < MAX_AREA_LIGHTS; a++) {
|
|
181
|
+
out[a * 4] = lights.sizes[a * 2] ?? 0;
|
|
182
|
+
out[a * 4 + 1] = lights.sizes[a * 2 + 1] ?? 0;
|
|
183
|
+
out[a * 4 + 2] = doors?.[a * 2] ?? 0;
|
|
184
|
+
out[a * 4 + 3] = doors?.[a * 2 + 1] ?? 0;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
131
188
|
/**
|
|
132
189
|
* How far a rectangle's light reaches when it names no range: where its irradiance on its own axis,
|
|
133
190
|
* `L · A / d²` for a rectangle of radiance `L` and area `A` seen from far, falls to
|
|
@@ -227,6 +284,7 @@ export function selectAreaLights(
|
|
|
227
284
|
? light.range
|
|
228
285
|
: areaRange(light.r, light.g, light.b, light.halfWidth, light.halfHeight);
|
|
229
286
|
}
|
|
287
|
+
if (out.barnDoors !== undefined) barnDoorsOf(light, out.barnDoors, slot * 2);
|
|
230
288
|
}
|
|
231
289
|
|
|
232
290
|
return out;
|
package/src/render/atmosphere.ts
CHANGED
|
@@ -47,6 +47,19 @@ export interface Atmosphere {
|
|
|
47
47
|
/** Linear mode only: where haze begins, and where it is total. */
|
|
48
48
|
fogNear?: number;
|
|
49
49
|
fogFar?: number;
|
|
50
|
+
/**
|
|
51
|
+
* The medium only: how far along the view ray it begins, in metres. Absent or 0 is at the
|
|
52
|
+
* camera, as every world before it was drawn.
|
|
53
|
+
*
|
|
54
|
+
* The air nearer than this is perfectly clear, and what lies beyond is fogged by the air past
|
|
55
|
+
* this point alone — so a faint, bright haze can lie over a distant crowd while the figures a
|
|
56
|
+
* few metres from the camera stay sharp, which no density can do when the medium starts at the
|
|
57
|
+
* eye: there it veils the whole frame, lifting the shadows and draining the colour before it
|
|
58
|
+
* reaches anything far enough to want it. Unlike `linear`'s `fogNear`, nothing past it is a
|
|
59
|
+
* ramp: it is the same extinction, integrated from further out. **What it gives up** is the air
|
|
60
|
+
* between the camera and the start, which a real medium has; that is the point of it.
|
|
61
|
+
*/
|
|
62
|
+
fogStart?: number;
|
|
50
63
|
underwater: UnderwaterAtmosphere | null;
|
|
51
64
|
}
|
|
52
65
|
|
|
@@ -132,7 +145,15 @@ export function resolveAtmosphere(
|
|
|
132
145
|
out.fogHeightFalloff = atmosphere.fogHeightFalloff;
|
|
133
146
|
out.fogEyeY = cameraY;
|
|
134
147
|
out.fogMode = atmosphere.fogMode === 'linear' ? 1 : 0;
|
|
135
|
-
|
|
148
|
+
/*
|
|
149
|
+
* **One slot, two meanings, and never both**: the ramp's near distance in linear mode, and in
|
|
150
|
+
* the medium where it begins (`fogStart`), which the medium never read before — so a world that
|
|
151
|
+
* left a `fogNear` on an exponential fog starts nothing, and draws exactly as it did.
|
|
152
|
+
*/
|
|
153
|
+
out.fogNear =
|
|
154
|
+
atmosphere.fogMode === 'linear'
|
|
155
|
+
? (atmosphere.fogNear ?? 0)
|
|
156
|
+
: Math.max(0, Number.isFinite(atmosphere.fogStart) ? (atmosphere.fogStart ?? 0) : 0);
|
|
136
157
|
out.fogFar = atmosphere.fogFar ?? 1;
|
|
137
158
|
/*
|
|
138
159
|
* The air medium stands in when nothing is submerged. The shaders multiply by the factor
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import type { Bounds } from '../../math/bounds.ts';
|
|
2
2
|
import type { Vec3 } from '../../math/color.ts';
|
|
3
|
+
import type { Camera } from '../camera.ts';
|
|
3
4
|
import type { ReadonlyMat4 } from 'gl-matrix';
|
|
4
5
|
import type { MeshData } from '../mesh.ts';
|
|
5
6
|
import type { ProbeBakeOptions } from '../reflectionProbe.ts';
|
|
6
7
|
import type { Renderer, TranslucentMeshOptions } from './webgl2/renderer.ts';
|
|
7
8
|
import type { InstancedOptions, MeshInstances } from '../instances.ts';
|
|
8
9
|
import type { SurfaceMaterial } from '../surfaceTexture.ts';
|
|
10
|
+
import type { SurfaceOverlay } from '../surfaceOverlay.ts';
|
|
9
11
|
import type { SurfaceSource } from '../compressedSource.ts';
|
|
10
12
|
import type { LightmapPage } from '../lightmap.ts';
|
|
13
|
+
import type { DeviceParticles } from '../particlePool.ts';
|
|
11
14
|
import type { ClothBindingData } from '../clothBindingData.ts';
|
|
12
15
|
import type { TextStyle } from '../textLayout.ts';
|
|
13
16
|
import type { SdfFont } from '../sdfFont.ts';
|
|
@@ -276,6 +279,7 @@ export type RendererApi = Omit<
|
|
|
276
279
|
| 'disposeCaustics'
|
|
277
280
|
| 'createParticles'
|
|
278
281
|
| 'drawParticles'
|
|
282
|
+
| 'drawDeviceParticles'
|
|
279
283
|
| 'disposeParticles'
|
|
280
284
|
| 'createWindStreaks'
|
|
281
285
|
| 'drawWindStreaks'
|
|
@@ -302,10 +306,13 @@ export type RendererApi = Omit<
|
|
|
302
306
|
| 'createSurfaceTexture'
|
|
303
307
|
| 'createSurfaceTextureArray'
|
|
304
308
|
| 'createLightmap'
|
|
309
|
+
| 'createSceneCapture'
|
|
310
|
+
| 'captureScene'
|
|
305
311
|
| 'updateSurfaceTexture'
|
|
306
312
|
| 'disposeSurfaceTexture'
|
|
307
313
|
| 'setSurfaceTexture'
|
|
308
314
|
| 'setMaterial'
|
|
315
|
+
| 'setSurfaceOverlay'
|
|
309
316
|
| 'createClothBinding'
|
|
310
317
|
| 'createClothParticles'
|
|
311
318
|
| 'updateClothParticles'
|
|
@@ -336,6 +343,13 @@ export type RendererApi = Omit<
|
|
|
336
343
|
setCloth(binding: ClothBindingHandle | null, particles?: ClothParticlesHandle | null): void;
|
|
337
344
|
disposeClothBinding(binding: ClothBindingHandle): void;
|
|
338
345
|
disposeClothParticles(particles: ClothParticlesHandle): void;
|
|
346
|
+
/**
|
|
347
|
+
* What the following draws lay over their surfaces beyond their materials — a rim of light, a
|
|
348
|
+
* dissolve with a glowing edge, wrinkle normals by region — or null for none, which every pass
|
|
349
|
+
* starts with. Per draw, so one character's whole set of materials changes together. Its images
|
|
350
|
+
* are regions of one atlas (`SurfaceOverlay.maps`). See `surfaceOverlay.ts`.
|
|
351
|
+
*/
|
|
352
|
+
setSurfaceOverlay(overlay: SurfaceOverlay | null): void;
|
|
339
353
|
/**
|
|
340
354
|
* Upload a caller's image, or a BC image's blocks with their stored chain where
|
|
341
355
|
* `compressedFormats` has the format. See `SurfaceTextureHandle` for the boundary this guards.
|
|
@@ -365,13 +379,41 @@ export type RendererApi = Omit<
|
|
|
365
379
|
|
|
366
380
|
/**
|
|
367
381
|
* A baked lightmap page on the device, as the texture a `lightmapModel` material takes for its
|
|
368
|
-
* `modelMap`: two layers of
|
|
369
|
-
*
|
|
382
|
+
* `modelMap`: two layers of four bytes a texel, the irradiance as an rgb9e5 word and the
|
|
383
|
+
* direction, which the lit stage decodes and filters itself, clamped at the page's edges, with no
|
|
384
|
+
* mip chain. A page whose irradiance arrives packed uploads where it lies. Disposed as any texture is, with
|
|
370
385
|
* `disposeSurfaceTexture`; `updateSurfaceTexture` refuses it, as it refuses any array. See
|
|
371
386
|
* `lightmap.ts` for what a surface does with it.
|
|
372
387
|
*/
|
|
373
388
|
createLightmap(page: LightmapPage): SurfaceTextureHandle;
|
|
374
389
|
|
|
390
|
+
/**
|
|
391
|
+
* A texture the scene can be drawn into from a camera of the caller's (`captureScene`), which a
|
|
392
|
+
* material then shows like any image — most often as its `emissive`, for a screen in the world
|
|
393
|
+
* showing what a camera elsewhere sees. `width` and `height` in pixels, 1 to 4096. It holds
|
|
394
|
+
* radiance, at the colour format a probe takes: half floats under `hdrScene`, bytes otherwise.
|
|
395
|
+
* Disposed as any texture is; `updateSurfaceTexture` refuses it. See `sceneCapture.ts`.
|
|
396
|
+
*/
|
|
397
|
+
createSceneCapture(width: number, height: number): SurfaceTextureHandle;
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Draw the scene into a capture from `camera`, at the moment a caller says, usually once a frame
|
|
401
|
+
* before the frame's own world. `draw` is handed the camera, its matrices updated for the
|
|
402
|
+
* capture's shape, and submits the scene exactly as it would to the screen — `bindMeshPass`,
|
|
403
|
+
* `drawMesh` and the rest; `bindMeshPass` the frame's own camera again afterwards. The output
|
|
404
|
+
* transform is held off, so a screen showing it is graded once, with the frame.
|
|
405
|
+
*
|
|
406
|
+
* **The mesh pass alone**, as a probe bake is: what a scene draws after its world is in the
|
|
407
|
+
* capture only if `draw` draws it, and no temporal resolve or effect after the world runs on it.
|
|
408
|
+
* Returns whether it drew: false for a texture that is not a capture.
|
|
409
|
+
*/
|
|
410
|
+
captureScene(
|
|
411
|
+
capture: SurfaceTextureHandle,
|
|
412
|
+
camera: Camera,
|
|
413
|
+
clearColor: Vec3,
|
|
414
|
+
draw: (camera: Camera) => void,
|
|
415
|
+
): boolean;
|
|
416
|
+
|
|
375
417
|
/**
|
|
376
418
|
* Replace a texture's pixels, keeping the GPU object and its sampler state.
|
|
377
419
|
*
|
|
@@ -735,6 +777,20 @@ export type RendererApi = Omit<
|
|
|
735
777
|
timeSeconds: number,
|
|
736
778
|
): void;
|
|
737
779
|
|
|
780
|
+
/**
|
|
781
|
+
* Draw a pool's particles from a buffer a caller's own compute wrote (`registerCompute`), where
|
|
782
|
+
* they lie: the pool's material, blend, flipbook and fades, its instance stream the caller's
|
|
783
|
+
* buffer. WebGPU only; WebGL2 says so once and draws nothing. Not sorted. See `DeviceParticles`
|
|
784
|
+
* for the layout.
|
|
785
|
+
*/
|
|
786
|
+
drawDeviceParticles(
|
|
787
|
+
batch: ParticleHandle,
|
|
788
|
+
particles: DeviceParticles,
|
|
789
|
+
camera: Parameters<Renderer['drawParticles']>[2],
|
|
790
|
+
env: Parameters<Renderer['drawParticles']>[3],
|
|
791
|
+
timeSeconds: number,
|
|
792
|
+
): void;
|
|
793
|
+
|
|
738
794
|
/** Release a particle pool. */
|
|
739
795
|
disposeParticles(batch: ParticleHandle): void;
|
|
740
796
|
|