@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
|
@@ -13,6 +13,9 @@ import {
|
|
|
13
13
|
glShadowDepthFunc,
|
|
14
14
|
} from '../../depthConvention.ts';
|
|
15
15
|
import type { FrameView } from '../../frameView.ts';
|
|
16
|
+
import type { Mover } from '../../recon/mover.ts';
|
|
17
|
+
import { resolveGlass } from '../../glass.ts';
|
|
18
|
+
import type { GlassOptions, ResolvedGlass } from '../../glass.ts';
|
|
16
19
|
import type { ClipControlExtension } from '../../depthConvention.ts';
|
|
17
20
|
import type { ReadonlyMat4 } from 'gl-matrix';
|
|
18
21
|
import type { Camera } from '../../camera.ts';
|
|
@@ -148,12 +151,16 @@ import {
|
|
|
148
151
|
DEPTH_SKINNED_VERT,
|
|
149
152
|
DEPTH_VERT,
|
|
150
153
|
} from '../../shaders/depth.ts';
|
|
151
|
-
import { PointShadowArray } from '../../pointShadowArray.ts';
|
|
152
|
-
import {
|
|
154
|
+
import { OCTAHEDRAL_EDGE, PointShadowArray, strideLayers } from '../../pointShadowArray.ts';
|
|
155
|
+
import { SunShadowArray, type SunShadowLayer } from '../../shadowMap.ts';
|
|
153
156
|
import { POINT_SHADOW_SAMPLER, PointShadowSystem } from '../../pointShadowSystem.ts';
|
|
154
157
|
import type { ShadowLight } from '../../pointShadowSystem.ts';
|
|
158
|
+
import { castsDepth, glassOf } from '../../shadowCasters.ts';
|
|
159
|
+
import { GlassCasterList, type GlassReplaySink } from '../../glassCasters.ts';
|
|
160
|
+
import { SunGlassTint } from '../../sunGlassTint.ts';
|
|
161
|
+
import { GlassTintPrograms, type GlassTintProgram } from './glassTintPrograms.ts';
|
|
155
162
|
import type { SceneCasterMaterial, ShadowCasterSink, ShadowCasters } from '../../shadowCasters.ts';
|
|
156
|
-
import { FACE_COUNT, PointShadowMap } from '../../pointShadowMap.ts';
|
|
163
|
+
import { FACE_COUNT, PointShadowMap, type PointGlassDraw } from '../../pointShadowMap.ts';
|
|
157
164
|
import { createResolvedPointShadows, DEFAULT_SOURCE_RADIUS } from '../../pointShadowImage.ts';
|
|
158
165
|
import {
|
|
159
166
|
createBakeScratch,
|
|
@@ -162,6 +169,8 @@ import {
|
|
|
162
169
|
} from '../../pointShadowBudget.ts';
|
|
163
170
|
import {
|
|
164
171
|
DIRECTIONAL_SHADOW_UNITS,
|
|
172
|
+
SUN_GLASS_TINT_TEXTURE_UNIT,
|
|
173
|
+
POINT_GLASS_TINT_TEXTURE_UNIT,
|
|
165
174
|
LIVE_POINT_SHADOW_MAPS,
|
|
166
175
|
MAX_POINT_LIGHTS,
|
|
167
176
|
NORMAL_TEXTURE_UNIT,
|
|
@@ -780,6 +789,13 @@ export interface TranslucentMeshOptions {
|
|
|
780
789
|
* clear, and it is the whole difference between this and a flat tint.
|
|
781
790
|
*/
|
|
782
791
|
readonly thicknessM?: number;
|
|
792
|
+
/**
|
|
793
|
+
* Glass: the pane shows what is behind it, by how much it lets through and how little it reflects
|
|
794
|
+
* at the view angle, blurred by how frosted it is, tinted, and glowing with the lights behind it.
|
|
795
|
+
* It keeps its own lighting and highlight, which `refraction` alone replaces. Absent, or letting
|
|
796
|
+
* nothing through, is not glass. See `glass.ts`; `refraction` still bends what a pane shows.
|
|
797
|
+
*/
|
|
798
|
+
readonly glass?: GlassOptions;
|
|
783
799
|
/**
|
|
784
800
|
* Whether this draw writes depth. `true`, which is what it has always done, unless a caller
|
|
785
801
|
* says otherwise.
|
|
@@ -1114,6 +1130,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
1114
1130
|
private skinPaletteSet = false;
|
|
1115
1131
|
|
|
1116
1132
|
private readonly emptyTexture2D: WebGLTexture;
|
|
1133
|
+
/** One glass resolution reused by every draw, so a pane allocates nothing. See `glass.ts`. */
|
|
1134
|
+
private readonly glassScratch: ResolvedGlass = { transmission: 0, frost: 0, tint: [1, 1, 1] };
|
|
1117
1135
|
/** DriftLight's stand-ins while no field is whole: no brick anywhere, and no light. */
|
|
1118
1136
|
private readonly emptyDriftIndex: WebGLTexture;
|
|
1119
1137
|
private readonly emptyDriftAtlas: WebGLTexture;
|
|
@@ -1186,6 +1204,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
1186
1204
|
refraction: number;
|
|
1187
1205
|
refractTint: Vec3 | undefined;
|
|
1188
1206
|
thicknessM: number;
|
|
1207
|
+
glass: GlassOptions | undefined;
|
|
1189
1208
|
} = {
|
|
1190
1209
|
lit: true,
|
|
1191
1210
|
fog: true,
|
|
@@ -1196,6 +1215,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
1196
1215
|
refraction: 0,
|
|
1197
1216
|
refractTint: undefined,
|
|
1198
1217
|
thicknessM: 0,
|
|
1218
|
+
glass: undefined,
|
|
1199
1219
|
};
|
|
1200
1220
|
|
|
1201
1221
|
private motionBlurScale = 1;
|
|
@@ -1263,6 +1283,9 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
1263
1283
|
* Scratch, filled in `endFrame`, which may not allocate.
|
|
1264
1284
|
*/
|
|
1265
1285
|
private frameProjection: ReadonlyMat4 | null = null;
|
|
1286
|
+
/** The temporal resolve's depth-to-metres row, from the inverse of `frameProjection`. */
|
|
1287
|
+
private readonly taaInvProjection = mat4.create();
|
|
1288
|
+
private readonly taaDepthToViewZ = Float32Array.of(0, 0, 0, 1);
|
|
1266
1289
|
private readonly aoInvProjection = mat4.create();
|
|
1267
1290
|
private readonly aoProjScale = new Float32Array(2);
|
|
1268
1291
|
/**
|
|
@@ -1321,9 +1344,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
1321
1344
|
private readonly depthInstancedCutoutUniforms: Record<string, WebGLUniformLocation>;
|
|
1322
1345
|
private readonly scatterDepthProgram: WebGLProgram;
|
|
1323
1346
|
private readonly scatterDepthUniforms: Record<string, WebGLUniformLocation>;
|
|
1324
|
-
|
|
1325
|
-
private readonly
|
|
1326
|
-
private readonly dynamicShadowMap: ShadowMap | null;
|
|
1347
|
+
/** The sun's static, moving and peeled maps, as layers of one array. See `shadowMap.ts`. */
|
|
1348
|
+
private readonly sunShadows: SunShadowArray | null;
|
|
1327
1349
|
private readonly pointShadows: PointShadowSystem<PointShadowMap> | null;
|
|
1328
1350
|
/**
|
|
1329
1351
|
* The layers a world's casting rectangles hold, above the point pool in the same array.
|
|
@@ -1548,7 +1570,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
1548
1570
|
/** Non-zero while the drawing buffer is pinned; see `lockDrawingBuffer`. */
|
|
1549
1571
|
private lockedWidth = 0;
|
|
1550
1572
|
private lockedHeight = 0;
|
|
1551
|
-
private
|
|
1573
|
+
private activeSunLayer: SunShadowLayer | null = null;
|
|
1552
1574
|
private reflectionPassActive = false;
|
|
1553
1575
|
private reflectionReadyThisFrame = false;
|
|
1554
1576
|
private reflectionAtmosphereY = 0;
|
|
@@ -1572,7 +1594,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
1572
1594
|
*
|
|
1573
1595
|
* **Not whether the map exists.** That was the test here, on the belief that a GL texture is
|
|
1574
1596
|
* born reading as the far plane; it is born zeroed, and a scene that built the layer and never
|
|
1575
|
-
* peeled it sampled an occluder at the light. `
|
|
1597
|
+
* peeled it sampled an occluder at the light. `SunShadowArray` clears itself at birth, so an
|
|
1576
1598
|
* unfilled layer would read as lit either way — this is what keeps the two backends one
|
|
1577
1599
|
* decision, and a frame that does not peel from paying a fetch per tap for nothing.
|
|
1578
1600
|
*/
|
|
@@ -1599,8 +1621,34 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
1599
1621
|
* Built once at construction, arrow properties closing over `this`: a sink created
|
|
1600
1622
|
* per pass would allocate six objects per light per frame.
|
|
1601
1623
|
*/
|
|
1624
|
+
/** What the sun's light keeps through glass, made the first time glass casts: `sunGlassTint.ts`. */
|
|
1625
|
+
private sunGlassTint: SunGlassTint | null = null;
|
|
1626
|
+
/** The five programs a pane casts its colour through, compiled the first time glass casts. */
|
|
1627
|
+
private glassTintPrograms: GlassTintPrograms | null = null;
|
|
1628
|
+
/** True while the glass list is replayed into the glass depth layer through `casterSink`. */
|
|
1629
|
+
private drawingGlassDepth = false;
|
|
1630
|
+
/** How many times a shadow array grew its glass layers: once per family, and never again. */
|
|
1631
|
+
private sunGlassRebuilds = 0;
|
|
1632
|
+
/** The light a tint pass is drawn for, and one pane's colour and clarity: uploaded per entry. */
|
|
1633
|
+
private readonly glassLight = new Float32Array(4);
|
|
1634
|
+
private readonly glassPane = new Float32Array(4);
|
|
1635
|
+
/** The glass the open shadow pass was offered, kept for its own passes: `glassCasters.ts`. */
|
|
1636
|
+
private readonly glassCasters = new GlassCasterList();
|
|
1637
|
+
/** One resolution every glass caster is read through, so recording one allocates nothing. */
|
|
1638
|
+
private readonly glassCasterScratch: ResolvedGlass = {
|
|
1639
|
+
transmission: 0,
|
|
1640
|
+
frost: 0,
|
|
1641
|
+
tint: [1, 1, 1],
|
|
1642
|
+
};
|
|
1602
1643
|
private readonly casterSink: ShadowCasterSink = {
|
|
1603
1644
|
mesh: (mesh, model, material) => {
|
|
1645
|
+
if (!this.drawingGlassDepth && !castsDepth(material)) {
|
|
1646
|
+
/* Glass: kept aside for its own two passes rather than dropped. See `glassCasters.ts`. */
|
|
1647
|
+
if (this.quality.glassShadows !== 'off' && glassOf(material, this.glassCasterScratch)) {
|
|
1648
|
+
this.glassCasters.recordMesh(mesh, model, material, this.glassCasterScratch);
|
|
1649
|
+
}
|
|
1650
|
+
return;
|
|
1651
|
+
}
|
|
1604
1652
|
this.shadowDrawBudget.ask();
|
|
1605
1653
|
const { gl } = this;
|
|
1606
1654
|
/* A two-sided caster casts from whichever face the light sees: nothing culled. */
|
|
@@ -1636,6 +1684,12 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
1636
1684
|
* columns the batch's own buffer supplies.
|
|
1637
1685
|
*/
|
|
1638
1686
|
instanced: (batch, data, material) => {
|
|
1687
|
+
if (!this.drawingGlassDepth && !castsDepth(material)) {
|
|
1688
|
+
if (this.quality.glassShadows !== 'off' && glassOf(material, this.glassCasterScratch)) {
|
|
1689
|
+
this.glassCasters.recordInstanced(batch, data, material, this.glassCasterScratch);
|
|
1690
|
+
}
|
|
1691
|
+
return;
|
|
1692
|
+
}
|
|
1639
1693
|
const { gl } = this;
|
|
1640
1694
|
const gpuBatch = batch as InstancedBatch;
|
|
1641
1695
|
const count = Math.min(data.count, gpuBatch.capacity);
|
|
@@ -1662,7 +1716,13 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
1662
1716
|
if (twoSided && this.depthPassCullsFaces) gl.enable(gl.CULL_FACE);
|
|
1663
1717
|
gl.useProgram(this.depthProgram);
|
|
1664
1718
|
},
|
|
1665
|
-
skinnedMesh: (mesh, model, palette) => {
|
|
1719
|
+
skinnedMesh: (mesh, model, palette, material) => {
|
|
1720
|
+
if (!this.drawingGlassDepth && !castsDepth(material)) {
|
|
1721
|
+
if (this.quality.glassShadows !== 'off' && glassOf(material, this.glassCasterScratch)) {
|
|
1722
|
+
this.glassCasters.recordSkinned(mesh, model, palette, material, this.glassCasterScratch);
|
|
1723
|
+
}
|
|
1724
|
+
return;
|
|
1725
|
+
}
|
|
1666
1726
|
this.shadowDrawBudget.ask();
|
|
1667
1727
|
const { gl } = this;
|
|
1668
1728
|
const u = this.depthSkinnedUniforms;
|
|
@@ -1745,9 +1805,60 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
1745
1805
|
gl.useProgram(this.depthProgram);
|
|
1746
1806
|
gl.uniform1i(this.depthUniforms['uPeelShadowLayer'] ?? null, 0);
|
|
1747
1807
|
gl.uniformMatrix4fv(this.depthUniforms['uLightViewProj'] ?? null, false, faceViewProj);
|
|
1808
|
+
/* A face is an enumeration of its own: its glass is its own. */
|
|
1809
|
+
this.glassCasters.clear();
|
|
1748
1810
|
casters(this.casterSink);
|
|
1811
|
+
/*
|
|
1812
|
+
* The first face to offer glass interleaves the array, before its opaque depth is resolved:
|
|
1813
|
+
* `PointShadowArray.growForGlass`. Nothing baked before this saw glass, so nothing re-bakes.
|
|
1814
|
+
*/
|
|
1815
|
+
if (this.glassCasters.count > 0 && this.quality.glassShadows !== 'off') {
|
|
1816
|
+
const tintEdge =
|
|
1817
|
+
this.quality.glassShadows === 'half' ? OCTAHEDRAL_EDGE >> 1 : OCTAHEDRAL_EDGE;
|
|
1818
|
+
this.pointShadowArray?.growForGlass(gl, tintEdge);
|
|
1819
|
+
}
|
|
1820
|
+
};
|
|
1821
|
+
|
|
1822
|
+
/**
|
|
1823
|
+
* A lamp's glass, after each face's opaque depth: `PointShadowMap.bake` asks and binds, this
|
|
1824
|
+
* draws, through the same sinks the sun's glass takes and in the face's own matrix.
|
|
1825
|
+
*/
|
|
1826
|
+
private readonly pointGlassDraw: PointGlassDraw = {
|
|
1827
|
+
offered: () => this.glassCasters.count > 0,
|
|
1828
|
+
draw: (pass, x, y, z) => {
|
|
1829
|
+
const { gl } = this;
|
|
1830
|
+
if (pass === 'depth') {
|
|
1831
|
+
/* Nothing on the unit the depth programs declare a previous map on: see `flushSunGlass`. */
|
|
1832
|
+
gl.activeTexture(gl.TEXTURE0);
|
|
1833
|
+
gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.emptyTexture2DArray);
|
|
1834
|
+
this.activeDepthPeel = false;
|
|
1835
|
+
gl.useProgram(this.depthProgram);
|
|
1836
|
+
gl.uniform1i(this.depthUniforms['uPeelShadowLayer'] ?? null, 0);
|
|
1837
|
+
gl.uniformMatrix4fv(
|
|
1838
|
+
this.depthUniforms['uLightViewProj'] ?? null,
|
|
1839
|
+
false,
|
|
1840
|
+
this.activeDepthViewProj ?? IDENTITY_MAT4,
|
|
1841
|
+
);
|
|
1842
|
+
this.drawingGlassDepth = true;
|
|
1843
|
+
this.glassCasters.replay(this.glassDepthSink);
|
|
1844
|
+
this.drawingGlassDepth = false;
|
|
1845
|
+
return;
|
|
1846
|
+
}
|
|
1847
|
+
this.glassTintPrograms ??= new GlassTintPrograms(gl);
|
|
1848
|
+
/* A place, w 1: the tint takes each pane's angle to the lamp from its own position. */
|
|
1849
|
+
this.glassLight[0] = x;
|
|
1850
|
+
this.glassLight[1] = y;
|
|
1851
|
+
this.glassLight[2] = z;
|
|
1852
|
+
this.glassLight[3] = 1;
|
|
1853
|
+
this.glassCasters.replay(this.glassTintSink);
|
|
1854
|
+
},
|
|
1749
1855
|
};
|
|
1750
1856
|
|
|
1857
|
+
/** The glass hook a bake is handed: none at all when glass shadows are off. */
|
|
1858
|
+
private get pointGlass(): PointGlassDraw | null {
|
|
1859
|
+
return this.quality.glassShadows === 'off' ? null : this.pointGlassDraw;
|
|
1860
|
+
}
|
|
1861
|
+
|
|
1751
1862
|
/**
|
|
1752
1863
|
* Viewport in CSS pixels rather than device pixels.
|
|
1753
1864
|
*
|
|
@@ -2713,20 +2824,18 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
2713
2824
|
gl.uniform1i(uniforms['uCutoutMap'] ?? null, SURFACE_TEXTURE_UNIT);
|
|
2714
2825
|
}
|
|
2715
2826
|
gl.useProgram(null);
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
this.dynamicShadowMap = this.quality.directionalShadows
|
|
2729
|
-
? new ShadowMap(gl, this.quality.directionalShadowMapSize)
|
|
2827
|
+
/*
|
|
2828
|
+
* The static world, its movers and — where more than one static depth layer is asked for —
|
|
2829
|
+
* the peel, as layers of one array. Movers get a layer of their own so a moving and a static
|
|
2830
|
+
* caster that overlap keep both distance fades: their transmissions multiply rather than one
|
|
2831
|
+
* depth replacing the other. The peel keeps the second static occluder the same way.
|
|
2832
|
+
*/
|
|
2833
|
+
this.sunShadows = this.quality.directionalShadows
|
|
2834
|
+
? new SunShadowArray(
|
|
2835
|
+
gl,
|
|
2836
|
+
this.quality.directionalShadowMapSize,
|
|
2837
|
+
this.quality.directionalShadowDepthLayers > 1,
|
|
2838
|
+
)
|
|
2730
2839
|
: null;
|
|
2731
2840
|
// Static maps are allocated once the world reports its light count. No
|
|
2732
2841
|
// cubemap resources exist at all when the quality profile disables them.
|
|
@@ -2918,9 +3027,9 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
2918
3027
|
* every one of them, per renderer, for as long as it lived; `renderer.test.ts` counts them.
|
|
2919
3028
|
*/
|
|
2920
3029
|
this.sceneTarget?.dispose();
|
|
2921
|
-
this.
|
|
2922
|
-
this.
|
|
2923
|
-
this.
|
|
3030
|
+
this.sunShadows?.dispose(gl);
|
|
3031
|
+
this.sunGlassTint?.dispose(gl);
|
|
3032
|
+
this.glassTintPrograms?.dispose(gl);
|
|
2924
3033
|
this.planarReflection?.dispose(gl);
|
|
2925
3034
|
this.pointShadows?.dispose();
|
|
2926
3035
|
gl.deleteVertexArray(this.panelVao);
|
|
@@ -3603,8 +3712,10 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
3603
3712
|
const lit = options.lit ?? true;
|
|
3604
3713
|
const fog = options.fog ?? true;
|
|
3605
3714
|
const toneMapped = options.toneMapped ?? true;
|
|
3606
|
-
/*
|
|
3607
|
-
|
|
3715
|
+
/* A blended batch shows what is behind it — refraction or glass — as a single draw does, and
|
|
3716
|
+
counts a material of its own for it by the rule both backends share (`materialChanges.ts`). */
|
|
3717
|
+
const refracting = blend && this.bindSeeThrough(u, options);
|
|
3718
|
+
const own = ownsMaterial({ opacity, lit, fog, toneMapped, refracting });
|
|
3608
3719
|
if (own) this.materials.dirty();
|
|
3609
3720
|
this.takeMaterial();
|
|
3610
3721
|
const depthWrite = options.depthWrite ?? true;
|
|
@@ -3624,20 +3735,9 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
3624
3735
|
* **A null snapshot leaves the strength at zero**, so the draw shades as an ordinary
|
|
3625
3736
|
* translucent one rather than sampling a black texture and painting the pane the colour of a
|
|
3626
3737
|
* hole. That is the defined state the two-backends rule asks for instead of a silent no-op.
|
|
3738
|
+
* The copy is bound above, before the material is counted, because whether it was taken is
|
|
3739
|
+
* part of what the count asks.
|
|
3627
3740
|
*/
|
|
3628
|
-
const refraction = options.refraction ?? 0;
|
|
3629
|
-
let refracting = false;
|
|
3630
|
-
if (refraction > 0) {
|
|
3631
|
-
const snapshot = this.sceneTarget?.snapshotColor() ?? null;
|
|
3632
|
-
if (snapshot !== null) {
|
|
3633
|
-
refracting = true;
|
|
3634
|
-
gl.activeTexture(gl.TEXTURE0 + REFRACT_SCENE_TEXTURE_UNIT);
|
|
3635
|
-
gl.bindTexture(gl.TEXTURE_2D, snapshot);
|
|
3636
|
-
gl.uniform1f(u['uRefractStrength'] ?? null, refraction);
|
|
3637
|
-
gl.uniform3fv(u['uRefractTint'] ?? null, options.refractTint ?? WHITE_TINT);
|
|
3638
|
-
gl.uniform1f(u['uRefractThickness'] ?? null, options.thicknessM ?? 0);
|
|
3639
|
-
}
|
|
3640
|
-
}
|
|
3641
3741
|
if (!lit) gl.uniform1i(u['uLightingEnabled'] ?? null, 0);
|
|
3642
3742
|
if (!fog) gl.uniform1i(u['uFogEnabled'] ?? null, 0);
|
|
3643
3743
|
if (!toneMapped) gl.uniform1i(u['uOutputTransform'] ?? null, Math.min(this.gradeCode(), 1));
|
|
@@ -3655,7 +3755,11 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
3655
3755
|
gl.polygonOffset(offset.slope, offset.units);
|
|
3656
3756
|
}
|
|
3657
3757
|
|
|
3758
|
+
/* Both sides of a double-sided material, as every mesh path does and WebGPU's pipeline does
|
|
3759
|
+
for both: without it a batch's far faces were culled here alone. */
|
|
3760
|
+
if (this.materialDoubleSided) gl.disable(gl.CULL_FACE);
|
|
3658
3761
|
mesh.drawInstances(gl, count);
|
|
3762
|
+
if (this.materialDoubleSided) gl.enable(gl.CULL_FACE);
|
|
3659
3763
|
|
|
3660
3764
|
if (layer > 0) {
|
|
3661
3765
|
gl.polygonOffset(0, 0);
|
|
@@ -3670,11 +3774,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
3670
3774
|
if (opacity < 1) gl.uniform1f(u['uOpacity'] ?? null, 1);
|
|
3671
3775
|
/* The binding is released with the uniform, which is the 2026-08-27 rule: a WebGL2 binding
|
|
3672
3776
|
outlives its frame, so a snapshot left on unit 14 is held against the next frame's copy. */
|
|
3673
|
-
if (refracting)
|
|
3674
|
-
gl.uniform1f(u['uRefractStrength'] ?? null, 0);
|
|
3675
|
-
gl.activeTexture(gl.TEXTURE0 + REFRACT_SCENE_TEXTURE_UNIT);
|
|
3676
|
-
gl.bindTexture(gl.TEXTURE_2D, this.emptyTexture2D);
|
|
3677
|
-
}
|
|
3777
|
+
if (refracting) this.unbindSeeThrough(u);
|
|
3678
3778
|
if (!lit) gl.uniform1i(u['uLightingEnabled'] ?? null, 1);
|
|
3679
3779
|
if (!fog) gl.uniform1i(u['uFogEnabled'] ?? null, 1);
|
|
3680
3780
|
if (!toneMapped) gl.uniform1i(u['uOutputTransform'] ?? null, this.gradeCode());
|
|
@@ -3682,6 +3782,53 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
3682
3782
|
this.useFlatProgram();
|
|
3683
3783
|
}
|
|
3684
3784
|
|
|
3785
|
+
/**
|
|
3786
|
+
* Bind what a translucent draw shows through it: the frame's copy, refraction's bend and absorption,
|
|
3787
|
+
* and glass. Returns whether the copy is bound, and so whether `unbindSeeThrough` owes it back.
|
|
3788
|
+
*
|
|
3789
|
+
* **A glass draw takes the copy even when it does not bend**, since a pane shows what is behind it
|
|
3790
|
+
* either way, and asks for the copy's mip chain, which a frosted pane reads blurred. A null copy
|
|
3791
|
+
* leaves both off, so the pane shades as ordinary translucency rather than sampling black.
|
|
3792
|
+
*
|
|
3793
|
+
* **Refused inside a probe's face or a mirror**, as WebGPU's `takeRefractSnapshot` refuses: the
|
|
3794
|
+
* copy is of the frame drawn to the screen, so a pane there would show the wrong picture, and
|
|
3795
|
+
* taking it mid-bake is how a probe's next face came to be attached to the scene's framebuffer.
|
|
3796
|
+
*/
|
|
3797
|
+
private bindSeeThrough(
|
|
3798
|
+
u: Record<string, WebGLUniformLocation>,
|
|
3799
|
+
options: TranslucentMeshOptions,
|
|
3800
|
+
): boolean {
|
|
3801
|
+
const { gl } = this;
|
|
3802
|
+
const refraction = options.refraction ?? 0;
|
|
3803
|
+
const glassy = resolveGlass(options.glass, this.glassScratch);
|
|
3804
|
+
if (refraction <= 0 && !glassy) return false;
|
|
3805
|
+
if (this.probePassActive || this.reflectionPassActive) return false;
|
|
3806
|
+
const snapshot = this.sceneTarget?.snapshotColor(false, glassy) ?? null;
|
|
3807
|
+
if (snapshot === null) return false;
|
|
3808
|
+
gl.activeTexture(gl.TEXTURE0 + REFRACT_SCENE_TEXTURE_UNIT);
|
|
3809
|
+
gl.bindTexture(gl.TEXTURE_2D, snapshot);
|
|
3810
|
+
const glass = this.glassScratch;
|
|
3811
|
+
/* Strength, path thickness, transmission, frost: one vector, for the budget the shader states. */
|
|
3812
|
+
gl.uniform4f(
|
|
3813
|
+
u['uSeeThrough'] ?? null,
|
|
3814
|
+
Math.max(refraction, 0),
|
|
3815
|
+
options.thicknessM ?? 0,
|
|
3816
|
+
glass.transmission,
|
|
3817
|
+
glass.frost,
|
|
3818
|
+
);
|
|
3819
|
+
gl.uniform3fv(u['uRefractTint'] ?? null, options.refractTint ?? WHITE_TINT);
|
|
3820
|
+
gl.uniform3fv(u['uGlassTint'] ?? null, glass.tint);
|
|
3821
|
+
return true;
|
|
3822
|
+
}
|
|
3823
|
+
|
|
3824
|
+
/** Put back what `bindSeeThrough` bound: the binding outlives its frame (the 2026-08-27 rule). */
|
|
3825
|
+
private unbindSeeThrough(u: Record<string, WebGLUniformLocation>): void {
|
|
3826
|
+
const { gl } = this;
|
|
3827
|
+
gl.uniform4f(u['uSeeThrough'] ?? null, 0, 0, 0, 0);
|
|
3828
|
+
gl.activeTexture(gl.TEXTURE0 + REFRACT_SCENE_TEXTURE_UNIT);
|
|
3829
|
+
gl.bindTexture(gl.TEXTURE_2D, this.emptyTexture2D);
|
|
3830
|
+
}
|
|
3831
|
+
|
|
3685
3832
|
disposeMesh(mesh: Mesh): void {
|
|
3686
3833
|
/*
|
|
3687
3834
|
* Nothing to release when the context is gone: it took every buffer and VAO with it,
|
|
@@ -4350,6 +4497,13 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4350
4497
|
*/
|
|
4351
4498
|
gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.pointShadowArray?.texture ?? this.emptyTexture2DArray);
|
|
4352
4499
|
gl.uniform1i(uniforms[POINT_SHADOW_SAMPLER] ?? null, firstUnit);
|
|
4500
|
+
/* What the lamps keep through glass: the one-texel stand-in, read as none, until glass casts. */
|
|
4501
|
+
gl.activeTexture(gl.TEXTURE0 + POINT_GLASS_TINT_TEXTURE_UNIT);
|
|
4502
|
+
gl.bindTexture(
|
|
4503
|
+
gl.TEXTURE_2D_ARRAY,
|
|
4504
|
+
this.pointShadowArray?.tintTexture ?? this.emptyTexture2DArray,
|
|
4505
|
+
);
|
|
4506
|
+
gl.uniform1i(uniforms['uPointGlassTints'] ?? null, POINT_GLASS_TINT_TEXTURE_UNIT);
|
|
4353
4507
|
|
|
4354
4508
|
gl.uniform1iv(uniforms['uPointShadowLayer[0]'] ?? null, resolved.layers);
|
|
4355
4509
|
/* Origin and far plane in one row; see `uPointShadowProjection` in the preamble. */
|
|
@@ -4723,21 +4877,17 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4723
4877
|
lightViewProj: ReadonlyMat4,
|
|
4724
4878
|
layer: 'static' | 'static-peel' | 'dynamic' = 'static',
|
|
4725
4879
|
): void {
|
|
4726
|
-
const
|
|
4727
|
-
|
|
4728
|
-
? this.shadowMap
|
|
4729
|
-
: layer === 'static-peel'
|
|
4730
|
-
? this.peeledShadowMap
|
|
4731
|
-
: this.dynamicShadowMap;
|
|
4732
|
-
if (shadowMap === null) {
|
|
4880
|
+
const sun = this.sunShadows;
|
|
4881
|
+
if (sun === null || (layer === 'static-peel' && sun.layers < 3)) {
|
|
4733
4882
|
this.shadowPassActive = false;
|
|
4734
|
-
this.
|
|
4883
|
+
this.activeSunLayer = null;
|
|
4735
4884
|
return;
|
|
4736
4885
|
}
|
|
4737
4886
|
const { gl } = this;
|
|
4738
4887
|
this.gpuTimer.begin('shadows');
|
|
4739
4888
|
this.shadowPassActive = true;
|
|
4740
|
-
this.
|
|
4889
|
+
this.activeSunLayer = layer;
|
|
4890
|
+
this.glassCasters.clear();
|
|
4741
4891
|
/* The static layer opens first, so a frame's peel counts from here. See `peelFilled`. */
|
|
4742
4892
|
if (layer === 'static') this.peelFilled = false;
|
|
4743
4893
|
/*
|
|
@@ -4764,16 +4914,16 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4764
4914
|
// The previous mesh pass left all directional maps bound for sampling.
|
|
4765
4915
|
// A texture cannot be sampled while it is attached to the active depth
|
|
4766
4916
|
// target, even when the shader branch would skip that sample.
|
|
4767
|
-
|
|
4768
|
-
gl.
|
|
4769
|
-
|
|
4770
|
-
shadowMap.begin(gl);
|
|
4917
|
+
gl.activeTexture(gl.TEXTURE0);
|
|
4918
|
+
gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.emptyTexture2DArray);
|
|
4919
|
+
sun.begin(gl, layer);
|
|
4771
4920
|
gl.useProgram(this.depthProgram);
|
|
4772
4921
|
const peel = layer === 'static-peel';
|
|
4773
4922
|
gl.uniform1i(this.depthUniforms['uPeelShadowLayer'] ?? null, peel ? 1 : 0);
|
|
4774
4923
|
if (peel) {
|
|
4924
|
+
/* The static layer, read while the peel is drawn aside: see `SunShadowArray`. */
|
|
4775
4925
|
gl.activeTexture(gl.TEXTURE0);
|
|
4776
|
-
gl.bindTexture(gl.
|
|
4926
|
+
gl.bindTexture(gl.TEXTURE_2D_ARRAY, sun.texture);
|
|
4777
4927
|
gl.uniform1i(this.depthUniforms['uPreviousShadowMap'] ?? null, 0);
|
|
4778
4928
|
}
|
|
4779
4929
|
/* The corrected one, matching `activeDepthViewProj` above: a pass must render its map with the
|
|
@@ -4885,18 +5035,165 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4885
5035
|
casters(this.casterSink);
|
|
4886
5036
|
}
|
|
4887
5037
|
|
|
5038
|
+
/**
|
|
5039
|
+
* Draw the pass's glass twice: where the nearest pane is, and what the panes let through.
|
|
5040
|
+
*
|
|
5041
|
+
* After the opaque casters, in the pass's own light matrix. A layer that has held glass is
|
|
5042
|
+
* redrawn even when this pass offered none, so glass that has gone takes its colour with it. The
|
|
5043
|
+
* first glass grows the array (`SunShadowArray.growForGlass`) and makes the tint target.
|
|
5044
|
+
*/
|
|
5045
|
+
private flushSunGlass(which: 'static' | 'dynamic'): void {
|
|
5046
|
+
const sun = this.sunShadows;
|
|
5047
|
+
const list = this.glassCasters;
|
|
5048
|
+
if (sun === null || this.quality.glassShadows === 'off') return;
|
|
5049
|
+
if (list.count === 0 && !sun.hasGlass) return;
|
|
5050
|
+
const { gl } = this;
|
|
5051
|
+
if (list.count > 0 && sun.growForGlass(gl)) this.sunGlassRebuilds++;
|
|
5052
|
+
|
|
5053
|
+
/* Where the nearest pane is: the depth programs, through the sink every opaque caster took. */
|
|
5054
|
+
gl.activeTexture(gl.TEXTURE0);
|
|
5055
|
+
gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.emptyTexture2DArray);
|
|
5056
|
+
sun.beginGlass(gl, which);
|
|
5057
|
+
this.activeDepthPeel = false;
|
|
5058
|
+
gl.useProgram(this.depthProgram);
|
|
5059
|
+
gl.uniform1i(this.depthUniforms['uPeelShadowLayer'] ?? null, 0);
|
|
5060
|
+
gl.uniformMatrix4fv(
|
|
5061
|
+
this.depthUniforms['uLightViewProj'] ?? null,
|
|
5062
|
+
false,
|
|
5063
|
+
this.activeDepthViewProj ?? IDENTITY_MAT4,
|
|
5064
|
+
);
|
|
5065
|
+
this.drawingGlassDepth = true;
|
|
5066
|
+
list.replay(this.glassDepthSink);
|
|
5067
|
+
this.drawingGlassDepth = false;
|
|
5068
|
+
sun.end(gl);
|
|
5069
|
+
|
|
5070
|
+
/* What the panes let through, every one on a ray, multiplied. */
|
|
5071
|
+
this.glassTintPrograms ??= new GlassTintPrograms(gl);
|
|
5072
|
+
const size = this.quality.glassShadows === 'half' ? Math.max(1, sun.size >> 1) : sun.size;
|
|
5073
|
+
const tint = (this.sunGlassTint ??= new SunGlassTint(gl, size));
|
|
5074
|
+
/*
|
|
5075
|
+
* The direction the light travels, off the light matrix's depth row: an orthographic
|
|
5076
|
+
* projection's depth grows along it. The sign is immaterial — the tint takes the cosine
|
|
5077
|
+
* absolutely — and w 0 says it is a direction rather than a place.
|
|
5078
|
+
*/
|
|
5079
|
+
const m = this.activeDepthViewProj ?? IDENTITY_MAT4;
|
|
5080
|
+
const length = Math.hypot(m[2] as number, m[6] as number, m[10] as number) || 1;
|
|
5081
|
+
this.glassLight[0] = (m[2] as number) / length;
|
|
5082
|
+
this.glassLight[1] = (m[6] as number) / length;
|
|
5083
|
+
this.glassLight[2] = (m[10] as number) / length;
|
|
5084
|
+
this.glassLight[3] = 0;
|
|
5085
|
+
tint.begin(gl, which === 'static' ? 0 : 1);
|
|
5086
|
+
list.replay(this.glassTintSink);
|
|
5087
|
+
tint.end(gl);
|
|
5088
|
+
gl.useProgram(this.depthProgram);
|
|
5089
|
+
}
|
|
5090
|
+
|
|
5091
|
+
/** The glass list into the glass depth layer: `casterSink` itself, with glass let through. */
|
|
5092
|
+
private readonly glassDepthSink: GlassReplaySink = {
|
|
5093
|
+
mesh: (mesh, model, material) => this.casterSink.mesh(mesh, model, material),
|
|
5094
|
+
instanced: (batch, data, material) => this.casterSink.instanced?.(batch, data, material),
|
|
5095
|
+
skinnedMesh: (mesh, model, palette, material) =>
|
|
5096
|
+
this.casterSink.skinnedMesh(mesh, model, palette, material),
|
|
5097
|
+
};
|
|
5098
|
+
|
|
5099
|
+
/** One pane's tint uniforms, and its cutout where it has one. */
|
|
5100
|
+
private bindGlassTint(
|
|
5101
|
+
program: GlassTintProgram,
|
|
5102
|
+
glass: ResolvedGlass,
|
|
5103
|
+
cutout: CutoutCaster<SurfaceTextureHandle> | null,
|
|
5104
|
+
): void {
|
|
5105
|
+
const { gl } = this;
|
|
5106
|
+
const u = program.uniforms;
|
|
5107
|
+
gl.useProgram(program.program);
|
|
5108
|
+
gl.uniformMatrix4fv(
|
|
5109
|
+
u['uLightViewProj'] ?? null,
|
|
5110
|
+
false,
|
|
5111
|
+
this.activeDepthViewProj ?? IDENTITY_MAT4,
|
|
5112
|
+
);
|
|
5113
|
+
this.uploadWind(u, this.frameWind);
|
|
5114
|
+
this.glassPane[0] = glass.transmission * glass.tint[0];
|
|
5115
|
+
this.glassPane[1] = glass.transmission * glass.tint[1];
|
|
5116
|
+
this.glassPane[2] = glass.transmission * glass.tint[2];
|
|
5117
|
+
this.glassPane[3] = 1 - glass.frost;
|
|
5118
|
+
gl.uniform4fv(u['uGlassPane'] ?? null, this.glassPane);
|
|
5119
|
+
gl.uniform4fv(u['uGlassLight'] ?? null, this.glassLight);
|
|
5120
|
+
if (cutout !== null) {
|
|
5121
|
+
gl.uniform2f(u['uUvScale'] ?? null, cutout.u, cutout.v);
|
|
5122
|
+
gl.uniform1f(u['uAlphaCutout'] ?? null, cutout.cutoff);
|
|
5123
|
+
(cutout.albedo as SurfaceTexture).bind(gl, SURFACE_TEXTURE_UNIT);
|
|
5124
|
+
gl.uniform1i(u['uCutoutMap'] ?? null, SURFACE_TEXTURE_UNIT);
|
|
5125
|
+
}
|
|
5126
|
+
}
|
|
5127
|
+
|
|
5128
|
+
/**
|
|
5129
|
+
* The glass list into the tint layer, through the tint programs, **culling nothing**: a light
|
|
5130
|
+
* crosses a pane whichever way the pane faces, and a closed glass object has two surfaces on a
|
|
5131
|
+
* ray, so every one counts. What that asks of a model is that a pane be one surface — two-sided
|
|
5132
|
+
* glass says so with `doubleSided`, and a second copy of its triangles is a second pane.
|
|
5133
|
+
*/
|
|
5134
|
+
private readonly glassTintSink: GlassReplaySink = {
|
|
5135
|
+
mesh: (mesh, model, material, glass) => {
|
|
5136
|
+
const programs = this.glassTintPrograms;
|
|
5137
|
+
if (programs === null) return;
|
|
5138
|
+
const { gl } = this;
|
|
5139
|
+
const cutout = cutoutOf(material);
|
|
5140
|
+
const program = cutout !== null ? programs.cutout : programs.rigid;
|
|
5141
|
+
this.bindGlassTint(program, glass, cutout);
|
|
5142
|
+
gl.uniformMatrix4fv(program.uniforms['uModel'] ?? null, false, model);
|
|
5143
|
+
gl.disable(gl.CULL_FACE);
|
|
5144
|
+
(mesh as Mesh).draw(gl);
|
|
5145
|
+
if (this.depthPassCullsFaces) gl.enable(gl.CULL_FACE);
|
|
5146
|
+
if (cutout !== null) this.restoreSurfaceTextureUnit();
|
|
5147
|
+
},
|
|
5148
|
+
instanced: (batch, data, material, glass) => {
|
|
5149
|
+
const programs = this.glassTintPrograms;
|
|
5150
|
+
if (programs === null) return;
|
|
5151
|
+
const { gl } = this;
|
|
5152
|
+
const gpuBatch = batch as InstancedBatch;
|
|
5153
|
+
const count = Math.min(data.count, gpuBatch.capacity);
|
|
5154
|
+
if (count === 0) return;
|
|
5155
|
+
const cutout = cutoutOf(material);
|
|
5156
|
+
this.bindGlassTint(
|
|
5157
|
+
cutout !== null ? programs.instancedCutout : programs.instanced,
|
|
5158
|
+
glass,
|
|
5159
|
+
cutout,
|
|
5160
|
+
);
|
|
5161
|
+
gl.disable(gl.CULL_FACE);
|
|
5162
|
+
gpuBatch.mesh.drawInstances(gl, count);
|
|
5163
|
+
if (this.depthPassCullsFaces) gl.enable(gl.CULL_FACE);
|
|
5164
|
+
if (cutout !== null) this.restoreSurfaceTextureUnit();
|
|
5165
|
+
},
|
|
5166
|
+
skinnedMesh: (mesh, model, palette, _material, glass) => {
|
|
5167
|
+
const programs = this.glassTintPrograms;
|
|
5168
|
+
if (programs === null) return;
|
|
5169
|
+
const { gl } = this;
|
|
5170
|
+
const program = programs.skinned;
|
|
5171
|
+
this.bindGlassTint(program, glass, null);
|
|
5172
|
+
gl.uniformMatrix4fv(program.uniforms['uModel'] ?? null, false, model);
|
|
5173
|
+
this.skinPalette.update(gl, palette);
|
|
5174
|
+
this.skinPalette.bind(gl, SKIN_PALETTE_TEXTURE_UNIT);
|
|
5175
|
+
gl.uniform1i(program.uniforms['uJointPalette'] ?? null, SKIN_PALETTE_TEXTURE_UNIT);
|
|
5176
|
+
gl.disable(gl.CULL_FACE);
|
|
5177
|
+
(mesh as Mesh).draw(gl);
|
|
5178
|
+
if (this.depthPassCullsFaces) gl.enable(gl.CULL_FACE);
|
|
5179
|
+
},
|
|
5180
|
+
};
|
|
5181
|
+
|
|
4888
5182
|
endShadowPass(): void {
|
|
4889
5183
|
if (this.contextLost) return;
|
|
4890
|
-
const
|
|
4891
|
-
|
|
4892
|
-
|
|
4893
|
-
|
|
5184
|
+
const sun = this.sunShadows;
|
|
5185
|
+
const layer = this.activeSunLayer;
|
|
5186
|
+
if (!this.shadowPassActive || sun === null || layer === null) return;
|
|
5187
|
+
sun.end(this.gl);
|
|
5188
|
+
if (layer === 'static-peel') this.peelFilled = true;
|
|
5189
|
+
/* The peel keeps no glass: its layer is the second opaque occluder and nothing else. */
|
|
5190
|
+
else this.flushSunGlass(layer === 'dynamic' ? 'dynamic' : 'static');
|
|
4894
5191
|
/* Back to the frame's sense; see `beginShadowPass` for why it left it. */
|
|
4895
5192
|
this.gl.depthFunc(glDepthFuncEqual(this.gl));
|
|
4896
5193
|
this.gl.clearDepth(this.depthClear);
|
|
4897
5194
|
this.gpuTimer.end();
|
|
4898
5195
|
this.shadowPassActive = false;
|
|
4899
|
-
this.
|
|
5196
|
+
this.activeSunLayer = null;
|
|
4900
5197
|
// Restore the drawing-buffer viewport the shadow pass overrode.
|
|
4901
5198
|
this.restoreViewport();
|
|
4902
5199
|
}
|
|
@@ -4935,6 +5232,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4935
5232
|
directionalShadows: this.quality.directionalShadows,
|
|
4936
5233
|
environmentProbe: this.probeCapture !== null,
|
|
4937
5234
|
nightEmissive: this.quality.nightEmissive,
|
|
5235
|
+
/* Off compiles none of the glass lookup: see `FlatShaderOptions.glassShadows`. */
|
|
5236
|
+
glassShadows: this.quality.glassShadows !== 'off',
|
|
4938
5237
|
};
|
|
4939
5238
|
const build = (budget: LightBudget): string =>
|
|
4940
5239
|
flatFrag({ ...variant, maxLights: budget.maxLights, maxAreaLights: budget.maxAreaLights });
|
|
@@ -5251,6 +5550,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
5251
5550
|
this.bakeOne(map, light, casters as ShadowCasters, maxFaces),
|
|
5252
5551
|
);
|
|
5253
5552
|
this.updateAreaShadows(areaLights, frameDt, staticCasters, dynamicCasters);
|
|
5553
|
+
/* The chain frost reads, once for the round, for the lights whose glass it drew or cleared. */
|
|
5554
|
+
this.pointShadowArray.finishTints(this.gl);
|
|
5254
5555
|
this.restoreViewport();
|
|
5255
5556
|
}
|
|
5256
5557
|
|
|
@@ -5311,6 +5612,10 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
5311
5612
|
* reason: it already holds everything it needs.
|
|
5312
5613
|
*/
|
|
5313
5614
|
set.resolve(areaLights, this.resolvedAreaShadows);
|
|
5615
|
+
/* As the array stores them: 2L once glass has interleaved it. See `strideLayers`. */
|
|
5616
|
+
const stride = this.pointShadowArray?.stride ?? 1;
|
|
5617
|
+
strideLayers(this.resolvedAreaShadows.layer, stride);
|
|
5618
|
+
strideLayers(this.resolvedAreaShadows.liveLayer, stride);
|
|
5314
5619
|
}
|
|
5315
5620
|
|
|
5316
5621
|
/**
|
|
@@ -5375,6 +5680,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
5375
5680
|
near,
|
|
5376
5681
|
DEFAULT_SOURCE_RADIUS,
|
|
5377
5682
|
maxFaces,
|
|
5683
|
+
this.pointGlass,
|
|
5378
5684
|
),
|
|
5379
5685
|
);
|
|
5380
5686
|
}
|
|
@@ -5398,6 +5704,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
5398
5704
|
light.shadowNear,
|
|
5399
5705
|
light.sourceRadius,
|
|
5400
5706
|
maxFaces,
|
|
5707
|
+
this.pointGlass,
|
|
5401
5708
|
),
|
|
5402
5709
|
);
|
|
5403
5710
|
}
|
|
@@ -5632,6 +5939,10 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
5632
5939
|
*
|
|
5633
5940
|
* What it gives up is one frame of history: the frame after a cut has no blur and no temporal
|
|
5634
5941
|
* blend, as the first frame of a session has none. Nothing else is reset.
|
|
5942
|
+
*
|
|
5943
|
+
* **A reconstructing renderer depends on it**: its history is the last shot's, and without this
|
|
5944
|
+
* call a cut blends that shot into the first frames of the next. Nothing detects a cut on the
|
|
5945
|
+
* caller's behalf, deliberately — a fast pan and a cut look alike from here.
|
|
5635
5946
|
*/
|
|
5636
5947
|
cameraCut(): void {
|
|
5637
5948
|
this.hasPreviousView = false;
|
|
@@ -6193,7 +6504,14 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
6193
6504
|
const view = this.frameViewProj;
|
|
6194
6505
|
const blur = this.quality.cameraMotionBlur * this.motionBlurScale;
|
|
6195
6506
|
let motion: { reprojection: Float32Array; strength: number; max: number } | undefined;
|
|
6196
|
-
let temporal:
|
|
6507
|
+
let temporal:
|
|
6508
|
+
| {
|
|
6509
|
+
reprojection: Float32Array;
|
|
6510
|
+
blend: number;
|
|
6511
|
+
periodStart: boolean;
|
|
6512
|
+
depthToViewZ: Float32Array;
|
|
6513
|
+
}
|
|
6514
|
+
| undefined;
|
|
6197
6515
|
/*
|
|
6198
6516
|
* **Both effects want the same one matrix**, so it is built once when either asks. The
|
|
6199
6517
|
* reprojection was previously kept only while the blur was on, which is why this reads as it
|
|
@@ -6222,9 +6540,19 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
6222
6540
|
* its early exit on it — so this is present every jittered frame rather than absent on some,
|
|
6223
6541
|
* and the pass still runs and fills the history for the next one.
|
|
6224
6542
|
*/
|
|
6543
|
+
/* The anti-flicker's same-surface test reads depths in metres, as the occlusion does. */
|
|
6544
|
+
if (this.frameProjection !== null) {
|
|
6545
|
+
mat4.invert(this.taaInvProjection, this.frameProjection);
|
|
6546
|
+
this.taaDepthToViewZ[0] = this.taaInvProjection[10] ?? 0;
|
|
6547
|
+
this.taaDepthToViewZ[1] = this.taaInvProjection[14] ?? 0;
|
|
6548
|
+
this.taaDepthToViewZ[2] = this.taaInvProjection[11] ?? 0;
|
|
6549
|
+
this.taaDepthToViewZ[3] = this.taaInvProjection[15] ?? 1;
|
|
6550
|
+
}
|
|
6225
6551
|
temporal = {
|
|
6226
6552
|
reprojection: this.reprojection as Float32Array,
|
|
6227
6553
|
blend: this.temporalHistoryUsable ? TEMPORAL_HISTORY_BLEND : 0,
|
|
6554
|
+
periodStart: this.temporalHistory.periodStart,
|
|
6555
|
+
depthToViewZ: this.taaDepthToViewZ,
|
|
6228
6556
|
};
|
|
6229
6557
|
}
|
|
6230
6558
|
|
|
@@ -6405,7 +6733,9 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
6405
6733
|
* flat paint with the effect on. The opaque frame is finished here, which is what a pane
|
|
6406
6734
|
* should show; the latch then serves every pane in the replay. Only when something refracts.
|
|
6407
6735
|
*/
|
|
6408
|
-
if (this.translucentQueue.refracts)
|
|
6736
|
+
if (this.translucentQueue.refracts) {
|
|
6737
|
+
this.sceneTarget?.snapshotColor(false, this.translucentQueue.glassy);
|
|
6738
|
+
}
|
|
6409
6739
|
this.oit.accumulateOit(this.canvas.width, this.canvas.height, oitDepth, (weighted) => {
|
|
6410
6740
|
this.oitReplaying = true;
|
|
6411
6741
|
this.useFlatProgram();
|
|
@@ -6422,6 +6752,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
6422
6752
|
options.refraction = draw.refraction;
|
|
6423
6753
|
options.refractTint = draw.refractTint as unknown as Vec3 | undefined;
|
|
6424
6754
|
options.thicknessM = draw.thicknessM;
|
|
6755
|
+
options.glass = draw.glass ?? undefined;
|
|
6425
6756
|
this.drawTranslucentMesh(draw.mesh as Mesh, draw.model, draw.opacity, options);
|
|
6426
6757
|
});
|
|
6427
6758
|
/* Back to zero for every other draw in the frame, exactly as `uOpacity` and `uTint` are. */
|
|
@@ -6484,9 +6815,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
6484
6815
|
/* The placeholder rather than null where a map is merely absent, for the reason
|
|
6485
6816
|
`emptyTexture.ts` gives. It is black, which as a depth is an occluder at the light,
|
|
6486
6817
|
so it is safe only because `sunShadow` is 0 whenever these maps do not exist. */
|
|
6487
|
-
|
|
6488
|
-
peeledShadowMap: this.peeledShadowMap?.texture ?? this.emptyTexture2D,
|
|
6489
|
-
dynamicShadowMap: this.dynamicShadowMap?.texture ?? this.emptyTexture2D,
|
|
6818
|
+
sunShadows: this.sunShadows?.texture ?? this.emptyTexture2DArray,
|
|
6490
6819
|
peeledEnabled: this.peelFilled,
|
|
6491
6820
|
},
|
|
6492
6821
|
);
|
|
@@ -6833,9 +7162,11 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
6833
7162
|
gl.activeTexture(gl.TEXTURE0 + REFRACT_SCENE_TEXTURE_UNIT);
|
|
6834
7163
|
gl.bindTexture(gl.TEXTURE_2D, this.emptyTexture2D);
|
|
6835
7164
|
gl.uniform1i(u['uRefractScene'] ?? null, REFRACT_SCENE_TEXTURE_UNIT);
|
|
6836
|
-
|
|
7165
|
+
/* Neither refracting nor glass, which is every draw that does not say otherwise. See
|
|
7166
|
+
`bindSeeThrough` for the four numbers in the one vector. */
|
|
7167
|
+
gl.uniform4f(u['uSeeThrough'] ?? null, 0, 0, 0, 0);
|
|
6837
7168
|
gl.uniform3fv(u['uRefractTint'] ?? null, WHITE_TINT);
|
|
6838
|
-
gl.
|
|
7169
|
+
gl.uniform3fv(u['uGlassTint'] ?? null, WHITE_TINT);
|
|
6839
7170
|
gl.uniform3fv(u['uDirectionalDir'] ?? null, env.directionalDir);
|
|
6840
7171
|
gl.uniform3fv(u['uDirectionalColor'] ?? null, env.directionalColor);
|
|
6841
7172
|
gl.uniform3fv(u['uAmbient'] ?? null, env.ambient);
|
|
@@ -6937,7 +7268,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
6937
7268
|
* its own copy of this lookup; the two backends have to project the same way.
|
|
6938
7269
|
*/
|
|
6939
7270
|
gl.uniformMatrix4fv(u['uLightViewProj'] ?? null, false, env.lightViewProj);
|
|
6940
|
-
gl.uniform1f(u['uShadowStrength'] ?? null, this.
|
|
7271
|
+
gl.uniform1f(u['uShadowStrength'] ?? null, this.sunShadows === null ? 0 : env.shadowStrength);
|
|
6941
7272
|
gl.uniform1f(u['uShadowMapSize'] ?? null, this.quality.directionalShadowMapSize);
|
|
6942
7273
|
gl.uniform1f(u['uShadowDepthSpan'] ?? null, env.shadowDepthSpan);
|
|
6943
7274
|
gl.uniform1f(u['uShadowMaxDistance'] ?? null, this.quality.directionalShadowMaxDistance);
|
|
@@ -6964,16 +7295,12 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
6964
7295
|
*/
|
|
6965
7296
|
if (this.quality.directionalShadows) {
|
|
6966
7297
|
gl.activeTexture(gl.TEXTURE0);
|
|
6967
|
-
gl.bindTexture(gl.
|
|
6968
|
-
gl.uniform1i(u['
|
|
6969
|
-
|
|
6970
|
-
gl.activeTexture(gl.
|
|
6971
|
-
gl.bindTexture(gl.
|
|
6972
|
-
gl.uniform1i(u['
|
|
6973
|
-
|
|
6974
|
-
gl.activeTexture(gl.TEXTURE2);
|
|
6975
|
-
gl.bindTexture(gl.TEXTURE_2D, this.dynamicShadowMap?.texture ?? this.emptyTexture2D);
|
|
6976
|
-
gl.uniform1i(u['uDynamicShadowMap'] ?? null, 2);
|
|
7298
|
+
gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.sunShadows?.texture ?? this.emptyTexture2DArray);
|
|
7299
|
+
gl.uniform1i(u['uSunShadows'] ?? null, 0);
|
|
7300
|
+
/* What the sun keeps through glass, or the one-texel stand-in the lookup reads as none. */
|
|
7301
|
+
gl.activeTexture(gl.TEXTURE0 + SUN_GLASS_TINT_TEXTURE_UNIT);
|
|
7302
|
+
gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.sunGlassTint?.texture ?? this.emptyTexture2DArray);
|
|
7303
|
+
gl.uniform1i(u['uSunGlassTints'] ?? null, SUN_GLASS_TINT_TEXTURE_UNIT);
|
|
6977
7304
|
}
|
|
6978
7305
|
|
|
6979
7306
|
if (this.pointShadows === null) {
|
|
@@ -6986,6 +7313,10 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
6986
7313
|
} else {
|
|
6987
7314
|
/* Chosen in `pointShadowSystem.ts` and bound here; see `resolve` for the split. */
|
|
6988
7315
|
this.pointShadows.resolve(this.resolvedPointShadows);
|
|
7316
|
+
/* As the array stores them: 2L once glass has interleaved it. See `strideLayers`. */
|
|
7317
|
+
const stride = this.pointShadowArray?.stride ?? 1;
|
|
7318
|
+
strideLayers(this.resolvedPointShadows.layers, stride);
|
|
7319
|
+
strideLayers(this.resolvedPointShadows.liveLayers, stride);
|
|
6989
7320
|
this.bindPointShadows(u, DIRECTIONAL_SHADOW_UNITS);
|
|
6990
7321
|
/*
|
|
6991
7322
|
* The rectangles' layers, into the array `bindPointShadows` just put on its unit. Resolved
|
|
@@ -7203,13 +7534,14 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
7203
7534
|
*/
|
|
7204
7535
|
tint: Vec3 | null = null,
|
|
7205
7536
|
/**
|
|
7206
|
-
* Where the mesh was last frame
|
|
7537
|
+
* Where the mesh was last frame — a `Mover` or a matrix — which this backend takes and ignores.
|
|
7207
7538
|
*
|
|
7208
7539
|
* Accepted rather than absent so that one scene draws through both renderers unchanged — the
|
|
7209
7540
|
* parity rule this repository is built on. Reconstruction is WebGPU's, having no compute stage
|
|
7210
|
-
* here, so there is no motion target for this to be written into
|
|
7541
|
+
* here, so there is no motion target for this to be written into, and a mover passed here is
|
|
7542
|
+
* neither read nor advanced.
|
|
7211
7543
|
*/
|
|
7212
|
-
_previousModel: ReadonlyMat4 | null = null,
|
|
7544
|
+
_previousModel: ReadonlyMat4 | Mover | null = null,
|
|
7213
7545
|
): void {
|
|
7214
7546
|
if (this.contextLost) return;
|
|
7215
7547
|
/* Geometry that has not all arrived is not drawn. See `Mesh.complete`. */
|
|
@@ -7454,19 +7786,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
7454
7786
|
* translucent one rather than sampling a black texture and painting the pane the colour of a
|
|
7455
7787
|
* hole. That is the defined state the two-backends rule asks for instead of a silent no-op.
|
|
7456
7788
|
*/
|
|
7457
|
-
const
|
|
7458
|
-
let refracting = false;
|
|
7459
|
-
if (refraction > 0) {
|
|
7460
|
-
const snapshot = this.sceneTarget?.snapshotColor() ?? null;
|
|
7461
|
-
if (snapshot !== null) {
|
|
7462
|
-
refracting = true;
|
|
7463
|
-
gl.activeTexture(gl.TEXTURE0 + REFRACT_SCENE_TEXTURE_UNIT);
|
|
7464
|
-
gl.bindTexture(gl.TEXTURE_2D, snapshot);
|
|
7465
|
-
gl.uniform1f(u['uRefractStrength'] ?? null, refraction);
|
|
7466
|
-
gl.uniform3fv(u['uRefractTint'] ?? null, options.refractTint ?? WHITE_TINT);
|
|
7467
|
-
gl.uniform1f(u['uRefractThickness'] ?? null, options.thicknessM ?? 0);
|
|
7468
|
-
}
|
|
7469
|
-
}
|
|
7789
|
+
const refracting = this.bindSeeThrough(u, options);
|
|
7470
7790
|
/*
|
|
7471
7791
|
* **The blend and the depth mask belong to whoever owns the pass.** In the ordinary case that
|
|
7472
7792
|
* is this draw; during an order-independent replay it is `OitPass`, which has set an additive
|
|
@@ -7509,6 +7829,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
7509
7829
|
if (ownsState) gl.disable(gl.BLEND);
|
|
7510
7830
|
gl.uniform1f(u['uOpacity'] ?? null, 1);
|
|
7511
7831
|
if (tint !== null) gl.uniform3fv(u['uTint'] ?? null, WHITE_TINT);
|
|
7832
|
+
if (refracting) this.unbindSeeThrough(u);
|
|
7512
7833
|
/* Put back for whatever is drawn next, exactly as `uOpacity` and `uTint` are — a pass
|
|
7513
7834
|
cannot inherit a material from the draw before it. */
|
|
7514
7835
|
if (!lit) gl.uniform1i(u['uLightingEnabled'] ?? null, 1);
|
|
@@ -7896,16 +8217,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
7896
8217
|
gl.uniform1i(u['uPeeledShadowEnabled'] ?? null, this.peelFilled ? 1 : 0);
|
|
7897
8218
|
|
|
7898
8219
|
gl.activeTexture(gl.TEXTURE0);
|
|
7899
|
-
gl.bindTexture(gl.
|
|
7900
|
-
gl.uniform1i(u['
|
|
7901
|
-
|
|
7902
|
-
gl.activeTexture(gl.TEXTURE1);
|
|
7903
|
-
gl.bindTexture(gl.TEXTURE_2D, this.peeledShadowMap?.texture ?? this.emptyTexture2D);
|
|
7904
|
-
gl.uniform1i(u['uPeeledShadowMap'] ?? null, 1);
|
|
7905
|
-
|
|
7906
|
-
gl.activeTexture(gl.TEXTURE2);
|
|
7907
|
-
gl.bindTexture(gl.TEXTURE_2D, this.dynamicShadowMap?.texture ?? this.emptyTexture2D);
|
|
7908
|
-
gl.uniform1i(u['uDynamicShadowMap'] ?? null, 2);
|
|
8220
|
+
gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.sunShadows?.texture ?? this.emptyTexture2DArray);
|
|
8221
|
+
gl.uniform1i(u['uSunShadows'] ?? null, 0);
|
|
7909
8222
|
}
|
|
7910
8223
|
}
|
|
7911
8224
|
|