@driftengine/core 4.8.7 → 4.10.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 +123 -11
- package/README.md +43 -38
- package/dist/index.d.ts +8 -1
- package/dist/index.js +11 -3
- package/dist/render/ambientHarmonics.d.ts +1 -1
- package/dist/render/ambientHarmonics.js +1 -1
- package/dist/render/areaLights.d.ts +4 -4
- package/dist/render/areaLights.js +1 -1
- package/dist/render/atmosphere.d.ts +13 -0
- package/dist/render/atmosphere.js +9 -1
- package/dist/render/backend/api.d.ts +19 -4
- package/dist/render/backend/createRenderer.d.ts +6 -0
- package/dist/render/backend/createRenderer.js +4 -1
- package/dist/render/backend/select.js +11 -5
- package/dist/render/backend/webgl2/boneAnimations.d.ts +29 -0
- package/dist/render/backend/webgl2/boneAnimations.js +80 -0
- package/dist/render/backend/webgl2/instanced.d.ts +5 -1
- package/dist/render/backend/webgl2/instanced.js +13 -1
- package/dist/render/backend/webgl2/renderer.d.ts +180 -4
- package/dist/render/backend/webgl2/renderer.js +550 -40
- package/dist/render/backend/webgl2/skinPaletteTexture.d.ts +5 -3
- package/dist/render/backend/webgl2/skinPaletteTexture.js +6 -4
- package/dist/render/backend/webgpu/boneAnimations.d.ts +34 -0
- package/dist/render/backend/webgpu/boneAnimations.js +76 -0
- package/dist/render/backend/webgpu/buffers.d.ts +19 -4
- package/dist/render/backend/webgpu/buffers.js +18 -11
- package/dist/render/backend/webgpu/compressedUpload.d.ts +0 -1
- package/dist/render/backend/webgpu/compressedUpload.js +53 -8
- package/dist/render/backend/webgpu/depthPass.d.ts +59 -3
- package/dist/render/backend/webgpu/depthPass.js +49 -14
- package/dist/render/backend/webgpu/device.d.ts +7 -1
- package/dist/render/backend/webgpu/device.js +31 -3
- package/dist/render/backend/webgpu/drawCommand.d.ts +18 -4
- package/dist/render/backend/webgpu/drawCommand.js +10 -3
- package/dist/render/backend/webgpu/flatPass.d.ts +100 -42
- package/dist/render/backend/webgpu/flatPass.js +191 -43
- package/dist/render/backend/webgpu/instanced.d.ts +14 -1
- package/dist/render/backend/webgpu/instanced.js +24 -1
- package/dist/render/backend/webgpu/materialStore.d.ts +101 -0
- package/dist/render/backend/webgpu/materialStore.js +265 -0
- package/dist/render/backend/webgpu/pipelineCache.d.ts +6 -0
- package/dist/render/backend/webgpu/pipelineCache.js +7 -0
- 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 +283 -37
- package/dist/render/backend/webgpu/renderer.js +1363 -202
- package/dist/render/backend/webgpu/skinPaletteRing.d.ts +7 -3
- package/dist/render/backend/webgpu/skinPaletteRing.js +13 -7
- package/dist/render/backend/webgpu/skinPaletteTexture.d.ts +6 -0
- package/dist/render/backend/webgpu/skinPaletteTexture.js +7 -1
- package/dist/render/backend/webgpu/staticBundles.d.ts +125 -0
- package/dist/render/backend/webgpu/staticBundles.js +241 -0
- package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +10 -3
- package/dist/render/backend/webgpu/surfaceTexturePass.js +20 -2
- package/dist/render/backend/webgpu/uniformRing.d.ts +21 -0
- package/dist/render/backend/webgpu/uniformRing.js +9 -1
- package/dist/render/bloomChain.d.ts +3 -3
- package/dist/render/boneAnimation.d.ts +92 -0
- package/dist/render/boneAnimation.js +133 -0
- package/dist/render/compressedSource.d.ts +33 -13
- package/dist/render/compressedSource.js +70 -25
- 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/glCompressed.js +83 -0
- package/dist/render/instances.d.ts +26 -0
- package/dist/render/lightBudget.d.ts +10 -0
- package/dist/render/lightBudget.js +10 -0
- package/dist/render/lightmap.d.ts +1 -1
- package/dist/render/mesh.d.ts +5 -0
- package/dist/render/mesh.js +6 -0
- package/dist/render/pass.d.ts +49 -0
- package/dist/render/pointLightSelection.d.ts +4 -4
- 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 +7 -1
- 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/boneAnimation.d.ts +19 -0
- package/dist/render/shaders/boneAnimation.js +63 -0
- package/dist/render/shaders/caustics.d.ts +1 -1
- package/dist/render/shaders/depth.d.ts +7 -0
- package/dist/render/shaders/depth.js +35 -10
- 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 +16 -0
- package/dist/render/shaders/flat/index.js +33 -5
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +43 -5
- package/dist/render/shaders/flat/models.js +1 -1
- 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 +3 -3
- package/dist/render/shaders/flat/preamble.js +38 -24
- 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 +93 -0
- package/dist/render/shaders/generated/depth.wgsl.js +93 -0
- 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 +3254 -2920
- package/dist/render/shaders/generated/flat.wgsl.js +3292 -2957
- 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/outputTransform.d.ts +7 -0
- package/dist/render/shaders/outputTransform.js +28 -14
- 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/vertexChannel.d.ts +7 -0
- package/dist/render/shaders/vertexChannel.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/staticDraws.d.ts +47 -0
- package/dist/render/staticDraws.js +43 -0
- package/dist/render/surfaceOverlay.d.ts +132 -0
- package/dist/render/surfaceOverlay.js +85 -0
- package/dist/render/surfaceTexture.d.ts +57 -7
- package/dist/render/surfaceTexture.js +125 -17
- 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 +24 -2
- package/src/render/ambientHarmonics.ts +1 -1
- package/src/render/areaLights.ts +4 -4
- package/src/render/atmosphere.ts +22 -1
- package/src/render/backend/api.ts +22 -3
- package/src/render/backend/createRenderer.ts +18 -1
- package/src/render/backend/select.ts +10 -5
- package/src/render/backend/webgl2/boneAnimations.ts +105 -0
- package/src/render/backend/webgl2/instanced.ts +20 -1
- package/src/render/backend/webgl2/renderer.ts +672 -37
- package/src/render/backend/webgl2/skinPaletteTexture.ts +6 -4
- package/src/render/backend/webgpu/boneAnimations.ts +97 -0
- package/src/render/backend/webgpu/buffers.ts +42 -16
- package/src/render/backend/webgpu/compressedUpload.ts +59 -9
- package/src/render/backend/webgpu/depthPass.ts +51 -10
- package/src/render/backend/webgpu/device.ts +42 -3
- package/src/render/backend/webgpu/drawCommand.ts +22 -4
- package/src/render/backend/webgpu/flatPass.ts +250 -40
- package/src/render/backend/webgpu/instanced.ts +32 -1
- package/src/render/backend/webgpu/materialStore.ts +276 -0
- package/src/render/backend/webgpu/pipelineCache.ts +7 -0
- 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 +1655 -230
- package/src/render/backend/webgpu/skinPaletteRing.ts +13 -7
- package/src/render/backend/webgpu/skinPaletteTexture.ts +7 -1
- package/src/render/backend/webgpu/staticBundles.ts +363 -0
- package/src/render/backend/webgpu/surfaceTexturePass.ts +29 -3
- package/src/render/backend/webgpu/uniformRing.ts +10 -1
- package/src/render/bloomChain.ts +3 -3
- package/src/render/boneAnimation.ts +223 -0
- package/src/render/compressedSource.ts +121 -29
- 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/glCompressed.ts +83 -0
- package/src/render/instances.ts +27 -0
- package/src/render/lightBudget.ts +11 -0
- package/src/render/lightmap.ts +1 -1
- package/src/render/mesh.ts +6 -0
- package/src/render/pass.ts +53 -1
- package/src/render/pointLightSelection.ts +4 -4
- 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 +7 -1
- package/src/render/shaders/boneAnimation.ts +63 -0
- package/src/render/shaders/depth.ts +45 -10
- package/src/render/shaders/filmLook.ts +2 -1
- package/src/render/shaders/flat/glassTint.ts +5 -1
- package/src/render/shaders/flat/index.ts +49 -5
- package/src/render/shaders/flat/main.ts +43 -5
- package/src/render/shaders/flat/models.ts +1 -1
- package/src/render/shaders/flat/overlay.ts +117 -0
- package/src/render/shaders/flat/pointShadow.ts +3 -3
- package/src/render/shaders/flat/preamble.ts +38 -24
- 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 +95 -0
- 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 +3292 -2957
- 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/outputTransform.ts +30 -14
- 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/shaders/vertexChannel.ts +27 -0
- package/src/render/ssrPass.ts +138 -1
- package/src/render/staticDraws.ts +95 -0
- package/src/render/surfaceOverlay.ts +195 -0
- package/src/render/surfaceTexture.ts +161 -27
- 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';
|
|
@@ -200,6 +218,8 @@ import {
|
|
|
200
218
|
DEPTH_CUTOUT_FRAG,
|
|
201
219
|
DEPTH_CUTOUT_VERT,
|
|
202
220
|
DEPTH_FRAG,
|
|
221
|
+
DEPTH_INSTANCED_ANIMATED_CUTOUT_VERT,
|
|
222
|
+
DEPTH_INSTANCED_ANIMATED_VERT,
|
|
203
223
|
DEPTH_INSTANCED_CUTOUT_VERT,
|
|
204
224
|
DEPTH_INSTANCED_VERT,
|
|
205
225
|
DEPTH_SKINNED_VERT,
|
|
@@ -214,6 +234,8 @@ import { GlassCasterList, type GlassReplaySink } from '../../glassCasters.ts';
|
|
|
214
234
|
import { SunGlassTint } from '../../sunGlassTint.ts';
|
|
215
235
|
import { GlassTintPrograms, type GlassTintProgram } from './glassTintPrograms.ts';
|
|
216
236
|
import type { SceneCasterMaterial, ShadowCasterSink, ShadowCasters } from '../../shadowCasters.ts';
|
|
237
|
+
import { captureStaticDraws, replayStaticDraws } from '../../staticDraws.ts';
|
|
238
|
+
import type { StaticDrawsHandle, StaticDrawsList } from '../../staticDraws.ts';
|
|
217
239
|
import { FACE_COUNT, PointShadowMap, type PointGlassDraw } from '../../pointShadowMap.ts';
|
|
218
240
|
import { createResolvedPointShadows, DEFAULT_SOURCE_RADIUS } from '../../pointShadowImage.ts';
|
|
219
241
|
import {
|
|
@@ -259,7 +281,11 @@ import { SurfaceTexture } from '../../surfaceTexture.ts';
|
|
|
259
281
|
import { sceneCaptureTexels } from '../../sceneCapture.ts';
|
|
260
282
|
import { GlSceneCaptureTarget } from './sceneCaptureTarget.ts';
|
|
261
283
|
import { glCompressedFormats } from '../../glCompressed.ts';
|
|
262
|
-
import type {
|
|
284
|
+
import type {
|
|
285
|
+
CompressedTextureFormat,
|
|
286
|
+
CompressedTextureSource,
|
|
287
|
+
SurfaceSource,
|
|
288
|
+
} from '../../compressedSource.ts';
|
|
263
289
|
import type { SurfaceMaterial, SurfaceTextureOptions } from '../../surfaceTexture.ts';
|
|
264
290
|
import { PlumeRenderer } from '../../plumeRenderer.ts';
|
|
265
291
|
import { createScatterDeform, resolveScatterDeform } from '../../scatterDeform.ts';
|
|
@@ -293,8 +319,22 @@ import { TextRenderer } from '../../textRenderer.ts';
|
|
|
293
319
|
import { SdfTextRenderer } from '../../sdfTextRenderer.ts';
|
|
294
320
|
import type { SdfFont } from '../../sdfFont.ts';
|
|
295
321
|
import type { SdfTextStyle } from '../../sdfTextLayout.ts';
|
|
296
|
-
import {
|
|
297
|
-
|
|
322
|
+
import {
|
|
323
|
+
MOTION_BLUR_MAX_UV,
|
|
324
|
+
OBJECT_BLUR_UNKEPT,
|
|
325
|
+
RUSH_REACH_UV,
|
|
326
|
+
clampMotionShare,
|
|
327
|
+
} from '../../vertexDefaults.ts';
|
|
328
|
+
import { OUTPUT_TRANSFORM_CODE, forwardTransformCode } from '../../vertexDefaults.ts';
|
|
329
|
+
import type { DisplayRange } from '../../displayRange.ts';
|
|
330
|
+
import {
|
|
331
|
+
DEFAULT_FILMIC_CURVE,
|
|
332
|
+
FILMIC_CLAMPED,
|
|
333
|
+
FILMIC_CONSTANTS,
|
|
334
|
+
FILMIC_WITHOUT_COMPOSITE,
|
|
335
|
+
resolveFilmicCurve,
|
|
336
|
+
type FilmicCurve,
|
|
337
|
+
} from '../../filmicCurve.ts';
|
|
298
338
|
import type { RenderBackend } from '../api.ts';
|
|
299
339
|
import { deviceSnappedCellSize } from '../../textLayout.ts';
|
|
300
340
|
import type { TextStyle } from '../../textLayout.ts';
|
|
@@ -303,6 +343,14 @@ import type { WindStreakOptions } from '../../windStreakRenderer.ts';
|
|
|
303
343
|
import type { WindField } from '../../windField.ts';
|
|
304
344
|
import type { FlockParams } from '../../flockRenderer.ts';
|
|
305
345
|
import { InstancedBatch } from './instanced.ts';
|
|
346
|
+
import { GlBoneAnimation } from './boneAnimations.ts';
|
|
347
|
+
import { packBoneAnimation } from '../../boneAnimation.ts';
|
|
348
|
+
import type { BoneAnimationClip, BoneAnimationHandle } from '../../boneAnimation.ts';
|
|
349
|
+
import {
|
|
350
|
+
BONE_PLACES_TEXTURE_UNIT,
|
|
351
|
+
BONE_TURNS_TEXTURE_UNIT,
|
|
352
|
+
INSTANCE_CLOCKS_TEXTURE_UNIT,
|
|
353
|
+
} from '../../lightBudget.ts';
|
|
306
354
|
import type { InstancedOptions, MeshInstances } from '../../instances.ts';
|
|
307
355
|
import { cutoutOf } from '../../cutoutCaster.ts';
|
|
308
356
|
import { CUTOUT_RESOLVE_CODE, resolveCutout } from '../../cutoutDither.ts';
|
|
@@ -1348,6 +1396,10 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
1348
1396
|
private readonly reflectionQueue = new ReflectionQueue();
|
|
1349
1397
|
/** Built on the first frame that submits a surface, and never for a scene with none. */
|
|
1350
1398
|
private ssr: SsrPass | null = null;
|
|
1399
|
+
/** The frame's materials' reflections, resolved; null where off. See `frameReflections.ts`. */
|
|
1400
|
+
private readonly frameReflections: ResolvedFrameReflections | null;
|
|
1401
|
+
/** Their two maps on the scene's framebuffer, null where they cannot be drawn. */
|
|
1402
|
+
private readonly reflectionTargets: ReflectionSurfaceTargets | null;
|
|
1351
1403
|
/** Said once where a reflection cannot be traced at all. */
|
|
1352
1404
|
private reflectionsRefused = false;
|
|
1353
1405
|
/** Where the frame's camera stands, which is what gives a reconstructed normal its sign. */
|
|
@@ -1430,6 +1482,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
1430
1482
|
* exactly as it asked to be and nothing about it changes.
|
|
1431
1483
|
*/
|
|
1432
1484
|
private exposure: number;
|
|
1485
|
+
/** The `filmic` curve's eight constants, held until `setFilmicCurve` is called again. */
|
|
1486
|
+
private readonly filmConstants = new Float32Array(FILMIC_CONSTANTS);
|
|
1433
1487
|
/**
|
|
1434
1488
|
* This frame's veil colour, and how much of it to composite. See `setFrameVeil`.
|
|
1435
1489
|
*
|
|
@@ -1544,6 +1598,11 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
1544
1598
|
private readonly depthCutoutUniforms: Record<string, WebGLUniformLocation>;
|
|
1545
1599
|
private readonly depthInstancedCutoutProgram: WebGLProgram;
|
|
1546
1600
|
private readonly depthInstancedCutoutUniforms: Record<string, WebGLUniformLocation>;
|
|
1601
|
+
/** A crowd's casters: the instanced depth programs that play a bone animation. */
|
|
1602
|
+
private readonly depthAnimatedProgram: WebGLProgram;
|
|
1603
|
+
private readonly depthAnimatedUniforms: Record<string, WebGLUniformLocation>;
|
|
1604
|
+
private readonly depthAnimatedCutoutProgram: WebGLProgram;
|
|
1605
|
+
private readonly depthAnimatedCutoutUniforms: Record<string, WebGLUniformLocation>;
|
|
1547
1606
|
private readonly scatterDepthProgram: WebGLProgram;
|
|
1548
1607
|
private readonly scatterDepthUniforms: Record<string, WebGLUniformLocation>;
|
|
1549
1608
|
/** The sun's static, moving and peeled maps, as layers of one array. See `shadowMap.ts`. */
|
|
@@ -1631,6 +1690,15 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
1631
1690
|
private warnedNoPointShadowArray = false;
|
|
1632
1691
|
/** Said once, for the retired ambient dial. */
|
|
1633
1692
|
private warnedEnvironmentAmbient = false;
|
|
1693
|
+
/** The overlay the draws wear, `uOverlay`: see `setSurfaceOverlay`. */
|
|
1694
|
+
private readonly overlayFloats = new Float32Array(OVERLAY_FLOATS);
|
|
1695
|
+
/** Its atlas, and the 2D copy of it bound where the refraction copy goes. */
|
|
1696
|
+
private overlayMaps: SurfaceTexture | null = null;
|
|
1697
|
+
private overlayTexture: WebGLTexture | null = null;
|
|
1698
|
+
/** Whether this device's lit stage has room for the overlay's vectors: asked once. */
|
|
1699
|
+
private overlayRoom: boolean | null = null;
|
|
1700
|
+
private warnedOverlayRoom = false;
|
|
1701
|
+
private warnedOverlayBlocks = false;
|
|
1634
1702
|
/** Whether a probe is being baked, so the passes that would recurse into one can tell. */
|
|
1635
1703
|
private probePassActive = false;
|
|
1636
1704
|
/**
|
|
@@ -1955,24 +2023,33 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
1955
2023
|
const gpuBatch = batch as InstancedBatch;
|
|
1956
2024
|
const count = Math.min(data.count, gpuBatch.capacity);
|
|
1957
2025
|
if (count === 0) return;
|
|
2026
|
+
const clip = gpuBatch.animation;
|
|
2027
|
+
if (clip !== null && clip.disposed) return;
|
|
1958
2028
|
this.shadowDrawBudget.ask();
|
|
1959
2029
|
const twoSided = material?.doubleSided === true;
|
|
1960
2030
|
if (twoSided) gl.disable(gl.CULL_FACE);
|
|
1961
2031
|
const cutout = cutoutOf(material);
|
|
1962
2032
|
if (cutout !== null) {
|
|
1963
|
-
|
|
1964
|
-
|
|
2033
|
+
/* A crowd casts where its clip has moved it, through the animated variant. */
|
|
2034
|
+
const uniforms =
|
|
2035
|
+
clip === null ? this.depthInstancedCutoutUniforms : this.depthAnimatedCutoutUniforms;
|
|
2036
|
+
gl.useProgram(
|
|
2037
|
+
clip === null ? this.depthInstancedCutoutProgram : this.depthAnimatedCutoutProgram,
|
|
2038
|
+
);
|
|
2039
|
+
this.bindCutoutDepth(uniforms, cutout);
|
|
2040
|
+
if (clip !== null) this.bindAnimation(uniforms, gpuBatch);
|
|
1965
2041
|
gpuBatch.draw(gl, count);
|
|
1966
2042
|
if (twoSided && this.depthPassCullsFaces) gl.enable(gl.CULL_FACE);
|
|
1967
2043
|
gl.useProgram(this.depthProgram);
|
|
1968
2044
|
this.restoreSurfaceTextureUnit();
|
|
1969
2045
|
return;
|
|
1970
2046
|
}
|
|
1971
|
-
const u = this.depthInstancedUniforms;
|
|
1972
|
-
gl.useProgram(this.depthInstancedProgram);
|
|
2047
|
+
const u = clip === null ? this.depthInstancedUniforms : this.depthAnimatedUniforms;
|
|
2048
|
+
gl.useProgram(clip === null ? this.depthInstancedProgram : this.depthAnimatedProgram);
|
|
1973
2049
|
gl.uniformMatrix4fv(u['uLightViewProj'] ?? null, false, this.activeDepthViewProj);
|
|
1974
2050
|
gl.uniform1i(u['uPeelShadowLayer'] ?? null, this.activeDepthPeel ? 1 : 0);
|
|
1975
2051
|
if (this.activeDepthPeel) gl.uniform1i(u['uPreviousShadowMap'] ?? null, 0);
|
|
2052
|
+
if (clip !== null) this.bindAnimation(u, gpuBatch);
|
|
1976
2053
|
gpuBatch.draw(gl, count);
|
|
1977
2054
|
if (twoSided && this.depthPassCullsFaces) gl.enable(gl.CULL_FACE);
|
|
1978
2055
|
gl.useProgram(this.depthProgram);
|
|
@@ -2298,6 +2375,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
2298
2375
|
gl: WebGL2RenderingContext;
|
|
2299
2376
|
outputTransform: number;
|
|
2300
2377
|
outputExposure: number;
|
|
2378
|
+
sceneColor: WebGLTexture | null;
|
|
2379
|
+
sceneDepth: WebGLTexture | null;
|
|
2301
2380
|
} | null = null;
|
|
2302
2381
|
private passDevice: PassDevice | null = null;
|
|
2303
2382
|
|
|
@@ -2309,6 +2388,9 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
2309
2388
|
* call. `init` runs now rather than at the first frame, because building a pipeline in the
|
|
2310
2389
|
* frame loop is the allocation the house rules are about.
|
|
2311
2390
|
*/
|
|
2391
|
+
/** Each registered pass's declared reads, as a mask, resolved once at registration. */
|
|
2392
|
+
private readonly passReads = new Map<PassHandle, number>();
|
|
2393
|
+
|
|
2312
2394
|
registerPass(definition: PassDefinition): PassHandle {
|
|
2313
2395
|
const handle = registerIn(this.passes, definition);
|
|
2314
2396
|
/*
|
|
@@ -2327,6 +2409,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
2327
2409
|
depthCorrection: correction,
|
|
2328
2410
|
};
|
|
2329
2411
|
if (definition.prepare !== undefined) this.preparingPasses++;
|
|
2412
|
+
/* Resolved once: `maskOf` builds an array, and the frame path may not. */
|
|
2413
|
+
this.passReads.set(handle, maskOf(...(definition.reads ?? [])));
|
|
2330
2414
|
definition.init?.(this.passDevice);
|
|
2331
2415
|
return handle;
|
|
2332
2416
|
}
|
|
@@ -2378,10 +2462,28 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
2378
2462
|
gl: this.gl,
|
|
2379
2463
|
outputTransform: 0,
|
|
2380
2464
|
outputExposure: 1,
|
|
2465
|
+
sceneColor: null,
|
|
2466
|
+
sceneDepth: null,
|
|
2381
2467
|
};
|
|
2382
2468
|
const context = this.passContext;
|
|
2383
2469
|
context.outputTransform = this.gradeCode();
|
|
2384
2470
|
context.outputExposure = this.gradeExposure();
|
|
2471
|
+
/*
|
|
2472
|
+
* The copies a pass declared it reads, taken here with everything drawn before it: colour
|
|
2473
|
+
* afresh at each reading pass, as WebGPU's `takePassColour` does, so a full-screen effect drawn
|
|
2474
|
+
* last reads the whole frame; depth afresh too, since a blit is the whole cost of either here.
|
|
2475
|
+
* Not in a mirror, a probe's face or a capture, whose pictures are not the frame's.
|
|
2476
|
+
*/
|
|
2477
|
+
const reads = this.passReads.get(handle) ?? 0;
|
|
2478
|
+
const own = !this.reflectionPassActive && !this.probePassActive && !this.capturePassActive;
|
|
2479
|
+
context.sceneColor =
|
|
2480
|
+
own && (reads & COLOR_SNAPSHOT) !== 0
|
|
2481
|
+
? (this.sceneTarget?.snapshotColor(true) ?? null)
|
|
2482
|
+
: null;
|
|
2483
|
+
context.sceneDepth =
|
|
2484
|
+
own && (reads & DEPTH_SNAPSHOT) !== 0
|
|
2485
|
+
? (this.sceneTarget?.snapshotDepth(true) ?? null)
|
|
2486
|
+
: null;
|
|
2385
2487
|
definition.draw(context as PassContext);
|
|
2386
2488
|
}
|
|
2387
2489
|
|
|
@@ -2389,6 +2491,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
2389
2491
|
unregisterPass(handle: PassHandle): void {
|
|
2390
2492
|
const definition = unregisterIn(this.passes, handle);
|
|
2391
2493
|
if (definition?.prepare !== undefined) this.preparingPasses--;
|
|
2494
|
+
this.passReads.delete(handle);
|
|
2392
2495
|
if (definition !== undefined && this.passDevice !== null) definition.dispose?.(this.passDevice);
|
|
2393
2496
|
}
|
|
2394
2497
|
|
|
@@ -2559,10 +2662,11 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
2559
2662
|
gl.bindTexture(gl.TEXTURE_2D, this.clusterTexture);
|
|
2560
2663
|
gl.uniform1i(u['uClusterTable'] ?? null, CLUSTER_TABLE_TEXTURE_UNIT);
|
|
2561
2664
|
gl.uniform1i(u['uClustered'] ?? null, on ? 1 : 0);
|
|
2562
|
-
if (!on) return;
|
|
2563
2665
|
|
|
2564
2666
|
/*
|
|
2565
|
-
* The frustum, read off the projection rather than taken as a parameter
|
|
2667
|
+
* The frustum, read off the projection rather than taken as a parameter — **and with the view,
|
|
2668
|
+
* written whether or not the lights are clustered**: a surface overlay's rim finds its place on
|
|
2669
|
+
* the screen through the same two (`overlayScreen`).
|
|
2566
2670
|
*
|
|
2567
2671
|
* A perspective matrix carries `1 / tan(fovY/2)` at [5] and that over the aspect at [0], and
|
|
2568
2672
|
* the near and far planes in [14] and [10]. Deriving them here means the binner and the shader
|
|
@@ -2580,6 +2684,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
2580
2684
|
this.clusterFrustum[3] = aspect;
|
|
2581
2685
|
gl.uniform4fv(u['uClusterFrustum'] ?? null, this.clusterFrustum);
|
|
2582
2686
|
gl.uniformMatrix4fv(u['uView'] ?? null, false, camera.view);
|
|
2687
|
+
if (!on) return;
|
|
2583
2688
|
|
|
2584
2689
|
this.clusterTable ??= createClusterTable();
|
|
2585
2690
|
/* The rectangles past the fixed arm's go into the table. See `ClusterLightSet.areas`. */
|
|
@@ -2683,6 +2788,13 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
2683
2788
|
this.quality = resolveRenderQuality(qualityOptions);
|
|
2684
2789
|
/* The grade this world asked for, until a frame says otherwise. See `setOutputExposure`. */
|
|
2685
2790
|
this.exposure = this.quality.outputExposure;
|
|
2791
|
+
resolveFilmicCurve(DEFAULT_FILMIC_CURVE, this.filmConstants);
|
|
2792
|
+
if (
|
|
2793
|
+
this.quality.outputTransform === 'filmic' &&
|
|
2794
|
+
!(this.quality.screenEffects && this.quality.hdrScene)
|
|
2795
|
+
) {
|
|
2796
|
+
console.warn(FILMIC_WITHOUT_COMPOSITE);
|
|
2797
|
+
}
|
|
2686
2798
|
/*
|
|
2687
2799
|
* Multisampling, but only when anything can use it.
|
|
2688
2800
|
*
|
|
@@ -2981,6 +3093,29 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
2981
3093
|
this.quality.discardResolvedAttachments,
|
|
2982
3094
|
)
|
|
2983
3095
|
: null;
|
|
3096
|
+
/*
|
|
3097
|
+
* The frame's materials' reflections, where the frame can carry them: scene light to swap the
|
|
3098
|
+
* probe's share in, and one sample, which the trace's depth copy and both maps are. Said once
|
|
3099
|
+
* where it cannot, in the words the other backend uses. See `frameReflections.ts`.
|
|
3100
|
+
*/
|
|
3101
|
+
this.frameReflections = resolveFrameReflections(this.quality.screenSpaceReflections);
|
|
3102
|
+
const reflectionsRefusal =
|
|
3103
|
+
this.frameReflections === null
|
|
3104
|
+
? null
|
|
3105
|
+
: this.sceneTarget === null || !this.sceneTarget.keepsRange
|
|
3106
|
+
? 'the frame keeps no scene light to swap the probe in (`screenEffects` and `hdrScene`)'
|
|
3107
|
+
: this.sceneTarget.sampleCount > 1
|
|
3108
|
+
? 'a multisampled frame has no single depth or colour to march through'
|
|
3109
|
+
: null;
|
|
3110
|
+
if (reflectionsRefusal !== null) {
|
|
3111
|
+
console.warn(`WebGL2: ${frameReflectionsRefused(reflectionsRefusal)}`);
|
|
3112
|
+
}
|
|
3113
|
+
this.reflectionTargets =
|
|
3114
|
+
this.frameReflections !== null && reflectionsRefusal === null
|
|
3115
|
+
? new ReflectionSurfaceTargets(gl)
|
|
3116
|
+
: null;
|
|
3117
|
+
/* The lit programs write the maps from the first frame they are rebuilt in. See `litOn`. */
|
|
3118
|
+
if (this.reflectionTargets !== null) this.askLit('REFLECTION_MAPS');
|
|
2984
3119
|
/* Screen-space skin needs the composite to add into; without one it is pre-integrated, said once. */
|
|
2985
3120
|
if (this.quality.skinScattering === 'screen-space' && this.sceneTarget === null) {
|
|
2986
3121
|
console.warn(
|
|
@@ -3077,10 +3212,33 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
3077
3212
|
this.depthInstancedCutoutProgram,
|
|
3078
3213
|
'depth-instanced-cutout',
|
|
3079
3214
|
);
|
|
3215
|
+
this.depthAnimatedProgram = compileProgram(
|
|
3216
|
+
gl,
|
|
3217
|
+
DEPTH_INSTANCED_ANIMATED_VERT,
|
|
3218
|
+
DEPTH_FRAG,
|
|
3219
|
+
'depth-instanced-animated',
|
|
3220
|
+
);
|
|
3221
|
+
this.depthAnimatedUniforms = uniformLocations(
|
|
3222
|
+
gl,
|
|
3223
|
+
this.depthAnimatedProgram,
|
|
3224
|
+
'depth-instanced-animated',
|
|
3225
|
+
);
|
|
3226
|
+
this.depthAnimatedCutoutProgram = compileProgram(
|
|
3227
|
+
gl,
|
|
3228
|
+
DEPTH_INSTANCED_ANIMATED_CUTOUT_VERT,
|
|
3229
|
+
DEPTH_CUTOUT_FRAG,
|
|
3230
|
+
'depth-instanced-animated-cutout',
|
|
3231
|
+
);
|
|
3232
|
+
this.depthAnimatedCutoutUniforms = uniformLocations(
|
|
3233
|
+
gl,
|
|
3234
|
+
this.depthAnimatedCutoutProgram,
|
|
3235
|
+
'depth-instanced-animated-cutout',
|
|
3236
|
+
);
|
|
3080
3237
|
/* The cutout map is always read from the surface unit, which the draw borrows and gives back. */
|
|
3081
3238
|
for (const [program, uniforms] of [
|
|
3082
3239
|
[this.depthCutoutProgram, this.depthCutoutUniforms],
|
|
3083
3240
|
[this.depthInstancedCutoutProgram, this.depthInstancedCutoutUniforms],
|
|
3241
|
+
[this.depthAnimatedCutoutProgram, this.depthAnimatedCutoutUniforms],
|
|
3084
3242
|
] as const) {
|
|
3085
3243
|
gl.useProgram(program);
|
|
3086
3244
|
gl.uniform1i(uniforms['uCutoutMap'] ?? null, SURFACE_TEXTURE_UNIT);
|
|
@@ -3274,6 +3432,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
3274
3432
|
this.lightVolumeProgram,
|
|
3275
3433
|
this.depthCutoutProgram,
|
|
3276
3434
|
this.depthInstancedCutoutProgram,
|
|
3435
|
+
this.depthAnimatedProgram,
|
|
3436
|
+
this.depthAnimatedCutoutProgram,
|
|
3277
3437
|
]) {
|
|
3278
3438
|
gl.deleteProgram(program);
|
|
3279
3439
|
}
|
|
@@ -3620,13 +3780,19 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
3620
3780
|
*
|
|
3621
3781
|
* Not a hot path — it re-uploads the whole image and regenerates the mip chain.
|
|
3622
3782
|
*/
|
|
3623
|
-
updateSurfaceTexture(
|
|
3783
|
+
updateSurfaceTexture(
|
|
3784
|
+
texture: SurfaceTexture,
|
|
3785
|
+
source: TexImageSource | CompressedTextureSource,
|
|
3786
|
+
): void {
|
|
3624
3787
|
if (this.contextLost) return;
|
|
3625
|
-
texture.update(this.gl, source);
|
|
3788
|
+
texture.update(this.gl, source, this.compressedFormats);
|
|
3789
|
+
/* New pixels are a new copy for the overlay's slot: see `SurfaceTexture.flatLayer`. */
|
|
3790
|
+
if (texture === this.overlayMaps) this.bindOverlayMaps(texture);
|
|
3626
3791
|
}
|
|
3627
3792
|
|
|
3628
3793
|
disposeSurfaceTexture(texture: SurfaceTexture): void {
|
|
3629
3794
|
if (this.contextLost) return;
|
|
3795
|
+
if (texture === this.overlayMaps) this.bindOverlayMaps(null);
|
|
3630
3796
|
texture.dispose(this.gl);
|
|
3631
3797
|
this.captureTargets.get(texture)?.dispose(this.gl);
|
|
3632
3798
|
this.captureTargets.delete(texture);
|
|
@@ -3856,6 +4022,13 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
3856
4022
|
this.materialDoubleSided = material?.doubleSided === true;
|
|
3857
4023
|
this.materialHasMaps = materialHasMaps(material);
|
|
3858
4024
|
this.currentMaterial = material;
|
|
4025
|
+
const reflectivity = material?.reflectivity;
|
|
4026
|
+
const environmentGain = material?.environmentGain;
|
|
4027
|
+
this.materialReflectivity =
|
|
4028
|
+
reflectivity === undefined ? null : Math.min(1, Math.max(0, reflectivity));
|
|
4029
|
+
this.materialEnvironmentGain =
|
|
4030
|
+
environmentGain === undefined ? null : Math.max(0, environmentGain);
|
|
4031
|
+
this.writeEnvironmentDials();
|
|
3859
4032
|
/* What the blur spreads this skin by: the last material to name a profile says. */
|
|
3860
4033
|
const model = material?.model;
|
|
3861
4034
|
warnUnpagedLightmap(model, material?.modelMap);
|
|
@@ -3903,7 +4076,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
3903
4076
|
const v = this.cutoutVector;
|
|
3904
4077
|
v[0] = material?.cutout ?? 0;
|
|
3905
4078
|
v[1] = CUTOUT_RESOLVE_CODE[this.cutoutResolveNow(material, translucent)];
|
|
3906
|
-
|
|
4079
|
+
/* z unused: the dither's frame is the pass's, in `uHighlightMin.w`. See `uCutout`. */
|
|
4080
|
+
v[2] = 0;
|
|
3907
4081
|
/* The material's lighting channels, which the lamps' are compared with. See `uCutout`. */
|
|
3908
4082
|
v[3] = lightChannelsOf(material?.lightChannels);
|
|
3909
4083
|
return v;
|
|
@@ -4021,6 +4195,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4021
4195
|
/* The first physical highlight asked for is when the lit programs start carrying one. */
|
|
4022
4196
|
if (material?.physicalSpecular === true) this.askLit('PHYSICAL_SPECULAR');
|
|
4023
4197
|
gl.uniform4fv(u['uModelParams'] ?? null, this.modelParams);
|
|
4198
|
+
/* The draw's overlay, which a program born mid-pass must wear too: see `setSurfaceOverlay`. */
|
|
4199
|
+
gl.uniform4fv(u['uOverlay'] ?? null, this.overlayFloats);
|
|
4024
4200
|
|
|
4025
4201
|
/* The albedo's effects table, or the stand-in the shader reads as none. Before the early
|
|
4026
4202
|
return, so a material with no albedo still leaves a complete texture on the unit. */
|
|
@@ -4115,11 +4291,97 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4115
4291
|
*/
|
|
4116
4292
|
setSurfaceReflectivity(amount: number): void {
|
|
4117
4293
|
if (this.contextLost) return;
|
|
4294
|
+
this.passReflectivity = Math.min(1, Math.max(0, amount));
|
|
4295
|
+
this.writeEnvironmentDials();
|
|
4296
|
+
}
|
|
4297
|
+
|
|
4298
|
+
/**
|
|
4299
|
+
* The reflectivity and environment gain the setters gave the pass, and the ones the material
|
|
4300
|
+
* set now states, or null where it states none (`SurfaceMaterial.reflectivity`). A draw wears the
|
|
4301
|
+
* material's where it has one and the pass's otherwise.
|
|
4302
|
+
*/
|
|
4303
|
+
private passReflectivity = 0;
|
|
4304
|
+
private passEnvironmentGain = 1;
|
|
4305
|
+
private materialReflectivity: number | null = null;
|
|
4306
|
+
private materialEnvironmentGain: number | null = null;
|
|
4307
|
+
|
|
4308
|
+
private writeEnvironmentDials(): void {
|
|
4118
4309
|
this.materials.dirty();
|
|
4119
|
-
this.environmentDials[1] =
|
|
4310
|
+
this.environmentDials[1] = this.materialReflectivity ?? this.passReflectivity;
|
|
4311
|
+
this.environmentDials[2] = this.materialEnvironmentGain ?? this.passEnvironmentGain;
|
|
4120
4312
|
this.writeFlatVector('uEnvironmentDials', this.environmentDials);
|
|
4121
4313
|
}
|
|
4122
4314
|
|
|
4315
|
+
/**
|
|
4316
|
+
* The overlay the following draws wear — a rim of light, a dissolve, wrinkles by region — or null
|
|
4317
|
+
* for none. Pass state, as the material is: `bindMeshPass` puts it back to none. See
|
|
4318
|
+
* `surfaceOverlay.ts` for what it lays and `shaders/flat/overlay.ts` for how.
|
|
4319
|
+
*
|
|
4320
|
+
* **Refused, said once, where the lit stage has no room for it.** Fifteen vectors more than the
|
|
4321
|
+
* program this device fits: counted from the source as `fitFlatProgram` counts, and on a device
|
|
4322
|
+
* whose limit is the 256 an Adreno 740 reports, the eight-light build at 254 has none. The draws
|
|
4323
|
+
* then wear nothing, which is the picture a game without the feature draws. Its atlas is bound as
|
|
4324
|
+
* a 2D copy of its first layer, in the refraction copy's unit (`SurfaceTexture.flatLayer`); a
|
|
4325
|
+
* compressed atlas has no copy and is read as none, also said once.
|
|
4326
|
+
*/
|
|
4327
|
+
setSurfaceOverlay(overlay: SurfaceOverlay | null): void {
|
|
4328
|
+
if (this.contextLost) return;
|
|
4329
|
+
let wanted = overlay;
|
|
4330
|
+
if (overlayLays(wanted) && !this.overlayFits()) {
|
|
4331
|
+
if (!this.warnedOverlayRoom) {
|
|
4332
|
+
this.warnedOverlayRoom = true;
|
|
4333
|
+
console.warn(
|
|
4334
|
+
'Renderer: setSurfaceOverlay is refused on this device. The overlay takes fifteen ' +
|
|
4335
|
+
'fragment uniform vectors and the lit shader already uses all but ' +
|
|
4336
|
+
`${this.overlayRoomLeft()} of the ${this.fragmentVectorLimit()} this GPU offers; ` +
|
|
4337
|
+
'draws wear no overlay. A lower `maxLights` makes the room, or WebGPU has it.',
|
|
4338
|
+
);
|
|
4339
|
+
}
|
|
4340
|
+
wanted = null;
|
|
4341
|
+
}
|
|
4342
|
+
packSurfaceOverlay(wanted, this.overlayFloats);
|
|
4343
|
+
if (overlayLays(wanted)) this.askLit('SURFACE_OVERLAY');
|
|
4344
|
+
this.materials.dirty();
|
|
4345
|
+
this.writeFlatVector('uOverlay', this.overlayFloats);
|
|
4346
|
+
this.bindOverlayMaps((wanted?.maps ?? null) as SurfaceTexture | null);
|
|
4347
|
+
}
|
|
4348
|
+
|
|
4349
|
+
/** The overlay's atlas into the refraction copy's unit, as a 2D copy, or the empty texture. */
|
|
4350
|
+
private bindOverlayMaps(maps: SurfaceTexture | null): void {
|
|
4351
|
+
const { gl } = this;
|
|
4352
|
+
this.overlayMaps = maps;
|
|
4353
|
+
this.overlayTexture = maps === null ? null : maps.flatLayer(gl);
|
|
4354
|
+
if (maps !== null && this.overlayTexture === null && !this.warnedOverlayBlocks) {
|
|
4355
|
+
this.warnedOverlayBlocks = true;
|
|
4356
|
+
console.warn(
|
|
4357
|
+
"Renderer: a surface overlay's maps are a compressed texture, which WebGL2 cannot copy " +
|
|
4358
|
+
'into the 2D image the lit stage reads them from. The overlay is drawn without its ' +
|
|
4359
|
+
'images; upload the atlas from an image instead.',
|
|
4360
|
+
);
|
|
4361
|
+
}
|
|
4362
|
+
gl.activeTexture(gl.TEXTURE0 + REFRACT_SCENE_TEXTURE_UNIT);
|
|
4363
|
+
gl.bindTexture(gl.TEXTURE_2D, this.overlayTexture ?? this.emptyTexture2D);
|
|
4364
|
+
}
|
|
4365
|
+
|
|
4366
|
+
/** The most fragment uniform vectors this GPU offers a stage. */
|
|
4367
|
+
private fragmentVectorLimit(): number {
|
|
4368
|
+
return this.gl.getParameter(this.gl.MAX_FRAGMENT_UNIFORM_VECTORS) as number;
|
|
4369
|
+
}
|
|
4370
|
+
|
|
4371
|
+
/** What the lit shader at this renderer's budget leaves of the limit, with no overlay. */
|
|
4372
|
+
private overlayRoomLeft(): number {
|
|
4373
|
+
return (
|
|
4374
|
+
this.fragmentVectorLimit() - countUniformVectors(this.flatFragAt(this.lightBudget, false))
|
|
4375
|
+
);
|
|
4376
|
+
}
|
|
4377
|
+
|
|
4378
|
+
/** Whether the lit shader with the overlay compiled in fits this GPU: counted once. */
|
|
4379
|
+
private overlayFits(): boolean {
|
|
4380
|
+
this.overlayRoom ??=
|
|
4381
|
+
countUniformVectors(this.flatFragAt(this.lightBudget, true)) <= this.fragmentVectorLimit();
|
|
4382
|
+
return this.overlayRoom;
|
|
4383
|
+
}
|
|
4384
|
+
|
|
4123
4385
|
/**
|
|
4124
4386
|
* How bright the environment the surfaces drawn next reflect is. 1 is the default and identity.
|
|
4125
4387
|
*
|
|
@@ -4134,9 +4396,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4134
4396
|
*/
|
|
4135
4397
|
setEnvironmentGain(gain: number): void {
|
|
4136
4398
|
if (this.contextLost) return;
|
|
4137
|
-
this.
|
|
4138
|
-
this.
|
|
4139
|
-
this.writeFlatVector('uEnvironmentDials', this.environmentDials);
|
|
4399
|
+
this.passEnvironmentGain = Math.max(0, gain);
|
|
4400
|
+
this.writeEnvironmentDials();
|
|
4140
4401
|
}
|
|
4141
4402
|
|
|
4142
4403
|
/**
|
|
@@ -4419,8 +4680,104 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4419
4680
|
'locations the joint indices and weights occupy, and this mesh supplies them.',
|
|
4420
4681
|
);
|
|
4421
4682
|
}
|
|
4683
|
+
const animation = (options.animation ?? null) as GlBoneAnimation | null;
|
|
4684
|
+
/* The bone a vertex follows is its second coordinates' u, which a mesh carries in its grain
|
|
4685
|
+
lane; a mesh with none has nothing to name a bone by. See `boneAnimation.ts`. */
|
|
4686
|
+
if (animation !== null && !mesh.hasGrain) {
|
|
4687
|
+
throw new Error(
|
|
4688
|
+
'WebGL2: a bone animation reads each vertex’s bone from the mesh’s second coordinates ' +
|
|
4689
|
+
'(`MeshData.lightmapUvs`), and this mesh has none.',
|
|
4690
|
+
);
|
|
4691
|
+
}
|
|
4422
4692
|
this.ensureInstancedProgram();
|
|
4423
|
-
|
|
4693
|
+
if (animation !== null) this.ensureAnimatedProgram();
|
|
4694
|
+
return new InstancedBatch(this.gl, mesh, capacity, options.cull ?? false, animation);
|
|
4695
|
+
}
|
|
4696
|
+
|
|
4697
|
+
/**
|
|
4698
|
+
* A bone animation an instanced batch can play (`InstancedOptions.animation`): per frame, a turn
|
|
4699
|
+
* and a place for each bone, which every vertex of the batch's mesh follows — the bone its second
|
|
4700
|
+
* coordinates' u names, times `boneScale` — at each instance's own moment of the clip. What a
|
|
4701
|
+
* crowd is made of: every instance moves at the frame's rate on its own clock, in the colour pass
|
|
4702
|
+
* and in every shadow, with nothing on the processor after this. See `boneAnimation.ts` for the
|
|
4703
|
+
* clip's layout and the arithmetic, which both backends run.
|
|
4704
|
+
*
|
|
4705
|
+
* What it gives up against skinning is the blend: a vertex follows one bone. Refused, with what
|
|
4706
|
+
* was wrong, for a clip whose numbers are not its size, or past 2,048 bones or frames.
|
|
4707
|
+
*/
|
|
4708
|
+
createBoneAnimation(clip: BoneAnimationClip): BoneAnimationHandle {
|
|
4709
|
+
return new GlBoneAnimation(this.gl, packBoneAnimation(clip));
|
|
4710
|
+
}
|
|
4711
|
+
|
|
4712
|
+
/** Release a clip. A batch still playing it draws nothing, and casts nothing, after. */
|
|
4713
|
+
disposeBoneAnimation(animation: BoneAnimationHandle): void {
|
|
4714
|
+
const clip = animation as GlBoneAnimation;
|
|
4715
|
+
if (this.contextLost || clip.disposed) return;
|
|
4716
|
+
clip.dispose(this.gl);
|
|
4717
|
+
}
|
|
4718
|
+
|
|
4719
|
+
/**
|
|
4720
|
+
* The clock a bone animation plays by this frame, in seconds on the caller's clock: an instance's
|
|
4721
|
+
* moment is this times its rate plus its phase (`MeshInstances.clocks`). **Set it before the
|
|
4722
|
+
* shadows are drawn**, once a frame, as the wind is set: a shadow drawn under one clock and its
|
|
4723
|
+
* caster under another come apart at the frame's rate. 0 until set, which holds every crowd at
|
|
4724
|
+
* the moment its phases name.
|
|
4725
|
+
*/
|
|
4726
|
+
setAnimationTime(seconds: number): void {
|
|
4727
|
+
if (Number.isFinite(seconds)) this.animationTime = seconds;
|
|
4728
|
+
}
|
|
4729
|
+
|
|
4730
|
+
/** The clock `setAnimationTime` set. */
|
|
4731
|
+
private animationTime = 0;
|
|
4732
|
+
|
|
4733
|
+
/**
|
|
4734
|
+
* An animated batch's clip, clocks and numbers into whichever program is bound: the three vertex
|
|
4735
|
+
* textures on units of their own (`lightBudget.ts`), the clip's speed and bone scale, the clock.
|
|
4736
|
+
*/
|
|
4737
|
+
private bindAnimation(u: Record<string, WebGLUniformLocation>, batch: InstancedBatch): void {
|
|
4738
|
+
const clip = batch.animation;
|
|
4739
|
+
const clocks = batch.clocks;
|
|
4740
|
+
if (clip === null || clocks === null) return;
|
|
4741
|
+
const { gl } = this;
|
|
4742
|
+
gl.activeTexture(gl.TEXTURE0 + BONE_PLACES_TEXTURE_UNIT);
|
|
4743
|
+
gl.bindTexture(gl.TEXTURE_2D, clip.places);
|
|
4744
|
+
gl.activeTexture(gl.TEXTURE0 + BONE_TURNS_TEXTURE_UNIT);
|
|
4745
|
+
gl.bindTexture(gl.TEXTURE_2D, clip.turns);
|
|
4746
|
+
gl.activeTexture(gl.TEXTURE0 + INSTANCE_CLOCKS_TEXTURE_UNIT);
|
|
4747
|
+
gl.bindTexture(gl.TEXTURE_2D, clocks.texture);
|
|
4748
|
+
gl.uniform1i(u['uBonePlaces'] ?? null, BONE_PLACES_TEXTURE_UNIT);
|
|
4749
|
+
gl.uniform1i(u['uBoneTurns'] ?? null, BONE_TURNS_TEXTURE_UNIT);
|
|
4750
|
+
gl.uniform1i(u['uInstanceClocks'] ?? null, INSTANCE_CLOCKS_TEXTURE_UNIT);
|
|
4751
|
+
gl.uniform2f(u['uBoneClip'] ?? null, clip.framesPerSecond, clip.boneScale);
|
|
4752
|
+
gl.uniform1f(u['uSceneTime'] ?? null, this.animationTime);
|
|
4753
|
+
}
|
|
4754
|
+
|
|
4755
|
+
/** The animated instanced program, compiled on first use. See `ensureInstancedProgram`. */
|
|
4756
|
+
private flatAnimatedProgram: WebGLProgram | null = null;
|
|
4757
|
+
private flatAnimatedUniforms: Record<string, WebGLUniformLocation> | null = null;
|
|
4758
|
+
|
|
4759
|
+
/** Compile the animated variant, once, and feed it this frame's pass state. */
|
|
4760
|
+
private ensureAnimatedProgram(): void {
|
|
4761
|
+
if (this.contextLost || this.flatAnimatedProgram !== null) return;
|
|
4762
|
+
const { gl } = this;
|
|
4763
|
+
const program = compileProgram(
|
|
4764
|
+
gl,
|
|
4765
|
+
flatVert({ skinned: false, morphed: false, instanced: true, animated: true }),
|
|
4766
|
+
this.flatFragSource,
|
|
4767
|
+
'flat.animated',
|
|
4768
|
+
);
|
|
4769
|
+
const uniforms = uniformLocations(gl, program, 'flat.animated');
|
|
4770
|
+
this.flatAnimatedProgram = program;
|
|
4771
|
+
this.flatAnimatedUniforms = uniforms;
|
|
4772
|
+
this.flatTargets.push({ program, uniforms });
|
|
4773
|
+
const camera = this.lastPassCamera;
|
|
4774
|
+
const env = this.lastPassEnv;
|
|
4775
|
+
if (camera !== null && env !== null) {
|
|
4776
|
+
gl.useProgram(program);
|
|
4777
|
+
this.writeMeshPassState(uniforms, camera, env);
|
|
4778
|
+
this.writeMaterialState(uniforms, this.currentMaterial);
|
|
4779
|
+
this.useFlatProgram();
|
|
4780
|
+
}
|
|
4424
4781
|
}
|
|
4425
4782
|
|
|
4426
4783
|
/** Push placement and colour. Only the live prefix. */
|
|
@@ -4483,18 +4840,29 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4483
4840
|
order. See `InstancedOptions.cull`. */
|
|
4484
4841
|
const culling = !blend && batch.cull;
|
|
4485
4842
|
if (culling && !batchBoxVisible(batch.box, this.frustum, this.occlusion)) return;
|
|
4843
|
+
/* A batch playing a bone animation takes the animated variant; a disposed clip draws nothing. */
|
|
4844
|
+
const clip = batch.animation;
|
|
4845
|
+
if (clip !== null && clip.disposed) return;
|
|
4486
4846
|
this.drawBudget.ask();
|
|
4487
4847
|
this.ensureInstancedProgram();
|
|
4848
|
+
if (clip !== null) this.ensureAnimatedProgram();
|
|
4488
4849
|
/* A material with a model takes that model's instanced program; see `modelProgram`. */
|
|
4489
4850
|
const modelKind = this.currentMaterial?.model?.kind ?? null;
|
|
4490
|
-
const modelled =
|
|
4491
|
-
|
|
4492
|
-
|
|
4851
|
+
const modelled =
|
|
4852
|
+
modelKind === null
|
|
4853
|
+
? null
|
|
4854
|
+
: this.modelProgram(clip === null ? 'instanced' : 'animated', modelKind);
|
|
4855
|
+
const program =
|
|
4856
|
+
modelled?.program ?? (clip === null ? this.flatInstancedProgram : this.flatAnimatedProgram);
|
|
4857
|
+
const u =
|
|
4858
|
+
modelled?.uniforms ??
|
|
4859
|
+
(clip === null ? this.flatInstancedUniforms : this.flatAnimatedUniforms);
|
|
4493
4860
|
if (program === null || u === null) return;
|
|
4494
4861
|
|
|
4495
4862
|
const { gl } = this;
|
|
4496
4863
|
gl.useProgram(program);
|
|
4497
4864
|
gl.uniform1i(u['uHasTangents'] ?? null, mesh.hasTangents ? 1 : 0);
|
|
4865
|
+
if (clip !== null) this.bindAnimation(u, batch);
|
|
4498
4866
|
|
|
4499
4867
|
const lit = options.lit ?? true;
|
|
4500
4868
|
/* Added light fades in the medium rather than receding into it. See `drawFog.ts`. */
|
|
@@ -4555,8 +4923,16 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4555
4923
|
if (coverage) gl.enable(gl.SAMPLE_ALPHA_TO_COVERAGE);
|
|
4556
4924
|
if (ditheredBlend)
|
|
4557
4925
|
gl.uniform4fv(u['uCutout'] ?? null, this.cutoutUniform(this.currentMaterial, true));
|
|
4558
|
-
|
|
4559
|
-
|
|
4926
|
+
const keeps = !blend && this.reflectionKeeps();
|
|
4927
|
+
if (keeps) this.reflectionTargets?.on();
|
|
4928
|
+
/* An animated batch reads its clocks by instance index, which the culled copy would reorder:
|
|
4929
|
+
it is culled whole and never instance by instance. */
|
|
4930
|
+
if (culling && clip === null && cullsInstances(count, mesh.indexCount)) {
|
|
4931
|
+
batch.drawCulled(gl, data, this.frustum);
|
|
4932
|
+
} else {
|
|
4933
|
+
batch.draw(gl, count);
|
|
4934
|
+
}
|
|
4935
|
+
if (keeps) this.reflectionTargets?.off();
|
|
4560
4936
|
if (coverage) gl.disable(gl.SAMPLE_ALPHA_TO_COVERAGE);
|
|
4561
4937
|
if (ditheredBlend)
|
|
4562
4938
|
gl.uniform4fv(u['uCutout'] ?? null, this.cutoutUniform(this.currentMaterial, false));
|
|
@@ -4617,7 +4993,9 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4617
4993
|
glass.transmission,
|
|
4618
4994
|
glass.frost,
|
|
4619
4995
|
);
|
|
4620
|
-
|
|
4996
|
+
/* w: this framebuffer's first row is the bottom, so up the screen is up the rows. */
|
|
4997
|
+
const tint = options.refractTint ?? WHITE_TINT;
|
|
4998
|
+
gl.uniform4f(u['uRefractTint'] ?? null, tint[0] ?? 1, tint[1] ?? 1, tint[2] ?? 1, 1);
|
|
4621
4999
|
gl.uniform3fv(u['uGlassTint'] ?? null, glass.tint);
|
|
4622
5000
|
return true;
|
|
4623
5001
|
}
|
|
@@ -4627,7 +5005,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4627
5005
|
const { gl } = this;
|
|
4628
5006
|
gl.uniform4f(u['uSeeThrough'] ?? null, 0, 0, 0, 0);
|
|
4629
5007
|
gl.activeTexture(gl.TEXTURE0 + REFRACT_SCENE_TEXTURE_UNIT);
|
|
4630
|
-
|
|
5008
|
+
/* The overlay's atlas, where one is held: it shares the unit. See `setSurfaceOverlay`. */
|
|
5009
|
+
gl.bindTexture(gl.TEXTURE_2D, this.overlayTexture ?? this.emptyTexture2D);
|
|
4631
5010
|
}
|
|
4632
5011
|
|
|
4633
5012
|
disposeMesh(mesh: Mesh): void {
|
|
@@ -4872,7 +5251,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4872
5251
|
*/
|
|
4873
5252
|
private gradeCode(): number {
|
|
4874
5253
|
return this.sceneTarget === null || !this.quality.hdrScene
|
|
4875
|
-
?
|
|
5254
|
+
? forwardTransformCode(this.quality.outputTransform)
|
|
4876
5255
|
: 0;
|
|
4877
5256
|
}
|
|
4878
5257
|
|
|
@@ -5927,6 +6306,71 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
5927
6306
|
casters(this.casterSink);
|
|
5928
6307
|
}
|
|
5929
6308
|
|
|
6309
|
+
/**
|
|
6310
|
+
* Draws that do not change between frames, enumerated once and kept: what a stage, a set or any
|
|
6311
|
+
* world that stands still draws, recorded so a frame replays it rather than issuing it.
|
|
6312
|
+
*
|
|
6313
|
+
* **The same enumeration `drawSceneCasters` takes**, called once, here: rigid meshes and instanced
|
|
6314
|
+
* batches, each with its material. A skinned mesh or a scatter batch is refused by name, since
|
|
6315
|
+
* both change every frame. What it captures is what the enumeration said at the time — the
|
|
6316
|
+
* matrices and the instance counts are copied, so a caller's scratch is free to move on — while
|
|
6317
|
+
* the meshes, batches, textures and materials stay the caller's: a list must be disposed, or made
|
|
6318
|
+
* again, before anything it holds is, and made again when a material it holds is changed.
|
|
6319
|
+
*
|
|
6320
|
+
* **What it is for is the frame's processor time.** WebGPU records the list into a render bundle
|
|
6321
|
+
* per view and replays it with one call, where a draw issued a frame at a time costs its encoding
|
|
6322
|
+
* every frame and again for every view that draws it. This backend has no bundles and replays the
|
|
6323
|
+
* entries through the scene-caster path every frame, at the cost a frame drawing them itself pays;
|
|
6324
|
+
* the picture is the same.
|
|
6325
|
+
*
|
|
6326
|
+
* What a list gives up against drawing each frame is culling: every entry is drawn in every view,
|
|
6327
|
+
* because a bundle cannot leave one out. A world split into lists by region culls by list.
|
|
6328
|
+
*/
|
|
6329
|
+
createStaticDraws(casters: ShadowCasters): StaticDrawsHandle {
|
|
6330
|
+
return captureStaticDraws(casters);
|
|
6331
|
+
}
|
|
6332
|
+
|
|
6333
|
+
/**
|
|
6334
|
+
* Draw a list into the open mesh pass, under the camera, the lights and the fog `bindMeshPass`
|
|
6335
|
+
* set — the frame's, a mirror's, a capture's. Opaque draws, through each entry's material.
|
|
6336
|
+
*
|
|
6337
|
+
* **Its draws are rigid and wear no overlay**, so a skin palette, morph weights, cloth or a surface
|
|
6338
|
+
* overlay set before it is put back to none, as `bindMeshPass` puts an overlay back; and no
|
|
6339
|
+
* material is left set after it, which is what `setMaterial(null)` leaves. The state a pass set —
|
|
6340
|
+
* the grain, the relief, the fog, the grade — is the list's to draw with, as it is any draw's, and
|
|
6341
|
+
* a list drawn under a different one is recorded again for it on WebGPU.
|
|
6342
|
+
*/
|
|
6343
|
+
drawStaticDraws(draws: StaticDrawsHandle): void {
|
|
6344
|
+
if (this.contextLost) return;
|
|
6345
|
+
const list = draws as StaticDrawsList;
|
|
6346
|
+
if (list.disposed) {
|
|
6347
|
+
this.warnStaticDisposed();
|
|
6348
|
+
return;
|
|
6349
|
+
}
|
|
6350
|
+
this.setSkinPalette(null);
|
|
6351
|
+
this.setMorphWeights(null);
|
|
6352
|
+
this.setCloth(null);
|
|
6353
|
+
this.setSurfaceOverlay(null);
|
|
6354
|
+
this.setMaterial(null);
|
|
6355
|
+
this.sceneCasterMaterial = undefined;
|
|
6356
|
+
replayStaticDraws(list, this.sceneCasterSink);
|
|
6357
|
+
this.setMaterial(null);
|
|
6358
|
+
this.sceneCasterMaterial = undefined;
|
|
6359
|
+
}
|
|
6360
|
+
|
|
6361
|
+
/** Release a list. Its meshes, batches and materials are the caller's and are not released. */
|
|
6362
|
+
disposeStaticDraws(draws: StaticDrawsHandle): void {
|
|
6363
|
+
(draws as StaticDrawsList).disposed = true;
|
|
6364
|
+
}
|
|
6365
|
+
|
|
6366
|
+
/** A disposed list drawn, said once: the frame loop does not throw. */
|
|
6367
|
+
private warnStaticDisposed(): void {
|
|
6368
|
+
if (this.warnedStaticDisposed) return;
|
|
6369
|
+
this.warnedStaticDisposed = true;
|
|
6370
|
+
console.warn('drawStaticDraws: this list was disposed, and draws nothing.');
|
|
6371
|
+
}
|
|
6372
|
+
private warnedStaticDisposed = false;
|
|
6373
|
+
|
|
5930
6374
|
/**
|
|
5931
6375
|
* Draw the pass's glass twice: where the nearest pane is, and what the panes let through.
|
|
5932
6376
|
*
|
|
@@ -6116,7 +6560,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
6116
6560
|
* throws a sentence with the device's numbers in it rather than a bare driver string.
|
|
6117
6561
|
*/
|
|
6118
6562
|
/** The lit fragment source this renderer's profile compiles, at a light budget. */
|
|
6119
|
-
private flatFragAt(budget: LightBudget): string {
|
|
6563
|
+
private flatFragAt(budget: LightBudget, overlay = this.litOn.SURFACE_OVERLAY): string {
|
|
6120
6564
|
return flatFrag({
|
|
6121
6565
|
pointShadows: this.quality.pointShadows,
|
|
6122
6566
|
directionalShadows: this.quality.directionalShadows,
|
|
@@ -6129,6 +6573,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
6129
6573
|
surfaceEffects: this.litOn.SURFACE_EFFECTS,
|
|
6130
6574
|
driftLight: this.litOn.DRIFT_LIGHT,
|
|
6131
6575
|
physicalSpecular: this.litOn.PHYSICAL_SPECULAR,
|
|
6576
|
+
surfaceOverlay: overlay,
|
|
6577
|
+
reflectionMaps: this.litOn.REFLECTION_MAPS,
|
|
6132
6578
|
maxLights: budget.maxLights,
|
|
6133
6579
|
maxAreaLights: budget.maxAreaLights,
|
|
6134
6580
|
});
|
|
@@ -6153,9 +6599,10 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
6153
6599
|
instanced: boolean,
|
|
6154
6600
|
label: string,
|
|
6155
6601
|
eight = false,
|
|
6602
|
+
animated = false,
|
|
6156
6603
|
): { program: WebGLProgram; uniforms: Record<string, WebGLUniformLocation> } | null => {
|
|
6157
6604
|
if (old === null) return null;
|
|
6158
|
-
const vertex = flatVert({ skinned, morphed, instanced });
|
|
6605
|
+
const vertex = flatVert({ skinned, morphed, instanced, animated });
|
|
6159
6606
|
const program = compileProgram(
|
|
6160
6607
|
gl,
|
|
6161
6608
|
eight ? skinEight(vertex) : vertex,
|
|
@@ -6188,6 +6635,17 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
6188
6635
|
const instanced = recompile(this.flatInstancedProgram, false, false, true, 'flat.instanced');
|
|
6189
6636
|
this.flatInstancedProgram = instanced?.program ?? null;
|
|
6190
6637
|
this.flatInstancedUniforms = instanced?.uniforms ?? null;
|
|
6638
|
+
const animated = recompile(
|
|
6639
|
+
this.flatAnimatedProgram,
|
|
6640
|
+
false,
|
|
6641
|
+
false,
|
|
6642
|
+
true,
|
|
6643
|
+
'flat.animated',
|
|
6644
|
+
false,
|
|
6645
|
+
true,
|
|
6646
|
+
);
|
|
6647
|
+
this.flatAnimatedProgram = animated?.program ?? null;
|
|
6648
|
+
this.flatAnimatedUniforms = animated?.uniforms ?? null;
|
|
6191
6649
|
|
|
6192
6650
|
for (const [key, entry] of this.clothFlat) {
|
|
6193
6651
|
const eight = key.startsWith('8');
|
|
@@ -6218,7 +6676,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
6218
6676
|
}
|
|
6219
6677
|
|
|
6220
6678
|
this.flatTargets.length = 0;
|
|
6221
|
-
for (const target of [plain, skinned, morphed, both, skinned8, both8, instanced]) {
|
|
6679
|
+
for (const target of [plain, skinned, morphed, both, skinned8, both8, instanced, animated]) {
|
|
6222
6680
|
if (target !== null) this.flatTargets.push(target);
|
|
6223
6681
|
}
|
|
6224
6682
|
for (const target of this.clothFlat.values()) this.flatTargets.push(target);
|
|
@@ -6954,11 +7412,22 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
6954
7412
|
* Set before `beginFrame` and held until changed, like `setSpeedRush`, which it sits
|
|
6955
7413
|
* beside for the same reason: both describe how the finished image is presented rather
|
|
6956
7414
|
* than anything in the world.
|
|
7415
|
+
*
|
|
7416
|
+
* `maxShare` is the longest smear, as a share of the frame (0.03 unless given, at most half): a
|
|
7417
|
+
* fast turn softens the frame rather than erasing it. **Each drawn object's own motion is
|
|
7418
|
+
* followed only under a reconstruction on WebGPU**, which draws the motion target it reads; here,
|
|
7419
|
+
* and on WebGPU without one, the camera's motion alone smears the frame, and a draw that names
|
|
7420
|
+
* where it was last frame is told so once.
|
|
6957
7421
|
*/
|
|
6958
|
-
setCameraMotionBlur(scale: number): void {
|
|
7422
|
+
setCameraMotionBlur(scale: number, maxShare = MOTION_BLUR_MAX_UV): void {
|
|
6959
7423
|
this.motionBlurScale = Math.min(Math.max(scale, 0), 1);
|
|
7424
|
+
this.motionBlurMax = clampMotionShare(maxShare);
|
|
6960
7425
|
}
|
|
6961
7426
|
|
|
7427
|
+
/** The longest smear, as a share of the frame. See `setCameraMotionBlur`. */
|
|
7428
|
+
private motionBlurMax = MOTION_BLUR_MAX_UV;
|
|
7429
|
+
private warnedObjectBlur = false;
|
|
7430
|
+
|
|
6962
7431
|
/**
|
|
6963
7432
|
* The camera has cut: this frame is a new shot, not the next moment of the last one.
|
|
6964
7433
|
*
|
|
@@ -7170,6 +7639,47 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
7170
7639
|
: this.quality.outputExposure;
|
|
7171
7640
|
}
|
|
7172
7641
|
|
|
7642
|
+
/**
|
|
7643
|
+
* The `filmic` curve this and every later frame is graded through, until it is set again: a
|
|
7644
|
+
* grading volume's five numbers, solved once here into the constants the resolve reads. Ignored
|
|
7645
|
+
* unless `outputTransform` is `filmic`. A number out of range is clamped into it and said once,
|
|
7646
|
+
* since a frame loop must not throw. See `filmicCurve.ts`.
|
|
7647
|
+
*/
|
|
7648
|
+
setFilmicCurve(curve: FilmicCurve): void {
|
|
7649
|
+
if (resolveFilmicCurve(curve, this.filmConstants) && !this.warnedFilmClamped) {
|
|
7650
|
+
this.warnedFilmClamped = true;
|
|
7651
|
+
console.warn(FILMIC_CLAMPED);
|
|
7652
|
+
}
|
|
7653
|
+
}
|
|
7654
|
+
|
|
7655
|
+
private warnedFilmClamped = false;
|
|
7656
|
+
|
|
7657
|
+
/**
|
|
7658
|
+
* The range the frame goes out in: `'high'` where the profile asked for `highDynamicRange`, the
|
|
7659
|
+
* frame holds light past white and the display shows it — on WebGPU — and `'standard'` otherwise.
|
|
7660
|
+
* **Always standard here**: this backend draws into a canvas of eight bits a channel, which has
|
|
7661
|
+
* nothing above white to show. Typed as the question rather than this backend's answer, because
|
|
7662
|
+
* the shared surface is derived from this class.
|
|
7663
|
+
*/
|
|
7664
|
+
readonly displayRange: DisplayRange = 'standard';
|
|
7665
|
+
|
|
7666
|
+
/** Why it is that range, in words, for a game choosing its look and for a bug report. */
|
|
7667
|
+
get displayRangeReason(): string {
|
|
7668
|
+
return this.quality.highDynamicRange
|
|
7669
|
+
? 'WebGL2 draws into an eight-bit canvas, which has no range above white'
|
|
7670
|
+
: 'highDynamicRange was not asked for';
|
|
7671
|
+
}
|
|
7672
|
+
|
|
7673
|
+
/**
|
|
7674
|
+
* How bright paper white and the display's peak are, in one unit, held until changed: their
|
|
7675
|
+
* ratio is how far above white the highlights may run where `displayRange` is `'high'`, and
|
|
7676
|
+
* nothing changes where it is standard, which is always here.
|
|
7677
|
+
*/
|
|
7678
|
+
setDisplayLuminance(paperWhite: number, peak: number): void {
|
|
7679
|
+
void paperWhite;
|
|
7680
|
+
void peak;
|
|
7681
|
+
}
|
|
7682
|
+
|
|
7173
7683
|
/**
|
|
7174
7684
|
* Composite a flat colour over the finished frame — for a cut dipping to white or to black.
|
|
7175
7685
|
*
|
|
@@ -7261,6 +7771,21 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
7261
7771
|
this.sceneTarget.setVignette(strength);
|
|
7262
7772
|
}
|
|
7263
7773
|
|
|
7774
|
+
/**
|
|
7775
|
+
* The lens's colour fringe, held until changed: `intensity` is a percentage, 0 is none, and red
|
|
7776
|
+
* and green are pulled toward the centre by their wavelength's distance from blue, growing from
|
|
7777
|
+
* `start` — a share of the half-frame, 0 the centre — to the whole of it at the edge. Applied to
|
|
7778
|
+
* scene light at the composite's first read. Needs `screenEffects`, and says so once rather than
|
|
7779
|
+
* doing nothing. See `shaders/fringe.ts`.
|
|
7780
|
+
*/
|
|
7781
|
+
setChromaticAberration(intensity: number, start = 0): void {
|
|
7782
|
+
if (this.sceneTarget === null) {
|
|
7783
|
+
this.warnLookWithoutComposite(intensity);
|
|
7784
|
+
return;
|
|
7785
|
+
}
|
|
7786
|
+
this.sceneTarget.setChromaticAberration(intensity, start);
|
|
7787
|
+
}
|
|
7788
|
+
|
|
7264
7789
|
/**
|
|
7265
7790
|
* Film grain, held until changed: an amplitude in display values (0 is none, 0.03 is a fine
|
|
7266
7791
|
* grain) and this frame's seed. **The seed is the caller's**, because the engine takes time from
|
|
@@ -7451,6 +7976,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
7451
7976
|
|
|
7452
7977
|
beginFrame(clearColor: Vec3): void {
|
|
7453
7978
|
if (this.contextLost) return;
|
|
7979
|
+
/* One frame of the jitter's sequence however many times the frame binds its camera. */
|
|
7980
|
+
this.temporalHistory.nextFrame();
|
|
7454
7981
|
this.skinScattered = false;
|
|
7455
7982
|
this.skinScatter?.reset();
|
|
7456
7983
|
if (this.litAsked) {
|
|
@@ -7520,6 +8047,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
7520
8047
|
}
|
|
7521
8048
|
|
|
7522
8049
|
this.sceneTarget?.begin(this.canvas.width, this.canvas.height);
|
|
8050
|
+
/* The materials' maps on the scene's framebuffer, cleared and disabled: `reflectionKeeps`. */
|
|
8051
|
+
this.reflectionTargets?.begin(this.canvas.width, this.canvas.height);
|
|
7523
8052
|
gl.clearColor(clearColor[0], clearColor[1], clearColor[2], 1);
|
|
7524
8053
|
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
|
7525
8054
|
}
|
|
@@ -7576,7 +8105,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
7576
8105
|
motion = {
|
|
7577
8106
|
reprojection: this.reprojection as Float32Array,
|
|
7578
8107
|
strength: blur,
|
|
7579
|
-
max:
|
|
8108
|
+
max: this.motionBlurMax,
|
|
7580
8109
|
};
|
|
7581
8110
|
}
|
|
7582
8111
|
}
|
|
@@ -7668,7 +8197,11 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
7668
8197
|
|
|
7669
8198
|
/* The grade travels to the resolve only where the mesh pass gave it up. See `bindMeshPass`. */
|
|
7670
8199
|
const lateGrade = this.quality.hdrScene
|
|
7671
|
-
? {
|
|
8200
|
+
? {
|
|
8201
|
+
transform: OUTPUT_TRANSFORM_CODE[this.quality.outputTransform] ?? 0,
|
|
8202
|
+
exposure: this.exposure,
|
|
8203
|
+
film: this.filmConstants,
|
|
8204
|
+
}
|
|
7672
8205
|
: { transform: 0, exposure: 1 };
|
|
7673
8206
|
/*
|
|
7674
8207
|
* Depth of field, on the same ceiling-and-dial split as bloom above: `depthOfField` decides
|
|
@@ -7744,6 +8277,49 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
7744
8277
|
* drawn over a reflective floor rather than under it, and before the post chain so bloom and
|
|
7745
8278
|
* the grade see a reflection exactly as they would see any other part of the picture.
|
|
7746
8279
|
*/
|
|
8280
|
+
/*
|
|
8281
|
+
* **The frame's materials' reflections first**, on the same terms: every pixel the opaque lit
|
|
8282
|
+
* draws left a reflection for, marched and swapped for the probe's share. The maps leave the
|
|
8283
|
+
* scene's framebuffer before the resolve draws into it while sampling them.
|
|
8284
|
+
*/
|
|
8285
|
+
const reflectionTargets = this.reflectionTargets;
|
|
8286
|
+
const probeMap = reflectionTargets?.probeTexture ?? null;
|
|
8287
|
+
const tintMap = reflectionTargets?.tintTexture ?? null;
|
|
8288
|
+
if (
|
|
8289
|
+
reflectionTargets !== null &&
|
|
8290
|
+
this.frameReflections !== null &&
|
|
8291
|
+
probeMap !== null &&
|
|
8292
|
+
tintMap !== null &&
|
|
8293
|
+
this.frameViewProj !== null &&
|
|
8294
|
+
this.sceneTarget !== null
|
|
8295
|
+
) {
|
|
8296
|
+
const scene = this.sceneTarget.colorAttachment();
|
|
8297
|
+
const depth = this.sceneTarget.snapshotDepth(true);
|
|
8298
|
+
if (scene !== null && depth !== null) {
|
|
8299
|
+
this.ssr ??= new SsrPass(this.gl, this.sceneTarget.keepsRange);
|
|
8300
|
+
this.ssr.traceMaterials(
|
|
8301
|
+
scene,
|
|
8302
|
+
depth,
|
|
8303
|
+
probeMap,
|
|
8304
|
+
tintMap,
|
|
8305
|
+
this.frameViewProj,
|
|
8306
|
+
this.frameEye,
|
|
8307
|
+
this.canvas.width,
|
|
8308
|
+
this.canvas.height,
|
|
8309
|
+
REFLECTION_EDGE_FADE,
|
|
8310
|
+
this.frameReflections,
|
|
8311
|
+
);
|
|
8312
|
+
this.sceneTarget.bind();
|
|
8313
|
+
reflectionTargets.detach();
|
|
8314
|
+
this.ssr.resolveMaterials(
|
|
8315
|
+
probeMap,
|
|
8316
|
+
tintMap,
|
|
8317
|
+
this.canvas.width,
|
|
8318
|
+
this.canvas.height,
|
|
8319
|
+
this.frameReflections,
|
|
8320
|
+
);
|
|
8321
|
+
}
|
|
8322
|
+
}
|
|
7747
8323
|
if (this.reflectionQueue.length > 0 && this.frameViewProj !== null) {
|
|
7748
8324
|
const scene = this.sceneTarget?.colorAttachment() ?? null;
|
|
7749
8325
|
const depth = this.sceneTarget?.snapshotDepth(true) ?? null;
|
|
@@ -8127,9 +8703,17 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
8127
8703
|
this.materialHasMaps = false;
|
|
8128
8704
|
this.currentMaterial = null;
|
|
8129
8705
|
this.currentSurfaceTexture = null;
|
|
8706
|
+
/* Nor an overlay: the atlas's unit is given the empty texture with the refraction copy's. */
|
|
8707
|
+
this.overlayFloats.fill(0);
|
|
8708
|
+
this.overlayMaps = null;
|
|
8709
|
+
this.overlayTexture = null;
|
|
8130
8710
|
/* Nor an ambient of its own: the frame's, until a draw asks. See `setAmbientSH`. */
|
|
8131
8711
|
this.ambientSH.fill(0);
|
|
8132
8712
|
/* No reflection and an environment at its own brightness; the emission the world asks for. */
|
|
8713
|
+
this.passReflectivity = 0;
|
|
8714
|
+
this.passEnvironmentGain = 1;
|
|
8715
|
+
this.materialReflectivity = null;
|
|
8716
|
+
this.materialEnvironmentGain = null;
|
|
8133
8717
|
this.environmentDials[1] = 0;
|
|
8134
8718
|
this.environmentDials[2] = 1;
|
|
8135
8719
|
this.emission[0] = env.emissiveGain;
|
|
@@ -8283,12 +8867,14 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
8283
8867
|
* unless a draw sets the strength above zero.
|
|
8284
8868
|
*/
|
|
8285
8869
|
gl.activeTexture(gl.TEXTURE0 + REFRACT_SCENE_TEXTURE_UNIT);
|
|
8286
|
-
gl.bindTexture(gl.TEXTURE_2D, this.emptyTexture2D);
|
|
8870
|
+
gl.bindTexture(gl.TEXTURE_2D, this.overlayTexture ?? this.emptyTexture2D);
|
|
8287
8871
|
gl.uniform1i(u['uRefractScene'] ?? null, REFRACT_SCENE_TEXTURE_UNIT);
|
|
8872
|
+
/* The draws' overlay, none at a pass's start, and its atlas above: see `setSurfaceOverlay`. */
|
|
8873
|
+
gl.uniform4fv(u['uOverlay'] ?? null, this.overlayFloats);
|
|
8288
8874
|
/* Neither refracting nor glass, which is every draw that does not say otherwise. See
|
|
8289
8875
|
`bindSeeThrough` for the four numbers in the one vector. */
|
|
8290
8876
|
gl.uniform4f(u['uSeeThrough'] ?? null, 0, 0, 0, 0);
|
|
8291
|
-
gl.
|
|
8877
|
+
gl.uniform4f(u['uRefractTint'] ?? null, 1, 1, 1, 1);
|
|
8292
8878
|
gl.uniform3fv(u['uGlassTint'] ?? null, WHITE_TINT);
|
|
8293
8879
|
gl.uniform3fv(u['uDirectionalDir'] ?? null, env.directionalDir);
|
|
8294
8880
|
gl.uniform3fv(u['uDirectionalColor'] ?? null, env.directionalColor);
|
|
@@ -8411,7 +8997,16 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
8411
8997
|
gl.uniform1i(u['uPeeledShadowEnabled'] ?? null, this.peelFilled ? 1 : 0);
|
|
8412
8998
|
gl.uniform1f(u['uShadowMaxSlope'] ?? null, this.quality.directionalShadowMaxSlope);
|
|
8413
8999
|
gl.uniform1i(u['uShadowFilterTaps'] ?? null, this.quality.shadowFilterTaps);
|
|
8414
|
-
|
|
9000
|
+
/* The near corner, and in its w the frame the cutout dither moves with. See `uHighlightMin`.
|
|
9001
|
+
An environment written by hand may leave the box out, as the far corner says below. */
|
|
9002
|
+
const min = env.highlightMin as ArrayLike<number> | undefined;
|
|
9003
|
+
gl.uniform4f(
|
|
9004
|
+
u['uHighlightMin'] ?? null,
|
|
9005
|
+
min?.[0] ?? 0,
|
|
9006
|
+
min?.[1] ?? 0,
|
|
9007
|
+
min?.[2] ?? 0,
|
|
9008
|
+
this.cutoutFrame,
|
|
9009
|
+
);
|
|
8415
9010
|
/* The far corner, and the gain in its w. See `uHighlightMax`. */
|
|
8416
9011
|
/* An environment written by hand may leave the box out; then there is none, as before. */
|
|
8417
9012
|
const max = env.highlightMax ?? NO_HIGHLIGHT;
|
|
@@ -8693,9 +9288,18 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
8693
9288
|
* here, so there is no motion target for this to be written into, and a mover passed here is
|
|
8694
9289
|
* neither read nor advanced.
|
|
8695
9290
|
*/
|
|
8696
|
-
|
|
9291
|
+
previousModel: ReadonlyMat4 | Mover | null = null,
|
|
8697
9292
|
): void {
|
|
8698
9293
|
if (this.contextLost) return;
|
|
9294
|
+
/* A mover's own motion is not blurred here: see `setCameraMotionBlur`. Said once. */
|
|
9295
|
+
if (
|
|
9296
|
+
previousModel !== null &&
|
|
9297
|
+
!this.warnedObjectBlur &&
|
|
9298
|
+
this.quality.cameraMotionBlur * this.motionBlurScale > 0
|
|
9299
|
+
) {
|
|
9300
|
+
this.warnedObjectBlur = true;
|
|
9301
|
+
console.warn(OBJECT_BLUR_UNKEPT);
|
|
9302
|
+
}
|
|
8699
9303
|
/* Geometry that has not all arrived is not drawn, nor a mesh with none. See `Mesh.complete`
|
|
8700
9304
|
and `Mesh.indexCount`. */
|
|
8701
9305
|
if (!mesh.complete || mesh.indexCount === 0) return;
|
|
@@ -8726,7 +9330,11 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
8726
9330
|
* light and its colour, each into the blur's own target.
|
|
8727
9331
|
*/
|
|
8728
9332
|
const splits = this.skinSplits();
|
|
9333
|
+
/* An opaque lit draw into the frame writes its material's reflection too: see `reflectionKeeps`. */
|
|
9334
|
+
const keeps = this.reflectionKeeps();
|
|
9335
|
+
if (keeps) this.reflectionTargets?.on();
|
|
8729
9336
|
this.drawMeshHalf(mesh, model, depthLayer, tint, splits ? 'scene' : 'whole');
|
|
9337
|
+
if (keeps) this.reflectionTargets?.off();
|
|
8730
9338
|
if (splits && this.skinScatter !== null) {
|
|
8731
9339
|
this.skinScatter.begin();
|
|
8732
9340
|
this.drawMeshHalf(mesh, model, depthLayer, tint, 'diffuse');
|
|
@@ -8741,6 +9349,21 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
8741
9349
|
* skin yet, this is the frame's own picture — not a mirror's or a probe's, nor an overlay after
|
|
8742
9350
|
* the frame — and the blur's target can be had.
|
|
8743
9351
|
*/
|
|
9352
|
+
/**
|
|
9353
|
+
* Whether a draw now is the frame's own, opaque and lit, and so writes the two maps beside its
|
|
9354
|
+
* colour: not a mirror's, a probe's or a capture's, whose framebuffers have no maps, and not after
|
|
9355
|
+
* the frame. See `reflectionSurfaceTargets.ts`.
|
|
9356
|
+
*/
|
|
9357
|
+
private reflectionKeeps(): boolean {
|
|
9358
|
+
return (
|
|
9359
|
+
this.reflectionTargets !== null &&
|
|
9360
|
+
!this.reflectionPassActive &&
|
|
9361
|
+
!this.probePassActive &&
|
|
9362
|
+
!this.capturePassActive &&
|
|
9363
|
+
!this.framePresented
|
|
9364
|
+
);
|
|
9365
|
+
}
|
|
9366
|
+
|
|
8744
9367
|
private skinSplits(): boolean {
|
|
8745
9368
|
const skin = this.skinScatter;
|
|
8746
9369
|
const scene = this.sceneTarget;
|
|
@@ -8769,10 +9392,16 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
8769
9392
|
const scene = this.sceneTarget;
|
|
8770
9393
|
const projection = this.frameProjection;
|
|
8771
9394
|
if (skin === null || scene === null || this.skinScattered) return;
|
|
9395
|
+
/*
|
|
9396
|
+
* **Not the frame's picture, so not the frame's moment**: a blended draw in a mirror, a probe
|
|
9397
|
+
* bake or a scene capture lands over none of the frame's skin. Latching there switched the
|
|
9398
|
+
* spread off for the frame, so a capture with a glowing sign in it left every face drawn after
|
|
9399
|
+
* it flat. `scripts/capture-check.mjs`.
|
|
9400
|
+
*/
|
|
9401
|
+
if (this.reflectionPassActive || this.probePassActive || this.capturePassActive) return;
|
|
8772
9402
|
/* Latched whether or not skin was drawn: a skin drawn after this point is the whole surface. */
|
|
8773
9403
|
this.skinScattered = true;
|
|
8774
9404
|
if (!skin.pending) return;
|
|
8775
|
-
if (this.reflectionPassActive || this.probePassActive || this.capturePassActive) return;
|
|
8776
9405
|
if (projection === null) return;
|
|
8777
9406
|
const depth = scene.snapshotDepth(true);
|
|
8778
9407
|
mat4.invert(this.skinInverseProjection, projection);
|
|
@@ -9839,6 +10468,12 @@ function litVertexOf(variant: string): string {
|
|
|
9839
10468
|
}
|
|
9840
10469
|
const skinned = variant.startsWith('skinned') || variant.startsWith('both');
|
|
9841
10470
|
const morphed = variant === 'morphed' || variant.startsWith('both');
|
|
9842
|
-
const
|
|
10471
|
+
const animated = variant === 'animated';
|
|
10472
|
+
const vertex = flatVert({
|
|
10473
|
+
skinned,
|
|
10474
|
+
morphed,
|
|
10475
|
+
instanced: variant === 'instanced' || animated,
|
|
10476
|
+
animated,
|
|
10477
|
+
});
|
|
9843
10478
|
return variant.endsWith('8') ? skinEight(vertex) : vertex;
|
|
9844
10479
|
}
|