@driftengine/core 4.10.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 +107 -0
- package/README.md +1 -1
- package/dist/index.d.ts +6 -5
- package/dist/index.js +2 -2
- 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 +141 -3
- package/dist/render/backend/webgl2/renderer.js +454 -42
- package/dist/render/backend/webgpu/buffers.d.ts +5 -0
- package/dist/render/backend/webgpu/buffers.js +98 -48
- package/dist/render/backend/webgpu/clothKernels.d.ts +8 -0
- package/dist/render/backend/webgpu/clothKernels.js +76 -0
- package/dist/render/backend/webgpu/clothOps.d.ts +39 -0
- package/dist/render/backend/webgpu/clothOps.js +78 -0
- package/dist/render/backend/webgpu/clothSetLayout.d.ts +25 -0
- package/dist/render/backend/webgpu/clothSetLayout.js +84 -0
- package/dist/render/backend/webgpu/clothSetPack.d.ts +33 -0
- package/dist/render/backend/webgpu/clothSetPack.js +144 -0
- package/dist/render/backend/webgpu/drawCommand.d.ts +2 -0
- package/dist/render/backend/webgpu/drawCommand.js +1 -0
- package/dist/render/backend/webgpu/flatPass.d.ts +1 -1
- package/dist/render/backend/webgpu/flatPass.js +2 -1
- package/dist/render/backend/webgpu/gpuClothSet.d.ts +80 -0
- package/dist/render/backend/webgpu/gpuClothSet.js +462 -0
- package/dist/render/backend/webgpu/latePass.js +1 -0
- package/dist/render/backend/webgpu/motionPass.js +15 -6
- package/dist/render/backend/webgpu/pipelineCache.d.ts +22 -1
- package/dist/render/backend/webgpu/pipelineCache.js +39 -1
- package/dist/render/backend/webgpu/postPass.d.ts +1 -1
- package/dist/render/backend/webgpu/renderer.d.ts +100 -1
- package/dist/render/backend/webgpu/renderer.js +423 -95
- package/dist/render/backend/webgpu/shaders/clothSolve.wgsl.d.ts +9 -2
- package/dist/render/backend/webgpu/shaders/clothSolve.wgsl.js +121 -47
- package/dist/render/backend/webgpu/staticBundles.js +2 -1
- package/dist/render/backend/webgpu/vertexFormats.d.ts +22 -0
- package/dist/render/backend/webgpu/vertexFormats.js +97 -0
- package/dist/render/globalMediumPass.d.ts +2 -0
- package/dist/render/globalMediumPass.js +1 -0
- package/dist/render/indexWidth.d.ts +17 -0
- package/dist/render/indexWidth.js +23 -0
- package/dist/render/instances.d.ts +13 -1
- package/dist/render/instances.js +10 -0
- package/dist/render/lightMatrix.d.ts +11 -0
- package/dist/render/lightMatrix.js +14 -0
- package/dist/render/mesh.d.ts +9 -0
- package/dist/render/mesh.js +38 -14
- package/dist/render/occlusionFade.d.ts +21 -0
- package/dist/render/occlusionFade.js +27 -0
- package/dist/render/renderQuality.d.ts +30 -0
- package/dist/render/renderQuality.js +5 -0
- package/dist/render/rendererHarness.d.ts +7 -0
- package/dist/render/rendererHarness.js +12 -0
- package/dist/render/sceneTarget.d.ts +2 -0
- package/dist/render/sceneTarget.js +1 -1
- package/dist/render/shader.d.ts +15 -0
- package/dist/render/shader.js +41 -0
- package/dist/render/shaders/ambientOcclusion.js +12 -1
- package/dist/render/shaders/flat/directionalShadow.js +84 -10
- package/dist/render/shaders/flat/glassTint.d.ts +1 -1
- package/dist/render/shaders/flat/glassTint.js +3 -0
- package/dist/render/shaders/flat/index.d.ts +15 -0
- package/dist/render/shaders/flat/index.js +24 -4
- package/dist/render/shaders/flat/layered.d.ts +27 -0
- package/dist/render/shaders/flat/layered.js +198 -0
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +50 -16
- package/dist/render/shaders/flat/mapReads.d.ts +10 -0
- package/dist/render/shaders/flat/mapReads.js +38 -0
- package/dist/render/shaders/flat/movingSun.d.ts +21 -0
- package/dist/render/shaders/flat/movingSun.js +46 -0
- package/dist/render/shaders/flat/normalMap.d.ts +6 -2
- package/dist/render/shaders/flat/normalMap.js +23 -3
- package/dist/render/shaders/flat/overlay.d.ts +3 -2
- package/dist/render/shaders/flat/overlay.js +41 -19
- package/dist/render/shaders/flat/preamble.js +23 -5
- package/dist/render/shaders/flat/worldUv.d.ts +22 -0
- package/dist/render/shaders/flat/worldUv.js +113 -0
- 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 +768 -368
- package/dist/render/shaders/generated/flat.wgsl.js +791 -391
- package/dist/render/shaders/generated/globalMedium.wgsl.d.ts +8 -3
- package/dist/render/shaders/generated/globalMedium.wgsl.js +8 -3
- package/dist/render/shaders/generated/lightVolume.wgsl.d.ts +15 -10
- package/dist/render/shaders/generated/lightVolume.wgsl.js +16 -11
- package/dist/render/shaders/globalMedium.js +9 -1
- package/dist/render/shaders/lightVolume.js +9 -1
- package/dist/render/shaders/tangentFrame.d.ts +1 -1
- package/dist/render/shaders/tangentFrame.js +16 -3
- package/dist/render/skinnedClothRun.d.ts +28 -0
- package/dist/render/skinnedClothRun.js +103 -35
- package/dist/render/surfaceLayers.d.ts +107 -0
- package/dist/render/surfaceLayers.js +82 -0
- package/dist/render/surfaceOverlay.d.ts +16 -3
- package/dist/render/surfaceOverlay.js +11 -6
- package/dist/render/surfaceProjection.d.ts +34 -0
- package/dist/render/surfaceProjection.js +30 -0
- package/dist/render/surfaceTexture.d.ts +39 -0
- package/dist/render/transmission.d.ts +21 -0
- package/dist/render/transmission.js +21 -0
- package/dist/render/vertexPacking.d.ts +48 -0
- package/dist/render/vertexPacking.js +68 -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 +6 -2
- package/src/render/ambientOcclusionPass.ts +4 -1
- package/src/render/backend/api.ts +31 -0
- package/src/render/backend/webgl2/renderer.ts +519 -56
- package/src/render/backend/webgpu/buffers.ts +153 -54
- package/src/render/backend/webgpu/clothKernels.ts +86 -0
- package/src/render/backend/webgpu/clothOps.ts +86 -0
- package/src/render/backend/webgpu/clothSetLayout.ts +102 -0
- package/src/render/backend/webgpu/clothSetPack.ts +197 -0
- package/src/render/backend/webgpu/drawCommand.ts +3 -0
- package/src/render/backend/webgpu/flatPass.ts +26 -28
- package/src/render/backend/webgpu/gpuClothSet.ts +564 -0
- package/src/render/backend/webgpu/latePass.ts +1 -0
- package/src/render/backend/webgpu/motionPass.ts +18 -7
- package/src/render/backend/webgpu/pipelineCache.ts +46 -7
- package/src/render/backend/webgpu/renderer.ts +622 -164
- package/src/render/backend/webgpu/shaders/clothSolve.wgsl.ts +121 -47
- package/src/render/backend/webgpu/staticBundles.ts +2 -1
- package/src/render/backend/webgpu/vertexFormats.ts +113 -0
- package/src/render/globalMediumPass.ts +7 -0
- package/src/render/indexWidth.ts +27 -0
- package/src/render/instances.ts +18 -1
- package/src/render/lightMatrix.ts +15 -0
- package/src/render/mesh.ts +66 -14
- package/src/render/occlusionFade.ts +29 -0
- package/src/render/renderQuality.ts +38 -0
- package/src/render/rendererHarness.ts +15 -0
- package/src/render/sceneTarget.ts +3 -0
- package/src/render/shader.ts +59 -0
- package/src/render/shaders/ambientOcclusion.ts +12 -1
- package/src/render/shaders/flat/directionalShadow.ts +84 -10
- package/src/render/shaders/flat/glassTint.ts +7 -1
- package/src/render/shaders/flat/index.ts +39 -4
- package/src/render/shaders/flat/layered.ts +198 -0
- package/src/render/shaders/flat/main.ts +50 -16
- package/src/render/shaders/flat/mapReads.ts +38 -0
- package/src/render/shaders/flat/movingSun.ts +46 -0
- package/src/render/shaders/flat/normalMap.ts +23 -3
- package/src/render/shaders/flat/overlay.ts +41 -19
- package/src/render/shaders/flat/preamble.ts +23 -5
- package/src/render/shaders/flat/worldUv.ts +113 -0
- package/src/render/shaders/generated/ambientOcclusion.wgsl.ts +6 -1
- package/src/render/shaders/generated/flat.wgsl.ts +791 -391
- package/src/render/shaders/generated/globalMedium.wgsl.ts +8 -3
- package/src/render/shaders/generated/lightVolume.wgsl.ts +16 -11
- package/src/render/shaders/globalMedium.ts +9 -1
- package/src/render/shaders/lightVolume.ts +9 -1
- package/src/render/shaders/tangentFrame.ts +16 -3
- package/src/render/skinnedClothRun.ts +138 -38
- package/src/render/surfaceLayers.ts +176 -0
- package/src/render/surfaceOverlay.ts +24 -7
- package/src/render/surfaceProjection.ts +57 -0
- package/src/render/surfaceTexture.ts +39 -0
- package/src/render/transmission.ts +27 -0
- package/src/render/vertexPacking.ts +109 -0
- package/src/ui/splash.ts +83 -14
- package/dist/render/backend/webgpu/gpuSkinnedCloth.d.ts +0 -74
- package/dist/render/backend/webgpu/gpuSkinnedCloth.js +0 -452
- package/src/render/backend/webgpu/gpuSkinnedCloth.ts +0 -502
|
@@ -33,9 +33,14 @@ import { clamp } from '../../../math/scalar.js';
|
|
|
33
33
|
import { attachContextLoss } from '../../contextLoss.js';
|
|
34
34
|
import { isWeakGpuFamily, webgl2RendererName } from '../../gpuCapability.js';
|
|
35
35
|
import { GpuTimer } from '../../gpuTimer.js';
|
|
36
|
-
import { compileProgram, uniformLocations } from '../../shader.js';
|
|
36
|
+
import { compileProgram, finishProgram, startProgram, uniformLocations } from '../../shader.js';
|
|
37
37
|
import { countUniformVectors, FULL_LIGHT_BUDGET, nextLightBudget, planLightBudget, } from '../../uniformVectorBudget.js';
|
|
38
38
|
import { OVERLAY_FLOATS, overlayLays, packSurfaceOverlay, } from '../../surfaceOverlay.js';
|
|
39
|
+
import { PROJECTION_FLOATS, packSurfaceProjection, projects } from '../../surfaceProjection.js';
|
|
40
|
+
import { resolveTransmission } from '../../transmission.js';
|
|
41
|
+
import { resolveOcclusionFade } from '../../occlusionFade.js';
|
|
42
|
+
import { orthographicDepthSpan } from '../../lightMatrix.js';
|
|
43
|
+
import { LAYER_FLOATS, layerMaskMap, layeredProjection, layersFitBeside, packSurfaceLayers, } from '../../surfaceLayers.js';
|
|
39
44
|
import { FILM_FRAG, FILM_VERT } from '../../shaders/film.js';
|
|
40
45
|
import { SceneTarget } from '../../sceneTarget.js';
|
|
41
46
|
import { FILM_LOOK_WITHOUT_COMPOSITE } from '../../shaders/filmLook.js';
|
|
@@ -291,6 +296,18 @@ const NO_CLIP_PLANE = new Float32Array(4);
|
|
|
291
296
|
const IDENTITY_CLIP = new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
|
|
292
297
|
/** Standing still, for a caller that asked for dust and not for wind. */
|
|
293
298
|
const NO_DRIFT = [0, 0, 0];
|
|
299
|
+
/** Whether two matrices hold the same sixteen numbers. */
|
|
300
|
+
function sameMatrix(a, b) {
|
|
301
|
+
for (let k = 0; k < 16; k++)
|
|
302
|
+
if (a[k] !== b[k])
|
|
303
|
+
return false;
|
|
304
|
+
return true;
|
|
305
|
+
}
|
|
306
|
+
/** How often `ready` asks the driver whether a program is done, and how many times before waiting. */
|
|
307
|
+
const READY_POLL_MS = 4;
|
|
308
|
+
const READY_MAX_POLLS = 500;
|
|
309
|
+
/** The halves a skin draws under the screen-space blur, each a program of its own. */
|
|
310
|
+
const SPLIT_SKIN_HALVES = ['scene', 'diffuse', 'albedo'];
|
|
294
311
|
export class WebGL2Renderer {
|
|
295
312
|
/**
|
|
296
313
|
* Private on purpose. The GL context never leaves this directory, so a
|
|
@@ -568,6 +585,23 @@ export class WebGL2Renderer {
|
|
|
568
585
|
* compiled the first time a draw asks for one. See `modelProgram`.
|
|
569
586
|
*/
|
|
570
587
|
modelFlat = new Map();
|
|
588
|
+
/**
|
|
589
|
+
* Model programs still compiling, where draws skip a compile (`pipelineCompile: 'skip'`) and the
|
|
590
|
+
* context offers `KHR_parallel_shader_compile`; `parallelCompile` is its completion query then,
|
|
591
|
+
* and null where draws wait for a program here instead. See `modelProgram`.
|
|
592
|
+
*/
|
|
593
|
+
modelPending = new Map();
|
|
594
|
+
/** What `drawVariantOf` answers, rewritten each draw rather than made. */
|
|
595
|
+
drawVariant = {
|
|
596
|
+
name: 'plain',
|
|
597
|
+
skinned: false,
|
|
598
|
+
morphed: false,
|
|
599
|
+
cloth: null,
|
|
600
|
+
clothed: undefined,
|
|
601
|
+
program: null,
|
|
602
|
+
uniforms: {},
|
|
603
|
+
};
|
|
604
|
+
parallelCompile;
|
|
571
605
|
/** The last palette chosen, which an eye's axis is turned by. See `setSkinPalette`. */
|
|
572
606
|
paletteData = null;
|
|
573
607
|
emptyTexture2D;
|
|
@@ -630,7 +664,7 @@ export class WebGL2Renderer {
|
|
|
630
664
|
oitReplaying = false;
|
|
631
665
|
/** `uSurfaceScene`, resolved once a pass. See `surfaceScene.ts`. */
|
|
632
666
|
surfaceScene = new Float32Array(4);
|
|
633
|
-
/** The
|
|
667
|
+
/** The three lanes of `uWriteMode`, kept here because a `vec3` is written whole. */
|
|
634
668
|
oitWeight = 0;
|
|
635
669
|
/**
|
|
636
670
|
* How the surfaces drawn next meet the medium: what `setSurfaceFog` set, and `FOG_RECEDE` from
|
|
@@ -638,6 +672,7 @@ export class WebGL2Renderer {
|
|
|
638
672
|
*/
|
|
639
673
|
surfaceFog = FOG_RECEDE;
|
|
640
674
|
ditherFade = 0;
|
|
675
|
+
ditherOpacity = 0;
|
|
641
676
|
/** Said once, not per frame, where the context cannot give the float target the sum needs. */
|
|
642
677
|
oitRefused = false;
|
|
643
678
|
/** Said once for the whole renderer. See `refuseOitMultisampled`. */
|
|
@@ -752,6 +787,8 @@ export class WebGL2Renderer {
|
|
|
752
787
|
aoProjScale = new Float32Array(2);
|
|
753
788
|
/** Where this frame's jitter put the occlusion, in the composite's uv. See `uAoOffset`. */
|
|
754
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]);
|
|
755
792
|
/**
|
|
756
793
|
* Whether this frame has already been resolved to the canvas.
|
|
757
794
|
*
|
|
@@ -905,6 +942,32 @@ export class WebGL2Renderer {
|
|
|
905
942
|
overlayTexture = null;
|
|
906
943
|
/** Whether this device's lit stage has room for the overlay's vectors: asked once. */
|
|
907
944
|
overlayRoom = null;
|
|
945
|
+
/**
|
|
946
|
+
* The matrix the sun's dynamic shadow pass was last drawn with, uncorrected, and whether one has
|
|
947
|
+
* been: what the moving layer is read through. See `movingLightFor`.
|
|
948
|
+
*/
|
|
949
|
+
movingLightViewProj = new Float32Array(16);
|
|
950
|
+
movingLightKnown = false;
|
|
951
|
+
/** Whether the lit stage has room for the moving layer's matrix, and whether its refusal was said. */
|
|
952
|
+
movingRoom = null;
|
|
953
|
+
warnedMovingRoom = false;
|
|
954
|
+
/** The same answer for a material's projection, and whether its refusal was said. */
|
|
955
|
+
projectionRoom = null;
|
|
956
|
+
warnedProjectionRoom = false;
|
|
957
|
+
/** `uWorldUv`, the current material's projection packed. See `surfaceProjection.ts`. */
|
|
958
|
+
projection = new Float32Array(PROJECTION_FLOATS);
|
|
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]);
|
|
964
|
+
/** `uLayers`, the current material's layers packed. See `surfaceLayers.ts`. */
|
|
965
|
+
layerFloats = new Float32Array(LAYER_FLOATS);
|
|
966
|
+
/** Whether the lit stage has room for layers' two vectors, and which refusals were said. */
|
|
967
|
+
layersRoom = null;
|
|
968
|
+
warnedLayersRoom = false;
|
|
969
|
+
warnedLayersModelMap = false;
|
|
970
|
+
warnedLayersTriplanar = false;
|
|
908
971
|
warnedOverlayRoom = false;
|
|
909
972
|
warnedOverlayBlocks = false;
|
|
910
973
|
/** Whether a probe is being baked, so the passes that would recurse into one can tell. */
|
|
@@ -2109,6 +2172,14 @@ export class WebGL2Renderer {
|
|
|
2109
2172
|
* Measured on an RX 9070 XT through ANGLE/Vulkan: offered. `scripts/depth-survey.mjs` is what
|
|
2110
2173
|
* asks, and it also reports what WebGPU can do.
|
|
2111
2174
|
*/
|
|
2175
|
+
/*
|
|
2176
|
+
* Where draws skip a compile, the query that says a program is done without waiting for it;
|
|
2177
|
+
* where the context has none, a program is waited for as it always was. See `modelProgram`.
|
|
2178
|
+
*/
|
|
2179
|
+
this.parallelCompile =
|
|
2180
|
+
this.quality.pipelineCompile === 'skip'
|
|
2181
|
+
? gl.getExtension('KHR_parallel_shader_compile')
|
|
2182
|
+
: null;
|
|
2112
2183
|
this.clipControl = gl.getExtension('EXT_clip_control');
|
|
2113
2184
|
if (REVERSED_DEPTH && this.clipControl !== null) {
|
|
2114
2185
|
this.clipControl.clipControlEXT(this.clipControl.LOWER_LEFT_EXT, this.clipControl.ZERO_TO_ONE_EXT);
|
|
@@ -2353,7 +2424,33 @@ export class WebGL2Renderer {
|
|
|
2353
2424
|
* this before its first frame gets the right behaviour on both without asking which backend
|
|
2354
2425
|
* it has, which is the contract `RendererApi` exists to keep.
|
|
2355
2426
|
*/
|
|
2356
|
-
|
|
2427
|
+
/**
|
|
2428
|
+
* Resolve once every program a draw has started compiling has finished, adopting each: what a
|
|
2429
|
+
* loading screen awaits after drawing what its scene will use, where draws skip a compile. See
|
|
2430
|
+
* `RenderQuality.pipelineCompile`; nothing is started anywhere else here, so otherwise it is now.
|
|
2431
|
+
*
|
|
2432
|
+
* **It waits without holding the thread**, as the other backend's does: it asks the driver
|
|
2433
|
+
* whether each is done, which does not wait, and yields between asks. A driver that never says
|
|
2434
|
+
* done is asked the way that waits after `READY_MAX_POLLS`, so this cannot hang a loading screen.
|
|
2435
|
+
*/
|
|
2436
|
+
async ready() {
|
|
2437
|
+
for (let polls = 0; this.modelPending.size > 0 && !this.contextLost; polls++) {
|
|
2438
|
+
for (const [key, started] of this.modelPending) {
|
|
2439
|
+
const done = polls >= READY_MAX_POLLS ||
|
|
2440
|
+
this.parallelCompile === null ||
|
|
2441
|
+
this.gl.getProgramParameter(started.program, this.parallelCompile.COMPLETION_STATUS_KHR) === true;
|
|
2442
|
+
if (!done)
|
|
2443
|
+
continue;
|
|
2444
|
+
this.modelPending.delete(key);
|
|
2445
|
+
this.adoptModel(key, finishProgram(this.gl, started), started.label);
|
|
2446
|
+
}
|
|
2447
|
+
this.useFlatProgram();
|
|
2448
|
+
if (this.modelPending.size > 0) {
|
|
2449
|
+
// platform: browser default — a yield between two asks of the driver, in a browser module
|
|
2450
|
+
await new Promise((resolve) => setTimeout(resolve, READY_POLL_MS));
|
|
2451
|
+
}
|
|
2452
|
+
}
|
|
2453
|
+
}
|
|
2357
2454
|
dispose(options = {}) {
|
|
2358
2455
|
if (this.disposed)
|
|
2359
2456
|
return;
|
|
@@ -2772,6 +2869,57 @@ export class WebGL2Renderer {
|
|
|
2772
2869
|
* `uScale`/`vScale` are repeats across the mesh's own UV range: geometry authored in
|
|
2773
2870
|
* metres is textured at whatever density the material wants without rebuilding it.
|
|
2774
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
|
+
}
|
|
2775
2923
|
/**
|
|
2776
2924
|
* Choose the joint palette the following `drawMesh` calls skin by, or null for none.
|
|
2777
2925
|
*
|
|
@@ -2808,7 +2956,30 @@ export class WebGL2Renderer {
|
|
|
2808
2956
|
return held;
|
|
2809
2957
|
const { gl } = this;
|
|
2810
2958
|
const label = `flat.${variant}.${kind}.${half}`;
|
|
2811
|
-
const
|
|
2959
|
+
const vertex = modelVertexOf(variant, kind);
|
|
2960
|
+
const fragment = skinHalfBound(modelBound(this.flatFragSource, kind), half);
|
|
2961
|
+
/*
|
|
2962
|
+
* **Where draws skip a compile, none until the driver says asking will not wait**: the program
|
|
2963
|
+
* is started at the first draw that wants it and that draw, and every one before it is ready,
|
|
2964
|
+
* is left out. See `RenderQuality.pipelineCompile`.
|
|
2965
|
+
*/
|
|
2966
|
+
if (this.parallelCompile !== null) {
|
|
2967
|
+
const started = this.modelPending.get(key);
|
|
2968
|
+
if (started === undefined) {
|
|
2969
|
+
this.modelPending.set(key, startProgram(gl, vertex, fragment, label));
|
|
2970
|
+
return null;
|
|
2971
|
+
}
|
|
2972
|
+
if (gl.getProgramParameter(started.program, this.parallelCompile.COMPLETION_STATUS_KHR) !== true) {
|
|
2973
|
+
return null;
|
|
2974
|
+
}
|
|
2975
|
+
this.modelPending.delete(key);
|
|
2976
|
+
return this.adoptModel(key, finishProgram(gl, started), label);
|
|
2977
|
+
}
|
|
2978
|
+
return this.adoptModel(key, compileProgram(gl, vertex, fragment, label), label);
|
|
2979
|
+
}
|
|
2980
|
+
/** A model program compiled: kept, written to by every material setter, and fed the pass. */
|
|
2981
|
+
adoptModel(key, program, label) {
|
|
2982
|
+
const { gl } = this;
|
|
2812
2983
|
const made = { program, uniforms: uniformLocations(gl, program, label) };
|
|
2813
2984
|
this.modelFlat.set(key, made);
|
|
2814
2985
|
this.flatTargets.push(made);
|
|
@@ -3085,10 +3256,17 @@ export class WebGL2Renderer {
|
|
|
3085
3256
|
* and read neither — one decision for every program, as `setMaterial` says.
|
|
3086
3257
|
*/
|
|
3087
3258
|
const modelMap = material?.modelMap ?? null;
|
|
3259
|
+
/* A layered material's mask, where it is a map, goes where a model's would: surfaceLayers.ts. */
|
|
3260
|
+
const layers = this.layersOf(material);
|
|
3261
|
+
const boundMap = modelMap ?? layerMaskMap(layers);
|
|
3088
3262
|
gl.activeTexture(gl.TEXTURE0 + MODEL_MAP_TEXTURE_UNIT);
|
|
3089
3263
|
gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.emptyTexture2DArray);
|
|
3090
|
-
if (
|
|
3091
|
-
|
|
3264
|
+
if (boundMap !== null)
|
|
3265
|
+
boundMap.bind(gl, MODEL_MAP_TEXTURE_UNIT);
|
|
3266
|
+
packSurfaceLayers(layers, this.layerFloats);
|
|
3267
|
+
if (layers !== null)
|
|
3268
|
+
this.askLit('LAYERED');
|
|
3269
|
+
gl.uniform4fv(u['uLayers'] ?? null, this.layerFloats);
|
|
3092
3270
|
gl.uniform1i(u['uModelMap'] ?? null, MODEL_MAP_TEXTURE_UNIT);
|
|
3093
3271
|
packModel(material?.model ?? null, modelMap !== null, this.modelParams, material?.physicalSpecular === true);
|
|
3094
3272
|
/* The first physical highlight asked for is when the lit programs start carrying one. */
|
|
@@ -3097,6 +3275,18 @@ export class WebGL2Renderer {
|
|
|
3097
3275
|
gl.uniform4fv(u['uModelParams'] ?? null, this.modelParams);
|
|
3098
3276
|
/* The draw's overlay, which a program born mid-pass must wear too: see `setSurfaceOverlay`. */
|
|
3099
3277
|
gl.uniform4fv(u['uOverlay'] ?? null, this.overlayFloats);
|
|
3278
|
+
gl.uniform4fv(u['uWorldUv'] ?? null, this.projection);
|
|
3279
|
+
/* How much light from behind the surface lets through, and its colour: transmission.ts. */
|
|
3280
|
+
resolveTransmission(material, this.transmission);
|
|
3281
|
+
this.writeTransmission(u);
|
|
3282
|
+
/* Maps placed by the world, where the material asks and the lit stage has room: worldUv.ts. */
|
|
3283
|
+
const projection = layeredProjection(material?.projection, layers);
|
|
3284
|
+
if (projection !== (material?.projection ?? null))
|
|
3285
|
+
this.warnLayersTriplanar();
|
|
3286
|
+
packSurfaceProjection(this.projectionFits(projection) ? projection : null, this.projection);
|
|
3287
|
+
if (this.projection[0] > 0)
|
|
3288
|
+
this.askLit('WORLD_UVS');
|
|
3289
|
+
gl.uniform4fv(u['uWorldUv'] ?? null, this.projection);
|
|
3100
3290
|
/* The albedo's effects table, or the stand-in the shader reads as none. Before the early
|
|
3101
3291
|
return, so a material with no albedo still leaves a complete texture on the unit. */
|
|
3102
3292
|
if (albedo === null) {
|
|
@@ -3261,14 +3451,107 @@ export class WebGL2Renderer {
|
|
|
3261
3451
|
}
|
|
3262
3452
|
/** What the lit shader at this renderer's budget leaves of the limit, with no overlay. */
|
|
3263
3453
|
overlayRoomLeft() {
|
|
3264
|
-
return (this.fragmentVectorLimit() -
|
|
3454
|
+
return (this.fragmentVectorLimit() -
|
|
3455
|
+
countUniformVectors(this.flatFragAt(this.lightBudget, { SURFACE_OVERLAY: false })));
|
|
3265
3456
|
}
|
|
3266
3457
|
/** Whether the lit shader with the overlay compiled in fits this GPU: counted once. */
|
|
3267
3458
|
overlayFits() {
|
|
3268
|
-
this.overlayRoom ??=
|
|
3269
|
-
countUniformVectors(this.flatFragAt(this.lightBudget, true)) <= this.fragmentVectorLimit();
|
|
3459
|
+
this.overlayRoom ??= this.switchFits('SURFACE_OVERLAY');
|
|
3270
3460
|
return this.overlayRoom;
|
|
3271
3461
|
}
|
|
3462
|
+
/**
|
|
3463
|
+
* Whether a material's projection can be drawn: none asked, or the lit stage has the room for its
|
|
3464
|
+
* one vector. **Refused, said once, where it has not**, as the overlay is, and the material's
|
|
3465
|
+
* maps are then laid by the mesh's coordinates, as they were before it existed.
|
|
3466
|
+
*/
|
|
3467
|
+
projectionFits(projection) {
|
|
3468
|
+
if (!projects(projection))
|
|
3469
|
+
return true;
|
|
3470
|
+
this.projectionRoom ??= this.switchFits('WORLD_UVS');
|
|
3471
|
+
if (!this.projectionRoom && !this.warnedProjectionRoom) {
|
|
3472
|
+
this.warnedProjectionRoom = true;
|
|
3473
|
+
console.warn("Renderer: a material's projection is refused on this device. It takes one fragment " +
|
|
3474
|
+
'uniform vector and the lit shader already uses all ' +
|
|
3475
|
+
`${this.fragmentVectorLimit()} this GPU offers; its maps follow the mesh's coordinates. ` +
|
|
3476
|
+
'A lower `maxLights` makes the room, or WebGPU has it.');
|
|
3477
|
+
}
|
|
3478
|
+
return this.projectionRoom;
|
|
3479
|
+
}
|
|
3480
|
+
/**
|
|
3481
|
+
* What the sun's moving layer is read through: the matrix its shadow pass was last drawn with,
|
|
3482
|
+
* where the lit stage carries one (`MOVING_SUN`); the environment's otherwise, which every caller
|
|
3483
|
+
* drawing both layers with one matrix hands it anyway.
|
|
3484
|
+
*/
|
|
3485
|
+
movingLightFor(env) {
|
|
3486
|
+
return this.movingLightKnown && this.litOn.MOVING_SUN
|
|
3487
|
+
? this.movingLightViewProj
|
|
3488
|
+
: env.lightViewProj;
|
|
3489
|
+
}
|
|
3490
|
+
/**
|
|
3491
|
+
* Turn the moving layer's own matrix on the first time a frame draws that layer with one the
|
|
3492
|
+
* environment's does not share. **Refused, said once, where the lit stage has no room for its five
|
|
3493
|
+
* vectors**: the layer is then read through the environment's matrix, so its shadows land where
|
|
3494
|
+
* that matrix puts them, and a caller there draws both layers with one.
|
|
3495
|
+
*/
|
|
3496
|
+
askMovingSun(env) {
|
|
3497
|
+
if (this.litWanted.MOVING_SUN || !this.movingLightKnown)
|
|
3498
|
+
return;
|
|
3499
|
+
if (sameMatrix(this.movingLightViewProj, env.lightViewProj))
|
|
3500
|
+
return;
|
|
3501
|
+
this.movingRoom ??= this.switchFits('MOVING_SUN');
|
|
3502
|
+
if (this.movingRoom) {
|
|
3503
|
+
this.askLit('MOVING_SUN');
|
|
3504
|
+
return;
|
|
3505
|
+
}
|
|
3506
|
+
if (this.warnedMovingRoom)
|
|
3507
|
+
return;
|
|
3508
|
+
this.warnedMovingRoom = true;
|
|
3509
|
+
console.warn("Renderer: the sun's moving layer cannot be read through a matrix of its own on this " +
|
|
3510
|
+
'device; it takes five fragment uniform vectors the lit shader does not have. It is read ' +
|
|
3511
|
+
"through the environment's `lightViewProj`, so draw the dynamic shadow pass with that " +
|
|
3512
|
+
'matrix here, or lower `maxLights` to make the room. WebGPU has it.');
|
|
3513
|
+
}
|
|
3514
|
+
/**
|
|
3515
|
+
* A material's layers where they can be drawn: none asked, a model's map in the mask's place, or
|
|
3516
|
+
* no room for their two vectors each leave one layer, the last two said once each.
|
|
3517
|
+
*/
|
|
3518
|
+
layersOf(material) {
|
|
3519
|
+
const layers = (material?.layers ?? null);
|
|
3520
|
+
if (layers === null || layers.repeats.length === 0)
|
|
3521
|
+
return null;
|
|
3522
|
+
if (!layersFitBeside(layers, material?.modelMap)) {
|
|
3523
|
+
if (!this.warnedLayersModelMap) {
|
|
3524
|
+
this.warnedLayersModelMap = true;
|
|
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.");
|
|
3528
|
+
}
|
|
3529
|
+
return null;
|
|
3530
|
+
}
|
|
3531
|
+
this.layersRoom ??= this.switchFits('LAYERED');
|
|
3532
|
+
if (this.layersRoom)
|
|
3533
|
+
return layers;
|
|
3534
|
+
if (!this.warnedLayersRoom) {
|
|
3535
|
+
this.warnedLayersRoom = true;
|
|
3536
|
+
console.warn("Renderer: a material's layers are refused on this device. They take four fragment uniform " +
|
|
3537
|
+
'vectors the lit shader does not have; the material is drawn as its base layer. A lower ' +
|
|
3538
|
+
'`maxLights` makes the room, or WebGPU has it.');
|
|
3539
|
+
}
|
|
3540
|
+
return null;
|
|
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
|
+
}
|
|
3550
|
+
/** Whether the lit shader with `feature` compiled in fits this GPU's fragment uniform vectors. */
|
|
3551
|
+
switchFits(feature) {
|
|
3552
|
+
const asIf = { [feature]: true };
|
|
3553
|
+
return (countUniformVectors(this.flatFragAt(this.lightBudget, asIf)) <= this.fragmentVectorLimit());
|
|
3554
|
+
}
|
|
3272
3555
|
/**
|
|
3273
3556
|
* How bright the environment the surfaces drawn next reflect is. 1 is the default and identity.
|
|
3274
3557
|
*
|
|
@@ -3410,9 +3693,38 @@ export class WebGL2Renderer {
|
|
|
3410
3693
|
}
|
|
3411
3694
|
this.useFlatProgram();
|
|
3412
3695
|
}
|
|
3413
|
-
/**
|
|
3696
|
+
/**
|
|
3697
|
+
* Whether the draws that follow spend their own opacity on a screen door rather than on blending:
|
|
3698
|
+
* an instance's `MeshInstances.alphas`, or a vertex's channel alpha, keeps that share of the
|
|
3699
|
+
* pixels `setDitherFade` would and draws the rest not at all. So an opaque or cut-out batch fades
|
|
3700
|
+
* one instance at a time, with no sorting and no blend, and an instance at 1 is drawn whole.
|
|
3701
|
+
*
|
|
3702
|
+
* Off is what `bindMeshPass` restores. It shares the fade's pattern, so a crossfade at `t` and
|
|
3703
|
+
* `-t` of an instance at opacity `a` covers that instance's share once, without holes. Material
|
|
3704
|
+
* state like `setDitherFade`; instanced draws read it, and shadow casters do not.
|
|
3705
|
+
*
|
|
3706
|
+
* What it gives up: a fade reads as grain while it lasts, which a temporal resolve smooths and a
|
|
3707
|
+
* plain frame does not; a blended draw that asks spends its opacity here and blends none of it;
|
|
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`).
|
|
3710
|
+
*/
|
|
3711
|
+
setDitherOpacity(on) {
|
|
3712
|
+
if (this.contextLost)
|
|
3713
|
+
return;
|
|
3714
|
+
this.materials.dirty();
|
|
3715
|
+
this.ditherOpacity = on ? 1 : 0;
|
|
3716
|
+
for (const target of this.flatTargets) {
|
|
3717
|
+
this.gl.useProgram(target.program);
|
|
3718
|
+
this.writeMode(target.uniforms);
|
|
3719
|
+
}
|
|
3720
|
+
this.useFlatProgram();
|
|
3721
|
+
}
|
|
3722
|
+
/**
|
|
3723
|
+
* `uWriteMode`: the order-independent weight, the fade and whether the opacity is dithered, which
|
|
3724
|
+
* share one uniform row.
|
|
3725
|
+
*/
|
|
3414
3726
|
writeMode(u) {
|
|
3415
|
-
this.gl.
|
|
3727
|
+
this.gl.uniform3f(u['uWriteMode'] ?? null, this.oitWeight, this.ditherFade, this.ditherOpacity);
|
|
3416
3728
|
}
|
|
3417
3729
|
/**
|
|
3418
3730
|
* How strong the microscopic relief on the surfaces drawn next is, and how coarse.
|
|
@@ -3708,6 +4020,9 @@ export class WebGL2Renderer {
|
|
|
3708
4020
|
const modelled = modelKind === null
|
|
3709
4021
|
? null
|
|
3710
4022
|
: this.modelProgram(clip === null ? 'instanced' : 'animated', modelKind);
|
|
4023
|
+
/* Its model's program still compiling, where draws skip a compile: left out until it lands. */
|
|
4024
|
+
if (modelKind !== null && modelled === null)
|
|
4025
|
+
return;
|
|
3711
4026
|
const program = modelled?.program ?? (clip === null ? this.flatInstancedProgram : this.flatAnimatedProgram);
|
|
3712
4027
|
const u = modelled?.uniforms ??
|
|
3713
4028
|
(clip === null ? this.flatInstancedUniforms : this.flatAnimatedUniforms);
|
|
@@ -3861,13 +4176,21 @@ export class WebGL2Renderer {
|
|
|
3861
4176
|
/* w: this framebuffer's first row is the bottom, so up the screen is up the rows. */
|
|
3862
4177
|
const tint = options.refractTint ?? WHITE_TINT;
|
|
3863
4178
|
gl.uniform4f(u['uRefractTint'] ?? null, tint[0] ?? 1, tint[1] ?? 1, tint[2] ?? 1, 1);
|
|
3864
|
-
|
|
4179
|
+
/* The pane's colour; w is the material's light through from behind, which glass never reads. */
|
|
4180
|
+
gl.uniform4f(u['uGlassTint'] ?? null, glass.tint[0] ?? 1, glass.tint[1] ?? 1, glass.tint[2] ?? 1, this.transmission[3]);
|
|
3865
4181
|
return true;
|
|
3866
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
|
+
}
|
|
3867
4188
|
/** Put back what `bindSeeThrough` bound: the binding outlives its frame (the 2026-08-27 rule). */
|
|
3868
4189
|
unbindSeeThrough(u) {
|
|
3869
4190
|
const { gl } = this;
|
|
3870
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);
|
|
3871
4194
|
gl.activeTexture(gl.TEXTURE0 + REFRACT_SCENE_TEXTURE_UNIT);
|
|
3872
4195
|
/* The overlay's atlas, where one is held: it shares the unit. See `setSurfaceOverlay`. */
|
|
3873
4196
|
gl.bindTexture(gl.TEXTURE_2D, this.overlayTexture ?? this.emptyTexture2D);
|
|
@@ -4749,6 +5072,11 @@ export class WebGL2Renderer {
|
|
|
4749
5072
|
this.shadowPassActive = true;
|
|
4750
5073
|
this.activeSunLayer = layer;
|
|
4751
5074
|
this.glassCasters.clear();
|
|
5075
|
+
/* The moving layer is read through the matrix it is drawn with: see `movingLightFor`. */
|
|
5076
|
+
if (layer === 'dynamic') {
|
|
5077
|
+
this.movingLightViewProj.set(lightViewProj);
|
|
5078
|
+
this.movingLightKnown = true;
|
|
5079
|
+
}
|
|
4752
5080
|
/* The static layer opens first, so a frame's peel counts from here. See `peelFilled`. */
|
|
4753
5081
|
if (layer === 'static')
|
|
4754
5082
|
this.peelFilled = false;
|
|
@@ -5147,7 +5475,9 @@ export class WebGL2Renderer {
|
|
|
5147
5475
|
* throws a sentence with the device's numbers in it rather than a bare driver string.
|
|
5148
5476
|
*/
|
|
5149
5477
|
/** The lit fragment source this renderer's profile compiles, at a light budget. */
|
|
5150
|
-
flatFragAt(budget,
|
|
5478
|
+
flatFragAt(budget,
|
|
5479
|
+
/* A switch counted as it would be, for asking whether it fits: see `switchFits`. */
|
|
5480
|
+
asIf = {}) {
|
|
5151
5481
|
return flatFrag({
|
|
5152
5482
|
pointShadows: this.quality.pointShadows,
|
|
5153
5483
|
directionalShadows: this.quality.directionalShadows,
|
|
@@ -5160,8 +5490,11 @@ export class WebGL2Renderer {
|
|
|
5160
5490
|
surfaceEffects: this.litOn.SURFACE_EFFECTS,
|
|
5161
5491
|
driftLight: this.litOn.DRIFT_LIGHT,
|
|
5162
5492
|
physicalSpecular: this.litOn.PHYSICAL_SPECULAR,
|
|
5163
|
-
surfaceOverlay:
|
|
5493
|
+
surfaceOverlay: asIf.SURFACE_OVERLAY ?? this.litOn.SURFACE_OVERLAY,
|
|
5164
5494
|
reflectionMaps: this.litOn.REFLECTION_MAPS,
|
|
5495
|
+
worldUvs: asIf.WORLD_UVS ?? this.litOn.WORLD_UVS,
|
|
5496
|
+
movingSun: asIf.MOVING_SUN ?? this.litOn.MOVING_SUN,
|
|
5497
|
+
layered: asIf.LAYERED ?? this.litOn.LAYERED,
|
|
5165
5498
|
maxLights: budget.maxLights,
|
|
5166
5499
|
maxAreaLights: budget.maxAreaLights,
|
|
5167
5500
|
});
|
|
@@ -5221,6 +5554,10 @@ export class WebGL2Renderer {
|
|
|
5221
5554
|
gl.deleteProgram(entry.program);
|
|
5222
5555
|
this.clothFlat.set(key, { program, uniforms: uniformLocations(gl, program, label) });
|
|
5223
5556
|
}
|
|
5557
|
+
/* Started against the source before this switch: begun again at the next draw that asks. */
|
|
5558
|
+
for (const started of this.modelPending.values())
|
|
5559
|
+
gl.deleteProgram(started.program);
|
|
5560
|
+
this.modelPending.clear();
|
|
5224
5561
|
for (const [key, entry] of this.modelFlat) {
|
|
5225
5562
|
const [variant, kind, half] = key.split('|');
|
|
5226
5563
|
const label = `flat.${variant}.${kind}.${half}`;
|
|
@@ -5918,6 +6255,19 @@ export class WebGL2Renderer {
|
|
|
5918
6255
|
}
|
|
5919
6256
|
this.sceneTarget.setLocalExposure(strength);
|
|
5920
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
|
+
}
|
|
5921
6271
|
/**
|
|
5922
6272
|
* Where this frame's lens is focused, how deep the sharp zone is, and how much of the ceiling
|
|
5923
6273
|
* to take.
|
|
@@ -6545,6 +6895,7 @@ export class WebGL2Renderer {
|
|
|
6545
6895
|
radius: this.quality.ambientOcclusionRadius,
|
|
6546
6896
|
projScale: this.aoProjScale,
|
|
6547
6897
|
invProjection: this.aoInvProjection,
|
|
6898
|
+
fade: this.aoFade,
|
|
6548
6899
|
offset: this.aoOffset,
|
|
6549
6900
|
};
|
|
6550
6901
|
}
|
|
@@ -6760,6 +7111,7 @@ export class WebGL2Renderer {
|
|
|
6760
7111
|
ambient: env?.ambient ?? [0, 0, 0],
|
|
6761
7112
|
sunShadow,
|
|
6762
7113
|
lightViewProj: env?.lightViewProj ?? IDENTITY_MAT4,
|
|
7114
|
+
movingLightViewProj: env === null ? IDENTITY_MAT4 : this.movingLightFor(env),
|
|
6763
7115
|
/* The placeholder rather than null where a map is merely absent, for the reason
|
|
6764
7116
|
`emptyTexture.ts` gives. It is black, which as a depth is an occluder at the light,
|
|
6765
7117
|
so it is safe only because `sunShadow` is 0 whenever these maps do not exist. */
|
|
@@ -6993,6 +7345,12 @@ export class WebGL2Renderer {
|
|
|
6993
7345
|
this.currentSurfaceTexture = null;
|
|
6994
7346
|
/* Nor an overlay: the atlas's unit is given the empty texture with the refraction copy's. */
|
|
6995
7347
|
this.overlayFloats.fill(0);
|
|
7348
|
+
/* Nor a projection: maps follow the mesh's coordinates until a material asks otherwise. */
|
|
7349
|
+
this.projection.fill(0);
|
|
7350
|
+
/* Nor light through from behind. See `SurfaceMaterial.diffuseTransmission`. */
|
|
7351
|
+
resolveTransmission(null, this.transmission);
|
|
7352
|
+
/* Nor layers: one layer until a material asks for more. */
|
|
7353
|
+
this.layerFloats.fill(0);
|
|
6996
7354
|
this.overlayMaps = null;
|
|
6997
7355
|
this.overlayTexture = null;
|
|
6998
7356
|
/* Nor an ambient of its own: the frame's, until a draw asks. See `setAmbientSH`. */
|
|
@@ -7006,6 +7364,12 @@ export class WebGL2Renderer {
|
|
|
7006
7364
|
this.environmentDials[2] = 1;
|
|
7007
7365
|
this.emission[0] = env.emissiveGain;
|
|
7008
7366
|
this.emission[1] = env.nightFactor;
|
|
7367
|
+
/* The moving layer's own matrix, the first time a frame draws that layer with one. */
|
|
7368
|
+
this.askMovingSun(env);
|
|
7369
|
+
/* No dither and no weighting: every draw whole, until a crossfade or an opacity asks. */
|
|
7370
|
+
this.oitWeight = 0;
|
|
7371
|
+
this.ditherFade = 0;
|
|
7372
|
+
this.ditherOpacity = 0;
|
|
7009
7373
|
for (const target of this.flatTargets) {
|
|
7010
7374
|
this.gl.useProgram(target.program);
|
|
7011
7375
|
this.writeMeshPassState(target.uniforms, camera, env);
|
|
@@ -7128,11 +7492,15 @@ export class WebGL2Renderer {
|
|
|
7128
7492
|
gl.uniform1i(u['uRefractScene'] ?? null, REFRACT_SCENE_TEXTURE_UNIT);
|
|
7129
7493
|
/* The draws' overlay, none at a pass's start, and its atlas above: see `setSurfaceOverlay`. */
|
|
7130
7494
|
gl.uniform4fv(u['uOverlay'] ?? null, this.overlayFloats);
|
|
7495
|
+
gl.uniform4fv(u['uWorldUv'] ?? null, this.projection);
|
|
7496
|
+
gl.uniform4fv(u['uLayers'] ?? null, this.layerFloats);
|
|
7131
7497
|
/* Neither refracting nor glass, which is every draw that does not say otherwise. See
|
|
7132
7498
|
`bindSeeThrough` for the four numbers in the one vector. */
|
|
7133
7499
|
gl.uniform4f(u['uSeeThrough'] ?? null, 0, 0, 0, 0);
|
|
7134
7500
|
gl.uniform4f(u['uRefractTint'] ?? null, 1, 1, 1, 1);
|
|
7135
|
-
|
|
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);
|
|
7136
7504
|
gl.uniform3fv(u['uDirectionalDir'] ?? null, env.directionalDir);
|
|
7137
7505
|
gl.uniform3fv(u['uDirectionalColor'] ?? null, env.directionalColor);
|
|
7138
7506
|
gl.uniform3fv(u['uAmbient'] ?? null, env.ambient);
|
|
@@ -7199,10 +7567,9 @@ export class WebGL2Renderer {
|
|
|
7199
7567
|
this.surfaceFog = FOG_RECEDE;
|
|
7200
7568
|
/* Grain on by default, so every scene written before it was a choice looks unchanged. */
|
|
7201
7569
|
gl.uniform1f(u['uGrain'] ?? null, 1);
|
|
7202
|
-
/*
|
|
7203
|
-
|
|
7204
|
-
this.
|
|
7205
|
-
gl.uniform2f(u['uWriteMode'] ?? null, 0, 0);
|
|
7570
|
+
/* The weighting and the dither as they stand, which `bindMeshPass` has just cleared: a program
|
|
7571
|
+
built in the middle of a pass takes what the caller set. See `setDitherFade`. */
|
|
7572
|
+
this.writeMode(u);
|
|
7206
7573
|
/* The draw's own ambient as it stands, which `bindMeshPass` has just cleared: a program built
|
|
7207
7574
|
in the middle of a pass takes what the caller set. See `setAmbientSH`. */
|
|
7208
7575
|
gl.uniform4fv(u['uAmbientSH[0]'] ?? null, this.ambientSH);
|
|
@@ -7241,6 +7608,10 @@ export class WebGL2Renderer {
|
|
|
7241
7608
|
gl.uniform1f(u['uShadowStrength'] ?? null, this.sunShadows === null ? 0 : env.shadowStrength);
|
|
7242
7609
|
gl.uniform1f(u['uShadowMapSize'] ?? null, this.quality.directionalShadowMapSize);
|
|
7243
7610
|
gl.uniform1f(u['uShadowDepthSpan'] ?? null, env.shadowDepthSpan);
|
|
7611
|
+
/* The moving layer's own matrix and span, where the program reads one: see `movingLightFor`. */
|
|
7612
|
+
const moving = this.movingLightFor(env);
|
|
7613
|
+
gl.uniformMatrix4fv(u['uMovingLightViewProj'] ?? null, false, moving);
|
|
7614
|
+
gl.uniform1f(u['uMovingShadowDepthSpan'] ?? null, orthographicDepthSpan(moving));
|
|
7244
7615
|
gl.uniform1f(u['uShadowMaxDistance'] ?? null, this.quality.directionalShadowMaxDistance);
|
|
7245
7616
|
gl.uniform1i(u['uPeeledShadowEnabled'] ?? null, this.peelFilled ? 1 : 0);
|
|
7246
7617
|
gl.uniform1f(u['uShadowMaxSlope'] ?? null, this.quality.directionalShadowMaxSlope);
|
|
@@ -7551,6 +7922,9 @@ export class WebGL2Renderer {
|
|
|
7551
7922
|
* light and its colour, each into the blur's own target.
|
|
7552
7923
|
*/
|
|
7553
7924
|
const splits = this.skinSplits();
|
|
7925
|
+
/* A split skin draws its three halves or none, where a half may still be compiling. */
|
|
7926
|
+
if (splits && this.parallelCompile !== null && !this.skinHalvesReady(mesh))
|
|
7927
|
+
return;
|
|
7554
7928
|
/* An opaque lit draw into the frame writes its material's reflection too: see `reflectionKeeps`. */
|
|
7555
7929
|
const keeps = this.reflectionKeeps();
|
|
7556
7930
|
if (keeps)
|
|
@@ -7629,8 +8003,25 @@ export class WebGL2Renderer {
|
|
|
7629
8003
|
this.useFlatProgram();
|
|
7630
8004
|
}
|
|
7631
8005
|
/** One draw of a mesh, as `drawMesh` describes, of the half of a skin `half` names. */
|
|
7632
|
-
|
|
7633
|
-
|
|
8006
|
+
/**
|
|
8007
|
+
* Whether every half a split skin draws has its program, starting any that has not: a skin drawn
|
|
8008
|
+
* without its kept halves would lose its diffuse light for the frames they take to compile.
|
|
8009
|
+
*/
|
|
8010
|
+
skinHalvesReady(mesh) {
|
|
8011
|
+
const variant = this.drawVariantOf(mesh).name;
|
|
8012
|
+
let ready = true;
|
|
8013
|
+
for (const half of SPLIT_SKIN_HALVES) {
|
|
8014
|
+
if (this.modelProgram(variant, 'skin', half) === null)
|
|
8015
|
+
ready = false;
|
|
8016
|
+
}
|
|
8017
|
+
return ready;
|
|
8018
|
+
}
|
|
8019
|
+
/**
|
|
8020
|
+
* The vertex variant a draw of `mesh` takes now, and its program and uniforms where it is not the
|
|
8021
|
+
* plain one: what a model's program is keyed by, and what `drawMeshHalf` draws with.
|
|
8022
|
+
*/
|
|
8023
|
+
drawVariantOf(mesh) {
|
|
8024
|
+
const out = this.drawVariant;
|
|
7634
8025
|
/*
|
|
7635
8026
|
* A rigged mesh takes the skinned program, an unrigged one the plain program.
|
|
7636
8027
|
*
|
|
@@ -7658,9 +8049,8 @@ export class WebGL2Renderer {
|
|
|
7658
8049
|
* when none were asked for — an unbound sampler would default to a shadow map's unit.
|
|
7659
8050
|
*/
|
|
7660
8051
|
const cloth = wantSkin ? this.cloth : null;
|
|
7661
|
-
const
|
|
7662
|
-
|
|
7663
|
-
: this.clothFlat.get(`${eight ? 8 : 4}|${mesh.morph !== null ? 'm' : '-'}`);
|
|
8052
|
+
const clothKey = `${eight ? 8 : 4}|${mesh.morph !== null ? 'm' : '-'}`;
|
|
8053
|
+
const clothed = cloth === null ? undefined : this.clothFlat.get(clothKey);
|
|
7664
8054
|
const bothUniforms = eight ? this.flatBoth8Uniforms : this.flatBothUniforms;
|
|
7665
8055
|
const skinnedUniforms = eight ? this.flatSkinned8Uniforms : this.flatSkinnedUniforms;
|
|
7666
8056
|
const both = wantSkin && wantMorph && bothUniforms !== null;
|
|
@@ -7673,24 +8063,27 @@ export class WebGL2Renderer {
|
|
|
7673
8063
|
: morphed
|
|
7674
8064
|
? this.flatMorphedUniforms
|
|
7675
8065
|
: null;
|
|
7676
|
-
|
|
7677
|
-
|
|
7678
|
-
|
|
7679
|
-
|
|
7680
|
-
|
|
7681
|
-
|
|
7682
|
-
|
|
7683
|
-
|
|
8066
|
+
out.skinned = skinned;
|
|
8067
|
+
out.morphed = morphed;
|
|
8068
|
+
out.cloth = cloth;
|
|
8069
|
+
out.clothed = clothed;
|
|
8070
|
+
out.uniforms = clothed?.uniforms ?? variant ?? this.flatUniforms;
|
|
8071
|
+
out.program =
|
|
8072
|
+
clothed !== undefined
|
|
8073
|
+
? clothed.program
|
|
8074
|
+
: skinned && morphed
|
|
7684
8075
|
? eight
|
|
7685
|
-
? this.
|
|
7686
|
-
: this.
|
|
7687
|
-
:
|
|
7688
|
-
?
|
|
7689
|
-
|
|
7690
|
-
|
|
7691
|
-
|
|
7692
|
-
|
|
7693
|
-
|
|
8076
|
+
? this.flatBoth8Program
|
|
8077
|
+
: this.flatBothProgram
|
|
8078
|
+
: skinned
|
|
8079
|
+
? eight
|
|
8080
|
+
? this.flatSkinned8Program
|
|
8081
|
+
: this.flatSkinnedProgram
|
|
8082
|
+
: morphed
|
|
8083
|
+
? this.flatMorphedProgram
|
|
8084
|
+
: null;
|
|
8085
|
+
out.name =
|
|
8086
|
+
clothed !== undefined
|
|
7694
8087
|
? `cloth${eight ? 8 : 4}${mesh.morph !== null ? 'm' : '-'}`
|
|
7695
8088
|
: skinned && morphed
|
|
7696
8089
|
? eight
|
|
@@ -7702,7 +8095,22 @@ export class WebGL2Renderer {
|
|
|
7702
8095
|
: 'skinned'
|
|
7703
8096
|
: morphed
|
|
7704
8097
|
? 'morphed'
|
|
7705
|
-
: 'plain'
|
|
8098
|
+
: 'plain';
|
|
8099
|
+
return out;
|
|
8100
|
+
}
|
|
8101
|
+
drawMeshHalf(mesh, model, depthLayer, tint, half) {
|
|
8102
|
+
const { gl } = this;
|
|
8103
|
+
const drawn = this.drawVariantOf(mesh);
|
|
8104
|
+
const { skinned, morphed, cloth, clothed } = drawn;
|
|
8105
|
+
let u = drawn.uniforms;
|
|
8106
|
+
let program = drawn.program;
|
|
8107
|
+
/* A material with a model takes that model's program for the same vertex variant. */
|
|
8108
|
+
const modelKind = this.currentMaterial?.model?.kind ?? null;
|
|
8109
|
+
if (modelKind !== null) {
|
|
8110
|
+
const modelled = this.modelProgram(drawn.name, modelKind, half);
|
|
8111
|
+
/* Still compiling, where draws skip a compile: left out until it lands. */
|
|
8112
|
+
if (modelled === null)
|
|
8113
|
+
return;
|
|
7706
8114
|
program = modelled.program;
|
|
7707
8115
|
u = modelled.uniforms;
|
|
7708
8116
|
}
|
|
@@ -7881,6 +8289,9 @@ export class WebGL2Renderer {
|
|
|
7881
8289
|
/* A material with a model takes that model's plain program; see `modelProgram`. */
|
|
7882
8290
|
const modelKind = this.currentMaterial?.model?.kind ?? null;
|
|
7883
8291
|
const modelled = modelKind === null ? null : this.modelProgram('plain', modelKind);
|
|
8292
|
+
/* Still compiling, where draws skip a compile: left out until it lands. */
|
|
8293
|
+
if (modelKind !== null && modelled === null)
|
|
8294
|
+
return;
|
|
7884
8295
|
const u = modelled?.uniforms ?? this.flatUniforms;
|
|
7885
8296
|
if (modelled === null)
|
|
7886
8297
|
this.useFlatProgram();
|
|
@@ -8360,6 +8771,7 @@ export class WebGL2Renderer {
|
|
|
8360
8771
|
/* Uncorrected, for the reason the lit program's copy of this line carries: the shaft's
|
|
8361
8772
|
lookup does its own `* 0.5 + 0.5` and correcting the matrix as well doubles it. */
|
|
8362
8773
|
gl.uniformMatrix4fv(u['uLightViewProj'] ?? null, false, env.lightViewProj);
|
|
8774
|
+
gl.uniformMatrix4fv(u['uMovingLightViewProj'] ?? null, false, this.movingLightFor(env));
|
|
8363
8775
|
gl.uniform1f(u['uShadowMapSize'] ?? null, this.quality.directionalShadowMapSize);
|
|
8364
8776
|
gl.uniform1i(u['uPeeledShadowEnabled'] ?? null, this.peelFilled ? 1 : 0);
|
|
8365
8777
|
gl.activeTexture(gl.TEXTURE0);
|