@driftengine/core 4.8.7 → 4.9.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 +61 -0
- package/README.md +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.js +7 -0
- package/dist/render/atmosphere.d.ts +13 -0
- package/dist/render/atmosphere.js +9 -1
- package/dist/render/backend/api.d.ts +9 -1
- package/dist/render/backend/createRenderer.d.ts +6 -0
- package/dist/render/backend/createRenderer.js +4 -1
- package/dist/render/backend/webgl2/renderer.d.ts +90 -2
- package/dist/render/backend/webgl2/renderer.js +297 -16
- package/dist/render/backend/webgpu/device.d.ts +7 -1
- package/dist/render/backend/webgpu/device.js +31 -3
- package/dist/render/backend/webgpu/flatPass.d.ts +16 -1
- package/dist/render/backend/webgpu/flatPass.js +58 -26
- package/dist/render/backend/webgpu/postPass.d.ts +51 -9
- package/dist/render/backend/webgpu/postPass.js +16 -2
- package/dist/render/backend/webgpu/reflectionSurfacePass.d.ts +30 -0
- package/dist/render/backend/webgpu/reflectionSurfacePass.js +100 -0
- package/dist/render/backend/webgpu/renderer.d.ts +131 -2
- package/dist/render/backend/webgpu/renderer.js +586 -70
- package/dist/render/displayRange.d.ts +31 -0
- package/dist/render/displayRange.js +47 -0
- package/dist/render/filmicCurve.d.ts +74 -0
- package/dist/render/filmicCurve.js +203 -0
- package/dist/render/fog.d.ts +4 -1
- package/dist/render/fog.js +12 -2
- package/dist/render/frame/resources.d.ts +2 -2
- package/dist/render/frame/resources.js +6 -0
- package/dist/render/frameReflections.d.ts +40 -0
- package/dist/render/frameReflections.js +48 -0
- package/dist/render/pass.d.ts +49 -0
- package/dist/render/reflectionSurfaceTargets.d.ts +28 -0
- package/dist/render/reflectionSurfaceTargets.js +99 -0
- package/dist/render/renderQuality.d.ts +39 -8
- package/dist/render/renderQuality.js +4 -0
- package/dist/render/sceneTarget.d.ts +5 -0
- package/dist/render/sceneTarget.js +24 -1
- package/dist/render/screenSpaceReflection.d.ts +5 -0
- package/dist/render/screenSpaceReflection.js +5 -0
- package/dist/render/shaders/arcane.d.ts +1 -1
- package/dist/render/shaders/bolt.d.ts +1 -1
- package/dist/render/shaders/caustics.d.ts +1 -1
- package/dist/render/shaders/film.d.ts +1 -1
- package/dist/render/shaders/filmLook.js +2 -1
- package/dist/render/shaders/fire.d.ts +1 -1
- package/dist/render/shaders/flat/glassTint.d.ts +1 -1
- package/dist/render/shaders/flat/glassTint.js +2 -0
- package/dist/render/shaders/flat/index.d.ts +10 -0
- package/dist/render/shaders/flat/index.js +6 -0
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +28 -0
- package/dist/render/shaders/flat/overlay.d.ts +16 -0
- package/dist/render/shaders/flat/overlay.js +117 -0
- package/dist/render/shaders/flat/pointShadow.js +1 -1
- package/dist/render/shaders/flat/reflectionSurface.d.ts +21 -0
- package/dist/render/shaders/flat/reflectionSurface.js +61 -0
- package/dist/render/shaders/fog.d.ts +1 -1
- package/dist/render/shaders/fog.js +9 -2
- package/dist/render/shaders/fringe.d.ts +29 -0
- package/dist/render/shaders/fringe.js +55 -0
- package/dist/render/shaders/generated/arcane.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/arcane.wgsl.js +1 -1
- package/dist/render/shaders/generated/bolt.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/bolt.wgsl.js +1 -1
- package/dist/render/shaders/generated/caustics.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/caustics.wgsl.js +1 -1
- package/dist/render/shaders/generated/film.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/film.wgsl.js +1 -1
- package/dist/render/shaders/generated/fire.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/fire.wgsl.js +1 -1
- package/dist/render/shaders/generated/flat.wgsl.d.ts +176 -16
- package/dist/render/shaders/generated/flat.wgsl.js +214 -54
- package/dist/render/shaders/generated/line.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/line.wgsl.js +1 -1
- package/dist/render/shaders/generated/particle.wgsl.d.ts +4 -4
- package/dist/render/shaders/generated/particle.wgsl.js +4 -4
- package/dist/render/shaders/generated/rush.wgsl.d.ts +56 -26
- package/dist/render/shaders/generated/rush.wgsl.js +56 -26
- package/dist/render/shaders/generated/scatter.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/scatter.wgsl.js +1 -1
- package/dist/render/shaders/generated/smoke.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/smoke.wgsl.js +1 -1
- package/dist/render/shaders/generated/ssrResolve.wgsl.d.ts +29 -0
- package/dist/render/shaders/generated/ssrResolve.wgsl.js +29 -0
- package/dist/render/shaders/generated/ssrTrace.wgsl.d.ts +22 -2
- package/dist/render/shaders/generated/ssrTrace.wgsl.js +22 -2
- package/dist/render/shaders/generated/water.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/water.wgsl.js +1 -1
- package/dist/render/shaders/gpudriven/fog.wgsl.d.ts +2 -2
- package/dist/render/shaders/gpudriven/fog.wgsl.js +11 -3
- package/dist/render/shaders/line.d.ts +1 -1
- package/dist/render/shaders/particle.d.ts +3 -3
- package/dist/render/shaders/rush.js +54 -11
- package/dist/render/shaders/scatter.d.ts +1 -1
- package/dist/render/shaders/smoke.d.ts +1 -1
- package/dist/render/shaders/ssrResolve.d.ts +17 -0
- package/dist/render/shaders/ssrResolve.js +51 -0
- package/dist/render/shaders/ssrTrace.js +26 -0
- package/dist/render/shaders/water.d.ts +1 -1
- package/dist/render/ssrPass.d.ts +16 -0
- package/dist/render/ssrPass.js +106 -1
- package/dist/render/surfaceOverlay.d.ts +132 -0
- package/dist/render/surfaceOverlay.js +85 -0
- package/dist/render/surfaceTexture.d.ts +19 -0
- package/dist/render/surfaceTexture.js +69 -0
- package/dist/render/temporalAa.d.ts +14 -0
- package/dist/render/temporalAa.js +20 -0
- package/dist/render/vertexDefaults.d.ts +17 -0
- package/dist/render/vertexDefaults.js +24 -0
- package/package.json +6 -6
- package/src/index.ts +16 -0
- package/src/render/atmosphere.ts +22 -1
- package/src/render/backend/api.ts +9 -0
- package/src/render/backend/createRenderer.ts +18 -1
- package/src/render/backend/webgl2/renderer.ts +357 -13
- package/src/render/backend/webgpu/device.ts +42 -3
- package/src/render/backend/webgpu/flatPass.ts +63 -26
- package/src/render/backend/webgpu/postPass.ts +18 -0
- package/src/render/backend/webgpu/reflectionSurfacePass.ts +114 -0
- package/src/render/backend/webgpu/renderer.ts +735 -83
- package/src/render/displayRange.ts +61 -0
- package/src/render/filmicCurve.ts +229 -0
- package/src/render/fog.ts +19 -3
- package/src/render/frame/resources.ts +6 -0
- package/src/render/frameReflections.ts +72 -0
- package/src/render/pass.ts +53 -1
- package/src/render/reflectionSurfaceTargets.ts +105 -0
- package/src/render/renderQuality.ts +44 -8
- package/src/render/sceneTarget.ts +30 -2
- package/src/render/screenSpaceReflection.ts +5 -0
- package/src/render/shaders/filmLook.ts +2 -1
- package/src/render/shaders/flat/glassTint.ts +5 -1
- package/src/render/shaders/flat/index.ts +16 -0
- package/src/render/shaders/flat/main.ts +28 -0
- package/src/render/shaders/flat/overlay.ts +117 -0
- package/src/render/shaders/flat/pointShadow.ts +1 -1
- package/src/render/shaders/flat/reflectionSurface.ts +61 -0
- package/src/render/shaders/fog.ts +9 -2
- package/src/render/shaders/fringe.ts +59 -0
- package/src/render/shaders/generated/arcane.wgsl.ts +1 -1
- package/src/render/shaders/generated/bolt.wgsl.ts +1 -1
- package/src/render/shaders/generated/caustics.wgsl.ts +1 -1
- package/src/render/shaders/generated/film.wgsl.ts +1 -1
- package/src/render/shaders/generated/fire.wgsl.ts +1 -1
- package/src/render/shaders/generated/flat.wgsl.ts +214 -54
- package/src/render/shaders/generated/line.wgsl.ts +1 -1
- package/src/render/shaders/generated/particle.wgsl.ts +4 -4
- package/src/render/shaders/generated/rush.wgsl.ts +56 -26
- package/src/render/shaders/generated/scatter.wgsl.ts +1 -1
- package/src/render/shaders/generated/smoke.wgsl.ts +1 -1
- package/src/render/shaders/generated/ssrResolve.wgsl.ts +30 -0
- package/src/render/shaders/generated/ssrTrace.wgsl.ts +22 -2
- package/src/render/shaders/generated/water.wgsl.ts +1 -1
- package/src/render/shaders/gpudriven/fog.wgsl.ts +11 -3
- package/src/render/shaders/rush.ts +54 -11
- package/src/render/shaders/ssrResolve.ts +52 -0
- package/src/render/shaders/ssrTrace.ts +26 -0
- package/src/render/ssrPass.ts +138 -1
- package/src/render/surfaceOverlay.ts +195 -0
- package/src/render/surfaceTexture.ts +67 -0
- package/src/render/temporalAa.ts +20 -0
- package/src/render/vertexDefaults.ts +28 -0
|
@@ -15,6 +15,11 @@ import {
|
|
|
15
15
|
glShadowDepthFunc,
|
|
16
16
|
} from '../../depthConvention.ts';
|
|
17
17
|
import type { FrameView } from '../../frameView.ts';
|
|
18
|
+
import { maskOf } from '../../frame/resources.ts';
|
|
19
|
+
|
|
20
|
+
/** The two copies a registered pass may declare it reads. See `drawPass`. */
|
|
21
|
+
const COLOR_SNAPSHOT = maskOf('colorSnapshot');
|
|
22
|
+
const DEPTH_SNAPSHOT = maskOf('depthSnapshot');
|
|
18
23
|
import type { Mover } from '../../recon/mover.ts';
|
|
19
24
|
import { resolveGlass } from '../../glass.ts';
|
|
20
25
|
import type { GlassOptions, ResolvedGlass } from '../../glass.ts';
|
|
@@ -31,6 +36,12 @@ import {
|
|
|
31
36
|
} from '../../temporalAa.ts';
|
|
32
37
|
import { DecalPass } from '../../decalPass.ts';
|
|
33
38
|
import { SsrPass } from '../../ssrPass.ts';
|
|
39
|
+
import { ReflectionSurfaceTargets } from '../../reflectionSurfaceTargets.ts';
|
|
40
|
+
import {
|
|
41
|
+
frameReflectionsRefused,
|
|
42
|
+
resolveFrameReflections,
|
|
43
|
+
type ResolvedFrameReflections,
|
|
44
|
+
} from '../../frameReflections.ts';
|
|
34
45
|
import { GlobalMediumPass } from '../../globalMediumPass.ts';
|
|
35
46
|
import {
|
|
36
47
|
DEFAULT_GLOBAL_MEDIUM,
|
|
@@ -81,11 +92,18 @@ import { isWeakGpuFamily, webgl2RendererName } from '../../gpuCapability.ts';
|
|
|
81
92
|
import { GpuTimer } from '../../gpuTimer.ts';
|
|
82
93
|
import { compileProgram, uniformLocations } from '../../shader.ts';
|
|
83
94
|
import {
|
|
95
|
+
countUniformVectors,
|
|
84
96
|
FULL_LIGHT_BUDGET,
|
|
85
97
|
type LightBudget,
|
|
86
98
|
nextLightBudget,
|
|
87
99
|
planLightBudget,
|
|
88
100
|
} from '../../uniformVectorBudget.ts';
|
|
101
|
+
import {
|
|
102
|
+
OVERLAY_FLOATS,
|
|
103
|
+
overlayLays,
|
|
104
|
+
packSurfaceOverlay,
|
|
105
|
+
type SurfaceOverlay,
|
|
106
|
+
} from '../../surfaceOverlay.ts';
|
|
89
107
|
import { FILM_FRAG, FILM_VERT } from '../../shaders/film.ts';
|
|
90
108
|
import { SceneTarget } from '../../sceneTarget.ts';
|
|
91
109
|
import type { ColourGradeLut } from '../../colourGrade.ts';
|
|
@@ -293,8 +311,22 @@ import { TextRenderer } from '../../textRenderer.ts';
|
|
|
293
311
|
import { SdfTextRenderer } from '../../sdfTextRenderer.ts';
|
|
294
312
|
import type { SdfFont } from '../../sdfFont.ts';
|
|
295
313
|
import type { SdfTextStyle } from '../../sdfTextLayout.ts';
|
|
296
|
-
import {
|
|
297
|
-
|
|
314
|
+
import {
|
|
315
|
+
MOTION_BLUR_MAX_UV,
|
|
316
|
+
OBJECT_BLUR_UNKEPT,
|
|
317
|
+
RUSH_REACH_UV,
|
|
318
|
+
clampMotionShare,
|
|
319
|
+
} from '../../vertexDefaults.ts';
|
|
320
|
+
import { OUTPUT_TRANSFORM_CODE, forwardTransformCode } from '../../vertexDefaults.ts';
|
|
321
|
+
import type { DisplayRange } from '../../displayRange.ts';
|
|
322
|
+
import {
|
|
323
|
+
DEFAULT_FILMIC_CURVE,
|
|
324
|
+
FILMIC_CLAMPED,
|
|
325
|
+
FILMIC_CONSTANTS,
|
|
326
|
+
FILMIC_WITHOUT_COMPOSITE,
|
|
327
|
+
resolveFilmicCurve,
|
|
328
|
+
type FilmicCurve,
|
|
329
|
+
} from '../../filmicCurve.ts';
|
|
298
330
|
import type { RenderBackend } from '../api.ts';
|
|
299
331
|
import { deviceSnappedCellSize } from '../../textLayout.ts';
|
|
300
332
|
import type { TextStyle } from '../../textLayout.ts';
|
|
@@ -1348,6 +1380,10 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
1348
1380
|
private readonly reflectionQueue = new ReflectionQueue();
|
|
1349
1381
|
/** Built on the first frame that submits a surface, and never for a scene with none. */
|
|
1350
1382
|
private ssr: SsrPass | null = null;
|
|
1383
|
+
/** The frame's materials' reflections, resolved; null where off. See `frameReflections.ts`. */
|
|
1384
|
+
private readonly frameReflections: ResolvedFrameReflections | null;
|
|
1385
|
+
/** Their two maps on the scene's framebuffer, null where they cannot be drawn. */
|
|
1386
|
+
private readonly reflectionTargets: ReflectionSurfaceTargets | null;
|
|
1351
1387
|
/** Said once where a reflection cannot be traced at all. */
|
|
1352
1388
|
private reflectionsRefused = false;
|
|
1353
1389
|
/** Where the frame's camera stands, which is what gives a reconstructed normal its sign. */
|
|
@@ -1430,6 +1466,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
1430
1466
|
* exactly as it asked to be and nothing about it changes.
|
|
1431
1467
|
*/
|
|
1432
1468
|
private exposure: number;
|
|
1469
|
+
/** The `filmic` curve's eight constants, held until `setFilmicCurve` is called again. */
|
|
1470
|
+
private readonly filmConstants = new Float32Array(FILMIC_CONSTANTS);
|
|
1433
1471
|
/**
|
|
1434
1472
|
* This frame's veil colour, and how much of it to composite. See `setFrameVeil`.
|
|
1435
1473
|
*
|
|
@@ -1631,6 +1669,15 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
1631
1669
|
private warnedNoPointShadowArray = false;
|
|
1632
1670
|
/** Said once, for the retired ambient dial. */
|
|
1633
1671
|
private warnedEnvironmentAmbient = false;
|
|
1672
|
+
/** The overlay the draws wear, `uOverlay`: see `setSurfaceOverlay`. */
|
|
1673
|
+
private readonly overlayFloats = new Float32Array(OVERLAY_FLOATS);
|
|
1674
|
+
/** Its atlas, and the 2D copy of it bound where the refraction copy goes. */
|
|
1675
|
+
private overlayMaps: SurfaceTexture | null = null;
|
|
1676
|
+
private overlayTexture: WebGLTexture | null = null;
|
|
1677
|
+
/** Whether this device's lit stage has room for the overlay's vectors: asked once. */
|
|
1678
|
+
private overlayRoom: boolean | null = null;
|
|
1679
|
+
private warnedOverlayRoom = false;
|
|
1680
|
+
private warnedOverlayBlocks = false;
|
|
1634
1681
|
/** Whether a probe is being baked, so the passes that would recurse into one can tell. */
|
|
1635
1682
|
private probePassActive = false;
|
|
1636
1683
|
/**
|
|
@@ -2298,6 +2345,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
2298
2345
|
gl: WebGL2RenderingContext;
|
|
2299
2346
|
outputTransform: number;
|
|
2300
2347
|
outputExposure: number;
|
|
2348
|
+
sceneColor: WebGLTexture | null;
|
|
2349
|
+
sceneDepth: WebGLTexture | null;
|
|
2301
2350
|
} | null = null;
|
|
2302
2351
|
private passDevice: PassDevice | null = null;
|
|
2303
2352
|
|
|
@@ -2309,6 +2358,9 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
2309
2358
|
* call. `init` runs now rather than at the first frame, because building a pipeline in the
|
|
2310
2359
|
* frame loop is the allocation the house rules are about.
|
|
2311
2360
|
*/
|
|
2361
|
+
/** Each registered pass's declared reads, as a mask, resolved once at registration. */
|
|
2362
|
+
private readonly passReads = new Map<PassHandle, number>();
|
|
2363
|
+
|
|
2312
2364
|
registerPass(definition: PassDefinition): PassHandle {
|
|
2313
2365
|
const handle = registerIn(this.passes, definition);
|
|
2314
2366
|
/*
|
|
@@ -2327,6 +2379,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
2327
2379
|
depthCorrection: correction,
|
|
2328
2380
|
};
|
|
2329
2381
|
if (definition.prepare !== undefined) this.preparingPasses++;
|
|
2382
|
+
/* Resolved once: `maskOf` builds an array, and the frame path may not. */
|
|
2383
|
+
this.passReads.set(handle, maskOf(...(definition.reads ?? [])));
|
|
2330
2384
|
definition.init?.(this.passDevice);
|
|
2331
2385
|
return handle;
|
|
2332
2386
|
}
|
|
@@ -2378,10 +2432,28 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
2378
2432
|
gl: this.gl,
|
|
2379
2433
|
outputTransform: 0,
|
|
2380
2434
|
outputExposure: 1,
|
|
2435
|
+
sceneColor: null,
|
|
2436
|
+
sceneDepth: null,
|
|
2381
2437
|
};
|
|
2382
2438
|
const context = this.passContext;
|
|
2383
2439
|
context.outputTransform = this.gradeCode();
|
|
2384
2440
|
context.outputExposure = this.gradeExposure();
|
|
2441
|
+
/*
|
|
2442
|
+
* The copies a pass declared it reads, taken here with everything drawn before it: colour
|
|
2443
|
+
* afresh at each reading pass, as WebGPU's `takePassColour` does, so a full-screen effect drawn
|
|
2444
|
+
* last reads the whole frame; depth afresh too, since a blit is the whole cost of either here.
|
|
2445
|
+
* Not in a mirror, a probe's face or a capture, whose pictures are not the frame's.
|
|
2446
|
+
*/
|
|
2447
|
+
const reads = this.passReads.get(handle) ?? 0;
|
|
2448
|
+
const own = !this.reflectionPassActive && !this.probePassActive && !this.capturePassActive;
|
|
2449
|
+
context.sceneColor =
|
|
2450
|
+
own && (reads & COLOR_SNAPSHOT) !== 0
|
|
2451
|
+
? (this.sceneTarget?.snapshotColor(true) ?? null)
|
|
2452
|
+
: null;
|
|
2453
|
+
context.sceneDepth =
|
|
2454
|
+
own && (reads & DEPTH_SNAPSHOT) !== 0
|
|
2455
|
+
? (this.sceneTarget?.snapshotDepth(true) ?? null)
|
|
2456
|
+
: null;
|
|
2385
2457
|
definition.draw(context as PassContext);
|
|
2386
2458
|
}
|
|
2387
2459
|
|
|
@@ -2389,6 +2461,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
2389
2461
|
unregisterPass(handle: PassHandle): void {
|
|
2390
2462
|
const definition = unregisterIn(this.passes, handle);
|
|
2391
2463
|
if (definition?.prepare !== undefined) this.preparingPasses--;
|
|
2464
|
+
this.passReads.delete(handle);
|
|
2392
2465
|
if (definition !== undefined && this.passDevice !== null) definition.dispose?.(this.passDevice);
|
|
2393
2466
|
}
|
|
2394
2467
|
|
|
@@ -2559,10 +2632,11 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
2559
2632
|
gl.bindTexture(gl.TEXTURE_2D, this.clusterTexture);
|
|
2560
2633
|
gl.uniform1i(u['uClusterTable'] ?? null, CLUSTER_TABLE_TEXTURE_UNIT);
|
|
2561
2634
|
gl.uniform1i(u['uClustered'] ?? null, on ? 1 : 0);
|
|
2562
|
-
if (!on) return;
|
|
2563
2635
|
|
|
2564
2636
|
/*
|
|
2565
|
-
* The frustum, read off the projection rather than taken as a parameter
|
|
2637
|
+
* The frustum, read off the projection rather than taken as a parameter — **and with the view,
|
|
2638
|
+
* written whether or not the lights are clustered**: a surface overlay's rim finds its place on
|
|
2639
|
+
* the screen through the same two (`overlayScreen`).
|
|
2566
2640
|
*
|
|
2567
2641
|
* A perspective matrix carries `1 / tan(fovY/2)` at [5] and that over the aspect at [0], and
|
|
2568
2642
|
* the near and far planes in [14] and [10]. Deriving them here means the binner and the shader
|
|
@@ -2580,6 +2654,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
2580
2654
|
this.clusterFrustum[3] = aspect;
|
|
2581
2655
|
gl.uniform4fv(u['uClusterFrustum'] ?? null, this.clusterFrustum);
|
|
2582
2656
|
gl.uniformMatrix4fv(u['uView'] ?? null, false, camera.view);
|
|
2657
|
+
if (!on) return;
|
|
2583
2658
|
|
|
2584
2659
|
this.clusterTable ??= createClusterTable();
|
|
2585
2660
|
/* The rectangles past the fixed arm's go into the table. See `ClusterLightSet.areas`. */
|
|
@@ -2683,6 +2758,13 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
2683
2758
|
this.quality = resolveRenderQuality(qualityOptions);
|
|
2684
2759
|
/* The grade this world asked for, until a frame says otherwise. See `setOutputExposure`. */
|
|
2685
2760
|
this.exposure = this.quality.outputExposure;
|
|
2761
|
+
resolveFilmicCurve(DEFAULT_FILMIC_CURVE, this.filmConstants);
|
|
2762
|
+
if (
|
|
2763
|
+
this.quality.outputTransform === 'filmic' &&
|
|
2764
|
+
!(this.quality.screenEffects && this.quality.hdrScene)
|
|
2765
|
+
) {
|
|
2766
|
+
console.warn(FILMIC_WITHOUT_COMPOSITE);
|
|
2767
|
+
}
|
|
2686
2768
|
/*
|
|
2687
2769
|
* Multisampling, but only when anything can use it.
|
|
2688
2770
|
*
|
|
@@ -2981,6 +3063,29 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
2981
3063
|
this.quality.discardResolvedAttachments,
|
|
2982
3064
|
)
|
|
2983
3065
|
: null;
|
|
3066
|
+
/*
|
|
3067
|
+
* The frame's materials' reflections, where the frame can carry them: scene light to swap the
|
|
3068
|
+
* probe's share in, and one sample, which the trace's depth copy and both maps are. Said once
|
|
3069
|
+
* where it cannot, in the words the other backend uses. See `frameReflections.ts`.
|
|
3070
|
+
*/
|
|
3071
|
+
this.frameReflections = resolveFrameReflections(this.quality.screenSpaceReflections);
|
|
3072
|
+
const reflectionsRefusal =
|
|
3073
|
+
this.frameReflections === null
|
|
3074
|
+
? null
|
|
3075
|
+
: this.sceneTarget === null || !this.sceneTarget.keepsRange
|
|
3076
|
+
? 'the frame keeps no scene light to swap the probe in (`screenEffects` and `hdrScene`)'
|
|
3077
|
+
: this.sceneTarget.sampleCount > 1
|
|
3078
|
+
? 'a multisampled frame has no single depth or colour to march through'
|
|
3079
|
+
: null;
|
|
3080
|
+
if (reflectionsRefusal !== null) {
|
|
3081
|
+
console.warn(`WebGL2: ${frameReflectionsRefused(reflectionsRefusal)}`);
|
|
3082
|
+
}
|
|
3083
|
+
this.reflectionTargets =
|
|
3084
|
+
this.frameReflections !== null && reflectionsRefusal === null
|
|
3085
|
+
? new ReflectionSurfaceTargets(gl)
|
|
3086
|
+
: null;
|
|
3087
|
+
/* The lit programs write the maps from the first frame they are rebuilt in. See `litOn`. */
|
|
3088
|
+
if (this.reflectionTargets !== null) this.askLit('REFLECTION_MAPS');
|
|
2984
3089
|
/* Screen-space skin needs the composite to add into; without one it is pre-integrated, said once. */
|
|
2985
3090
|
if (this.quality.skinScattering === 'screen-space' && this.sceneTarget === null) {
|
|
2986
3091
|
console.warn(
|
|
@@ -3623,10 +3728,13 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
3623
3728
|
updateSurfaceTexture(texture: SurfaceTexture, source: TexImageSource): void {
|
|
3624
3729
|
if (this.contextLost) return;
|
|
3625
3730
|
texture.update(this.gl, source);
|
|
3731
|
+
/* New pixels are a new copy for the overlay's slot: see `SurfaceTexture.flatLayer`. */
|
|
3732
|
+
if (texture === this.overlayMaps) this.bindOverlayMaps(texture);
|
|
3626
3733
|
}
|
|
3627
3734
|
|
|
3628
3735
|
disposeSurfaceTexture(texture: SurfaceTexture): void {
|
|
3629
3736
|
if (this.contextLost) return;
|
|
3737
|
+
if (texture === this.overlayMaps) this.bindOverlayMaps(null);
|
|
3630
3738
|
texture.dispose(this.gl);
|
|
3631
3739
|
this.captureTargets.get(texture)?.dispose(this.gl);
|
|
3632
3740
|
this.captureTargets.delete(texture);
|
|
@@ -4021,6 +4129,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4021
4129
|
/* The first physical highlight asked for is when the lit programs start carrying one. */
|
|
4022
4130
|
if (material?.physicalSpecular === true) this.askLit('PHYSICAL_SPECULAR');
|
|
4023
4131
|
gl.uniform4fv(u['uModelParams'] ?? null, this.modelParams);
|
|
4132
|
+
/* The draw's overlay, which a program born mid-pass must wear too: see `setSurfaceOverlay`. */
|
|
4133
|
+
gl.uniform4fv(u['uOverlay'] ?? null, this.overlayFloats);
|
|
4024
4134
|
|
|
4025
4135
|
/* The albedo's effects table, or the stand-in the shader reads as none. Before the early
|
|
4026
4136
|
return, so a material with no albedo still leaves a complete texture on the unit. */
|
|
@@ -4120,6 +4230,76 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4120
4230
|
this.writeFlatVector('uEnvironmentDials', this.environmentDials);
|
|
4121
4231
|
}
|
|
4122
4232
|
|
|
4233
|
+
/**
|
|
4234
|
+
* The overlay the following draws wear — a rim of light, a dissolve, wrinkles by region — or null
|
|
4235
|
+
* for none. Pass state, as the material is: `bindMeshPass` puts it back to none. See
|
|
4236
|
+
* `surfaceOverlay.ts` for what it lays and `shaders/flat/overlay.ts` for how.
|
|
4237
|
+
*
|
|
4238
|
+
* **Refused, said once, where the lit stage has no room for it.** Fifteen vectors more than the
|
|
4239
|
+
* program this device fits: counted from the source as `fitFlatProgram` counts, and on a device
|
|
4240
|
+
* whose limit is the 256 an Adreno 740 reports, the eight-light build at 254 has none. The draws
|
|
4241
|
+
* then wear nothing, which is the picture a game without the feature draws. Its atlas is bound as
|
|
4242
|
+
* a 2D copy of its first layer, in the refraction copy's unit (`SurfaceTexture.flatLayer`); a
|
|
4243
|
+
* compressed atlas has no copy and is read as none, also said once.
|
|
4244
|
+
*/
|
|
4245
|
+
setSurfaceOverlay(overlay: SurfaceOverlay | null): void {
|
|
4246
|
+
if (this.contextLost) return;
|
|
4247
|
+
let wanted = overlay;
|
|
4248
|
+
if (overlayLays(wanted) && !this.overlayFits()) {
|
|
4249
|
+
if (!this.warnedOverlayRoom) {
|
|
4250
|
+
this.warnedOverlayRoom = true;
|
|
4251
|
+
console.warn(
|
|
4252
|
+
'Renderer: setSurfaceOverlay is refused on this device. The overlay takes fifteen ' +
|
|
4253
|
+
'fragment uniform vectors and the lit shader already uses all but ' +
|
|
4254
|
+
`${this.overlayRoomLeft()} of the ${this.fragmentVectorLimit()} this GPU offers; ` +
|
|
4255
|
+
'draws wear no overlay. A lower `maxLights` makes the room, or WebGPU has it.',
|
|
4256
|
+
);
|
|
4257
|
+
}
|
|
4258
|
+
wanted = null;
|
|
4259
|
+
}
|
|
4260
|
+
packSurfaceOverlay(wanted, this.overlayFloats);
|
|
4261
|
+
if (overlayLays(wanted)) this.askLit('SURFACE_OVERLAY');
|
|
4262
|
+
this.materials.dirty();
|
|
4263
|
+
this.writeFlatVector('uOverlay', this.overlayFloats);
|
|
4264
|
+
this.bindOverlayMaps((wanted?.maps ?? null) as SurfaceTexture | null);
|
|
4265
|
+
}
|
|
4266
|
+
|
|
4267
|
+
/** The overlay's atlas into the refraction copy's unit, as a 2D copy, or the empty texture. */
|
|
4268
|
+
private bindOverlayMaps(maps: SurfaceTexture | null): void {
|
|
4269
|
+
const { gl } = this;
|
|
4270
|
+
this.overlayMaps = maps;
|
|
4271
|
+
this.overlayTexture = maps === null ? null : maps.flatLayer(gl);
|
|
4272
|
+
if (maps !== null && this.overlayTexture === null && !this.warnedOverlayBlocks) {
|
|
4273
|
+
this.warnedOverlayBlocks = true;
|
|
4274
|
+
console.warn(
|
|
4275
|
+
"Renderer: a surface overlay's maps are a compressed texture, which WebGL2 cannot copy " +
|
|
4276
|
+
'into the 2D image the lit stage reads them from. The overlay is drawn without its ' +
|
|
4277
|
+
'images; upload the atlas from an image instead.',
|
|
4278
|
+
);
|
|
4279
|
+
}
|
|
4280
|
+
gl.activeTexture(gl.TEXTURE0 + REFRACT_SCENE_TEXTURE_UNIT);
|
|
4281
|
+
gl.bindTexture(gl.TEXTURE_2D, this.overlayTexture ?? this.emptyTexture2D);
|
|
4282
|
+
}
|
|
4283
|
+
|
|
4284
|
+
/** The most fragment uniform vectors this GPU offers a stage. */
|
|
4285
|
+
private fragmentVectorLimit(): number {
|
|
4286
|
+
return this.gl.getParameter(this.gl.MAX_FRAGMENT_UNIFORM_VECTORS) as number;
|
|
4287
|
+
}
|
|
4288
|
+
|
|
4289
|
+
/** What the lit shader at this renderer's budget leaves of the limit, with no overlay. */
|
|
4290
|
+
private overlayRoomLeft(): number {
|
|
4291
|
+
return (
|
|
4292
|
+
this.fragmentVectorLimit() - countUniformVectors(this.flatFragAt(this.lightBudget, false))
|
|
4293
|
+
);
|
|
4294
|
+
}
|
|
4295
|
+
|
|
4296
|
+
/** Whether the lit shader with the overlay compiled in fits this GPU: counted once. */
|
|
4297
|
+
private overlayFits(): boolean {
|
|
4298
|
+
this.overlayRoom ??=
|
|
4299
|
+
countUniformVectors(this.flatFragAt(this.lightBudget, true)) <= this.fragmentVectorLimit();
|
|
4300
|
+
return this.overlayRoom;
|
|
4301
|
+
}
|
|
4302
|
+
|
|
4123
4303
|
/**
|
|
4124
4304
|
* How bright the environment the surfaces drawn next reflect is. 1 is the default and identity.
|
|
4125
4305
|
*
|
|
@@ -4555,8 +4735,11 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4555
4735
|
if (coverage) gl.enable(gl.SAMPLE_ALPHA_TO_COVERAGE);
|
|
4556
4736
|
if (ditheredBlend)
|
|
4557
4737
|
gl.uniform4fv(u['uCutout'] ?? null, this.cutoutUniform(this.currentMaterial, true));
|
|
4738
|
+
const keeps = !blend && this.reflectionKeeps();
|
|
4739
|
+
if (keeps) this.reflectionTargets?.on();
|
|
4558
4740
|
if (culling && cullsInstances(count, mesh.indexCount)) batch.drawCulled(gl, data, this.frustum);
|
|
4559
4741
|
else batch.draw(gl, count);
|
|
4742
|
+
if (keeps) this.reflectionTargets?.off();
|
|
4560
4743
|
if (coverage) gl.disable(gl.SAMPLE_ALPHA_TO_COVERAGE);
|
|
4561
4744
|
if (ditheredBlend)
|
|
4562
4745
|
gl.uniform4fv(u['uCutout'] ?? null, this.cutoutUniform(this.currentMaterial, false));
|
|
@@ -4627,7 +4810,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4627
4810
|
const { gl } = this;
|
|
4628
4811
|
gl.uniform4f(u['uSeeThrough'] ?? null, 0, 0, 0, 0);
|
|
4629
4812
|
gl.activeTexture(gl.TEXTURE0 + REFRACT_SCENE_TEXTURE_UNIT);
|
|
4630
|
-
|
|
4813
|
+
/* The overlay's atlas, where one is held: it shares the unit. See `setSurfaceOverlay`. */
|
|
4814
|
+
gl.bindTexture(gl.TEXTURE_2D, this.overlayTexture ?? this.emptyTexture2D);
|
|
4631
4815
|
}
|
|
4632
4816
|
|
|
4633
4817
|
disposeMesh(mesh: Mesh): void {
|
|
@@ -4872,7 +5056,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4872
5056
|
*/
|
|
4873
5057
|
private gradeCode(): number {
|
|
4874
5058
|
return this.sceneTarget === null || !this.quality.hdrScene
|
|
4875
|
-
?
|
|
5059
|
+
? forwardTransformCode(this.quality.outputTransform)
|
|
4876
5060
|
: 0;
|
|
4877
5061
|
}
|
|
4878
5062
|
|
|
@@ -6116,7 +6300,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
6116
6300
|
* throws a sentence with the device's numbers in it rather than a bare driver string.
|
|
6117
6301
|
*/
|
|
6118
6302
|
/** The lit fragment source this renderer's profile compiles, at a light budget. */
|
|
6119
|
-
private flatFragAt(budget: LightBudget): string {
|
|
6303
|
+
private flatFragAt(budget: LightBudget, overlay = this.litOn.SURFACE_OVERLAY): string {
|
|
6120
6304
|
return flatFrag({
|
|
6121
6305
|
pointShadows: this.quality.pointShadows,
|
|
6122
6306
|
directionalShadows: this.quality.directionalShadows,
|
|
@@ -6129,6 +6313,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
6129
6313
|
surfaceEffects: this.litOn.SURFACE_EFFECTS,
|
|
6130
6314
|
driftLight: this.litOn.DRIFT_LIGHT,
|
|
6131
6315
|
physicalSpecular: this.litOn.PHYSICAL_SPECULAR,
|
|
6316
|
+
surfaceOverlay: overlay,
|
|
6317
|
+
reflectionMaps: this.litOn.REFLECTION_MAPS,
|
|
6132
6318
|
maxLights: budget.maxLights,
|
|
6133
6319
|
maxAreaLights: budget.maxAreaLights,
|
|
6134
6320
|
});
|
|
@@ -6954,11 +7140,22 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
6954
7140
|
* Set before `beginFrame` and held until changed, like `setSpeedRush`, which it sits
|
|
6955
7141
|
* beside for the same reason: both describe how the finished image is presented rather
|
|
6956
7142
|
* than anything in the world.
|
|
7143
|
+
*
|
|
7144
|
+
* `maxShare` is the longest smear, as a share of the frame (0.03 unless given, at most half): a
|
|
7145
|
+
* fast turn softens the frame rather than erasing it. **Each drawn object's own motion is
|
|
7146
|
+
* followed only under a reconstruction on WebGPU**, which draws the motion target it reads; here,
|
|
7147
|
+
* and on WebGPU without one, the camera's motion alone smears the frame, and a draw that names
|
|
7148
|
+
* where it was last frame is told so once.
|
|
6957
7149
|
*/
|
|
6958
|
-
setCameraMotionBlur(scale: number): void {
|
|
7150
|
+
setCameraMotionBlur(scale: number, maxShare = MOTION_BLUR_MAX_UV): void {
|
|
6959
7151
|
this.motionBlurScale = Math.min(Math.max(scale, 0), 1);
|
|
7152
|
+
this.motionBlurMax = clampMotionShare(maxShare);
|
|
6960
7153
|
}
|
|
6961
7154
|
|
|
7155
|
+
/** The longest smear, as a share of the frame. See `setCameraMotionBlur`. */
|
|
7156
|
+
private motionBlurMax = MOTION_BLUR_MAX_UV;
|
|
7157
|
+
private warnedObjectBlur = false;
|
|
7158
|
+
|
|
6962
7159
|
/**
|
|
6963
7160
|
* The camera has cut: this frame is a new shot, not the next moment of the last one.
|
|
6964
7161
|
*
|
|
@@ -7170,6 +7367,47 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
7170
7367
|
: this.quality.outputExposure;
|
|
7171
7368
|
}
|
|
7172
7369
|
|
|
7370
|
+
/**
|
|
7371
|
+
* The `filmic` curve this and every later frame is graded through, until it is set again: a
|
|
7372
|
+
* grading volume's five numbers, solved once here into the constants the resolve reads. Ignored
|
|
7373
|
+
* unless `outputTransform` is `filmic`. A number out of range is clamped into it and said once,
|
|
7374
|
+
* since a frame loop must not throw. See `filmicCurve.ts`.
|
|
7375
|
+
*/
|
|
7376
|
+
setFilmicCurve(curve: FilmicCurve): void {
|
|
7377
|
+
if (resolveFilmicCurve(curve, this.filmConstants) && !this.warnedFilmClamped) {
|
|
7378
|
+
this.warnedFilmClamped = true;
|
|
7379
|
+
console.warn(FILMIC_CLAMPED);
|
|
7380
|
+
}
|
|
7381
|
+
}
|
|
7382
|
+
|
|
7383
|
+
private warnedFilmClamped = false;
|
|
7384
|
+
|
|
7385
|
+
/**
|
|
7386
|
+
* The range the frame goes out in: `'high'` where the profile asked for `highDynamicRange`, the
|
|
7387
|
+
* frame holds light past white and the display shows it — on WebGPU — and `'standard'` otherwise.
|
|
7388
|
+
* **Always standard here**: this backend draws into a canvas of eight bits a channel, which has
|
|
7389
|
+
* nothing above white to show. Typed as the question rather than this backend's answer, because
|
|
7390
|
+
* the shared surface is derived from this class.
|
|
7391
|
+
*/
|
|
7392
|
+
readonly displayRange: DisplayRange = 'standard';
|
|
7393
|
+
|
|
7394
|
+
/** Why it is that range, in words, for a game choosing its look and for a bug report. */
|
|
7395
|
+
get displayRangeReason(): string {
|
|
7396
|
+
return this.quality.highDynamicRange
|
|
7397
|
+
? 'WebGL2 draws into an eight-bit canvas, which has no range above white'
|
|
7398
|
+
: 'highDynamicRange was not asked for';
|
|
7399
|
+
}
|
|
7400
|
+
|
|
7401
|
+
/**
|
|
7402
|
+
* How bright paper white and the display's peak are, in one unit, held until changed: their
|
|
7403
|
+
* ratio is how far above white the highlights may run where `displayRange` is `'high'`, and
|
|
7404
|
+
* nothing changes where it is standard, which is always here.
|
|
7405
|
+
*/
|
|
7406
|
+
setDisplayLuminance(paperWhite: number, peak: number): void {
|
|
7407
|
+
void paperWhite;
|
|
7408
|
+
void peak;
|
|
7409
|
+
}
|
|
7410
|
+
|
|
7173
7411
|
/**
|
|
7174
7412
|
* Composite a flat colour over the finished frame — for a cut dipping to white or to black.
|
|
7175
7413
|
*
|
|
@@ -7261,6 +7499,21 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
7261
7499
|
this.sceneTarget.setVignette(strength);
|
|
7262
7500
|
}
|
|
7263
7501
|
|
|
7502
|
+
/**
|
|
7503
|
+
* The lens's colour fringe, held until changed: `intensity` is a percentage, 0 is none, and red
|
|
7504
|
+
* and green are pulled toward the centre by their wavelength's distance from blue, growing from
|
|
7505
|
+
* `start` — a share of the half-frame, 0 the centre — to the whole of it at the edge. Applied to
|
|
7506
|
+
* scene light at the composite's first read. Needs `screenEffects`, and says so once rather than
|
|
7507
|
+
* doing nothing. See `shaders/fringe.ts`.
|
|
7508
|
+
*/
|
|
7509
|
+
setChromaticAberration(intensity: number, start = 0): void {
|
|
7510
|
+
if (this.sceneTarget === null) {
|
|
7511
|
+
this.warnLookWithoutComposite(intensity);
|
|
7512
|
+
return;
|
|
7513
|
+
}
|
|
7514
|
+
this.sceneTarget.setChromaticAberration(intensity, start);
|
|
7515
|
+
}
|
|
7516
|
+
|
|
7264
7517
|
/**
|
|
7265
7518
|
* Film grain, held until changed: an amplitude in display values (0 is none, 0.03 is a fine
|
|
7266
7519
|
* grain) and this frame's seed. **The seed is the caller's**, because the engine takes time from
|
|
@@ -7451,6 +7704,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
7451
7704
|
|
|
7452
7705
|
beginFrame(clearColor: Vec3): void {
|
|
7453
7706
|
if (this.contextLost) return;
|
|
7707
|
+
/* One frame of the jitter's sequence however many times the frame binds its camera. */
|
|
7708
|
+
this.temporalHistory.nextFrame();
|
|
7454
7709
|
this.skinScattered = false;
|
|
7455
7710
|
this.skinScatter?.reset();
|
|
7456
7711
|
if (this.litAsked) {
|
|
@@ -7520,6 +7775,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
7520
7775
|
}
|
|
7521
7776
|
|
|
7522
7777
|
this.sceneTarget?.begin(this.canvas.width, this.canvas.height);
|
|
7778
|
+
/* The materials' maps on the scene's framebuffer, cleared and disabled: `reflectionKeeps`. */
|
|
7779
|
+
this.reflectionTargets?.begin(this.canvas.width, this.canvas.height);
|
|
7523
7780
|
gl.clearColor(clearColor[0], clearColor[1], clearColor[2], 1);
|
|
7524
7781
|
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
|
7525
7782
|
}
|
|
@@ -7576,7 +7833,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
7576
7833
|
motion = {
|
|
7577
7834
|
reprojection: this.reprojection as Float32Array,
|
|
7578
7835
|
strength: blur,
|
|
7579
|
-
max:
|
|
7836
|
+
max: this.motionBlurMax,
|
|
7580
7837
|
};
|
|
7581
7838
|
}
|
|
7582
7839
|
}
|
|
@@ -7668,7 +7925,11 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
7668
7925
|
|
|
7669
7926
|
/* The grade travels to the resolve only where the mesh pass gave it up. See `bindMeshPass`. */
|
|
7670
7927
|
const lateGrade = this.quality.hdrScene
|
|
7671
|
-
? {
|
|
7928
|
+
? {
|
|
7929
|
+
transform: OUTPUT_TRANSFORM_CODE[this.quality.outputTransform] ?? 0,
|
|
7930
|
+
exposure: this.exposure,
|
|
7931
|
+
film: this.filmConstants,
|
|
7932
|
+
}
|
|
7672
7933
|
: { transform: 0, exposure: 1 };
|
|
7673
7934
|
/*
|
|
7674
7935
|
* Depth of field, on the same ceiling-and-dial split as bloom above: `depthOfField` decides
|
|
@@ -7744,6 +8005,49 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
7744
8005
|
* drawn over a reflective floor rather than under it, and before the post chain so bloom and
|
|
7745
8006
|
* the grade see a reflection exactly as they would see any other part of the picture.
|
|
7746
8007
|
*/
|
|
8008
|
+
/*
|
|
8009
|
+
* **The frame's materials' reflections first**, on the same terms: every pixel the opaque lit
|
|
8010
|
+
* draws left a reflection for, marched and swapped for the probe's share. The maps leave the
|
|
8011
|
+
* scene's framebuffer before the resolve draws into it while sampling them.
|
|
8012
|
+
*/
|
|
8013
|
+
const reflectionTargets = this.reflectionTargets;
|
|
8014
|
+
const probeMap = reflectionTargets?.probeTexture ?? null;
|
|
8015
|
+
const tintMap = reflectionTargets?.tintTexture ?? null;
|
|
8016
|
+
if (
|
|
8017
|
+
reflectionTargets !== null &&
|
|
8018
|
+
this.frameReflections !== null &&
|
|
8019
|
+
probeMap !== null &&
|
|
8020
|
+
tintMap !== null &&
|
|
8021
|
+
this.frameViewProj !== null &&
|
|
8022
|
+
this.sceneTarget !== null
|
|
8023
|
+
) {
|
|
8024
|
+
const scene = this.sceneTarget.colorAttachment();
|
|
8025
|
+
const depth = this.sceneTarget.snapshotDepth(true);
|
|
8026
|
+
if (scene !== null && depth !== null) {
|
|
8027
|
+
this.ssr ??= new SsrPass(this.gl, this.sceneTarget.keepsRange);
|
|
8028
|
+
this.ssr.traceMaterials(
|
|
8029
|
+
scene,
|
|
8030
|
+
depth,
|
|
8031
|
+
probeMap,
|
|
8032
|
+
tintMap,
|
|
8033
|
+
this.frameViewProj,
|
|
8034
|
+
this.frameEye,
|
|
8035
|
+
this.canvas.width,
|
|
8036
|
+
this.canvas.height,
|
|
8037
|
+
REFLECTION_EDGE_FADE,
|
|
8038
|
+
this.frameReflections,
|
|
8039
|
+
);
|
|
8040
|
+
this.sceneTarget.bind();
|
|
8041
|
+
reflectionTargets.detach();
|
|
8042
|
+
this.ssr.resolveMaterials(
|
|
8043
|
+
probeMap,
|
|
8044
|
+
tintMap,
|
|
8045
|
+
this.canvas.width,
|
|
8046
|
+
this.canvas.height,
|
|
8047
|
+
this.frameReflections,
|
|
8048
|
+
);
|
|
8049
|
+
}
|
|
8050
|
+
}
|
|
7747
8051
|
if (this.reflectionQueue.length > 0 && this.frameViewProj !== null) {
|
|
7748
8052
|
const scene = this.sceneTarget?.colorAttachment() ?? null;
|
|
7749
8053
|
const depth = this.sceneTarget?.snapshotDepth(true) ?? null;
|
|
@@ -8127,6 +8431,10 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
8127
8431
|
this.materialHasMaps = false;
|
|
8128
8432
|
this.currentMaterial = null;
|
|
8129
8433
|
this.currentSurfaceTexture = null;
|
|
8434
|
+
/* Nor an overlay: the atlas's unit is given the empty texture with the refraction copy's. */
|
|
8435
|
+
this.overlayFloats.fill(0);
|
|
8436
|
+
this.overlayMaps = null;
|
|
8437
|
+
this.overlayTexture = null;
|
|
8130
8438
|
/* Nor an ambient of its own: the frame's, until a draw asks. See `setAmbientSH`. */
|
|
8131
8439
|
this.ambientSH.fill(0);
|
|
8132
8440
|
/* No reflection and an environment at its own brightness; the emission the world asks for. */
|
|
@@ -8283,8 +8591,10 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
8283
8591
|
* unless a draw sets the strength above zero.
|
|
8284
8592
|
*/
|
|
8285
8593
|
gl.activeTexture(gl.TEXTURE0 + REFRACT_SCENE_TEXTURE_UNIT);
|
|
8286
|
-
gl.bindTexture(gl.TEXTURE_2D, this.emptyTexture2D);
|
|
8594
|
+
gl.bindTexture(gl.TEXTURE_2D, this.overlayTexture ?? this.emptyTexture2D);
|
|
8287
8595
|
gl.uniform1i(u['uRefractScene'] ?? null, REFRACT_SCENE_TEXTURE_UNIT);
|
|
8596
|
+
/* The draws' overlay, none at a pass's start, and its atlas above: see `setSurfaceOverlay`. */
|
|
8597
|
+
gl.uniform4fv(u['uOverlay'] ?? null, this.overlayFloats);
|
|
8288
8598
|
/* Neither refracting nor glass, which is every draw that does not say otherwise. See
|
|
8289
8599
|
`bindSeeThrough` for the four numbers in the one vector. */
|
|
8290
8600
|
gl.uniform4f(u['uSeeThrough'] ?? null, 0, 0, 0, 0);
|
|
@@ -8693,9 +9003,18 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
8693
9003
|
* here, so there is no motion target for this to be written into, and a mover passed here is
|
|
8694
9004
|
* neither read nor advanced.
|
|
8695
9005
|
*/
|
|
8696
|
-
|
|
9006
|
+
previousModel: ReadonlyMat4 | Mover | null = null,
|
|
8697
9007
|
): void {
|
|
8698
9008
|
if (this.contextLost) return;
|
|
9009
|
+
/* A mover's own motion is not blurred here: see `setCameraMotionBlur`. Said once. */
|
|
9010
|
+
if (
|
|
9011
|
+
previousModel !== null &&
|
|
9012
|
+
!this.warnedObjectBlur &&
|
|
9013
|
+
this.quality.cameraMotionBlur * this.motionBlurScale > 0
|
|
9014
|
+
) {
|
|
9015
|
+
this.warnedObjectBlur = true;
|
|
9016
|
+
console.warn(OBJECT_BLUR_UNKEPT);
|
|
9017
|
+
}
|
|
8699
9018
|
/* Geometry that has not all arrived is not drawn, nor a mesh with none. See `Mesh.complete`
|
|
8700
9019
|
and `Mesh.indexCount`. */
|
|
8701
9020
|
if (!mesh.complete || mesh.indexCount === 0) return;
|
|
@@ -8726,7 +9045,11 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
8726
9045
|
* light and its colour, each into the blur's own target.
|
|
8727
9046
|
*/
|
|
8728
9047
|
const splits = this.skinSplits();
|
|
9048
|
+
/* An opaque lit draw into the frame writes its material's reflection too: see `reflectionKeeps`. */
|
|
9049
|
+
const keeps = this.reflectionKeeps();
|
|
9050
|
+
if (keeps) this.reflectionTargets?.on();
|
|
8729
9051
|
this.drawMeshHalf(mesh, model, depthLayer, tint, splits ? 'scene' : 'whole');
|
|
9052
|
+
if (keeps) this.reflectionTargets?.off();
|
|
8730
9053
|
if (splits && this.skinScatter !== null) {
|
|
8731
9054
|
this.skinScatter.begin();
|
|
8732
9055
|
this.drawMeshHalf(mesh, model, depthLayer, tint, 'diffuse');
|
|
@@ -8741,6 +9064,21 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
8741
9064
|
* skin yet, this is the frame's own picture — not a mirror's or a probe's, nor an overlay after
|
|
8742
9065
|
* the frame — and the blur's target can be had.
|
|
8743
9066
|
*/
|
|
9067
|
+
/**
|
|
9068
|
+
* Whether a draw now is the frame's own, opaque and lit, and so writes the two maps beside its
|
|
9069
|
+
* colour: not a mirror's, a probe's or a capture's, whose framebuffers have no maps, and not after
|
|
9070
|
+
* the frame. See `reflectionSurfaceTargets.ts`.
|
|
9071
|
+
*/
|
|
9072
|
+
private reflectionKeeps(): boolean {
|
|
9073
|
+
return (
|
|
9074
|
+
this.reflectionTargets !== null &&
|
|
9075
|
+
!this.reflectionPassActive &&
|
|
9076
|
+
!this.probePassActive &&
|
|
9077
|
+
!this.capturePassActive &&
|
|
9078
|
+
!this.framePresented
|
|
9079
|
+
);
|
|
9080
|
+
}
|
|
9081
|
+
|
|
8744
9082
|
private skinSplits(): boolean {
|
|
8745
9083
|
const skin = this.skinScatter;
|
|
8746
9084
|
const scene = this.sceneTarget;
|
|
@@ -8769,10 +9107,16 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
8769
9107
|
const scene = this.sceneTarget;
|
|
8770
9108
|
const projection = this.frameProjection;
|
|
8771
9109
|
if (skin === null || scene === null || this.skinScattered) return;
|
|
9110
|
+
/*
|
|
9111
|
+
* **Not the frame's picture, so not the frame's moment**: a blended draw in a mirror, a probe
|
|
9112
|
+
* bake or a scene capture lands over none of the frame's skin. Latching there switched the
|
|
9113
|
+
* spread off for the frame, so a capture with a glowing sign in it left every face drawn after
|
|
9114
|
+
* it flat. `scripts/capture-check.mjs`.
|
|
9115
|
+
*/
|
|
9116
|
+
if (this.reflectionPassActive || this.probePassActive || this.capturePassActive) return;
|
|
8772
9117
|
/* Latched whether or not skin was drawn: a skin drawn after this point is the whole surface. */
|
|
8773
9118
|
this.skinScattered = true;
|
|
8774
9119
|
if (!skin.pending) return;
|
|
8775
|
-
if (this.reflectionPassActive || this.probePassActive || this.capturePassActive) return;
|
|
8776
9120
|
if (projection === null) return;
|
|
8777
9121
|
const depth = scene.snapshotDepth(true);
|
|
8778
9122
|
mat4.invert(this.skinInverseProjection, projection);
|