@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
|
@@ -4,11 +4,17 @@ import { FOG_OFF, FOG_RECEDE, fogModeOf } from '../drawFog.js';
|
|
|
4
4
|
import { blendCovering, blendKeeping } from '../../sceneCoverage.js';
|
|
5
5
|
import { mat4 } from 'gl-matrix';
|
|
6
6
|
import { GL_DEPTH_REMAP, MAX_DEPTH_LAYER, depthClearFor, depthOffsetForLayer, GL_SHADOW_REMAP, REVERSED_DEPTH, SHADOW_DEPTH_CLEAR, glDepthFuncEqual, glShadowDepthFunc, } from '../../depthConvention.js';
|
|
7
|
+
import { maskOf } from '../../frame/resources.js';
|
|
8
|
+
/** The two copies a registered pass may declare it reads. See `drawPass`. */
|
|
9
|
+
const COLOR_SNAPSHOT = maskOf('colorSnapshot');
|
|
10
|
+
const DEPTH_SNAPSHOT = maskOf('depthSnapshot');
|
|
7
11
|
import { resolveGlass } from '../../glass.js';
|
|
8
12
|
import { PickableSet } from '../../pickable.js';
|
|
9
13
|
import { TEMPORAL_HISTORY_BLEND, TemporalHistory, jitterOffset, jitterProjection, } from '../../temporalAa.js';
|
|
10
14
|
import { DecalPass } from '../../decalPass.js';
|
|
11
15
|
import { SsrPass } from '../../ssrPass.js';
|
|
16
|
+
import { ReflectionSurfaceTargets } from '../../reflectionSurfaceTargets.js';
|
|
17
|
+
import { frameReflectionsRefused, resolveFrameReflections, } from '../../frameReflections.js';
|
|
12
18
|
import { GlobalMediumPass } from '../../globalMediumPass.js';
|
|
13
19
|
import { DEFAULT_GLOBAL_MEDIUM, WEAK_GPU_MEDIUM_STEPS, mediumActive, resolveGlobalMedium, } from '../../globalMedium.js';
|
|
14
20
|
import { REFLECTION_EDGE_FADE, ReflectionQueue } from '../../screenSpaceReflection.js';
|
|
@@ -28,7 +34,8 @@ import { attachContextLoss } from '../../contextLoss.js';
|
|
|
28
34
|
import { isWeakGpuFamily, webgl2RendererName } from '../../gpuCapability.js';
|
|
29
35
|
import { GpuTimer } from '../../gpuTimer.js';
|
|
30
36
|
import { compileProgram, uniformLocations } from '../../shader.js';
|
|
31
|
-
import { FULL_LIGHT_BUDGET, nextLightBudget, planLightBudget, } from '../../uniformVectorBudget.js';
|
|
37
|
+
import { countUniformVectors, FULL_LIGHT_BUDGET, nextLightBudget, planLightBudget, } from '../../uniformVectorBudget.js';
|
|
38
|
+
import { OVERLAY_FLOATS, overlayLays, packSurfaceOverlay, } from '../../surfaceOverlay.js';
|
|
32
39
|
import { FILM_FRAG, FILM_VERT } from '../../shaders/film.js';
|
|
33
40
|
import { SceneTarget } from '../../sceneTarget.js';
|
|
34
41
|
import { FILM_LOOK_WITHOUT_COMPOSITE } from '../../shaders/filmLook.js';
|
|
@@ -104,8 +111,9 @@ import { SCATTER_DEPTH_VERT, SCATTER_FRAG, SCATTER_VERT } from '../../shaders/sc
|
|
|
104
111
|
import { FlockRenderer } from '../../flockRenderer.js';
|
|
105
112
|
import { TextRenderer } from '../../textRenderer.js';
|
|
106
113
|
import { SdfTextRenderer } from '../../sdfTextRenderer.js';
|
|
107
|
-
import { MOTION_BLUR_MAX_UV, RUSH_REACH_UV } from '../../vertexDefaults.js';
|
|
108
|
-
import { OUTPUT_TRANSFORM_CODE } from '../../vertexDefaults.js';
|
|
114
|
+
import { MOTION_BLUR_MAX_UV, OBJECT_BLUR_UNKEPT, RUSH_REACH_UV, clampMotionShare, } from '../../vertexDefaults.js';
|
|
115
|
+
import { OUTPUT_TRANSFORM_CODE, forwardTransformCode } from '../../vertexDefaults.js';
|
|
116
|
+
import { DEFAULT_FILMIC_CURVE, FILMIC_CLAMPED, FILMIC_CONSTANTS, FILMIC_WITHOUT_COMPOSITE, resolveFilmicCurve, } from '../../filmicCurve.js';
|
|
109
117
|
import { deviceSnappedCellSize } from '../../textLayout.js';
|
|
110
118
|
import { WindStreakRenderer } from '../../windStreakRenderer.js';
|
|
111
119
|
import { InstancedBatch } from './instanced.js';
|
|
@@ -604,6 +612,10 @@ export class WebGL2Renderer {
|
|
|
604
612
|
reflectionQueue = new ReflectionQueue();
|
|
605
613
|
/** Built on the first frame that submits a surface, and never for a scene with none. */
|
|
606
614
|
ssr = null;
|
|
615
|
+
/** The frame's materials' reflections, resolved; null where off. See `frameReflections.ts`. */
|
|
616
|
+
frameReflections;
|
|
617
|
+
/** Their two maps on the scene's framebuffer, null where they cannot be drawn. */
|
|
618
|
+
reflectionTargets;
|
|
607
619
|
/** Said once where a reflection cannot be traced at all. */
|
|
608
620
|
reflectionsRefused = false;
|
|
609
621
|
/** Where the frame's camera stands, which is what gives a reconstructed normal its sign. */
|
|
@@ -674,6 +686,8 @@ export class WebGL2Renderer {
|
|
|
674
686
|
* exactly as it asked to be and nothing about it changes.
|
|
675
687
|
*/
|
|
676
688
|
exposure;
|
|
689
|
+
/** The `filmic` curve's eight constants, held until `setFilmicCurve` is called again. */
|
|
690
|
+
filmConstants = new Float32Array(FILMIC_CONSTANTS);
|
|
677
691
|
/**
|
|
678
692
|
* This frame's veil colour, and how much of it to composite. See `setFrameVeil`.
|
|
679
693
|
*
|
|
@@ -875,6 +889,15 @@ export class WebGL2Renderer {
|
|
|
875
889
|
warnedNoPointShadowArray = false;
|
|
876
890
|
/** Said once, for the retired ambient dial. */
|
|
877
891
|
warnedEnvironmentAmbient = false;
|
|
892
|
+
/** The overlay the draws wear, `uOverlay`: see `setSurfaceOverlay`. */
|
|
893
|
+
overlayFloats = new Float32Array(OVERLAY_FLOATS);
|
|
894
|
+
/** Its atlas, and the 2D copy of it bound where the refraction copy goes. */
|
|
895
|
+
overlayMaps = null;
|
|
896
|
+
overlayTexture = null;
|
|
897
|
+
/** Whether this device's lit stage has room for the overlay's vectors: asked once. */
|
|
898
|
+
overlayRoom = null;
|
|
899
|
+
warnedOverlayRoom = false;
|
|
900
|
+
warnedOverlayBlocks = false;
|
|
878
901
|
/** Whether a probe is being baked, so the passes that would recurse into one can tell. */
|
|
879
902
|
probePassActive = false;
|
|
880
903
|
/**
|
|
@@ -1503,6 +1526,8 @@ export class WebGL2Renderer {
|
|
|
1503
1526
|
* call. `init` runs now rather than at the first frame, because building a pipeline in the
|
|
1504
1527
|
* frame loop is the allocation the house rules are about.
|
|
1505
1528
|
*/
|
|
1529
|
+
/** Each registered pass's declared reads, as a mask, resolved once at registration. */
|
|
1530
|
+
passReads = new Map();
|
|
1506
1531
|
registerPass(definition) {
|
|
1507
1532
|
const handle = registerIn(this.passes, definition);
|
|
1508
1533
|
/*
|
|
@@ -1522,6 +1547,8 @@ export class WebGL2Renderer {
|
|
|
1522
1547
|
};
|
|
1523
1548
|
if (definition.prepare !== undefined)
|
|
1524
1549
|
this.preparingPasses++;
|
|
1550
|
+
/* Resolved once: `maskOf` builds an array, and the frame path may not. */
|
|
1551
|
+
this.passReads.set(handle, maskOf(...(definition.reads ?? [])));
|
|
1525
1552
|
definition.init?.(this.passDevice);
|
|
1526
1553
|
return handle;
|
|
1527
1554
|
}
|
|
@@ -1573,10 +1600,28 @@ export class WebGL2Renderer {
|
|
|
1573
1600
|
gl: this.gl,
|
|
1574
1601
|
outputTransform: 0,
|
|
1575
1602
|
outputExposure: 1,
|
|
1603
|
+
sceneColor: null,
|
|
1604
|
+
sceneDepth: null,
|
|
1576
1605
|
};
|
|
1577
1606
|
const context = this.passContext;
|
|
1578
1607
|
context.outputTransform = this.gradeCode();
|
|
1579
1608
|
context.outputExposure = this.gradeExposure();
|
|
1609
|
+
/*
|
|
1610
|
+
* The copies a pass declared it reads, taken here with everything drawn before it: colour
|
|
1611
|
+
* afresh at each reading pass, as WebGPU's `takePassColour` does, so a full-screen effect drawn
|
|
1612
|
+
* last reads the whole frame; depth afresh too, since a blit is the whole cost of either here.
|
|
1613
|
+
* Not in a mirror, a probe's face or a capture, whose pictures are not the frame's.
|
|
1614
|
+
*/
|
|
1615
|
+
const reads = this.passReads.get(handle) ?? 0;
|
|
1616
|
+
const own = !this.reflectionPassActive && !this.probePassActive && !this.capturePassActive;
|
|
1617
|
+
context.sceneColor =
|
|
1618
|
+
own && (reads & COLOR_SNAPSHOT) !== 0
|
|
1619
|
+
? (this.sceneTarget?.snapshotColor(true) ?? null)
|
|
1620
|
+
: null;
|
|
1621
|
+
context.sceneDepth =
|
|
1622
|
+
own && (reads & DEPTH_SNAPSHOT) !== 0
|
|
1623
|
+
? (this.sceneTarget?.snapshotDepth(true) ?? null)
|
|
1624
|
+
: null;
|
|
1580
1625
|
definition.draw(context);
|
|
1581
1626
|
}
|
|
1582
1627
|
/** Let go of a pass. A handle kept past this draws nothing; see `PassHandle`. */
|
|
@@ -1584,6 +1629,7 @@ export class WebGL2Renderer {
|
|
|
1584
1629
|
const definition = unregisterIn(this.passes, handle);
|
|
1585
1630
|
if (definition?.prepare !== undefined)
|
|
1586
1631
|
this.preparingPasses--;
|
|
1632
|
+
this.passReads.delete(handle);
|
|
1587
1633
|
if (definition !== undefined && this.passDevice !== null)
|
|
1588
1634
|
definition.dispose?.(this.passDevice);
|
|
1589
1635
|
}
|
|
@@ -1734,10 +1780,10 @@ export class WebGL2Renderer {
|
|
|
1734
1780
|
gl.bindTexture(gl.TEXTURE_2D, this.clusterTexture);
|
|
1735
1781
|
gl.uniform1i(u['uClusterTable'] ?? null, CLUSTER_TABLE_TEXTURE_UNIT);
|
|
1736
1782
|
gl.uniform1i(u['uClustered'] ?? null, on ? 1 : 0);
|
|
1737
|
-
if (!on)
|
|
1738
|
-
return;
|
|
1739
1783
|
/*
|
|
1740
|
-
* The frustum, read off the projection rather than taken as a parameter
|
|
1784
|
+
* The frustum, read off the projection rather than taken as a parameter — **and with the view,
|
|
1785
|
+
* written whether or not the lights are clustered**: a surface overlay's rim finds its place on
|
|
1786
|
+
* the screen through the same two (`overlayScreen`).
|
|
1741
1787
|
*
|
|
1742
1788
|
* A perspective matrix carries `1 / tan(fovY/2)` at [5] and that over the aspect at [0], and
|
|
1743
1789
|
* the near and far planes in [14] and [10]. Deriving them here means the binner and the shader
|
|
@@ -1755,6 +1801,8 @@ export class WebGL2Renderer {
|
|
|
1755
1801
|
this.clusterFrustum[3] = aspect;
|
|
1756
1802
|
gl.uniform4fv(u['uClusterFrustum'] ?? null, this.clusterFrustum);
|
|
1757
1803
|
gl.uniformMatrix4fv(u['uView'] ?? null, false, camera.view);
|
|
1804
|
+
if (!on)
|
|
1805
|
+
return;
|
|
1758
1806
|
this.clusterTable ??= createClusterTable();
|
|
1759
1807
|
/* The rectangles past the fixed arm's go into the table. See `ClusterLightSet.areas`. */
|
|
1760
1808
|
const lights = fillClusterLightSet(env, this.clusterLights, this.shadedAreaLights);
|
|
@@ -1828,6 +1876,11 @@ export class WebGL2Renderer {
|
|
|
1828
1876
|
this.quality = resolveRenderQuality(qualityOptions);
|
|
1829
1877
|
/* The grade this world asked for, until a frame says otherwise. See `setOutputExposure`. */
|
|
1830
1878
|
this.exposure = this.quality.outputExposure;
|
|
1879
|
+
resolveFilmicCurve(DEFAULT_FILMIC_CURVE, this.filmConstants);
|
|
1880
|
+
if (this.quality.outputTransform === 'filmic' &&
|
|
1881
|
+
!(this.quality.screenEffects && this.quality.hdrScene)) {
|
|
1882
|
+
console.warn(FILMIC_WITHOUT_COMPOSITE);
|
|
1883
|
+
}
|
|
1831
1884
|
/*
|
|
1832
1885
|
* Multisampling, but only when anything can use it.
|
|
1833
1886
|
*
|
|
@@ -2086,6 +2139,29 @@ export class WebGL2Renderer {
|
|
|
2086
2139
|
this.sceneTarget = this.quality.screenEffects
|
|
2087
2140
|
? new SceneTarget(gl, this.quality.sceneSamples, this.quality.hdrScene, this.quality.discardResolvedAttachments)
|
|
2088
2141
|
: null;
|
|
2142
|
+
/*
|
|
2143
|
+
* The frame's materials' reflections, where the frame can carry them: scene light to swap the
|
|
2144
|
+
* probe's share in, and one sample, which the trace's depth copy and both maps are. Said once
|
|
2145
|
+
* where it cannot, in the words the other backend uses. See `frameReflections.ts`.
|
|
2146
|
+
*/
|
|
2147
|
+
this.frameReflections = resolveFrameReflections(this.quality.screenSpaceReflections);
|
|
2148
|
+
const reflectionsRefusal = this.frameReflections === null
|
|
2149
|
+
? null
|
|
2150
|
+
: this.sceneTarget === null || !this.sceneTarget.keepsRange
|
|
2151
|
+
? 'the frame keeps no scene light to swap the probe in (`screenEffects` and `hdrScene`)'
|
|
2152
|
+
: this.sceneTarget.sampleCount > 1
|
|
2153
|
+
? 'a multisampled frame has no single depth or colour to march through'
|
|
2154
|
+
: null;
|
|
2155
|
+
if (reflectionsRefusal !== null) {
|
|
2156
|
+
console.warn(`WebGL2: ${frameReflectionsRefused(reflectionsRefusal)}`);
|
|
2157
|
+
}
|
|
2158
|
+
this.reflectionTargets =
|
|
2159
|
+
this.frameReflections !== null && reflectionsRefusal === null
|
|
2160
|
+
? new ReflectionSurfaceTargets(gl)
|
|
2161
|
+
: null;
|
|
2162
|
+
/* The lit programs write the maps from the first frame they are rebuilt in. See `litOn`. */
|
|
2163
|
+
if (this.reflectionTargets !== null)
|
|
2164
|
+
this.askLit('REFLECTION_MAPS');
|
|
2089
2165
|
/* Screen-space skin needs the composite to add into; without one it is pre-integrated, said once. */
|
|
2090
2166
|
if (this.quality.skinScattering === 'screen-space' && this.sceneTarget === null) {
|
|
2091
2167
|
console.warn("WebGL2: skinScattering 'screen-space' needs screenEffects to add into; skin is pre-integrated");
|
|
@@ -2643,10 +2719,15 @@ export class WebGL2Renderer {
|
|
|
2643
2719
|
if (this.contextLost)
|
|
2644
2720
|
return;
|
|
2645
2721
|
texture.update(this.gl, source);
|
|
2722
|
+
/* New pixels are a new copy for the overlay's slot: see `SurfaceTexture.flatLayer`. */
|
|
2723
|
+
if (texture === this.overlayMaps)
|
|
2724
|
+
this.bindOverlayMaps(texture);
|
|
2646
2725
|
}
|
|
2647
2726
|
disposeSurfaceTexture(texture) {
|
|
2648
2727
|
if (this.contextLost)
|
|
2649
2728
|
return;
|
|
2729
|
+
if (texture === this.overlayMaps)
|
|
2730
|
+
this.bindOverlayMaps(null);
|
|
2650
2731
|
texture.dispose(this.gl);
|
|
2651
2732
|
this.captureTargets.get(texture)?.dispose(this.gl);
|
|
2652
2733
|
this.captureTargets.delete(texture);
|
|
@@ -2981,6 +3062,8 @@ export class WebGL2Renderer {
|
|
|
2981
3062
|
if (material?.physicalSpecular === true)
|
|
2982
3063
|
this.askLit('PHYSICAL_SPECULAR');
|
|
2983
3064
|
gl.uniform4fv(u['uModelParams'] ?? null, this.modelParams);
|
|
3065
|
+
/* The draw's overlay, which a program born mid-pass must wear too: see `setSurfaceOverlay`. */
|
|
3066
|
+
gl.uniform4fv(u['uOverlay'] ?? null, this.overlayFloats);
|
|
2984
3067
|
/* The albedo's effects table, or the stand-in the shader reads as none. Before the early
|
|
2985
3068
|
return, so a material with no albedo still leaves a complete texture on the unit. */
|
|
2986
3069
|
if (albedo === null) {
|
|
@@ -3078,6 +3161,67 @@ export class WebGL2Renderer {
|
|
|
3078
3161
|
this.environmentDials[1] = Math.min(1, Math.max(0, amount));
|
|
3079
3162
|
this.writeFlatVector('uEnvironmentDials', this.environmentDials);
|
|
3080
3163
|
}
|
|
3164
|
+
/**
|
|
3165
|
+
* The overlay the following draws wear — a rim of light, a dissolve, wrinkles by region — or null
|
|
3166
|
+
* for none. Pass state, as the material is: `bindMeshPass` puts it back to none. See
|
|
3167
|
+
* `surfaceOverlay.ts` for what it lays and `shaders/flat/overlay.ts` for how.
|
|
3168
|
+
*
|
|
3169
|
+
* **Refused, said once, where the lit stage has no room for it.** Fifteen vectors more than the
|
|
3170
|
+
* program this device fits: counted from the source as `fitFlatProgram` counts, and on a device
|
|
3171
|
+
* whose limit is the 256 an Adreno 740 reports, the eight-light build at 254 has none. The draws
|
|
3172
|
+
* then wear nothing, which is the picture a game without the feature draws. Its atlas is bound as
|
|
3173
|
+
* a 2D copy of its first layer, in the refraction copy's unit (`SurfaceTexture.flatLayer`); a
|
|
3174
|
+
* compressed atlas has no copy and is read as none, also said once.
|
|
3175
|
+
*/
|
|
3176
|
+
setSurfaceOverlay(overlay) {
|
|
3177
|
+
if (this.contextLost)
|
|
3178
|
+
return;
|
|
3179
|
+
let wanted = overlay;
|
|
3180
|
+
if (overlayLays(wanted) && !this.overlayFits()) {
|
|
3181
|
+
if (!this.warnedOverlayRoom) {
|
|
3182
|
+
this.warnedOverlayRoom = true;
|
|
3183
|
+
console.warn('Renderer: setSurfaceOverlay is refused on this device. The overlay takes fifteen ' +
|
|
3184
|
+
'fragment uniform vectors and the lit shader already uses all but ' +
|
|
3185
|
+
`${this.overlayRoomLeft()} of the ${this.fragmentVectorLimit()} this GPU offers; ` +
|
|
3186
|
+
'draws wear no overlay. A lower `maxLights` makes the room, or WebGPU has it.');
|
|
3187
|
+
}
|
|
3188
|
+
wanted = null;
|
|
3189
|
+
}
|
|
3190
|
+
packSurfaceOverlay(wanted, this.overlayFloats);
|
|
3191
|
+
if (overlayLays(wanted))
|
|
3192
|
+
this.askLit('SURFACE_OVERLAY');
|
|
3193
|
+
this.materials.dirty();
|
|
3194
|
+
this.writeFlatVector('uOverlay', this.overlayFloats);
|
|
3195
|
+
this.bindOverlayMaps((wanted?.maps ?? null));
|
|
3196
|
+
}
|
|
3197
|
+
/** The overlay's atlas into the refraction copy's unit, as a 2D copy, or the empty texture. */
|
|
3198
|
+
bindOverlayMaps(maps) {
|
|
3199
|
+
const { gl } = this;
|
|
3200
|
+
this.overlayMaps = maps;
|
|
3201
|
+
this.overlayTexture = maps === null ? null : maps.flatLayer(gl);
|
|
3202
|
+
if (maps !== null && this.overlayTexture === null && !this.warnedOverlayBlocks) {
|
|
3203
|
+
this.warnedOverlayBlocks = true;
|
|
3204
|
+
console.warn("Renderer: a surface overlay's maps are a compressed texture, which WebGL2 cannot copy " +
|
|
3205
|
+
'into the 2D image the lit stage reads them from. The overlay is drawn without its ' +
|
|
3206
|
+
'images; upload the atlas from an image instead.');
|
|
3207
|
+
}
|
|
3208
|
+
gl.activeTexture(gl.TEXTURE0 + REFRACT_SCENE_TEXTURE_UNIT);
|
|
3209
|
+
gl.bindTexture(gl.TEXTURE_2D, this.overlayTexture ?? this.emptyTexture2D);
|
|
3210
|
+
}
|
|
3211
|
+
/** The most fragment uniform vectors this GPU offers a stage. */
|
|
3212
|
+
fragmentVectorLimit() {
|
|
3213
|
+
return this.gl.getParameter(this.gl.MAX_FRAGMENT_UNIFORM_VECTORS);
|
|
3214
|
+
}
|
|
3215
|
+
/** What the lit shader at this renderer's budget leaves of the limit, with no overlay. */
|
|
3216
|
+
overlayRoomLeft() {
|
|
3217
|
+
return (this.fragmentVectorLimit() - countUniformVectors(this.flatFragAt(this.lightBudget, false)));
|
|
3218
|
+
}
|
|
3219
|
+
/** Whether the lit shader with the overlay compiled in fits this GPU: counted once. */
|
|
3220
|
+
overlayFits() {
|
|
3221
|
+
this.overlayRoom ??=
|
|
3222
|
+
countUniformVectors(this.flatFragAt(this.lightBudget, true)) <= this.fragmentVectorLimit();
|
|
3223
|
+
return this.overlayRoom;
|
|
3224
|
+
}
|
|
3081
3225
|
/**
|
|
3082
3226
|
* How bright the environment the surfaces drawn next reflect is. 1 is the default and identity.
|
|
3083
3227
|
*
|
|
@@ -3496,10 +3640,15 @@ export class WebGL2Renderer {
|
|
|
3496
3640
|
gl.enable(gl.SAMPLE_ALPHA_TO_COVERAGE);
|
|
3497
3641
|
if (ditheredBlend)
|
|
3498
3642
|
gl.uniform4fv(u['uCutout'] ?? null, this.cutoutUniform(this.currentMaterial, true));
|
|
3643
|
+
const keeps = !blend && this.reflectionKeeps();
|
|
3644
|
+
if (keeps)
|
|
3645
|
+
this.reflectionTargets?.on();
|
|
3499
3646
|
if (culling && cullsInstances(count, mesh.indexCount))
|
|
3500
3647
|
batch.drawCulled(gl, data, this.frustum);
|
|
3501
3648
|
else
|
|
3502
3649
|
batch.draw(gl, count);
|
|
3650
|
+
if (keeps)
|
|
3651
|
+
this.reflectionTargets?.off();
|
|
3503
3652
|
if (coverage)
|
|
3504
3653
|
gl.disable(gl.SAMPLE_ALPHA_TO_COVERAGE);
|
|
3505
3654
|
if (ditheredBlend)
|
|
@@ -3570,7 +3719,8 @@ export class WebGL2Renderer {
|
|
|
3570
3719
|
const { gl } = this;
|
|
3571
3720
|
gl.uniform4f(u['uSeeThrough'] ?? null, 0, 0, 0, 0);
|
|
3572
3721
|
gl.activeTexture(gl.TEXTURE0 + REFRACT_SCENE_TEXTURE_UNIT);
|
|
3573
|
-
|
|
3722
|
+
/* The overlay's atlas, where one is held: it shares the unit. See `setSurfaceOverlay`. */
|
|
3723
|
+
gl.bindTexture(gl.TEXTURE_2D, this.overlayTexture ?? this.emptyTexture2D);
|
|
3574
3724
|
}
|
|
3575
3725
|
disposeMesh(mesh) {
|
|
3576
3726
|
/*
|
|
@@ -3781,7 +3931,7 @@ export class WebGL2Renderer {
|
|
|
3781
3931
|
*/
|
|
3782
3932
|
gradeCode() {
|
|
3783
3933
|
return this.sceneTarget === null || !this.quality.hdrScene
|
|
3784
|
-
?
|
|
3934
|
+
? forwardTransformCode(this.quality.outputTransform)
|
|
3785
3935
|
: 0;
|
|
3786
3936
|
}
|
|
3787
3937
|
gradeExposure() {
|
|
@@ -4784,7 +4934,7 @@ export class WebGL2Renderer {
|
|
|
4784
4934
|
* throws a sentence with the device's numbers in it rather than a bare driver string.
|
|
4785
4935
|
*/
|
|
4786
4936
|
/** The lit fragment source this renderer's profile compiles, at a light budget. */
|
|
4787
|
-
flatFragAt(budget) {
|
|
4937
|
+
flatFragAt(budget, overlay = this.litOn.SURFACE_OVERLAY) {
|
|
4788
4938
|
return flatFrag({
|
|
4789
4939
|
pointShadows: this.quality.pointShadows,
|
|
4790
4940
|
directionalShadows: this.quality.directionalShadows,
|
|
@@ -4797,6 +4947,8 @@ export class WebGL2Renderer {
|
|
|
4797
4947
|
surfaceEffects: this.litOn.SURFACE_EFFECTS,
|
|
4798
4948
|
driftLight: this.litOn.DRIFT_LIGHT,
|
|
4799
4949
|
physicalSpecular: this.litOn.PHYSICAL_SPECULAR,
|
|
4950
|
+
surfaceOverlay: overlay,
|
|
4951
|
+
reflectionMaps: this.litOn.REFLECTION_MAPS,
|
|
4800
4952
|
maxLights: budget.maxLights,
|
|
4801
4953
|
maxAreaLights: budget.maxAreaLights,
|
|
4802
4954
|
});
|
|
@@ -5468,10 +5620,20 @@ export class WebGL2Renderer {
|
|
|
5468
5620
|
* Set before `beginFrame` and held until changed, like `setSpeedRush`, which it sits
|
|
5469
5621
|
* beside for the same reason: both describe how the finished image is presented rather
|
|
5470
5622
|
* than anything in the world.
|
|
5623
|
+
*
|
|
5624
|
+
* `maxShare` is the longest smear, as a share of the frame (0.03 unless given, at most half): a
|
|
5625
|
+
* fast turn softens the frame rather than erasing it. **Each drawn object's own motion is
|
|
5626
|
+
* followed only under a reconstruction on WebGPU**, which draws the motion target it reads; here,
|
|
5627
|
+
* and on WebGPU without one, the camera's motion alone smears the frame, and a draw that names
|
|
5628
|
+
* where it was last frame is told so once.
|
|
5471
5629
|
*/
|
|
5472
|
-
setCameraMotionBlur(scale) {
|
|
5630
|
+
setCameraMotionBlur(scale, maxShare = MOTION_BLUR_MAX_UV) {
|
|
5473
5631
|
this.motionBlurScale = Math.min(Math.max(scale, 0), 1);
|
|
5632
|
+
this.motionBlurMax = clampMotionShare(maxShare);
|
|
5474
5633
|
}
|
|
5634
|
+
/** The longest smear, as a share of the frame. See `setCameraMotionBlur`. */
|
|
5635
|
+
motionBlurMax = MOTION_BLUR_MAX_UV;
|
|
5636
|
+
warnedObjectBlur = false;
|
|
5475
5637
|
/**
|
|
5476
5638
|
* The camera has cut: this frame is a new shot, not the next moment of the last one.
|
|
5477
5639
|
*
|
|
@@ -5671,6 +5833,42 @@ export class WebGL2Renderer {
|
|
|
5671
5833
|
? Math.max(exposure, 1e-3)
|
|
5672
5834
|
: this.quality.outputExposure;
|
|
5673
5835
|
}
|
|
5836
|
+
/**
|
|
5837
|
+
* The `filmic` curve this and every later frame is graded through, until it is set again: a
|
|
5838
|
+
* grading volume's five numbers, solved once here into the constants the resolve reads. Ignored
|
|
5839
|
+
* unless `outputTransform` is `filmic`. A number out of range is clamped into it and said once,
|
|
5840
|
+
* since a frame loop must not throw. See `filmicCurve.ts`.
|
|
5841
|
+
*/
|
|
5842
|
+
setFilmicCurve(curve) {
|
|
5843
|
+
if (resolveFilmicCurve(curve, this.filmConstants) && !this.warnedFilmClamped) {
|
|
5844
|
+
this.warnedFilmClamped = true;
|
|
5845
|
+
console.warn(FILMIC_CLAMPED);
|
|
5846
|
+
}
|
|
5847
|
+
}
|
|
5848
|
+
warnedFilmClamped = false;
|
|
5849
|
+
/**
|
|
5850
|
+
* The range the frame goes out in: `'high'` where the profile asked for `highDynamicRange`, the
|
|
5851
|
+
* frame holds light past white and the display shows it — on WebGPU — and `'standard'` otherwise.
|
|
5852
|
+
* **Always standard here**: this backend draws into a canvas of eight bits a channel, which has
|
|
5853
|
+
* nothing above white to show. Typed as the question rather than this backend's answer, because
|
|
5854
|
+
* the shared surface is derived from this class.
|
|
5855
|
+
*/
|
|
5856
|
+
displayRange = 'standard';
|
|
5857
|
+
/** Why it is that range, in words, for a game choosing its look and for a bug report. */
|
|
5858
|
+
get displayRangeReason() {
|
|
5859
|
+
return this.quality.highDynamicRange
|
|
5860
|
+
? 'WebGL2 draws into an eight-bit canvas, which has no range above white'
|
|
5861
|
+
: 'highDynamicRange was not asked for';
|
|
5862
|
+
}
|
|
5863
|
+
/**
|
|
5864
|
+
* How bright paper white and the display's peak are, in one unit, held until changed: their
|
|
5865
|
+
* ratio is how far above white the highlights may run where `displayRange` is `'high'`, and
|
|
5866
|
+
* nothing changes where it is standard, which is always here.
|
|
5867
|
+
*/
|
|
5868
|
+
setDisplayLuminance(paperWhite, peak) {
|
|
5869
|
+
void paperWhite;
|
|
5870
|
+
void peak;
|
|
5871
|
+
}
|
|
5674
5872
|
/**
|
|
5675
5873
|
* Composite a flat colour over the finished frame — for a cut dipping to white or to black.
|
|
5676
5874
|
*
|
|
@@ -5757,6 +5955,20 @@ export class WebGL2Renderer {
|
|
|
5757
5955
|
}
|
|
5758
5956
|
this.sceneTarget.setVignette(strength);
|
|
5759
5957
|
}
|
|
5958
|
+
/**
|
|
5959
|
+
* The lens's colour fringe, held until changed: `intensity` is a percentage, 0 is none, and red
|
|
5960
|
+
* and green are pulled toward the centre by their wavelength's distance from blue, growing from
|
|
5961
|
+
* `start` — a share of the half-frame, 0 the centre — to the whole of it at the edge. Applied to
|
|
5962
|
+
* scene light at the composite's first read. Needs `screenEffects`, and says so once rather than
|
|
5963
|
+
* doing nothing. See `shaders/fringe.ts`.
|
|
5964
|
+
*/
|
|
5965
|
+
setChromaticAberration(intensity, start = 0) {
|
|
5966
|
+
if (this.sceneTarget === null) {
|
|
5967
|
+
this.warnLookWithoutComposite(intensity);
|
|
5968
|
+
return;
|
|
5969
|
+
}
|
|
5970
|
+
this.sceneTarget.setChromaticAberration(intensity, start);
|
|
5971
|
+
}
|
|
5760
5972
|
/**
|
|
5761
5973
|
* Film grain, held until changed: an amplitude in display values (0 is none, 0.03 is a fine
|
|
5762
5974
|
* grain) and this frame's seed. **The seed is the caller's**, because the engine takes time from
|
|
@@ -5937,6 +6149,8 @@ export class WebGL2Renderer {
|
|
|
5937
6149
|
beginFrame(clearColor) {
|
|
5938
6150
|
if (this.contextLost)
|
|
5939
6151
|
return;
|
|
6152
|
+
/* One frame of the jitter's sequence however many times the frame binds its camera. */
|
|
6153
|
+
this.temporalHistory.nextFrame();
|
|
5940
6154
|
this.skinScattered = false;
|
|
5941
6155
|
this.skinScatter?.reset();
|
|
5942
6156
|
if (this.litAsked) {
|
|
@@ -6003,6 +6217,8 @@ export class WebGL2Renderer {
|
|
|
6003
6217
|
}
|
|
6004
6218
|
}
|
|
6005
6219
|
this.sceneTarget?.begin(this.canvas.width, this.canvas.height);
|
|
6220
|
+
/* The materials' maps on the scene's framebuffer, cleared and disabled: `reflectionKeeps`. */
|
|
6221
|
+
this.reflectionTargets?.begin(this.canvas.width, this.canvas.height);
|
|
6006
6222
|
gl.clearColor(clearColor[0], clearColor[1], clearColor[2], 1);
|
|
6007
6223
|
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
|
6008
6224
|
}
|
|
@@ -6052,7 +6268,7 @@ export class WebGL2Renderer {
|
|
|
6052
6268
|
motion = {
|
|
6053
6269
|
reprojection: this.reprojection,
|
|
6054
6270
|
strength: blur,
|
|
6055
|
-
max:
|
|
6271
|
+
max: this.motionBlurMax,
|
|
6056
6272
|
};
|
|
6057
6273
|
}
|
|
6058
6274
|
}
|
|
@@ -6132,7 +6348,11 @@ export class WebGL2Renderer {
|
|
|
6132
6348
|
: undefined;
|
|
6133
6349
|
/* The grade travels to the resolve only where the mesh pass gave it up. See `bindMeshPass`. */
|
|
6134
6350
|
const lateGrade = this.quality.hdrScene
|
|
6135
|
-
? {
|
|
6351
|
+
? {
|
|
6352
|
+
transform: OUTPUT_TRANSFORM_CODE[this.quality.outputTransform] ?? 0,
|
|
6353
|
+
exposure: this.exposure,
|
|
6354
|
+
film: this.filmConstants,
|
|
6355
|
+
}
|
|
6136
6356
|
: { transform: 0, exposure: 1 };
|
|
6137
6357
|
/*
|
|
6138
6358
|
* Depth of field, on the same ceiling-and-dial split as bloom above: `depthOfField` decides
|
|
@@ -6194,6 +6414,30 @@ export class WebGL2Renderer {
|
|
|
6194
6414
|
* drawn over a reflective floor rather than under it, and before the post chain so bloom and
|
|
6195
6415
|
* the grade see a reflection exactly as they would see any other part of the picture.
|
|
6196
6416
|
*/
|
|
6417
|
+
/*
|
|
6418
|
+
* **The frame's materials' reflections first**, on the same terms: every pixel the opaque lit
|
|
6419
|
+
* draws left a reflection for, marched and swapped for the probe's share. The maps leave the
|
|
6420
|
+
* scene's framebuffer before the resolve draws into it while sampling them.
|
|
6421
|
+
*/
|
|
6422
|
+
const reflectionTargets = this.reflectionTargets;
|
|
6423
|
+
const probeMap = reflectionTargets?.probeTexture ?? null;
|
|
6424
|
+
const tintMap = reflectionTargets?.tintTexture ?? null;
|
|
6425
|
+
if (reflectionTargets !== null &&
|
|
6426
|
+
this.frameReflections !== null &&
|
|
6427
|
+
probeMap !== null &&
|
|
6428
|
+
tintMap !== null &&
|
|
6429
|
+
this.frameViewProj !== null &&
|
|
6430
|
+
this.sceneTarget !== null) {
|
|
6431
|
+
const scene = this.sceneTarget.colorAttachment();
|
|
6432
|
+
const depth = this.sceneTarget.snapshotDepth(true);
|
|
6433
|
+
if (scene !== null && depth !== null) {
|
|
6434
|
+
this.ssr ??= new SsrPass(this.gl, this.sceneTarget.keepsRange);
|
|
6435
|
+
this.ssr.traceMaterials(scene, depth, probeMap, tintMap, this.frameViewProj, this.frameEye, this.canvas.width, this.canvas.height, REFLECTION_EDGE_FADE, this.frameReflections);
|
|
6436
|
+
this.sceneTarget.bind();
|
|
6437
|
+
reflectionTargets.detach();
|
|
6438
|
+
this.ssr.resolveMaterials(probeMap, tintMap, this.canvas.width, this.canvas.height, this.frameReflections);
|
|
6439
|
+
}
|
|
6440
|
+
}
|
|
6197
6441
|
if (this.reflectionQueue.length > 0 && this.frameViewProj !== null) {
|
|
6198
6442
|
const scene = this.sceneTarget?.colorAttachment() ?? null;
|
|
6199
6443
|
const depth = this.sceneTarget?.snapshotDepth(true) ?? null;
|
|
@@ -6531,6 +6775,10 @@ export class WebGL2Renderer {
|
|
|
6531
6775
|
this.materialHasMaps = false;
|
|
6532
6776
|
this.currentMaterial = null;
|
|
6533
6777
|
this.currentSurfaceTexture = null;
|
|
6778
|
+
/* Nor an overlay: the atlas's unit is given the empty texture with the refraction copy's. */
|
|
6779
|
+
this.overlayFloats.fill(0);
|
|
6780
|
+
this.overlayMaps = null;
|
|
6781
|
+
this.overlayTexture = null;
|
|
6534
6782
|
/* Nor an ambient of its own: the frame's, until a draw asks. See `setAmbientSH`. */
|
|
6535
6783
|
this.ambientSH.fill(0);
|
|
6536
6784
|
/* No reflection and an environment at its own brightness; the emission the world asks for. */
|
|
@@ -6656,8 +6904,10 @@ export class WebGL2Renderer {
|
|
|
6656
6904
|
* unless a draw sets the strength above zero.
|
|
6657
6905
|
*/
|
|
6658
6906
|
gl.activeTexture(gl.TEXTURE0 + REFRACT_SCENE_TEXTURE_UNIT);
|
|
6659
|
-
gl.bindTexture(gl.TEXTURE_2D, this.emptyTexture2D);
|
|
6907
|
+
gl.bindTexture(gl.TEXTURE_2D, this.overlayTexture ?? this.emptyTexture2D);
|
|
6660
6908
|
gl.uniform1i(u['uRefractScene'] ?? null, REFRACT_SCENE_TEXTURE_UNIT);
|
|
6909
|
+
/* The draws' overlay, none at a pass's start, and its atlas above: see `setSurfaceOverlay`. */
|
|
6910
|
+
gl.uniform4fv(u['uOverlay'] ?? null, this.overlayFloats);
|
|
6661
6911
|
/* Neither refracting nor glass, which is every draw that does not say otherwise. See
|
|
6662
6912
|
`bindSeeThrough` for the four numbers in the one vector. */
|
|
6663
6913
|
gl.uniform4f(u['uSeeThrough'] ?? null, 0, 0, 0, 0);
|
|
@@ -7036,9 +7286,16 @@ export class WebGL2Renderer {
|
|
|
7036
7286
|
* here, so there is no motion target for this to be written into, and a mover passed here is
|
|
7037
7287
|
* neither read nor advanced.
|
|
7038
7288
|
*/
|
|
7039
|
-
|
|
7289
|
+
previousModel = null) {
|
|
7040
7290
|
if (this.contextLost)
|
|
7041
7291
|
return;
|
|
7292
|
+
/* A mover's own motion is not blurred here: see `setCameraMotionBlur`. Said once. */
|
|
7293
|
+
if (previousModel !== null &&
|
|
7294
|
+
!this.warnedObjectBlur &&
|
|
7295
|
+
this.quality.cameraMotionBlur * this.motionBlurScale > 0) {
|
|
7296
|
+
this.warnedObjectBlur = true;
|
|
7297
|
+
console.warn(OBJECT_BLUR_UNKEPT);
|
|
7298
|
+
}
|
|
7042
7299
|
/* Geometry that has not all arrived is not drawn, nor a mesh with none. See `Mesh.complete`
|
|
7043
7300
|
and `Mesh.indexCount`. */
|
|
7044
7301
|
if (!mesh.complete || mesh.indexCount === 0)
|
|
@@ -7071,7 +7328,13 @@ export class WebGL2Renderer {
|
|
|
7071
7328
|
* light and its colour, each into the blur's own target.
|
|
7072
7329
|
*/
|
|
7073
7330
|
const splits = this.skinSplits();
|
|
7331
|
+
/* An opaque lit draw into the frame writes its material's reflection too: see `reflectionKeeps`. */
|
|
7332
|
+
const keeps = this.reflectionKeeps();
|
|
7333
|
+
if (keeps)
|
|
7334
|
+
this.reflectionTargets?.on();
|
|
7074
7335
|
this.drawMeshHalf(mesh, model, depthLayer, tint, splits ? 'scene' : 'whole');
|
|
7336
|
+
if (keeps)
|
|
7337
|
+
this.reflectionTargets?.off();
|
|
7075
7338
|
if (splits && this.skinScatter !== null) {
|
|
7076
7339
|
this.skinScatter.begin();
|
|
7077
7340
|
this.drawMeshHalf(mesh, model, depthLayer, tint, 'diffuse');
|
|
@@ -7085,6 +7348,18 @@ export class WebGL2Renderer {
|
|
|
7085
7348
|
* skin yet, this is the frame's own picture — not a mirror's or a probe's, nor an overlay after
|
|
7086
7349
|
* the frame — and the blur's target can be had.
|
|
7087
7350
|
*/
|
|
7351
|
+
/**
|
|
7352
|
+
* Whether a draw now is the frame's own, opaque and lit, and so writes the two maps beside its
|
|
7353
|
+
* colour: not a mirror's, a probe's or a capture's, whose framebuffers have no maps, and not after
|
|
7354
|
+
* the frame. See `reflectionSurfaceTargets.ts`.
|
|
7355
|
+
*/
|
|
7356
|
+
reflectionKeeps() {
|
|
7357
|
+
return (this.reflectionTargets !== null &&
|
|
7358
|
+
!this.reflectionPassActive &&
|
|
7359
|
+
!this.probePassActive &&
|
|
7360
|
+
!this.capturePassActive &&
|
|
7361
|
+
!this.framePresented);
|
|
7362
|
+
}
|
|
7088
7363
|
skinSplits() {
|
|
7089
7364
|
const skin = this.skinScatter;
|
|
7090
7365
|
const scene = this.sceneTarget;
|
|
@@ -7111,12 +7386,18 @@ export class WebGL2Renderer {
|
|
|
7111
7386
|
const projection = this.frameProjection;
|
|
7112
7387
|
if (skin === null || scene === null || this.skinScattered)
|
|
7113
7388
|
return;
|
|
7389
|
+
/*
|
|
7390
|
+
* **Not the frame's picture, so not the frame's moment**: a blended draw in a mirror, a probe
|
|
7391
|
+
* bake or a scene capture lands over none of the frame's skin. Latching there switched the
|
|
7392
|
+
* spread off for the frame, so a capture with a glowing sign in it left every face drawn after
|
|
7393
|
+
* it flat. `scripts/capture-check.mjs`.
|
|
7394
|
+
*/
|
|
7395
|
+
if (this.reflectionPassActive || this.probePassActive || this.capturePassActive)
|
|
7396
|
+
return;
|
|
7114
7397
|
/* Latched whether or not skin was drawn: a skin drawn after this point is the whole surface. */
|
|
7115
7398
|
this.skinScattered = true;
|
|
7116
7399
|
if (!skin.pending)
|
|
7117
7400
|
return;
|
|
7118
|
-
if (this.reflectionPassActive || this.probePassActive || this.capturePassActive)
|
|
7119
|
-
return;
|
|
7120
7401
|
if (projection === null)
|
|
7121
7402
|
return;
|
|
7122
7403
|
const depth = scene.snapshotDepth(true);
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* split matters because these two fail for unrelated reasons: a device is lost by a driver
|
|
7
7
|
* reset the page did not cause, and a frame is wrong because of something the frame did.
|
|
8
8
|
*/
|
|
9
|
+
import type { DisplayRange, DisplayRangeChoice } from '../../displayRange.ts';
|
|
9
10
|
/** What the renderer needs from the canvas it draws into, with the API's shape absorbed. */
|
|
10
11
|
export interface GpuSurface {
|
|
11
12
|
readonly device: GPUDevice;
|
|
@@ -21,6 +22,9 @@ export interface GpuSurface {
|
|
|
21
22
|
readonly canvas: HTMLCanvasElement;
|
|
22
23
|
/** The swap chain's format, which every render pipeline has to be built against. */
|
|
23
24
|
readonly format: GPUTextureFormat;
|
|
25
|
+
/** The range the canvas was configured in, and why. See `displayRange.ts`. */
|
|
26
|
+
readonly displayRange: DisplayRange;
|
|
27
|
+
readonly displayRangeReason: string;
|
|
24
28
|
/** Whether the device has gone. Read per frame; never throws. */
|
|
25
29
|
readonly lost: boolean;
|
|
26
30
|
/** Called when the device is lost. Fires immediately if it already has been. */
|
|
@@ -49,4 +53,6 @@ export interface GpuSurface {
|
|
|
49
53
|
* consumer its GPU had failed every time it unmounted a canvas on purpose, so a disposed
|
|
50
54
|
* surface stays silent.
|
|
51
55
|
*/
|
|
52
|
-
export declare function createGpuSurface(canvas: HTMLCanvasElement, device: GPUDevice
|
|
56
|
+
export declare function createGpuSurface(canvas: HTMLCanvasElement, device: GPUDevice,
|
|
57
|
+
/** The range the profile and the display allow (`chooseDisplayRange`); standard unless given. */
|
|
58
|
+
wanted?: DisplayRangeChoice): GpuSurface;
|