@driftengine/core 4.9.0 → 4.10.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 +62 -11
- package/README.md +43 -38
- package/dist/index.d.ts +3 -1
- package/dist/index.js +4 -3
- package/dist/render/ambientHarmonics.d.ts +1 -1
- package/dist/render/ambientHarmonics.js +1 -1
- package/dist/render/areaLights.d.ts +4 -4
- package/dist/render/areaLights.js +1 -1
- package/dist/render/backend/api.d.ts +10 -3
- package/dist/render/backend/select.js +11 -5
- package/dist/render/backend/webgl2/boneAnimations.d.ts +29 -0
- package/dist/render/backend/webgl2/boneAnimations.js +80 -0
- package/dist/render/backend/webgl2/instanced.d.ts +5 -1
- package/dist/render/backend/webgl2/instanced.js +13 -1
- package/dist/render/backend/webgl2/renderer.d.ts +90 -2
- package/dist/render/backend/webgl2/renderer.js +253 -24
- package/dist/render/backend/webgl2/skinPaletteTexture.d.ts +5 -3
- package/dist/render/backend/webgl2/skinPaletteTexture.js +6 -4
- package/dist/render/backend/webgpu/boneAnimations.d.ts +34 -0
- package/dist/render/backend/webgpu/boneAnimations.js +76 -0
- package/dist/render/backend/webgpu/buffers.d.ts +19 -4
- package/dist/render/backend/webgpu/buffers.js +18 -11
- package/dist/render/backend/webgpu/compressedUpload.d.ts +0 -1
- package/dist/render/backend/webgpu/compressedUpload.js +53 -8
- package/dist/render/backend/webgpu/depthPass.d.ts +59 -3
- package/dist/render/backend/webgpu/depthPass.js +49 -14
- package/dist/render/backend/webgpu/drawCommand.d.ts +18 -4
- package/dist/render/backend/webgpu/drawCommand.js +10 -3
- package/dist/render/backend/webgpu/flatPass.d.ts +85 -42
- package/dist/render/backend/webgpu/flatPass.js +136 -20
- package/dist/render/backend/webgpu/instanced.d.ts +14 -1
- package/dist/render/backend/webgpu/instanced.js +24 -1
- package/dist/render/backend/webgpu/materialStore.d.ts +101 -0
- package/dist/render/backend/webgpu/materialStore.js +265 -0
- package/dist/render/backend/webgpu/pipelineCache.d.ts +6 -0
- package/dist/render/backend/webgpu/pipelineCache.js +7 -0
- package/dist/render/backend/webgpu/renderer.d.ts +152 -35
- package/dist/render/backend/webgpu/renderer.js +787 -142
- package/dist/render/backend/webgpu/skinPaletteRing.d.ts +7 -3
- package/dist/render/backend/webgpu/skinPaletteRing.js +13 -7
- package/dist/render/backend/webgpu/skinPaletteTexture.d.ts +6 -0
- package/dist/render/backend/webgpu/skinPaletteTexture.js +7 -1
- package/dist/render/backend/webgpu/staticBundles.d.ts +125 -0
- package/dist/render/backend/webgpu/staticBundles.js +241 -0
- package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +10 -3
- package/dist/render/backend/webgpu/surfaceTexturePass.js +20 -2
- package/dist/render/backend/webgpu/uniformRing.d.ts +21 -0
- package/dist/render/backend/webgpu/uniformRing.js +9 -1
- package/dist/render/bloomChain.d.ts +3 -3
- package/dist/render/boneAnimation.d.ts +92 -0
- package/dist/render/boneAnimation.js +133 -0
- package/dist/render/compressedSource.d.ts +33 -13
- package/dist/render/compressedSource.js +70 -25
- package/dist/render/glCompressed.js +83 -0
- package/dist/render/instances.d.ts +26 -0
- package/dist/render/lightBudget.d.ts +10 -0
- package/dist/render/lightBudget.js +10 -0
- package/dist/render/lightmap.d.ts +1 -1
- package/dist/render/mesh.d.ts +5 -0
- package/dist/render/mesh.js +6 -0
- package/dist/render/pointLightSelection.d.ts +4 -4
- package/dist/render/screenSpaceReflection.d.ts +2 -1
- package/dist/render/shaders/boneAnimation.d.ts +19 -0
- package/dist/render/shaders/boneAnimation.js +63 -0
- package/dist/render/shaders/depth.d.ts +7 -0
- package/dist/render/shaders/depth.js +35 -10
- package/dist/render/shaders/flat/index.d.ts +6 -0
- package/dist/render/shaders/flat/index.js +27 -5
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +15 -5
- package/dist/render/shaders/flat/models.js +1 -1
- package/dist/render/shaders/flat/overlay.js +1 -1
- package/dist/render/shaders/flat/pointShadow.js +2 -2
- package/dist/render/shaders/flat/preamble.js +38 -24
- package/dist/render/shaders/generated/depth.wgsl.d.ts +93 -0
- package/dist/render/shaders/generated/depth.wgsl.js +93 -0
- package/dist/render/shaders/generated/flat.wgsl.d.ts +3077 -2903
- package/dist/render/shaders/generated/flat.wgsl.js +3100 -2925
- package/dist/render/shaders/outputTransform.d.ts +7 -0
- package/dist/render/shaders/outputTransform.js +28 -14
- package/dist/render/shaders/vertexChannel.d.ts +7 -0
- package/dist/render/shaders/vertexChannel.js +26 -0
- package/dist/render/staticDraws.d.ts +47 -0
- package/dist/render/staticDraws.js +43 -0
- package/dist/render/surfaceTexture.d.ts +38 -7
- package/dist/render/surfaceTexture.js +56 -17
- package/package.json +6 -6
- package/src/index.ts +8 -2
- package/src/render/ambientHarmonics.ts +1 -1
- package/src/render/areaLights.ts +4 -4
- package/src/render/backend/api.ts +13 -3
- package/src/render/backend/select.ts +10 -5
- package/src/render/backend/webgl2/boneAnimations.ts +105 -0
- package/src/render/backend/webgl2/instanced.ts +20 -1
- package/src/render/backend/webgl2/renderer.ts +315 -24
- package/src/render/backend/webgl2/skinPaletteTexture.ts +6 -4
- package/src/render/backend/webgpu/boneAnimations.ts +97 -0
- package/src/render/backend/webgpu/buffers.ts +42 -16
- package/src/render/backend/webgpu/compressedUpload.ts +59 -9
- package/src/render/backend/webgpu/depthPass.ts +51 -10
- package/src/render/backend/webgpu/drawCommand.ts +22 -4
- package/src/render/backend/webgpu/flatPass.ts +187 -14
- package/src/render/backend/webgpu/instanced.ts +32 -1
- package/src/render/backend/webgpu/materialStore.ts +276 -0
- package/src/render/backend/webgpu/pipelineCache.ts +7 -0
- package/src/render/backend/webgpu/renderer.ts +928 -155
- package/src/render/backend/webgpu/skinPaletteRing.ts +13 -7
- package/src/render/backend/webgpu/skinPaletteTexture.ts +7 -1
- package/src/render/backend/webgpu/staticBundles.ts +363 -0
- package/src/render/backend/webgpu/surfaceTexturePass.ts +29 -3
- package/src/render/backend/webgpu/uniformRing.ts +10 -1
- package/src/render/bloomChain.ts +3 -3
- package/src/render/boneAnimation.ts +223 -0
- package/src/render/compressedSource.ts +121 -29
- package/src/render/glCompressed.ts +83 -0
- package/src/render/instances.ts +27 -0
- package/src/render/lightBudget.ts +11 -0
- package/src/render/lightmap.ts +1 -1
- package/src/render/mesh.ts +6 -0
- package/src/render/pointLightSelection.ts +4 -4
- package/src/render/screenSpaceReflection.ts +2 -1
- package/src/render/shaders/boneAnimation.ts +63 -0
- package/src/render/shaders/depth.ts +45 -10
- package/src/render/shaders/flat/index.ts +33 -5
- package/src/render/shaders/flat/main.ts +15 -5
- package/src/render/shaders/flat/models.ts +1 -1
- package/src/render/shaders/flat/overlay.ts +1 -1
- package/src/render/shaders/flat/pointShadow.ts +2 -2
- package/src/render/shaders/flat/preamble.ts +38 -24
- package/src/render/shaders/generated/depth.wgsl.ts +95 -0
- package/src/render/shaders/generated/flat.wgsl.ts +3100 -2925
- package/src/render/shaders/outputTransform.ts +30 -14
- package/src/render/shaders/vertexChannel.ts +27 -0
- package/src/render/staticDraws.ts +95 -0
- package/src/render/surfaceTexture.ts +94 -27
|
@@ -48,20 +48,8 @@ vec3 highlightShoulder(vec3 c) {
|
|
|
48
48
|
}
|
|
49
49
|
`;
|
|
50
50
|
|
|
51
|
-
/**
|
|
52
|
-
|
|
53
|
-
*
|
|
54
|
-
* Declares its own two uniforms, so including it is the whole of what a fragment stage has to
|
|
55
|
-
* do besides calling it. Both are ints and floats rather than defines because the choice is a
|
|
56
|
-
* caller's, per draw, and a permutation per output transform would double a pipeline count for
|
|
57
|
-
* a branch that costs one compare.
|
|
58
|
-
*/
|
|
59
|
-
export const OUTPUT_TRANSFORM_GLSL = `
|
|
60
|
-
/** 0 none, 1 sRGB, 2 ACES then sRGB, 3 the highlight shoulder then sRGB. See renderQuality.ts. */
|
|
61
|
-
uniform int uOutputTransform;
|
|
62
|
-
/** Scales the scene into the tone curve. 1 is the reference grade. See renderQuality.ts. */
|
|
63
|
-
uniform float uOutputExposure;
|
|
64
|
-
|
|
51
|
+
/** The fit and `applyOutputTransform`, below whichever declaration of the two uniforms. */
|
|
52
|
+
const OUTPUT_TRANSFORM_BODY_GLSL = `
|
|
65
53
|
/*
|
|
66
54
|
* ACES: the reference fit, not the cheap one.
|
|
67
55
|
*
|
|
@@ -128,3 +116,31 @@ vec3 applyOutputTransform(vec3 c) {
|
|
|
128
116
|
return linearToSrgb(c);
|
|
129
117
|
}
|
|
130
118
|
`;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* `uOutputTransform` and `uOutputExposure`, the fit, and `applyOutputTransform`.
|
|
122
|
+
*
|
|
123
|
+
* Declares its own two uniforms, so including it is the whole of what a fragment stage has to
|
|
124
|
+
* do besides calling it. Both are ints and floats rather than defines because the choice is a
|
|
125
|
+
* caller's, per draw, and a permutation per output transform would double a pipeline count for
|
|
126
|
+
* a branch that costs one compare.
|
|
127
|
+
*/
|
|
128
|
+
export const OUTPUT_TRANSFORM_GLSL = `
|
|
129
|
+
/** 0 none, 1 sRGB, 2 ACES then sRGB, 3 the highlight shoulder then sRGB. See renderQuality.ts. */
|
|
130
|
+
uniform int uOutputTransform;
|
|
131
|
+
/** Scales the scene into the tone curve. 1 is the reference grade. See renderQuality.ts. */
|
|
132
|
+
uniform float uOutputExposure;
|
|
133
|
+
${OUTPUT_TRANSFORM_BODY_GLSL}`;
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* The same, for the lit stage, whose two uniforms live in its material block: a draw that opts out
|
|
137
|
+
* of the grade sets them, so they change with the material and not with the pass. See
|
|
138
|
+
* `hoistUniformBlock` in `scripts/wgsl/transform.mjs`. Text otherwise identical, so WebGL2 runs
|
|
139
|
+
* the same program it always has.
|
|
140
|
+
*/
|
|
141
|
+
export const LIT_OUTPUT_TRANSFORM_GLSL = `
|
|
142
|
+
/** 0 none, 1 sRGB, 2 ACES then sRGB, 3 the highlight shoulder then sRGB. See renderQuality.ts. */
|
|
143
|
+
uniform int uOutputTransform; // wgsl:material
|
|
144
|
+
/** Scales the scene into the tone curve. 1 is the reference grade. See renderQuality.ts. */
|
|
145
|
+
uniform float uOutputExposure; // wgsl:material
|
|
146
|
+
${OUTPUT_TRANSFORM_BODY_GLSL}`;
|
|
@@ -39,6 +39,33 @@ uniform float uWindTime;
|
|
|
39
39
|
uniform vec2 uWindSpatialPhase;
|
|
40
40
|
`;
|
|
41
41
|
|
|
42
|
+
/**
|
|
43
|
+
* The same, for the lit stage, whose wind lives in its view block: the frame's and not the draw's,
|
|
44
|
+
* so a draw's own block does not change when the wind does. See `hoistUniformBlock` in
|
|
45
|
+
* `scripts/wgsl/transform.mjs`. Text otherwise identical, so WebGL2 runs the same program it always
|
|
46
|
+
* has; the depth stage keeps the unmarked chunk, having no view block.
|
|
47
|
+
*/
|
|
48
|
+
export const LIT_CHANNEL_ATTRIBUTE = `
|
|
49
|
+
layout(location = 13) in vec4 aChannel;
|
|
50
|
+
|
|
51
|
+
/*
|
|
52
|
+
* The frame's wind, already converted by \`resolveScatterDeform\` — a normalised direction, metres
|
|
53
|
+
* of bend, a gust amplitude and a scaled clock.
|
|
54
|
+
*
|
|
55
|
+
* **The same numbers the scatter batch reads, and that is the rule rather than an economy.** A
|
|
56
|
+
* second conversion here would be a second wind however identical its inputs, and the failure is
|
|
57
|
+
* not localised: it shows up as a scene that does not cohere, a canopy leaning one way while the
|
|
58
|
+
* grass beneath it leans another, with no single element looking wrong. See "One wind, sampled
|
|
59
|
+
* once" in AGENTS.md.
|
|
60
|
+
*/
|
|
61
|
+
uniform vec2 uWindDirection; // wgsl:view
|
|
62
|
+
uniform float uWindSpeed; // wgsl:view
|
|
63
|
+
uniform float uWindGust; // wgsl:view
|
|
64
|
+
uniform float uWindTime; // wgsl:view
|
|
65
|
+
/* Spatial frequency of the travelling gust, per metre. */
|
|
66
|
+
uniform vec2 uWindSpatialPhase; // wgsl:view
|
|
67
|
+
`;
|
|
68
|
+
|
|
42
69
|
/**
|
|
43
70
|
* Where the wind puts a vertex, given how much of it that vertex takes.
|
|
44
71
|
*
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Draws that do not change between frames, captured once from an enumeration and replayed into any
|
|
3
|
+
* colour pass: what `createStaticDraws` keeps, and the one rule both backends replay it by.
|
|
4
|
+
*
|
|
5
|
+
* **Captured, not referenced.** An enumeration is a closure over the caller's state, written to
|
|
6
|
+
* allocate nothing, so it hands a sink its scratch: the matrix it fills for every entry and the
|
|
7
|
+
* count it rewrites for the next batch. A list kept across frames copies both when it is made, so
|
|
8
|
+
* what it draws is what the enumeration said then. Meshes, batches and materials are held by
|
|
9
|
+
* reference: they are the caller's, and disposing one a list still holds is the caller's error.
|
|
10
|
+
*
|
|
11
|
+
* **Rigid meshes and instanced batches only.** A skinned mesh is posed every frame and a scatter
|
|
12
|
+
* batch bends with every frame's wind, so neither can be kept, and both are refused by name rather
|
|
13
|
+
* than captured in the pose they happened to have. A caller draws them each frame as it always has.
|
|
14
|
+
*
|
|
15
|
+
* What a backend does with the list is its own: WebGL2 replays the entries every frame through
|
|
16
|
+
* the scene-caster path, and WebGPU records them once into a render bundle per view. See
|
|
17
|
+
* `RendererApi.createStaticDraws` for what a caller is promised, which is the same on both.
|
|
18
|
+
*/
|
|
19
|
+
import type { InstancedHandle, MeshHandle } from './backend/api.ts';
|
|
20
|
+
import type { MeshInstances } from './instances.ts';
|
|
21
|
+
import type { SceneCasterMaterial, ShadowCasterSink, ShadowCasters } from './shadowCasters.ts';
|
|
22
|
+
|
|
23
|
+
/** A list from `createStaticDraws`. Opaque but for its size. */
|
|
24
|
+
export interface StaticDrawsHandle {
|
|
25
|
+
/** How many entries it holds: meshes and batches, a batch counting once. */
|
|
26
|
+
readonly draws: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** One captured entry. */
|
|
30
|
+
export type StaticEntry =
|
|
31
|
+
| {
|
|
32
|
+
readonly kind: 'mesh';
|
|
33
|
+
readonly mesh: MeshHandle;
|
|
34
|
+
readonly model: Float32Array;
|
|
35
|
+
readonly material: SceneCasterMaterial;
|
|
36
|
+
}
|
|
37
|
+
| {
|
|
38
|
+
readonly kind: 'instanced';
|
|
39
|
+
readonly batch: InstancedHandle;
|
|
40
|
+
readonly data: MeshInstances;
|
|
41
|
+
readonly material: SceneCasterMaterial;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/** What both backends hold: the entries, and whether the list has been disposed. */
|
|
45
|
+
export interface StaticDrawsList extends StaticDrawsHandle {
|
|
46
|
+
readonly entries: readonly StaticEntry[];
|
|
47
|
+
disposed: boolean;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Enumerate `casters` once into a list. Allocates: it runs when the list is made, not per frame. */
|
|
51
|
+
export function captureStaticDraws(casters: ShadowCasters): StaticDrawsList {
|
|
52
|
+
const entries: StaticEntry[] = [];
|
|
53
|
+
const sink: ShadowCasterSink = {
|
|
54
|
+
mesh: (mesh, model, material) => {
|
|
55
|
+
entries.push({
|
|
56
|
+
kind: 'mesh',
|
|
57
|
+
mesh,
|
|
58
|
+
model: new Float32Array(model as Float32Array),
|
|
59
|
+
material: material ?? null,
|
|
60
|
+
});
|
|
61
|
+
},
|
|
62
|
+
instanced: (batch, data, material) => {
|
|
63
|
+
/* The batch's placements are on the device already; the count is the caller's scratch. */
|
|
64
|
+
entries.push({ kind: 'instanced', batch, data: { ...data }, material: material ?? null });
|
|
65
|
+
},
|
|
66
|
+
skinnedMesh: () => {
|
|
67
|
+
throw new Error(
|
|
68
|
+
'createStaticDraws: a skinned mesh is posed every frame, so a list kept across frames ' +
|
|
69
|
+
'cannot hold one. Draw it each frame, with drawMesh or drawSceneCasters.',
|
|
70
|
+
);
|
|
71
|
+
},
|
|
72
|
+
scatter: () => {
|
|
73
|
+
throw new Error(
|
|
74
|
+
'createStaticDraws: a scatter batch bends with every frame’s wind, so a list kept across ' +
|
|
75
|
+
'frames cannot hold one. Draw it each frame with drawScatter.',
|
|
76
|
+
);
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
casters(sink);
|
|
80
|
+
return {
|
|
81
|
+
entries,
|
|
82
|
+
get draws() {
|
|
83
|
+
return entries.length;
|
|
84
|
+
},
|
|
85
|
+
disposed: false,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Hand every entry to `sink`, in the order it was captured. */
|
|
90
|
+
export function replayStaticDraws(list: StaticDrawsList, sink: ShadowCasterSink): void {
|
|
91
|
+
for (const entry of list.entries) {
|
|
92
|
+
if (entry.kind === 'mesh') sink.mesh(entry.mesh, entry.model, entry.material);
|
|
93
|
+
else sink.instanced?.(entry.batch, entry.data, entry.material);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -10,7 +10,12 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import type { SurfaceModel } from './surfaceModel.ts';
|
|
12
12
|
import { layerSize, refuseArrayUpdate, sourceSize } from './textureSource.ts';
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
compressedLayers,
|
|
15
|
+
isCompressedSource,
|
|
16
|
+
planBlocks,
|
|
17
|
+
refuseBlockUpdate,
|
|
18
|
+
} from './compressedSource.ts';
|
|
14
19
|
import type {
|
|
15
20
|
BlockFormat,
|
|
16
21
|
CompressedTextureFormat,
|
|
@@ -119,7 +124,7 @@ export interface SurfaceMaterial<Texture = SurfaceTexture> {
|
|
|
119
124
|
* which is every light's default too, so a scene that names no channel is lit as it always was.
|
|
120
125
|
*
|
|
121
126
|
* A point or spot light shades this surface only where its `PointLightSet.lightChannels` and this
|
|
122
|
-
* share a bit —
|
|
127
|
+
* share a bit — lighting channels. A character's own key and rim lights on channel 2,
|
|
123
128
|
* and the character's materials on 3, light the character and leave the floor around it as the
|
|
124
129
|
* stage's lights alone have it. **What it does not reach**: the sun, the sky and the probes, area
|
|
125
130
|
* lights, DriftLight's volume and the GPU-driven pipeline light every surface whatever its mask.
|
|
@@ -153,6 +158,22 @@ export interface SurfaceMaterial<Texture = SurfaceTexture> {
|
|
|
153
158
|
* which is the opposite of what the caller asked for.
|
|
154
159
|
*/
|
|
155
160
|
occlusionStrength?: number;
|
|
161
|
+
/**
|
|
162
|
+
* How much of the environment this material's draws mirror, 0 to 1: the number
|
|
163
|
+
* `setSurfaceReflectivity` sets for every draw, stated by the material for its own. Absent, its
|
|
164
|
+
* draws wear the setter's, as every material's did before this existed.
|
|
165
|
+
*
|
|
166
|
+
* **Here so a static list can carry it.** A list keeps each entry's material and none of the
|
|
167
|
+
* renderer's state between draws (`createStaticDraws`), so a stage whose batches each mirror their
|
|
168
|
+
* own share of a baked reflection says so in the material it records. What it gives up is nothing a
|
|
169
|
+
* setter could do: one called after this material is set is ignored for the material's draws.
|
|
170
|
+
*/
|
|
171
|
+
reflectivity?: number;
|
|
172
|
+
/**
|
|
173
|
+
* How bright the environment this material's draws reflect is: `setEnvironmentGain`'s number,
|
|
174
|
+
* stated by the material for its own, and absent the setter's. Held non-negative.
|
|
175
|
+
*/
|
|
176
|
+
environmentGain?: number;
|
|
156
177
|
/**
|
|
157
178
|
* Scales the emissive map, per channel. Default 1.
|
|
158
179
|
*
|
|
@@ -176,14 +197,14 @@ export interface SurfaceMaterial<Texture = SurfaceTexture> {
|
|
|
176
197
|
modelMap?: Texture | null;
|
|
177
198
|
/**
|
|
178
199
|
* Whether a lamp's and the sun's highlight on this surface is GGX's own, `π · D · Vis · F · N·L`,
|
|
179
|
-
* as
|
|
180
|
-
* peak of one. **False by default**, every surface as it was.
|
|
200
|
+
* as physically based renderers and this engine's skin and eye shade it, rather than the engine's
|
|
201
|
+
* lobe scaled to a peak of one. **False by default**, every surface as it was.
|
|
181
202
|
*
|
|
182
203
|
* The peak-normalised lobe is a look control: the specular attribute says how strong a highlight
|
|
183
204
|
* is and roughness how wide, apart. Physical, they are entangled as they are in a real surface —
|
|
184
205
|
* the peak is `1 / (4 α²)` of the light head-on, so a polished surface's highlight is many times
|
|
185
206
|
* the look's and a rough one's lower and broader — and the specular attribute is read as the
|
|
186
|
-
* reflectance at normal incidence, F0:
|
|
207
|
+
* reflectance at normal incidence, F0: `0.08 × Specular`, 0.04 at its default. Fresnel
|
|
187
208
|
* then brightens every surface toward grazing, not only a metal. A rectangle's highlight is the
|
|
188
209
|
* lobe integrated over it with the attribute as F0 already, so it is unchanged either way.
|
|
189
210
|
*
|
|
@@ -292,7 +313,9 @@ export class SurfaceTexture {
|
|
|
292
313
|
/** The per-layer effects table, or null for a texture given none. See `surfaceEffects.ts`. */
|
|
293
314
|
private effectsTable: WebGLTexture | null = null;
|
|
294
315
|
/** The block format a compressed texture holds, or null for one uploaded from images. */
|
|
295
|
-
private
|
|
316
|
+
private blockFormat: BlockFormat | null;
|
|
317
|
+
/** How the texture is sampled, kept so blocks swapped in by `update` are sampled the same way. */
|
|
318
|
+
private readonly sampling: { wrap: number; nearest: boolean; anisotropy: number };
|
|
296
319
|
/** Level 0's size, which `flatLayer` copies; kept as `update` changes it. */
|
|
297
320
|
private width = 0;
|
|
298
321
|
private height = 0;
|
|
@@ -404,27 +427,13 @@ export class SurfaceTexture {
|
|
|
404
427
|
this.storage = this.srgb ? gl.SRGB8_ALPHA8 : gl.RGBA8;
|
|
405
428
|
uploadImages(gl, sources, this.srgb);
|
|
406
429
|
}
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MAG_FILTER, nearest ? gl.NEAREST : gl.LINEAR);
|
|
414
|
-
gl.texParameteri(
|
|
415
|
-
gl.TEXTURE_2D_ARRAY,
|
|
416
|
-
gl.TEXTURE_MIN_FILTER,
|
|
417
|
-
this.mipmapped
|
|
418
|
-
? nearest
|
|
419
|
-
? gl.NEAREST_MIPMAP_LINEAR
|
|
420
|
-
: gl.LINEAR_MIPMAP_LINEAR
|
|
421
|
-
: nearest
|
|
422
|
-
? gl.NEAREST
|
|
423
|
-
: gl.LINEAR,
|
|
424
|
-
);
|
|
425
|
-
|
|
430
|
+
this.sampling = {
|
|
431
|
+
wrap,
|
|
432
|
+
nearest: options.filter === 'nearest',
|
|
433
|
+
anisotropy: options.anisotropy ?? DEFAULT_ANISOTROPY,
|
|
434
|
+
};
|
|
435
|
+
this.applySampling(gl);
|
|
426
436
|
if (this.mipmapped && this.blockFormat === null) gl.generateMipmap(gl.TEXTURE_2D_ARRAY);
|
|
427
|
-
applyAnisotropy(gl, options.anisotropy ?? DEFAULT_ANISOTROPY);
|
|
428
437
|
gl.bindTexture(gl.TEXTURE_2D_ARRAY, null);
|
|
429
438
|
|
|
430
439
|
/* Half floats, nearest, clamped: a table read by texelFetch, whose every whole number is under
|
|
@@ -475,9 +484,17 @@ export class SurfaceTexture {
|
|
|
475
484
|
* Not a hot path: it re-uploads the whole image and regenerates the chain. A caller
|
|
476
485
|
* doing this every frame at any size wants to know that it costs what it costs.
|
|
477
486
|
*/
|
|
478
|
-
update(
|
|
487
|
+
update(
|
|
488
|
+
gl: WebGL2RenderingContext,
|
|
489
|
+
source: TexImageSource | CompressedTextureSource,
|
|
490
|
+
compressed: readonly CompressedTextureFormat[] = [],
|
|
491
|
+
): void {
|
|
479
492
|
if (this.texture === null) return;
|
|
480
493
|
refuseArrayUpdate(this.layers);
|
|
494
|
+
if (isCompressedSource(source)) {
|
|
495
|
+
this.swapToBlocks(gl, source, compressed);
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
481
498
|
refuseBlockUpdate(this.blockFormat);
|
|
482
499
|
const { width, height } = sourceSize(source);
|
|
483
500
|
this.width = width;
|
|
@@ -501,6 +518,56 @@ export class SurfaceTexture {
|
|
|
501
518
|
gl.bindTexture(gl.TEXTURE_2D_ARRAY, null);
|
|
502
519
|
}
|
|
503
520
|
|
|
521
|
+
/**
|
|
522
|
+
* Blocks in place of the image, behind the same handle: **a new GL object** rather than the
|
|
523
|
+
* image's levels respecified, because a chain shorter than the image's would leave the levels
|
|
524
|
+
* past it allocated, and immutable storage could not be respecified at all. Sampled as the image
|
|
525
|
+
* was, and refused before anything changes where the context does not take the format.
|
|
526
|
+
*/
|
|
527
|
+
private swapToBlocks(
|
|
528
|
+
gl: WebGL2RenderingContext,
|
|
529
|
+
source: CompressedTextureSource,
|
|
530
|
+
compressed: readonly CompressedTextureFormat[],
|
|
531
|
+
): void {
|
|
532
|
+
const plan = planBlocks([source], this.srgb, compressed);
|
|
533
|
+
const texture = gl.createTexture();
|
|
534
|
+
if (texture === null) throw new Error('SurfaceTexture: createTexture failed');
|
|
535
|
+
this.dropFlat(gl);
|
|
536
|
+
if (this.texture !== null) gl.deleteTexture(this.texture);
|
|
537
|
+
this.texture = texture;
|
|
538
|
+
this.blockFormat = source.format;
|
|
539
|
+
this.width = source.width;
|
|
540
|
+
this.height = source.height;
|
|
541
|
+
gl.bindTexture(gl.TEXTURE_2D_ARRAY, texture);
|
|
542
|
+
uploadCompressedArray(gl, plan.name, [source], this.mipmapped ? plan.levels : 1);
|
|
543
|
+
this.applySampling(gl);
|
|
544
|
+
gl.bindTexture(gl.TEXTURE_2D_ARRAY, null);
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* Wrap, filters and anisotropy on the bound texture. Nearest where the caller said their pixels
|
|
549
|
+
* are the subject; see `filter`. The mip chain is still blended between levels, because that is
|
|
550
|
+
* minification and this option is about magnification.
|
|
551
|
+
*/
|
|
552
|
+
private applySampling(gl: WebGL2RenderingContext): void {
|
|
553
|
+
const { wrap, nearest, anisotropy } = this.sampling;
|
|
554
|
+
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_WRAP_S, wrap);
|
|
555
|
+
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_WRAP_T, wrap);
|
|
556
|
+
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MAG_FILTER, nearest ? gl.NEAREST : gl.LINEAR);
|
|
557
|
+
gl.texParameteri(
|
|
558
|
+
gl.TEXTURE_2D_ARRAY,
|
|
559
|
+
gl.TEXTURE_MIN_FILTER,
|
|
560
|
+
this.mipmapped
|
|
561
|
+
? nearest
|
|
562
|
+
? gl.NEAREST_MIPMAP_LINEAR
|
|
563
|
+
: gl.LINEAR_MIPMAP_LINEAR
|
|
564
|
+
: nearest
|
|
565
|
+
? gl.NEAREST
|
|
566
|
+
: gl.LINEAR,
|
|
567
|
+
);
|
|
568
|
+
applyAnisotropy(gl, anisotropy);
|
|
569
|
+
}
|
|
570
|
+
|
|
504
571
|
/**
|
|
505
572
|
* Attach the only layer as the bound framebuffer's colour: a scene capture's target, drawn into
|
|
506
573
|
* by `captureScene`. Engine-internal, as `bind` is.
|