@driftengine/core 4.11.0 → 4.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.json +36 -0
- package/README.md +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/render/ambientOcclusionPass.d.ts +3 -2
- package/dist/render/ambientOcclusionPass.js +4 -2
- package/dist/render/backend/api.d.ts +21 -1
- package/dist/render/backend/webgl2/renderer.d.ts +45 -4
- package/dist/render/backend/webgl2/renderer.js +112 -19
- package/dist/render/backend/webgpu/renderer.d.ts +52 -1
- package/dist/render/backend/webgpu/renderer.js +237 -89
- package/dist/render/instances.d.ts +13 -1
- package/dist/render/instances.js +10 -0
- package/dist/render/occlusionFade.d.ts +21 -0
- package/dist/render/occlusionFade.js +27 -0
- package/dist/render/sceneTarget.d.ts +2 -0
- package/dist/render/sceneTarget.js +1 -1
- package/dist/render/shaders/ambientOcclusion.js +12 -1
- package/dist/render/shaders/flat/index.js +9 -2
- package/dist/render/shaders/flat/layered.d.ts +15 -7
- package/dist/render/shaders/flat/layered.js +114 -31
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +10 -3
- package/dist/render/shaders/flat/normalMap.d.ts +1 -1
- package/dist/render/shaders/flat/normalMap.js +11 -2
- package/dist/render/shaders/generated/ambientOcclusion.wgsl.d.ts +6 -1
- package/dist/render/shaders/generated/ambientOcclusion.wgsl.js +6 -1
- package/dist/render/shaders/generated/flat.wgsl.d.ts +48 -48
- package/dist/render/shaders/generated/flat.wgsl.js +71 -71
- package/dist/render/surfaceLayers.d.ts +83 -15
- package/dist/render/surfaceLayers.js +69 -13
- package/dist/render/surfaceTexture.d.ts +13 -4
- package/dist/render/transmission.d.ts +21 -0
- package/dist/render/transmission.js +21 -0
- package/dist/ui/splash.d.ts +42 -2
- package/dist/ui/splash.js +78 -13
- package/package.json +6 -6
- package/src/index.ts +2 -0
- package/src/render/ambientOcclusionPass.ts +4 -1
- package/src/render/backend/api.ts +31 -0
- package/src/render/backend/webgl2/renderer.ts +130 -20
- package/src/render/backend/webgpu/renderer.ts +371 -139
- package/src/render/instances.ts +18 -1
- package/src/render/occlusionFade.ts +29 -0
- package/src/render/sceneTarget.ts +3 -0
- package/src/render/shaders/ambientOcclusion.ts +12 -1
- package/src/render/shaders/flat/index.ts +9 -2
- package/src/render/shaders/flat/layered.ts +114 -31
- package/src/render/shaders/flat/main.ts +10 -3
- package/src/render/shaders/flat/normalMap.ts +11 -2
- package/src/render/shaders/generated/ambientOcclusion.wgsl.ts +6 -1
- package/src/render/shaders/generated/flat.wgsl.ts +71 -71
- package/src/render/surfaceLayers.ts +138 -19
- package/src/render/surfaceTexture.ts +13 -4
- package/src/render/transmission.ts +27 -0
- package/src/ui/splash.ts +83 -14
package/CHANGELOG.json
CHANGED
|
@@ -1,4 +1,40 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "4.11.1",
|
|
4
|
+
"date": "2026-10-10",
|
|
5
|
+
"entries": [
|
|
6
|
+
{
|
|
7
|
+
"kind": "added",
|
|
8
|
+
"short": "`SurfaceMaterial.layers` takes its mask from the ORM array or the vertex colour, sums layers, places them by the world, lays one by facing up, and lays a normal under them.",
|
|
9
|
+
"text": "`layers` covers more of the ways a layered surface is blended. `mask: 'orm'` reads the mask from the ORM array's layer past the material's layers, which leaves the model's slot to a lightmap's page, so a lightmapped surface blends its layers; `mask: 'vertex'` takes the weights from the vertex colour, which then tints nothing. `blend: 'sum'` mixes the base toward each layer times its weight, by the weights' sum held to 1, besides laying each over the ones before it. Under a `projection` the layers are placed by the world, each at its own repeats a metre, and `addMask` adds a mask placed the same way to one layer's weight. `facing` weighs one layer by how much the surface faces up, and `meshNormal` lays a normal map read at the mesh's own coordinates under the layers' blended normal. A triplanar projection lays layers on the horizontal plane, and says so once. On `demo/dev/layerFamilies.html` a mask carried by the ORM array draws exactly as the same mask as a map, keeps its layers under a lightmap's page, and every mode draws the same on both backends."
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"kind": "added",
|
|
13
|
+
"short": "`SurfaceMaterial.transmissionColor` names the colour light through a thin surface takes, where it was always the surface's own.",
|
|
14
|
+
"text": "`transmissionColor`, linear, is the colour the light from behind a thin surface (`diffuseTransmission`) takes through it, in place of the surface's own colour: a printed banner glowing a flat red behind its print rather than lit through in the print's colours. Absent, the surface's colour, as before. It rides the glass tint's colour lanes, which no opaque material reads, and a pane drawn between two draws of the material puts it back. On `demo/dev/thinLight.html?color=0,0.4,1` a red banner lit from behind went from 163, 24, 19 to 16, 74, 171 under the sun with no red let through, the same to the pixel on both backends."
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"kind": "added",
|
|
18
|
+
"short": "`setAmbientOcclusionFade(distance, radius)` fades ambient occlusion out with distance, so a sky dome or a far range is not shaded in rings.",
|
|
19
|
+
"text": "Ambient occlusion is whole up to `distance` metres from the eye and gone `radius` metres past it, held until changed; a negative distance, the default, fades nothing. Far off, the depth buffer's steps are metres apart and the estimate shaded them in bands across a sky dome or a mountain range; past the fade those pixels are left open and skip the estimate's walk. Bound in DriftScript as `drift/render`'s `occlusionFade`. On `demo/dev/aoFade.html` a ridge 3 km off went from 66,267 banded pixels to none, while the feet of the near boxes darkened exactly as before, on both backends."
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"kind": "added",
|
|
23
|
+
"short": "A lightmapped instance fades by `setDitherOpacity` too: its opacity rides its page region in sixty-fourths, where it had none.",
|
|
24
|
+
"text": "A lightmapped batch carries its page regions in the lane `MeshInstances.alphas` would take, so its instances could not fade. Their opacity now travels as whole steps added to the region's U offset, 64 of them, the screen door's own resolution, and the vertex stage takes them back off. An opaque instance's region is written to the bit as before; a fading one's offset keeps 16 bits of fraction, a sixteenth of a texel on a 4,096 page. On `demo/dev/lightmap.html?mode=tiles&fade=0.5` the faded tiles keep 50.0% of their pixels, each lit exactly as unfaded, and nothing else changes, on both backends."
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"kind": "added",
|
|
28
|
+
"short": "`prepareMesh` and `prepareInstanced` compile a draw's pipelines in its material before its first frame, so a scene drawn under `pipelineCompile: 'skip'` shows whole.",
|
|
29
|
+
"text": "`prepareMesh(mesh, material)` and `prepareInstanced(batch, material)` compile, off the main thread, every pipeline a draw of that mesh or batch in that material takes that making it did not: a two-sided, cut-out or surface-model variant, a skin's halves under the screen-space blur, and the material's lit switches; `{ translucent: true }` adds the blended ones. `ready()` resolves once they have. Under `pipelineCompile: 'skip'` a draw whose pipeline is new is left out until it lands, so a figure brought on screen came without its skin and hair for a few frames; prepared, it is drawn whole in its first frame. On WebGPU a prepared key is the one the draw asks for, worked out by the same function; on WebGL2 a surface model's programs are what a draw can wait for, and are what it prepares."
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"kind": "added",
|
|
33
|
+
"short": "`holdSplash(promise, { capMs })` keeps the engine's badge up until the game's own first screen has loaded, under a cap of its own.",
|
|
34
|
+
"text": "The badge left three seconds after the first frame, twenty at most, so a game whose first screen loads for longer was seen loading. `holdSplash` keeps it up until the promise settles, either way, as well as until the first frame; frames run behind it while the game loads, since a load may need them, and are held once it has settled for what is left of the minimum. `capMs` is the game's ceiling in place of the engine's twenty seconds, the longest of several holds, and the call answers whether there was a badge to hold. On `demo/dev/splashHold.html` a load settling at 6.2 s took the badge down at 6.7 s with 361 frames run under it, a rejected load the same, and an 8-second cap at 8.4 s."
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
},
|
|
2
38
|
{
|
|
3
39
|
"version": "4.11.0",
|
|
4
40
|
"date": "2026-10-10",
|
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ WebGL2 everywhere else, behind one API, and carries the fixed-step loop, the ren
|
|
|
6
6
|
input, the scene graph, geometry, text and the whole of `@driftengine/physics`, which it re-exports.
|
|
7
7
|
Everything else is a separate package a game adds when it reaches for it.
|
|
8
8
|
|
|
9
|
-
**Cost:
|
|
9
|
+
**Cost: 904.6 KB gzipped, importing `createRenderer`.** Measured by `scripts/size-gate.test.mjs`,
|
|
10
10
|
which fails the build if the figure drifts by more than 3%.
|
|
11
11
|
|
|
12
12
|
The manual is at **[driftengine.dev/docs](https://driftengine.dev/docs)**, with an example running
|
package/dist/index.d.ts
CHANGED
|
@@ -155,7 +155,7 @@ export { BLOOM_LEVELS } from './render/bloomChain.ts';
|
|
|
155
155
|
export type { BloomResponse } from './render/bloomChain.ts';
|
|
156
156
|
export { probeDevice } from './render/backend/probe.ts';
|
|
157
157
|
export type { ProbeVerdict } from './render/backend/probe.ts';
|
|
158
|
-
export type { BoltHandle, CausticsHandle, ClothBindingHandle, ClothParticlesHandle, FlockHandle, IncrementalMeshHandle, LineHandle, MeshHandle, MeshOptions, RenderBackend, RendererApi, ParticleHandle, PlumeHandle, ScatterHandle, InstancedHandle, SdfTextHandle, SurfaceTextureHandle, TextHandle, WaterHandle, WindStreakHandle, } from './render/backend/api.ts';
|
|
158
|
+
export type { BoltHandle, CausticsHandle, ClothBindingHandle, ClothParticlesHandle, FlockHandle, IncrementalMeshHandle, LineHandle, MeshHandle, MeshOptions, PrepareOptions, RenderBackend, RendererApi, ParticleHandle, PlumeHandle, ScatterHandle, InstancedHandle, SdfTextHandle, SurfaceTextureHandle, TextHandle, WaterHandle, WindStreakHandle, } from './render/backend/api.ts';
|
|
159
159
|
export type { ClothBindingData } from './render/clothBindingData.ts';
|
|
160
160
|
export { anisotropicModel, eyeModel, hairModel, lightmapModel, skinModel, } from './render/surfaceModel.ts';
|
|
161
161
|
export type { AnisotropicModel, EyeModel, HairModel, LightmapModel, SkinModel, SurfaceModel, SurfaceModelKind, } from './render/surfaceModel.ts';
|
|
@@ -344,7 +344,7 @@ export type { TouchControlsOptions } from './input/touchControls.ts';
|
|
|
344
344
|
export { pixelCursor } from './ui/pixelCursor.ts';
|
|
345
345
|
export { fullscreenWanted, requestFullscreenOnGesture } from './ui/fullscreen.ts';
|
|
346
346
|
export type { FullscreenConditions } from './ui/fullscreen.ts';
|
|
347
|
-
export { SPLASH_FADE_MS, SPLASH_HARD_CAP_MS, SPLASH_MIN_MS, forcedSplash, mountSplash, splashDecision, splashWanted, } from './ui/splash.ts';
|
|
347
|
+
export { SPLASH_FADE_MS, SPLASH_HARD_CAP_MS, SPLASH_MIN_MS, forcedSplash, holdSplash, mountSplash, splashDecision, splashWanted, } from './ui/splash.ts';
|
|
348
348
|
export type { MountedSplash, SplashConditions, SplashDecision, SplashOptions, } from './ui/splash.ts';
|
|
349
349
|
export { DEFAULT_CLIP_FPS, FrameRecorder, offerClip } from './ui/frameRecorder.ts';
|
|
350
350
|
export { stillFrame } from './ui/stillFrame.ts';
|
package/dist/index.js
CHANGED
|
@@ -439,7 +439,7 @@ export { fullscreenWanted, requestFullscreenOnGesture } from './ui/fullscreen.js
|
|
|
439
439
|
* they are here for the consumer that declines the automatic one and places its own, and because
|
|
440
440
|
* the two decisions behind it are worth reading before overriding either.
|
|
441
441
|
*/
|
|
442
|
-
export { SPLASH_FADE_MS, SPLASH_HARD_CAP_MS, SPLASH_MIN_MS, forcedSplash, mountSplash, splashDecision, splashWanted, } from './ui/splash.js';
|
|
442
|
+
export { SPLASH_FADE_MS, SPLASH_HARD_CAP_MS, SPLASH_MIN_MS, forcedSplash, holdSplash, mountSplash, splashDecision, splashWanted, } from './ui/splash.js';
|
|
443
443
|
export { DEFAULT_CLIP_FPS, FrameRecorder, offerClip } from './ui/frameRecorder.js';
|
|
444
444
|
// Not on `FrameRecorder`, so a screenshot does not cost a clip pipeline. See the module.
|
|
445
445
|
export { stillFrame } from './ui/stillFrame.js';
|
|
@@ -46,8 +46,9 @@ export declare class AmbientOcclusionPass {
|
|
|
46
46
|
* by, or null if this pass could not run, which the caller must read as "no occlusion"
|
|
47
47
|
* rather than as black.
|
|
48
48
|
*
|
|
49
|
-
* `invProjection` is column-major, as `gl-matrix` produces it.
|
|
49
|
+
* `invProjection` is column-major, as `gl-matrix` produces it. `fade` is `uFade`, where the
|
|
50
|
+
* occlusion fades out with distance (`occlusionFade.ts`).
|
|
50
51
|
*/
|
|
51
|
-
run(depth: WebGLTexture, width: number, height: number, radius: number, projScale: Float32Array, invProjection: Float32Array): WebGLTexture | null;
|
|
52
|
+
run(depth: WebGLTexture, width: number, height: number, radius: number, projScale: Float32Array, invProjection: Float32Array, fade: Float32Array): WebGLTexture | null;
|
|
52
53
|
dispose(): void;
|
|
53
54
|
}
|
|
@@ -102,9 +102,10 @@ export class AmbientOcclusionPass {
|
|
|
102
102
|
* by, or null if this pass could not run, which the caller must read as "no occlusion"
|
|
103
103
|
* rather than as black.
|
|
104
104
|
*
|
|
105
|
-
* `invProjection` is column-major, as `gl-matrix` produces it.
|
|
105
|
+
* `invProjection` is column-major, as `gl-matrix` produces it. `fade` is `uFade`, where the
|
|
106
|
+
* occlusion fades out with distance (`occlusionFade.ts`).
|
|
106
107
|
*/
|
|
107
|
-
run(depth, width, height, radius, projScale, invProjection) {
|
|
108
|
+
run(depth, width, height, radius, projScale, invProjection, fade) {
|
|
108
109
|
if (!this.ensureSize(width, height))
|
|
109
110
|
return null;
|
|
110
111
|
const { gl } = this;
|
|
@@ -123,6 +124,7 @@ export class AmbientOcclusionPass {
|
|
|
123
124
|
gl.uniform1i(this.estimateUniforms['uDepth'] ?? null, 0);
|
|
124
125
|
gl.uniform2fv(this.estimateUniforms['uProjScale'] ?? null, projScale);
|
|
125
126
|
gl.uniform1f(this.estimateUniforms['uRadius'] ?? null, radius);
|
|
127
|
+
gl.uniform2fv(this.estimateUniforms['uFade'] ?? null, fade);
|
|
126
128
|
gl.uniformMatrix4fv(this.estimateUniforms['uInvProjection'] ?? null, false, invProjection);
|
|
127
129
|
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
|
128
130
|
/*
|
|
@@ -177,6 +177,14 @@ export interface SurfaceTextureHandle {
|
|
|
177
177
|
* `WebGLBuffer` in a private field, so no WebGPU object can satisfy it however identical its
|
|
178
178
|
* shape. It is not meant to be read; it is meant to be enforced.
|
|
179
179
|
*/
|
|
180
|
+
/** Which of a draw's pipelines `prepareMesh` and `prepareInstanced` compile beside the opaque ones. */
|
|
181
|
+
export interface PrepareOptions {
|
|
182
|
+
/**
|
|
183
|
+
* The blended ones too, for a draw made with `drawTranslucentMesh` or `drawTranslucentInstanced`.
|
|
184
|
+
* Off by default: an opaque surface would pay a compile it never draws with.
|
|
185
|
+
*/
|
|
186
|
+
readonly translucent?: boolean;
|
|
187
|
+
}
|
|
180
188
|
/** What a mesh needs to be told at creation that its geometry cannot say. */
|
|
181
189
|
export interface MeshOptions {
|
|
182
190
|
/**
|
|
@@ -190,7 +198,7 @@ export interface MeshOptions {
|
|
|
190
198
|
}
|
|
191
199
|
export type RendererApi = Omit<{
|
|
192
200
|
[K in keyof Renderer]: Renderer[K];
|
|
193
|
-
}, 'gpuTimer' | 'createMesh' | 'createMeshIncremental' | 'updateMesh' | 'drawMesh' | 'disposeMesh' | 'drawTranslucentMesh' | 'drawLightVolume' | 'drawFilm' | 'createScatter' | 'drawScatter' | 'uploadScatter' | 'disposeScatter' | 'createInstanced' | 'uploadInstanced' | 'drawInstanced' | 'drawTranslucentInstanced' | 'disposeInstanced' | 'createWater' | 'drawWater' | 'disposeWater' | 'createPlumes' | 'drawPlumes' | 'disposePlumes' | 'setPlumeScale' | 'createCaustics' | 'drawCaustics' | 'disposeCaustics' | 'createParticles' | 'drawParticles' | 'drawDeviceParticles' | 'disposeParticles' | 'createWindStreaks' | 'drawWindStreaks' | 'disposeWindStreaks' | 'createFlock' | 'drawFlock' | 'disposeFlock' | 'createBolts' | 'drawBolts' | 'disposeBolts' | 'createLines' | 'drawLines' | 'disposeLines' | 'createText' | 'setText' | 'setPlate' | 'drawText' | 'textWidth' | 'disposeText' | 'createSdfText' | 'setSdfText' | 'drawSdfText' | 'disposeSdfText' | 'createSurfaceTexture' | 'createSurfaceTextureArray' | 'createLightmap' | 'createSceneCapture' | 'captureScene' | 'updateSurfaceTexture' | 'disposeSurfaceTexture' | 'setSurfaceTexture' | 'setMaterial' | 'setSurfaceOverlay' | 'createClothBinding' | 'createClothParticles' | 'updateClothParticles' | 'setCloth' | 'disposeClothBinding' | 'disposeClothParticles'> & {
|
|
201
|
+
}, 'gpuTimer' | 'createMesh' | 'createMeshIncremental' | 'updateMesh' | 'drawMesh' | 'disposeMesh' | 'drawTranslucentMesh' | 'drawLightVolume' | 'drawFilm' | 'createScatter' | 'drawScatter' | 'uploadScatter' | 'disposeScatter' | 'createInstanced' | 'uploadInstanced' | 'drawInstanced' | 'drawTranslucentInstanced' | 'disposeInstanced' | 'createWater' | 'drawWater' | 'disposeWater' | 'createPlumes' | 'drawPlumes' | 'disposePlumes' | 'setPlumeScale' | 'createCaustics' | 'drawCaustics' | 'disposeCaustics' | 'createParticles' | 'drawParticles' | 'drawDeviceParticles' | 'disposeParticles' | 'createWindStreaks' | 'drawWindStreaks' | 'disposeWindStreaks' | 'createFlock' | 'drawFlock' | 'disposeFlock' | 'createBolts' | 'drawBolts' | 'disposeBolts' | 'createLines' | 'drawLines' | 'disposeLines' | 'createText' | 'setText' | 'setPlate' | 'drawText' | 'textWidth' | 'disposeText' | 'createSdfText' | 'setSdfText' | 'drawSdfText' | 'disposeSdfText' | 'createSurfaceTexture' | 'createSurfaceTextureArray' | 'createLightmap' | 'createSceneCapture' | 'captureScene' | 'updateSurfaceTexture' | 'disposeSurfaceTexture' | 'setSurfaceTexture' | 'setMaterial' | 'prepareMesh' | 'prepareInstanced' | 'setSurfaceOverlay' | 'createClothBinding' | 'createClothParticles' | 'updateClothParticles' | 'setCloth' | 'disposeClothBinding' | 'disposeClothParticles'> & {
|
|
194
202
|
/**
|
|
195
203
|
* A mesh's cloth binding: which simulation triangle each of its vertices follows, where on it and
|
|
196
204
|
* by how much (`ClothBindingData`). The mesh must be skinned — a binding blends each vertex
|
|
@@ -299,6 +307,18 @@ export type RendererApi = Omit<{
|
|
|
299
307
|
* setter each — and `setSurfaceTexture` is now a wrapper over it.
|
|
300
308
|
*/
|
|
301
309
|
setMaterial(material: SurfaceMaterial<SurfaceTextureHandle> | null): void;
|
|
310
|
+
/**
|
|
311
|
+
* Compile, off the main thread, every pipeline a draw of `mesh` in `material` takes that making
|
|
312
|
+
* the mesh did not — a two-sided, cut-out or shaded-by-a-model variant, a material's lit switches,
|
|
313
|
+
* a skin's halves — so the first frame that draws it waits on none; `ready()` resolves once they
|
|
314
|
+
* have. For `pipelineCompile: 'skip'` above all, where a draw whose pipeline is new is left out
|
|
315
|
+
* until it lands: a figure brought on screen whole rather than without its face for a few frames.
|
|
316
|
+
* A rigged mesh is prepared as drawn with a palette, and one with targets both with weights and
|
|
317
|
+
* without. Call it between frames: it sets `material` to read it and leaves none set after.
|
|
318
|
+
*/
|
|
319
|
+
prepareMesh(mesh: MeshHandle, material: SurfaceMaterial<SurfaceTextureHandle> | null, options?: PrepareOptions): void;
|
|
320
|
+
/** `prepareMesh` for an instanced batch drawn in `material`, its own pipelines being its own. */
|
|
321
|
+
prepareInstanced(batch: InstancedHandle, material: SurfaceMaterial<SurfaceTextureHandle> | null, options?: PrepareOptions): void;
|
|
302
322
|
/**
|
|
303
323
|
* A 3D text object: one string, drawn as instanced glyph cubes over the scene.
|
|
304
324
|
*
|
|
@@ -49,7 +49,7 @@ import type { RenderQuality, RenderQualityOptions } from '../../renderQuality.ts
|
|
|
49
49
|
import { Mesh } from '../../mesh.ts';
|
|
50
50
|
import type { IncrementalMesh } from '../../mesh.ts';
|
|
51
51
|
import type { MeshData } from '../../mesh.ts';
|
|
52
|
-
import type { MeshOptions } from '../api.ts';
|
|
52
|
+
import type { MeshOptions, PrepareOptions } from '../api.ts';
|
|
53
53
|
import { SurfaceTexture } from '../../surfaceTexture.ts';
|
|
54
54
|
import type { CompressedTextureFormat, CompressedTextureSource, SurfaceSource } from '../../compressedSource.ts';
|
|
55
55
|
import type { SurfaceMaterial, SurfaceTextureOptions } from '../../surfaceTexture.ts';
|
|
@@ -1114,6 +1114,8 @@ export declare class WebGL2Renderer implements RendererApi {
|
|
|
1114
1114
|
private readonly aoProjScale;
|
|
1115
1115
|
/** Where this frame's jitter put the occlusion, in the composite's uv. See `uAoOffset`. */
|
|
1116
1116
|
private readonly aoOffset;
|
|
1117
|
+
/** Where occlusion fades out with distance, `uFade`: none until a caller sets one. */
|
|
1118
|
+
private readonly aoFade;
|
|
1117
1119
|
/**
|
|
1118
1120
|
* Whether this frame has already been resolved to the canvas.
|
|
1119
1121
|
*
|
|
@@ -1281,14 +1283,18 @@ export declare class WebGL2Renderer implements RendererApi {
|
|
|
1281
1283
|
private warnedProjectionRoom;
|
|
1282
1284
|
/** `uWorldUv`, the current material's projection packed. See `surfaceProjection.ts`. */
|
|
1283
1285
|
private readonly projection;
|
|
1284
|
-
/**
|
|
1285
|
-
|
|
1286
|
+
/**
|
|
1287
|
+
* `uGlassTint` as the current material leaves it: its light from behind and that light's colour,
|
|
1288
|
+
* which a pane overwrites for its own draw and `unbindSeeThrough` puts back. See `transmission.ts`.
|
|
1289
|
+
*/
|
|
1290
|
+
private readonly transmission;
|
|
1286
1291
|
/** `uLayers`, the current material's layers packed. See `surfaceLayers.ts`. */
|
|
1287
1292
|
private readonly layerFloats;
|
|
1288
1293
|
/** Whether the lit stage has room for layers' two vectors, and which refusals were said. */
|
|
1289
1294
|
private layersRoom;
|
|
1290
1295
|
private warnedLayersRoom;
|
|
1291
1296
|
private warnedLayersModelMap;
|
|
1297
|
+
private warnedLayersTriplanar;
|
|
1292
1298
|
private warnedOverlayRoom;
|
|
1293
1299
|
private warnedOverlayBlocks;
|
|
1294
1300
|
/** Whether a probe is being baked, so the passes that would recurse into one can tell. */
|
|
@@ -1959,6 +1965,25 @@ export declare class WebGL2Renderer implements RendererApi {
|
|
|
1959
1965
|
* `uScale`/`vScale` are repeats across the mesh's own UV range: geometry authored in
|
|
1960
1966
|
* metres is textured at whatever density the material wants without rebuilding it.
|
|
1961
1967
|
*/
|
|
1968
|
+
/**
|
|
1969
|
+
* Compile, off the frame, every program a draw of `mesh` in `material` takes that making the mesh
|
|
1970
|
+
* did not, so the first frame that draws it waits on none: the program of the material's shading
|
|
1971
|
+
* model for each vertex variant the mesh can be drawn with (its rig, with a palette; its targets,
|
|
1972
|
+
* with weights and without), a skin's halves under the screen-space blur, and the lit switches
|
|
1973
|
+
* the material turns on. `ready()` resolves once they have.
|
|
1974
|
+
*
|
|
1975
|
+
* **For `pipelineCompile: 'skip'` above all**, where a draw whose program is new is left out until
|
|
1976
|
+
* it lands: a figure brought on screen whole rather than without its face and hair for the frames
|
|
1977
|
+
* those take. Where draws wait, the programs compile here, at the call, rather than in a frame.
|
|
1978
|
+
* Two-sided and cut-out surfaces are draw state on this backend and need nothing; on WebGPU they
|
|
1979
|
+
* are pipelines of their own, prepared the same way.
|
|
1980
|
+
*
|
|
1981
|
+
* **Call it between frames**: it sets `material` to read it and leaves no material set after, as
|
|
1982
|
+
* `setMaterial(null)` does. `options` is the other backend's; blended draws share a program here.
|
|
1983
|
+
*/
|
|
1984
|
+
prepareMesh(mesh: Mesh, material: SurfaceMaterial | null, _options?: PrepareOptions): void;
|
|
1985
|
+
/** `prepareMesh` for an instanced batch: its own programs, and its model's for its variant. */
|
|
1986
|
+
prepareInstanced(batch: InstancedBatch, material: SurfaceMaterial | null, _options?: PrepareOptions): void;
|
|
1962
1987
|
/**
|
|
1963
1988
|
* Choose the joint palette the following `drawMesh` calls skin by, or null for none.
|
|
1964
1989
|
*
|
|
@@ -2140,6 +2165,8 @@ export declare class WebGL2Renderer implements RendererApi {
|
|
|
2140
2165
|
* no room for their two vectors each leave one layer, the last two said once each.
|
|
2141
2166
|
*/
|
|
2142
2167
|
private layersOf;
|
|
2168
|
+
/** Says once that a layered material's triplanar projection is laid on the horizontal plane. */
|
|
2169
|
+
private warnLayersTriplanar;
|
|
2143
2170
|
/** Whether the lit shader with `feature` compiled in fits this GPU's fragment uniform vectors. */
|
|
2144
2171
|
private switchFits;
|
|
2145
2172
|
/**
|
|
@@ -2236,7 +2263,8 @@ export declare class WebGL2Renderer implements RendererApi {
|
|
|
2236
2263
|
*
|
|
2237
2264
|
* What it gives up: a fade reads as grain while it lasts, which a temporal resolve smooths and a
|
|
2238
2265
|
* plain frame does not; a blended draw that asks spends its opacity here and blends none of it;
|
|
2239
|
-
* and a lightmapped batch
|
|
2266
|
+
* and a lightmapped batch's opacities ride its page regions, so they fade in steps of a
|
|
2267
|
+
* sixty-fourth, which is the pattern's own resolution (`MeshInstances.lightmapRegions`).
|
|
2240
2268
|
*/
|
|
2241
2269
|
setDitherOpacity(on: boolean): void;
|
|
2242
2270
|
/**
|
|
@@ -2404,6 +2432,8 @@ export declare class WebGL2Renderer implements RendererApi {
|
|
|
2404
2432
|
* taking it mid-bake is how a probe's next face came to be attached to the scene's framebuffer.
|
|
2405
2433
|
*/
|
|
2406
2434
|
private bindSeeThrough;
|
|
2435
|
+
/** `uGlassTint` as the current material leaves it. See `transmission`. */
|
|
2436
|
+
private writeTransmission;
|
|
2407
2437
|
/** Put back what `bindSeeThrough` bound: the binding outlives its frame (the 2026-08-27 rule). */
|
|
2408
2438
|
private unbindSeeThrough;
|
|
2409
2439
|
disposeMesh(mesh: Mesh): void;
|
|
@@ -3293,6 +3323,17 @@ export declare class WebGL2Renderer implements RendererApi {
|
|
|
3293
3323
|
* once rather than doing nothing. See `shaders/localExposure.ts`.
|
|
3294
3324
|
*/
|
|
3295
3325
|
setLocalExposure(strength: number): void;
|
|
3326
|
+
/**
|
|
3327
|
+
* Where ambient occlusion fades out with distance: whole up to `distance` metres from the eye and
|
|
3328
|
+
* gone `radius` metres past it, so the far depth buffer's steps — a sky dome kilometres off, a
|
|
3329
|
+
* mountain range — are not shaded in rings, and those pixels skip the occlusion's sampling.
|
|
3330
|
+
*
|
|
3331
|
+
* A distance that is not a finite number at or above zero is no fade, which is the default, and
|
|
3332
|
+
* `Infinity` reads as "never fades"; a radius below zero is a cut at `distance`. Set before
|
|
3333
|
+
* `endFrame` and held until changed, like `setDepthOfField`. Nothing at all when
|
|
3334
|
+
* `ambientOcclusion` is 0. See `occlusionFade.ts` for what it gives up.
|
|
3335
|
+
*/
|
|
3336
|
+
setAmbientOcclusionFade(distance: number, radius: number): void;
|
|
3296
3337
|
/**
|
|
3297
3338
|
* Where this frame's lens is focused, how deep the sharp zone is, and how much of the ceiling
|
|
3298
3339
|
* to take.
|
|
@@ -37,8 +37,10 @@ import { compileProgram, finishProgram, startProgram, uniformLocations } from '.
|
|
|
37
37
|
import { countUniformVectors, FULL_LIGHT_BUDGET, nextLightBudget, planLightBudget, } from '../../uniformVectorBudget.js';
|
|
38
38
|
import { OVERLAY_FLOATS, overlayLays, packSurfaceOverlay, } from '../../surfaceOverlay.js';
|
|
39
39
|
import { PROJECTION_FLOATS, packSurfaceProjection, projects } from '../../surfaceProjection.js';
|
|
40
|
+
import { resolveTransmission } from '../../transmission.js';
|
|
41
|
+
import { resolveOcclusionFade } from '../../occlusionFade.js';
|
|
40
42
|
import { orthographicDepthSpan } from '../../lightMatrix.js';
|
|
41
|
-
import { LAYER_FLOATS, packSurfaceLayers } from '../../surfaceLayers.js';
|
|
43
|
+
import { LAYER_FLOATS, layerMaskMap, layeredProjection, layersFitBeside, packSurfaceLayers, } from '../../surfaceLayers.js';
|
|
42
44
|
import { FILM_FRAG, FILM_VERT } from '../../shaders/film.js';
|
|
43
45
|
import { SceneTarget } from '../../sceneTarget.js';
|
|
44
46
|
import { FILM_LOOK_WITHOUT_COMPOSITE } from '../../shaders/filmLook.js';
|
|
@@ -785,6 +787,8 @@ export class WebGL2Renderer {
|
|
|
785
787
|
aoProjScale = new Float32Array(2);
|
|
786
788
|
/** Where this frame's jitter put the occlusion, in the composite's uv. See `uAoOffset`. */
|
|
787
789
|
aoOffset = new Float32Array(2);
|
|
790
|
+
/** Where occlusion fades out with distance, `uFade`: none until a caller sets one. */
|
|
791
|
+
aoFade = new Float32Array([-1, 0]);
|
|
788
792
|
/**
|
|
789
793
|
* Whether this frame has already been resolved to the canvas.
|
|
790
794
|
*
|
|
@@ -952,14 +956,18 @@ export class WebGL2Renderer {
|
|
|
952
956
|
warnedProjectionRoom = false;
|
|
953
957
|
/** `uWorldUv`, the current material's projection packed. See `surfaceProjection.ts`. */
|
|
954
958
|
projection = new Float32Array(PROJECTION_FLOATS);
|
|
955
|
-
/**
|
|
956
|
-
|
|
959
|
+
/**
|
|
960
|
+
* `uGlassTint` as the current material leaves it: its light from behind and that light's colour,
|
|
961
|
+
* which a pane overwrites for its own draw and `unbindSeeThrough` puts back. See `transmission.ts`.
|
|
962
|
+
*/
|
|
963
|
+
transmission = new Float32Array([-1, -1, -1, 0]);
|
|
957
964
|
/** `uLayers`, the current material's layers packed. See `surfaceLayers.ts`. */
|
|
958
965
|
layerFloats = new Float32Array(LAYER_FLOATS);
|
|
959
966
|
/** Whether the lit stage has room for layers' two vectors, and which refusals were said. */
|
|
960
967
|
layersRoom = null;
|
|
961
968
|
warnedLayersRoom = false;
|
|
962
969
|
warnedLayersModelMap = false;
|
|
970
|
+
warnedLayersTriplanar = false;
|
|
963
971
|
warnedOverlayRoom = false;
|
|
964
972
|
warnedOverlayBlocks = false;
|
|
965
973
|
/** Whether a probe is being baked, so the passes that would recurse into one can tell. */
|
|
@@ -2861,6 +2869,57 @@ export class WebGL2Renderer {
|
|
|
2861
2869
|
* `uScale`/`vScale` are repeats across the mesh's own UV range: geometry authored in
|
|
2862
2870
|
* metres is textured at whatever density the material wants without rebuilding it.
|
|
2863
2871
|
*/
|
|
2872
|
+
/**
|
|
2873
|
+
* Compile, off the frame, every program a draw of `mesh` in `material` takes that making the mesh
|
|
2874
|
+
* did not, so the first frame that draws it waits on none: the program of the material's shading
|
|
2875
|
+
* model for each vertex variant the mesh can be drawn with (its rig, with a palette; its targets,
|
|
2876
|
+
* with weights and without), a skin's halves under the screen-space blur, and the lit switches
|
|
2877
|
+
* the material turns on. `ready()` resolves once they have.
|
|
2878
|
+
*
|
|
2879
|
+
* **For `pipelineCompile: 'skip'` above all**, where a draw whose program is new is left out until
|
|
2880
|
+
* it lands: a figure brought on screen whole rather than without its face and hair for the frames
|
|
2881
|
+
* those take. Where draws wait, the programs compile here, at the call, rather than in a frame.
|
|
2882
|
+
* Two-sided and cut-out surfaces are draw state on this backend and need nothing; on WebGPU they
|
|
2883
|
+
* are pipelines of their own, prepared the same way.
|
|
2884
|
+
*
|
|
2885
|
+
* **Call it between frames**: it sets `material` to read it and leaves no material set after, as
|
|
2886
|
+
* `setMaterial(null)` does. `options` is the other backend's; blended draws share a program here.
|
|
2887
|
+
*/
|
|
2888
|
+
prepareMesh(mesh, material, _options = {}) {
|
|
2889
|
+
if (this.contextLost)
|
|
2890
|
+
return;
|
|
2891
|
+
this.setMaterial(material);
|
|
2892
|
+
const kind = material?.model?.kind ?? null;
|
|
2893
|
+
if (kind !== null) {
|
|
2894
|
+
const eight = mesh.isSkinnedEight;
|
|
2895
|
+
const morphs = mesh.morph !== null;
|
|
2896
|
+
const plain = mesh.isSkinned ? (eight ? 'skinned8' : 'skinned') : 'plain';
|
|
2897
|
+
const morphed = mesh.isSkinned ? (eight ? 'both8' : 'both') : 'morphed';
|
|
2898
|
+
for (const variant of morphs ? [plain, morphed] : [plain]) {
|
|
2899
|
+
this.modelProgram(variant, kind);
|
|
2900
|
+
/* A skin under the screen-space blur draws three halves, each a program of its own. */
|
|
2901
|
+
if (kind === 'skin' && this.skinScatter !== null && this.sceneTarget !== null) {
|
|
2902
|
+
for (const half of SPLIT_SKIN_HALVES)
|
|
2903
|
+
this.modelProgram(variant, kind, half);
|
|
2904
|
+
}
|
|
2905
|
+
}
|
|
2906
|
+
}
|
|
2907
|
+
this.setMaterial(null);
|
|
2908
|
+
}
|
|
2909
|
+
/** `prepareMesh` for an instanced batch: its own programs, and its model's for its variant. */
|
|
2910
|
+
prepareInstanced(batch, material, _options = {}) {
|
|
2911
|
+
if (this.contextLost)
|
|
2912
|
+
return;
|
|
2913
|
+
this.setMaterial(material);
|
|
2914
|
+
const animated = batch.animation !== null;
|
|
2915
|
+
this.ensureInstancedProgram();
|
|
2916
|
+
if (animated)
|
|
2917
|
+
this.ensureAnimatedProgram();
|
|
2918
|
+
const kind = material?.model?.kind ?? null;
|
|
2919
|
+
if (kind !== null)
|
|
2920
|
+
this.modelProgram(animated ? 'animated' : 'instanced', kind);
|
|
2921
|
+
this.setMaterial(null);
|
|
2922
|
+
}
|
|
2864
2923
|
/**
|
|
2865
2924
|
* Choose the joint palette the following `drawMesh` calls skin by, or null for none.
|
|
2866
2925
|
*
|
|
@@ -3197,9 +3256,9 @@ export class WebGL2Renderer {
|
|
|
3197
3256
|
* and read neither — one decision for every program, as `setMaterial` says.
|
|
3198
3257
|
*/
|
|
3199
3258
|
const modelMap = material?.modelMap ?? null;
|
|
3200
|
-
/* A layered material's mask goes where a model's
|
|
3259
|
+
/* A layered material's mask, where it is a map, goes where a model's would: surfaceLayers.ts. */
|
|
3201
3260
|
const layers = this.layersOf(material);
|
|
3202
|
-
const boundMap = modelMap ?? layers
|
|
3261
|
+
const boundMap = modelMap ?? layerMaskMap(layers);
|
|
3203
3262
|
gl.activeTexture(gl.TEXTURE0 + MODEL_MAP_TEXTURE_UNIT);
|
|
3204
3263
|
gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.emptyTexture2DArray);
|
|
3205
3264
|
if (boundMap !== null)
|
|
@@ -3217,11 +3276,13 @@ export class WebGL2Renderer {
|
|
|
3217
3276
|
/* The draw's overlay, which a program born mid-pass must wear too: see `setSurfaceOverlay`. */
|
|
3218
3277
|
gl.uniform4fv(u['uOverlay'] ?? null, this.overlayFloats);
|
|
3219
3278
|
gl.uniform4fv(u['uWorldUv'] ?? null, this.projection);
|
|
3220
|
-
/* How much light from behind the surface lets through,
|
|
3221
|
-
|
|
3222
|
-
|
|
3279
|
+
/* How much light from behind the surface lets through, and its colour: transmission.ts. */
|
|
3280
|
+
resolveTransmission(material, this.transmission);
|
|
3281
|
+
this.writeTransmission(u);
|
|
3223
3282
|
/* Maps placed by the world, where the material asks and the lit stage has room: worldUv.ts. */
|
|
3224
|
-
const projection = material?.projection
|
|
3283
|
+
const projection = layeredProjection(material?.projection, layers);
|
|
3284
|
+
if (projection !== (material?.projection ?? null))
|
|
3285
|
+
this.warnLayersTriplanar();
|
|
3225
3286
|
packSurfaceProjection(this.projectionFits(projection) ? projection : null, this.projection);
|
|
3226
3287
|
if (this.projection[0] > 0)
|
|
3227
3288
|
this.askLit('WORLD_UVS');
|
|
@@ -3455,14 +3516,15 @@ export class WebGL2Renderer {
|
|
|
3455
3516
|
* no room for their two vectors each leave one layer, the last two said once each.
|
|
3456
3517
|
*/
|
|
3457
3518
|
layersOf(material) {
|
|
3458
|
-
const layers = material?.layers ?? null;
|
|
3519
|
+
const layers = (material?.layers ?? null);
|
|
3459
3520
|
if (layers === null || layers.repeats.length === 0)
|
|
3460
3521
|
return null;
|
|
3461
|
-
if ((material?.modelMap
|
|
3522
|
+
if (!layersFitBeside(layers, material?.modelMap)) {
|
|
3462
3523
|
if (!this.warnedLayersModelMap) {
|
|
3463
3524
|
this.warnedLayersModelMap = true;
|
|
3464
|
-
console.warn("Renderer: a material's layers are drawn as one: its mask is read where a
|
|
3465
|
-
'goes, and this material carries a `modelMap` too.
|
|
3525
|
+
console.warn("Renderer: a material's layers are drawn as one: its mask is a map, read where a " +
|
|
3526
|
+
"model's map goes, and this material carries a `modelMap` too. A mask in the ORM " +
|
|
3527
|
+
"array (`mask: 'orm'`) or the vertex colour (`mask: 'vertex'`) leaves the slot free.");
|
|
3466
3528
|
}
|
|
3467
3529
|
return null;
|
|
3468
3530
|
}
|
|
@@ -3471,12 +3533,20 @@ export class WebGL2Renderer {
|
|
|
3471
3533
|
return layers;
|
|
3472
3534
|
if (!this.warnedLayersRoom) {
|
|
3473
3535
|
this.warnedLayersRoom = true;
|
|
3474
|
-
console.warn("Renderer: a material's layers are refused on this device. They take
|
|
3536
|
+
console.warn("Renderer: a material's layers are refused on this device. They take four fragment uniform " +
|
|
3475
3537
|
'vectors the lit shader does not have; the material is drawn as its base layer. A lower ' +
|
|
3476
3538
|
'`maxLights` makes the room, or WebGPU has it.');
|
|
3477
3539
|
}
|
|
3478
3540
|
return null;
|
|
3479
3541
|
}
|
|
3542
|
+
/** Says once that a layered material's triplanar projection is laid on the horizontal plane. */
|
|
3543
|
+
warnLayersTriplanar() {
|
|
3544
|
+
if (this.warnedLayersTriplanar)
|
|
3545
|
+
return;
|
|
3546
|
+
this.warnedLayersTriplanar = true;
|
|
3547
|
+
console.warn("Renderer: a layered material's triplanar projection is laid on the horizontal plane: every " +
|
|
3548
|
+
"layer's maps on three planes would be three times the reads. See `SurfaceMaterial.layers`.");
|
|
3549
|
+
}
|
|
3480
3550
|
/** Whether the lit shader with `feature` compiled in fits this GPU's fragment uniform vectors. */
|
|
3481
3551
|
switchFits(feature) {
|
|
3482
3552
|
const asIf = { [feature]: true };
|
|
@@ -3635,7 +3705,8 @@ export class WebGL2Renderer {
|
|
|
3635
3705
|
*
|
|
3636
3706
|
* What it gives up: a fade reads as grain while it lasts, which a temporal resolve smooths and a
|
|
3637
3707
|
* plain frame does not; a blended draw that asks spends its opacity here and blends none of it;
|
|
3638
|
-
* and a lightmapped batch
|
|
3708
|
+
* and a lightmapped batch's opacities ride its page regions, so they fade in steps of a
|
|
3709
|
+
* sixty-fourth, which is the pattern's own resolution (`MeshInstances.lightmapRegions`).
|
|
3639
3710
|
*/
|
|
3640
3711
|
setDitherOpacity(on) {
|
|
3641
3712
|
if (this.contextLost)
|
|
@@ -4106,13 +4177,20 @@ export class WebGL2Renderer {
|
|
|
4106
4177
|
const tint = options.refractTint ?? WHITE_TINT;
|
|
4107
4178
|
gl.uniform4f(u['uRefractTint'] ?? null, tint[0] ?? 1, tint[1] ?? 1, tint[2] ?? 1, 1);
|
|
4108
4179
|
/* The pane's colour; w is the material's light through from behind, which glass never reads. */
|
|
4109
|
-
gl.uniform4f(u['uGlassTint'] ?? null, glass.tint[0] ?? 1, glass.tint[1] ?? 1, glass.tint[2] ?? 1, this.
|
|
4180
|
+
gl.uniform4f(u['uGlassTint'] ?? null, glass.tint[0] ?? 1, glass.tint[1] ?? 1, glass.tint[2] ?? 1, this.transmission[3]);
|
|
4110
4181
|
return true;
|
|
4111
4182
|
}
|
|
4183
|
+
/** `uGlassTint` as the current material leaves it. See `transmission`. */
|
|
4184
|
+
writeTransmission(u) {
|
|
4185
|
+
const t = this.transmission;
|
|
4186
|
+
this.gl.uniform4f(u['uGlassTint'] ?? null, t[0] ?? -1, t[1] ?? -1, t[2] ?? -1, t[3] ?? 0);
|
|
4187
|
+
}
|
|
4112
4188
|
/** Put back what `bindSeeThrough` bound: the binding outlives its frame (the 2026-08-27 rule). */
|
|
4113
4189
|
unbindSeeThrough(u) {
|
|
4114
4190
|
const { gl } = this;
|
|
4115
4191
|
gl.uniform4f(u['uSeeThrough'] ?? null, 0, 0, 0, 0);
|
|
4192
|
+
/* The material's light through, over the pane's tint: transmission.ts. */
|
|
4193
|
+
this.writeTransmission(u);
|
|
4116
4194
|
gl.activeTexture(gl.TEXTURE0 + REFRACT_SCENE_TEXTURE_UNIT);
|
|
4117
4195
|
/* The overlay's atlas, where one is held: it shares the unit. See `setSurfaceOverlay`. */
|
|
4118
4196
|
gl.bindTexture(gl.TEXTURE_2D, this.overlayTexture ?? this.emptyTexture2D);
|
|
@@ -6177,6 +6255,19 @@ export class WebGL2Renderer {
|
|
|
6177
6255
|
}
|
|
6178
6256
|
this.sceneTarget.setLocalExposure(strength);
|
|
6179
6257
|
}
|
|
6258
|
+
/**
|
|
6259
|
+
* Where ambient occlusion fades out with distance: whole up to `distance` metres from the eye and
|
|
6260
|
+
* gone `radius` metres past it, so the far depth buffer's steps — a sky dome kilometres off, a
|
|
6261
|
+
* mountain range — are not shaded in rings, and those pixels skip the occlusion's sampling.
|
|
6262
|
+
*
|
|
6263
|
+
* A distance that is not a finite number at or above zero is no fade, which is the default, and
|
|
6264
|
+
* `Infinity` reads as "never fades"; a radius below zero is a cut at `distance`. Set before
|
|
6265
|
+
* `endFrame` and held until changed, like `setDepthOfField`. Nothing at all when
|
|
6266
|
+
* `ambientOcclusion` is 0. See `occlusionFade.ts` for what it gives up.
|
|
6267
|
+
*/
|
|
6268
|
+
setAmbientOcclusionFade(distance, radius) {
|
|
6269
|
+
resolveOcclusionFade(distance, radius, this.aoFade);
|
|
6270
|
+
}
|
|
6180
6271
|
/**
|
|
6181
6272
|
* Where this frame's lens is focused, how deep the sharp zone is, and how much of the ceiling
|
|
6182
6273
|
* to take.
|
|
@@ -6804,6 +6895,7 @@ export class WebGL2Renderer {
|
|
|
6804
6895
|
radius: this.quality.ambientOcclusionRadius,
|
|
6805
6896
|
projScale: this.aoProjScale,
|
|
6806
6897
|
invProjection: this.aoInvProjection,
|
|
6898
|
+
fade: this.aoFade,
|
|
6807
6899
|
offset: this.aoOffset,
|
|
6808
6900
|
};
|
|
6809
6901
|
}
|
|
@@ -7256,7 +7348,7 @@ export class WebGL2Renderer {
|
|
|
7256
7348
|
/* Nor a projection: maps follow the mesh's coordinates until a material asks otherwise. */
|
|
7257
7349
|
this.projection.fill(0);
|
|
7258
7350
|
/* Nor light through from behind. See `SurfaceMaterial.diffuseTransmission`. */
|
|
7259
|
-
this.
|
|
7351
|
+
resolveTransmission(null, this.transmission);
|
|
7260
7352
|
/* Nor layers: one layer until a material asks for more. */
|
|
7261
7353
|
this.layerFloats.fill(0);
|
|
7262
7354
|
this.overlayMaps = null;
|
|
@@ -7406,8 +7498,9 @@ export class WebGL2Renderer {
|
|
|
7406
7498
|
`bindSeeThrough` for the four numbers in the one vector. */
|
|
7407
7499
|
gl.uniform4f(u['uSeeThrough'] ?? null, 0, 0, 0, 0);
|
|
7408
7500
|
gl.uniform4f(u['uRefractTint'] ?? null, 1, 1, 1, 1);
|
|
7409
|
-
/*
|
|
7410
|
-
|
|
7501
|
+
/* No light through from behind: no material at a pass's start. See `writeTransmission`. */
|
|
7502
|
+
const through = this.transmission;
|
|
7503
|
+
gl.uniform4f(u['uGlassTint'] ?? null, through[0] ?? -1, through[1] ?? -1, through[2] ?? -1, through[3] ?? 0);
|
|
7411
7504
|
gl.uniform3fv(u['uDirectionalDir'] ?? null, env.directionalDir);
|
|
7412
7505
|
gl.uniform3fv(u['uDirectionalColor'] ?? null, env.directionalColor);
|
|
7413
7506
|
gl.uniform3fv(u['uAmbient'] ?? null, env.ambient);
|
|
@@ -7,7 +7,7 @@ import type { PickHit, PickableSource } from '../../pickable.ts';
|
|
|
7
7
|
import type { RendererApi } from '../api.ts';
|
|
8
8
|
import { GpuTimestamps } from './gpuTimestamps.ts';
|
|
9
9
|
import type { Environment, FilmOptions, InsetRect, LightVolumeDrawOptions, SkyColors, TranslucentMeshOptions } from '../webgl2/renderer.ts';
|
|
10
|
-
import type { IncrementalMeshHandle, MeshOptions } from '../api.ts';
|
|
10
|
+
import type { IncrementalMeshHandle, MeshOptions, PrepareOptions } from '../api.ts';
|
|
11
11
|
import type { LightmapPage } from '../../lightmap.ts';
|
|
12
12
|
import { type BloomResponse } from '../../bloomChain.ts';
|
|
13
13
|
import type { WaterBody } from '../../waterRenderer.ts';
|
|
@@ -851,6 +851,8 @@ export declare class WebGPURenderer implements RendererApi {
|
|
|
851
851
|
private temporalHistoryUsable;
|
|
852
852
|
private readonly aoStaging;
|
|
853
853
|
private readonly aoFloats;
|
|
854
|
+
/** Where occlusion fades out with distance, `uFade`: none until a caller sets one. */
|
|
855
|
+
private readonly aoFade;
|
|
854
856
|
private aoBindGroup;
|
|
855
857
|
private readonly aoBlurLayout;
|
|
856
858
|
/**
|
|
@@ -1187,7 +1189,13 @@ export declare class WebGPURenderer implements RendererApi {
|
|
|
1187
1189
|
private readonly projection;
|
|
1188
1190
|
/** `uLayers`, the material's layers packed by `packSurfaceLayers`. */
|
|
1189
1191
|
private readonly layerFloats;
|
|
1192
|
+
/**
|
|
1193
|
+
* `uGlassTint` as the material leaves it, its light from behind and that light's colour, which a
|
|
1194
|
+
* pane overwrites for its own draw and `releaseSeeThrough` puts back. See `transmission.ts`.
|
|
1195
|
+
*/
|
|
1196
|
+
private readonly transmission;
|
|
1190
1197
|
private warnedLayersModelMap;
|
|
1198
|
+
private warnedLayersTriplanar;
|
|
1191
1199
|
/** Its numbers, `uModelParams`, packed by `packModel`. */
|
|
1192
1200
|
private readonly modelParams;
|
|
1193
1201
|
/**
|
|
@@ -2093,6 +2101,8 @@ export declare class WebGPURenderer implements RendererApi {
|
|
|
2093
2101
|
* said once, leave one layer.
|
|
2094
2102
|
*/
|
|
2095
2103
|
private layersOf;
|
|
2104
|
+
/** Says once that a layered material's triplanar projection is laid on the horizontal plane. */
|
|
2105
|
+
private warnLayersTriplanar;
|
|
2096
2106
|
/**
|
|
2097
2107
|
* What the sun's moving layer is read through: the matrix its shadow pass was last drawn with,
|
|
2098
2108
|
* where the lit pipelines carry one (`MOVING_SUN`); the environment's otherwise, which every caller
|
|
@@ -2478,6 +2488,35 @@ export declare class WebGPURenderer implements RendererApi {
|
|
|
2478
2488
|
*/
|
|
2479
2489
|
private refuseOitMultisampled;
|
|
2480
2490
|
drawTranslucentMesh(mesh: GpuMesh, model: ReadonlyMat4, opacity: number, options?: TranslucentMeshOptions): void;
|
|
2491
|
+
/**
|
|
2492
|
+
* Compile off the main thread every pipeline a draw of `mesh` in `material` takes. **The other
|
|
2493
|
+
* backend's `prepareMesh` carries the contract**; here a draw's key is `meshShape`'s, so a prepared
|
|
2494
|
+
* key is exactly the one the draw asks for, and the compiles are started as a skipping draw starts
|
|
2495
|
+
* them (`PipelineCache.lit`) whatever `pipelineCompile` says, into the frame's own caches.
|
|
2496
|
+
*/
|
|
2497
|
+
prepareMesh(mesh: GpuMesh, material: SurfaceMaterial<GpuSurfaceTexture> | null, options?: PrepareOptions): void;
|
|
2498
|
+
/** `prepareMesh` for an instanced batch. See the other backend's `prepareInstanced`. */
|
|
2499
|
+
prepareInstanced(batch: GpuInstancedBatch, material: SurfaceMaterial<GpuSurfaceTexture> | null, options?: PrepareOptions): void;
|
|
2500
|
+
/**
|
|
2501
|
+
* `material` set, every cache a draw in it may build into passed to `start` with whether that draw
|
|
2502
|
+
* blends, compiles started rather than built (`PipelineCache.lit`), and then no material set and
|
|
2503
|
+
* each cache back to what `pipelineCompile` says.
|
|
2504
|
+
*/
|
|
2505
|
+
private preparing;
|
|
2506
|
+
/** What `meshShape` worked out for the mesh draw at hand. Reused, so a draw allocates no record. */
|
|
2507
|
+
private readonly drawShape;
|
|
2508
|
+
/**
|
|
2509
|
+
* The key a mesh draw's pipeline is cached under, and the pipeline state that key encodes, under
|
|
2510
|
+
* the current material and these options, into `drawShape`. **The one statement of it**:
|
|
2511
|
+
* `submitMesh` draws by it and `prepareMesh` compiles by it, so the two cannot disagree about a key.
|
|
2512
|
+
*/
|
|
2513
|
+
private meshShape;
|
|
2514
|
+
/**
|
|
2515
|
+
* The pipeline `drawShape` names: the one built, one built here, or none where draws skip a
|
|
2516
|
+
* compile and this one has not landed (`PipelineCache.lit`) — and none for a split skin until all
|
|
2517
|
+
* of its halves have.
|
|
2518
|
+
*/
|
|
2519
|
+
private meshPipeline;
|
|
2481
2520
|
/**
|
|
2482
2521
|
* @param blend Which of the two entry points above this came through, and **not** something to
|
|
2483
2522
|
* infer from `opacity`.
|
|
@@ -2568,6 +2607,13 @@ export declare class WebGPURenderer implements RendererApi {
|
|
|
2568
2607
|
* **What it gives up** against `drawMesh` is skinning and morphing, refused where the variant
|
|
2569
2608
|
* is built, and a per-instance opacity: `opacity` here dims the whole batch.
|
|
2570
2609
|
*/
|
|
2610
|
+
/**
|
|
2611
|
+
* An instanced draw's pipeline under the current material and these options: the one built, one
|
|
2612
|
+
* built here, or none while it compiles (`PipelineCache.lit`), its key, base and model left in
|
|
2613
|
+
* `drawShape`. **The one statement of an instanced key**: `submitInstanced` draws by it and
|
|
2614
|
+
* `prepareInstanced` compiles by it.
|
|
2615
|
+
*/
|
|
2616
|
+
private instancedPipeline;
|
|
2571
2617
|
/**
|
|
2572
2618
|
* The next of a batch's cull slots this frame, with its cull queued against the current view; null
|
|
2573
2619
|
* when the batch has drawn from `MAX_CULL_SLOTS` views already or the frame's job ring is full,
|
|
@@ -3994,6 +4040,11 @@ export declare class WebGPURenderer implements RendererApi {
|
|
|
3994
4040
|
setLocalExposure(strength: number): void;
|
|
3995
4041
|
/** The exposure pass, built once; the composite's group holds placeholders until it exists. */
|
|
3996
4042
|
private ensureExposurePass;
|
|
4043
|
+
/**
|
|
4044
|
+
* Where ambient occlusion fades out with distance. **The other backend's
|
|
4045
|
+
* `setAmbientOcclusionFade` carries the reasoning**; this is the same setter over the same state.
|
|
4046
|
+
*/
|
|
4047
|
+
setAmbientOcclusionFade(distance: number, radius: number): void;
|
|
3997
4048
|
/**
|
|
3998
4049
|
* Where this frame's lens is focused, how deep the sharp zone is, and how much of the ceiling
|
|
3999
4050
|
* to take. **The other backend's `setDepthOfField` carries the reasoning**, whole: the
|