@driftengine/core 4.4.1 → 4.5.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 +72 -0
- package/README.md +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +7 -0
- package/dist/render/backend/api.d.ts +17 -11
- package/dist/render/backend/webgl2/glassTintPrograms.d.ts +13 -0
- package/dist/render/backend/webgl2/glassTintPrograms.js +38 -0
- package/dist/render/backend/webgl2/renderer.d.ts +82 -8
- package/dist/render/backend/webgl2/renderer.js +373 -105
- package/dist/render/backend/webgpu/buffers.d.ts +19 -1
- package/dist/render/backend/webgpu/buffers.js +27 -1
- package/dist/render/backend/webgpu/depthPass.d.ts +1 -1
- package/dist/render/backend/webgpu/depthPass.js +2 -1
- package/dist/render/backend/webgpu/flatPass.d.ts +2 -0
- package/dist/render/backend/webgpu/flatPass.js +20 -1
- package/dist/render/backend/webgpu/glassTintPass.d.ts +28 -0
- package/dist/render/backend/webgpu/glassTintPass.js +205 -0
- package/dist/render/backend/webgpu/gpuDrivenPass.d.ts +60 -1
- package/dist/render/backend/webgpu/gpuDrivenPass.js +305 -5
- package/dist/render/backend/webgpu/instanced.d.ts +12 -1
- package/dist/render/backend/webgpu/instanced.js +20 -1
- package/dist/render/backend/webgpu/latePass.d.ts +26 -0
- package/dist/render/backend/webgpu/latePass.js +133 -0
- package/dist/render/backend/webgpu/lightVolumePass.js +5 -1
- package/dist/render/backend/webgpu/motionPass.d.ts +95 -0
- package/dist/render/backend/webgpu/motionPass.js +491 -0
- package/dist/render/backend/webgpu/pipelineCache.d.ts +10 -1
- package/dist/render/backend/webgpu/pipelineCache.js +11 -1
- package/dist/render/backend/webgpu/pointGlassTintPass.d.ts +32 -0
- package/dist/render/backend/webgpu/pointGlassTintPass.js +193 -0
- package/dist/render/backend/webgpu/pointShadowPass.d.ts +35 -6
- package/dist/render/backend/webgpu/pointShadowPass.js +105 -20
- package/dist/render/backend/webgpu/postPass.d.ts +21 -15
- package/dist/render/backend/webgpu/postPass.js +17 -4
- package/dist/render/backend/webgpu/renderer.d.ts +181 -54
- package/dist/render/backend/webgpu/renderer.js +1011 -394
- package/dist/render/backend/webgpu/scatterPass.js +2 -1
- package/dist/render/backend/webgpu/skinPaletteRing.d.ts +4 -0
- package/dist/render/backend/webgpu/skinPaletteRing.js +4 -0
- package/dist/render/backend/webgpu/snapshotMips.d.ts +29 -0
- package/dist/render/backend/webgpu/snapshotMips.js +52 -0
- package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +5 -0
- package/dist/render/backend/webgpu/surfaceTexturePass.js +1 -1
- package/dist/render/flameLight.js +15 -2
- package/dist/render/glass.d.ts +45 -0
- package/dist/render/glass.js +39 -0
- package/dist/render/glassCasters.d.ts +40 -0
- package/dist/render/glassCasters.js +84 -0
- package/dist/render/glassShadow.d.ts +78 -0
- package/dist/render/glassShadow.js +125 -0
- package/dist/render/globalMediumPass.d.ts +2 -3
- package/dist/render/globalMediumPass.js +2 -8
- package/dist/render/gpudriven/lit.d.ts +12 -0
- package/dist/render/gpudriven/lit.js +23 -0
- package/dist/render/gpudriven/materialTable.d.ts +15 -5
- package/dist/render/gpudriven/materialTable.js +26 -6
- package/dist/render/gpudriven/pipeline.d.ts +1 -1
- package/dist/render/gpudriven/pipeline.js +4 -3
- package/dist/render/gpudriven/sunGlass.d.ts +35 -0
- package/dist/render/gpudriven/sunGlass.js +89 -0
- package/dist/render/lightBudget.d.ts +17 -5
- package/dist/render/lightBudget.js +17 -5
- package/dist/render/pointGlassTint.d.ts +59 -0
- package/dist/render/pointGlassTint.js +198 -0
- package/dist/render/pointShadowArray.d.ts +56 -3
- package/dist/render/pointShadowArray.js +127 -16
- package/dist/render/pointShadowBudget.js +51 -41
- package/dist/render/pointShadowMap.d.ts +23 -1
- package/dist/render/pointShadowMap.js +27 -1
- package/dist/render/recon/changeFrames.d.ts +15 -0
- package/dist/render/recon/changeFrames.js +21 -0
- package/dist/render/recon/disocclusion.d.ts +36 -3
- package/dist/render/recon/disocclusion.js +56 -4
- package/dist/render/recon/lateRouting.d.ts +14 -0
- package/dist/render/recon/lateRouting.js +18 -0
- package/dist/render/recon/mover.d.ts +57 -0
- package/dist/render/recon/mover.js +86 -0
- package/dist/render/recon/resolve.js +95 -12
- package/dist/render/renderQuality.d.ts +35 -0
- package/dist/render/renderQuality.js +7 -0
- package/dist/render/rendererHarness.js +12 -0
- package/dist/render/sceneTarget.d.ts +9 -1
- package/dist/render/sceneTarget.js +59 -13
- package/dist/render/shaders/depth.d.ts +16 -1
- package/dist/render/shaders/depth.js +85 -9
- package/dist/render/shaders/flat/directionalShadow.js +91 -6
- package/dist/render/shaders/flat/glassTint.d.ts +6 -0
- package/dist/render/shaders/flat/glassTint.js +25 -0
- package/dist/render/shaders/flat/index.d.ts +13 -0
- package/dist/render/shaders/flat/index.js +4 -0
- package/dist/render/shaders/flat/lampGlass.d.ts +1 -0
- package/dist/render/shaders/flat/lampGlass.js +183 -0
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +115 -15
- package/dist/render/shaders/flat/pointShadow.d.ts +1 -1
- package/dist/render/shaders/flat/pointShadow.js +2 -0
- package/dist/render/shaders/flat/preamble.js +21 -6
- package/dist/render/shaders/generated/depth.wgsl.d.ts +227 -4
- package/dist/render/shaders/generated/depth.wgsl.js +227 -4
- package/dist/render/shaders/generated/flat.wgsl.d.ts +360 -312
- package/dist/render/shaders/generated/flat.wgsl.js +376 -328
- package/dist/render/shaders/generated/globalMedium.wgsl.d.ts +3 -13
- package/dist/render/shaders/generated/globalMedium.wgsl.js +3 -13
- package/dist/render/shaders/generated/lightVolume.wgsl.d.ts +3 -13
- package/dist/render/shaders/generated/lightVolume.wgsl.js +4 -14
- package/dist/render/shaders/generated/octahedralResolve.wgsl.d.ts +29 -0
- package/dist/render/shaders/generated/octahedralResolve.wgsl.js +29 -0
- package/dist/render/shaders/generated/temporalResolve.wgsl.d.ts +31 -6
- package/dist/render/shaders/generated/temporalResolve.wgsl.js +31 -6
- package/dist/render/shaders/globalMedium.js +6 -6
- package/dist/render/shaders/gpudriven/blendRaster.wgsl.d.ts +7 -0
- package/dist/render/shaders/gpudriven/blendRaster.wgsl.js +86 -1
- package/dist/render/shaders/gpudriven/cull.wgsl.d.ts +1 -1
- package/dist/render/shaders/gpudriven/cull.wgsl.js +11 -2
- package/dist/render/shaders/gpudriven/materialTable.wgsl.d.ts +1 -1
- package/dist/render/shaders/gpudriven/materialTable.wgsl.js +3 -1
- package/dist/render/shaders/gpudriven/shade.wgsl.js +50 -1
- package/dist/render/shaders/gpudriven/sunGlass.wgsl.d.ts +9 -0
- package/dist/render/shaders/gpudriven/sunGlass.wgsl.js +133 -0
- package/dist/render/shaders/lightVolume.js +6 -6
- package/dist/render/shaders/octahedralResolve.d.ts +8 -0
- package/dist/render/shaders/octahedralResolve.js +48 -17
- package/dist/render/shaders/recon/lateDepth.wgsl.d.ts +1 -0
- package/dist/render/shaders/recon/lateDepth.wgsl.js +44 -0
- package/dist/render/shaders/recon/motion.wgsl.d.ts +1 -1
- package/dist/render/shaders/recon/motion.wgsl.js +84 -0
- package/dist/render/shaders/recon/resolve.wgsl.d.ts +1 -1
- package/dist/render/shaders/recon/resolve.wgsl.js +112 -8
- package/dist/render/shaders/temporalResolve.js +133 -5
- package/dist/render/shadowCasters.d.ts +20 -1
- package/dist/render/shadowCasters.js +15 -1
- package/dist/render/shadowMap.d.ts +54 -5
- package/dist/render/shadowMap.js +190 -36
- package/dist/render/sunGlassTint.d.ts +25 -0
- package/dist/render/sunGlassTint.js +79 -0
- package/dist/render/temporalAa.d.ts +145 -0
- package/dist/render/temporalAa.js +190 -0
- package/dist/render/temporalPass.d.ts +16 -14
- package/dist/render/temporalPass.js +59 -14
- package/dist/render/translucentQueue.d.ts +6 -0
- package/dist/render/translucentQueue.js +15 -1
- package/package.json +6 -6
- package/src/index.ts +10 -0
- package/src/render/backend/api.ts +15 -9
- package/src/render/backend/webgl2/glassTintPrograms.ts +58 -0
- package/src/render/backend/webgl2/renderer.ts +422 -109
- package/src/render/backend/webgpu/buffers.ts +50 -2
- package/src/render/backend/webgpu/depthPass.ts +2 -1
- package/src/render/backend/webgpu/flatPass.ts +22 -1
- package/src/render/backend/webgpu/glassTintPass.ts +244 -0
- package/src/render/backend/webgpu/gpuDrivenPass.ts +349 -5
- package/src/render/backend/webgpu/instanced.ts +21 -1
- package/src/render/backend/webgpu/latePass.ts +152 -0
- package/src/render/backend/webgpu/lightVolumePass.ts +5 -1
- package/src/render/backend/webgpu/motionPass.ts +535 -0
- package/src/render/backend/webgpu/pipelineCache.ts +12 -1
- package/src/render/backend/webgpu/pointGlassTintPass.ts +233 -0
- package/src/render/backend/webgpu/pointShadowPass.ts +158 -26
- package/src/render/backend/webgpu/postPass.ts +34 -4
- package/src/render/backend/webgpu/renderer.ts +1178 -425
- package/src/render/backend/webgpu/scatterPass.ts +2 -1
- package/src/render/backend/webgpu/skinPaletteRing.ts +4 -0
- package/src/render/backend/webgpu/snapshotMips.ts +80 -0
- package/src/render/backend/webgpu/surfaceTexturePass.ts +1 -1
- package/src/render/flameLight.ts +15 -2
- package/src/render/glass.ts +74 -0
- package/src/render/glassCasters.ts +164 -0
- package/src/render/glassShadow.ts +153 -0
- package/src/render/globalMediumPass.ts +4 -11
- package/src/render/gpudriven/lit.ts +31 -0
- package/src/render/gpudriven/materialTable.ts +28 -6
- package/src/render/gpudriven/pipeline.ts +4 -3
- package/src/render/gpudriven/sunGlass.ts +117 -0
- package/src/render/lightBudget.ts +19 -5
- package/src/render/pointGlassTint.ts +250 -0
- package/src/render/pointShadowArray.ts +188 -22
- package/src/render/pointShadowBudget.ts +49 -39
- package/src/render/pointShadowMap.ts +41 -0
- package/src/render/recon/changeFrames.ts +24 -0
- package/src/render/recon/disocclusion.ts +54 -3
- package/src/render/recon/lateRouting.ts +29 -0
- package/src/render/recon/mover.ts +118 -0
- package/src/render/recon/resolve.ts +137 -10
- package/src/render/renderQuality.ts +47 -0
- package/src/render/rendererHarness.ts +12 -0
- package/src/render/sceneTarget.ts +66 -12
- package/src/render/shaders/depth.ts +109 -9
- package/src/render/shaders/flat/directionalShadow.ts +97 -6
- package/src/render/shaders/flat/glassTint.ts +25 -0
- package/src/render/shaders/flat/index.ts +17 -0
- package/src/render/shaders/flat/lampGlass.ts +185 -0
- package/src/render/shaders/flat/main.ts +115 -15
- package/src/render/shaders/flat/pointShadow.ts +2 -0
- package/src/render/shaders/flat/preamble.ts +21 -6
- package/src/render/shaders/generated/depth.wgsl.ts +234 -4
- package/src/render/shaders/generated/flat.wgsl.ts +376 -328
- package/src/render/shaders/generated/globalMedium.wgsl.ts +3 -13
- package/src/render/shaders/generated/lightVolume.wgsl.ts +4 -14
- package/src/render/shaders/generated/octahedralResolve.wgsl.ts +30 -0
- package/src/render/shaders/generated/temporalResolve.wgsl.ts +31 -6
- package/src/render/shaders/globalMedium.ts +6 -6
- package/src/render/shaders/gpudriven/blendRaster.wgsl.ts +88 -1
- package/src/render/shaders/gpudriven/cull.wgsl.ts +11 -2
- package/src/render/shaders/gpudriven/materialTable.wgsl.ts +3 -1
- package/src/render/shaders/gpudriven/shade.wgsl.ts +50 -1
- package/src/render/shaders/gpudriven/sunGlass.wgsl.ts +139 -0
- package/src/render/shaders/lightVolume.ts +6 -6
- package/src/render/shaders/octahedralResolve.ts +50 -17
- package/src/render/shaders/recon/lateDepth.wgsl.ts +47 -0
- package/src/render/shaders/recon/motion.wgsl.ts +84 -0
- package/src/render/shaders/recon/resolve.wgsl.ts +112 -8
- package/src/render/shaders/temporalResolve.ts +141 -5
- package/src/render/shadowCasters.ts +28 -1
- package/src/render/shadowMap.ts +231 -37
- package/src/render/sunGlassTint.ts +86 -0
- package/src/render/temporalAa.ts +227 -0
- package/src/render/temporalPass.ts +88 -12
- package/src/render/translucentQueue.ts +20 -1
|
@@ -2,6 +2,7 @@ import { FrameBudget } from '../budget.js';
|
|
|
2
2
|
import { MaterialChanges, ownsMaterial } from '../materialChanges.js';
|
|
3
3
|
import { mat4 } from 'gl-matrix';
|
|
4
4
|
import { GL_DEPTH_REMAP, MAX_DEPTH_LAYER, depthClearFor, depthOffsetForLayer, GL_SHADOW_REMAP, REVERSED_DEPTH, SHADOW_DEPTH_CLEAR, glDepthFuncEqual, glShadowDepthFunc, } from '../../depthConvention.js';
|
|
5
|
+
import { resolveGlass } from '../../glass.js';
|
|
5
6
|
import { PickableSet } from '../../pickable.js';
|
|
6
7
|
import { TEMPORAL_HISTORY_BLEND, TemporalHistory, jitterOffset, jitterProjection, } from '../../temporalAa.js';
|
|
7
8
|
import { DecalPass } from '../../decalPass.js';
|
|
@@ -51,13 +52,17 @@ import { areaShadowLayerCount, castingRange, createResolvedAreaShadows, firstAre
|
|
|
51
52
|
import { equirectToCubeFaces } from '../../equirectToCube.js';
|
|
52
53
|
import { SKY_FRAG, skyVertFor } from '../../shaders/sky.js';
|
|
53
54
|
import { DEPTH_CUTOUT_FRAG, DEPTH_CUTOUT_VERT, DEPTH_FRAG, DEPTH_INSTANCED_CUTOUT_VERT, DEPTH_INSTANCED_VERT, DEPTH_SKINNED_VERT, DEPTH_VERT, } from '../../shaders/depth.js';
|
|
54
|
-
import { PointShadowArray } from '../../pointShadowArray.js';
|
|
55
|
-
import {
|
|
55
|
+
import { OCTAHEDRAL_EDGE, PointShadowArray, strideLayers } from '../../pointShadowArray.js';
|
|
56
|
+
import { SunShadowArray } from '../../shadowMap.js';
|
|
56
57
|
import { POINT_SHADOW_SAMPLER, PointShadowSystem } from '../../pointShadowSystem.js';
|
|
58
|
+
import { castsDepth, glassOf } from '../../shadowCasters.js';
|
|
59
|
+
import { GlassCasterList } from '../../glassCasters.js';
|
|
60
|
+
import { SunGlassTint } from '../../sunGlassTint.js';
|
|
61
|
+
import { GlassTintPrograms } from './glassTintPrograms.js';
|
|
57
62
|
import { FACE_COUNT, PointShadowMap } from '../../pointShadowMap.js';
|
|
58
63
|
import { createResolvedPointShadows, DEFAULT_SOURCE_RADIUS } from '../../pointShadowImage.js';
|
|
59
64
|
import { createBakeScratch, runPointShadowBakes, selectCastingLights, } from '../../pointShadowBudget.js';
|
|
60
|
-
import { DIRECTIONAL_SHADOW_UNITS, 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, COOKIE_ATLAS_TEXTURE_UNIT, REFRACT_SCENE_TEXTURE_UNIT, DRIFT_LIGHT_ATLAS_TEXTURE_UNIT, DRIFT_LIGHT_INDEX_TEXTURE_UNIT, COOKIE_TILE, IES_ATLAS_TEXTURE_UNIT, bindAreaLights, bindAreaShadows, openCones, } from '../../lightBudget.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, COOKIE_ATLAS_TEXTURE_UNIT, REFRACT_SCENE_TEXTURE_UNIT, DRIFT_LIGHT_ATLAS_TEXTURE_UNIT, DRIFT_LIGHT_INDEX_TEXTURE_UNIT, COOKIE_TILE, IES_ATLAS_TEXTURE_UNIT, bindAreaLights, bindAreaShadows, openCones, } from '../../lightBudget.js';
|
|
61
66
|
import { resolveRenderQuality } from '../../renderQuality.js';
|
|
62
67
|
import { drawingBufferSize } from '../../drawingBuffer.js';
|
|
63
68
|
import { Mesh, createMeshIncremental } from '../../mesh.js';
|
|
@@ -488,6 +493,8 @@ export class WebGL2Renderer {
|
|
|
488
493
|
/** Whether a palette is currently chosen. Distinct from whether one was ever uploaded. */
|
|
489
494
|
skinPaletteSet = false;
|
|
490
495
|
emptyTexture2D;
|
|
496
|
+
/** One glass resolution reused by every draw, so a pane allocates nothing. See `glass.ts`. */
|
|
497
|
+
glassScratch = { transmission: 0, frost: 0, tint: [1, 1, 1] };
|
|
491
498
|
/** DriftLight's stand-ins while no field is whole: no brick anywhere, and no light. */
|
|
492
499
|
emptyDriftIndex;
|
|
493
500
|
emptyDriftAtlas;
|
|
@@ -560,6 +567,7 @@ export class WebGL2Renderer {
|
|
|
560
567
|
refraction: 0,
|
|
561
568
|
refractTint: undefined,
|
|
562
569
|
thicknessM: 0,
|
|
570
|
+
glass: undefined,
|
|
563
571
|
};
|
|
564
572
|
motionBlurScale = 1;
|
|
565
573
|
/** How much of the bloom ceiling this frame takes. See `setBloom`. */
|
|
@@ -626,6 +634,9 @@ export class WebGL2Renderer {
|
|
|
626
634
|
* Scratch, filled in `endFrame`, which may not allocate.
|
|
627
635
|
*/
|
|
628
636
|
frameProjection = null;
|
|
637
|
+
/** The temporal resolve's depth-to-metres row, from the inverse of `frameProjection`. */
|
|
638
|
+
taaInvProjection = mat4.create();
|
|
639
|
+
taaDepthToViewZ = Float32Array.of(0, 0, 0, 1);
|
|
629
640
|
aoInvProjection = mat4.create();
|
|
630
641
|
aoProjScale = new Float32Array(2);
|
|
631
642
|
/**
|
|
@@ -684,9 +695,8 @@ export class WebGL2Renderer {
|
|
|
684
695
|
depthInstancedCutoutUniforms;
|
|
685
696
|
scatterDepthProgram;
|
|
686
697
|
scatterDepthUniforms;
|
|
687
|
-
shadowMap
|
|
688
|
-
|
|
689
|
-
dynamicShadowMap;
|
|
698
|
+
/** The sun's static, moving and peeled maps, as layers of one array. See `shadowMap.ts`. */
|
|
699
|
+
sunShadows;
|
|
690
700
|
pointShadows;
|
|
691
701
|
/**
|
|
692
702
|
* The layers a world's casting rectangles hold, above the point pool in the same array.
|
|
@@ -892,7 +902,7 @@ export class WebGL2Renderer {
|
|
|
892
902
|
/** Non-zero while the drawing buffer is pinned; see `lockDrawingBuffer`. */
|
|
893
903
|
lockedWidth = 0;
|
|
894
904
|
lockedHeight = 0;
|
|
895
|
-
|
|
905
|
+
activeSunLayer = null;
|
|
896
906
|
reflectionPassActive = false;
|
|
897
907
|
reflectionReadyThisFrame = false;
|
|
898
908
|
reflectionAtmosphereY = 0;
|
|
@@ -914,7 +924,7 @@ export class WebGL2Renderer {
|
|
|
914
924
|
*
|
|
915
925
|
* **Not whether the map exists.** That was the test here, on the belief that a GL texture is
|
|
916
926
|
* born reading as the far plane; it is born zeroed, and a scene that built the layer and never
|
|
917
|
-
* peeled it sampled an occluder at the light. `
|
|
927
|
+
* peeled it sampled an occluder at the light. `SunShadowArray` clears itself at birth, so an
|
|
918
928
|
* unfilled layer would read as lit either way — this is what keeps the two backends one
|
|
919
929
|
* decision, and a frame that does not peel from paying a fetch per tap for nothing.
|
|
920
930
|
*/
|
|
@@ -940,8 +950,34 @@ export class WebGL2Renderer {
|
|
|
940
950
|
* Built once at construction, arrow properties closing over `this`: a sink created
|
|
941
951
|
* per pass would allocate six objects per light per frame.
|
|
942
952
|
*/
|
|
953
|
+
/** What the sun's light keeps through glass, made the first time glass casts: `sunGlassTint.ts`. */
|
|
954
|
+
sunGlassTint = null;
|
|
955
|
+
/** The five programs a pane casts its colour through, compiled the first time glass casts. */
|
|
956
|
+
glassTintPrograms = null;
|
|
957
|
+
/** True while the glass list is replayed into the glass depth layer through `casterSink`. */
|
|
958
|
+
drawingGlassDepth = false;
|
|
959
|
+
/** How many times a shadow array grew its glass layers: once per family, and never again. */
|
|
960
|
+
sunGlassRebuilds = 0;
|
|
961
|
+
/** The light a tint pass is drawn for, and one pane's colour and clarity: uploaded per entry. */
|
|
962
|
+
glassLight = new Float32Array(4);
|
|
963
|
+
glassPane = new Float32Array(4);
|
|
964
|
+
/** The glass the open shadow pass was offered, kept for its own passes: `glassCasters.ts`. */
|
|
965
|
+
glassCasters = new GlassCasterList();
|
|
966
|
+
/** One resolution every glass caster is read through, so recording one allocates nothing. */
|
|
967
|
+
glassCasterScratch = {
|
|
968
|
+
transmission: 0,
|
|
969
|
+
frost: 0,
|
|
970
|
+
tint: [1, 1, 1],
|
|
971
|
+
};
|
|
943
972
|
casterSink = {
|
|
944
973
|
mesh: (mesh, model, material) => {
|
|
974
|
+
if (!this.drawingGlassDepth && !castsDepth(material)) {
|
|
975
|
+
/* Glass: kept aside for its own two passes rather than dropped. See `glassCasters.ts`. */
|
|
976
|
+
if (this.quality.glassShadows !== 'off' && glassOf(material, this.glassCasterScratch)) {
|
|
977
|
+
this.glassCasters.recordMesh(mesh, model, material, this.glassCasterScratch);
|
|
978
|
+
}
|
|
979
|
+
return;
|
|
980
|
+
}
|
|
945
981
|
this.shadowDrawBudget.ask();
|
|
946
982
|
const { gl } = this;
|
|
947
983
|
/* A two-sided caster casts from whichever face the light sees: nothing culled. */
|
|
@@ -981,6 +1017,12 @@ export class WebGL2Renderer {
|
|
|
981
1017
|
* columns the batch's own buffer supplies.
|
|
982
1018
|
*/
|
|
983
1019
|
instanced: (batch, data, material) => {
|
|
1020
|
+
if (!this.drawingGlassDepth && !castsDepth(material)) {
|
|
1021
|
+
if (this.quality.glassShadows !== 'off' && glassOf(material, this.glassCasterScratch)) {
|
|
1022
|
+
this.glassCasters.recordInstanced(batch, data, material, this.glassCasterScratch);
|
|
1023
|
+
}
|
|
1024
|
+
return;
|
|
1025
|
+
}
|
|
984
1026
|
const { gl } = this;
|
|
985
1027
|
const gpuBatch = batch;
|
|
986
1028
|
const count = Math.min(data.count, gpuBatch.capacity);
|
|
@@ -1012,7 +1054,13 @@ export class WebGL2Renderer {
|
|
|
1012
1054
|
gl.enable(gl.CULL_FACE);
|
|
1013
1055
|
gl.useProgram(this.depthProgram);
|
|
1014
1056
|
},
|
|
1015
|
-
skinnedMesh: (mesh, model, palette) => {
|
|
1057
|
+
skinnedMesh: (mesh, model, palette, material) => {
|
|
1058
|
+
if (!this.drawingGlassDepth && !castsDepth(material)) {
|
|
1059
|
+
if (this.quality.glassShadows !== 'off' && glassOf(material, this.glassCasterScratch)) {
|
|
1060
|
+
this.glassCasters.recordSkinned(mesh, model, palette, material, this.glassCasterScratch);
|
|
1061
|
+
}
|
|
1062
|
+
return;
|
|
1063
|
+
}
|
|
1016
1064
|
this.shadowDrawBudget.ask();
|
|
1017
1065
|
const { gl } = this;
|
|
1018
1066
|
const u = this.depthSkinnedUniforms;
|
|
@@ -1095,8 +1143,52 @@ export class WebGL2Renderer {
|
|
|
1095
1143
|
gl.useProgram(this.depthProgram);
|
|
1096
1144
|
gl.uniform1i(this.depthUniforms['uPeelShadowLayer'] ?? null, 0);
|
|
1097
1145
|
gl.uniformMatrix4fv(this.depthUniforms['uLightViewProj'] ?? null, false, faceViewProj);
|
|
1146
|
+
/* A face is an enumeration of its own: its glass is its own. */
|
|
1147
|
+
this.glassCasters.clear();
|
|
1098
1148
|
casters(this.casterSink);
|
|
1149
|
+
/*
|
|
1150
|
+
* The first face to offer glass interleaves the array, before its opaque depth is resolved:
|
|
1151
|
+
* `PointShadowArray.growForGlass`. Nothing baked before this saw glass, so nothing re-bakes.
|
|
1152
|
+
*/
|
|
1153
|
+
if (this.glassCasters.count > 0 && this.quality.glassShadows !== 'off') {
|
|
1154
|
+
const tintEdge = this.quality.glassShadows === 'half' ? OCTAHEDRAL_EDGE >> 1 : OCTAHEDRAL_EDGE;
|
|
1155
|
+
this.pointShadowArray?.growForGlass(gl, tintEdge);
|
|
1156
|
+
}
|
|
1099
1157
|
};
|
|
1158
|
+
/**
|
|
1159
|
+
* A lamp's glass, after each face's opaque depth: `PointShadowMap.bake` asks and binds, this
|
|
1160
|
+
* draws, through the same sinks the sun's glass takes and in the face's own matrix.
|
|
1161
|
+
*/
|
|
1162
|
+
pointGlassDraw = {
|
|
1163
|
+
offered: () => this.glassCasters.count > 0,
|
|
1164
|
+
draw: (pass, x, y, z) => {
|
|
1165
|
+
const { gl } = this;
|
|
1166
|
+
if (pass === 'depth') {
|
|
1167
|
+
/* Nothing on the unit the depth programs declare a previous map on: see `flushSunGlass`. */
|
|
1168
|
+
gl.activeTexture(gl.TEXTURE0);
|
|
1169
|
+
gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.emptyTexture2DArray);
|
|
1170
|
+
this.activeDepthPeel = false;
|
|
1171
|
+
gl.useProgram(this.depthProgram);
|
|
1172
|
+
gl.uniform1i(this.depthUniforms['uPeelShadowLayer'] ?? null, 0);
|
|
1173
|
+
gl.uniformMatrix4fv(this.depthUniforms['uLightViewProj'] ?? null, false, this.activeDepthViewProj ?? IDENTITY_MAT4);
|
|
1174
|
+
this.drawingGlassDepth = true;
|
|
1175
|
+
this.glassCasters.replay(this.glassDepthSink);
|
|
1176
|
+
this.drawingGlassDepth = false;
|
|
1177
|
+
return;
|
|
1178
|
+
}
|
|
1179
|
+
this.glassTintPrograms ??= new GlassTintPrograms(gl);
|
|
1180
|
+
/* A place, w 1: the tint takes each pane's angle to the lamp from its own position. */
|
|
1181
|
+
this.glassLight[0] = x;
|
|
1182
|
+
this.glassLight[1] = y;
|
|
1183
|
+
this.glassLight[2] = z;
|
|
1184
|
+
this.glassLight[3] = 1;
|
|
1185
|
+
this.glassCasters.replay(this.glassTintSink);
|
|
1186
|
+
},
|
|
1187
|
+
};
|
|
1188
|
+
/** The glass hook a bake is handed: none at all when glass shadows are off. */
|
|
1189
|
+
get pointGlass() {
|
|
1190
|
+
return this.quality.glassShadows === 'off' ? null : this.pointGlassDraw;
|
|
1191
|
+
}
|
|
1100
1192
|
/**
|
|
1101
1193
|
* Viewport in CSS pixels rather than device pixels.
|
|
1102
1194
|
*
|
|
@@ -1903,20 +1995,14 @@ export class WebGL2Renderer {
|
|
|
1903
1995
|
gl.uniform1i(uniforms['uCutoutMap'] ?? null, SURFACE_TEXTURE_UNIT);
|
|
1904
1996
|
}
|
|
1905
1997
|
gl.useProgram(null);
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
: null;
|
|
1915
|
-
// Movers get an independent layer. It preserves distance fading when a
|
|
1916
|
-
// moving and a static caster overlap: their transmissions multiply rather
|
|
1917
|
-
// than one depth replacing the other.
|
|
1918
|
-
this.dynamicShadowMap = this.quality.directionalShadows
|
|
1919
|
-
? new ShadowMap(gl, this.quality.directionalShadowMapSize)
|
|
1998
|
+
/*
|
|
1999
|
+
* The static world, its movers and — where more than one static depth layer is asked for —
|
|
2000
|
+
* the peel, as layers of one array. Movers get a layer of their own so a moving and a static
|
|
2001
|
+
* caster that overlap keep both distance fades: their transmissions multiply rather than one
|
|
2002
|
+
* depth replacing the other. The peel keeps the second static occluder the same way.
|
|
2003
|
+
*/
|
|
2004
|
+
this.sunShadows = this.quality.directionalShadows
|
|
2005
|
+
? new SunShadowArray(gl, this.quality.directionalShadowMapSize, this.quality.directionalShadowDepthLayers > 1)
|
|
1920
2006
|
: null;
|
|
1921
2007
|
// Static maps are allocated once the world reports its light count. No
|
|
1922
2008
|
// cubemap resources exist at all when the quality profile disables them.
|
|
@@ -2085,9 +2171,9 @@ export class WebGL2Renderer {
|
|
|
2085
2171
|
* every one of them, per renderer, for as long as it lived; `renderer.test.ts` counts them.
|
|
2086
2172
|
*/
|
|
2087
2173
|
this.sceneTarget?.dispose();
|
|
2088
|
-
this.
|
|
2089
|
-
this.
|
|
2090
|
-
this.
|
|
2174
|
+
this.sunShadows?.dispose(gl);
|
|
2175
|
+
this.sunGlassTint?.dispose(gl);
|
|
2176
|
+
this.glassTintPrograms?.dispose(gl);
|
|
2091
2177
|
this.planarReflection?.dispose(gl);
|
|
2092
2178
|
this.pointShadows?.dispose();
|
|
2093
2179
|
gl.deleteVertexArray(this.panelVao);
|
|
@@ -2716,8 +2802,10 @@ export class WebGL2Renderer {
|
|
|
2716
2802
|
const lit = options.lit ?? true;
|
|
2717
2803
|
const fog = options.fog ?? true;
|
|
2718
2804
|
const toneMapped = options.toneMapped ?? true;
|
|
2719
|
-
/*
|
|
2720
|
-
|
|
2805
|
+
/* A blended batch shows what is behind it — refraction or glass — as a single draw does, and
|
|
2806
|
+
counts a material of its own for it by the rule both backends share (`materialChanges.ts`). */
|
|
2807
|
+
const refracting = blend && this.bindSeeThrough(u, options);
|
|
2808
|
+
const own = ownsMaterial({ opacity, lit, fog, toneMapped, refracting });
|
|
2721
2809
|
if (own)
|
|
2722
2810
|
this.materials.dirty();
|
|
2723
2811
|
this.takeMaterial();
|
|
@@ -2738,20 +2826,9 @@ export class WebGL2Renderer {
|
|
|
2738
2826
|
* **A null snapshot leaves the strength at zero**, so the draw shades as an ordinary
|
|
2739
2827
|
* translucent one rather than sampling a black texture and painting the pane the colour of a
|
|
2740
2828
|
* hole. That is the defined state the two-backends rule asks for instead of a silent no-op.
|
|
2829
|
+
* The copy is bound above, before the material is counted, because whether it was taken is
|
|
2830
|
+
* part of what the count asks.
|
|
2741
2831
|
*/
|
|
2742
|
-
const refraction = options.refraction ?? 0;
|
|
2743
|
-
let refracting = false;
|
|
2744
|
-
if (refraction > 0) {
|
|
2745
|
-
const snapshot = this.sceneTarget?.snapshotColor() ?? null;
|
|
2746
|
-
if (snapshot !== null) {
|
|
2747
|
-
refracting = true;
|
|
2748
|
-
gl.activeTexture(gl.TEXTURE0 + REFRACT_SCENE_TEXTURE_UNIT);
|
|
2749
|
-
gl.bindTexture(gl.TEXTURE_2D, snapshot);
|
|
2750
|
-
gl.uniform1f(u['uRefractStrength'] ?? null, refraction);
|
|
2751
|
-
gl.uniform3fv(u['uRefractTint'] ?? null, options.refractTint ?? WHITE_TINT);
|
|
2752
|
-
gl.uniform1f(u['uRefractThickness'] ?? null, options.thicknessM ?? 0);
|
|
2753
|
-
}
|
|
2754
|
-
}
|
|
2755
2832
|
if (!lit)
|
|
2756
2833
|
gl.uniform1i(u['uLightingEnabled'] ?? null, 0);
|
|
2757
2834
|
if (!fog)
|
|
@@ -2771,7 +2848,13 @@ export class WebGL2Renderer {
|
|
|
2771
2848
|
gl.enable(gl.POLYGON_OFFSET_FILL);
|
|
2772
2849
|
gl.polygonOffset(offset.slope, offset.units);
|
|
2773
2850
|
}
|
|
2851
|
+
/* Both sides of a double-sided material, as every mesh path does and WebGPU's pipeline does
|
|
2852
|
+
for both: without it a batch's far faces were culled here alone. */
|
|
2853
|
+
if (this.materialDoubleSided)
|
|
2854
|
+
gl.disable(gl.CULL_FACE);
|
|
2774
2855
|
mesh.drawInstances(gl, count);
|
|
2856
|
+
if (this.materialDoubleSided)
|
|
2857
|
+
gl.enable(gl.CULL_FACE);
|
|
2775
2858
|
if (layer > 0) {
|
|
2776
2859
|
gl.polygonOffset(0, 0);
|
|
2777
2860
|
gl.disable(gl.POLYGON_OFFSET_FILL);
|
|
@@ -2787,11 +2870,8 @@ export class WebGL2Renderer {
|
|
|
2787
2870
|
gl.uniform1f(u['uOpacity'] ?? null, 1);
|
|
2788
2871
|
/* The binding is released with the uniform, which is the 2026-08-27 rule: a WebGL2 binding
|
|
2789
2872
|
outlives its frame, so a snapshot left on unit 14 is held against the next frame's copy. */
|
|
2790
|
-
if (refracting)
|
|
2791
|
-
|
|
2792
|
-
gl.activeTexture(gl.TEXTURE0 + REFRACT_SCENE_TEXTURE_UNIT);
|
|
2793
|
-
gl.bindTexture(gl.TEXTURE_2D, this.emptyTexture2D);
|
|
2794
|
-
}
|
|
2873
|
+
if (refracting)
|
|
2874
|
+
this.unbindSeeThrough(u);
|
|
2795
2875
|
if (!lit)
|
|
2796
2876
|
gl.uniform1i(u['uLightingEnabled'] ?? null, 1);
|
|
2797
2877
|
if (!fog)
|
|
@@ -2802,6 +2882,45 @@ export class WebGL2Renderer {
|
|
|
2802
2882
|
this.materials.dirty();
|
|
2803
2883
|
this.useFlatProgram();
|
|
2804
2884
|
}
|
|
2885
|
+
/**
|
|
2886
|
+
* Bind what a translucent draw shows through it: the frame's copy, refraction's bend and absorption,
|
|
2887
|
+
* and glass. Returns whether the copy is bound, and so whether `unbindSeeThrough` owes it back.
|
|
2888
|
+
*
|
|
2889
|
+
* **A glass draw takes the copy even when it does not bend**, since a pane shows what is behind it
|
|
2890
|
+
* either way, and asks for the copy's mip chain, which a frosted pane reads blurred. A null copy
|
|
2891
|
+
* leaves both off, so the pane shades as ordinary translucency rather than sampling black.
|
|
2892
|
+
*
|
|
2893
|
+
* **Refused inside a probe's face or a mirror**, as WebGPU's `takeRefractSnapshot` refuses: the
|
|
2894
|
+
* copy is of the frame drawn to the screen, so a pane there would show the wrong picture, and
|
|
2895
|
+
* taking it mid-bake is how a probe's next face came to be attached to the scene's framebuffer.
|
|
2896
|
+
*/
|
|
2897
|
+
bindSeeThrough(u, options) {
|
|
2898
|
+
const { gl } = this;
|
|
2899
|
+
const refraction = options.refraction ?? 0;
|
|
2900
|
+
const glassy = resolveGlass(options.glass, this.glassScratch);
|
|
2901
|
+
if (refraction <= 0 && !glassy)
|
|
2902
|
+
return false;
|
|
2903
|
+
if (this.probePassActive || this.reflectionPassActive)
|
|
2904
|
+
return false;
|
|
2905
|
+
const snapshot = this.sceneTarget?.snapshotColor(false, glassy) ?? null;
|
|
2906
|
+
if (snapshot === null)
|
|
2907
|
+
return false;
|
|
2908
|
+
gl.activeTexture(gl.TEXTURE0 + REFRACT_SCENE_TEXTURE_UNIT);
|
|
2909
|
+
gl.bindTexture(gl.TEXTURE_2D, snapshot);
|
|
2910
|
+
const glass = this.glassScratch;
|
|
2911
|
+
/* Strength, path thickness, transmission, frost: one vector, for the budget the shader states. */
|
|
2912
|
+
gl.uniform4f(u['uSeeThrough'] ?? null, Math.max(refraction, 0), options.thicknessM ?? 0, glass.transmission, glass.frost);
|
|
2913
|
+
gl.uniform3fv(u['uRefractTint'] ?? null, options.refractTint ?? WHITE_TINT);
|
|
2914
|
+
gl.uniform3fv(u['uGlassTint'] ?? null, glass.tint);
|
|
2915
|
+
return true;
|
|
2916
|
+
}
|
|
2917
|
+
/** Put back what `bindSeeThrough` bound: the binding outlives its frame (the 2026-08-27 rule). */
|
|
2918
|
+
unbindSeeThrough(u) {
|
|
2919
|
+
const { gl } = this;
|
|
2920
|
+
gl.uniform4f(u['uSeeThrough'] ?? null, 0, 0, 0, 0);
|
|
2921
|
+
gl.activeTexture(gl.TEXTURE0 + REFRACT_SCENE_TEXTURE_UNIT);
|
|
2922
|
+
gl.bindTexture(gl.TEXTURE_2D, this.emptyTexture2D);
|
|
2923
|
+
}
|
|
2805
2924
|
disposeMesh(mesh) {
|
|
2806
2925
|
/*
|
|
2807
2926
|
* Nothing to release when the context is gone: it took every buffer and VAO with it,
|
|
@@ -3335,6 +3454,10 @@ export class WebGL2Renderer {
|
|
|
3335
3454
|
*/
|
|
3336
3455
|
gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.pointShadowArray?.texture ?? this.emptyTexture2DArray);
|
|
3337
3456
|
gl.uniform1i(uniforms[POINT_SHADOW_SAMPLER] ?? null, firstUnit);
|
|
3457
|
+
/* What the lamps keep through glass: the one-texel stand-in, read as none, until glass casts. */
|
|
3458
|
+
gl.activeTexture(gl.TEXTURE0 + POINT_GLASS_TINT_TEXTURE_UNIT);
|
|
3459
|
+
gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.pointShadowArray?.tintTexture ?? this.emptyTexture2DArray);
|
|
3460
|
+
gl.uniform1i(uniforms['uPointGlassTints'] ?? null, POINT_GLASS_TINT_TEXTURE_UNIT);
|
|
3338
3461
|
gl.uniform1iv(uniforms['uPointShadowLayer[0]'] ?? null, resolved.layers);
|
|
3339
3462
|
/* Origin and far plane in one row; see `uPointShadowProjection` in the preamble. */
|
|
3340
3463
|
gl.uniform4fv(uniforms['uPointShadowProjection[0]'] ?? null, resolved.projections);
|
|
@@ -3611,20 +3734,17 @@ export class WebGL2Renderer {
|
|
|
3611
3734
|
* emitting, because a stale map must never be sampled.
|
|
3612
3735
|
*/
|
|
3613
3736
|
beginShadowPass(lightViewProj, layer = 'static') {
|
|
3614
|
-
const
|
|
3615
|
-
|
|
3616
|
-
: layer === 'static-peel'
|
|
3617
|
-
? this.peeledShadowMap
|
|
3618
|
-
: this.dynamicShadowMap;
|
|
3619
|
-
if (shadowMap === null) {
|
|
3737
|
+
const sun = this.sunShadows;
|
|
3738
|
+
if (sun === null || (layer === 'static-peel' && sun.layers < 3)) {
|
|
3620
3739
|
this.shadowPassActive = false;
|
|
3621
|
-
this.
|
|
3740
|
+
this.activeSunLayer = null;
|
|
3622
3741
|
return;
|
|
3623
3742
|
}
|
|
3624
3743
|
const { gl } = this;
|
|
3625
3744
|
this.gpuTimer.begin('shadows');
|
|
3626
3745
|
this.shadowPassActive = true;
|
|
3627
|
-
this.
|
|
3746
|
+
this.activeSunLayer = layer;
|
|
3747
|
+
this.glassCasters.clear();
|
|
3628
3748
|
/* The static layer opens first, so a frame's peel counts from here. See `peelFilled`. */
|
|
3629
3749
|
if (layer === 'static')
|
|
3630
3750
|
this.peelFilled = false;
|
|
@@ -3652,16 +3772,16 @@ export class WebGL2Renderer {
|
|
|
3652
3772
|
// The previous mesh pass left all directional maps bound for sampling.
|
|
3653
3773
|
// A texture cannot be sampled while it is attached to the active depth
|
|
3654
3774
|
// target, even when the shader branch would skip that sample.
|
|
3655
|
-
|
|
3656
|
-
gl.
|
|
3657
|
-
|
|
3658
|
-
shadowMap.begin(gl);
|
|
3775
|
+
gl.activeTexture(gl.TEXTURE0);
|
|
3776
|
+
gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.emptyTexture2DArray);
|
|
3777
|
+
sun.begin(gl, layer);
|
|
3659
3778
|
gl.useProgram(this.depthProgram);
|
|
3660
3779
|
const peel = layer === 'static-peel';
|
|
3661
3780
|
gl.uniform1i(this.depthUniforms['uPeelShadowLayer'] ?? null, peel ? 1 : 0);
|
|
3662
3781
|
if (peel) {
|
|
3782
|
+
/* The static layer, read while the peel is drawn aside: see `SunShadowArray`. */
|
|
3663
3783
|
gl.activeTexture(gl.TEXTURE0);
|
|
3664
|
-
gl.bindTexture(gl.
|
|
3784
|
+
gl.bindTexture(gl.TEXTURE_2D_ARRAY, sun.texture);
|
|
3665
3785
|
gl.uniform1i(this.depthUniforms['uPreviousShadowMap'] ?? null, 0);
|
|
3666
3786
|
}
|
|
3667
3787
|
/* The corrected one, matching `activeDepthViewProj` above: a pass must render its map with the
|
|
@@ -3767,21 +3887,158 @@ export class WebGL2Renderer {
|
|
|
3767
3887
|
return;
|
|
3768
3888
|
casters(this.casterSink);
|
|
3769
3889
|
}
|
|
3890
|
+
/**
|
|
3891
|
+
* Draw the pass's glass twice: where the nearest pane is, and what the panes let through.
|
|
3892
|
+
*
|
|
3893
|
+
* After the opaque casters, in the pass's own light matrix. A layer that has held glass is
|
|
3894
|
+
* redrawn even when this pass offered none, so glass that has gone takes its colour with it. The
|
|
3895
|
+
* first glass grows the array (`SunShadowArray.growForGlass`) and makes the tint target.
|
|
3896
|
+
*/
|
|
3897
|
+
flushSunGlass(which) {
|
|
3898
|
+
const sun = this.sunShadows;
|
|
3899
|
+
const list = this.glassCasters;
|
|
3900
|
+
if (sun === null || this.quality.glassShadows === 'off')
|
|
3901
|
+
return;
|
|
3902
|
+
if (list.count === 0 && !sun.hasGlass)
|
|
3903
|
+
return;
|
|
3904
|
+
const { gl } = this;
|
|
3905
|
+
if (list.count > 0 && sun.growForGlass(gl))
|
|
3906
|
+
this.sunGlassRebuilds++;
|
|
3907
|
+
/* Where the nearest pane is: the depth programs, through the sink every opaque caster took. */
|
|
3908
|
+
gl.activeTexture(gl.TEXTURE0);
|
|
3909
|
+
gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.emptyTexture2DArray);
|
|
3910
|
+
sun.beginGlass(gl, which);
|
|
3911
|
+
this.activeDepthPeel = false;
|
|
3912
|
+
gl.useProgram(this.depthProgram);
|
|
3913
|
+
gl.uniform1i(this.depthUniforms['uPeelShadowLayer'] ?? null, 0);
|
|
3914
|
+
gl.uniformMatrix4fv(this.depthUniforms['uLightViewProj'] ?? null, false, this.activeDepthViewProj ?? IDENTITY_MAT4);
|
|
3915
|
+
this.drawingGlassDepth = true;
|
|
3916
|
+
list.replay(this.glassDepthSink);
|
|
3917
|
+
this.drawingGlassDepth = false;
|
|
3918
|
+
sun.end(gl);
|
|
3919
|
+
/* What the panes let through, every one on a ray, multiplied. */
|
|
3920
|
+
this.glassTintPrograms ??= new GlassTintPrograms(gl);
|
|
3921
|
+
const size = this.quality.glassShadows === 'half' ? Math.max(1, sun.size >> 1) : sun.size;
|
|
3922
|
+
const tint = (this.sunGlassTint ??= new SunGlassTint(gl, size));
|
|
3923
|
+
/*
|
|
3924
|
+
* The direction the light travels, off the light matrix's depth row: an orthographic
|
|
3925
|
+
* projection's depth grows along it. The sign is immaterial — the tint takes the cosine
|
|
3926
|
+
* absolutely — and w 0 says it is a direction rather than a place.
|
|
3927
|
+
*/
|
|
3928
|
+
const m = this.activeDepthViewProj ?? IDENTITY_MAT4;
|
|
3929
|
+
const length = Math.hypot(m[2], m[6], m[10]) || 1;
|
|
3930
|
+
this.glassLight[0] = m[2] / length;
|
|
3931
|
+
this.glassLight[1] = m[6] / length;
|
|
3932
|
+
this.glassLight[2] = m[10] / length;
|
|
3933
|
+
this.glassLight[3] = 0;
|
|
3934
|
+
tint.begin(gl, which === 'static' ? 0 : 1);
|
|
3935
|
+
list.replay(this.glassTintSink);
|
|
3936
|
+
tint.end(gl);
|
|
3937
|
+
gl.useProgram(this.depthProgram);
|
|
3938
|
+
}
|
|
3939
|
+
/** The glass list into the glass depth layer: `casterSink` itself, with glass let through. */
|
|
3940
|
+
glassDepthSink = {
|
|
3941
|
+
mesh: (mesh, model, material) => this.casterSink.mesh(mesh, model, material),
|
|
3942
|
+
instanced: (batch, data, material) => this.casterSink.instanced?.(batch, data, material),
|
|
3943
|
+
skinnedMesh: (mesh, model, palette, material) => this.casterSink.skinnedMesh(mesh, model, palette, material),
|
|
3944
|
+
};
|
|
3945
|
+
/** One pane's tint uniforms, and its cutout where it has one. */
|
|
3946
|
+
bindGlassTint(program, glass, cutout) {
|
|
3947
|
+
const { gl } = this;
|
|
3948
|
+
const u = program.uniforms;
|
|
3949
|
+
gl.useProgram(program.program);
|
|
3950
|
+
gl.uniformMatrix4fv(u['uLightViewProj'] ?? null, false, this.activeDepthViewProj ?? IDENTITY_MAT4);
|
|
3951
|
+
this.uploadWind(u, this.frameWind);
|
|
3952
|
+
this.glassPane[0] = glass.transmission * glass.tint[0];
|
|
3953
|
+
this.glassPane[1] = glass.transmission * glass.tint[1];
|
|
3954
|
+
this.glassPane[2] = glass.transmission * glass.tint[2];
|
|
3955
|
+
this.glassPane[3] = 1 - glass.frost;
|
|
3956
|
+
gl.uniform4fv(u['uGlassPane'] ?? null, this.glassPane);
|
|
3957
|
+
gl.uniform4fv(u['uGlassLight'] ?? null, this.glassLight);
|
|
3958
|
+
if (cutout !== null) {
|
|
3959
|
+
gl.uniform2f(u['uUvScale'] ?? null, cutout.u, cutout.v);
|
|
3960
|
+
gl.uniform1f(u['uAlphaCutout'] ?? null, cutout.cutoff);
|
|
3961
|
+
cutout.albedo.bind(gl, SURFACE_TEXTURE_UNIT);
|
|
3962
|
+
gl.uniform1i(u['uCutoutMap'] ?? null, SURFACE_TEXTURE_UNIT);
|
|
3963
|
+
}
|
|
3964
|
+
}
|
|
3965
|
+
/**
|
|
3966
|
+
* The glass list into the tint layer, through the tint programs, **culling nothing**: a light
|
|
3967
|
+
* crosses a pane whichever way the pane faces, and a closed glass object has two surfaces on a
|
|
3968
|
+
* ray, so every one counts. What that asks of a model is that a pane be one surface — two-sided
|
|
3969
|
+
* glass says so with `doubleSided`, and a second copy of its triangles is a second pane.
|
|
3970
|
+
*/
|
|
3971
|
+
glassTintSink = {
|
|
3972
|
+
mesh: (mesh, model, material, glass) => {
|
|
3973
|
+
const programs = this.glassTintPrograms;
|
|
3974
|
+
if (programs === null)
|
|
3975
|
+
return;
|
|
3976
|
+
const { gl } = this;
|
|
3977
|
+
const cutout = cutoutOf(material);
|
|
3978
|
+
const program = cutout !== null ? programs.cutout : programs.rigid;
|
|
3979
|
+
this.bindGlassTint(program, glass, cutout);
|
|
3980
|
+
gl.uniformMatrix4fv(program.uniforms['uModel'] ?? null, false, model);
|
|
3981
|
+
gl.disable(gl.CULL_FACE);
|
|
3982
|
+
mesh.draw(gl);
|
|
3983
|
+
if (this.depthPassCullsFaces)
|
|
3984
|
+
gl.enable(gl.CULL_FACE);
|
|
3985
|
+
if (cutout !== null)
|
|
3986
|
+
this.restoreSurfaceTextureUnit();
|
|
3987
|
+
},
|
|
3988
|
+
instanced: (batch, data, material, glass) => {
|
|
3989
|
+
const programs = this.glassTintPrograms;
|
|
3990
|
+
if (programs === null)
|
|
3991
|
+
return;
|
|
3992
|
+
const { gl } = this;
|
|
3993
|
+
const gpuBatch = batch;
|
|
3994
|
+
const count = Math.min(data.count, gpuBatch.capacity);
|
|
3995
|
+
if (count === 0)
|
|
3996
|
+
return;
|
|
3997
|
+
const cutout = cutoutOf(material);
|
|
3998
|
+
this.bindGlassTint(cutout !== null ? programs.instancedCutout : programs.instanced, glass, cutout);
|
|
3999
|
+
gl.disable(gl.CULL_FACE);
|
|
4000
|
+
gpuBatch.mesh.drawInstances(gl, count);
|
|
4001
|
+
if (this.depthPassCullsFaces)
|
|
4002
|
+
gl.enable(gl.CULL_FACE);
|
|
4003
|
+
if (cutout !== null)
|
|
4004
|
+
this.restoreSurfaceTextureUnit();
|
|
4005
|
+
},
|
|
4006
|
+
skinnedMesh: (mesh, model, palette, _material, glass) => {
|
|
4007
|
+
const programs = this.glassTintPrograms;
|
|
4008
|
+
if (programs === null)
|
|
4009
|
+
return;
|
|
4010
|
+
const { gl } = this;
|
|
4011
|
+
const program = programs.skinned;
|
|
4012
|
+
this.bindGlassTint(program, glass, null);
|
|
4013
|
+
gl.uniformMatrix4fv(program.uniforms['uModel'] ?? null, false, model);
|
|
4014
|
+
this.skinPalette.update(gl, palette);
|
|
4015
|
+
this.skinPalette.bind(gl, SKIN_PALETTE_TEXTURE_UNIT);
|
|
4016
|
+
gl.uniform1i(program.uniforms['uJointPalette'] ?? null, SKIN_PALETTE_TEXTURE_UNIT);
|
|
4017
|
+
gl.disable(gl.CULL_FACE);
|
|
4018
|
+
mesh.draw(gl);
|
|
4019
|
+
if (this.depthPassCullsFaces)
|
|
4020
|
+
gl.enable(gl.CULL_FACE);
|
|
4021
|
+
},
|
|
4022
|
+
};
|
|
3770
4023
|
endShadowPass() {
|
|
3771
4024
|
if (this.contextLost)
|
|
3772
4025
|
return;
|
|
3773
|
-
const
|
|
3774
|
-
|
|
4026
|
+
const sun = this.sunShadows;
|
|
4027
|
+
const layer = this.activeSunLayer;
|
|
4028
|
+
if (!this.shadowPassActive || sun === null || layer === null)
|
|
3775
4029
|
return;
|
|
3776
|
-
|
|
3777
|
-
if (
|
|
4030
|
+
sun.end(this.gl);
|
|
4031
|
+
if (layer === 'static-peel')
|
|
3778
4032
|
this.peelFilled = true;
|
|
4033
|
+
/* The peel keeps no glass: its layer is the second opaque occluder and nothing else. */
|
|
4034
|
+
else
|
|
4035
|
+
this.flushSunGlass(layer === 'dynamic' ? 'dynamic' : 'static');
|
|
3779
4036
|
/* Back to the frame's sense; see `beginShadowPass` for why it left it. */
|
|
3780
4037
|
this.gl.depthFunc(glDepthFuncEqual(this.gl));
|
|
3781
4038
|
this.gl.clearDepth(this.depthClear);
|
|
3782
4039
|
this.gpuTimer.end();
|
|
3783
4040
|
this.shadowPassActive = false;
|
|
3784
|
-
this.
|
|
4041
|
+
this.activeSunLayer = null;
|
|
3785
4042
|
// Restore the drawing-buffer viewport the shadow pass overrode.
|
|
3786
4043
|
this.restoreViewport();
|
|
3787
4044
|
}
|
|
@@ -3815,6 +4072,8 @@ export class WebGL2Renderer {
|
|
|
3815
4072
|
directionalShadows: this.quality.directionalShadows,
|
|
3816
4073
|
environmentProbe: this.probeCapture !== null,
|
|
3817
4074
|
nightEmissive: this.quality.nightEmissive,
|
|
4075
|
+
/* Off compiles none of the glass lookup: see `FlatShaderOptions.glassShadows`. */
|
|
4076
|
+
glassShadows: this.quality.glassShadows !== 'off',
|
|
3818
4077
|
};
|
|
3819
4078
|
const build = (budget) => flatFrag({ ...variant, maxLights: budget.maxLights, maxAreaLights: budget.maxAreaLights });
|
|
3820
4079
|
const vert = flatVert({ skinned: false, morphed: false, instanced: false });
|
|
@@ -4078,6 +4337,8 @@ export class WebGL2Renderer {
|
|
|
4078
4337
|
*/
|
|
4079
4338
|
runPointShadowBakes(pointShadows, lights, this.quality.pointShadowFacesPerFrame, this.quality.liveShadowFacesPerFrame, this.quality.pointShadowRebakeDistance, FACE_COUNT, this.bakeScratch, staticCasters, dynamicCasters, (map, light, casters, maxFaces) => this.bakeOne(map, light, casters, maxFaces));
|
|
4080
4339
|
this.updateAreaShadows(areaLights, frameDt, staticCasters, dynamicCasters);
|
|
4340
|
+
/* The chain frost reads, once for the round, for the lights whose glass it drew or cleared. */
|
|
4341
|
+
this.pointShadowArray.finishTints(this.gl);
|
|
4081
4342
|
this.restoreViewport();
|
|
4082
4343
|
}
|
|
4083
4344
|
/**
|
|
@@ -4124,6 +4385,10 @@ export class WebGL2Renderer {
|
|
|
4124
4385
|
* reason: it already holds everything it needs.
|
|
4125
4386
|
*/
|
|
4126
4387
|
set.resolve(areaLights, this.resolvedAreaShadows);
|
|
4388
|
+
/* As the array stores them: 2L once glass has interleaved it. See `strideLayers`. */
|
|
4389
|
+
const stride = this.pointShadowArray?.stride ?? 1;
|
|
4390
|
+
strideLayers(this.resolvedAreaShadows.layer, stride);
|
|
4391
|
+
strideLayers(this.resolvedAreaShadows.liveLayer, stride);
|
|
4127
4392
|
}
|
|
4128
4393
|
/**
|
|
4129
4394
|
* Every point-shadow bake, under the one set of conventions all of them need.
|
|
@@ -4169,12 +4434,12 @@ export class WebGL2Renderer {
|
|
|
4169
4434
|
* map is stale on every frame it is checked — `pointShadowImage.ts` records that exact bug
|
|
4170
4435
|
* from two readers disagreeing about this argument's default.
|
|
4171
4436
|
*/
|
|
4172
|
-
return this.bakePointShadow(drawCasters, () => map.bake(gl, light.x, light.y, light.z, range, this.drawPointShadowFace, near, DEFAULT_SOURCE_RADIUS, maxFaces));
|
|
4437
|
+
return this.bakePointShadow(drawCasters, () => map.bake(gl, light.x, light.y, light.z, range, this.drawPointShadowFace, near, DEFAULT_SOURCE_RADIUS, maxFaces, this.pointGlass));
|
|
4173
4438
|
}
|
|
4174
4439
|
/** Spend up to `maxFaces` on this map, returning how many it used. */
|
|
4175
4440
|
bakeOne(map, light, drawCasters, maxFaces) {
|
|
4176
4441
|
const { gl } = this;
|
|
4177
|
-
return this.bakePointShadow(drawCasters, () => map.bake(gl, light.x, light.y, light.z, light.radius, this.drawPointShadowFace, light.shadowNear, light.sourceRadius, maxFaces));
|
|
4442
|
+
return this.bakePointShadow(drawCasters, () => map.bake(gl, light.x, light.y, light.z, light.radius, this.drawPointShadowFace, light.shadowNear, light.sourceRadius, maxFaces, this.pointGlass));
|
|
4178
4443
|
}
|
|
4179
4444
|
/**
|
|
4180
4445
|
* Draw into a rectangle of the canvas, on its own terms.
|
|
@@ -4385,6 +4650,10 @@ export class WebGL2Renderer {
|
|
|
4385
4650
|
*
|
|
4386
4651
|
* What it gives up is one frame of history: the frame after a cut has no blur and no temporal
|
|
4387
4652
|
* blend, as the first frame of a session has none. Nothing else is reset.
|
|
4653
|
+
*
|
|
4654
|
+
* **A reconstructing renderer depends on it**: its history is the last shot's, and without this
|
|
4655
|
+
* call a cut blends that shot into the first frames of the next. Nothing detects a cut on the
|
|
4656
|
+
* caller's behalf, deliberately — a fast pan and a cut look alike from here.
|
|
4388
4657
|
*/
|
|
4389
4658
|
cameraCut() {
|
|
4390
4659
|
this.hasPreviousView = false;
|
|
@@ -4943,9 +5212,19 @@ export class WebGL2Renderer {
|
|
|
4943
5212
|
* its early exit on it — so this is present every jittered frame rather than absent on some,
|
|
4944
5213
|
* and the pass still runs and fills the history for the next one.
|
|
4945
5214
|
*/
|
|
5215
|
+
/* The anti-flicker's same-surface test reads depths in metres, as the occlusion does. */
|
|
5216
|
+
if (this.frameProjection !== null) {
|
|
5217
|
+
mat4.invert(this.taaInvProjection, this.frameProjection);
|
|
5218
|
+
this.taaDepthToViewZ[0] = this.taaInvProjection[10] ?? 0;
|
|
5219
|
+
this.taaDepthToViewZ[1] = this.taaInvProjection[14] ?? 0;
|
|
5220
|
+
this.taaDepthToViewZ[2] = this.taaInvProjection[11] ?? 0;
|
|
5221
|
+
this.taaDepthToViewZ[3] = this.taaInvProjection[15] ?? 1;
|
|
5222
|
+
}
|
|
4946
5223
|
temporal = {
|
|
4947
5224
|
reprojection: this.reprojection,
|
|
4948
5225
|
blend: this.temporalHistoryUsable ? TEMPORAL_HISTORY_BLEND : 0,
|
|
5226
|
+
periodStart: this.temporalHistory.periodStart,
|
|
5227
|
+
depthToViewZ: this.taaDepthToViewZ,
|
|
4949
5228
|
};
|
|
4950
5229
|
}
|
|
4951
5230
|
/*
|
|
@@ -5090,8 +5369,9 @@ export class WebGL2Renderer {
|
|
|
5090
5369
|
* flat paint with the effect on. The opaque frame is finished here, which is what a pane
|
|
5091
5370
|
* should show; the latch then serves every pane in the replay. Only when something refracts.
|
|
5092
5371
|
*/
|
|
5093
|
-
if (this.translucentQueue.refracts)
|
|
5094
|
-
this.sceneTarget?.snapshotColor();
|
|
5372
|
+
if (this.translucentQueue.refracts) {
|
|
5373
|
+
this.sceneTarget?.snapshotColor(false, this.translucentQueue.glassy);
|
|
5374
|
+
}
|
|
5095
5375
|
this.oit.accumulateOit(this.canvas.width, this.canvas.height, oitDepth, (weighted) => {
|
|
5096
5376
|
this.oitReplaying = true;
|
|
5097
5377
|
this.useFlatProgram();
|
|
@@ -5108,6 +5388,7 @@ export class WebGL2Renderer {
|
|
|
5108
5388
|
options.refraction = draw.refraction;
|
|
5109
5389
|
options.refractTint = draw.refractTint;
|
|
5110
5390
|
options.thicknessM = draw.thicknessM;
|
|
5391
|
+
options.glass = draw.glass ?? undefined;
|
|
5111
5392
|
this.drawTranslucentMesh(draw.mesh, draw.model, draw.opacity, options);
|
|
5112
5393
|
});
|
|
5113
5394
|
/* Back to zero for every other draw in the frame, exactly as `uOpacity` and `uTint` are. */
|
|
@@ -5157,9 +5438,7 @@ export class WebGL2Renderer {
|
|
|
5157
5438
|
/* The placeholder rather than null where a map is merely absent, for the reason
|
|
5158
5439
|
`emptyTexture.ts` gives. It is black, which as a depth is an occluder at the light,
|
|
5159
5440
|
so it is safe only because `sunShadow` is 0 whenever these maps do not exist. */
|
|
5160
|
-
|
|
5161
|
-
peeledShadowMap: this.peeledShadowMap?.texture ?? this.emptyTexture2D,
|
|
5162
|
-
dynamicShadowMap: this.dynamicShadowMap?.texture ?? this.emptyTexture2D,
|
|
5441
|
+
sunShadows: this.sunShadows?.texture ?? this.emptyTexture2DArray,
|
|
5163
5442
|
peeledEnabled: this.peelFilled,
|
|
5164
5443
|
});
|
|
5165
5444
|
if (marched) {
|
|
@@ -5462,9 +5741,11 @@ export class WebGL2Renderer {
|
|
|
5462
5741
|
gl.activeTexture(gl.TEXTURE0 + REFRACT_SCENE_TEXTURE_UNIT);
|
|
5463
5742
|
gl.bindTexture(gl.TEXTURE_2D, this.emptyTexture2D);
|
|
5464
5743
|
gl.uniform1i(u['uRefractScene'] ?? null, REFRACT_SCENE_TEXTURE_UNIT);
|
|
5465
|
-
|
|
5744
|
+
/* Neither refracting nor glass, which is every draw that does not say otherwise. See
|
|
5745
|
+
`bindSeeThrough` for the four numbers in the one vector. */
|
|
5746
|
+
gl.uniform4f(u['uSeeThrough'] ?? null, 0, 0, 0, 0);
|
|
5466
5747
|
gl.uniform3fv(u['uRefractTint'] ?? null, WHITE_TINT);
|
|
5467
|
-
gl.
|
|
5748
|
+
gl.uniform3fv(u['uGlassTint'] ?? null, WHITE_TINT);
|
|
5468
5749
|
gl.uniform3fv(u['uDirectionalDir'] ?? null, env.directionalDir);
|
|
5469
5750
|
gl.uniform3fv(u['uDirectionalColor'] ?? null, env.directionalColor);
|
|
5470
5751
|
gl.uniform3fv(u['uAmbient'] ?? null, env.ambient);
|
|
@@ -5560,7 +5841,7 @@ export class WebGL2Renderer {
|
|
|
5560
5841
|
* its own copy of this lookup; the two backends have to project the same way.
|
|
5561
5842
|
*/
|
|
5562
5843
|
gl.uniformMatrix4fv(u['uLightViewProj'] ?? null, false, env.lightViewProj);
|
|
5563
|
-
gl.uniform1f(u['uShadowStrength'] ?? null, this.
|
|
5844
|
+
gl.uniform1f(u['uShadowStrength'] ?? null, this.sunShadows === null ? 0 : env.shadowStrength);
|
|
5564
5845
|
gl.uniform1f(u['uShadowMapSize'] ?? null, this.quality.directionalShadowMapSize);
|
|
5565
5846
|
gl.uniform1f(u['uShadowDepthSpan'] ?? null, env.shadowDepthSpan);
|
|
5566
5847
|
gl.uniform1f(u['uShadowMaxDistance'] ?? null, this.quality.directionalShadowMaxDistance);
|
|
@@ -5585,14 +5866,12 @@ export class WebGL2Renderer {
|
|
|
5585
5866
|
*/
|
|
5586
5867
|
if (this.quality.directionalShadows) {
|
|
5587
5868
|
gl.activeTexture(gl.TEXTURE0);
|
|
5588
|
-
gl.bindTexture(gl.
|
|
5589
|
-
gl.uniform1i(u['
|
|
5590
|
-
|
|
5591
|
-
gl.
|
|
5592
|
-
gl.
|
|
5593
|
-
gl.
|
|
5594
|
-
gl.bindTexture(gl.TEXTURE_2D, this.dynamicShadowMap?.texture ?? this.emptyTexture2D);
|
|
5595
|
-
gl.uniform1i(u['uDynamicShadowMap'] ?? null, 2);
|
|
5869
|
+
gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.sunShadows?.texture ?? this.emptyTexture2DArray);
|
|
5870
|
+
gl.uniform1i(u['uSunShadows'] ?? null, 0);
|
|
5871
|
+
/* What the sun keeps through glass, or the one-texel stand-in the lookup reads as none. */
|
|
5872
|
+
gl.activeTexture(gl.TEXTURE0 + SUN_GLASS_TINT_TEXTURE_UNIT);
|
|
5873
|
+
gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.sunGlassTint?.texture ?? this.emptyTexture2DArray);
|
|
5874
|
+
gl.uniform1i(u['uSunGlassTints'] ?? null, SUN_GLASS_TINT_TEXTURE_UNIT);
|
|
5596
5875
|
}
|
|
5597
5876
|
if (this.pointShadows === null) {
|
|
5598
5877
|
/*
|
|
@@ -5605,6 +5884,10 @@ export class WebGL2Renderer {
|
|
|
5605
5884
|
else {
|
|
5606
5885
|
/* Chosen in `pointShadowSystem.ts` and bound here; see `resolve` for the split. */
|
|
5607
5886
|
this.pointShadows.resolve(this.resolvedPointShadows);
|
|
5887
|
+
/* As the array stores them: 2L once glass has interleaved it. See `strideLayers`. */
|
|
5888
|
+
const stride = this.pointShadowArray?.stride ?? 1;
|
|
5889
|
+
strideLayers(this.resolvedPointShadows.layers, stride);
|
|
5890
|
+
strideLayers(this.resolvedPointShadows.liveLayers, stride);
|
|
5608
5891
|
this.bindPointShadows(u, DIRECTIONAL_SHADOW_UNITS);
|
|
5609
5892
|
/*
|
|
5610
5893
|
* The rectangles' layers, into the array `bindPointShadows` just put on its unit. Resolved
|
|
@@ -5810,11 +6093,12 @@ export class WebGL2Renderer {
|
|
|
5810
6093
|
*/
|
|
5811
6094
|
tint = null,
|
|
5812
6095
|
/**
|
|
5813
|
-
* Where the mesh was last frame
|
|
6096
|
+
* Where the mesh was last frame — a `Mover` or a matrix — which this backend takes and ignores.
|
|
5814
6097
|
*
|
|
5815
6098
|
* Accepted rather than absent so that one scene draws through both renderers unchanged — the
|
|
5816
6099
|
* parity rule this repository is built on. Reconstruction is WebGPU's, having no compute stage
|
|
5817
|
-
* here, so there is no motion target for this to be written into
|
|
6100
|
+
* here, so there is no motion target for this to be written into, and a mover passed here is
|
|
6101
|
+
* neither read nor advanced.
|
|
5818
6102
|
*/
|
|
5819
6103
|
_previousModel = null) {
|
|
5820
6104
|
if (this.contextLost)
|
|
@@ -6068,19 +6352,7 @@ export class WebGL2Renderer {
|
|
|
6068
6352
|
* translucent one rather than sampling a black texture and painting the pane the colour of a
|
|
6069
6353
|
* hole. That is the defined state the two-backends rule asks for instead of a silent no-op.
|
|
6070
6354
|
*/
|
|
6071
|
-
const
|
|
6072
|
-
let refracting = false;
|
|
6073
|
-
if (refraction > 0) {
|
|
6074
|
-
const snapshot = this.sceneTarget?.snapshotColor() ?? null;
|
|
6075
|
-
if (snapshot !== null) {
|
|
6076
|
-
refracting = true;
|
|
6077
|
-
gl.activeTexture(gl.TEXTURE0 + REFRACT_SCENE_TEXTURE_UNIT);
|
|
6078
|
-
gl.bindTexture(gl.TEXTURE_2D, snapshot);
|
|
6079
|
-
gl.uniform1f(u['uRefractStrength'] ?? null, refraction);
|
|
6080
|
-
gl.uniform3fv(u['uRefractTint'] ?? null, options.refractTint ?? WHITE_TINT);
|
|
6081
|
-
gl.uniform1f(u['uRefractThickness'] ?? null, options.thicknessM ?? 0);
|
|
6082
|
-
}
|
|
6083
|
-
}
|
|
6355
|
+
const refracting = this.bindSeeThrough(u, options);
|
|
6084
6356
|
/*
|
|
6085
6357
|
* **The blend and the depth mask belong to whoever owns the pass.** In the ordinary case that
|
|
6086
6358
|
* is this draw; during an order-independent replay it is `OitPass`, which has set an additive
|
|
@@ -6130,6 +6402,8 @@ export class WebGL2Renderer {
|
|
|
6130
6402
|
gl.uniform1f(u['uOpacity'] ?? null, 1);
|
|
6131
6403
|
if (tint !== null)
|
|
6132
6404
|
gl.uniform3fv(u['uTint'] ?? null, WHITE_TINT);
|
|
6405
|
+
if (refracting)
|
|
6406
|
+
this.unbindSeeThrough(u);
|
|
6133
6407
|
/* Put back for whatever is drawn next, exactly as `uOpacity` and `uTint` are — a pass
|
|
6134
6408
|
cannot inherit a material from the draw before it. */
|
|
6135
6409
|
if (!lit)
|
|
@@ -6466,14 +6740,8 @@ export class WebGL2Renderer {
|
|
|
6466
6740
|
gl.uniform1f(u['uShadowMapSize'] ?? null, this.quality.directionalShadowMapSize);
|
|
6467
6741
|
gl.uniform1i(u['uPeeledShadowEnabled'] ?? null, this.peelFilled ? 1 : 0);
|
|
6468
6742
|
gl.activeTexture(gl.TEXTURE0);
|
|
6469
|
-
gl.bindTexture(gl.
|
|
6470
|
-
gl.uniform1i(u['
|
|
6471
|
-
gl.activeTexture(gl.TEXTURE1);
|
|
6472
|
-
gl.bindTexture(gl.TEXTURE_2D, this.peeledShadowMap?.texture ?? this.emptyTexture2D);
|
|
6473
|
-
gl.uniform1i(u['uPeeledShadowMap'] ?? null, 1);
|
|
6474
|
-
gl.activeTexture(gl.TEXTURE2);
|
|
6475
|
-
gl.bindTexture(gl.TEXTURE_2D, this.dynamicShadowMap?.texture ?? this.emptyTexture2D);
|
|
6476
|
-
gl.uniform1i(u['uDynamicShadowMap'] ?? null, 2);
|
|
6743
|
+
gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.sunShadows?.texture ?? this.emptyTexture2DArray);
|
|
6744
|
+
gl.uniform1i(u['uSunShadows'] ?? null, 0);
|
|
6477
6745
|
}
|
|
6478
6746
|
}
|
|
6479
6747
|
gl.uniform3fv(u['uCameraLocal'] ?? null, cameraLocal);
|