@driftengine/core 4.5.0 → 4.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.json +51 -0
- package/README.md +1 -1
- package/dist/geometry/boxCsg.d.ts +28 -0
- package/dist/geometry/boxCsg.js +139 -0
- package/dist/geometry/csg.d.ts +36 -0
- package/dist/geometry/csg.js +329 -0
- package/dist/geometry/meshBuilder.js +13 -0
- package/dist/geometry/meshConcat.js +1 -0
- package/dist/geometry/pixelFont.js +2 -0
- package/dist/geometry/solid.d.ts +61 -0
- package/dist/geometry/solid.js +152 -0
- package/dist/geometry/solidFlat.d.ts +35 -0
- package/dist/geometry/solidFlat.js +428 -0
- package/dist/geometry/solidHarness.d.ts +19 -0
- package/dist/geometry/solidHarness.js +66 -0
- package/dist/geometry/solidNormals.d.ts +17 -0
- package/dist/geometry/solidNormals.js +86 -0
- package/dist/geometry/solidRound.d.ts +41 -0
- package/dist/geometry/solidRound.js +323 -0
- package/dist/geometry/solidWriter.d.ts +16 -0
- package/dist/geometry/solidWriter.js +42 -0
- package/dist/index.d.ts +23 -2
- package/dist/index.js +13 -1
- package/dist/render/backend/api.d.ts +17 -3
- package/dist/render/backend/drawFog.d.ts +26 -0
- package/dist/render/backend/drawFog.js +30 -0
- package/dist/render/backend/materialChanges.d.ts +2 -1
- package/dist/render/backend/materialChanges.js +6 -1
- package/dist/render/backend/webgl2/instanced.d.ts +24 -5
- package/dist/render/backend/webgl2/instanced.js +72 -15
- package/dist/render/backend/webgl2/renderer.d.ts +122 -8
- package/dist/render/backend/webgl2/renderer.js +238 -121
- package/dist/render/backend/webgpu/buffers.d.ts +10 -0
- package/dist/render/backend/webgpu/buffers.js +34 -3
- package/dist/render/backend/webgpu/depthPass.js +5 -1
- package/dist/render/backend/webgpu/drawCommand.d.ts +5 -0
- package/dist/render/backend/webgpu/drawCommand.js +1 -0
- package/dist/render/backend/webgpu/flatPass.d.ts +4 -1
- package/dist/render/backend/webgpu/flatPass.js +38 -9
- package/dist/render/backend/webgpu/glassTintPass.js +2 -1
- package/dist/render/backend/webgpu/instanceCullPass.d.ts +34 -0
- package/dist/render/backend/webgpu/instanceCullPass.js +144 -0
- package/dist/render/backend/webgpu/instanced.d.ts +11 -1
- package/dist/render/backend/webgpu/instanced.js +21 -3
- package/dist/render/backend/webgpu/latePass.d.ts +6 -2
- package/dist/render/backend/webgpu/latePass.js +11 -4
- package/dist/render/backend/webgpu/postPass.d.ts +1 -1
- package/dist/render/backend/webgpu/renderer.d.ts +100 -10
- package/dist/render/backend/webgpu/renderer.js +462 -201
- package/dist/render/backend/webgpu/sdfTextPass.js +6 -1
- package/dist/render/backend/webgpu/shaders/instanceCull.wgsl.d.ts +18 -0
- package/dist/render/backend/webgpu/shaders/instanceCull.wgsl.js +59 -0
- package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +17 -3
- package/dist/render/backend/webgpu/surfaceTexturePass.js +77 -38
- package/dist/render/driftLight/bake.d.ts +7 -0
- package/dist/render/driftLight/bake.js +61 -56
- package/dist/render/driftLight/denseField.d.ts +46 -0
- package/dist/render/driftLight/denseField.js +109 -0
- package/dist/render/driftLight/lightField.d.ts +10 -5
- package/dist/render/driftLight/lightField.js +25 -19
- package/dist/render/driftLight/presence.d.ts +50 -0
- package/dist/render/driftLight/presence.js +43 -0
- package/dist/render/driftLight/uniforms.d.ts +3 -3
- package/dist/render/driftLight/uniforms.js +5 -6
- package/dist/render/driftLight/worldLightField.d.ts +41 -0
- package/dist/render/driftLight/worldLightField.js +66 -0
- package/dist/render/fixtureAtlas.d.ts +20 -0
- package/dist/render/fixtureAtlas.js +49 -0
- package/dist/render/instanceCull.d.ts +63 -0
- package/dist/render/instanceCull.js +160 -0
- package/dist/render/instances.d.ts +15 -0
- package/dist/render/lightBudget.d.ts +22 -33
- package/dist/render/lightBudget.js +24 -35
- package/dist/render/lightGrid.d.ts +42 -0
- package/dist/render/lightGrid.js +87 -0
- package/dist/render/mesh.d.ts +18 -24
- package/dist/render/mesh.js +51 -68
- package/dist/render/occlusion.d.ts +10 -0
- package/dist/render/occlusion.js +36 -7
- package/dist/render/pointLightSelection.d.ts +7 -0
- package/dist/render/pointLightSelection.js +29 -4
- package/dist/render/rainField.d.ts +13 -0
- package/dist/render/rainField.js +27 -5
- package/dist/render/recon/lateDepth.d.ts +11 -0
- package/dist/render/recon/lateDepth.js +70 -0
- package/dist/render/recon/resolve.d.ts +34 -4
- package/dist/render/recon/resolve.js +161 -20
- package/dist/render/rendererHarness.js +4 -0
- package/dist/render/sceneTarget.d.ts +2 -0
- package/dist/render/sceneTarget.js +2 -0
- package/dist/render/shaders/depth.js +12 -9
- package/dist/render/shaders/flat/driftLight.d.ts +1 -1
- package/dist/render/shaders/flat/driftLight.js +32 -7
- package/dist/render/shaders/flat/index.js +5 -3
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +75 -23
- package/dist/render/shaders/flat/preamble.js +75 -20
- package/dist/render/shaders/flat/surfaceEffects.d.ts +29 -0
- package/dist/render/shaders/flat/surfaceEffects.js +152 -0
- package/dist/render/shaders/generated/depth.wgsl.d.ts +8 -8
- package/dist/render/shaders/generated/depth.wgsl.js +8 -8
- package/dist/render/shaders/generated/flat.wgsl.d.ts +1320 -1320
- package/dist/render/shaders/generated/flat.wgsl.js +1359 -1345
- package/dist/render/shaders/generated/lightVolume.wgsl.js +18 -4
- package/dist/render/shaders/generated/rush.wgsl.d.ts +21 -16
- package/dist/render/shaders/generated/rush.wgsl.js +21 -16
- package/dist/render/shaders/generated/sdfText.wgsl.d.ts +2 -2
- package/dist/render/shaders/generated/sdfText.wgsl.js +2 -2
- package/dist/render/shaders/generated/text.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/text.wgsl.js +1 -1
- package/dist/render/shaders/gi/driftLight.wgsl.d.ts +1 -1
- package/dist/render/shaders/gi/driftLight.wgsl.js +27 -4
- package/dist/render/shaders/recon/lateDepth.wgsl.js +38 -13
- package/dist/render/shaders/recon/resolve.wgsl.d.ts +8 -7
- package/dist/render/shaders/recon/resolve.wgsl.js +161 -27
- package/dist/render/shaders/rush.js +11 -1
- package/dist/render/shaders/sdfText.d.ts +1 -1
- package/dist/render/shaders/sdfText.js +3 -2
- package/dist/render/shaders/text.d.ts +1 -1
- package/dist/render/shaders/text.js +18 -9
- package/dist/render/surfaceEffects.d.ts +83 -0
- package/dist/render/surfaceEffects.js +75 -0
- package/dist/render/surfaceScene.d.ts +8 -0
- package/dist/render/surfaceScene.js +8 -0
- package/dist/render/surfaceTexture.d.ts +18 -2
- package/dist/render/surfaceTexture.js +68 -15
- package/dist/render/temporalAa.d.ts +25 -6
- package/dist/render/temporalAa.js +45 -15
- package/dist/render/textureSource.d.ts +30 -0
- package/dist/render/textureSource.js +53 -0
- package/dist/render/vertexDefaults.js +2 -0
- package/dist/world/hlod.d.ts +104 -0
- package/dist/world/hlod.js +214 -0
- package/package.json +6 -6
- package/src/geometry/boxCsg.ts +186 -0
- package/src/geometry/csg.ts +384 -0
- package/src/geometry/meshBuilder.ts +15 -0
- package/src/geometry/meshConcat.ts +1 -0
- package/src/geometry/pixelFont.ts +2 -0
- package/src/geometry/solid.ts +189 -0
- package/src/geometry/solidFlat.ts +522 -0
- package/src/geometry/solidHarness.ts +83 -0
- package/src/geometry/solidNormals.ts +103 -0
- package/src/geometry/solidRound.ts +443 -0
- package/src/geometry/solidWriter.ts +58 -0
- package/src/index.ts +51 -1
- package/src/render/backend/api.ts +21 -2
- package/src/render/backend/drawFog.ts +31 -0
- package/src/render/backend/materialChanges.ts +9 -2
- package/src/render/backend/webgl2/instanced.ts +80 -14
- package/src/render/backend/webgl2/renderer.ts +300 -143
- package/src/render/backend/webgpu/buffers.ts +42 -3
- package/src/render/backend/webgpu/depthPass.ts +5 -1
- package/src/render/backend/webgpu/drawCommand.ts +6 -0
- package/src/render/backend/webgpu/flatPass.ts +41 -11
- package/src/render/backend/webgpu/glassTintPass.ts +2 -1
- package/src/render/backend/webgpu/instanceCullPass.ts +165 -0
- package/src/render/backend/webgpu/instanced.ts +21 -3
- package/src/render/backend/webgpu/latePass.ts +12 -3
- package/src/render/backend/webgpu/renderer.ts +540 -226
- package/src/render/backend/webgpu/sdfTextPass.ts +6 -1
- package/src/render/backend/webgpu/shaders/instanceCull.wgsl.ts +61 -0
- package/src/render/backend/webgpu/surfaceTexturePass.ts +90 -50
- package/src/render/driftLight/bake.ts +85 -62
- package/src/render/driftLight/denseField.ts +149 -0
- package/src/render/driftLight/lightField.ts +33 -18
- package/src/render/driftLight/presence.ts +69 -0
- package/src/render/driftLight/uniforms.ts +8 -9
- package/src/render/driftLight/worldLightField.ts +83 -0
- package/src/render/fixtureAtlas.ts +63 -0
- package/src/render/instanceCull.ts +205 -0
- package/src/render/instances.ts +16 -0
- package/src/render/lightBudget.ts +25 -36
- package/src/render/lightGrid.ts +130 -0
- package/src/render/mesh.ts +59 -72
- package/src/render/occlusion.ts +51 -7
- package/src/render/pointLightSelection.ts +72 -4
- package/src/render/rainField.ts +39 -5
- package/src/render/recon/lateDepth.ts +83 -0
- package/src/render/recon/resolve.ts +206 -20
- package/src/render/rendererHarness.ts +4 -0
- package/src/render/sceneTarget.ts +4 -0
- package/src/render/shaders/depth.ts +12 -9
- package/src/render/shaders/flat/driftLight.ts +32 -7
- package/src/render/shaders/flat/index.ts +5 -3
- package/src/render/shaders/flat/main.ts +75 -23
- package/src/render/shaders/flat/preamble.ts +75 -20
- package/src/render/shaders/flat/surfaceEffects.ts +152 -0
- package/src/render/shaders/generated/depth.wgsl.ts +8 -8
- package/src/render/shaders/generated/flat.wgsl.ts +1361 -1345
- package/src/render/shaders/generated/lightVolume.wgsl.ts +20 -4
- package/src/render/shaders/generated/rush.wgsl.ts +21 -16
- package/src/render/shaders/generated/sdfText.wgsl.ts +2 -2
- package/src/render/shaders/generated/text.wgsl.ts +1 -1
- package/src/render/shaders/gi/driftLight.wgsl.ts +27 -4
- package/src/render/shaders/recon/lateDepth.wgsl.ts +38 -13
- package/src/render/shaders/recon/resolve.wgsl.ts +161 -27
- package/src/render/shaders/rush.ts +11 -1
- package/src/render/shaders/sdfText.ts +3 -2
- package/src/render/shaders/text.ts +18 -9
- package/src/render/surfaceEffects.ts +132 -0
- package/src/render/surfaceScene.ts +16 -0
- package/src/render/surfaceTexture.ts +110 -19
- package/src/render/temporalAa.ts +78 -14
- package/src/render/textureSource.ts +72 -0
- package/src/render/vertexDefaults.ts +2 -0
- package/src/world/hlod.ts +290 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { FrameBudget } from '../budget.js';
|
|
2
2
|
import { MaterialChanges, ownsMaterial } from '../materialChanges.js';
|
|
3
|
+
import { FOG_RECEDE, fogModeOf } from '../drawFog.js';
|
|
3
4
|
import { mat4 } from 'gl-matrix';
|
|
4
5
|
import { GL_DEPTH_REMAP, MAX_DEPTH_LAYER, depthClearFor, depthOffsetForLayer, GL_SHADOW_REMAP, REVERSED_DEPTH, SHADOW_DEPTH_CLEAR, glDepthFuncEqual, glShadowDepthFunc, } from '../../depthConvention.js';
|
|
5
6
|
import { resolveGlass } from '../../glass.js';
|
|
@@ -16,6 +17,7 @@ import { OIT_MULTISAMPLE_REFUSAL } from '../../orderIndependent.js';
|
|
|
16
17
|
import { INDIRECT_LIGHT_WEBGL2_REFUSAL } from '../../gi/probeTrace.js';
|
|
17
18
|
import { TranslucentQueue } from '../../translucentQueue.js';
|
|
18
19
|
import { createFrustum, frustumFromViewProjection } from '../../../math/frustum.js';
|
|
20
|
+
import { batchBoxVisible, cullsInstances } from '../../instanceCull.js';
|
|
19
21
|
import { OcclusionBuffer } from '../../occlusion.js';
|
|
20
22
|
import { boundsVisible } from '../../visibility.js';
|
|
21
23
|
import { createPassRegistry, drainRegistry, passAt, registerIn, unregisterIn } from '../../pass.js';
|
|
@@ -32,6 +34,7 @@ import { FILM_LOOK_WITHOUT_COMPOSITE } from '../../shaders/filmLook.js';
|
|
|
32
34
|
import { bloomProfileWarning, bloomThresholdOf } from '../../bloomChain.js';
|
|
33
35
|
import { createEmptyTexture2D, createEmptyTexture2DArray, createEmptyTexture3D, createEmptyTextureCube, } from '../../emptyTexture.js';
|
|
34
36
|
import { LightField } from '../../driftLight/lightField.js';
|
|
37
|
+
import { WorldLightField } from '../../driftLight/worldLightField.js';
|
|
35
38
|
import { createDriftLightUniforms, resolveDriftLight } from '../../driftLight/uniforms.js';
|
|
36
39
|
import { flatFrag, flatVert } from '../../shaders/flat/index.js';
|
|
37
40
|
import { MORPH_DELTA_TEXTURE_UNIT, SKIN_PALETTE_TEXTURE_UNIT } from '../../lightBudget.js';
|
|
@@ -47,6 +50,8 @@ import { ProbeSweeps } from '../../probeSweeps.js';
|
|
|
47
50
|
import { octahedralEdgeFor } from '../../prefilterEnvMap.js';
|
|
48
51
|
import { EnvironmentPrefilterPass } from '../../prefilterPass.js';
|
|
49
52
|
import { packIesAtlas } from '../../iesProfile.js';
|
|
53
|
+
import { fixtureAtlasLayout, fixtureAtlasTexels } from '../../fixtureAtlas.js';
|
|
54
|
+
import { resolveSurfaceScene } from '../../surfaceScene.js';
|
|
50
55
|
import { MAX_AREA_LIGHTS } from '../../areaLights.js';
|
|
51
56
|
import { areaShadowLayerCount, castingRange, createResolvedAreaShadows, firstAreaShadowLayer, AreaShadowSet, } from '../../areaShadowSet.js';
|
|
52
57
|
import { equirectToCubeFaces } from '../../equirectToCube.js';
|
|
@@ -62,7 +67,7 @@ import { GlassTintPrograms } from './glassTintPrograms.js';
|
|
|
62
67
|
import { FACE_COUNT, PointShadowMap } from '../../pointShadowMap.js';
|
|
63
68
|
import { createResolvedPointShadows, DEFAULT_SOURCE_RADIUS } from '../../pointShadowImage.js';
|
|
64
69
|
import { createBakeScratch, runPointShadowBakes, selectCastingLights, } from '../../pointShadowBudget.js';
|
|
65
|
-
import { DIRECTIONAL_SHADOW_UNITS, SUN_GLASS_TINT_TEXTURE_UNIT, POINT_GLASS_TINT_TEXTURE_UNIT, LIVE_POINT_SHADOW_MAPS, MAX_POINT_LIGHTS, NORMAL_TEXTURE_UNIT, ORM_TEXTURE_UNIT, POINT_SHADOW_POOL, POINT_SHADOW_UNITS, CLUSTER_TABLE_TEXTURE_UNIT, EMISSIVE_TEXTURE_UNIT, ENVIRONMENT_TEXTURE_UNIT, SURFACE_TEXTURE_UNIT, bindPointLights,
|
|
70
|
+
import { DIRECTIONAL_SHADOW_UNITS, SUN_GLASS_TINT_TEXTURE_UNIT, POINT_GLASS_TINT_TEXTURE_UNIT, LIVE_POINT_SHADOW_MAPS, MAX_POINT_LIGHTS, NORMAL_TEXTURE_UNIT, ORM_TEXTURE_UNIT, POINT_SHADOW_POOL, POINT_SHADOW_UNITS, CLUSTER_TABLE_TEXTURE_UNIT, EMISSIVE_TEXTURE_UNIT, ENVIRONMENT_TEXTURE_UNIT, SURFACE_TEXTURE_UNIT, bindPointLights, REFRACT_SCENE_TEXTURE_UNIT, DRIFT_LIGHT_ATLAS_TEXTURE_UNIT, DRIFT_LIGHT_INDEX_TEXTURE_UNIT, COOKIE_TILE, FIXTURE_ATLAS_TEXTURE_UNIT, SURFACE_EFFECTS_TEXTURE_UNIT, bindAreaLights, bindAreaShadows, openCones, } from '../../lightBudget.js';
|
|
66
71
|
import { resolveRenderQuality } from '../../renderQuality.js';
|
|
67
72
|
import { drawingBufferSize } from '../../drawingBuffer.js';
|
|
68
73
|
import { Mesh, createMeshIncremental } from '../../mesh.js';
|
|
@@ -236,6 +241,8 @@ const IDENTITY_MAT4 = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
|
|
|
236
241
|
export { MAX_DEPTH_LAYER } from '../../depthConvention.js';
|
|
237
242
|
/** The identity tint, handed back after any draw that asked for one. See `drawMesh`. */
|
|
238
243
|
const WHITE_TINT = new Float32Array([1, 1, 1]);
|
|
244
|
+
/** The lit stage's clip plane when there is no mirror: zero, which clips nothing. */
|
|
245
|
+
const NO_CLIP_PLANE = new Float32Array(4);
|
|
239
246
|
/** Handed to the shader when a batch has no press field. See `drawScatter`. */
|
|
240
247
|
/**
|
|
241
248
|
* What a contributed pass pre-multiplies its projection by on this backend: nothing.
|
|
@@ -546,6 +553,11 @@ export class WebGL2Renderer {
|
|
|
546
553
|
oitActive = false;
|
|
547
554
|
/** True while the queue is being replayed, so a replayed draw does not record itself again. */
|
|
548
555
|
oitReplaying = false;
|
|
556
|
+
/** `uSurfaceScene`, resolved once a pass. See `surfaceScene.ts`. */
|
|
557
|
+
surfaceScene = new Float32Array(4);
|
|
558
|
+
/** The two halves of `uWriteMode`, kept here because a `vec2` is written whole. */
|
|
559
|
+
oitWeight = 0;
|
|
560
|
+
ditherFade = 0;
|
|
549
561
|
/** Said once, not per frame, where the context cannot give the float target the sum needs. */
|
|
550
562
|
oitRefused = false;
|
|
551
563
|
/** Said once for the whole renderer. See `refuseOitMultisampled`. */
|
|
@@ -639,6 +651,8 @@ export class WebGL2Renderer {
|
|
|
639
651
|
taaDepthToViewZ = Float32Array.of(0, 0, 0, 1);
|
|
640
652
|
aoInvProjection = mat4.create();
|
|
641
653
|
aoProjScale = new Float32Array(2);
|
|
654
|
+
/** Where this frame's jitter put the occlusion, in the composite's uv. See `uAoOffset`. */
|
|
655
|
+
aoOffset = new Float32Array(2);
|
|
642
656
|
/**
|
|
643
657
|
* Whether this frame has already been resolved to the canvas.
|
|
644
658
|
*
|
|
@@ -730,12 +744,15 @@ export class WebGL2Renderer {
|
|
|
730
744
|
* world. See `prepareStaticPointShadows`.
|
|
731
745
|
*/
|
|
732
746
|
pointShadowArray = null;
|
|
733
|
-
/** The
|
|
734
|
-
|
|
735
|
-
/**
|
|
747
|
+
/** The fixture atlas — cookies over profiles — allocated on first use. See `fixtureAtlas.ts`. */
|
|
748
|
+
fixtureTexture = null;
|
|
749
|
+
/** The profiles as packed, kept so a new set of cookies can rebuild the atlas around them. */
|
|
750
|
+
iesAtlas = packIesAtlas([]);
|
|
751
|
+
/** The cookie images, kept so a new set of profiles can rebuild the atlas around them. */
|
|
752
|
+
cookieImages = [];
|
|
753
|
+
/** Rows of profile in the atlas. One until a consumer loads a profile, which is the row of ones. */
|
|
736
754
|
iesRows = 1;
|
|
737
|
-
|
|
738
|
-
/** Tiles in the cookie atlas. 0 is the white placeholder and the whole off path. */
|
|
755
|
+
/** Cookie tiles in the atlas. 0 is none, and the whole off path. */
|
|
739
756
|
cookieTiles = 0;
|
|
740
757
|
/** Horizontal planes each profile occupies. 1 unless something asymmetric was loaded. */
|
|
741
758
|
iesPlanes = 1;
|
|
@@ -1036,7 +1053,7 @@ export class WebGL2Renderer {
|
|
|
1036
1053
|
if (cutout !== null) {
|
|
1037
1054
|
gl.useProgram(this.depthInstancedCutoutProgram);
|
|
1038
1055
|
this.bindCutoutDepth(this.depthInstancedCutoutUniforms, cutout);
|
|
1039
|
-
gpuBatch.
|
|
1056
|
+
gpuBatch.draw(gl, count);
|
|
1040
1057
|
if (twoSided && this.depthPassCullsFaces)
|
|
1041
1058
|
gl.enable(gl.CULL_FACE);
|
|
1042
1059
|
gl.useProgram(this.depthProgram);
|
|
@@ -1049,7 +1066,7 @@ export class WebGL2Renderer {
|
|
|
1049
1066
|
gl.uniform1i(u['uPeelShadowLayer'] ?? null, this.activeDepthPeel ? 1 : 0);
|
|
1050
1067
|
if (this.activeDepthPeel)
|
|
1051
1068
|
gl.uniform1i(u['uPreviousShadowMap'] ?? null, 0);
|
|
1052
|
-
gpuBatch.
|
|
1069
|
+
gpuBatch.draw(gl, count);
|
|
1053
1070
|
if (twoSided && this.depthPassCullsFaces)
|
|
1054
1071
|
gl.enable(gl.CULL_FACE);
|
|
1055
1072
|
gl.useProgram(this.depthProgram);
|
|
@@ -1305,6 +1322,13 @@ export class WebGL2Renderer {
|
|
|
1305
1322
|
occluded(bounds, model) {
|
|
1306
1323
|
return this.occlusion?.occluded(bounds, model) ?? false;
|
|
1307
1324
|
}
|
|
1325
|
+
/**
|
|
1326
|
+
* Whether a world-space box is wholly behind the declared occluders — the tighter question for
|
|
1327
|
+
* something that is a box, such as a region. False with no occlusion buffer. See `occluded`.
|
|
1328
|
+
*/
|
|
1329
|
+
occludedBox(min, max) {
|
|
1330
|
+
return this.occlusion?.occludedBox(min, max) ?? false;
|
|
1331
|
+
}
|
|
1308
1332
|
/* -- Registered passes ---------------------------------------------------------------- */
|
|
1309
1333
|
passes = createPassRegistry();
|
|
1310
1334
|
/** Built once so invoking somebody else's pass allocates nothing. See `PassDefinition.draw`. */
|
|
@@ -1478,80 +1502,57 @@ export class WebGL2Renderer {
|
|
|
1478
1502
|
* `createSurfaceTexture` and the material maps take.
|
|
1479
1503
|
*/
|
|
1480
1504
|
setSpotCookies(images) {
|
|
1481
|
-
|
|
1482
|
-
this.
|
|
1483
|
-
if (this.cookieTexture === null)
|
|
1484
|
-
this.cookieTexture = gl.createTexture();
|
|
1485
|
-
gl.activeTexture(gl.TEXTURE0 + COOKIE_ATLAS_TEXTURE_UNIT);
|
|
1486
|
-
gl.bindTexture(gl.TEXTURE_2D, this.cookieTexture);
|
|
1487
|
-
if (images.length === 0) {
|
|
1488
|
-
/* White, not black: this multiplies a light's colour. */
|
|
1489
|
-
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([255, 255, 255, 255]));
|
|
1490
|
-
}
|
|
1491
|
-
else {
|
|
1492
|
-
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, COOKIE_TILE * images.length, COOKIE_TILE, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
|
1493
|
-
for (let tile = 0; tile < images.length; tile++) {
|
|
1494
|
-
const image = images[tile];
|
|
1495
|
-
if (image === undefined)
|
|
1496
|
-
continue;
|
|
1497
|
-
gl.texSubImage2D(gl.TEXTURE_2D, 0, tile * COOKIE_TILE, 0, gl.RGBA, gl.UNSIGNED_BYTE, image);
|
|
1498
|
-
}
|
|
1499
|
-
}
|
|
1500
|
-
/* Linear and clamped: a cookie is an image, and the shader's own inset is what keeps a tile
|
|
1501
|
-
from reaching its neighbour — clamping alone cannot, the seam being interior to the row. */
|
|
1502
|
-
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
|
1503
|
-
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
|
1504
|
-
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
1505
|
-
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
1506
|
-
gl.activeTexture(gl.TEXTURE0);
|
|
1505
|
+
this.cookieImages = images.slice();
|
|
1506
|
+
this.buildFixtureAtlas();
|
|
1507
1507
|
}
|
|
1508
1508
|
setIesProfiles(profiles) {
|
|
1509
|
+
this.iesAtlas = packIesAtlas(profiles);
|
|
1510
|
+
this.buildFixtureAtlas();
|
|
1511
|
+
}
|
|
1512
|
+
/**
|
|
1513
|
+
* The fixture atlas from the profiles and cookies in hand: the profiles' rows at the bottom, the
|
|
1514
|
+
* cookies' tiles on top, one `RGBA16F` texture. See `fixtureAtlas.ts` for the layout.
|
|
1515
|
+
*
|
|
1516
|
+
* **Half float, matching what the other backend must use**: a 32-bit float texture is not
|
|
1517
|
+
* filterable in core WebGPU, and both tables are curves and images that want linear filtering.
|
|
1518
|
+
* Linear and clamped: a profile is sampled between two angles, and a cookie's own inset is what
|
|
1519
|
+
* keeps a tile from its neighbour — clamping alone cannot, the seam being interior to the row.
|
|
1520
|
+
*/
|
|
1521
|
+
buildFixtureAtlas() {
|
|
1509
1522
|
const { gl } = this;
|
|
1510
|
-
const
|
|
1511
|
-
if (this.
|
|
1512
|
-
this.
|
|
1513
|
-
gl.activeTexture(gl.TEXTURE0 +
|
|
1514
|
-
gl.bindTexture(gl.TEXTURE_2D, this.
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1523
|
+
const layout = fixtureAtlasLayout(this.iesAtlas.height, this.cookieImages.length);
|
|
1524
|
+
if (this.fixtureTexture === null)
|
|
1525
|
+
this.fixtureTexture = gl.createTexture();
|
|
1526
|
+
gl.activeTexture(gl.TEXTURE0 + FIXTURE_ATLAS_TEXTURE_UNIT);
|
|
1527
|
+
gl.bindTexture(gl.TEXTURE_2D, this.fixtureTexture);
|
|
1528
|
+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA16F, layout.width, layout.height, 0, gl.RGBA, gl.FLOAT, fixtureAtlasTexels(layout, this.iesAtlas));
|
|
1529
|
+
for (let tile = 0; tile < this.cookieImages.length; tile++) {
|
|
1530
|
+
const image = this.cookieImages[tile];
|
|
1531
|
+
if (image === undefined)
|
|
1532
|
+
continue;
|
|
1533
|
+
gl.texSubImage2D(gl.TEXTURE_2D, 0, tile * COOKIE_TILE, 0, gl.RGBA, gl.HALF_FLOAT, image);
|
|
1534
|
+
}
|
|
1521
1535
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
|
1522
1536
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
|
1523
1537
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
1524
1538
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
* the other backend must use: a 32-bit float texture is not filterable in core WebGPU, and this
|
|
1529
|
-
* row is a curve that wants linear filtering. Keeping the two formats the same is the
|
|
1530
|
-
* 2026-08-13 rule rather than tidiness; the driver converts the float data on the way in.
|
|
1531
|
-
*/
|
|
1532
|
-
gl.texImage2D(gl.TEXTURE_2D, 0, gl.R16F, atlas.width, atlas.height, 0, gl.RED, gl.FLOAT, atlas.data);
|
|
1533
|
-
this.iesRows = atlas.height;
|
|
1534
|
-
this.iesPlanes = atlas.planes;
|
|
1539
|
+
this.iesRows = this.iesAtlas.height;
|
|
1540
|
+
this.iesPlanes = this.iesAtlas.planes;
|
|
1541
|
+
this.cookieTiles = this.cookieImages.length;
|
|
1535
1542
|
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
1543
|
+
gl.activeTexture(gl.TEXTURE0);
|
|
1536
1544
|
}
|
|
1537
|
-
/** Bind the atlas and say
|
|
1545
|
+
/** Bind the fixture atlas and say what is in it. Always, for the reason `bindClusters` gives. */
|
|
1538
1546
|
bindIesAtlas(u) {
|
|
1539
1547
|
const { gl } = this;
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
gl.
|
|
1544
|
-
gl.
|
|
1548
|
+
/* Built on demand rather than at construction, for the reason the grade's placeholder is. */
|
|
1549
|
+
if (this.fixtureTexture === null)
|
|
1550
|
+
this.buildFixtureAtlas();
|
|
1551
|
+
gl.activeTexture(gl.TEXTURE0 + FIXTURE_ATLAS_TEXTURE_UNIT);
|
|
1552
|
+
gl.bindTexture(gl.TEXTURE_2D, this.fixtureTexture);
|
|
1553
|
+
gl.uniform1i(u['uFixtureAtlas'] ?? null, FIXTURE_ATLAS_TEXTURE_UNIT);
|
|
1545
1554
|
gl.uniform1f(u['uIesAtlasRows'] ?? null, this.iesRows);
|
|
1546
1555
|
gl.uniform1f(u['uIesPlaneCount'] ?? null, this.iesPlanes);
|
|
1547
|
-
/* Built on demand rather than at construction, for the reason the grade's placeholder is: a
|
|
1548
|
-
scene that never projects a cookie should not carry a texture, and a declared sampler still
|
|
1549
|
-
needs a complete one. */
|
|
1550
|
-
if (this.cookieTexture === null)
|
|
1551
|
-
this.setSpotCookies([]);
|
|
1552
|
-
gl.activeTexture(gl.TEXTURE0 + COOKIE_ATLAS_TEXTURE_UNIT);
|
|
1553
|
-
gl.bindTexture(gl.TEXTURE_2D, this.cookieTexture);
|
|
1554
|
-
gl.uniform1i(u['uCookieAtlas'] ?? null, COOKIE_ATLAS_TEXTURE_UNIT);
|
|
1555
1556
|
gl.uniform1f(u['uCookieTiles'] ?? null, this.cookieTiles);
|
|
1556
1557
|
gl.activeTexture(gl.TEXTURE0);
|
|
1557
1558
|
}
|
|
@@ -2180,10 +2181,8 @@ export class WebGL2Renderer {
|
|
|
2180
2181
|
gl.deleteVertexArray(this.skyVao);
|
|
2181
2182
|
if (this.panelBuffer !== null)
|
|
2182
2183
|
gl.deleteBuffer(this.panelBuffer);
|
|
2183
|
-
if (this.
|
|
2184
|
-
gl.deleteTexture(this.
|
|
2185
|
-
if (this.iesTexture !== null)
|
|
2186
|
-
gl.deleteTexture(this.iesTexture);
|
|
2184
|
+
if (this.fixtureTexture !== null)
|
|
2185
|
+
gl.deleteTexture(this.fixtureTexture);
|
|
2187
2186
|
if (this.clusterTexture !== null)
|
|
2188
2187
|
gl.deleteTexture(this.clusterTexture);
|
|
2189
2188
|
}
|
|
@@ -2331,6 +2330,15 @@ export class WebGL2Renderer {
|
|
|
2331
2330
|
createSurfaceTexture(source, options = {}) {
|
|
2332
2331
|
return new SurfaceTexture(this.gl, source, options);
|
|
2333
2332
|
}
|
|
2333
|
+
/**
|
|
2334
|
+
* Upload several images of one size as the layers of one array, for a merged mesh that names a
|
|
2335
|
+
* layer per vertex (`MeshData.layers`). One material binding then covers every image, which is
|
|
2336
|
+
* what makes a block wearing forty facades one draw. Every layer must be the same size, and the
|
|
2337
|
+
* refusal names the first that is not.
|
|
2338
|
+
*/
|
|
2339
|
+
createSurfaceTextureArray(sources, options = {}) {
|
|
2340
|
+
return new SurfaceTexture(this.gl, sources, options);
|
|
2341
|
+
}
|
|
2334
2342
|
/**
|
|
2335
2343
|
* Replace a surface texture's pixels, keeping the GPU object and its sampler state.
|
|
2336
2344
|
*
|
|
@@ -2444,7 +2452,7 @@ export class WebGL2Renderer {
|
|
|
2444
2452
|
* page-faulted an RDNA4 card and wedged the device. See `emptyTexture.ts`.
|
|
2445
2453
|
*/
|
|
2446
2454
|
gl.activeTexture(gl.TEXTURE0 + NORMAL_TEXTURE_UNIT);
|
|
2447
|
-
gl.bindTexture(gl.
|
|
2455
|
+
gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.emptyTexture2DArray);
|
|
2448
2456
|
if (normal !== null)
|
|
2449
2457
|
normal.bind(gl, NORMAL_TEXTURE_UNIT);
|
|
2450
2458
|
gl.uniform1i(u['uNormalMap'] ?? null, NORMAL_TEXTURE_UNIT);
|
|
@@ -2462,7 +2470,7 @@ export class WebGL2Renderer {
|
|
|
2462
2470
|
*/
|
|
2463
2471
|
const orm = material?.orm ?? null;
|
|
2464
2472
|
gl.activeTexture(gl.TEXTURE0 + ORM_TEXTURE_UNIT);
|
|
2465
|
-
gl.bindTexture(gl.
|
|
2473
|
+
gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.emptyTexture2DArray);
|
|
2466
2474
|
if (orm !== null)
|
|
2467
2475
|
orm.bind(gl, ORM_TEXTURE_UNIT);
|
|
2468
2476
|
gl.uniform1i(u['uOrmMap'] ?? null, ORM_TEXTURE_UNIT);
|
|
@@ -2480,7 +2488,7 @@ export class WebGL2Renderer {
|
|
|
2480
2488
|
*/
|
|
2481
2489
|
const emissiveMap = material?.emissive ?? null;
|
|
2482
2490
|
gl.activeTexture(gl.TEXTURE0 + EMISSIVE_TEXTURE_UNIT);
|
|
2483
|
-
gl.bindTexture(gl.
|
|
2491
|
+
gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.emptyTexture2DArray);
|
|
2484
2492
|
if (emissiveMap !== null)
|
|
2485
2493
|
emissiveMap.bind(gl, EMISSIVE_TEXTURE_UNIT);
|
|
2486
2494
|
gl.uniform1i(u['uEmissiveMap'] ?? null, EMISSIVE_TEXTURE_UNIT);
|
|
@@ -2496,7 +2504,21 @@ export class WebGL2Renderer {
|
|
|
2496
2504
|
*/
|
|
2497
2505
|
gl.uniform2f(u['uUvScale'] ?? null, material?.uScale ?? 1, material?.vScale ?? 1);
|
|
2498
2506
|
gl.uniform1i(u['uDoubleSided'] ?? null, material?.doubleSided === true ? 1 : 0);
|
|
2507
|
+
/* The albedo's effects table, or the stand-in the shader reads as none. Before the early
|
|
2508
|
+
return, so a material with no albedo still leaves a complete texture on the unit. */
|
|
2499
2509
|
if (albedo === null) {
|
|
2510
|
+
gl.activeTexture(gl.TEXTURE0 + SURFACE_EFFECTS_TEXTURE_UNIT);
|
|
2511
|
+
gl.bindTexture(gl.TEXTURE_2D, this.emptyTexture2D);
|
|
2512
|
+
}
|
|
2513
|
+
else {
|
|
2514
|
+
albedo.bindEffects(gl, SURFACE_EFFECTS_TEXTURE_UNIT, this.emptyTexture2D);
|
|
2515
|
+
}
|
|
2516
|
+
gl.uniform1i(u['uSurfaceEffects'] ?? null, SURFACE_EFFECTS_TEXTURE_UNIT);
|
|
2517
|
+
if (albedo === null) {
|
|
2518
|
+
/* The array stand-in, for the reason the maps above take one: a sampler reads its unit's
|
|
2519
|
+
array target, and with nothing there it is incomplete. */
|
|
2520
|
+
gl.activeTexture(gl.TEXTURE0 + SURFACE_TEXTURE_UNIT);
|
|
2521
|
+
gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.emptyTexture2DArray);
|
|
2500
2522
|
gl.uniform1i(u['uAlbedoEnabled'] ?? null, 0);
|
|
2501
2523
|
return;
|
|
2502
2524
|
}
|
|
@@ -2531,7 +2553,7 @@ export class WebGL2Renderer {
|
|
|
2531
2553
|
return;
|
|
2532
2554
|
}
|
|
2533
2555
|
gl.activeTexture(gl.TEXTURE0 + SURFACE_TEXTURE_UNIT);
|
|
2534
|
-
gl.bindTexture(gl.
|
|
2556
|
+
gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.emptyTexture2DArray);
|
|
2535
2557
|
}
|
|
2536
2558
|
/**
|
|
2537
2559
|
* A pass-level scale over the grain the geometry itself declared, 0 to 1.
|
|
@@ -2559,8 +2581,7 @@ export class WebGL2Renderer {
|
|
|
2559
2581
|
if (this.contextLost)
|
|
2560
2582
|
return;
|
|
2561
2583
|
this.materials.dirty();
|
|
2562
|
-
this.
|
|
2563
|
-
this.gl.uniform1f(this.flatUniforms['uReflectivity'] ?? null, Math.min(1, Math.max(0, amount)));
|
|
2584
|
+
this.writeFlatFloat('uReflectivity', Math.min(1, Math.max(0, amount)));
|
|
2564
2585
|
}
|
|
2565
2586
|
/**
|
|
2566
2587
|
* How bright the environment the surfaces drawn next reflect is. 1 is the default and identity.
|
|
@@ -2578,8 +2599,7 @@ export class WebGL2Renderer {
|
|
|
2578
2599
|
if (this.contextLost)
|
|
2579
2600
|
return;
|
|
2580
2601
|
this.materials.dirty();
|
|
2581
|
-
this.
|
|
2582
|
-
this.gl.uniform1f(this.flatUniforms['uEnvironmentGain'] ?? null, Math.max(0, gain));
|
|
2602
|
+
this.writeFlatFloat('uEnvironmentGain', Math.max(0, gain));
|
|
2583
2603
|
}
|
|
2584
2604
|
/**
|
|
2585
2605
|
* Kept as a no-op that says so once, because it was a stand-in for a term that now exists.
|
|
@@ -2606,8 +2626,51 @@ export class WebGL2Renderer {
|
|
|
2606
2626
|
if (this.contextLost)
|
|
2607
2627
|
return;
|
|
2608
2628
|
this.materials.dirty();
|
|
2629
|
+
this.writeFlatFloat('uGrain', Math.min(1, Math.max(0, amount)));
|
|
2630
|
+
}
|
|
2631
|
+
/**
|
|
2632
|
+
* One float of material state, into every flat program.
|
|
2633
|
+
*
|
|
2634
|
+
* **Every one, for `setMaterial`'s reason**: an instanced or skinned draw runs a program of its
|
|
2635
|
+
* own and is entitled to the material its caller set before it. These setters wrote the plain
|
|
2636
|
+
* program alone until 2026-09-30, so on this backend a batch drew with the pass defaults — grain
|
|
2637
|
+
* full, no reflection, no relief — while the other backend, whose material block every variant
|
|
2638
|
+
* reads, drew what was asked.
|
|
2639
|
+
*/
|
|
2640
|
+
writeFlatFloat(name, value) {
|
|
2641
|
+
for (const target of this.flatTargets) {
|
|
2642
|
+
this.gl.useProgram(target.program);
|
|
2643
|
+
this.gl.uniform1f(target.uniforms[name] ?? null, value);
|
|
2644
|
+
}
|
|
2645
|
+
this.useFlatProgram();
|
|
2646
|
+
}
|
|
2647
|
+
/**
|
|
2648
|
+
* How much of the surfaces drawn next survives a screen-door dither, for crossfading two levels
|
|
2649
|
+
* of detail of one thing.
|
|
2650
|
+
*
|
|
2651
|
+
* 0 is off and is what `bindMeshPass` restores. A positive amount keeps that share of the
|
|
2652
|
+
* pattern's cells, a negative one keeps the complement, so the level fading in drawn at `t` and
|
|
2653
|
+
* the one fading out at `-t` cover every pixel exactly once — no hole, no pixel drawn twice.
|
|
2654
|
+
* Material state like `setSurfaceGrain`; instanced draws read it too, and shadow casters, which
|
|
2655
|
+
* are a pass of their own, do not. See `uDither` in `flat/preamble.ts` for what it costs.
|
|
2656
|
+
*/
|
|
2657
|
+
setDitherFade(amount) {
|
|
2658
|
+
if (this.contextLost)
|
|
2659
|
+
return;
|
|
2660
|
+
this.materials.dirty();
|
|
2661
|
+
this.useFlatProgram();
|
|
2662
|
+
this.ditherFade = Math.min(1, Math.max(-1, amount));
|
|
2663
|
+
/* Every flat program, as `setMaterial` writes them: an instanced or skinned draw is entitled
|
|
2664
|
+
to the fade the caller set before it, and a second program holding none draws whole. */
|
|
2665
|
+
for (const target of this.flatTargets) {
|
|
2666
|
+
this.gl.useProgram(target.program);
|
|
2667
|
+
this.writeMode(target.uniforms);
|
|
2668
|
+
}
|
|
2609
2669
|
this.useFlatProgram();
|
|
2610
|
-
|
|
2670
|
+
}
|
|
2671
|
+
/** `uWriteMode`: the order-independent weight and the dither, which share one uniform row. */
|
|
2672
|
+
writeMode(u) {
|
|
2673
|
+
this.gl.uniform2f(u['uWriteMode'] ?? null, this.oitWeight, this.ditherFade);
|
|
2611
2674
|
}
|
|
2612
2675
|
/**
|
|
2613
2676
|
* How strong the microscopic relief on the surfaces drawn next is, and how coarse.
|
|
@@ -2636,10 +2699,8 @@ export class WebGL2Renderer {
|
|
|
2636
2699
|
if (this.contextLost)
|
|
2637
2700
|
return;
|
|
2638
2701
|
this.materials.dirty();
|
|
2639
|
-
this.
|
|
2640
|
-
|
|
2641
|
-
this.gl.uniform1f(u['uRelief'] ?? null, Math.min(1, Math.max(0, amount)));
|
|
2642
|
-
this.gl.uniform1f(u['uReliefCycles'] ?? null, Math.max(0.01, cyclesPerMetre));
|
|
2702
|
+
this.writeFlatFloat('uRelief', Math.min(1, Math.max(0, amount)));
|
|
2703
|
+
this.writeFlatFloat('uReliefCycles', Math.max(0.01, cyclesPerMetre));
|
|
2643
2704
|
}
|
|
2644
2705
|
/**
|
|
2645
2706
|
* How hard the bound surface texture's own luminance turns the shading normal, 0 for not at all.
|
|
@@ -2670,8 +2731,7 @@ export class WebGL2Renderer {
|
|
|
2670
2731
|
if (this.contextLost)
|
|
2671
2732
|
return;
|
|
2672
2733
|
this.materials.dirty();
|
|
2673
|
-
this.
|
|
2674
|
-
this.gl.uniform1f(this.flatUniforms['uTextureRelief'] ?? null, Number.isFinite(scale) ? scale : 0);
|
|
2734
|
+
this.writeFlatFloat('uTextureRelief', Number.isFinite(scale) ? scale : 0);
|
|
2675
2735
|
}
|
|
2676
2736
|
/**
|
|
2677
2737
|
* Release a mesh's GPU buffers.
|
|
@@ -2698,8 +2758,7 @@ export class WebGL2Renderer {
|
|
|
2698
2758
|
if (this.contextLost)
|
|
2699
2759
|
return;
|
|
2700
2760
|
this.materials.dirty();
|
|
2701
|
-
this.
|
|
2702
|
-
this.gl.uniform1f(this.flatUniforms['uEmissiveGain'] ?? null, gain);
|
|
2761
|
+
this.writeFlatFloat('uEmissiveGain', gain);
|
|
2703
2762
|
}
|
|
2704
2763
|
/* -- Instanced meshes --------------------------------------------------------------- */
|
|
2705
2764
|
/** The instanced program, compiled on first use. See `ensureInstancedProgram`. */
|
|
@@ -2738,7 +2797,7 @@ export class WebGL2Renderer {
|
|
|
2738
2797
|
* One batch per mesh: the attributes bind to the mesh's own vertex array, of which a mesh has
|
|
2739
2798
|
* one. `Mesh.attachInstances` is what refuses a second, and says why.
|
|
2740
2799
|
*/
|
|
2741
|
-
createInstanced(mesh, capacity) {
|
|
2800
|
+
createInstanced(mesh, capacity, options = {}) {
|
|
2742
2801
|
/* The same refusal as the other backend, for the same reason: the instanced attributes take
|
|
2743
2802
|
the two locations a skinned mesh's joints and weights already occupy. */
|
|
2744
2803
|
if (mesh.isSkinned) {
|
|
@@ -2746,7 +2805,7 @@ export class WebGL2Renderer {
|
|
|
2746
2805
|
'locations the joint indices and weights occupy, and this mesh supplies them.');
|
|
2747
2806
|
}
|
|
2748
2807
|
this.ensureInstancedProgram();
|
|
2749
|
-
return new InstancedBatch(this.gl, mesh, capacity);
|
|
2808
|
+
return new InstancedBatch(this.gl, mesh, capacity, options.cull ?? false);
|
|
2750
2809
|
}
|
|
2751
2810
|
/** Push placement and colour. Only the live prefix. */
|
|
2752
2811
|
uploadInstanced(batch, data) {
|
|
@@ -2790,6 +2849,12 @@ export class WebGL2Renderer {
|
|
|
2790
2849
|
const count = Math.min(data.count, batch.capacity);
|
|
2791
2850
|
if (count === 0)
|
|
2792
2851
|
return;
|
|
2852
|
+
/* A culling batch's camera draw: the whole batch against the view and the occluders first,
|
|
2853
|
+
then instance by instance where it survives. Blended draws keep every instance and their
|
|
2854
|
+
order. See `InstancedOptions.cull`. */
|
|
2855
|
+
const culling = !blend && batch.cull;
|
|
2856
|
+
if (culling && !batchBoxVisible(batch.box, this.frustum, this.occlusion))
|
|
2857
|
+
return;
|
|
2793
2858
|
this.drawBudget.ask();
|
|
2794
2859
|
this.ensureInstancedProgram();
|
|
2795
2860
|
const program = this.flatInstancedProgram;
|
|
@@ -2800,7 +2865,8 @@ export class WebGL2Renderer {
|
|
|
2800
2865
|
gl.useProgram(program);
|
|
2801
2866
|
gl.uniform1i(u['uHasTangents'] ?? null, mesh.hasTangents ? 1 : 0);
|
|
2802
2867
|
const lit = options.lit ?? true;
|
|
2803
|
-
|
|
2868
|
+
/* Added light fades in the medium rather than receding into it. See `drawFog.ts`. */
|
|
2869
|
+
const fog = fogModeOf(options.fog ?? true, blend && options.additive === true);
|
|
2804
2870
|
const toneMapped = options.toneMapped ?? true;
|
|
2805
2871
|
/* A blended batch shows what is behind it — refraction or glass — as a single draw does, and
|
|
2806
2872
|
counts a material of its own for it by the rule both backends share (`materialChanges.ts`). */
|
|
@@ -2831,13 +2897,16 @@ export class WebGL2Renderer {
|
|
|
2831
2897
|
*/
|
|
2832
2898
|
if (!lit)
|
|
2833
2899
|
gl.uniform1i(u['uLightingEnabled'] ?? null, 0);
|
|
2834
|
-
if (
|
|
2835
|
-
gl.uniform1i(u['uFogEnabled'] ?? null,
|
|
2900
|
+
if (fog !== FOG_RECEDE)
|
|
2901
|
+
gl.uniform1i(u['uFogEnabled'] ?? null, fog);
|
|
2836
2902
|
if (!toneMapped)
|
|
2837
2903
|
gl.uniform1i(u['uOutputTransform'] ?? null, Math.min(this.gradeCode(), 1));
|
|
2838
2904
|
if (blend) {
|
|
2839
2905
|
gl.enable(gl.BLEND);
|
|
2840
|
-
|
|
2906
|
+
if (options.additive === true)
|
|
2907
|
+
gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE, gl.ZERO, gl.ONE);
|
|
2908
|
+
else
|
|
2909
|
+
gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
|
|
2841
2910
|
if (!depthWrite)
|
|
2842
2911
|
gl.depthMask(false);
|
|
2843
2912
|
}
|
|
@@ -2852,7 +2921,10 @@ export class WebGL2Renderer {
|
|
|
2852
2921
|
for both: without it a batch's far faces were culled here alone. */
|
|
2853
2922
|
if (this.materialDoubleSided)
|
|
2854
2923
|
gl.disable(gl.CULL_FACE);
|
|
2855
|
-
|
|
2924
|
+
if (culling && cullsInstances(count, mesh.indexCount))
|
|
2925
|
+
batch.drawCulled(gl, data, this.frustum);
|
|
2926
|
+
else
|
|
2927
|
+
batch.draw(gl, count);
|
|
2856
2928
|
if (this.materialDoubleSided)
|
|
2857
2929
|
gl.enable(gl.CULL_FACE);
|
|
2858
2930
|
if (layer > 0) {
|
|
@@ -2874,8 +2946,8 @@ export class WebGL2Renderer {
|
|
|
2874
2946
|
this.unbindSeeThrough(u);
|
|
2875
2947
|
if (!lit)
|
|
2876
2948
|
gl.uniform1i(u['uLightingEnabled'] ?? null, 1);
|
|
2877
|
-
if (
|
|
2878
|
-
gl.uniform1i(u['uFogEnabled'] ?? null,
|
|
2949
|
+
if (fog !== FOG_RECEDE)
|
|
2950
|
+
gl.uniform1i(u['uFogEnabled'] ?? null, FOG_RECEDE);
|
|
2879
2951
|
if (!toneMapped)
|
|
2880
2952
|
gl.uniform1i(u['uOutputTransform'] ?? null, this.gradeCode());
|
|
2881
2953
|
if (own)
|
|
@@ -3997,7 +4069,7 @@ export class WebGL2Renderer {
|
|
|
3997
4069
|
const cutout = cutoutOf(material);
|
|
3998
4070
|
this.bindGlassTint(cutout !== null ? programs.instancedCutout : programs.instanced, glass, cutout);
|
|
3999
4071
|
gl.disable(gl.CULL_FACE);
|
|
4000
|
-
gpuBatch.
|
|
4072
|
+
gpuBatch.draw(gl, count);
|
|
4001
4073
|
if (this.depthPassCullsFaces)
|
|
4002
4074
|
gl.enable(gl.CULL_FACE);
|
|
4003
4075
|
if (cutout !== null)
|
|
@@ -5247,11 +5319,20 @@ export class WebGL2Renderer {
|
|
|
5247
5319
|
metres into a radius in UV. */
|
|
5248
5320
|
this.aoProjScale[0] = projection[0] ?? 1;
|
|
5249
5321
|
this.aoProjScale[1] = projection[5] ?? 1;
|
|
5322
|
+
/* The occlusion read where this frame's jitter put it (`uAoOffset`): a texel stands at its
|
|
5323
|
+
index plus a half less the jitter, in the drawing buffer the jitter was spread over. */
|
|
5324
|
+
this.aoOffset[0] = this.temporalJittering
|
|
5325
|
+
? this.temporalJitterX / Math.max(1, this.gl.drawingBufferWidth)
|
|
5326
|
+
: 0;
|
|
5327
|
+
this.aoOffset[1] = this.temporalJittering
|
|
5328
|
+
? this.temporalJitterY / Math.max(1, this.gl.drawingBufferHeight)
|
|
5329
|
+
: 0;
|
|
5250
5330
|
ao = {
|
|
5251
5331
|
strength: aoStrength,
|
|
5252
5332
|
radius: this.quality.ambientOcclusionRadius,
|
|
5253
5333
|
projScale: this.aoProjScale,
|
|
5254
5334
|
invProjection: this.aoInvProjection,
|
|
5335
|
+
offset: this.aoOffset,
|
|
5255
5336
|
};
|
|
5256
5337
|
}
|
|
5257
5338
|
/*
|
|
@@ -5375,7 +5456,8 @@ export class WebGL2Renderer {
|
|
|
5375
5456
|
this.oit.accumulateOit(this.canvas.width, this.canvas.height, oitDepth, (weighted) => {
|
|
5376
5457
|
this.oitReplaying = true;
|
|
5377
5458
|
this.useFlatProgram();
|
|
5378
|
-
this.
|
|
5459
|
+
this.oitWeight = weighted ? 1 : 0;
|
|
5460
|
+
this.writeMode(flat);
|
|
5379
5461
|
this.translucentQueue.replay((draw) => {
|
|
5380
5462
|
options.lit = draw.lit;
|
|
5381
5463
|
options.fog = draw.fog;
|
|
@@ -5392,7 +5474,8 @@ export class WebGL2Renderer {
|
|
|
5392
5474
|
this.drawTranslucentMesh(draw.mesh, draw.model, draw.opacity, options);
|
|
5393
5475
|
});
|
|
5394
5476
|
/* Back to zero for every other draw in the frame, exactly as `uOpacity` and `uTint` are. */
|
|
5395
|
-
this.
|
|
5477
|
+
this.oitWeight = 0;
|
|
5478
|
+
this.writeMode(flat);
|
|
5396
5479
|
this.oitReplaying = false;
|
|
5397
5480
|
});
|
|
5398
5481
|
this.sceneTarget?.bind();
|
|
@@ -5551,7 +5634,15 @@ export class WebGL2Renderer {
|
|
|
5551
5634
|
* A reflection pass binds its own camera and is skipped: blurring toward a mirror's
|
|
5552
5635
|
* previous view is not what a viewer moved through.
|
|
5553
5636
|
*/
|
|
5554
|
-
|
|
5637
|
+
/*
|
|
5638
|
+
* **Not after the present**: a mesh pass bound then is the interface's, over a picture already
|
|
5639
|
+
* resolved. Deciding the frame again drew the interface jittered with nothing to resolve it,
|
|
5640
|
+
* and opened a second temporal frame, so the sequence stepped twice a frame, the world saw every
|
|
5641
|
+
* other position, and the anti-flicker's period never started.
|
|
5642
|
+
*/
|
|
5643
|
+
if (this.framePresented)
|
|
5644
|
+
this.temporalJittering = false;
|
|
5645
|
+
if (!this.reflectionPassActive && !this.probePassActive && !this.framePresented) {
|
|
5555
5646
|
this.frameViewProj = camera.viewProjection;
|
|
5556
5647
|
/* Copied rather than referenced: a caller moves its camera in place, and the decal pass
|
|
5557
5648
|
reads this at the end of the frame. */
|
|
@@ -5657,6 +5748,19 @@ export class WebGL2Renderer {
|
|
|
5657
5748
|
this.lightField = field;
|
|
5658
5749
|
return field;
|
|
5659
5750
|
}
|
|
5751
|
+
/**
|
|
5752
|
+
* Hand a world's DriftLight volume to the renderer: every fixed light of a city, summed offline by
|
|
5753
|
+
* `bakeDenseField`, standing in past the frame's exact choice. Replaces any field made before, of
|
|
5754
|
+
* either kind; the scene calls `field.follow(buffer.complete, ...)` each frame as it would for
|
|
5755
|
+
* `createLightField`, and marks the lights the volume summed `inLightField` so none is counted
|
|
5756
|
+
* twice. See `driftLight/worldLightField.ts`.
|
|
5757
|
+
*/
|
|
5758
|
+
createWorldLightField(volume, options = {}) {
|
|
5759
|
+
this.forgetLightFieldVolumes();
|
|
5760
|
+
const field = new WorldLightField(volume, options);
|
|
5761
|
+
this.lightField = field;
|
|
5762
|
+
return field;
|
|
5763
|
+
}
|
|
5660
5764
|
/** Let go of the field and its volumes; the scene's lights are all exact again. */
|
|
5661
5765
|
disposeLightField() {
|
|
5662
5766
|
this.forgetLightFieldVolumes();
|
|
@@ -5676,14 +5780,14 @@ export class WebGL2Renderer {
|
|
|
5676
5780
|
*/
|
|
5677
5781
|
uploadLightField() {
|
|
5678
5782
|
const field = this.lightField;
|
|
5679
|
-
if (field === null || this.driftAtlas !== null || !field.ready || field.
|
|
5783
|
+
if (field === null || this.driftAtlas !== null || !field.ready || field.empty) {
|
|
5680
5784
|
return;
|
|
5681
5785
|
}
|
|
5682
5786
|
const { gl } = this;
|
|
5683
|
-
const [ix, iy, iz] = field.
|
|
5787
|
+
const [ix, iy, iz] = field.indexDims;
|
|
5684
5788
|
const index = gl.createTexture();
|
|
5685
5789
|
gl.bindTexture(gl.TEXTURE_3D, index);
|
|
5686
|
-
gl.texImage3D(gl.TEXTURE_3D, 0, gl.R32UI, ix, iy, iz, 0, gl.RED_INTEGER, gl.UNSIGNED_INT, field.
|
|
5790
|
+
gl.texImage3D(gl.TEXTURE_3D, 0, gl.R32UI, ix, iy, iz, 0, gl.RED_INTEGER, gl.UNSIGNED_INT, field.index);
|
|
5687
5791
|
gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
|
5688
5792
|
gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
|
5689
5793
|
const [aw, ah, ad] = field.atlasSize;
|
|
@@ -5811,6 +5915,10 @@ export class WebGL2Renderer {
|
|
|
5811
5915
|
gl.uniform1i(u['uFogEnabled'] ?? null, 1);
|
|
5812
5916
|
/* Grain on by default, so every scene written before it was a choice looks unchanged. */
|
|
5813
5917
|
gl.uniform1f(u['uGrain'] ?? null, 1);
|
|
5918
|
+
/* No dither and no weighting: every draw whole, until a crossfade asks. See `setDitherFade`. */
|
|
5919
|
+
this.oitWeight = 0;
|
|
5920
|
+
this.ditherFade = 0;
|
|
5921
|
+
gl.uniform2f(u['uWriteMode'] ?? null, 0, 0);
|
|
5814
5922
|
/* No environment reflection unless a caller asks, so existing scenes are unchanged. */
|
|
5815
5923
|
gl.uniform1f(u['uReflectivity'] ?? null, 0);
|
|
5816
5924
|
/* 1, not 0: this one is a multiplier and its identity is one. See `setEnvironmentGain`. */
|
|
@@ -5820,11 +5928,11 @@ export class WebGL2Renderer {
|
|
|
5820
5928
|
gl.uniform1f(u['uReliefCycles'] ?? null, 60);
|
|
5821
5929
|
/* And no relief read off a surface texture either, for the same reason. */
|
|
5822
5930
|
gl.uniform1f(u['uTextureRelief'] ?? null, 0);
|
|
5823
|
-
|
|
5931
|
+
/* The mirror's plane in a reflection pass, and zero — which clips nothing — in every other. */
|
|
5824
5932
|
const reflection = this.planarReflection;
|
|
5825
|
-
|
|
5826
|
-
|
|
5827
|
-
|
|
5933
|
+
gl.uniform4fv(u['uClipPlane'] ?? null, this.reflectionPassActive && reflection !== null ? reflection.clipPlane : NO_CLIP_PLANE);
|
|
5934
|
+
resolveSurfaceScene(env, this.surfaceScene);
|
|
5935
|
+
gl.uniform4fv(u['uSurfaceScene'] ?? null, this.surfaceScene);
|
|
5828
5936
|
bindAtmosphere(gl, u, env, this.reflectionPassActive ? this.reflectionAtmosphereY : (camera.position[1] ?? 0), this.quality.underwaterAtmosphere);
|
|
5829
5937
|
gl.uniform1f(u['uEmissiveGain'] ?? null, env.emissiveGain);
|
|
5830
5938
|
gl.uniform1f(u['uNightFactor'] ?? null, env.nightFactor);
|
|
@@ -5981,9 +6089,13 @@ export class WebGL2Renderer {
|
|
|
5981
6089
|
}
|
|
5982
6090
|
this.bindDriftLight(u, camera.position);
|
|
5983
6091
|
gl.activeTexture(gl.TEXTURE0 + SURFACE_TEXTURE_UNIT);
|
|
5984
|
-
gl.bindTexture(gl.
|
|
6092
|
+
gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.emptyTexture2DArray);
|
|
5985
6093
|
gl.uniform1i(u['uAlbedo'] ?? null, SURFACE_TEXTURE_UNIT);
|
|
5986
6094
|
gl.uniform1i(u['uAlbedoEnabled'] ?? null, 0);
|
|
6095
|
+
/* No albedo, so no effects table: the stand-in, which the shader reads as none. */
|
|
6096
|
+
gl.activeTexture(gl.TEXTURE0 + SURFACE_EFFECTS_TEXTURE_UNIT);
|
|
6097
|
+
gl.bindTexture(gl.TEXTURE_2D, this.emptyTexture2D);
|
|
6098
|
+
gl.uniform1i(u['uSurfaceEffects'] ?? null, SURFACE_EFFECTS_TEXTURE_UNIT);
|
|
5987
6099
|
gl.uniform2f(u['uUvScale'] ?? null, 1, 1);
|
|
5988
6100
|
gl.uniform1f(u['uAlbedoCutout'] ?? null, 0);
|
|
5989
6101
|
gl.uniform1i(u['uDoubleSided'] ?? null, 0);
|
|
@@ -6007,18 +6119,18 @@ export class WebGL2Renderer {
|
|
|
6007
6119
|
* fetch a sampler's descriptor before it evaluates the branch that would have skipped the read.
|
|
6008
6120
|
*/
|
|
6009
6121
|
gl.activeTexture(gl.TEXTURE0 + NORMAL_TEXTURE_UNIT);
|
|
6010
|
-
gl.bindTexture(gl.
|
|
6122
|
+
gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.emptyTexture2DArray);
|
|
6011
6123
|
gl.uniform1i(u['uNormalMap'] ?? null, NORMAL_TEXTURE_UNIT);
|
|
6012
6124
|
gl.uniform1f(u['uNormalStrength'] ?? null, 0);
|
|
6013
6125
|
/* And the ORM map, for the reason directly above and the one above that. */
|
|
6014
6126
|
gl.activeTexture(gl.TEXTURE0 + ORM_TEXTURE_UNIT);
|
|
6015
|
-
gl.bindTexture(gl.
|
|
6127
|
+
gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.emptyTexture2DArray);
|
|
6016
6128
|
gl.uniform1i(u['uOrmMap'] ?? null, ORM_TEXTURE_UNIT);
|
|
6017
6129
|
gl.uniform1i(u['uOrmEnabled'] ?? null, 0);
|
|
6018
6130
|
gl.uniform3f(u['uOrmScale'] ?? null, 1, 1, 1);
|
|
6019
6131
|
/* And the emissive map. The scale goes back to one, which is the identity it multiplies by. */
|
|
6020
6132
|
gl.activeTexture(gl.TEXTURE0 + EMISSIVE_TEXTURE_UNIT);
|
|
6021
|
-
gl.bindTexture(gl.
|
|
6133
|
+
gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.emptyTexture2DArray);
|
|
6022
6134
|
gl.uniform1i(u['uEmissiveMap'] ?? null, EMISSIVE_TEXTURE_UNIT);
|
|
6023
6135
|
gl.uniform1i(u['uEmissiveMapEnabled'] ?? null, 0);
|
|
6024
6136
|
gl.uniform3f(u['uEmissiveScale'] ?? null, 1, 1, 1);
|
|
@@ -6304,7 +6416,7 @@ export class WebGL2Renderer {
|
|
|
6304
6416
|
* The two buffers are two blend states over the same geometry, so the set has to survive until
|
|
6305
6417
|
* the end of the pass. Everything is copied — see `TranslucentQueue`.
|
|
6306
6418
|
*/
|
|
6307
|
-
if (this.oitActive && !this.oitReplaying) {
|
|
6419
|
+
if (this.oitActive && !this.oitReplaying && options.additive !== true) {
|
|
6308
6420
|
this.translucentQueue.record(mesh, model, opacity, options);
|
|
6309
6421
|
return;
|
|
6310
6422
|
}
|
|
@@ -6322,12 +6434,13 @@ export class WebGL2Renderer {
|
|
|
6322
6434
|
* drew with, and only a caller that asks pays for the branch that skips them.
|
|
6323
6435
|
*/
|
|
6324
6436
|
const lit = options.lit ?? true;
|
|
6325
|
-
|
|
6437
|
+
/* Added light fades in the medium rather than receding into it. See `drawFog.ts`. */
|
|
6438
|
+
const fog = fogModeOf(options.fog ?? true, options.additive === true);
|
|
6326
6439
|
const toneMapped = options.toneMapped ?? true;
|
|
6327
6440
|
if (!lit)
|
|
6328
6441
|
gl.uniform1i(u['uLightingEnabled'] ?? null, 0);
|
|
6329
|
-
if (
|
|
6330
|
-
gl.uniform1i(u['uFogEnabled'] ?? null,
|
|
6442
|
+
if (fog !== FOG_RECEDE)
|
|
6443
|
+
gl.uniform1i(u['uFogEnabled'] ?? null, fog);
|
|
6331
6444
|
/* `1` is sRGB alone: the conversion without the curve, which is what dropping the tone map
|
|
6332
6445
|
means rather than dropping the whole transform. `Math.min` rather than a literal, so a
|
|
6333
6446
|
renderer asked for `none` stays at none and one asked for `srgb` is already there. */
|
|
@@ -6368,7 +6481,11 @@ export class WebGL2Renderer {
|
|
|
6368
6481
|
const ownsState = !this.oitReplaying;
|
|
6369
6482
|
if (ownsState) {
|
|
6370
6483
|
gl.enable(gl.BLEND);
|
|
6371
|
-
|
|
6484
|
+
/* Added, where asked: colour times alpha onto what is there, and the target's alpha kept. */
|
|
6485
|
+
if (options.additive === true)
|
|
6486
|
+
gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE, gl.ZERO, gl.ONE);
|
|
6487
|
+
else
|
|
6488
|
+
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
|
6372
6489
|
}
|
|
6373
6490
|
/*
|
|
6374
6491
|
* The two depth options, both scoped to this draw and handed straight back — the same
|
|
@@ -6408,8 +6525,8 @@ export class WebGL2Renderer {
|
|
|
6408
6525
|
cannot inherit a material from the draw before it. */
|
|
6409
6526
|
if (!lit)
|
|
6410
6527
|
gl.uniform1i(u['uLightingEnabled'] ?? null, 1);
|
|
6411
|
-
if (
|
|
6412
|
-
gl.uniform1i(u['uFogEnabled'] ?? null,
|
|
6528
|
+
if (fog !== FOG_RECEDE)
|
|
6529
|
+
gl.uniform1i(u['uFogEnabled'] ?? null, FOG_RECEDE);
|
|
6413
6530
|
if (!toneMapped)
|
|
6414
6531
|
gl.uniform1i(u['uOutputTransform'] ?? null, this.gradeCode());
|
|
6415
6532
|
if (own)
|