@driftengine/core 4.8.6 → 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 +132 -0
- package/README.md +1 -1
- package/dist/index.d.ts +7 -2
- package/dist/index.js +8 -1
- package/dist/render/anisotropicLobe.d.ts +10 -0
- package/dist/render/anisotropicLobe.js +19 -0
- package/dist/render/areaLights.d.ts +35 -0
- package/dist/render/areaLights.js +35 -0
- package/dist/render/atmosphere.d.ts +13 -0
- package/dist/render/atmosphere.js +9 -1
- package/dist/render/backend/api.d.ts +41 -3
- 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 +140 -16
- package/dist/render/backend/webgl2/renderer.js +436 -44
- package/dist/render/backend/webgl2/sceneCaptureTarget.d.ts +29 -0
- package/dist/render/backend/webgl2/sceneCaptureTarget.js +96 -0
- 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/particlePass.js +2 -1
- 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 +166 -3
- package/dist/render/backend/webgpu/renderer.js +738 -83
- package/dist/render/backend/webgpu/sceneCaptureTarget.d.ts +24 -0
- package/dist/render/backend/webgpu/sceneCaptureTarget.js +59 -0
- package/dist/render/backend/webgpu/shaders/clusterBin.wgsl.d.ts +1 -1
- package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +4 -1
- package/dist/render/backend/webgpu/surfaceTexturePass.js +34 -8
- package/dist/render/backend/webgpu/waterPass.d.ts +1 -1
- package/dist/render/clusteredLights.d.ts +23 -5
- package/dist/render/clusteredLights.js +35 -8
- package/dist/render/displayRange.d.ts +31 -0
- package/dist/render/displayRange.js +47 -0
- package/dist/render/driftLight/bake.d.ts +2 -0
- package/dist/render/driftLight/bake.js +1 -1
- package/dist/render/driftLight/falloff.d.ts +4 -2
- package/dist/render/driftLight/falloff.js +8 -2
- 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/gi/bounceLights.d.ts +3 -2
- package/dist/render/gi/bounceLights.js +8 -3
- package/dist/render/instanceCull.js +5 -0
- package/dist/render/instances.d.ts +19 -5
- package/dist/render/instances.js +9 -3
- package/dist/render/lightBudget.d.ts +12 -1
- package/dist/render/lightBudget.js +23 -3
- package/dist/render/lightmap.d.ts +23 -9
- package/dist/render/lightmap.js +40 -18
- package/dist/render/particlePool.d.ts +21 -0
- package/dist/render/particlePool.js +7 -0
- package/dist/render/pass.d.ts +49 -0
- package/dist/render/pointLightSelection.d.ts +13 -0
- package/dist/render/pointLightSelection.js +3 -1
- package/dist/render/reflectionSurfaceTargets.d.ts +28 -0
- package/dist/render/reflectionSurfaceTargets.js +99 -0
- package/dist/render/renderQuality.d.ts +47 -15
- package/dist/render/renderQuality.js +4 -0
- package/dist/render/rendererHarness.js +3 -0
- package/dist/render/rgb9e5.d.ts +22 -0
- package/dist/render/rgb9e5.js +47 -0
- package/dist/render/sceneCapture.d.ts +29 -0
- package/dist/render/sceneCapture.js +36 -0
- package/dist/render/sceneTarget.d.ts +5 -0
- package/dist/render/sceneTarget.js +24 -1
- package/dist/render/screenSpaceReflection.d.ts +32 -1
- package/dist/render/screenSpaceReflection.js +14 -0
- package/dist/render/shaders/arcane.d.ts +1 -1
- package/dist/render/shaders/bolt.d.ts +1 -1
- package/dist/render/shaders/bolt.js +7 -1
- package/dist/render/shaders/caustics.d.ts +1 -1
- package/dist/render/shaders/depth.js +11 -0
- 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/anisotropic.d.ts +1 -1
- package/dist/render/shaders/flat/anisotropic.js +37 -3
- package/dist/render/shaders/flat/areaLight.d.ts +1 -1
- package/dist/render/shaders/flat/areaLight.js +46 -0
- package/dist/render/shaders/flat/glassTint.d.ts +3 -2
- package/dist/render/shaders/flat/glassTint.js +5 -1
- package/dist/render/shaders/flat/index.d.ts +15 -0
- package/dist/render/shaders/flat/index.js +22 -2
- package/dist/render/shaders/flat/lobes.d.ts +6 -0
- package/dist/render/shaders/flat/lobes.js +49 -43
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +85 -22
- package/dist/render/shaders/flat/models.d.ts +5 -2
- package/dist/render/shaders/flat/models.js +100 -16
- 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/preamble.js +10 -3
- 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/depth.wgsl.d.ts +4 -4
- package/dist/render/shaders/generated/depth.wgsl.js +4 -4
- 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 +192 -16
- package/dist/render/shaders/generated/flat.wgsl.js +230 -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 +32 -7
- package/dist/render/shaders/generated/ssrTrace.wgsl.js +32 -7
- package/dist/render/shaders/generated/water.wgsl.d.ts +23 -16
- package/dist/render/shaders/generated/water.wgsl.js +23 -16
- package/dist/render/shaders/gi/probeBake.wgsl.js +9 -5
- 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/recon/motion.wgsl.d.ts +1 -1
- package/dist/render/shaders/recon/motion.wgsl.js +13 -2
- 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 +46 -1
- package/dist/render/shaders/water.d.ts +1 -1
- package/dist/render/shaders/water.js +12 -3
- package/dist/render/skinScatter.d.ts +3 -1
- package/dist/render/skinScatter.js +3 -1
- package/dist/render/ssrPass.d.ts +16 -0
- package/dist/render/ssrPass.js +107 -1
- package/dist/render/surfaceModel.d.ts +29 -8
- package/dist/render/surfaceModel.js +33 -9
- package/dist/render/surfaceOverlay.d.ts +132 -0
- package/dist/render/surfaceOverlay.js +85 -0
- package/dist/render/surfaceTexture.d.ts +44 -1
- package/dist/render/surfaceTexture.js +96 -5
- 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 +22 -2
- package/src/render/anisotropicLobe.ts +29 -0
- package/src/render/areaLights.ts +58 -0
- package/src/render/atmosphere.ts +22 -1
- package/src/render/backend/api.ts +58 -2
- package/src/render/backend/createRenderer.ts +18 -1
- package/src/render/backend/webgl2/renderer.ts +548 -50
- package/src/render/backend/webgl2/sceneCaptureTarget.ts +130 -0
- package/src/render/backend/webgpu/device.ts +42 -3
- package/src/render/backend/webgpu/flatPass.ts +63 -26
- package/src/render/backend/webgpu/particlePass.ts +2 -1
- 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 +927 -100
- package/src/render/backend/webgpu/sceneCaptureTarget.ts +63 -0
- package/src/render/backend/webgpu/surfaceTexturePass.ts +44 -14
- package/src/render/clusteredLights.ts +41 -8
- package/src/render/displayRange.ts +61 -0
- package/src/render/driftLight/bake.ts +3 -1
- package/src/render/driftLight/falloff.ts +13 -2
- 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/gi/bounceLights.ts +12 -3
- package/src/render/instanceCull.ts +4 -0
- package/src/render/instances.ts +26 -6
- package/src/render/lightBudget.ts +40 -2
- package/src/render/lightmap.ts +64 -23
- package/src/render/particlePool.ts +23 -0
- package/src/render/pass.ts +53 -1
- package/src/render/pointLightSelection.ts +20 -1
- package/src/render/reflectionSurfaceTargets.ts +105 -0
- package/src/render/renderQuality.ts +52 -15
- package/src/render/rendererHarness.ts +3 -0
- package/src/render/rgb9e5.ts +52 -0
- package/src/render/sceneCapture.ts +56 -0
- package/src/render/sceneTarget.ts +30 -2
- package/src/render/screenSpaceReflection.ts +38 -1
- package/src/render/shaders/bolt.ts +7 -1
- package/src/render/shaders/depth.ts +11 -0
- package/src/render/shaders/filmLook.ts +2 -1
- package/src/render/shaders/flat/anisotropic.ts +37 -3
- package/src/render/shaders/flat/areaLight.ts +46 -0
- package/src/render/shaders/flat/glassTint.ts +13 -2
- package/src/render/shaders/flat/index.ts +37 -2
- package/src/render/shaders/flat/lobes.ts +50 -43
- package/src/render/shaders/flat/main.ts +85 -22
- package/src/render/shaders/flat/models.ts +100 -16
- package/src/render/shaders/flat/overlay.ts +117 -0
- package/src/render/shaders/flat/pointShadow.ts +1 -1
- package/src/render/shaders/flat/preamble.ts +10 -3
- 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/depth.wgsl.ts +4 -4
- 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 +230 -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 +32 -7
- package/src/render/shaders/generated/water.wgsl.ts +23 -16
- package/src/render/shaders/gi/probeBake.wgsl.ts +9 -5
- package/src/render/shaders/gpudriven/fog.wgsl.ts +11 -3
- package/src/render/shaders/recon/motion.wgsl.ts +13 -2
- package/src/render/shaders/rush.ts +54 -11
- package/src/render/shaders/ssrResolve.ts +52 -0
- package/src/render/shaders/ssrTrace.ts +46 -1
- package/src/render/shaders/water.ts +12 -3
- package/src/render/skinScatter.ts +3 -1
- package/src/render/ssrPass.ts +139 -1
- package/src/render/surfaceModel.ts +47 -10
- package/src/render/surfaceOverlay.ts +195 -0
- package/src/render/surfaceTexture.ts +136 -9
- 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';
|
|
@@ -136,7 +154,12 @@ import {
|
|
|
136
154
|
MORPH_DELTA_TEXTURE_UNIT,
|
|
137
155
|
SKIN_PALETTE_TEXTURE_UNIT,
|
|
138
156
|
} from '../../lightBudget.ts';
|
|
139
|
-
import {
|
|
157
|
+
import {
|
|
158
|
+
MODEL_PARAM_FLOATS,
|
|
159
|
+
eyeAxisInWorld,
|
|
160
|
+
packModel,
|
|
161
|
+
skinProfileDistance,
|
|
162
|
+
} from '../../surfaceModel.ts';
|
|
140
163
|
import type { SurfaceModelKind } from '../../surfaceModel.ts';
|
|
141
164
|
import { lightmapRegionsBound, modelBound, skinHalfBound } from '../../shaders/flat/models.ts';
|
|
142
165
|
import {
|
|
@@ -251,6 +274,8 @@ import type { IncrementalMesh } from '../../mesh.ts';
|
|
|
251
274
|
import type { MeshData } from '../../mesh.ts';
|
|
252
275
|
import type { MeshOptions } from '../api.ts';
|
|
253
276
|
import { SurfaceTexture } from '../../surfaceTexture.ts';
|
|
277
|
+
import { sceneCaptureTexels } from '../../sceneCapture.ts';
|
|
278
|
+
import { GlSceneCaptureTarget } from './sceneCaptureTarget.ts';
|
|
254
279
|
import { glCompressedFormats } from '../../glCompressed.ts';
|
|
255
280
|
import type { CompressedTextureFormat, SurfaceSource } from '../../compressedSource.ts';
|
|
256
281
|
import type { SurfaceMaterial, SurfaceTextureOptions } from '../../surfaceTexture.ts';
|
|
@@ -274,7 +299,7 @@ import { PlanarReflection } from '../../planarReflection.ts';
|
|
|
274
299
|
import { InstancedMesh } from '../../instancedMesh.ts';
|
|
275
300
|
import { ParticleBatch } from '../../particleBatch.ts';
|
|
276
301
|
import type { ParticleBatchOptions } from '../../particleBatch.ts';
|
|
277
|
-
import type { ParticleInstances } from '../../particlePool.ts';
|
|
302
|
+
import type { DeviceParticles, ParticleInstances } from '../../particlePool.ts';
|
|
278
303
|
import { BoltBatch } from '../../boltBatch.ts';
|
|
279
304
|
import type { BoltSegments } from '../../boltPool.ts';
|
|
280
305
|
import { LineBatch } from '../../lineBatch.ts';
|
|
@@ -286,8 +311,22 @@ import { TextRenderer } from '../../textRenderer.ts';
|
|
|
286
311
|
import { SdfTextRenderer } from '../../sdfTextRenderer.ts';
|
|
287
312
|
import type { SdfFont } from '../../sdfFont.ts';
|
|
288
313
|
import type { SdfTextStyle } from '../../sdfTextLayout.ts';
|
|
289
|
-
import {
|
|
290
|
-
|
|
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';
|
|
291
330
|
import type { RenderBackend } from '../api.ts';
|
|
292
331
|
import { deviceSnappedCellSize } from '../../textLayout.ts';
|
|
293
332
|
import type { TextStyle } from '../../textLayout.ts';
|
|
@@ -435,6 +474,11 @@ export interface Environment extends Atmosphere {
|
|
|
435
474
|
lightDirections: Float32Array;
|
|
436
475
|
/** Two per active light: the cosine of the inner cone angle, then of the outer. */
|
|
437
476
|
lightConeCos: Float32Array;
|
|
477
|
+
/**
|
|
478
|
+
* One per active light: its own falloff exponent, or 0 for the frame's. Optional: absent is 0 for
|
|
479
|
+
* every light. See `PointLightSource.falloffExponent`.
|
|
480
|
+
*/
|
|
481
|
+
lightFalloffExponents?: Float32Array;
|
|
438
482
|
/** One per active light: a row of the photometric atlas, or −1 for none. */
|
|
439
483
|
lightIesProfiles: Float32Array;
|
|
440
484
|
/**
|
|
@@ -1336,6 +1380,10 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
1336
1380
|
private readonly reflectionQueue = new ReflectionQueue();
|
|
1337
1381
|
/** Built on the first frame that submits a surface, and never for a scene with none. */
|
|
1338
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;
|
|
1339
1387
|
/** Said once where a reflection cannot be traced at all. */
|
|
1340
1388
|
private reflectionsRefused = false;
|
|
1341
1389
|
/** Where the frame's camera stands, which is what gives a reconstructed normal its sign. */
|
|
@@ -1418,6 +1466,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
1418
1466
|
* exactly as it asked to be and nothing about it changes.
|
|
1419
1467
|
*/
|
|
1420
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);
|
|
1421
1471
|
/**
|
|
1422
1472
|
* This frame's veil colour, and how much of it to composite. See `setFrameVeil`.
|
|
1423
1473
|
*
|
|
@@ -1619,8 +1669,26 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
1619
1669
|
private warnedNoPointShadowArray = false;
|
|
1620
1670
|
/** Said once, for the retired ambient dial. */
|
|
1621
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;
|
|
1622
1681
|
/** Whether a probe is being baked, so the passes that would recurse into one can tell. */
|
|
1623
1682
|
private probePassActive = false;
|
|
1683
|
+
/**
|
|
1684
|
+
* Whether a scene capture is being drawn (`captureScene`): an off-screen pass like a bake, so the
|
|
1685
|
+
* frame's own view, its refraction copy and skin's spread are left alone, but lit as the frame is.
|
|
1686
|
+
*/
|
|
1687
|
+
private capturePassActive = false;
|
|
1688
|
+
/** Each capture's framebuffer, beside the texture a material binds. See `createSceneCapture`. */
|
|
1689
|
+
private readonly captureTargets = new WeakMap<SurfaceTexture, GlSceneCaptureTarget>();
|
|
1690
|
+
/** Said once, where a driver refuses to draw into a capture. */
|
|
1691
|
+
private warnedCapture = false;
|
|
1624
1692
|
/** Whether the bake under way lights its faces by the grid. `ProbeBakeOptions.bounce`. */
|
|
1625
1693
|
private probeBounce = false;
|
|
1626
1694
|
/** The clip-control extension, or null where this context does not offer it. */
|
|
@@ -1692,12 +1760,14 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
1692
1760
|
* the camera's matrix unchanged and multiplies nothing.
|
|
1693
1761
|
*/
|
|
1694
1762
|
private viewProjFor(camera: { readonly viewProjection: ReadonlyMat4 }): ReadonlyMat4 {
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1763
|
+
/* A capture's rows are flipped as a probe face's are, so it reads as an image does. */
|
|
1764
|
+
const scene =
|
|
1765
|
+
this.probePassActive || this.capturePassActive
|
|
1766
|
+
? (cubeFaceProjection(
|
|
1767
|
+
this.cubeFaceViewProj,
|
|
1768
|
+
this.sceneMatrix(camera.viewProjection) as unknown as ArrayLike<number>,
|
|
1769
|
+
) as unknown as ReadonlyMat4)
|
|
1770
|
+
: this.sceneMatrix(camera.viewProjection);
|
|
1701
1771
|
/*
|
|
1702
1772
|
* **The jitter goes on here and nowhere else.** This is the one funnel every geometry upload
|
|
1703
1773
|
* passes through, so one line covers the mesh passes, the sky, the water and the effects
|
|
@@ -2275,6 +2345,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
2275
2345
|
gl: WebGL2RenderingContext;
|
|
2276
2346
|
outputTransform: number;
|
|
2277
2347
|
outputExposure: number;
|
|
2348
|
+
sceneColor: WebGLTexture | null;
|
|
2349
|
+
sceneDepth: WebGLTexture | null;
|
|
2278
2350
|
} | null = null;
|
|
2279
2351
|
private passDevice: PassDevice | null = null;
|
|
2280
2352
|
|
|
@@ -2286,6 +2358,9 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
2286
2358
|
* call. `init` runs now rather than at the first frame, because building a pipeline in the
|
|
2287
2359
|
* frame loop is the allocation the house rules are about.
|
|
2288
2360
|
*/
|
|
2361
|
+
/** Each registered pass's declared reads, as a mask, resolved once at registration. */
|
|
2362
|
+
private readonly passReads = new Map<PassHandle, number>();
|
|
2363
|
+
|
|
2289
2364
|
registerPass(definition: PassDefinition): PassHandle {
|
|
2290
2365
|
const handle = registerIn(this.passes, definition);
|
|
2291
2366
|
/*
|
|
@@ -2304,6 +2379,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
2304
2379
|
depthCorrection: correction,
|
|
2305
2380
|
};
|
|
2306
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 ?? [])));
|
|
2307
2384
|
definition.init?.(this.passDevice);
|
|
2308
2385
|
return handle;
|
|
2309
2386
|
}
|
|
@@ -2355,10 +2432,28 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
2355
2432
|
gl: this.gl,
|
|
2356
2433
|
outputTransform: 0,
|
|
2357
2434
|
outputExposure: 1,
|
|
2435
|
+
sceneColor: null,
|
|
2436
|
+
sceneDepth: null,
|
|
2358
2437
|
};
|
|
2359
2438
|
const context = this.passContext;
|
|
2360
2439
|
context.outputTransform = this.gradeCode();
|
|
2361
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;
|
|
2362
2457
|
definition.draw(context as PassContext);
|
|
2363
2458
|
}
|
|
2364
2459
|
|
|
@@ -2366,6 +2461,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
2366
2461
|
unregisterPass(handle: PassHandle): void {
|
|
2367
2462
|
const definition = unregisterIn(this.passes, handle);
|
|
2368
2463
|
if (definition?.prepare !== undefined) this.preparingPasses--;
|
|
2464
|
+
this.passReads.delete(handle);
|
|
2369
2465
|
if (definition !== undefined && this.passDevice !== null) definition.dispose?.(this.passDevice);
|
|
2370
2466
|
}
|
|
2371
2467
|
|
|
@@ -2536,10 +2632,11 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
2536
2632
|
gl.bindTexture(gl.TEXTURE_2D, this.clusterTexture);
|
|
2537
2633
|
gl.uniform1i(u['uClusterTable'] ?? null, CLUSTER_TABLE_TEXTURE_UNIT);
|
|
2538
2634
|
gl.uniform1i(u['uClustered'] ?? null, on ? 1 : 0);
|
|
2539
|
-
if (!on) return;
|
|
2540
2635
|
|
|
2541
2636
|
/*
|
|
2542
|
-
* 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`).
|
|
2543
2640
|
*
|
|
2544
2641
|
* A perspective matrix carries `1 / tan(fovY/2)` at [5] and that over the aspect at [0], and
|
|
2545
2642
|
* the near and far planes in [14] and [10]. Deriving them here means the binner and the shader
|
|
@@ -2557,6 +2654,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
2557
2654
|
this.clusterFrustum[3] = aspect;
|
|
2558
2655
|
gl.uniform4fv(u['uClusterFrustum'] ?? null, this.clusterFrustum);
|
|
2559
2656
|
gl.uniformMatrix4fv(u['uView'] ?? null, false, camera.view);
|
|
2657
|
+
if (!on) return;
|
|
2560
2658
|
|
|
2561
2659
|
this.clusterTable ??= createClusterTable();
|
|
2562
2660
|
/* The rectangles past the fixed arm's go into the table. See `ClusterLightSet.areas`. */
|
|
@@ -2660,6 +2758,13 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
2660
2758
|
this.quality = resolveRenderQuality(qualityOptions);
|
|
2661
2759
|
/* The grade this world asked for, until a frame says otherwise. See `setOutputExposure`. */
|
|
2662
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
|
+
}
|
|
2663
2768
|
/*
|
|
2664
2769
|
* Multisampling, but only when anything can use it.
|
|
2665
2770
|
*
|
|
@@ -2958,6 +3063,29 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
2958
3063
|
this.quality.discardResolvedAttachments,
|
|
2959
3064
|
)
|
|
2960
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');
|
|
2961
3089
|
/* Screen-space skin needs the composite to add into; without one it is pre-integrated, said once. */
|
|
2962
3090
|
if (this.quality.skinScattering === 'screen-space' && this.sceneTarget === null) {
|
|
2963
3091
|
console.warn(
|
|
@@ -3499,6 +3627,92 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
3499
3627
|
return new SurfaceTexture(this.gl, lightmapTexels(page), { wrap: 'clamp', mipmap: false });
|
|
3500
3628
|
}
|
|
3501
3629
|
|
|
3630
|
+
/**
|
|
3631
|
+
* A texture the scene can be drawn into, for a material to show. See `RendererApi` and
|
|
3632
|
+
* `sceneCapture.ts`. Half floats where the frame keeps range, as a probe does.
|
|
3633
|
+
*/
|
|
3634
|
+
createSceneCapture(width: number, height: number): SurfaceTexture {
|
|
3635
|
+
const float = this.quality.hdrScene && this.gl.getExtension('EXT_color_buffer_float') !== null;
|
|
3636
|
+
const texture = new SurfaceTexture(this.gl, sceneCaptureTexels(width, height, float), {
|
|
3637
|
+
wrap: 'clamp',
|
|
3638
|
+
mipmap: false,
|
|
3639
|
+
});
|
|
3640
|
+
this.captureTargets.set(
|
|
3641
|
+
texture,
|
|
3642
|
+
new GlSceneCaptureTarget(
|
|
3643
|
+
this.gl,
|
|
3644
|
+
texture,
|
|
3645
|
+
width,
|
|
3646
|
+
height,
|
|
3647
|
+
this.sceneTarget?.sampleCount ?? 1,
|
|
3648
|
+
float,
|
|
3649
|
+
),
|
|
3650
|
+
);
|
|
3651
|
+
return texture;
|
|
3652
|
+
}
|
|
3653
|
+
|
|
3654
|
+
/**
|
|
3655
|
+
* Draw the scene into a capture from `camera`: `draw` is handed the camera, its matrices updated
|
|
3656
|
+
* for the capture's shape, and submits the scene as it would to the screen. See `RendererApi`.
|
|
3657
|
+
*
|
|
3658
|
+
* **Radiance, as a probe bake stores it**: the output transform and exposure are held off for the
|
|
3659
|
+
* pass, so the frame grades a screen showing it once. No temporal jitter, which a capture has no
|
|
3660
|
+
* resolve to undo. Returns whether it drew: false for a texture that is not a capture, or one the
|
|
3661
|
+
* driver will not draw into.
|
|
3662
|
+
*/
|
|
3663
|
+
captureScene(
|
|
3664
|
+
capture: SurfaceTexture,
|
|
3665
|
+
camera: Camera,
|
|
3666
|
+
clearColor: Vec3,
|
|
3667
|
+
draw: (camera: Camera) => void,
|
|
3668
|
+
): boolean {
|
|
3669
|
+
if (this.contextLost) return false;
|
|
3670
|
+
const target = this.captureTargets.get(capture);
|
|
3671
|
+
if (target === undefined) return false;
|
|
3672
|
+
if (!target.usable) {
|
|
3673
|
+
if (!this.warnedCapture) {
|
|
3674
|
+
this.warnedCapture = true;
|
|
3675
|
+
console.warn(
|
|
3676
|
+
'Renderer: this driver will not draw into a scene capture, so it keeps its clear. ' +
|
|
3677
|
+
'Nothing else is affected.',
|
|
3678
|
+
);
|
|
3679
|
+
}
|
|
3680
|
+
return false;
|
|
3681
|
+
}
|
|
3682
|
+
camera.updateMatrices(target.width / target.height);
|
|
3683
|
+
const { gl } = this;
|
|
3684
|
+
const jittering = this.temporalJittering;
|
|
3685
|
+
this.temporalJittering = false;
|
|
3686
|
+
this.capturePassActive = true;
|
|
3687
|
+
this.useFlatProgram();
|
|
3688
|
+
gl.uniform1i(this.flatUniforms['uOutputTransform'] ?? null, 0);
|
|
3689
|
+
gl.uniform1f(this.flatUniforms['uOutputExposure'] ?? null, 1);
|
|
3690
|
+
this.materials.dirty();
|
|
3691
|
+
target.begin(gl, clearColor, this.depthClear);
|
|
3692
|
+
/*
|
|
3693
|
+
* **Rows flipped, as a probe face's are.** A GL framebuffer's first row is its bottom and an
|
|
3694
|
+
* image's is its top, so a capture drawn upright would be shown upside down by every material
|
|
3695
|
+
* reading it as the image it is; `viewProjFor` negates clip y for the pass, and negating one axis
|
|
3696
|
+
* reverses every triangle's winding, which the front face follows. WebGPU's rows already run
|
|
3697
|
+
* the image's way.
|
|
3698
|
+
*/
|
|
3699
|
+
gl.frontFace(gl.CW);
|
|
3700
|
+
try {
|
|
3701
|
+
draw(camera);
|
|
3702
|
+
} finally {
|
|
3703
|
+
target.end(gl);
|
|
3704
|
+
gl.frontFace(gl.CCW);
|
|
3705
|
+
this.useFlatProgram();
|
|
3706
|
+
gl.uniform1i(this.flatUniforms['uOutputTransform'] ?? null, this.gradeCode());
|
|
3707
|
+
gl.uniform1f(this.flatUniforms['uOutputExposure'] ?? null, this.gradeExposure());
|
|
3708
|
+
this.materials.dirty();
|
|
3709
|
+
this.capturePassActive = false;
|
|
3710
|
+
this.temporalJittering = jittering;
|
|
3711
|
+
this.restoreViewport();
|
|
3712
|
+
}
|
|
3713
|
+
return true;
|
|
3714
|
+
}
|
|
3715
|
+
|
|
3502
3716
|
/**
|
|
3503
3717
|
* Replace a surface texture's pixels, keeping the GPU object and its sampler state.
|
|
3504
3718
|
*
|
|
@@ -3514,11 +3728,16 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
3514
3728
|
updateSurfaceTexture(texture: SurfaceTexture, source: TexImageSource): void {
|
|
3515
3729
|
if (this.contextLost) return;
|
|
3516
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);
|
|
3517
3733
|
}
|
|
3518
3734
|
|
|
3519
3735
|
disposeSurfaceTexture(texture: SurfaceTexture): void {
|
|
3520
3736
|
if (this.contextLost) return;
|
|
3737
|
+
if (texture === this.overlayMaps) this.bindOverlayMaps(null);
|
|
3521
3738
|
texture.dispose(this.gl);
|
|
3739
|
+
this.captureTargets.get(texture)?.dispose(this.gl);
|
|
3740
|
+
this.captureTargets.delete(texture);
|
|
3522
3741
|
}
|
|
3523
3742
|
|
|
3524
3743
|
/**
|
|
@@ -3751,9 +3970,9 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
3751
3970
|
if (model?.kind === 'skin' && this.skinScatter !== null) {
|
|
3752
3971
|
this.skinScatter.setProfile(
|
|
3753
3972
|
model.profile,
|
|
3754
|
-
model
|
|
3755
|
-
model
|
|
3756
|
-
model
|
|
3973
|
+
skinProfileDistance(model, 0),
|
|
3974
|
+
skinProfileDistance(model, 1),
|
|
3975
|
+
skinProfileDistance(model, 2),
|
|
3757
3976
|
);
|
|
3758
3977
|
}
|
|
3759
3978
|
/*
|
|
@@ -3901,8 +4120,17 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
3901
4120
|
gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.emptyTexture2DArray);
|
|
3902
4121
|
if (modelMap !== null) modelMap.bind(gl, MODEL_MAP_TEXTURE_UNIT);
|
|
3903
4122
|
gl.uniform1i(u['uModelMap'] ?? null, MODEL_MAP_TEXTURE_UNIT);
|
|
3904
|
-
packModel(
|
|
4123
|
+
packModel(
|
|
4124
|
+
material?.model ?? null,
|
|
4125
|
+
modelMap !== null,
|
|
4126
|
+
this.modelParams,
|
|
4127
|
+
material?.physicalSpecular === true,
|
|
4128
|
+
);
|
|
4129
|
+
/* The first physical highlight asked for is when the lit programs start carrying one. */
|
|
4130
|
+
if (material?.physicalSpecular === true) this.askLit('PHYSICAL_SPECULAR');
|
|
3905
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);
|
|
3906
4134
|
|
|
3907
4135
|
/* The albedo's effects table, or the stand-in the shader reads as none. Before the early
|
|
3908
4136
|
return, so a material with no albedo still leaves a complete texture on the unit. */
|
|
@@ -3963,18 +4191,6 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
3963
4191
|
gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.emptyTexture2DArray);
|
|
3964
4192
|
}
|
|
3965
4193
|
|
|
3966
|
-
/**
|
|
3967
|
-
* A pass-level scale over the grain the geometry itself declared, 0 to 1.
|
|
3968
|
-
*
|
|
3969
|
-
* Which surfaces are mineral is now `MeshData.grain`, stated per vertex through
|
|
3970
|
-
* `MeshBuilder.setGrain` — because it is a property of a material, and absent means none.
|
|
3971
|
-
* This stays as the override for the case that has no answer to state: an imported model
|
|
3972
|
-
* arrives with no grain information at all, and a caller drawing one decides for it.
|
|
3973
|
-
*
|
|
3974
|
-
* Pass state, like `setSurfaceTexture`, because a material covers many draws. The default
|
|
3975
|
-
* is 1 and `bindMeshPass` restores it, so it scales geometry that declared grain and
|
|
3976
|
-
* leaves geometry that did not exactly as it is.
|
|
3977
|
-
*/
|
|
3978
4194
|
/**
|
|
3979
4195
|
* The ambient the following draws take, as nine spherical-harmonic coefficients of incoming
|
|
3980
4196
|
* radiance — red, green and blue of each, twenty-seven numbers — or `null` for the frame's own:
|
|
@@ -3986,16 +4202,6 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
3986
4202
|
* only — a glossy surface still reflects the probes — and it is pass state like the material,
|
|
3987
4203
|
* which `bindMeshPass` clears, so a mirror or a probe bake drawn after does not inherit it.
|
|
3988
4204
|
*/
|
|
3989
|
-
/**
|
|
3990
|
-
* How much of the environment the following draws mirror, 0 to 1.
|
|
3991
|
-
*
|
|
3992
|
-
* Pass state beside the grain, and the counterpart to it: grain is a surface being uneven,
|
|
3993
|
-
* this is a surface being smooth enough to carry an image. Polished paint, glass, chrome
|
|
3994
|
-
* and still water want it; plaster and stone want none.
|
|
3995
|
-
*
|
|
3996
|
-
* Defaults to zero and bindMeshPass restores it, so a scene that never calls this looks
|
|
3997
|
-
* exactly as it did.
|
|
3998
|
-
*/
|
|
3999
4205
|
setAmbientSH(coefficients: ArrayLike<number> | null): void {
|
|
4000
4206
|
if (this.contextLost) return;
|
|
4001
4207
|
packAmbientSH(coefficients, this.ambientSH);
|
|
@@ -4007,6 +4213,16 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4007
4213
|
this.useFlatProgram();
|
|
4008
4214
|
}
|
|
4009
4215
|
|
|
4216
|
+
/**
|
|
4217
|
+
* How much of the environment the following draws mirror, 0 to 1.
|
|
4218
|
+
*
|
|
4219
|
+
* Pass state beside the grain, and the counterpart to it: grain is a surface being uneven,
|
|
4220
|
+
* this is a surface being smooth enough to carry an image. Polished paint, glass, chrome
|
|
4221
|
+
* and still water want it; plaster and stone want none.
|
|
4222
|
+
*
|
|
4223
|
+
* Defaults to zero and bindMeshPass restores it, so a scene that never calls this looks
|
|
4224
|
+
* exactly as it did.
|
|
4225
|
+
*/
|
|
4010
4226
|
setSurfaceReflectivity(amount: number): void {
|
|
4011
4227
|
if (this.contextLost) return;
|
|
4012
4228
|
this.materials.dirty();
|
|
@@ -4014,6 +4230,76 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4014
4230
|
this.writeFlatVector('uEnvironmentDials', this.environmentDials);
|
|
4015
4231
|
}
|
|
4016
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
|
+
|
|
4017
4303
|
/**
|
|
4018
4304
|
* How bright the environment the surfaces drawn next reflect is. 1 is the default and identity.
|
|
4019
4305
|
*
|
|
@@ -4056,6 +4342,18 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4056
4342
|
);
|
|
4057
4343
|
}
|
|
4058
4344
|
|
|
4345
|
+
/**
|
|
4346
|
+
* A pass-level scale over the grain the geometry itself declared, 0 to 1.
|
|
4347
|
+
*
|
|
4348
|
+
* Which surfaces are mineral is now `MeshData.grain`, stated per vertex through
|
|
4349
|
+
* `MeshBuilder.setGrain` — because it is a property of a material, and absent means none.
|
|
4350
|
+
* This stays as the override for the case that has no answer to state: an imported model
|
|
4351
|
+
* arrives with no grain information at all, and a caller drawing one decides for it.
|
|
4352
|
+
*
|
|
4353
|
+
* Pass state, like `setSurfaceTexture`, because a material covers many draws. The default
|
|
4354
|
+
* is 1 and `bindMeshPass` restores it, so it scales geometry that declared grain and
|
|
4355
|
+
* leaves geometry that did not exactly as it is.
|
|
4356
|
+
*/
|
|
4059
4357
|
setSurfaceGrain(amount: number): void {
|
|
4060
4358
|
if (this.contextLost) return;
|
|
4061
4359
|
this.materials.dirty();
|
|
@@ -4437,8 +4735,11 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4437
4735
|
if (coverage) gl.enable(gl.SAMPLE_ALPHA_TO_COVERAGE);
|
|
4438
4736
|
if (ditheredBlend)
|
|
4439
4737
|
gl.uniform4fv(u['uCutout'] ?? null, this.cutoutUniform(this.currentMaterial, true));
|
|
4738
|
+
const keeps = !blend && this.reflectionKeeps();
|
|
4739
|
+
if (keeps) this.reflectionTargets?.on();
|
|
4440
4740
|
if (culling && cullsInstances(count, mesh.indexCount)) batch.drawCulled(gl, data, this.frustum);
|
|
4441
4741
|
else batch.draw(gl, count);
|
|
4742
|
+
if (keeps) this.reflectionTargets?.off();
|
|
4442
4743
|
if (coverage) gl.disable(gl.SAMPLE_ALPHA_TO_COVERAGE);
|
|
4443
4744
|
if (ditheredBlend)
|
|
4444
4745
|
gl.uniform4fv(u['uCutout'] ?? null, this.cutoutUniform(this.currentMaterial, false));
|
|
@@ -4485,7 +4786,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4485
4786
|
const refraction = options.refraction ?? 0;
|
|
4486
4787
|
const glassy = resolveGlass(options.glass, this.glassScratch);
|
|
4487
4788
|
if (refraction <= 0 && !glassy) return false;
|
|
4488
|
-
if (this.probePassActive || this.reflectionPassActive) return false;
|
|
4789
|
+
if (this.probePassActive || this.reflectionPassActive || this.capturePassActive) return false;
|
|
4489
4790
|
const snapshot = this.sceneTarget?.snapshotColor(false, glassy) ?? null;
|
|
4490
4791
|
if (snapshot === null) return false;
|
|
4491
4792
|
gl.activeTexture(gl.TEXTURE0 + REFRACT_SCENE_TEXTURE_UNIT);
|
|
@@ -4509,7 +4810,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4509
4810
|
const { gl } = this;
|
|
4510
4811
|
gl.uniform4f(u['uSeeThrough'] ?? null, 0, 0, 0, 0);
|
|
4511
4812
|
gl.activeTexture(gl.TEXTURE0 + REFRACT_SCENE_TEXTURE_UNIT);
|
|
4512
|
-
|
|
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);
|
|
4513
4815
|
}
|
|
4514
4816
|
|
|
4515
4817
|
disposeMesh(mesh: Mesh): void {
|
|
@@ -4754,7 +5056,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4754
5056
|
*/
|
|
4755
5057
|
private gradeCode(): number {
|
|
4756
5058
|
return this.sceneTarget === null || !this.quality.hdrScene
|
|
4757
|
-
?
|
|
5059
|
+
? forwardTransformCode(this.quality.outputTransform)
|
|
4758
5060
|
: 0;
|
|
4759
5061
|
}
|
|
4760
5062
|
|
|
@@ -4847,6 +5149,29 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4847
5149
|
return new ParticleBatch(this.gl, capacity, options);
|
|
4848
5150
|
}
|
|
4849
5151
|
|
|
5152
|
+
/**
|
|
5153
|
+
* Particles a caller's own compute wrote: WebGPU's alone, since this backend has no compute stage
|
|
5154
|
+
* to have written them (`computeSupported`). Said once, and nothing is drawn.
|
|
5155
|
+
*/
|
|
5156
|
+
drawDeviceParticles(
|
|
5157
|
+
_batch: ParticleBatch,
|
|
5158
|
+
_particles: DeviceParticles,
|
|
5159
|
+
_camera: Camera,
|
|
5160
|
+
_env: Environment,
|
|
5161
|
+
_timeSeconds: number,
|
|
5162
|
+
): void {
|
|
5163
|
+
if (this.warnedDeviceParticles) return;
|
|
5164
|
+
this.warnedDeviceParticles = true;
|
|
5165
|
+
console.warn(
|
|
5166
|
+
'Renderer: drawDeviceParticles draws particles a compute shader wrote, and WebGL2 has no ' +
|
|
5167
|
+
'compute stage, so nothing is drawn. Check `computeSupported`, and draw a pool with ' +
|
|
5168
|
+
'`drawParticles` where it is false.',
|
|
5169
|
+
);
|
|
5170
|
+
}
|
|
5171
|
+
|
|
5172
|
+
/** Said once: see `drawDeviceParticles`. */
|
|
5173
|
+
private warnedDeviceParticles = false;
|
|
5174
|
+
|
|
4850
5175
|
/**
|
|
4851
5176
|
* Draw a live particle pool.
|
|
4852
5177
|
*
|
|
@@ -4913,7 +5238,12 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4913
5238
|
const u = batch.uniforms;
|
|
4914
5239
|
const scene = this.sceneTarget;
|
|
4915
5240
|
let depth: WebGLTexture | null = null;
|
|
4916
|
-
if (
|
|
5241
|
+
if (
|
|
5242
|
+
batch.softDepth > 0 &&
|
|
5243
|
+
!this.reflectionPassActive &&
|
|
5244
|
+
!this.probePassActive &&
|
|
5245
|
+
!this.capturePassActive
|
|
5246
|
+
) {
|
|
4917
5247
|
depth = scene?.snapshotDepth(true) ?? null;
|
|
4918
5248
|
if (depth === null && !this.warnedSpriteDepth) {
|
|
4919
5249
|
this.warnedSpriteDepth = true;
|
|
@@ -5970,7 +6300,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
5970
6300
|
* throws a sentence with the device's numbers in it rather than a bare driver string.
|
|
5971
6301
|
*/
|
|
5972
6302
|
/** The lit fragment source this renderer's profile compiles, at a light budget. */
|
|
5973
|
-
private flatFragAt(budget: LightBudget): string {
|
|
6303
|
+
private flatFragAt(budget: LightBudget, overlay = this.litOn.SURFACE_OVERLAY): string {
|
|
5974
6304
|
return flatFrag({
|
|
5975
6305
|
pointShadows: this.quality.pointShadows,
|
|
5976
6306
|
directionalShadows: this.quality.directionalShadows,
|
|
@@ -5982,6 +6312,9 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
5982
6312
|
lightFixtures: this.litOn.LIGHT_FIXTURES,
|
|
5983
6313
|
surfaceEffects: this.litOn.SURFACE_EFFECTS,
|
|
5984
6314
|
driftLight: this.litOn.DRIFT_LIGHT,
|
|
6315
|
+
physicalSpecular: this.litOn.PHYSICAL_SPECULAR,
|
|
6316
|
+
surfaceOverlay: overlay,
|
|
6317
|
+
reflectionMaps: this.litOn.REFLECTION_MAPS,
|
|
5985
6318
|
maxLights: budget.maxLights,
|
|
5986
6319
|
maxAreaLights: budget.maxAreaLights,
|
|
5987
6320
|
});
|
|
@@ -6807,11 +7140,22 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
6807
7140
|
* Set before `beginFrame` and held until changed, like `setSpeedRush`, which it sits
|
|
6808
7141
|
* beside for the same reason: both describe how the finished image is presented rather
|
|
6809
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.
|
|
6810
7149
|
*/
|
|
6811
|
-
setCameraMotionBlur(scale: number): void {
|
|
7150
|
+
setCameraMotionBlur(scale: number, maxShare = MOTION_BLUR_MAX_UV): void {
|
|
6812
7151
|
this.motionBlurScale = Math.min(Math.max(scale, 0), 1);
|
|
7152
|
+
this.motionBlurMax = clampMotionShare(maxShare);
|
|
6813
7153
|
}
|
|
6814
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
|
+
|
|
6815
7159
|
/**
|
|
6816
7160
|
* The camera has cut: this frame is a new shot, not the next moment of the last one.
|
|
6817
7161
|
*
|
|
@@ -7023,6 +7367,47 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
7023
7367
|
: this.quality.outputExposure;
|
|
7024
7368
|
}
|
|
7025
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
|
+
|
|
7026
7411
|
/**
|
|
7027
7412
|
* Composite a flat colour over the finished frame — for a cut dipping to white or to black.
|
|
7028
7413
|
*
|
|
@@ -7114,6 +7499,21 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
7114
7499
|
this.sceneTarget.setVignette(strength);
|
|
7115
7500
|
}
|
|
7116
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
|
+
|
|
7117
7517
|
/**
|
|
7118
7518
|
* Film grain, held until changed: an amplitude in display values (0 is none, 0.03 is a fine
|
|
7119
7519
|
* grain) and this frame's seed. **The seed is the caller's**, because the engine takes time from
|
|
@@ -7304,6 +7704,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
7304
7704
|
|
|
7305
7705
|
beginFrame(clearColor: Vec3): void {
|
|
7306
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();
|
|
7307
7709
|
this.skinScattered = false;
|
|
7308
7710
|
this.skinScatter?.reset();
|
|
7309
7711
|
if (this.litAsked) {
|
|
@@ -7373,6 +7775,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
7373
7775
|
}
|
|
7374
7776
|
|
|
7375
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);
|
|
7376
7780
|
gl.clearColor(clearColor[0], clearColor[1], clearColor[2], 1);
|
|
7377
7781
|
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
|
7378
7782
|
}
|
|
@@ -7429,7 +7833,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
7429
7833
|
motion = {
|
|
7430
7834
|
reprojection: this.reprojection as Float32Array,
|
|
7431
7835
|
strength: blur,
|
|
7432
|
-
max:
|
|
7836
|
+
max: this.motionBlurMax,
|
|
7433
7837
|
};
|
|
7434
7838
|
}
|
|
7435
7839
|
}
|
|
@@ -7521,7 +7925,11 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
7521
7925
|
|
|
7522
7926
|
/* The grade travels to the resolve only where the mesh pass gave it up. See `bindMeshPass`. */
|
|
7523
7927
|
const lateGrade = this.quality.hdrScene
|
|
7524
|
-
? {
|
|
7928
|
+
? {
|
|
7929
|
+
transform: OUTPUT_TRANSFORM_CODE[this.quality.outputTransform] ?? 0,
|
|
7930
|
+
exposure: this.exposure,
|
|
7931
|
+
film: this.filmConstants,
|
|
7932
|
+
}
|
|
7525
7933
|
: { transform: 0, exposure: 1 };
|
|
7526
7934
|
/*
|
|
7527
7935
|
* Depth of field, on the same ceiling-and-dial split as bloom above: `depthOfField` decides
|
|
@@ -7597,6 +8005,49 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
7597
8005
|
* drawn over a reflective floor rather than under it, and before the post chain so bloom and
|
|
7598
8006
|
* the grade see a reflection exactly as they would see any other part of the picture.
|
|
7599
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
|
+
}
|
|
7600
8051
|
if (this.reflectionQueue.length > 0 && this.frameViewProj !== null) {
|
|
7601
8052
|
const scene = this.sceneTarget?.colorAttachment() ?? null;
|
|
7602
8053
|
const depth = this.sceneTarget?.snapshotDepth(true) ?? null;
|
|
@@ -7877,7 +8328,12 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
7877
8328
|
* other position, and the anti-flicker's period never started.
|
|
7878
8329
|
*/
|
|
7879
8330
|
if (this.framePresented) this.temporalJittering = false;
|
|
7880
|
-
if (
|
|
8331
|
+
if (
|
|
8332
|
+
!this.reflectionPassActive &&
|
|
8333
|
+
!this.probePassActive &&
|
|
8334
|
+
!this.capturePassActive &&
|
|
8335
|
+
!this.framePresented
|
|
8336
|
+
) {
|
|
7881
8337
|
this.frameViewProj = camera.viewProjection;
|
|
7882
8338
|
/* Copied rather than referenced: a caller moves its camera in place, and the decal pass
|
|
7883
8339
|
reads this at the end of the frame. */
|
|
@@ -7975,6 +8431,10 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
7975
8431
|
this.materialHasMaps = false;
|
|
7976
8432
|
this.currentMaterial = null;
|
|
7977
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;
|
|
7978
8438
|
/* Nor an ambient of its own: the frame's, until a draw asks. See `setAmbientSH`. */
|
|
7979
8439
|
this.ambientSH.fill(0);
|
|
7980
8440
|
/* No reflection and an environment at its own brightness; the emission the world asks for. */
|
|
@@ -8131,8 +8591,10 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
8131
8591
|
* unless a draw sets the strength above zero.
|
|
8132
8592
|
*/
|
|
8133
8593
|
gl.activeTexture(gl.TEXTURE0 + REFRACT_SCENE_TEXTURE_UNIT);
|
|
8134
|
-
gl.bindTexture(gl.TEXTURE_2D, this.emptyTexture2D);
|
|
8594
|
+
gl.bindTexture(gl.TEXTURE_2D, this.overlayTexture ?? this.emptyTexture2D);
|
|
8135
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);
|
|
8136
8598
|
/* Neither refracting nor glass, which is every draw that does not say otherwise. See
|
|
8137
8599
|
`bindSeeThrough` for the four numbers in the one vector. */
|
|
8138
8600
|
gl.uniform4f(u['uSeeThrough'] ?? null, 0, 0, 0, 0);
|
|
@@ -8541,9 +9003,18 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
8541
9003
|
* here, so there is no motion target for this to be written into, and a mover passed here is
|
|
8542
9004
|
* neither read nor advanced.
|
|
8543
9005
|
*/
|
|
8544
|
-
|
|
9006
|
+
previousModel: ReadonlyMat4 | Mover | null = null,
|
|
8545
9007
|
): void {
|
|
8546
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
|
+
}
|
|
8547
9018
|
/* Geometry that has not all arrived is not drawn, nor a mesh with none. See `Mesh.complete`
|
|
8548
9019
|
and `Mesh.indexCount`. */
|
|
8549
9020
|
if (!mesh.complete || mesh.indexCount === 0) return;
|
|
@@ -8574,7 +9045,11 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
8574
9045
|
* light and its colour, each into the blur's own target.
|
|
8575
9046
|
*/
|
|
8576
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();
|
|
8577
9051
|
this.drawMeshHalf(mesh, model, depthLayer, tint, splits ? 'scene' : 'whole');
|
|
9052
|
+
if (keeps) this.reflectionTargets?.off();
|
|
8578
9053
|
if (splits && this.skinScatter !== null) {
|
|
8579
9054
|
this.skinScatter.begin();
|
|
8580
9055
|
this.drawMeshHalf(mesh, model, depthLayer, tint, 'diffuse');
|
|
@@ -8589,6 +9064,21 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
8589
9064
|
* skin yet, this is the frame's own picture — not a mirror's or a probe's, nor an overlay after
|
|
8590
9065
|
* the frame — and the blur's target can be had.
|
|
8591
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
|
+
|
|
8592
9082
|
private skinSplits(): boolean {
|
|
8593
9083
|
const skin = this.skinScatter;
|
|
8594
9084
|
const scene = this.sceneTarget;
|
|
@@ -8599,6 +9089,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
8599
9089
|
!this.skinScattered &&
|
|
8600
9090
|
!this.reflectionPassActive &&
|
|
8601
9091
|
!this.probePassActive &&
|
|
9092
|
+
!this.capturePassActive &&
|
|
8602
9093
|
!this.framePresented &&
|
|
8603
9094
|
skin.ready(scene)
|
|
8604
9095
|
);
|
|
@@ -8616,10 +9107,17 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
8616
9107
|
const scene = this.sceneTarget;
|
|
8617
9108
|
const projection = this.frameProjection;
|
|
8618
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;
|
|
8619
9117
|
/* Latched whether or not skin was drawn: a skin drawn after this point is the whole surface. */
|
|
8620
9118
|
this.skinScattered = true;
|
|
8621
9119
|
if (!skin.pending) return;
|
|
8622
|
-
if (
|
|
9120
|
+
if (projection === null) return;
|
|
8623
9121
|
const depth = scene.snapshotDepth(true);
|
|
8624
9122
|
mat4.invert(this.skinInverseProjection, projection);
|
|
8625
9123
|
skin.spread(scene, depth, this.emptyTexture2D, projection, this.skinInverseProjection);
|