@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
|
@@ -4,11 +4,17 @@ import { FOG_OFF, FOG_RECEDE, fogModeOf } from '../drawFog.js';
|
|
|
4
4
|
import { blendCovering, blendKeeping } from '../../sceneCoverage.js';
|
|
5
5
|
import { mat4 } from 'gl-matrix';
|
|
6
6
|
import { GL_DEPTH_REMAP, MAX_DEPTH_LAYER, depthClearFor, depthOffsetForLayer, GL_SHADOW_REMAP, REVERSED_DEPTH, SHADOW_DEPTH_CLEAR, glDepthFuncEqual, glShadowDepthFunc, } from '../../depthConvention.js';
|
|
7
|
+
import { maskOf } from '../../frame/resources.js';
|
|
8
|
+
/** The two copies a registered pass may declare it reads. See `drawPass`. */
|
|
9
|
+
const COLOR_SNAPSHOT = maskOf('colorSnapshot');
|
|
10
|
+
const DEPTH_SNAPSHOT = maskOf('depthSnapshot');
|
|
7
11
|
import { resolveGlass } from '../../glass.js';
|
|
8
12
|
import { PickableSet } from '../../pickable.js';
|
|
9
13
|
import { TEMPORAL_HISTORY_BLEND, TemporalHistory, jitterOffset, jitterProjection, } from '../../temporalAa.js';
|
|
10
14
|
import { DecalPass } from '../../decalPass.js';
|
|
11
15
|
import { SsrPass } from '../../ssrPass.js';
|
|
16
|
+
import { ReflectionSurfaceTargets } from '../../reflectionSurfaceTargets.js';
|
|
17
|
+
import { frameReflectionsRefused, resolveFrameReflections, } from '../../frameReflections.js';
|
|
12
18
|
import { GlobalMediumPass } from '../../globalMediumPass.js';
|
|
13
19
|
import { DEFAULT_GLOBAL_MEDIUM, WEAK_GPU_MEDIUM_STEPS, mediumActive, resolveGlobalMedium, } from '../../globalMedium.js';
|
|
14
20
|
import { REFLECTION_EDGE_FADE, ReflectionQueue } from '../../screenSpaceReflection.js';
|
|
@@ -28,7 +34,8 @@ import { attachContextLoss } from '../../contextLoss.js';
|
|
|
28
34
|
import { isWeakGpuFamily, webgl2RendererName } from '../../gpuCapability.js';
|
|
29
35
|
import { GpuTimer } from '../../gpuTimer.js';
|
|
30
36
|
import { compileProgram, uniformLocations } from '../../shader.js';
|
|
31
|
-
import { FULL_LIGHT_BUDGET, nextLightBudget, planLightBudget, } from '../../uniformVectorBudget.js';
|
|
37
|
+
import { countUniformVectors, FULL_LIGHT_BUDGET, nextLightBudget, planLightBudget, } from '../../uniformVectorBudget.js';
|
|
38
|
+
import { OVERLAY_FLOATS, overlayLays, packSurfaceOverlay, } from '../../surfaceOverlay.js';
|
|
32
39
|
import { FILM_FRAG, FILM_VERT } from '../../shaders/film.js';
|
|
33
40
|
import { SceneTarget } from '../../sceneTarget.js';
|
|
34
41
|
import { FILM_LOOK_WITHOUT_COMPOSITE } from '../../shaders/filmLook.js';
|
|
@@ -70,7 +77,7 @@ import { MAX_AREA_LIGHTS } from '../../areaLights.js';
|
|
|
70
77
|
import { areaShadowLayerCount, castingRange, createResolvedAreaShadows, firstAreaShadowLayer, AreaShadowSet, } from '../../areaShadowSet.js';
|
|
71
78
|
import { equirectToCubeFaces } from '../../equirectToCube.js';
|
|
72
79
|
import { SKY_FRAG, skyVertFor } from '../../shaders/sky.js';
|
|
73
|
-
import { DEPTH_CUTOUT_FRAG, DEPTH_CUTOUT_VERT, DEPTH_FRAG, DEPTH_INSTANCED_CUTOUT_VERT, DEPTH_INSTANCED_VERT, DEPTH_SKINNED_VERT, DEPTH_VERT, } from '../../shaders/depth.js';
|
|
80
|
+
import { DEPTH_CUTOUT_FRAG, DEPTH_CUTOUT_VERT, DEPTH_FRAG, DEPTH_INSTANCED_ANIMATED_CUTOUT_VERT, DEPTH_INSTANCED_ANIMATED_VERT, DEPTH_INSTANCED_CUTOUT_VERT, DEPTH_INSTANCED_VERT, DEPTH_SKINNED_VERT, DEPTH_VERT, } from '../../shaders/depth.js';
|
|
74
81
|
import { OCTAHEDRAL_EDGE, PointShadowArray, strideLayers } from '../../pointShadowArray.js';
|
|
75
82
|
import { SunShadowArray } from '../../shadowMap.js';
|
|
76
83
|
import { POINT_SHADOW_SAMPLER, PointShadowSystem } from '../../pointShadowSystem.js';
|
|
@@ -78,6 +85,7 @@ import { castsDepth, glassOf } from '../../shadowCasters.js';
|
|
|
78
85
|
import { GlassCasterList } from '../../glassCasters.js';
|
|
79
86
|
import { SunGlassTint } from '../../sunGlassTint.js';
|
|
80
87
|
import { GlassTintPrograms } from './glassTintPrograms.js';
|
|
88
|
+
import { captureStaticDraws, replayStaticDraws } from '../../staticDraws.js';
|
|
81
89
|
import { FACE_COUNT, PointShadowMap } from '../../pointShadowMap.js';
|
|
82
90
|
import { createResolvedPointShadows, DEFAULT_SOURCE_RADIUS } from '../../pointShadowImage.js';
|
|
83
91
|
import { createBakeScratch, runPointShadowBakes, selectCastingLights, } from '../../pointShadowBudget.js';
|
|
@@ -104,11 +112,15 @@ import { SCATTER_DEPTH_VERT, SCATTER_FRAG, SCATTER_VERT } from '../../shaders/sc
|
|
|
104
112
|
import { FlockRenderer } from '../../flockRenderer.js';
|
|
105
113
|
import { TextRenderer } from '../../textRenderer.js';
|
|
106
114
|
import { SdfTextRenderer } from '../../sdfTextRenderer.js';
|
|
107
|
-
import { MOTION_BLUR_MAX_UV, RUSH_REACH_UV } from '../../vertexDefaults.js';
|
|
108
|
-
import { OUTPUT_TRANSFORM_CODE } from '../../vertexDefaults.js';
|
|
115
|
+
import { MOTION_BLUR_MAX_UV, OBJECT_BLUR_UNKEPT, RUSH_REACH_UV, clampMotionShare, } from '../../vertexDefaults.js';
|
|
116
|
+
import { OUTPUT_TRANSFORM_CODE, forwardTransformCode } from '../../vertexDefaults.js';
|
|
117
|
+
import { DEFAULT_FILMIC_CURVE, FILMIC_CLAMPED, FILMIC_CONSTANTS, FILMIC_WITHOUT_COMPOSITE, resolveFilmicCurve, } from '../../filmicCurve.js';
|
|
109
118
|
import { deviceSnappedCellSize } from '../../textLayout.js';
|
|
110
119
|
import { WindStreakRenderer } from '../../windStreakRenderer.js';
|
|
111
120
|
import { InstancedBatch } from './instanced.js';
|
|
121
|
+
import { GlBoneAnimation } from './boneAnimations.js';
|
|
122
|
+
import { packBoneAnimation } from '../../boneAnimation.js';
|
|
123
|
+
import { BONE_PLACES_TEXTURE_UNIT, BONE_TURNS_TEXTURE_UNIT, INSTANCE_CLOCKS_TEXTURE_UNIT, } from '../../lightBudget.js';
|
|
112
124
|
import { cutoutOf } from '../../cutoutCaster.js';
|
|
113
125
|
import { CUTOUT_RESOLVE_CODE, resolveCutout } from '../../cutoutDither.js';
|
|
114
126
|
import { materialHasMaps, noteMapsWithoutUvs } from '../../mapsWithoutUvs.js';
|
|
@@ -604,6 +616,10 @@ export class WebGL2Renderer {
|
|
|
604
616
|
reflectionQueue = new ReflectionQueue();
|
|
605
617
|
/** Built on the first frame that submits a surface, and never for a scene with none. */
|
|
606
618
|
ssr = null;
|
|
619
|
+
/** The frame's materials' reflections, resolved; null where off. See `frameReflections.ts`. */
|
|
620
|
+
frameReflections;
|
|
621
|
+
/** Their two maps on the scene's framebuffer, null where they cannot be drawn. */
|
|
622
|
+
reflectionTargets;
|
|
607
623
|
/** Said once where a reflection cannot be traced at all. */
|
|
608
624
|
reflectionsRefused = false;
|
|
609
625
|
/** Where the frame's camera stands, which is what gives a reconstructed normal its sign. */
|
|
@@ -674,6 +690,8 @@ export class WebGL2Renderer {
|
|
|
674
690
|
* exactly as it asked to be and nothing about it changes.
|
|
675
691
|
*/
|
|
676
692
|
exposure;
|
|
693
|
+
/** The `filmic` curve's eight constants, held until `setFilmicCurve` is called again. */
|
|
694
|
+
filmConstants = new Float32Array(FILMIC_CONSTANTS);
|
|
677
695
|
/**
|
|
678
696
|
* This frame's veil colour, and how much of it to composite. See `setFrameVeil`.
|
|
679
697
|
*
|
|
@@ -788,6 +806,11 @@ export class WebGL2Renderer {
|
|
|
788
806
|
depthCutoutUniforms;
|
|
789
807
|
depthInstancedCutoutProgram;
|
|
790
808
|
depthInstancedCutoutUniforms;
|
|
809
|
+
/** A crowd's casters: the instanced depth programs that play a bone animation. */
|
|
810
|
+
depthAnimatedProgram;
|
|
811
|
+
depthAnimatedUniforms;
|
|
812
|
+
depthAnimatedCutoutProgram;
|
|
813
|
+
depthAnimatedCutoutUniforms;
|
|
791
814
|
scatterDepthProgram;
|
|
792
815
|
scatterDepthUniforms;
|
|
793
816
|
/** The sun's static, moving and peeled maps, as layers of one array. See `shadowMap.ts`. */
|
|
@@ -875,6 +898,15 @@ export class WebGL2Renderer {
|
|
|
875
898
|
warnedNoPointShadowArray = false;
|
|
876
899
|
/** Said once, for the retired ambient dial. */
|
|
877
900
|
warnedEnvironmentAmbient = false;
|
|
901
|
+
/** The overlay the draws wear, `uOverlay`: see `setSurfaceOverlay`. */
|
|
902
|
+
overlayFloats = new Float32Array(OVERLAY_FLOATS);
|
|
903
|
+
/** Its atlas, and the 2D copy of it bound where the refraction copy goes. */
|
|
904
|
+
overlayMaps = null;
|
|
905
|
+
overlayTexture = null;
|
|
906
|
+
/** Whether this device's lit stage has room for the overlay's vectors: asked once. */
|
|
907
|
+
overlayRoom = null;
|
|
908
|
+
warnedOverlayRoom = false;
|
|
909
|
+
warnedOverlayBlocks = false;
|
|
878
910
|
/** Whether a probe is being baked, so the passes that would recurse into one can tell. */
|
|
879
911
|
probePassActive = false;
|
|
880
912
|
/**
|
|
@@ -1182,14 +1214,21 @@ export class WebGL2Renderer {
|
|
|
1182
1214
|
const count = Math.min(data.count, gpuBatch.capacity);
|
|
1183
1215
|
if (count === 0)
|
|
1184
1216
|
return;
|
|
1217
|
+
const clip = gpuBatch.animation;
|
|
1218
|
+
if (clip !== null && clip.disposed)
|
|
1219
|
+
return;
|
|
1185
1220
|
this.shadowDrawBudget.ask();
|
|
1186
1221
|
const twoSided = material?.doubleSided === true;
|
|
1187
1222
|
if (twoSided)
|
|
1188
1223
|
gl.disable(gl.CULL_FACE);
|
|
1189
1224
|
const cutout = cutoutOf(material);
|
|
1190
1225
|
if (cutout !== null) {
|
|
1191
|
-
|
|
1192
|
-
this.
|
|
1226
|
+
/* A crowd casts where its clip has moved it, through the animated variant. */
|
|
1227
|
+
const uniforms = clip === null ? this.depthInstancedCutoutUniforms : this.depthAnimatedCutoutUniforms;
|
|
1228
|
+
gl.useProgram(clip === null ? this.depthInstancedCutoutProgram : this.depthAnimatedCutoutProgram);
|
|
1229
|
+
this.bindCutoutDepth(uniforms, cutout);
|
|
1230
|
+
if (clip !== null)
|
|
1231
|
+
this.bindAnimation(uniforms, gpuBatch);
|
|
1193
1232
|
gpuBatch.draw(gl, count);
|
|
1194
1233
|
if (twoSided && this.depthPassCullsFaces)
|
|
1195
1234
|
gl.enable(gl.CULL_FACE);
|
|
@@ -1197,12 +1236,14 @@ export class WebGL2Renderer {
|
|
|
1197
1236
|
this.restoreSurfaceTextureUnit();
|
|
1198
1237
|
return;
|
|
1199
1238
|
}
|
|
1200
|
-
const u = this.depthInstancedUniforms;
|
|
1201
|
-
gl.useProgram(this.depthInstancedProgram);
|
|
1239
|
+
const u = clip === null ? this.depthInstancedUniforms : this.depthAnimatedUniforms;
|
|
1240
|
+
gl.useProgram(clip === null ? this.depthInstancedProgram : this.depthAnimatedProgram);
|
|
1202
1241
|
gl.uniformMatrix4fv(u['uLightViewProj'] ?? null, false, this.activeDepthViewProj);
|
|
1203
1242
|
gl.uniform1i(u['uPeelShadowLayer'] ?? null, this.activeDepthPeel ? 1 : 0);
|
|
1204
1243
|
if (this.activeDepthPeel)
|
|
1205
1244
|
gl.uniform1i(u['uPreviousShadowMap'] ?? null, 0);
|
|
1245
|
+
if (clip !== null)
|
|
1246
|
+
this.bindAnimation(u, gpuBatch);
|
|
1206
1247
|
gpuBatch.draw(gl, count);
|
|
1207
1248
|
if (twoSided && this.depthPassCullsFaces)
|
|
1208
1249
|
gl.enable(gl.CULL_FACE);
|
|
@@ -1503,6 +1544,8 @@ export class WebGL2Renderer {
|
|
|
1503
1544
|
* call. `init` runs now rather than at the first frame, because building a pipeline in the
|
|
1504
1545
|
* frame loop is the allocation the house rules are about.
|
|
1505
1546
|
*/
|
|
1547
|
+
/** Each registered pass's declared reads, as a mask, resolved once at registration. */
|
|
1548
|
+
passReads = new Map();
|
|
1506
1549
|
registerPass(definition) {
|
|
1507
1550
|
const handle = registerIn(this.passes, definition);
|
|
1508
1551
|
/*
|
|
@@ -1522,6 +1565,8 @@ export class WebGL2Renderer {
|
|
|
1522
1565
|
};
|
|
1523
1566
|
if (definition.prepare !== undefined)
|
|
1524
1567
|
this.preparingPasses++;
|
|
1568
|
+
/* Resolved once: `maskOf` builds an array, and the frame path may not. */
|
|
1569
|
+
this.passReads.set(handle, maskOf(...(definition.reads ?? [])));
|
|
1525
1570
|
definition.init?.(this.passDevice);
|
|
1526
1571
|
return handle;
|
|
1527
1572
|
}
|
|
@@ -1573,10 +1618,28 @@ export class WebGL2Renderer {
|
|
|
1573
1618
|
gl: this.gl,
|
|
1574
1619
|
outputTransform: 0,
|
|
1575
1620
|
outputExposure: 1,
|
|
1621
|
+
sceneColor: null,
|
|
1622
|
+
sceneDepth: null,
|
|
1576
1623
|
};
|
|
1577
1624
|
const context = this.passContext;
|
|
1578
1625
|
context.outputTransform = this.gradeCode();
|
|
1579
1626
|
context.outputExposure = this.gradeExposure();
|
|
1627
|
+
/*
|
|
1628
|
+
* The copies a pass declared it reads, taken here with everything drawn before it: colour
|
|
1629
|
+
* afresh at each reading pass, as WebGPU's `takePassColour` does, so a full-screen effect drawn
|
|
1630
|
+
* last reads the whole frame; depth afresh too, since a blit is the whole cost of either here.
|
|
1631
|
+
* Not in a mirror, a probe's face or a capture, whose pictures are not the frame's.
|
|
1632
|
+
*/
|
|
1633
|
+
const reads = this.passReads.get(handle) ?? 0;
|
|
1634
|
+
const own = !this.reflectionPassActive && !this.probePassActive && !this.capturePassActive;
|
|
1635
|
+
context.sceneColor =
|
|
1636
|
+
own && (reads & COLOR_SNAPSHOT) !== 0
|
|
1637
|
+
? (this.sceneTarget?.snapshotColor(true) ?? null)
|
|
1638
|
+
: null;
|
|
1639
|
+
context.sceneDepth =
|
|
1640
|
+
own && (reads & DEPTH_SNAPSHOT) !== 0
|
|
1641
|
+
? (this.sceneTarget?.snapshotDepth(true) ?? null)
|
|
1642
|
+
: null;
|
|
1580
1643
|
definition.draw(context);
|
|
1581
1644
|
}
|
|
1582
1645
|
/** Let go of a pass. A handle kept past this draws nothing; see `PassHandle`. */
|
|
@@ -1584,6 +1647,7 @@ export class WebGL2Renderer {
|
|
|
1584
1647
|
const definition = unregisterIn(this.passes, handle);
|
|
1585
1648
|
if (definition?.prepare !== undefined)
|
|
1586
1649
|
this.preparingPasses--;
|
|
1650
|
+
this.passReads.delete(handle);
|
|
1587
1651
|
if (definition !== undefined && this.passDevice !== null)
|
|
1588
1652
|
definition.dispose?.(this.passDevice);
|
|
1589
1653
|
}
|
|
@@ -1734,10 +1798,10 @@ export class WebGL2Renderer {
|
|
|
1734
1798
|
gl.bindTexture(gl.TEXTURE_2D, this.clusterTexture);
|
|
1735
1799
|
gl.uniform1i(u['uClusterTable'] ?? null, CLUSTER_TABLE_TEXTURE_UNIT);
|
|
1736
1800
|
gl.uniform1i(u['uClustered'] ?? null, on ? 1 : 0);
|
|
1737
|
-
if (!on)
|
|
1738
|
-
return;
|
|
1739
1801
|
/*
|
|
1740
|
-
* The frustum, read off the projection rather than taken as a parameter
|
|
1802
|
+
* The frustum, read off the projection rather than taken as a parameter — **and with the view,
|
|
1803
|
+
* written whether or not the lights are clustered**: a surface overlay's rim finds its place on
|
|
1804
|
+
* the screen through the same two (`overlayScreen`).
|
|
1741
1805
|
*
|
|
1742
1806
|
* A perspective matrix carries `1 / tan(fovY/2)` at [5] and that over the aspect at [0], and
|
|
1743
1807
|
* the near and far planes in [14] and [10]. Deriving them here means the binner and the shader
|
|
@@ -1755,6 +1819,8 @@ export class WebGL2Renderer {
|
|
|
1755
1819
|
this.clusterFrustum[3] = aspect;
|
|
1756
1820
|
gl.uniform4fv(u['uClusterFrustum'] ?? null, this.clusterFrustum);
|
|
1757
1821
|
gl.uniformMatrix4fv(u['uView'] ?? null, false, camera.view);
|
|
1822
|
+
if (!on)
|
|
1823
|
+
return;
|
|
1758
1824
|
this.clusterTable ??= createClusterTable();
|
|
1759
1825
|
/* The rectangles past the fixed arm's go into the table. See `ClusterLightSet.areas`. */
|
|
1760
1826
|
const lights = fillClusterLightSet(env, this.clusterLights, this.shadedAreaLights);
|
|
@@ -1828,6 +1894,11 @@ export class WebGL2Renderer {
|
|
|
1828
1894
|
this.quality = resolveRenderQuality(qualityOptions);
|
|
1829
1895
|
/* The grade this world asked for, until a frame says otherwise. See `setOutputExposure`. */
|
|
1830
1896
|
this.exposure = this.quality.outputExposure;
|
|
1897
|
+
resolveFilmicCurve(DEFAULT_FILMIC_CURVE, this.filmConstants);
|
|
1898
|
+
if (this.quality.outputTransform === 'filmic' &&
|
|
1899
|
+
!(this.quality.screenEffects && this.quality.hdrScene)) {
|
|
1900
|
+
console.warn(FILMIC_WITHOUT_COMPOSITE);
|
|
1901
|
+
}
|
|
1831
1902
|
/*
|
|
1832
1903
|
* Multisampling, but only when anything can use it.
|
|
1833
1904
|
*
|
|
@@ -2086,6 +2157,29 @@ export class WebGL2Renderer {
|
|
|
2086
2157
|
this.sceneTarget = this.quality.screenEffects
|
|
2087
2158
|
? new SceneTarget(gl, this.quality.sceneSamples, this.quality.hdrScene, this.quality.discardResolvedAttachments)
|
|
2088
2159
|
: null;
|
|
2160
|
+
/*
|
|
2161
|
+
* The frame's materials' reflections, where the frame can carry them: scene light to swap the
|
|
2162
|
+
* probe's share in, and one sample, which the trace's depth copy and both maps are. Said once
|
|
2163
|
+
* where it cannot, in the words the other backend uses. See `frameReflections.ts`.
|
|
2164
|
+
*/
|
|
2165
|
+
this.frameReflections = resolveFrameReflections(this.quality.screenSpaceReflections);
|
|
2166
|
+
const reflectionsRefusal = this.frameReflections === null
|
|
2167
|
+
? null
|
|
2168
|
+
: this.sceneTarget === null || !this.sceneTarget.keepsRange
|
|
2169
|
+
? 'the frame keeps no scene light to swap the probe in (`screenEffects` and `hdrScene`)'
|
|
2170
|
+
: this.sceneTarget.sampleCount > 1
|
|
2171
|
+
? 'a multisampled frame has no single depth or colour to march through'
|
|
2172
|
+
: null;
|
|
2173
|
+
if (reflectionsRefusal !== null) {
|
|
2174
|
+
console.warn(`WebGL2: ${frameReflectionsRefused(reflectionsRefusal)}`);
|
|
2175
|
+
}
|
|
2176
|
+
this.reflectionTargets =
|
|
2177
|
+
this.frameReflections !== null && reflectionsRefusal === null
|
|
2178
|
+
? new ReflectionSurfaceTargets(gl)
|
|
2179
|
+
: null;
|
|
2180
|
+
/* The lit programs write the maps from the first frame they are rebuilt in. See `litOn`. */
|
|
2181
|
+
if (this.reflectionTargets !== null)
|
|
2182
|
+
this.askLit('REFLECTION_MAPS');
|
|
2089
2183
|
/* Screen-space skin needs the composite to add into; without one it is pre-integrated, said once. */
|
|
2090
2184
|
if (this.quality.skinScattering === 'screen-space' && this.sceneTarget === null) {
|
|
2091
2185
|
console.warn("WebGL2: skinScattering 'screen-space' needs screenEffects to add into; skin is pre-integrated");
|
|
@@ -2154,10 +2248,15 @@ export class WebGL2Renderer {
|
|
|
2154
2248
|
this.depthCutoutUniforms = uniformLocations(gl, this.depthCutoutProgram, 'depth-cutout');
|
|
2155
2249
|
this.depthInstancedCutoutProgram = compileProgram(gl, DEPTH_INSTANCED_CUTOUT_VERT, DEPTH_CUTOUT_FRAG, 'depth-instanced-cutout');
|
|
2156
2250
|
this.depthInstancedCutoutUniforms = uniformLocations(gl, this.depthInstancedCutoutProgram, 'depth-instanced-cutout');
|
|
2251
|
+
this.depthAnimatedProgram = compileProgram(gl, DEPTH_INSTANCED_ANIMATED_VERT, DEPTH_FRAG, 'depth-instanced-animated');
|
|
2252
|
+
this.depthAnimatedUniforms = uniformLocations(gl, this.depthAnimatedProgram, 'depth-instanced-animated');
|
|
2253
|
+
this.depthAnimatedCutoutProgram = compileProgram(gl, DEPTH_INSTANCED_ANIMATED_CUTOUT_VERT, DEPTH_CUTOUT_FRAG, 'depth-instanced-animated-cutout');
|
|
2254
|
+
this.depthAnimatedCutoutUniforms = uniformLocations(gl, this.depthAnimatedCutoutProgram, 'depth-instanced-animated-cutout');
|
|
2157
2255
|
/* The cutout map is always read from the surface unit, which the draw borrows and gives back. */
|
|
2158
2256
|
for (const [program, uniforms] of [
|
|
2159
2257
|
[this.depthCutoutProgram, this.depthCutoutUniforms],
|
|
2160
2258
|
[this.depthInstancedCutoutProgram, this.depthInstancedCutoutUniforms],
|
|
2259
|
+
[this.depthAnimatedCutoutProgram, this.depthAnimatedCutoutUniforms],
|
|
2161
2260
|
]) {
|
|
2162
2261
|
gl.useProgram(program);
|
|
2163
2262
|
gl.uniform1i(uniforms['uCutoutMap'] ?? null, SURFACE_TEXTURE_UNIT);
|
|
@@ -2325,6 +2424,8 @@ export class WebGL2Renderer {
|
|
|
2325
2424
|
this.lightVolumeProgram,
|
|
2326
2425
|
this.depthCutoutProgram,
|
|
2327
2426
|
this.depthInstancedCutoutProgram,
|
|
2427
|
+
this.depthAnimatedProgram,
|
|
2428
|
+
this.depthAnimatedCutoutProgram,
|
|
2328
2429
|
]) {
|
|
2329
2430
|
gl.deleteProgram(program);
|
|
2330
2431
|
}
|
|
@@ -2642,11 +2743,16 @@ export class WebGL2Renderer {
|
|
|
2642
2743
|
updateSurfaceTexture(texture, source) {
|
|
2643
2744
|
if (this.contextLost)
|
|
2644
2745
|
return;
|
|
2645
|
-
texture.update(this.gl, source);
|
|
2746
|
+
texture.update(this.gl, source, this.compressedFormats);
|
|
2747
|
+
/* New pixels are a new copy for the overlay's slot: see `SurfaceTexture.flatLayer`. */
|
|
2748
|
+
if (texture === this.overlayMaps)
|
|
2749
|
+
this.bindOverlayMaps(texture);
|
|
2646
2750
|
}
|
|
2647
2751
|
disposeSurfaceTexture(texture) {
|
|
2648
2752
|
if (this.contextLost)
|
|
2649
2753
|
return;
|
|
2754
|
+
if (texture === this.overlayMaps)
|
|
2755
|
+
this.bindOverlayMaps(null);
|
|
2650
2756
|
texture.dispose(this.gl);
|
|
2651
2757
|
this.captureTargets.get(texture)?.dispose(this.gl);
|
|
2652
2758
|
this.captureTargets.delete(texture);
|
|
@@ -2848,6 +2954,13 @@ export class WebGL2Renderer {
|
|
|
2848
2954
|
this.materialDoubleSided = material?.doubleSided === true;
|
|
2849
2955
|
this.materialHasMaps = materialHasMaps(material);
|
|
2850
2956
|
this.currentMaterial = material;
|
|
2957
|
+
const reflectivity = material?.reflectivity;
|
|
2958
|
+
const environmentGain = material?.environmentGain;
|
|
2959
|
+
this.materialReflectivity =
|
|
2960
|
+
reflectivity === undefined ? null : Math.min(1, Math.max(0, reflectivity));
|
|
2961
|
+
this.materialEnvironmentGain =
|
|
2962
|
+
environmentGain === undefined ? null : Math.max(0, environmentGain);
|
|
2963
|
+
this.writeEnvironmentDials();
|
|
2851
2964
|
/* What the blur spreads this skin by: the last material to name a profile says. */
|
|
2852
2965
|
const model = material?.model;
|
|
2853
2966
|
warnUnpagedLightmap(model, material?.modelMap);
|
|
@@ -2885,7 +2998,8 @@ export class WebGL2Renderer {
|
|
|
2885
2998
|
const v = this.cutoutVector;
|
|
2886
2999
|
v[0] = material?.cutout ?? 0;
|
|
2887
3000
|
v[1] = CUTOUT_RESOLVE_CODE[this.cutoutResolveNow(material, translucent)];
|
|
2888
|
-
|
|
3001
|
+
/* z unused: the dither's frame is the pass's, in `uHighlightMin.w`. See `uCutout`. */
|
|
3002
|
+
v[2] = 0;
|
|
2889
3003
|
/* The material's lighting channels, which the lamps' are compared with. See `uCutout`. */
|
|
2890
3004
|
v[3] = lightChannelsOf(material?.lightChannels);
|
|
2891
3005
|
return v;
|
|
@@ -2981,6 +3095,8 @@ export class WebGL2Renderer {
|
|
|
2981
3095
|
if (material?.physicalSpecular === true)
|
|
2982
3096
|
this.askLit('PHYSICAL_SPECULAR');
|
|
2983
3097
|
gl.uniform4fv(u['uModelParams'] ?? null, this.modelParams);
|
|
3098
|
+
/* The draw's overlay, which a program born mid-pass must wear too: see `setSurfaceOverlay`. */
|
|
3099
|
+
gl.uniform4fv(u['uOverlay'] ?? null, this.overlayFloats);
|
|
2984
3100
|
/* The albedo's effects table, or the stand-in the shader reads as none. Before the early
|
|
2985
3101
|
return, so a material with no albedo still leaves a complete texture on the unit. */
|
|
2986
3102
|
if (albedo === null) {
|
|
@@ -3074,10 +3190,85 @@ export class WebGL2Renderer {
|
|
|
3074
3190
|
setSurfaceReflectivity(amount) {
|
|
3075
3191
|
if (this.contextLost)
|
|
3076
3192
|
return;
|
|
3193
|
+
this.passReflectivity = Math.min(1, Math.max(0, amount));
|
|
3194
|
+
this.writeEnvironmentDials();
|
|
3195
|
+
}
|
|
3196
|
+
/**
|
|
3197
|
+
* The reflectivity and environment gain the setters gave the pass, and the ones the material
|
|
3198
|
+
* set now states, or null where it states none (`SurfaceMaterial.reflectivity`). A draw wears the
|
|
3199
|
+
* material's where it has one and the pass's otherwise.
|
|
3200
|
+
*/
|
|
3201
|
+
passReflectivity = 0;
|
|
3202
|
+
passEnvironmentGain = 1;
|
|
3203
|
+
materialReflectivity = null;
|
|
3204
|
+
materialEnvironmentGain = null;
|
|
3205
|
+
writeEnvironmentDials() {
|
|
3077
3206
|
this.materials.dirty();
|
|
3078
|
-
this.environmentDials[1] =
|
|
3207
|
+
this.environmentDials[1] = this.materialReflectivity ?? this.passReflectivity;
|
|
3208
|
+
this.environmentDials[2] = this.materialEnvironmentGain ?? this.passEnvironmentGain;
|
|
3079
3209
|
this.writeFlatVector('uEnvironmentDials', this.environmentDials);
|
|
3080
3210
|
}
|
|
3211
|
+
/**
|
|
3212
|
+
* The overlay the following draws wear — a rim of light, a dissolve, wrinkles by region — or null
|
|
3213
|
+
* for none. Pass state, as the material is: `bindMeshPass` puts it back to none. See
|
|
3214
|
+
* `surfaceOverlay.ts` for what it lays and `shaders/flat/overlay.ts` for how.
|
|
3215
|
+
*
|
|
3216
|
+
* **Refused, said once, where the lit stage has no room for it.** Fifteen vectors more than the
|
|
3217
|
+
* program this device fits: counted from the source as `fitFlatProgram` counts, and on a device
|
|
3218
|
+
* whose limit is the 256 an Adreno 740 reports, the eight-light build at 254 has none. The draws
|
|
3219
|
+
* then wear nothing, which is the picture a game without the feature draws. Its atlas is bound as
|
|
3220
|
+
* a 2D copy of its first layer, in the refraction copy's unit (`SurfaceTexture.flatLayer`); a
|
|
3221
|
+
* compressed atlas has no copy and is read as none, also said once.
|
|
3222
|
+
*/
|
|
3223
|
+
setSurfaceOverlay(overlay) {
|
|
3224
|
+
if (this.contextLost)
|
|
3225
|
+
return;
|
|
3226
|
+
let wanted = overlay;
|
|
3227
|
+
if (overlayLays(wanted) && !this.overlayFits()) {
|
|
3228
|
+
if (!this.warnedOverlayRoom) {
|
|
3229
|
+
this.warnedOverlayRoom = true;
|
|
3230
|
+
console.warn('Renderer: setSurfaceOverlay is refused on this device. The overlay takes fifteen ' +
|
|
3231
|
+
'fragment uniform vectors and the lit shader already uses all but ' +
|
|
3232
|
+
`${this.overlayRoomLeft()} of the ${this.fragmentVectorLimit()} this GPU offers; ` +
|
|
3233
|
+
'draws wear no overlay. A lower `maxLights` makes the room, or WebGPU has it.');
|
|
3234
|
+
}
|
|
3235
|
+
wanted = null;
|
|
3236
|
+
}
|
|
3237
|
+
packSurfaceOverlay(wanted, this.overlayFloats);
|
|
3238
|
+
if (overlayLays(wanted))
|
|
3239
|
+
this.askLit('SURFACE_OVERLAY');
|
|
3240
|
+
this.materials.dirty();
|
|
3241
|
+
this.writeFlatVector('uOverlay', this.overlayFloats);
|
|
3242
|
+
this.bindOverlayMaps((wanted?.maps ?? null));
|
|
3243
|
+
}
|
|
3244
|
+
/** The overlay's atlas into the refraction copy's unit, as a 2D copy, or the empty texture. */
|
|
3245
|
+
bindOverlayMaps(maps) {
|
|
3246
|
+
const { gl } = this;
|
|
3247
|
+
this.overlayMaps = maps;
|
|
3248
|
+
this.overlayTexture = maps === null ? null : maps.flatLayer(gl);
|
|
3249
|
+
if (maps !== null && this.overlayTexture === null && !this.warnedOverlayBlocks) {
|
|
3250
|
+
this.warnedOverlayBlocks = true;
|
|
3251
|
+
console.warn("Renderer: a surface overlay's maps are a compressed texture, which WebGL2 cannot copy " +
|
|
3252
|
+
'into the 2D image the lit stage reads them from. The overlay is drawn without its ' +
|
|
3253
|
+
'images; upload the atlas from an image instead.');
|
|
3254
|
+
}
|
|
3255
|
+
gl.activeTexture(gl.TEXTURE0 + REFRACT_SCENE_TEXTURE_UNIT);
|
|
3256
|
+
gl.bindTexture(gl.TEXTURE_2D, this.overlayTexture ?? this.emptyTexture2D);
|
|
3257
|
+
}
|
|
3258
|
+
/** The most fragment uniform vectors this GPU offers a stage. */
|
|
3259
|
+
fragmentVectorLimit() {
|
|
3260
|
+
return this.gl.getParameter(this.gl.MAX_FRAGMENT_UNIFORM_VECTORS);
|
|
3261
|
+
}
|
|
3262
|
+
/** What the lit shader at this renderer's budget leaves of the limit, with no overlay. */
|
|
3263
|
+
overlayRoomLeft() {
|
|
3264
|
+
return (this.fragmentVectorLimit() - countUniformVectors(this.flatFragAt(this.lightBudget, false)));
|
|
3265
|
+
}
|
|
3266
|
+
/** Whether the lit shader with the overlay compiled in fits this GPU: counted once. */
|
|
3267
|
+
overlayFits() {
|
|
3268
|
+
this.overlayRoom ??=
|
|
3269
|
+
countUniformVectors(this.flatFragAt(this.lightBudget, true)) <= this.fragmentVectorLimit();
|
|
3270
|
+
return this.overlayRoom;
|
|
3271
|
+
}
|
|
3081
3272
|
/**
|
|
3082
3273
|
* How bright the environment the surfaces drawn next reflect is. 1 is the default and identity.
|
|
3083
3274
|
*
|
|
@@ -3093,9 +3284,8 @@ export class WebGL2Renderer {
|
|
|
3093
3284
|
setEnvironmentGain(gain) {
|
|
3094
3285
|
if (this.contextLost)
|
|
3095
3286
|
return;
|
|
3096
|
-
this.
|
|
3097
|
-
this.
|
|
3098
|
-
this.writeFlatVector('uEnvironmentDials', this.environmentDials);
|
|
3287
|
+
this.passEnvironmentGain = Math.max(0, gain);
|
|
3288
|
+
this.writeEnvironmentDials();
|
|
3099
3289
|
}
|
|
3100
3290
|
/**
|
|
3101
3291
|
* Kept as a no-op that says so once, because it was a stand-in for a term that now exists.
|
|
@@ -3364,8 +3554,95 @@ export class WebGL2Renderer {
|
|
|
3364
3554
|
throw new Error('WebGL2: a skinned mesh cannot be instanced — an instanced draw binds the two attribute ' +
|
|
3365
3555
|
'locations the joint indices and weights occupy, and this mesh supplies them.');
|
|
3366
3556
|
}
|
|
3557
|
+
const animation = (options.animation ?? null);
|
|
3558
|
+
/* The bone a vertex follows is its second coordinates' u, which a mesh carries in its grain
|
|
3559
|
+
lane; a mesh with none has nothing to name a bone by. See `boneAnimation.ts`. */
|
|
3560
|
+
if (animation !== null && !mesh.hasGrain) {
|
|
3561
|
+
throw new Error('WebGL2: a bone animation reads each vertex’s bone from the mesh’s second coordinates ' +
|
|
3562
|
+
'(`MeshData.lightmapUvs`), and this mesh has none.');
|
|
3563
|
+
}
|
|
3367
3564
|
this.ensureInstancedProgram();
|
|
3368
|
-
|
|
3565
|
+
if (animation !== null)
|
|
3566
|
+
this.ensureAnimatedProgram();
|
|
3567
|
+
return new InstancedBatch(this.gl, mesh, capacity, options.cull ?? false, animation);
|
|
3568
|
+
}
|
|
3569
|
+
/**
|
|
3570
|
+
* A bone animation an instanced batch can play (`InstancedOptions.animation`): per frame, a turn
|
|
3571
|
+
* and a place for each bone, which every vertex of the batch's mesh follows — the bone its second
|
|
3572
|
+
* coordinates' u names, times `boneScale` — at each instance's own moment of the clip. What a
|
|
3573
|
+
* crowd is made of: every instance moves at the frame's rate on its own clock, in the colour pass
|
|
3574
|
+
* and in every shadow, with nothing on the processor after this. See `boneAnimation.ts` for the
|
|
3575
|
+
* clip's layout and the arithmetic, which both backends run.
|
|
3576
|
+
*
|
|
3577
|
+
* What it gives up against skinning is the blend: a vertex follows one bone. Refused, with what
|
|
3578
|
+
* was wrong, for a clip whose numbers are not its size, or past 2,048 bones or frames.
|
|
3579
|
+
*/
|
|
3580
|
+
createBoneAnimation(clip) {
|
|
3581
|
+
return new GlBoneAnimation(this.gl, packBoneAnimation(clip));
|
|
3582
|
+
}
|
|
3583
|
+
/** Release a clip. A batch still playing it draws nothing, and casts nothing, after. */
|
|
3584
|
+
disposeBoneAnimation(animation) {
|
|
3585
|
+
const clip = animation;
|
|
3586
|
+
if (this.contextLost || clip.disposed)
|
|
3587
|
+
return;
|
|
3588
|
+
clip.dispose(this.gl);
|
|
3589
|
+
}
|
|
3590
|
+
/**
|
|
3591
|
+
* The clock a bone animation plays by this frame, in seconds on the caller's clock: an instance's
|
|
3592
|
+
* moment is this times its rate plus its phase (`MeshInstances.clocks`). **Set it before the
|
|
3593
|
+
* shadows are drawn**, once a frame, as the wind is set: a shadow drawn under one clock and its
|
|
3594
|
+
* caster under another come apart at the frame's rate. 0 until set, which holds every crowd at
|
|
3595
|
+
* the moment its phases name.
|
|
3596
|
+
*/
|
|
3597
|
+
setAnimationTime(seconds) {
|
|
3598
|
+
if (Number.isFinite(seconds))
|
|
3599
|
+
this.animationTime = seconds;
|
|
3600
|
+
}
|
|
3601
|
+
/** The clock `setAnimationTime` set. */
|
|
3602
|
+
animationTime = 0;
|
|
3603
|
+
/**
|
|
3604
|
+
* An animated batch's clip, clocks and numbers into whichever program is bound: the three vertex
|
|
3605
|
+
* textures on units of their own (`lightBudget.ts`), the clip's speed and bone scale, the clock.
|
|
3606
|
+
*/
|
|
3607
|
+
bindAnimation(u, batch) {
|
|
3608
|
+
const clip = batch.animation;
|
|
3609
|
+
const clocks = batch.clocks;
|
|
3610
|
+
if (clip === null || clocks === null)
|
|
3611
|
+
return;
|
|
3612
|
+
const { gl } = this;
|
|
3613
|
+
gl.activeTexture(gl.TEXTURE0 + BONE_PLACES_TEXTURE_UNIT);
|
|
3614
|
+
gl.bindTexture(gl.TEXTURE_2D, clip.places);
|
|
3615
|
+
gl.activeTexture(gl.TEXTURE0 + BONE_TURNS_TEXTURE_UNIT);
|
|
3616
|
+
gl.bindTexture(gl.TEXTURE_2D, clip.turns);
|
|
3617
|
+
gl.activeTexture(gl.TEXTURE0 + INSTANCE_CLOCKS_TEXTURE_UNIT);
|
|
3618
|
+
gl.bindTexture(gl.TEXTURE_2D, clocks.texture);
|
|
3619
|
+
gl.uniform1i(u['uBonePlaces'] ?? null, BONE_PLACES_TEXTURE_UNIT);
|
|
3620
|
+
gl.uniform1i(u['uBoneTurns'] ?? null, BONE_TURNS_TEXTURE_UNIT);
|
|
3621
|
+
gl.uniform1i(u['uInstanceClocks'] ?? null, INSTANCE_CLOCKS_TEXTURE_UNIT);
|
|
3622
|
+
gl.uniform2f(u['uBoneClip'] ?? null, clip.framesPerSecond, clip.boneScale);
|
|
3623
|
+
gl.uniform1f(u['uSceneTime'] ?? null, this.animationTime);
|
|
3624
|
+
}
|
|
3625
|
+
/** The animated instanced program, compiled on first use. See `ensureInstancedProgram`. */
|
|
3626
|
+
flatAnimatedProgram = null;
|
|
3627
|
+
flatAnimatedUniforms = null;
|
|
3628
|
+
/** Compile the animated variant, once, and feed it this frame's pass state. */
|
|
3629
|
+
ensureAnimatedProgram() {
|
|
3630
|
+
if (this.contextLost || this.flatAnimatedProgram !== null)
|
|
3631
|
+
return;
|
|
3632
|
+
const { gl } = this;
|
|
3633
|
+
const program = compileProgram(gl, flatVert({ skinned: false, morphed: false, instanced: true, animated: true }), this.flatFragSource, 'flat.animated');
|
|
3634
|
+
const uniforms = uniformLocations(gl, program, 'flat.animated');
|
|
3635
|
+
this.flatAnimatedProgram = program;
|
|
3636
|
+
this.flatAnimatedUniforms = uniforms;
|
|
3637
|
+
this.flatTargets.push({ program, uniforms });
|
|
3638
|
+
const camera = this.lastPassCamera;
|
|
3639
|
+
const env = this.lastPassEnv;
|
|
3640
|
+
if (camera !== null && env !== null) {
|
|
3641
|
+
gl.useProgram(program);
|
|
3642
|
+
this.writeMeshPassState(uniforms, camera, env);
|
|
3643
|
+
this.writeMaterialState(uniforms, this.currentMaterial);
|
|
3644
|
+
this.useFlatProgram();
|
|
3645
|
+
}
|
|
3369
3646
|
}
|
|
3370
3647
|
/** Push placement and colour. Only the live prefix. */
|
|
3371
3648
|
uploadInstanced(batch, data) {
|
|
@@ -3418,18 +3695,29 @@ export class WebGL2Renderer {
|
|
|
3418
3695
|
const culling = !blend && batch.cull;
|
|
3419
3696
|
if (culling && !batchBoxVisible(batch.box, this.frustum, this.occlusion))
|
|
3420
3697
|
return;
|
|
3698
|
+
/* A batch playing a bone animation takes the animated variant; a disposed clip draws nothing. */
|
|
3699
|
+
const clip = batch.animation;
|
|
3700
|
+
if (clip !== null && clip.disposed)
|
|
3701
|
+
return;
|
|
3421
3702
|
this.drawBudget.ask();
|
|
3422
3703
|
this.ensureInstancedProgram();
|
|
3704
|
+
if (clip !== null)
|
|
3705
|
+
this.ensureAnimatedProgram();
|
|
3423
3706
|
/* A material with a model takes that model's instanced program; see `modelProgram`. */
|
|
3424
3707
|
const modelKind = this.currentMaterial?.model?.kind ?? null;
|
|
3425
|
-
const modelled = modelKind === null
|
|
3426
|
-
|
|
3427
|
-
|
|
3708
|
+
const modelled = modelKind === null
|
|
3709
|
+
? null
|
|
3710
|
+
: this.modelProgram(clip === null ? 'instanced' : 'animated', modelKind);
|
|
3711
|
+
const program = modelled?.program ?? (clip === null ? this.flatInstancedProgram : this.flatAnimatedProgram);
|
|
3712
|
+
const u = modelled?.uniforms ??
|
|
3713
|
+
(clip === null ? this.flatInstancedUniforms : this.flatAnimatedUniforms);
|
|
3428
3714
|
if (program === null || u === null)
|
|
3429
3715
|
return;
|
|
3430
3716
|
const { gl } = this;
|
|
3431
3717
|
gl.useProgram(program);
|
|
3432
3718
|
gl.uniform1i(u['uHasTangents'] ?? null, mesh.hasTangents ? 1 : 0);
|
|
3719
|
+
if (clip !== null)
|
|
3720
|
+
this.bindAnimation(u, batch);
|
|
3433
3721
|
const lit = options.lit ?? true;
|
|
3434
3722
|
/* Added light fades in the medium rather than receding into it. See `drawFog.ts`. */
|
|
3435
3723
|
const fog = fogModeOf(options.fog ?? true, blend && options.additive === true);
|
|
@@ -3496,10 +3784,19 @@ export class WebGL2Renderer {
|
|
|
3496
3784
|
gl.enable(gl.SAMPLE_ALPHA_TO_COVERAGE);
|
|
3497
3785
|
if (ditheredBlend)
|
|
3498
3786
|
gl.uniform4fv(u['uCutout'] ?? null, this.cutoutUniform(this.currentMaterial, true));
|
|
3499
|
-
|
|
3787
|
+
const keeps = !blend && this.reflectionKeeps();
|
|
3788
|
+
if (keeps)
|
|
3789
|
+
this.reflectionTargets?.on();
|
|
3790
|
+
/* An animated batch reads its clocks by instance index, which the culled copy would reorder:
|
|
3791
|
+
it is culled whole and never instance by instance. */
|
|
3792
|
+
if (culling && clip === null && cullsInstances(count, mesh.indexCount)) {
|
|
3500
3793
|
batch.drawCulled(gl, data, this.frustum);
|
|
3501
|
-
|
|
3794
|
+
}
|
|
3795
|
+
else {
|
|
3502
3796
|
batch.draw(gl, count);
|
|
3797
|
+
}
|
|
3798
|
+
if (keeps)
|
|
3799
|
+
this.reflectionTargets?.off();
|
|
3503
3800
|
if (coverage)
|
|
3504
3801
|
gl.disable(gl.SAMPLE_ALPHA_TO_COVERAGE);
|
|
3505
3802
|
if (ditheredBlend)
|
|
@@ -3561,7 +3858,9 @@ export class WebGL2Renderer {
|
|
|
3561
3858
|
const glass = this.glassScratch;
|
|
3562
3859
|
/* Strength, path thickness, transmission, frost: one vector, for the budget the shader states. */
|
|
3563
3860
|
gl.uniform4f(u['uSeeThrough'] ?? null, Math.max(refraction, 0), options.thicknessM ?? 0, glass.transmission, glass.frost);
|
|
3564
|
-
|
|
3861
|
+
/* w: this framebuffer's first row is the bottom, so up the screen is up the rows. */
|
|
3862
|
+
const tint = options.refractTint ?? WHITE_TINT;
|
|
3863
|
+
gl.uniform4f(u['uRefractTint'] ?? null, tint[0] ?? 1, tint[1] ?? 1, tint[2] ?? 1, 1);
|
|
3565
3864
|
gl.uniform3fv(u['uGlassTint'] ?? null, glass.tint);
|
|
3566
3865
|
return true;
|
|
3567
3866
|
}
|
|
@@ -3570,7 +3869,8 @@ export class WebGL2Renderer {
|
|
|
3570
3869
|
const { gl } = this;
|
|
3571
3870
|
gl.uniform4f(u['uSeeThrough'] ?? null, 0, 0, 0, 0);
|
|
3572
3871
|
gl.activeTexture(gl.TEXTURE0 + REFRACT_SCENE_TEXTURE_UNIT);
|
|
3573
|
-
|
|
3872
|
+
/* The overlay's atlas, where one is held: it shares the unit. See `setSurfaceOverlay`. */
|
|
3873
|
+
gl.bindTexture(gl.TEXTURE_2D, this.overlayTexture ?? this.emptyTexture2D);
|
|
3574
3874
|
}
|
|
3575
3875
|
disposeMesh(mesh) {
|
|
3576
3876
|
/*
|
|
@@ -3781,7 +4081,7 @@ export class WebGL2Renderer {
|
|
|
3781
4081
|
*/
|
|
3782
4082
|
gradeCode() {
|
|
3783
4083
|
return this.sceneTarget === null || !this.quality.hdrScene
|
|
3784
|
-
?
|
|
4084
|
+
? forwardTransformCode(this.quality.outputTransform)
|
|
3785
4085
|
: 0;
|
|
3786
4086
|
}
|
|
3787
4087
|
gradeExposure() {
|
|
@@ -4603,6 +4903,69 @@ export class WebGL2Renderer {
|
|
|
4603
4903
|
return;
|
|
4604
4904
|
casters(this.casterSink);
|
|
4605
4905
|
}
|
|
4906
|
+
/**
|
|
4907
|
+
* Draws that do not change between frames, enumerated once and kept: what a stage, a set or any
|
|
4908
|
+
* world that stands still draws, recorded so a frame replays it rather than issuing it.
|
|
4909
|
+
*
|
|
4910
|
+
* **The same enumeration `drawSceneCasters` takes**, called once, here: rigid meshes and instanced
|
|
4911
|
+
* batches, each with its material. A skinned mesh or a scatter batch is refused by name, since
|
|
4912
|
+
* both change every frame. What it captures is what the enumeration said at the time — the
|
|
4913
|
+
* matrices and the instance counts are copied, so a caller's scratch is free to move on — while
|
|
4914
|
+
* the meshes, batches, textures and materials stay the caller's: a list must be disposed, or made
|
|
4915
|
+
* again, before anything it holds is, and made again when a material it holds is changed.
|
|
4916
|
+
*
|
|
4917
|
+
* **What it is for is the frame's processor time.** WebGPU records the list into a render bundle
|
|
4918
|
+
* per view and replays it with one call, where a draw issued a frame at a time costs its encoding
|
|
4919
|
+
* every frame and again for every view that draws it. This backend has no bundles and replays the
|
|
4920
|
+
* entries through the scene-caster path every frame, at the cost a frame drawing them itself pays;
|
|
4921
|
+
* the picture is the same.
|
|
4922
|
+
*
|
|
4923
|
+
* What a list gives up against drawing each frame is culling: every entry is drawn in every view,
|
|
4924
|
+
* because a bundle cannot leave one out. A world split into lists by region culls by list.
|
|
4925
|
+
*/
|
|
4926
|
+
createStaticDraws(casters) {
|
|
4927
|
+
return captureStaticDraws(casters);
|
|
4928
|
+
}
|
|
4929
|
+
/**
|
|
4930
|
+
* Draw a list into the open mesh pass, under the camera, the lights and the fog `bindMeshPass`
|
|
4931
|
+
* set — the frame's, a mirror's, a capture's. Opaque draws, through each entry's material.
|
|
4932
|
+
*
|
|
4933
|
+
* **Its draws are rigid and wear no overlay**, so a skin palette, morph weights, cloth or a surface
|
|
4934
|
+
* overlay set before it is put back to none, as `bindMeshPass` puts an overlay back; and no
|
|
4935
|
+
* material is left set after it, which is what `setMaterial(null)` leaves. The state a pass set —
|
|
4936
|
+
* the grain, the relief, the fog, the grade — is the list's to draw with, as it is any draw's, and
|
|
4937
|
+
* a list drawn under a different one is recorded again for it on WebGPU.
|
|
4938
|
+
*/
|
|
4939
|
+
drawStaticDraws(draws) {
|
|
4940
|
+
if (this.contextLost)
|
|
4941
|
+
return;
|
|
4942
|
+
const list = draws;
|
|
4943
|
+
if (list.disposed) {
|
|
4944
|
+
this.warnStaticDisposed();
|
|
4945
|
+
return;
|
|
4946
|
+
}
|
|
4947
|
+
this.setSkinPalette(null);
|
|
4948
|
+
this.setMorphWeights(null);
|
|
4949
|
+
this.setCloth(null);
|
|
4950
|
+
this.setSurfaceOverlay(null);
|
|
4951
|
+
this.setMaterial(null);
|
|
4952
|
+
this.sceneCasterMaterial = undefined;
|
|
4953
|
+
replayStaticDraws(list, this.sceneCasterSink);
|
|
4954
|
+
this.setMaterial(null);
|
|
4955
|
+
this.sceneCasterMaterial = undefined;
|
|
4956
|
+
}
|
|
4957
|
+
/** Release a list. Its meshes, batches and materials are the caller's and are not released. */
|
|
4958
|
+
disposeStaticDraws(draws) {
|
|
4959
|
+
draws.disposed = true;
|
|
4960
|
+
}
|
|
4961
|
+
/** A disposed list drawn, said once: the frame loop does not throw. */
|
|
4962
|
+
warnStaticDisposed() {
|
|
4963
|
+
if (this.warnedStaticDisposed)
|
|
4964
|
+
return;
|
|
4965
|
+
this.warnedStaticDisposed = true;
|
|
4966
|
+
console.warn('drawStaticDraws: this list was disposed, and draws nothing.');
|
|
4967
|
+
}
|
|
4968
|
+
warnedStaticDisposed = false;
|
|
4606
4969
|
/**
|
|
4607
4970
|
* Draw the pass's glass twice: where the nearest pane is, and what the panes let through.
|
|
4608
4971
|
*
|
|
@@ -4784,7 +5147,7 @@ export class WebGL2Renderer {
|
|
|
4784
5147
|
* throws a sentence with the device's numbers in it rather than a bare driver string.
|
|
4785
5148
|
*/
|
|
4786
5149
|
/** The lit fragment source this renderer's profile compiles, at a light budget. */
|
|
4787
|
-
flatFragAt(budget) {
|
|
5150
|
+
flatFragAt(budget, overlay = this.litOn.SURFACE_OVERLAY) {
|
|
4788
5151
|
return flatFrag({
|
|
4789
5152
|
pointShadows: this.quality.pointShadows,
|
|
4790
5153
|
directionalShadows: this.quality.directionalShadows,
|
|
@@ -4797,6 +5160,8 @@ export class WebGL2Renderer {
|
|
|
4797
5160
|
surfaceEffects: this.litOn.SURFACE_EFFECTS,
|
|
4798
5161
|
driftLight: this.litOn.DRIFT_LIGHT,
|
|
4799
5162
|
physicalSpecular: this.litOn.PHYSICAL_SPECULAR,
|
|
5163
|
+
surfaceOverlay: overlay,
|
|
5164
|
+
reflectionMaps: this.litOn.REFLECTION_MAPS,
|
|
4800
5165
|
maxLights: budget.maxLights,
|
|
4801
5166
|
maxAreaLights: budget.maxAreaLights,
|
|
4802
5167
|
});
|
|
@@ -4813,10 +5178,10 @@ export class WebGL2Renderer {
|
|
|
4813
5178
|
const { gl } = this;
|
|
4814
5179
|
Object.assign(this.litOn, this.litWanted);
|
|
4815
5180
|
this.flatFragSource = this.flatFragAt(this.lightBudget);
|
|
4816
|
-
const recompile = (old, skinned, morphed, instanced, label, eight = false) => {
|
|
5181
|
+
const recompile = (old, skinned, morphed, instanced, label, eight = false, animated = false) => {
|
|
4817
5182
|
if (old === null)
|
|
4818
5183
|
return null;
|
|
4819
|
-
const vertex = flatVert({ skinned, morphed, instanced });
|
|
5184
|
+
const vertex = flatVert({ skinned, morphed, instanced, animated });
|
|
4820
5185
|
const program = compileProgram(gl, eight ? skinEight(vertex) : vertex, this.flatFragSource, label);
|
|
4821
5186
|
gl.deleteProgram(old);
|
|
4822
5187
|
return { program, uniforms: uniformLocations(gl, program, label) };
|
|
@@ -4844,6 +5209,9 @@ export class WebGL2Renderer {
|
|
|
4844
5209
|
const instanced = recompile(this.flatInstancedProgram, false, false, true, 'flat.instanced');
|
|
4845
5210
|
this.flatInstancedProgram = instanced?.program ?? null;
|
|
4846
5211
|
this.flatInstancedUniforms = instanced?.uniforms ?? null;
|
|
5212
|
+
const animated = recompile(this.flatAnimatedProgram, false, false, true, 'flat.animated', false, true);
|
|
5213
|
+
this.flatAnimatedProgram = animated?.program ?? null;
|
|
5214
|
+
this.flatAnimatedUniforms = animated?.uniforms ?? null;
|
|
4847
5215
|
for (const [key, entry] of this.clothFlat) {
|
|
4848
5216
|
const eight = key.startsWith('8');
|
|
4849
5217
|
const withMorph = key.endsWith('m');
|
|
@@ -4861,7 +5229,7 @@ export class WebGL2Renderer {
|
|
|
4861
5229
|
this.modelFlat.set(key, { program, uniforms: uniformLocations(gl, program, label) });
|
|
4862
5230
|
}
|
|
4863
5231
|
this.flatTargets.length = 0;
|
|
4864
|
-
for (const target of [plain, skinned, morphed, both, skinned8, both8, instanced]) {
|
|
5232
|
+
for (const target of [plain, skinned, morphed, both, skinned8, both8, instanced, animated]) {
|
|
4865
5233
|
if (target !== null)
|
|
4866
5234
|
this.flatTargets.push(target);
|
|
4867
5235
|
}
|
|
@@ -5468,10 +5836,20 @@ export class WebGL2Renderer {
|
|
|
5468
5836
|
* Set before `beginFrame` and held until changed, like `setSpeedRush`, which it sits
|
|
5469
5837
|
* beside for the same reason: both describe how the finished image is presented rather
|
|
5470
5838
|
* than anything in the world.
|
|
5839
|
+
*
|
|
5840
|
+
* `maxShare` is the longest smear, as a share of the frame (0.03 unless given, at most half): a
|
|
5841
|
+
* fast turn softens the frame rather than erasing it. **Each drawn object's own motion is
|
|
5842
|
+
* followed only under a reconstruction on WebGPU**, which draws the motion target it reads; here,
|
|
5843
|
+
* and on WebGPU without one, the camera's motion alone smears the frame, and a draw that names
|
|
5844
|
+
* where it was last frame is told so once.
|
|
5471
5845
|
*/
|
|
5472
|
-
setCameraMotionBlur(scale) {
|
|
5846
|
+
setCameraMotionBlur(scale, maxShare = MOTION_BLUR_MAX_UV) {
|
|
5473
5847
|
this.motionBlurScale = Math.min(Math.max(scale, 0), 1);
|
|
5848
|
+
this.motionBlurMax = clampMotionShare(maxShare);
|
|
5474
5849
|
}
|
|
5850
|
+
/** The longest smear, as a share of the frame. See `setCameraMotionBlur`. */
|
|
5851
|
+
motionBlurMax = MOTION_BLUR_MAX_UV;
|
|
5852
|
+
warnedObjectBlur = false;
|
|
5475
5853
|
/**
|
|
5476
5854
|
* The camera has cut: this frame is a new shot, not the next moment of the last one.
|
|
5477
5855
|
*
|
|
@@ -5671,6 +6049,42 @@ export class WebGL2Renderer {
|
|
|
5671
6049
|
? Math.max(exposure, 1e-3)
|
|
5672
6050
|
: this.quality.outputExposure;
|
|
5673
6051
|
}
|
|
6052
|
+
/**
|
|
6053
|
+
* The `filmic` curve this and every later frame is graded through, until it is set again: a
|
|
6054
|
+
* grading volume's five numbers, solved once here into the constants the resolve reads. Ignored
|
|
6055
|
+
* unless `outputTransform` is `filmic`. A number out of range is clamped into it and said once,
|
|
6056
|
+
* since a frame loop must not throw. See `filmicCurve.ts`.
|
|
6057
|
+
*/
|
|
6058
|
+
setFilmicCurve(curve) {
|
|
6059
|
+
if (resolveFilmicCurve(curve, this.filmConstants) && !this.warnedFilmClamped) {
|
|
6060
|
+
this.warnedFilmClamped = true;
|
|
6061
|
+
console.warn(FILMIC_CLAMPED);
|
|
6062
|
+
}
|
|
6063
|
+
}
|
|
6064
|
+
warnedFilmClamped = false;
|
|
6065
|
+
/**
|
|
6066
|
+
* The range the frame goes out in: `'high'` where the profile asked for `highDynamicRange`, the
|
|
6067
|
+
* frame holds light past white and the display shows it — on WebGPU — and `'standard'` otherwise.
|
|
6068
|
+
* **Always standard here**: this backend draws into a canvas of eight bits a channel, which has
|
|
6069
|
+
* nothing above white to show. Typed as the question rather than this backend's answer, because
|
|
6070
|
+
* the shared surface is derived from this class.
|
|
6071
|
+
*/
|
|
6072
|
+
displayRange = 'standard';
|
|
6073
|
+
/** Why it is that range, in words, for a game choosing its look and for a bug report. */
|
|
6074
|
+
get displayRangeReason() {
|
|
6075
|
+
return this.quality.highDynamicRange
|
|
6076
|
+
? 'WebGL2 draws into an eight-bit canvas, which has no range above white'
|
|
6077
|
+
: 'highDynamicRange was not asked for';
|
|
6078
|
+
}
|
|
6079
|
+
/**
|
|
6080
|
+
* How bright paper white and the display's peak are, in one unit, held until changed: their
|
|
6081
|
+
* ratio is how far above white the highlights may run where `displayRange` is `'high'`, and
|
|
6082
|
+
* nothing changes where it is standard, which is always here.
|
|
6083
|
+
*/
|
|
6084
|
+
setDisplayLuminance(paperWhite, peak) {
|
|
6085
|
+
void paperWhite;
|
|
6086
|
+
void peak;
|
|
6087
|
+
}
|
|
5674
6088
|
/**
|
|
5675
6089
|
* Composite a flat colour over the finished frame — for a cut dipping to white or to black.
|
|
5676
6090
|
*
|
|
@@ -5757,6 +6171,20 @@ export class WebGL2Renderer {
|
|
|
5757
6171
|
}
|
|
5758
6172
|
this.sceneTarget.setVignette(strength);
|
|
5759
6173
|
}
|
|
6174
|
+
/**
|
|
6175
|
+
* The lens's colour fringe, held until changed: `intensity` is a percentage, 0 is none, and red
|
|
6176
|
+
* and green are pulled toward the centre by their wavelength's distance from blue, growing from
|
|
6177
|
+
* `start` — a share of the half-frame, 0 the centre — to the whole of it at the edge. Applied to
|
|
6178
|
+
* scene light at the composite's first read. Needs `screenEffects`, and says so once rather than
|
|
6179
|
+
* doing nothing. See `shaders/fringe.ts`.
|
|
6180
|
+
*/
|
|
6181
|
+
setChromaticAberration(intensity, start = 0) {
|
|
6182
|
+
if (this.sceneTarget === null) {
|
|
6183
|
+
this.warnLookWithoutComposite(intensity);
|
|
6184
|
+
return;
|
|
6185
|
+
}
|
|
6186
|
+
this.sceneTarget.setChromaticAberration(intensity, start);
|
|
6187
|
+
}
|
|
5760
6188
|
/**
|
|
5761
6189
|
* Film grain, held until changed: an amplitude in display values (0 is none, 0.03 is a fine
|
|
5762
6190
|
* grain) and this frame's seed. **The seed is the caller's**, because the engine takes time from
|
|
@@ -5937,6 +6365,8 @@ export class WebGL2Renderer {
|
|
|
5937
6365
|
beginFrame(clearColor) {
|
|
5938
6366
|
if (this.contextLost)
|
|
5939
6367
|
return;
|
|
6368
|
+
/* One frame of the jitter's sequence however many times the frame binds its camera. */
|
|
6369
|
+
this.temporalHistory.nextFrame();
|
|
5940
6370
|
this.skinScattered = false;
|
|
5941
6371
|
this.skinScatter?.reset();
|
|
5942
6372
|
if (this.litAsked) {
|
|
@@ -6003,6 +6433,8 @@ export class WebGL2Renderer {
|
|
|
6003
6433
|
}
|
|
6004
6434
|
}
|
|
6005
6435
|
this.sceneTarget?.begin(this.canvas.width, this.canvas.height);
|
|
6436
|
+
/* The materials' maps on the scene's framebuffer, cleared and disabled: `reflectionKeeps`. */
|
|
6437
|
+
this.reflectionTargets?.begin(this.canvas.width, this.canvas.height);
|
|
6006
6438
|
gl.clearColor(clearColor[0], clearColor[1], clearColor[2], 1);
|
|
6007
6439
|
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
|
6008
6440
|
}
|
|
@@ -6052,7 +6484,7 @@ export class WebGL2Renderer {
|
|
|
6052
6484
|
motion = {
|
|
6053
6485
|
reprojection: this.reprojection,
|
|
6054
6486
|
strength: blur,
|
|
6055
|
-
max:
|
|
6487
|
+
max: this.motionBlurMax,
|
|
6056
6488
|
};
|
|
6057
6489
|
}
|
|
6058
6490
|
}
|
|
@@ -6132,7 +6564,11 @@ export class WebGL2Renderer {
|
|
|
6132
6564
|
: undefined;
|
|
6133
6565
|
/* The grade travels to the resolve only where the mesh pass gave it up. See `bindMeshPass`. */
|
|
6134
6566
|
const lateGrade = this.quality.hdrScene
|
|
6135
|
-
? {
|
|
6567
|
+
? {
|
|
6568
|
+
transform: OUTPUT_TRANSFORM_CODE[this.quality.outputTransform] ?? 0,
|
|
6569
|
+
exposure: this.exposure,
|
|
6570
|
+
film: this.filmConstants,
|
|
6571
|
+
}
|
|
6136
6572
|
: { transform: 0, exposure: 1 };
|
|
6137
6573
|
/*
|
|
6138
6574
|
* Depth of field, on the same ceiling-and-dial split as bloom above: `depthOfField` decides
|
|
@@ -6194,6 +6630,30 @@ export class WebGL2Renderer {
|
|
|
6194
6630
|
* drawn over a reflective floor rather than under it, and before the post chain so bloom and
|
|
6195
6631
|
* the grade see a reflection exactly as they would see any other part of the picture.
|
|
6196
6632
|
*/
|
|
6633
|
+
/*
|
|
6634
|
+
* **The frame's materials' reflections first**, on the same terms: every pixel the opaque lit
|
|
6635
|
+
* draws left a reflection for, marched and swapped for the probe's share. The maps leave the
|
|
6636
|
+
* scene's framebuffer before the resolve draws into it while sampling them.
|
|
6637
|
+
*/
|
|
6638
|
+
const reflectionTargets = this.reflectionTargets;
|
|
6639
|
+
const probeMap = reflectionTargets?.probeTexture ?? null;
|
|
6640
|
+
const tintMap = reflectionTargets?.tintTexture ?? null;
|
|
6641
|
+
if (reflectionTargets !== null &&
|
|
6642
|
+
this.frameReflections !== null &&
|
|
6643
|
+
probeMap !== null &&
|
|
6644
|
+
tintMap !== null &&
|
|
6645
|
+
this.frameViewProj !== null &&
|
|
6646
|
+
this.sceneTarget !== null) {
|
|
6647
|
+
const scene = this.sceneTarget.colorAttachment();
|
|
6648
|
+
const depth = this.sceneTarget.snapshotDepth(true);
|
|
6649
|
+
if (scene !== null && depth !== null) {
|
|
6650
|
+
this.ssr ??= new SsrPass(this.gl, this.sceneTarget.keepsRange);
|
|
6651
|
+
this.ssr.traceMaterials(scene, depth, probeMap, tintMap, this.frameViewProj, this.frameEye, this.canvas.width, this.canvas.height, REFLECTION_EDGE_FADE, this.frameReflections);
|
|
6652
|
+
this.sceneTarget.bind();
|
|
6653
|
+
reflectionTargets.detach();
|
|
6654
|
+
this.ssr.resolveMaterials(probeMap, tintMap, this.canvas.width, this.canvas.height, this.frameReflections);
|
|
6655
|
+
}
|
|
6656
|
+
}
|
|
6197
6657
|
if (this.reflectionQueue.length > 0 && this.frameViewProj !== null) {
|
|
6198
6658
|
const scene = this.sceneTarget?.colorAttachment() ?? null;
|
|
6199
6659
|
const depth = this.sceneTarget?.snapshotDepth(true) ?? null;
|
|
@@ -6531,9 +6991,17 @@ export class WebGL2Renderer {
|
|
|
6531
6991
|
this.materialHasMaps = false;
|
|
6532
6992
|
this.currentMaterial = null;
|
|
6533
6993
|
this.currentSurfaceTexture = null;
|
|
6994
|
+
/* Nor an overlay: the atlas's unit is given the empty texture with the refraction copy's. */
|
|
6995
|
+
this.overlayFloats.fill(0);
|
|
6996
|
+
this.overlayMaps = null;
|
|
6997
|
+
this.overlayTexture = null;
|
|
6534
6998
|
/* Nor an ambient of its own: the frame's, until a draw asks. See `setAmbientSH`. */
|
|
6535
6999
|
this.ambientSH.fill(0);
|
|
6536
7000
|
/* No reflection and an environment at its own brightness; the emission the world asks for. */
|
|
7001
|
+
this.passReflectivity = 0;
|
|
7002
|
+
this.passEnvironmentGain = 1;
|
|
7003
|
+
this.materialReflectivity = null;
|
|
7004
|
+
this.materialEnvironmentGain = null;
|
|
6537
7005
|
this.environmentDials[1] = 0;
|
|
6538
7006
|
this.environmentDials[2] = 1;
|
|
6539
7007
|
this.emission[0] = env.emissiveGain;
|
|
@@ -6656,12 +7124,14 @@ export class WebGL2Renderer {
|
|
|
6656
7124
|
* unless a draw sets the strength above zero.
|
|
6657
7125
|
*/
|
|
6658
7126
|
gl.activeTexture(gl.TEXTURE0 + REFRACT_SCENE_TEXTURE_UNIT);
|
|
6659
|
-
gl.bindTexture(gl.TEXTURE_2D, this.emptyTexture2D);
|
|
7127
|
+
gl.bindTexture(gl.TEXTURE_2D, this.overlayTexture ?? this.emptyTexture2D);
|
|
6660
7128
|
gl.uniform1i(u['uRefractScene'] ?? null, REFRACT_SCENE_TEXTURE_UNIT);
|
|
7129
|
+
/* The draws' overlay, none at a pass's start, and its atlas above: see `setSurfaceOverlay`. */
|
|
7130
|
+
gl.uniform4fv(u['uOverlay'] ?? null, this.overlayFloats);
|
|
6661
7131
|
/* Neither refracting nor glass, which is every draw that does not say otherwise. See
|
|
6662
7132
|
`bindSeeThrough` for the four numbers in the one vector. */
|
|
6663
7133
|
gl.uniform4f(u['uSeeThrough'] ?? null, 0, 0, 0, 0);
|
|
6664
|
-
gl.
|
|
7134
|
+
gl.uniform4f(u['uRefractTint'] ?? null, 1, 1, 1, 1);
|
|
6665
7135
|
gl.uniform3fv(u['uGlassTint'] ?? null, WHITE_TINT);
|
|
6666
7136
|
gl.uniform3fv(u['uDirectionalDir'] ?? null, env.directionalDir);
|
|
6667
7137
|
gl.uniform3fv(u['uDirectionalColor'] ?? null, env.directionalColor);
|
|
@@ -6775,7 +7245,10 @@ export class WebGL2Renderer {
|
|
|
6775
7245
|
gl.uniform1i(u['uPeeledShadowEnabled'] ?? null, this.peelFilled ? 1 : 0);
|
|
6776
7246
|
gl.uniform1f(u['uShadowMaxSlope'] ?? null, this.quality.directionalShadowMaxSlope);
|
|
6777
7247
|
gl.uniform1i(u['uShadowFilterTaps'] ?? null, this.quality.shadowFilterTaps);
|
|
6778
|
-
|
|
7248
|
+
/* The near corner, and in its w the frame the cutout dither moves with. See `uHighlightMin`.
|
|
7249
|
+
An environment written by hand may leave the box out, as the far corner says below. */
|
|
7250
|
+
const min = env.highlightMin;
|
|
7251
|
+
gl.uniform4f(u['uHighlightMin'] ?? null, min?.[0] ?? 0, min?.[1] ?? 0, min?.[2] ?? 0, this.cutoutFrame);
|
|
6779
7252
|
/* The far corner, and the gain in its w. See `uHighlightMax`. */
|
|
6780
7253
|
/* An environment written by hand may leave the box out; then there is none, as before. */
|
|
6781
7254
|
const max = env.highlightMax ?? NO_HIGHLIGHT;
|
|
@@ -7036,9 +7509,16 @@ export class WebGL2Renderer {
|
|
|
7036
7509
|
* here, so there is no motion target for this to be written into, and a mover passed here is
|
|
7037
7510
|
* neither read nor advanced.
|
|
7038
7511
|
*/
|
|
7039
|
-
|
|
7512
|
+
previousModel = null) {
|
|
7040
7513
|
if (this.contextLost)
|
|
7041
7514
|
return;
|
|
7515
|
+
/* A mover's own motion is not blurred here: see `setCameraMotionBlur`. Said once. */
|
|
7516
|
+
if (previousModel !== null &&
|
|
7517
|
+
!this.warnedObjectBlur &&
|
|
7518
|
+
this.quality.cameraMotionBlur * this.motionBlurScale > 0) {
|
|
7519
|
+
this.warnedObjectBlur = true;
|
|
7520
|
+
console.warn(OBJECT_BLUR_UNKEPT);
|
|
7521
|
+
}
|
|
7042
7522
|
/* Geometry that has not all arrived is not drawn, nor a mesh with none. See `Mesh.complete`
|
|
7043
7523
|
and `Mesh.indexCount`. */
|
|
7044
7524
|
if (!mesh.complete || mesh.indexCount === 0)
|
|
@@ -7071,7 +7551,13 @@ export class WebGL2Renderer {
|
|
|
7071
7551
|
* light and its colour, each into the blur's own target.
|
|
7072
7552
|
*/
|
|
7073
7553
|
const splits = this.skinSplits();
|
|
7554
|
+
/* An opaque lit draw into the frame writes its material's reflection too: see `reflectionKeeps`. */
|
|
7555
|
+
const keeps = this.reflectionKeeps();
|
|
7556
|
+
if (keeps)
|
|
7557
|
+
this.reflectionTargets?.on();
|
|
7074
7558
|
this.drawMeshHalf(mesh, model, depthLayer, tint, splits ? 'scene' : 'whole');
|
|
7559
|
+
if (keeps)
|
|
7560
|
+
this.reflectionTargets?.off();
|
|
7075
7561
|
if (splits && this.skinScatter !== null) {
|
|
7076
7562
|
this.skinScatter.begin();
|
|
7077
7563
|
this.drawMeshHalf(mesh, model, depthLayer, tint, 'diffuse');
|
|
@@ -7085,6 +7571,18 @@ export class WebGL2Renderer {
|
|
|
7085
7571
|
* skin yet, this is the frame's own picture — not a mirror's or a probe's, nor an overlay after
|
|
7086
7572
|
* the frame — and the blur's target can be had.
|
|
7087
7573
|
*/
|
|
7574
|
+
/**
|
|
7575
|
+
* Whether a draw now is the frame's own, opaque and lit, and so writes the two maps beside its
|
|
7576
|
+
* colour: not a mirror's, a probe's or a capture's, whose framebuffers have no maps, and not after
|
|
7577
|
+
* the frame. See `reflectionSurfaceTargets.ts`.
|
|
7578
|
+
*/
|
|
7579
|
+
reflectionKeeps() {
|
|
7580
|
+
return (this.reflectionTargets !== null &&
|
|
7581
|
+
!this.reflectionPassActive &&
|
|
7582
|
+
!this.probePassActive &&
|
|
7583
|
+
!this.capturePassActive &&
|
|
7584
|
+
!this.framePresented);
|
|
7585
|
+
}
|
|
7088
7586
|
skinSplits() {
|
|
7089
7587
|
const skin = this.skinScatter;
|
|
7090
7588
|
const scene = this.sceneTarget;
|
|
@@ -7111,12 +7609,18 @@ export class WebGL2Renderer {
|
|
|
7111
7609
|
const projection = this.frameProjection;
|
|
7112
7610
|
if (skin === null || scene === null || this.skinScattered)
|
|
7113
7611
|
return;
|
|
7612
|
+
/*
|
|
7613
|
+
* **Not the frame's picture, so not the frame's moment**: a blended draw in a mirror, a probe
|
|
7614
|
+
* bake or a scene capture lands over none of the frame's skin. Latching there switched the
|
|
7615
|
+
* spread off for the frame, so a capture with a glowing sign in it left every face drawn after
|
|
7616
|
+
* it flat. `scripts/capture-check.mjs`.
|
|
7617
|
+
*/
|
|
7618
|
+
if (this.reflectionPassActive || this.probePassActive || this.capturePassActive)
|
|
7619
|
+
return;
|
|
7114
7620
|
/* Latched whether or not skin was drawn: a skin drawn after this point is the whole surface. */
|
|
7115
7621
|
this.skinScattered = true;
|
|
7116
7622
|
if (!skin.pending)
|
|
7117
7623
|
return;
|
|
7118
|
-
if (this.reflectionPassActive || this.probePassActive || this.capturePassActive)
|
|
7119
|
-
return;
|
|
7120
7624
|
if (projection === null)
|
|
7121
7625
|
return;
|
|
7122
7626
|
const depth = scene.snapshotDepth(true);
|
|
@@ -8069,6 +8573,12 @@ function litVertexOf(variant) {
|
|
|
8069
8573
|
}
|
|
8070
8574
|
const skinned = variant.startsWith('skinned') || variant.startsWith('both');
|
|
8071
8575
|
const morphed = variant === 'morphed' || variant.startsWith('both');
|
|
8072
|
-
const
|
|
8576
|
+
const animated = variant === 'animated';
|
|
8577
|
+
const vertex = flatVert({
|
|
8578
|
+
skinned,
|
|
8579
|
+
morphed,
|
|
8580
|
+
instanced: variant === 'instanced' || animated,
|
|
8581
|
+
animated,
|
|
8582
|
+
});
|
|
8073
8583
|
return variant.endsWith('8') ? skinEight(vertex) : vertex;
|
|
8074
8584
|
}
|