@driftengine/core 4.4.2 → 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 +46 -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 +79 -8
- package/dist/render/backend/webgl2/renderer.js +360 -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 +2 -12
- package/dist/render/backend/webgpu/renderer.d.ts +171 -54
- package/dist/render/backend/webgpu/renderer.js +944 -390
- 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/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/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/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/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 +7 -1
- package/dist/render/sceneTarget.js +58 -12
- 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/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/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/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 +401 -108
- 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/renderer.ts +1108 -421
- 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/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/pointShadowMap.ts +41 -0
- package/src/render/recon/changeFrames.ts +24 -0
- package/src/render/recon/lateRouting.ts +29 -0
- package/src/render/recon/mover.ts +118 -0
- package/src/render/renderQuality.ts +47 -0
- package/src/render/rendererHarness.ts +12 -0
- package/src/render/sceneTarget.ts +58 -11
- 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/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/shadowCasters.ts +28 -1
- package/src/render/shadowMap.ts +231 -37
- package/src/render/sunGlassTint.ts +86 -0
- package/src/render/translucentQueue.ts +20 -1
|
@@ -56,6 +56,7 @@ export const DEFAULT_RENDER_QUALITY = Object.freeze({
|
|
|
56
56
|
shadowFilterTaps: MAX_SHADOW_FILTER_TAPS,
|
|
57
57
|
lightVolumeSamples: 32,
|
|
58
58
|
directionalShadowDepthLayers: 2,
|
|
59
|
+
glassShadows: 'full',
|
|
59
60
|
directionalShadowMaxDistance: 6,
|
|
60
61
|
directionalShadowMaxSlope: 3,
|
|
61
62
|
pointShadowRebakeDistance: 0.001,
|
|
@@ -205,6 +206,7 @@ export function resolveRenderQuality(options = {}) {
|
|
|
205
206
|
shadowFilterTaps: options.shadowFilterTaps ?? DEFAULT_RENDER_QUALITY.shadowFilterTaps,
|
|
206
207
|
lightVolumeSamples: options.lightVolumeSamples ?? DEFAULT_RENDER_QUALITY.lightVolumeSamples,
|
|
207
208
|
directionalShadowDepthLayers: options.directionalShadowDepthLayers ?? DEFAULT_RENDER_QUALITY.directionalShadowDepthLayers,
|
|
209
|
+
glassShadows: options.glassShadows ?? DEFAULT_RENDER_QUALITY.glassShadows,
|
|
208
210
|
directionalShadowMaxDistance: options.directionalShadowMaxDistance ?? DEFAULT_RENDER_QUALITY.directionalShadowMaxDistance,
|
|
209
211
|
directionalShadowMaxSlope: options.directionalShadowMaxSlope ?? DEFAULT_RENDER_QUALITY.directionalShadowMaxSlope,
|
|
210
212
|
pointShadowRebakeDistance: options.pointShadowRebakeDistance ?? DEFAULT_RENDER_QUALITY.pointShadowRebakeDistance,
|
|
@@ -286,6 +288,11 @@ export function resolveRenderQuality(options = {}) {
|
|
|
286
288
|
if (quality.directionalShadowDepthLayers !== 1 && quality.directionalShadowDepthLayers !== 2) {
|
|
287
289
|
throw new Error(`RenderQuality.directionalShadowDepthLayers must be 1 or 2, got ${quality.directionalShadowDepthLayers}`);
|
|
288
290
|
}
|
|
291
|
+
if (quality.glassShadows !== 'full' &&
|
|
292
|
+
quality.glassShadows !== 'half' &&
|
|
293
|
+
quality.glassShadows !== 'off') {
|
|
294
|
+
throw new Error(`RenderQuality.glassShadows must be 'full', 'half' or 'off', got ${String(quality.glassShadows)}`);
|
|
295
|
+
}
|
|
289
296
|
if (quality.plumeNoiseOctaves > 3) {
|
|
290
297
|
throw new Error(`RenderQuality.plumeNoiseOctaves must be <= 3, got ${quality.plumeNoiseOctaves}`);
|
|
291
298
|
}
|
|
@@ -86,6 +86,18 @@ export function recordingGl(options = {}) {
|
|
|
86
86
|
COLOR_BUFFER_BIT: 0x00004000,
|
|
87
87
|
/* Named so a test can tell a mesh that declared itself deforming from one that did not. */
|
|
88
88
|
ARRAY_BUFFER: 0x8892,
|
|
89
|
+
/* Named so a blend a pass sets can be read back as numbers: glass multiplies what it lets
|
|
90
|
+
through, and a test that cannot tell DST_COLOR from a recorder cannot tell it multiplies. */
|
|
91
|
+
ZERO: 0,
|
|
92
|
+
DST_ALPHA: 0x0304,
|
|
93
|
+
DST_COLOR: 0x0306,
|
|
94
|
+
FUNC_ADD: 0x8006,
|
|
95
|
+
COLOR: 0x1800,
|
|
96
|
+
DEPTH: 0x1801,
|
|
97
|
+
/* Named so a copy can be read as from-and-to: a blit's two framebuffers are the whole claim. */
|
|
98
|
+
READ_FRAMEBUFFER: 0x8ca8,
|
|
99
|
+
DRAW_FRAMEBUFFER: 0x8ca9,
|
|
100
|
+
BLEND: 0x0be2,
|
|
89
101
|
STATIC_DRAW: 0x88e4,
|
|
90
102
|
DYNAMIC_DRAW: 0x88e8,
|
|
91
103
|
};
|
|
@@ -72,6 +72,8 @@ export declare class SceneTarget {
|
|
|
72
72
|
private colorCopyFramebuffer;
|
|
73
73
|
/** Whether this frame has filled it. One snapshot serves every refracting draw in a frame. */
|
|
74
74
|
private colorCopyFilled;
|
|
75
|
+
/** Whether this frame's copy has its mip chain, which frosted glass reads. See `snapshotColor`. */
|
|
76
|
+
private colorCopyMips;
|
|
75
77
|
/**
|
|
76
78
|
* The colour grade, as a 3D texture on unit 4, and the table it was built from.
|
|
77
79
|
*
|
|
@@ -221,7 +223,11 @@ export declare class SceneTarget {
|
|
|
221
223
|
* Null where the copy could not be made, and a caller must read that as "do not refract" rather
|
|
222
224
|
* than as a black texture — refracting against black paints every pane the colour of a hole.
|
|
223
225
|
*/
|
|
224
|
-
snapshotColor(afresh?: boolean
|
|
226
|
+
snapshotColor(afresh?: boolean,
|
|
227
|
+
/** Fill the copy's mip chain as well, once a frame: what frosted glass reads. */
|
|
228
|
+
mips?: boolean): WebGLTexture | null;
|
|
229
|
+
/** The copy's mip chain from its level 0, once per copy. */
|
|
230
|
+
private fillColorCopyMips;
|
|
225
231
|
snapshotDepth(
|
|
226
232
|
/**
|
|
227
233
|
* Take the copy again even though one has already been taken this frame.
|
|
@@ -76,6 +76,8 @@ export class SceneTarget {
|
|
|
76
76
|
colorCopyFramebuffer = null;
|
|
77
77
|
/** Whether this frame has filled it. One snapshot serves every refracting draw in a frame. */
|
|
78
78
|
colorCopyFilled = false;
|
|
79
|
+
/** Whether this frame's copy has its mip chain, which frosted glass reads. See `snapshotColor`. */
|
|
80
|
+
colorCopyMips = false;
|
|
79
81
|
/**
|
|
80
82
|
* The colour grade, as a 3D texture on unit 4, and the table it was built from.
|
|
81
83
|
*
|
|
@@ -241,13 +243,25 @@ export class SceneTarget {
|
|
|
241
243
|
if (this.colorCopy === null)
|
|
242
244
|
this.colorCopy = gl.createTexture();
|
|
243
245
|
gl.bindTexture(gl.TEXTURE_2D, this.colorCopy);
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
246
|
+
/*
|
|
247
|
+
* **Every level, so glass can read a blurrier one.** Frosted glass reads the copy at a level its
|
|
248
|
+
* frost picks, and a texture sampled through a mipmapping filter with a level missing is
|
|
249
|
+
* incomplete and reads black — so the chain is allocated here, once per size, and filled only on
|
|
250
|
+
* a frame that draws glass (`snapshotColor`). Plain refraction reads level 0, as it always has.
|
|
251
|
+
*/
|
|
252
|
+
const levels = Math.floor(Math.log2(Math.max(width, height))) + 1;
|
|
253
|
+
for (let level = 0; level < levels; level += 1) {
|
|
254
|
+
const w = Math.max(1, width >> level);
|
|
255
|
+
const h = Math.max(1, height >> level);
|
|
256
|
+
if (this.floatColor) {
|
|
257
|
+
gl.texImage2D(gl.TEXTURE_2D, level, gl.RGBA16F, w, h, 0, gl.RGBA, gl.HALF_FLOAT, null);
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
gl.texImage2D(gl.TEXTURE_2D, level, gl.RGBA, w, h, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
|
261
|
+
}
|
|
249
262
|
}
|
|
250
|
-
gl.texParameteri(gl.TEXTURE_2D, gl.
|
|
263
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAX_LEVEL, levels - 1);
|
|
264
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
|
|
251
265
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
|
252
266
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
253
267
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
@@ -359,6 +373,7 @@ export class SceneTarget {
|
|
|
359
373
|
/* Last frame's snapshot is last frame's scene. See `snapshotDepth`. */
|
|
360
374
|
this.depthCopyFilled = false;
|
|
361
375
|
this.colorCopyFilled = false;
|
|
376
|
+
this.colorCopyMips = false;
|
|
362
377
|
this.bind();
|
|
363
378
|
}
|
|
364
379
|
/**
|
|
@@ -449,15 +464,22 @@ export class SceneTarget {
|
|
|
449
464
|
* Null where the copy could not be made, and a caller must read that as "do not refract" rather
|
|
450
465
|
* than as a black texture — refracting against black paints every pane the colour of a hole.
|
|
451
466
|
*/
|
|
452
|
-
snapshotColor(afresh = false
|
|
467
|
+
snapshotColor(afresh = false,
|
|
468
|
+
/** Fill the copy's mip chain as well, once a frame: what frosted glass reads. */
|
|
469
|
+
mips = false) {
|
|
453
470
|
if (this.colorCopy === null || this.colorCopyFramebuffer === null)
|
|
454
471
|
return null;
|
|
455
|
-
if (this.colorCopyFilled && !afresh)
|
|
472
|
+
if (this.colorCopyFilled && !afresh) {
|
|
473
|
+
if (mips)
|
|
474
|
+
this.fillColorCopyMips();
|
|
456
475
|
return this.colorCopy;
|
|
476
|
+
}
|
|
457
477
|
const { gl } = this;
|
|
458
478
|
const source = this.msaaFramebuffer ?? this.framebuffer;
|
|
459
479
|
if (source === null)
|
|
460
480
|
return null;
|
|
481
|
+
const drawing = gl.getParameter(gl.DRAW_FRAMEBUFFER_BINDING);
|
|
482
|
+
const reading = gl.getParameter(gl.READ_FRAMEBUFFER_BINDING);
|
|
461
483
|
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, source);
|
|
462
484
|
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, this.colorCopyFramebuffer);
|
|
463
485
|
/* Drained first, for the reason `resolve` gives at length: `getError` reports whatever is
|
|
@@ -470,13 +492,34 @@ export class SceneTarget {
|
|
|
470
492
|
choice only decides which refusals are possible. */
|
|
471
493
|
gl.blitFramebuffer(0, 0, this.width, this.height, 0, 0, this.width, this.height, gl.COLOR_BUFFER_BIT, gl.NEAREST);
|
|
472
494
|
const refused = gl.getError() !== gl.NO_ERROR;
|
|
473
|
-
/*
|
|
474
|
-
|
|
495
|
+
/*
|
|
496
|
+
* **Back to what the caller was drawing into, whichever that was** — read before the copy and
|
|
497
|
+
* handed back, never assumed. It was the scene's framebuffer, assumed, and a pane drawn into a
|
|
498
|
+
* reflection probe's face then left the scene's bound: the probe attached its next face to it,
|
|
499
|
+
* a face-sized colour beside a screen-sized depth, and every draw after refused as incomplete.
|
|
500
|
+
*/
|
|
501
|
+
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, drawing);
|
|
502
|
+
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, reading);
|
|
475
503
|
if (refused)
|
|
476
504
|
return null;
|
|
477
505
|
this.colorCopyFilled = true;
|
|
506
|
+
this.colorCopyMips = false;
|
|
507
|
+
if (mips)
|
|
508
|
+
this.fillColorCopyMips();
|
|
478
509
|
return this.colorCopy;
|
|
479
510
|
}
|
|
511
|
+
/** The copy's mip chain from its level 0, once per copy. */
|
|
512
|
+
fillColorCopyMips() {
|
|
513
|
+
if (this.colorCopyMips || this.colorCopy === null)
|
|
514
|
+
return;
|
|
515
|
+
const { gl } = this;
|
|
516
|
+
/* Bound on whichever unit is active and handed back, so the caller's texture there survives. */
|
|
517
|
+
const previous = gl.getParameter(gl.TEXTURE_BINDING_2D);
|
|
518
|
+
gl.bindTexture(gl.TEXTURE_2D, this.colorCopy);
|
|
519
|
+
gl.generateMipmap(gl.TEXTURE_2D);
|
|
520
|
+
gl.bindTexture(gl.TEXTURE_2D, previous);
|
|
521
|
+
this.colorCopyMips = true;
|
|
522
|
+
}
|
|
480
523
|
snapshotDepth(
|
|
481
524
|
/**
|
|
482
525
|
* Take the copy again even though one has already been taken this frame.
|
|
@@ -499,6 +542,8 @@ export class SceneTarget {
|
|
|
499
542
|
const source = this.msaaFramebuffer ?? this.framebuffer;
|
|
500
543
|
if (source === null)
|
|
501
544
|
return null;
|
|
545
|
+
const drawing = gl.getParameter(gl.DRAW_FRAMEBUFFER_BINDING);
|
|
546
|
+
const reading = gl.getParameter(gl.READ_FRAMEBUFFER_BINDING);
|
|
502
547
|
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, source);
|
|
503
548
|
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, this.depthCopyFramebuffer);
|
|
504
549
|
/* Drained first, for the reason `resolve` gives at length: `getError` reports whatever is
|
|
@@ -509,8 +554,9 @@ export class SceneTarget {
|
|
|
509
554
|
}
|
|
510
555
|
gl.blitFramebuffer(0, 0, this.width, this.height, 0, 0, this.width, this.height, gl.DEPTH_BUFFER_BIT, gl.NEAREST);
|
|
511
556
|
const refused = gl.getError() !== gl.NO_ERROR;
|
|
512
|
-
/* Back to what the caller was drawing into,
|
|
513
|
-
gl.bindFramebuffer(gl.
|
|
557
|
+
/* Back to what the caller was drawing into, read rather than assumed: see `snapshotColor`. */
|
|
558
|
+
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, drawing);
|
|
559
|
+
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, reading);
|
|
514
560
|
if (refused)
|
|
515
561
|
return null;
|
|
516
562
|
this.depthCopyFilled = true;
|
|
@@ -23,7 +23,7 @@ export declare const DEPTH_INSTANCED_VERT: string;
|
|
|
23
23
|
*/
|
|
24
24
|
export declare const DEPTH_CUTOUT_VERT: string;
|
|
25
25
|
export declare const DEPTH_INSTANCED_CUTOUT_VERT: string;
|
|
26
|
-
export declare const DEPTH_FRAG = "#version 300 es\nprecision highp float;\nin vec4 vLightPosition;\n\nuniform highp
|
|
26
|
+
export declare const DEPTH_FRAG = "#version 300 es\nprecision highp float;\nin vec4 vLightPosition;\n\nuniform highp sampler2DArray uPreviousShadowMap;\nuniform int uPeelShadowLayer;\n\nvoid main() {\n if (uPeelShadowLayer != 0) {\n vec3 p = vLightPosition.xyz / vLightPosition.w;\n vec2 uv = p.xy * 0.5 + 0.5;\n float previousDepth = texture(uPreviousShadowMap, vec3(uv, 0.0)).r;\n // Discard the already-recorded surface and everything in front of it. The\n // ordinary depth test then stores the next independently fading occluder.\n if (gl_FragCoord.z <= previousDepth + 0.00001) discard;\n }\n}\n";
|
|
27
27
|
/**
|
|
28
28
|
* The cutout caster's depth: the plain pass, and a texel of alpha under the cutoff writes nothing.
|
|
29
29
|
*
|
|
@@ -37,3 +37,18 @@ export declare const DEPTH_FRAG = "#version 300 es\nprecision highp float;\nin v
|
|
|
37
37
|
* map takes the next.
|
|
38
38
|
*/
|
|
39
39
|
export declare const DEPTH_CUTOUT_FRAG: string;
|
|
40
|
+
/**
|
|
41
|
+
* The glass tint's vertex stages: every way a caster is drawn, with the world position handed on.
|
|
42
|
+
*
|
|
43
|
+
* **The same five as the depth pass, and for the same reason**: a pane must cast its colour from
|
|
44
|
+
* exactly where it casts its depth, deformed and placed the same way, or the coloured patch parts
|
|
45
|
+
* from the pane's own shadow edge. The only addition is `vGlassWorld`.
|
|
46
|
+
*/
|
|
47
|
+
export declare const GLASS_TINT_VERT: string;
|
|
48
|
+
export declare const GLASS_TINT_SKINNED_VERT: string;
|
|
49
|
+
export declare const GLASS_TINT_INSTANCED_VERT: string;
|
|
50
|
+
export declare const GLASS_TINT_CUTOUT_VERT: string;
|
|
51
|
+
export declare const GLASS_TINT_INSTANCED_CUTOUT_VERT: string;
|
|
52
|
+
export declare const GLASS_TINT_FRAG = "#version 300 es\nprecision highp float;\nin vec4 vLightPosition;\nin vec3 vGlassWorld;\n\n/* rgb: transmission times tint; a: clarity, one less the frost. */\nuniform vec4 uGlassPane;\n/* xyz: the light's position (w 1) or the direction toward it (w 0). */\nuniform vec4 uGlassLight;\n\nout vec4 outTint;\n\nvoid main() {\n\n vec3 n = normalize(cross(dFdx(vGlassWorld), dFdy(vGlassWorld)));\n vec3 toLight = uGlassLight.w > 0.5 ? normalize(uGlassLight.xyz - vGlassWorld) : uGlassLight.xyz;\n float cosLight = abs(dot(n, toLight));\n float reflected = 0.04 + 0.96 * pow(1.0 - cosLight, 5.0);\n outTint = vec4(uGlassPane.rgb * (1.0 - reflected), uGlassPane.a);\n\n}\n";
|
|
53
|
+
/** A cut-out pane: its colour lands only where its depth does, by the depth cutout's own test. */
|
|
54
|
+
export declare const GLASS_TINT_CUTOUT_FRAG: string;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { resolveConditionals } from './conditionals.js';
|
|
2
|
+
import { SUN_STATIC_LAYER } from '../shadowMap.js';
|
|
2
3
|
import { CUTOUT_COVERAGE_GLSL } from './cutoutCoverage.js';
|
|
3
4
|
import { SKINNING_GLSL } from './skinning.js';
|
|
4
5
|
import { CHANNEL_ATTRIBUTE, CHANNEL_BEND } from './vertexChannel.js';
|
|
@@ -72,6 +73,11 @@ out vec4 vLightPosition;
|
|
|
72
73
|
out vec2 vUv;
|
|
73
74
|
flat out float vAlphaCutout;
|
|
74
75
|
#endif
|
|
76
|
+
#if GLASS
|
|
77
|
+
/* Where on the pane this is, for the tint's own normal and its angle to the light. Last, so the
|
|
78
|
+
outputs before it keep the locations the plain variants give them. */
|
|
79
|
+
out vec3 vGlassWorld;
|
|
80
|
+
#endif
|
|
75
81
|
|
|
76
82
|
void main() {
|
|
77
83
|
#if SKINNED
|
|
@@ -99,12 +105,15 @@ void main() {
|
|
|
99
105
|
vUv = aUv * uUvScale;
|
|
100
106
|
vAlphaCutout = uAlphaCutout;
|
|
101
107
|
#endif
|
|
108
|
+
#if GLASS
|
|
109
|
+
vGlassWorld = bent;
|
|
110
|
+
#endif
|
|
102
111
|
}
|
|
103
112
|
`;
|
|
104
113
|
/** The rigid variant: the world, a prop, a mesh with no rig behind it. */
|
|
105
|
-
export const DEPTH_VERT = resolveConditionals(DEPTH_VERT_SOURCE, { SKINNED: false, INSTANCED: false, CUTOUT: false }, 'depth');
|
|
114
|
+
export const DEPTH_VERT = resolveConditionals(DEPTH_VERT_SOURCE, { SKINNED: false, INSTANCED: false, CUTOUT: false, GLASS: false }, 'depth');
|
|
106
115
|
/** The skinned variant, which reads a joint palette and moves the vertex by it. */
|
|
107
|
-
export const DEPTH_SKINNED_VERT = resolveConditionals(DEPTH_VERT_SOURCE, { SKINNED: true, INSTANCED: false, CUTOUT: false }, 'depth-skinned');
|
|
116
|
+
export const DEPTH_SKINNED_VERT = resolveConditionals(DEPTH_VERT_SOURCE, { SKINNED: true, INSTANCED: false, CUTOUT: false, GLASS: false }, 'depth-skinned');
|
|
108
117
|
/**
|
|
109
118
|
* The variant an instanced draw casts its shadow through.
|
|
110
119
|
*
|
|
@@ -113,7 +122,7 @@ export const DEPTH_SKINNED_VERT = resolveConditionals(DEPTH_VERT_SOURCE, { SKINN
|
|
|
113
122
|
* right place and the rest stacked underneath it, which reads as a lighting fault rather than as
|
|
114
123
|
* a missing variant, and is the shape the per-draw ring exists to prevent one level up.
|
|
115
124
|
*/
|
|
116
|
-
export const DEPTH_INSTANCED_VERT = resolveConditionals(DEPTH_VERT_SOURCE, { SKINNED: false, INSTANCED: true, CUTOUT: false }, 'depth-instanced');
|
|
125
|
+
export const DEPTH_INSTANCED_VERT = resolveConditionals(DEPTH_VERT_SOURCE, { SKINNED: false, INSTANCED: true, CUTOUT: false, GLASS: false }, 'depth-instanced');
|
|
117
126
|
/**
|
|
118
127
|
* The variants a cutout caster casts through: a leaf card, a chain link, a fence, whose shape is in
|
|
119
128
|
* its texture's alpha rather than in its geometry.
|
|
@@ -124,20 +133,20 @@ export const DEPTH_INSTANCED_VERT = resolveConditionals(DEPTH_VERT_SOURCE, { SKI
|
|
|
124
133
|
* leaves every opaque caster byte for byte as it was. What it costs is one more program each, and
|
|
125
134
|
* a skinned cutout, which nothing has asked for, is not among them.
|
|
126
135
|
*/
|
|
127
|
-
export const DEPTH_CUTOUT_VERT = resolveConditionals(DEPTH_VERT_SOURCE, { SKINNED: false, INSTANCED: false, CUTOUT: true }, 'depth-cutout');
|
|
128
|
-
export const DEPTH_INSTANCED_CUTOUT_VERT = resolveConditionals(DEPTH_VERT_SOURCE, { SKINNED: false, INSTANCED: true, CUTOUT: true }, 'depth-instanced-cutout');
|
|
136
|
+
export const DEPTH_CUTOUT_VERT = resolveConditionals(DEPTH_VERT_SOURCE, { SKINNED: false, INSTANCED: false, CUTOUT: true, GLASS: false }, 'depth-cutout');
|
|
137
|
+
export const DEPTH_INSTANCED_CUTOUT_VERT = resolveConditionals(DEPTH_VERT_SOURCE, { SKINNED: false, INSTANCED: true, CUTOUT: true, GLASS: false }, 'depth-instanced-cutout');
|
|
129
138
|
export const DEPTH_FRAG = `#version 300 es
|
|
130
139
|
precision highp float;
|
|
131
140
|
in vec4 vLightPosition;
|
|
132
141
|
|
|
133
|
-
uniform highp
|
|
142
|
+
uniform highp sampler2DArray uPreviousShadowMap;
|
|
134
143
|
uniform int uPeelShadowLayer;
|
|
135
144
|
|
|
136
145
|
void main() {
|
|
137
146
|
if (uPeelShadowLayer != 0) {
|
|
138
147
|
vec3 p = vLightPosition.xyz / vLightPosition.w;
|
|
139
148
|
vec2 uv = p.xy * 0.5 + 0.5;
|
|
140
|
-
float previousDepth = texture(uPreviousShadowMap, uv).r;
|
|
149
|
+
float previousDepth = texture(uPreviousShadowMap, vec3(uv, ${SUN_STATIC_LAYER}.0)).r;
|
|
141
150
|
// Discard the already-recorded surface and everything in front of it. The
|
|
142
151
|
// ordinary depth test then stores the next independently fading occluder.
|
|
143
152
|
if (gl_FragCoord.z <= previousDepth + 0.00001) discard;
|
|
@@ -162,7 +171,7 @@ in vec4 vLightPosition;
|
|
|
162
171
|
in vec2 vUv;
|
|
163
172
|
flat in float vAlphaCutout;
|
|
164
173
|
|
|
165
|
-
uniform highp
|
|
174
|
+
uniform highp sampler2DArray uPreviousShadowMap;
|
|
166
175
|
uniform int uPeelShadowLayer;
|
|
167
176
|
uniform highp sampler2D uCutoutMap;
|
|
168
177
|
${CUTOUT_COVERAGE_GLSL}
|
|
@@ -172,9 +181,76 @@ void main() {
|
|
|
172
181
|
if (uPeelShadowLayer != 0) {
|
|
173
182
|
vec3 p = vLightPosition.xyz / vLightPosition.w;
|
|
174
183
|
vec2 uv = p.xy * 0.5 + 0.5;
|
|
175
|
-
float previousDepth = textureLod(uPreviousShadowMap, uv, 0.0).r;
|
|
184
|
+
float previousDepth = textureLod(uPreviousShadowMap, vec3(uv, ${SUN_STATIC_LAYER}.0), 0.0).r;
|
|
176
185
|
if (gl_FragCoord.z <= previousDepth + 0.00001) discard;
|
|
177
186
|
}
|
|
178
187
|
if (alpha < vAlphaCutout) discard;
|
|
179
188
|
}
|
|
180
189
|
`;
|
|
190
|
+
/**
|
|
191
|
+
* The glass tint's vertex stages: every way a caster is drawn, with the world position handed on.
|
|
192
|
+
*
|
|
193
|
+
* **The same five as the depth pass, and for the same reason**: a pane must cast its colour from
|
|
194
|
+
* exactly where it casts its depth, deformed and placed the same way, or the coloured patch parts
|
|
195
|
+
* from the pane's own shadow edge. The only addition is `vGlassWorld`.
|
|
196
|
+
*/
|
|
197
|
+
export const GLASS_TINT_VERT = resolveConditionals(DEPTH_VERT_SOURCE, { SKINNED: false, INSTANCED: false, CUTOUT: false, GLASS: true }, 'glass-tint');
|
|
198
|
+
export const GLASS_TINT_SKINNED_VERT = resolveConditionals(DEPTH_VERT_SOURCE, { SKINNED: true, INSTANCED: false, CUTOUT: false, GLASS: true }, 'glass-tint-skinned');
|
|
199
|
+
export const GLASS_TINT_INSTANCED_VERT = resolveConditionals(DEPTH_VERT_SOURCE, { SKINNED: false, INSTANCED: true, CUTOUT: false, GLASS: true }, 'glass-tint-instanced');
|
|
200
|
+
export const GLASS_TINT_CUTOUT_VERT = resolveConditionals(DEPTH_VERT_SOURCE, { SKINNED: false, INSTANCED: false, CUTOUT: true, GLASS: true }, 'glass-tint-cutout');
|
|
201
|
+
export const GLASS_TINT_INSTANCED_CUTOUT_VERT = resolveConditionals(DEPTH_VERT_SOURCE, { SKINNED: false, INSTANCED: true, CUTOUT: true, GLASS: true }, 'glass-tint-instanced-cutout');
|
|
202
|
+
/**
|
|
203
|
+
* What a pane lets through: its transmission and colour, less what it reflects toward the light,
|
|
204
|
+
* and how clear it is — `glassShadow.ts`'s `paneTexel`, stated for the device. Drawn with a
|
|
205
|
+
* multiplying blend and no depth test, so every pane on a ray contributes whatever order they
|
|
206
|
+
* come in.
|
|
207
|
+
*
|
|
208
|
+
* **The pane's normal is its triangle's**, from the derivatives of its world position, first and in
|
|
209
|
+
* uniform control flow: a pane is flat, a sign does not matter to a cosine taken absolutely, and it
|
|
210
|
+
* spares the five vertex stages a normal they would carry for this alone. What it gives up is a
|
|
211
|
+
* curved pane, whose Fresnel steps per triangle; what would change it is glass that is mostly bulbs.
|
|
212
|
+
*/
|
|
213
|
+
const GLASS_TINT_BODY = /* glsl */ `
|
|
214
|
+
vec3 n = normalize(cross(dFdx(vGlassWorld), dFdy(vGlassWorld)));
|
|
215
|
+
vec3 toLight = uGlassLight.w > 0.5 ? normalize(uGlassLight.xyz - vGlassWorld) : uGlassLight.xyz;
|
|
216
|
+
float cosLight = abs(dot(n, toLight));
|
|
217
|
+
float reflected = 0.04 + 0.96 * pow(1.0 - cosLight, 5.0);
|
|
218
|
+
outTint = vec4(uGlassPane.rgb * (1.0 - reflected), uGlassPane.a);
|
|
219
|
+
`;
|
|
220
|
+
export const GLASS_TINT_FRAG = `#version 300 es
|
|
221
|
+
precision highp float;
|
|
222
|
+
in vec4 vLightPosition;
|
|
223
|
+
in vec3 vGlassWorld;
|
|
224
|
+
|
|
225
|
+
/* rgb: transmission times tint; a: clarity, one less the frost. */
|
|
226
|
+
uniform vec4 uGlassPane;
|
|
227
|
+
/* xyz: the light's position (w 1) or the direction toward it (w 0). */
|
|
228
|
+
uniform vec4 uGlassLight;
|
|
229
|
+
|
|
230
|
+
out vec4 outTint;
|
|
231
|
+
|
|
232
|
+
void main() {
|
|
233
|
+
${GLASS_TINT_BODY}
|
|
234
|
+
}
|
|
235
|
+
`;
|
|
236
|
+
/** A cut-out pane: its colour lands only where its depth does, by the depth cutout's own test. */
|
|
237
|
+
export const GLASS_TINT_CUTOUT_FRAG = `#version 300 es
|
|
238
|
+
precision highp float;
|
|
239
|
+
in vec4 vLightPosition;
|
|
240
|
+
in vec2 vUv;
|
|
241
|
+
flat in float vAlphaCutout;
|
|
242
|
+
in vec3 vGlassWorld;
|
|
243
|
+
|
|
244
|
+
uniform vec4 uGlassPane;
|
|
245
|
+
uniform vec4 uGlassLight;
|
|
246
|
+
uniform highp sampler2D uCutoutMap;
|
|
247
|
+
${CUTOUT_COVERAGE_GLSL}
|
|
248
|
+
out vec4 outTint;
|
|
249
|
+
|
|
250
|
+
void main() {
|
|
251
|
+
/* First, in uniform control flow, as the depth cutout samples it. */
|
|
252
|
+
float alpha = cutoutAlpha(texture(uCutoutMap, vUv).a, vUv * vec2(textureSize(uCutoutMap, 0)));
|
|
253
|
+
${GLASS_TINT_BODY}
|
|
254
|
+
if (alpha < vAlphaCutout) discard;
|
|
255
|
+
}
|
|
256
|
+
`;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/** Filtering the directional cascade, with receiver-plane depth gradient compensation. */
|
|
2
2
|
import { DIRECTIONAL_SHADOW_FADE_START, MAX_SHADOW_FILTER_TAPS } from '../../renderQuality.js';
|
|
3
|
+
import { SUN_DYNAMIC_LAYER, SUN_GLASS_MOVING_LAYER, SUN_GLASS_STATIC_LAYER, SUN_PEELED_LAYER, SUN_STATIC_LAYER, } from '../../shadowMap.js';
|
|
4
|
+
import { FROST_RADIUS_CAP, FROST_SPREAD } from '../../glassShadow.js';
|
|
3
5
|
export const DIRECTIONALSHADOW_GLSL = `vec2 receiverPlaneDepthGradient(vec3 p) {
|
|
4
6
|
vec3 dx = dFdx(p);
|
|
5
7
|
vec3 dy = dFdy(p);
|
|
@@ -126,10 +128,10 @@ float shadowFactor(float directionalNdl) {
|
|
|
126
128
|
*/
|
|
127
129
|
float coarseCompare = p.z - 1.0 / uShadowDepthSpan;
|
|
128
130
|
float coarse =
|
|
129
|
-
directionalVisibility(p.z, coarseCompare, textureLod(
|
|
130
|
-
directionalVisibility(p.z, coarseCompare, textureLod(
|
|
131
|
+
directionalVisibility(p.z, coarseCompare, textureLod(uSunShadows, vec3(p.xy, ${SUN_STATIC_LAYER}.0), 0.0).r) *
|
|
132
|
+
directionalVisibility(p.z, coarseCompare, textureLod(uSunShadows, vec3(p.xy, ${SUN_DYNAMIC_LAYER}.0), 0.0).r);
|
|
131
133
|
if (uPeeledShadowEnabled != 0) {
|
|
132
|
-
coarse *= directionalVisibility(p.z, coarseCompare, textureLod(
|
|
134
|
+
coarse *= directionalVisibility(p.z, coarseCompare, textureLod(uSunShadows, vec3(p.xy, ${SUN_PEELED_LAYER}.0), 0.0).r);
|
|
133
135
|
}
|
|
134
136
|
float strength = uShadowStrength * edgeFade * lowElevationFade;
|
|
135
137
|
if (receiverFade <= 0.0) return mix(1.0, coarse, strength);
|
|
@@ -190,11 +192,11 @@ float shadowFactor(float directionalNdl) {
|
|
|
190
192
|
* \`shadowMap.ts\` allocates these with one storage level and \`NEAREST\` filtering, so
|
|
191
193
|
* there is no mip to select and level zero is the only thing \`texture\` could have read.
|
|
192
194
|
*/
|
|
193
|
-
float staticDepth = textureLod(
|
|
194
|
-
float dynamicDepth = textureLod(
|
|
195
|
+
float staticDepth = textureLod(uSunShadows, vec3(sampleUv, ${SUN_STATIC_LAYER}.0), 0.0).r;
|
|
196
|
+
float dynamicDepth = textureLod(uSunShadows, vec3(sampleUv, ${SUN_DYNAMIC_LAYER}.0), 0.0).r;
|
|
195
197
|
staticLit += directionalVisibility(tapReceiverDepth, compare, staticDepth);
|
|
196
198
|
if (uPeeledShadowEnabled != 0) {
|
|
197
|
-
float peeledDepth = textureLod(
|
|
199
|
+
float peeledDepth = textureLod(uSunShadows, vec3(sampleUv, ${SUN_PEELED_LAYER}.0), 0.0).r;
|
|
198
200
|
peeledLit += directionalVisibility(tapReceiverDepth, compare, peeledDepth);
|
|
199
201
|
} else {
|
|
200
202
|
peeledLit += 1.0;
|
|
@@ -214,6 +216,89 @@ float shadowFactor(float directionalNdl) {
|
|
|
214
216
|
|
|
215
217
|
return mix(1.0, mix(coarse, lit, receiverFade), strength);
|
|
216
218
|
}
|
|
219
|
+
|
|
220
|
+
/*
|
|
221
|
+
* One of the sun's two glass layers at receiver p: how milky the nearest pane is and how far behind
|
|
222
|
+
* it this receiver stands, the spread that follows (glassShadow.ts's frostRadius, capped), and the
|
|
223
|
+
* tint's level for that spread. Then glassShadow.ts's spreadTint: the outline from taps at the base
|
|
224
|
+
* radius — where the clear pane's patch ends — and the colour from the spread taps, each read coarse,
|
|
225
|
+
* unmixed from the ground around it and weighed by the pane's share of it, so a frosted patch mixes
|
|
226
|
+
* its panes' colours and keeps the light the clear one lets through. What it gives up is a pane lying
|
|
227
|
+
* behind the receiver lending it colour within the spread, which only a receiver between two panes
|
|
228
|
+
* meets. One function for both layers, because two copies were most of the lit shaders' growth.
|
|
229
|
+
*/
|
|
230
|
+
vec3 sunGlassLayer(vec3 p, float paneLayer, float tintLayer, float bias, float texel, float uvPerMetre) {
|
|
231
|
+
float tintSize = float(textureSize(uSunGlassTints, 0).x);
|
|
232
|
+
float clarity = textureLod(uSunGlassTints, vec3(p.xy, tintLayer), 0.0).a;
|
|
233
|
+
float behind =
|
|
234
|
+
max(p.z - textureLod(uSunShadows, vec3(p.xy, paneLayer), 0.0).r, 0.0) * uShadowDepthSpan;
|
|
235
|
+
float radius = clamp(
|
|
236
|
+
(1.0 - clarity) * ${FROST_SPREAD.toFixed(6)} * behind * uvPerMetre,
|
|
237
|
+
texel,
|
|
238
|
+
texel * ${FROST_RADIUS_CAP.toFixed(1)}
|
|
239
|
+
);
|
|
240
|
+
float lod = log2(max(radius * tintSize, 1.0));
|
|
241
|
+
float cover = 0.0;
|
|
242
|
+
vec3 colour = vec3(0.0);
|
|
243
|
+
float found = 0.0;
|
|
244
|
+
for (int i = 0; i < ${MAX_SHADOW_FILTER_TAPS}; i++) {
|
|
245
|
+
if (i >= uShadowFilterTaps) break;
|
|
246
|
+
vec2 offset = DIRECTIONAL_PCF_OFFSETS[i];
|
|
247
|
+
vec2 edgeUv = p.xy + offset * texel;
|
|
248
|
+
cover += p.z > textureLod(uSunShadows, vec3(edgeUv, paneLayer), 0.0).r + bias ? 1.0 : 0.0;
|
|
249
|
+
/* The spread taps weigh by the pane's unmixed share rather than a depth test: continuous, so a
|
|
250
|
+
colour crossing between two panes blends rather than stepping through sixteen bands. */
|
|
251
|
+
vec4 pane = glassUnmix(textureLod(uSunGlassTints, vec3(p.xy + offset * radius, tintLayer), lod), clarity);
|
|
252
|
+
colour += pane.rgb * pane.a;
|
|
253
|
+
found += pane.a;
|
|
254
|
+
}
|
|
255
|
+
float taps = float(max(uShadowFilterTaps, 1));
|
|
256
|
+
return mix(vec3(1.0), found > 0.0 ? colour / found : vec3(1.0), cover / taps);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/*
|
|
260
|
+
* What the sun's light keeps of itself through glass at this receiver: glassShadow.ts's tap rule
|
|
261
|
+
* and frost spread, over shadowFactor's own taps. A tap counts a pane only when the receiver is
|
|
262
|
+
* farther from the sun than that pane (the glass depth layers), so a surface on the sun's side of a
|
|
263
|
+
* pane, and the pane itself, keep the sun's own colour; the taps average with equal weight, so the
|
|
264
|
+
* spread moves light rather than losing it. Frost widens the taps by how far behind the nearest pane
|
|
265
|
+
* the receiver is, capped, with the tap count unchanged. What it gives up is spread past the edge of
|
|
266
|
+
* an opaque frame's shadow, which this lookup never sees. Faded with the opaque shadow's own fades.
|
|
267
|
+
*/
|
|
268
|
+
vec3 sunGlassLookup(float directionalNdl) {
|
|
269
|
+
vec3 p = vLightPos.xyz / vLightPos.w;
|
|
270
|
+
p = p * 0.5 + 0.5;
|
|
271
|
+
/* Map units per metre along the receiver, from derivatives taken first in uniform control flow,
|
|
272
|
+
so a frost spread stated in metres lands the same whatever size of light matrix a consumer built. */
|
|
273
|
+
float uvPerMetre = length(dFdx(p.xy)) / max(length(dFdx(vWorldPos)), 1e-6);
|
|
274
|
+
if (uShadowStrength <= 0.0 || textureSize(uSunGlassTints, 0).x <= 1) return vec3(1.0);
|
|
275
|
+
if (directionalNdl <= 0.0) return vec3(1.0);
|
|
276
|
+
if (p.z > 1.0 || p.x < 0.0 || p.x > 1.0 || p.y < 0.0 || p.y > 1.0) return vec3(1.0);
|
|
277
|
+
vec2 fromCentre = abs(p.xy - 0.5) * 2.0;
|
|
278
|
+
float edgeFade = 1.0 - smoothstep(0.72, 0.98, max(fromCentre.x, fromCentre.y));
|
|
279
|
+
edgeFade *= 1.0 - smoothstep(0.90, 1.0, p.z);
|
|
280
|
+
float shadowSlope = length(uDirectionalDir.xz) / max(uDirectionalDir.y, 0.001);
|
|
281
|
+
float lowElevationFade = 1.0 - smoothstep(
|
|
282
|
+
uShadowMaxSlope * ${DIRECTIONAL_SHADOW_FADE_START.toFixed(2)},
|
|
283
|
+
uShadowMaxSlope,
|
|
284
|
+
shadowSlope
|
|
285
|
+
);
|
|
286
|
+
float strength = uShadowStrength * edgeFade * lowElevationFade;
|
|
287
|
+
if (strength <= 0.0) return vec3(1.0);
|
|
288
|
+
|
|
289
|
+
float bias = 0.14 / uShadowDepthSpan;
|
|
290
|
+
float texel = 1.35 / uShadowMapSize;
|
|
291
|
+
vec3 staticTint = sunGlassLayer(p, ${SUN_GLASS_STATIC_LAYER}.0, 0.0, bias, texel, uvPerMetre);
|
|
292
|
+
vec3 movingTint = sunGlassLayer(p, ${SUN_GLASS_MOVING_LAYER}.0, 1.0, bias, texel, uvPerMetre);
|
|
293
|
+
return mix(vec3(1.0), staticTint * movingTint, strength);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/* The way in, asking first whether this build reads glass at all (GLASS_SHADOWS). A constant, so
|
|
297
|
+
the lookup's derivatives are still taken in uniform control flow. */
|
|
298
|
+
vec3 sunGlassTint(float directionalNdl) {
|
|
299
|
+
if (GLASS_SHADOWS) return sunGlassLookup(directionalNdl);
|
|
300
|
+
return vec3(1.0);
|
|
301
|
+
}
|
|
217
302
|
#endif
|
|
218
303
|
|
|
219
304
|
/**
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the lit stage shares between every glass tint it reads: whether it reads any, and
|
|
3
|
+
* `glassShadow.ts`'s `unmixTint`, stated once for the sun's lookup and the lamps'. At depth zero,
|
|
4
|
+
* outside every conditional, so each lookup can call it whichever shadows a permutation compiles.
|
|
5
|
+
*/
|
|
6
|
+
export declare function glassTintGlsl(glassShadows: boolean): string;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the lit stage shares between every glass tint it reads: whether it reads any, and
|
|
3
|
+
* `glassShadow.ts`'s `unmixTint`, stated once for the sun's lookup and the lamps'. At depth zero,
|
|
4
|
+
* outside every conditional, so each lookup can call it whichever shadows a permutation compiles.
|
|
5
|
+
*/
|
|
6
|
+
export function glassTintGlsl(glassShadows) {
|
|
7
|
+
return /* glsl */ `
|
|
8
|
+
/*
|
|
9
|
+
* Whether this build reads glass at all, which every way into the lookups asks first — and as a
|
|
10
|
+
* branch's own condition, never negated: see \`overridableConstants\` in scripts/wgsl/transform.mjs.
|
|
11
|
+
*/
|
|
12
|
+
const bool GLASS_SHADOWS = ${glassShadows ? 'true' : 'false'}; // wgsl:override
|
|
13
|
+
/*
|
|
14
|
+
* A coarse level of a glass tint with the open ground's white taken back out, by how milky the pane
|
|
15
|
+
* at the centre is: rgb the pane's colour, a its share of the texel (glassShadow.ts's unmixTint).
|
|
16
|
+
* A clear centre is never read coarse, so it is returned as it stands.
|
|
17
|
+
*/
|
|
18
|
+
vec4 glassUnmix(vec4 mip, float centreClarity) {
|
|
19
|
+
if (centreClarity >= 1.0) return vec4(mip.rgb, 1.0);
|
|
20
|
+
float share = clamp((1.0 - mip.a) / (1.0 - centreClarity), 0.0, 1.0);
|
|
21
|
+
if (share <= 0.0) return vec4(1.0, 1.0, 1.0, 0.0);
|
|
22
|
+
return vec4(clamp((mip.rgb - vec3(1.0 - share)) / share, 0.0, 1.0), share);
|
|
23
|
+
}
|
|
24
|
+
`;
|
|
25
|
+
}
|
|
@@ -79,6 +79,19 @@ export interface FlatShaderOptions {
|
|
|
79
79
|
* for its instructions not to be there.
|
|
80
80
|
*/
|
|
81
81
|
readonly nightEmissive: boolean;
|
|
82
|
+
/**
|
|
83
|
+
* Whether this build reads glass in its shadows at all: `glassShadows` other than `'off'`. True
|
|
84
|
+
* when absent, which is what the generated WGSL is built with.
|
|
85
|
+
*
|
|
86
|
+
* **Not a permutation axis, and the payload is why.** Every lit permutation carries the glass
|
|
87
|
+
* lookups, and a second copy of each without them would be most of the generated file again. So
|
|
88
|
+
* it is one switch, `GLASS_SHADOWS`, that every way into the lookups branches on first: written
|
|
89
|
+
* into this source for WebGL2, and a pipeline-overridable constant on the generated WGSL, which
|
|
90
|
+
* the device compiles away as it would under `#if`. That matters because the lookups returning
|
|
91
|
+
* at their first line still cost the lit pass its registers — 0.42 ms at 720p on WebGL2 on the
|
|
92
|
+
* courtyard at night, measured, and nothing once compiled out.
|
|
93
|
+
*/
|
|
94
|
+
readonly glassShadows?: boolean;
|
|
82
95
|
/**
|
|
83
96
|
* How many point lights this build declares room for. `MAX_POINT_LIGHTS` when absent.
|
|
84
97
|
*
|
|
@@ -7,6 +7,7 @@ import { preambleGlsl } from './preamble.js';
|
|
|
7
7
|
import { LOBES_GLSL } from './lobes.js';
|
|
8
8
|
import { POINTSHADOW_GLSL } from './pointShadow.js';
|
|
9
9
|
import { DIRECTIONALSHADOW_GLSL } from './directionalShadow.js';
|
|
10
|
+
import { glassTintGlsl } from './glassTint.js';
|
|
10
11
|
import { MORPH_GLSL } from '../morph.js';
|
|
11
12
|
import { SKINNING_GLSL } from '../skinning.js';
|
|
12
13
|
import { SURFACE_GLSL } from './surface.js';
|
|
@@ -354,6 +355,9 @@ export function flatFrag(options) {
|
|
|
354
355
|
* is at depth zero, and it is still declared before `pointShadow` uses it.
|
|
355
356
|
*/
|
|
356
357
|
OCTAHEDRAL_GLSL,
|
|
358
|
+
/* Depth zero as well: both glass lookups, sun and lamps, call it, and every permutation
|
|
359
|
+
declares its switch, which the pipelines set whether the permutation reads it or not. */
|
|
360
|
+
glassTintGlsl(options.glassShadows ?? true),
|
|
357
361
|
LOBES_GLSL,
|
|
358
362
|
POINTSHADOW_GLSL,
|
|
359
363
|
DIRECTIONALSHADOW_GLSL,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const LAMP_GLASS_GLSL: string;
|