@driftengine/core 4.8.5 → 4.8.7
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 +4392 -4250
- package/README.md +1 -1
- package/dist/index.d.ts +12 -4
- package/dist/index.js +12 -2
- package/dist/render/ambientHarmonics.d.ts +46 -0
- package/dist/render/ambientHarmonics.js +80 -0
- package/dist/render/anisotropicLobe.d.ts +10 -0
- package/dist/render/anisotropicLobe.js +19 -0
- package/dist/render/areaLights.d.ts +54 -0
- package/dist/render/areaLights.js +55 -0
- package/dist/render/backend/api.d.ts +43 -2
- package/dist/render/backend/webgl2/instanced.d.ts +2 -0
- package/dist/render/backend/webgl2/instanced.js +11 -2
- package/dist/render/backend/webgl2/renderer.d.ts +120 -11
- package/dist/render/backend/webgl2/renderer.js +356 -53
- package/dist/render/backend/webgl2/sceneCaptureTarget.d.ts +29 -0
- package/dist/render/backend/webgl2/sceneCaptureTarget.js +96 -0
- package/dist/render/backend/webgl2/skinScatter.d.ts +14 -0
- package/dist/render/backend/webgl2/skinScatter.js +88 -26
- package/dist/render/backend/webgpu/buffers.js +2 -1
- package/dist/render/backend/webgpu/clusterBinner.js +3 -2
- package/dist/render/backend/webgpu/flatPass.d.ts +7 -5
- package/dist/render/backend/webgpu/flatPass.js +15 -13
- package/dist/render/backend/webgpu/particlePass.d.ts +37 -4
- package/dist/render/backend/webgpu/particlePass.js +81 -12
- package/dist/render/backend/webgpu/postPass.d.ts +6 -1
- package/dist/render/backend/webgpu/postPass.js +7 -2
- package/dist/render/backend/webgpu/renderer.d.ts +93 -8
- package/dist/render/backend/webgpu/renderer.js +483 -93
- package/dist/render/backend/webgpu/sceneCaptureTarget.d.ts +24 -0
- package/dist/render/backend/webgpu/sceneCaptureTarget.js +59 -0
- package/dist/render/backend/webgpu/shaders/clusterBin.wgsl.d.ts +1 -1
- package/dist/render/backend/webgpu/shaders/clusterBin.wgsl.js +41 -1
- package/dist/render/backend/webgpu/skinPaletteRing.d.ts +40 -28
- package/dist/render/backend/webgpu/skinPaletteRing.js +69 -32
- package/dist/render/backend/webgpu/skinScatterPass.d.ts +10 -2
- package/dist/render/backend/webgpu/skinScatterPass.js +59 -9
- package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +5 -1
- package/dist/render/backend/webgpu/surfaceTexturePass.js +45 -5
- package/dist/render/backend/webgpu/waterPass.d.ts +1 -1
- package/dist/render/bloomChain.d.ts +44 -0
- package/dist/render/bloomChain.js +28 -0
- package/dist/render/bloomPass.d.ts +3 -1
- package/dist/render/bloomPass.js +16 -2
- package/dist/render/clusteredLights.d.ts +78 -6
- package/dist/render/clusteredLights.js +214 -13
- package/dist/render/driftLight/bake.d.ts +2 -0
- package/dist/render/driftLight/bake.js +1 -1
- package/dist/render/driftLight/falloff.d.ts +4 -2
- package/dist/render/driftLight/falloff.js +8 -2
- package/dist/render/fixtureAtlas.js +1 -1
- package/dist/render/gi/bounceLights.d.ts +3 -2
- package/dist/render/gi/bounceLights.js +8 -3
- package/dist/render/instanceCull.js +12 -0
- package/dist/render/instances.d.ts +47 -11
- package/dist/render/instances.js +24 -9
- package/dist/render/lightBudget.d.ts +38 -2
- package/dist/render/lightBudget.js +48 -5
- package/dist/render/lightmap.d.ts +116 -0
- package/dist/render/lightmap.js +140 -0
- package/dist/render/mesh.js +2 -1
- package/dist/render/particleBatch.d.ts +59 -3
- package/dist/render/particleBatch.js +53 -2
- package/dist/render/particleMaterial.d.ts +6 -1
- package/dist/render/particleMaterial.js +10 -2
- package/dist/render/particlePool.d.ts +32 -0
- package/dist/render/particlePool.js +9 -0
- package/dist/render/particleSort.d.ts +34 -0
- package/dist/render/particleSort.js +96 -0
- package/dist/render/pointLightSelection.d.ts +13 -0
- package/dist/render/pointLightSelection.js +3 -1
- package/dist/render/renderQuality.d.ts +8 -7
- package/dist/render/rendererHarness.js +3 -0
- package/dist/render/rgb9e5.d.ts +22 -0
- package/dist/render/rgb9e5.js +47 -0
- package/dist/render/sceneCapture.d.ts +29 -0
- package/dist/render/sceneCapture.js +36 -0
- package/dist/render/sceneTarget.d.ts +5 -0
- package/dist/render/sceneTarget.js +8 -1
- package/dist/render/screenSpaceReflection.d.ts +27 -1
- package/dist/render/screenSpaceReflection.js +9 -0
- package/dist/render/shaders/bloom.d.ts +8 -4
- package/dist/render/shaders/bloom.js +20 -3
- package/dist/render/shaders/bolt.d.ts +1 -1
- package/dist/render/shaders/bolt.js +7 -1
- package/dist/render/shaders/depth.js +11 -0
- package/dist/render/shaders/flat/anisotropic.d.ts +1 -1
- package/dist/render/shaders/flat/anisotropic.js +37 -3
- package/dist/render/shaders/flat/areaLight.d.ts +10 -0
- package/dist/render/shaders/flat/areaLight.js +142 -0
- package/dist/render/shaders/flat/glassTint.d.ts +3 -2
- package/dist/render/shaders/flat/glassTint.js +3 -1
- package/dist/render/shaders/flat/index.d.ts +5 -0
- package/dist/render/shaders/flat/index.js +46 -6
- package/dist/render/shaders/flat/lobes.d.ts +6 -0
- package/dist/render/shaders/flat/lobes.js +49 -43
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +158 -121
- package/dist/render/shaders/flat/models.d.ts +21 -6
- package/dist/render/shaders/flat/models.js +161 -17
- package/dist/render/shaders/flat/normalMap.d.ts +10 -0
- package/dist/render/shaders/flat/normalMap.js +25 -0
- package/dist/render/shaders/flat/preamble.js +84 -40
- package/dist/render/shaders/flat/skin.js +6 -5
- package/dist/render/shaders/generated/bloom.wgsl.d.ts +13 -3
- package/dist/render/shaders/generated/bloom.wgsl.js +13 -3
- 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/depth.wgsl.d.ts +4 -4
- package/dist/render/shaders/generated/depth.wgsl.js +4 -4
- package/dist/render/shaders/generated/flat.wgsl.d.ts +1647 -2044
- package/dist/render/shaders/generated/flat.wgsl.js +1685 -2082
- package/dist/render/shaders/generated/particle.wgsl.d.ts +115 -1
- package/dist/render/shaders/generated/particle.wgsl.js +115 -1
- package/dist/render/shaders/generated/skinBlur.wgsl.d.ts +12 -2
- package/dist/render/shaders/generated/skinBlur.wgsl.js +12 -2
- package/dist/render/shaders/generated/ssrTrace.wgsl.d.ts +11 -6
- package/dist/render/shaders/generated/ssrTrace.wgsl.js +11 -6
- package/dist/render/shaders/generated/water.wgsl.d.ts +23 -16
- package/dist/render/shaders/generated/water.wgsl.js +23 -16
- package/dist/render/shaders/gi/probeBake.wgsl.js +9 -5
- package/dist/render/shaders/particle.d.ts +17 -1
- package/dist/render/shaders/particle.js +133 -1
- package/dist/render/shaders/recon/motion.wgsl.d.ts +1 -1
- package/dist/render/shaders/recon/motion.wgsl.js +13 -2
- package/dist/render/shaders/skinBlur.js +25 -3
- package/dist/render/shaders/ssrTrace.js +20 -1
- package/dist/render/shaders/water.d.ts +1 -1
- package/dist/render/shaders/water.js +12 -3
- package/dist/render/skinBlur.d.ts +8 -5
- package/dist/render/skinBlur.js +8 -5
- package/dist/render/skinScatter.d.ts +3 -1
- package/dist/render/skinScatter.js +3 -1
- package/dist/render/ssrPass.js +1 -0
- package/dist/render/surfaceModel.d.ts +49 -8
- package/dist/render/surfaceModel.js +54 -8
- package/dist/render/surfaceTexture.d.ts +38 -1
- package/dist/render/surfaceTexture.js +37 -2
- package/package.json +6 -6
- package/src/index.ts +26 -3
- package/src/render/ambientHarmonics.ts +92 -0
- package/src/render/anisotropicLobe.ts +29 -0
- package/src/render/areaLights.ts +97 -0
- package/src/render/backend/api.ts +61 -1
- package/src/render/backend/webgl2/instanced.ts +12 -2
- package/src/render/backend/webgl2/renderer.ts +470 -59
- package/src/render/backend/webgl2/sceneCaptureTarget.ts +130 -0
- package/src/render/backend/webgl2/skinScatter.ts +120 -43
- package/src/render/backend/webgpu/buffers.ts +2 -1
- package/src/render/backend/webgpu/clusterBinner.ts +3 -1
- package/src/render/backend/webgpu/flatPass.ts +18 -13
- package/src/render/backend/webgpu/particlePass.ts +119 -12
- package/src/render/backend/webgpu/postPass.ts +7 -2
- package/src/render/backend/webgpu/renderer.ts +589 -113
- package/src/render/backend/webgpu/sceneCaptureTarget.ts +63 -0
- package/src/render/backend/webgpu/shaders/clusterBin.wgsl.ts +41 -1
- package/src/render/backend/webgpu/skinPaletteRing.ts +65 -33
- package/src/render/backend/webgpu/skinScatterPass.ts +59 -9
- package/src/render/backend/webgpu/surfaceTexturePass.ts +57 -6
- package/src/render/bloomChain.ts +69 -0
- package/src/render/bloomPass.ts +26 -2
- package/src/render/clusteredLights.ts +259 -12
- package/src/render/driftLight/bake.ts +3 -1
- package/src/render/driftLight/falloff.ts +13 -2
- package/src/render/fixtureAtlas.ts +1 -1
- package/src/render/gi/bounceLights.ts +12 -3
- package/src/render/instanceCull.ts +9 -0
- package/src/render/instances.ts +61 -12
- package/src/render/lightBudget.ts +88 -4
- package/src/render/lightmap.ts +243 -0
- package/src/render/mesh.ts +2 -1
- package/src/render/particleBatch.ts +95 -4
- package/src/render/particleMaterial.ts +11 -1
- package/src/render/particlePool.ts +36 -0
- package/src/render/particleSort.ts +133 -0
- package/src/render/pointLightSelection.ts +20 -1
- package/src/render/renderQuality.ts +8 -7
- package/src/render/rendererHarness.ts +3 -0
- package/src/render/rgb9e5.ts +52 -0
- package/src/render/sceneCapture.ts +56 -0
- package/src/render/sceneTarget.ts +18 -1
- package/src/render/screenSpaceReflection.ts +33 -1
- package/src/render/shaders/bloom.ts +20 -3
- package/src/render/shaders/bolt.ts +7 -1
- package/src/render/shaders/depth.ts +11 -0
- package/src/render/shaders/flat/anisotropic.ts +37 -3
- package/src/render/shaders/flat/areaLight.ts +142 -0
- package/src/render/shaders/flat/glassTint.ts +9 -2
- package/src/render/shaders/flat/index.ts +51 -6
- package/src/render/shaders/flat/lobes.ts +50 -43
- package/src/render/shaders/flat/main.ts +158 -121
- package/src/render/shaders/flat/models.ts +170 -21
- package/src/render/shaders/flat/normalMap.ts +25 -0
- package/src/render/shaders/flat/preamble.ts +84 -40
- package/src/render/shaders/flat/skin.ts +6 -5
- package/src/render/shaders/generated/bloom.wgsl.ts +13 -3
- package/src/render/shaders/generated/bolt.wgsl.ts +1 -1
- package/src/render/shaders/generated/depth.wgsl.ts +4 -4
- package/src/render/shaders/generated/flat.wgsl.ts +1685 -2082
- package/src/render/shaders/generated/particle.wgsl.ts +116 -1
- package/src/render/shaders/generated/skinBlur.wgsl.ts +12 -2
- package/src/render/shaders/generated/ssrTrace.wgsl.ts +11 -6
- package/src/render/shaders/generated/water.wgsl.ts +23 -16
- package/src/render/shaders/gi/probeBake.wgsl.ts +9 -5
- package/src/render/shaders/particle.ts +134 -1
- package/src/render/shaders/recon/motion.wgsl.ts +13 -2
- package/src/render/shaders/skinBlur.ts +25 -3
- package/src/render/shaders/ssrTrace.ts +20 -1
- package/src/render/shaders/water.ts +12 -3
- package/src/render/skinBlur.ts +8 -5
- package/src/render/skinScatter.ts +3 -1
- package/src/render/ssrPass.ts +1 -0
- package/src/render/surfaceModel.ts +82 -10
- package/src/render/surfaceTexture.ts +101 -3
|
@@ -63,7 +63,9 @@ import {
|
|
|
63
63
|
clusteredMode,
|
|
64
64
|
createClusterLightSet,
|
|
65
65
|
createClusterTable,
|
|
66
|
+
clusteredLightTotal,
|
|
66
67
|
fillClusterLightSet,
|
|
68
|
+
lightChannelsOf,
|
|
67
69
|
} from '../../clusteredLights.ts';
|
|
68
70
|
import type {
|
|
69
71
|
PassContext,
|
|
@@ -88,7 +90,14 @@ import { FILM_FRAG, FILM_VERT } from '../../shaders/film.ts';
|
|
|
88
90
|
import { SceneTarget } from '../../sceneTarget.ts';
|
|
89
91
|
import type { ColourGradeLut } from '../../colourGrade.ts';
|
|
90
92
|
import { FILM_LOOK_WITHOUT_COMPOSITE } from '../../shaders/filmLook.ts';
|
|
91
|
-
import {
|
|
93
|
+
import { AMBIENT_SH_FLOATS, packAmbientSH } from '../../ambientHarmonics.ts';
|
|
94
|
+
import {
|
|
95
|
+
bloomProfileWarning,
|
|
96
|
+
bloomThresholdOf,
|
|
97
|
+
defaultBloomResponse,
|
|
98
|
+
resolveBloomResponse,
|
|
99
|
+
type BloomResponse,
|
|
100
|
+
} from '../../bloomChain.ts';
|
|
92
101
|
import {
|
|
93
102
|
createEmptyTexture2D,
|
|
94
103
|
createEmptyTexture2DArray,
|
|
@@ -127,9 +136,21 @@ import {
|
|
|
127
136
|
MORPH_DELTA_TEXTURE_UNIT,
|
|
128
137
|
SKIN_PALETTE_TEXTURE_UNIT,
|
|
129
138
|
} from '../../lightBudget.ts';
|
|
130
|
-
import {
|
|
139
|
+
import {
|
|
140
|
+
MODEL_PARAM_FLOATS,
|
|
141
|
+
eyeAxisInWorld,
|
|
142
|
+
packModel,
|
|
143
|
+
skinProfileDistance,
|
|
144
|
+
} from '../../surfaceModel.ts';
|
|
131
145
|
import type { SurfaceModelKind } from '../../surfaceModel.ts';
|
|
132
|
-
import { modelBound, skinHalfBound } from '../../shaders/flat/models.ts';
|
|
146
|
+
import { lightmapRegionsBound, modelBound, skinHalfBound } from '../../shaders/flat/models.ts';
|
|
147
|
+
import {
|
|
148
|
+
lightmapTexels,
|
|
149
|
+
warnRegionsWithoutPage,
|
|
150
|
+
warnUnpagedLightmap,
|
|
151
|
+
withLightmapUvs,
|
|
152
|
+
} from '../../lightmap.ts';
|
|
153
|
+
import type { LightmapPage } from '../../lightmap.ts';
|
|
133
154
|
import type { SkinHalf } from '../../shaders/flat/models.ts';
|
|
134
155
|
import { GlSkinScatter } from './skinScatter.ts';
|
|
135
156
|
import {
|
|
@@ -224,6 +245,7 @@ import {
|
|
|
224
245
|
bindAreaLights,
|
|
225
246
|
bindAreaShadows,
|
|
226
247
|
openCones,
|
|
248
|
+
PARTICLE_DEPTH_UNIT,
|
|
227
249
|
} from '../../lightBudget.ts';
|
|
228
250
|
import { resolveRenderQuality } from '../../renderQuality.ts';
|
|
229
251
|
import type { RenderQuality, RenderQualityOptions } from '../../renderQuality.ts';
|
|
@@ -234,6 +256,8 @@ import type { IncrementalMesh } from '../../mesh.ts';
|
|
|
234
256
|
import type { MeshData } from '../../mesh.ts';
|
|
235
257
|
import type { MeshOptions } from '../api.ts';
|
|
236
258
|
import { SurfaceTexture } from '../../surfaceTexture.ts';
|
|
259
|
+
import { sceneCaptureTexels } from '../../sceneCapture.ts';
|
|
260
|
+
import { GlSceneCaptureTarget } from './sceneCaptureTarget.ts';
|
|
237
261
|
import { glCompressedFormats } from '../../glCompressed.ts';
|
|
238
262
|
import type { CompressedTextureFormat, SurfaceSource } from '../../compressedSource.ts';
|
|
239
263
|
import type { SurfaceMaterial, SurfaceTextureOptions } from '../../surfaceTexture.ts';
|
|
@@ -257,7 +281,7 @@ import { PlanarReflection } from '../../planarReflection.ts';
|
|
|
257
281
|
import { InstancedMesh } from '../../instancedMesh.ts';
|
|
258
282
|
import { ParticleBatch } from '../../particleBatch.ts';
|
|
259
283
|
import type { ParticleBatchOptions } from '../../particleBatch.ts';
|
|
260
|
-
import type { ParticleInstances } from '../../particlePool.ts';
|
|
284
|
+
import type { DeviceParticles, ParticleInstances } from '../../particlePool.ts';
|
|
261
285
|
import { BoltBatch } from '../../boltBatch.ts';
|
|
262
286
|
import type { BoltSegments } from '../../boltPool.ts';
|
|
263
287
|
import { LineBatch } from '../../lineBatch.ts';
|
|
@@ -418,6 +442,11 @@ export interface Environment extends Atmosphere {
|
|
|
418
442
|
lightDirections: Float32Array;
|
|
419
443
|
/** Two per active light: the cosine of the inner cone angle, then of the outer. */
|
|
420
444
|
lightConeCos: Float32Array;
|
|
445
|
+
/**
|
|
446
|
+
* One per active light: its own falloff exponent, or 0 for the frame's. Optional: absent is 0 for
|
|
447
|
+
* every light. See `PointLightSource.falloffExponent`.
|
|
448
|
+
*/
|
|
449
|
+
lightFalloffExponents?: Float32Array;
|
|
421
450
|
/** One per active light: a row of the photometric atlas, or −1 for none. */
|
|
422
451
|
lightIesProfiles: Float32Array;
|
|
423
452
|
/**
|
|
@@ -430,6 +459,12 @@ export interface Environment extends Atmosphere {
|
|
|
430
459
|
lightIesAxes: Float32Array;
|
|
431
460
|
/** One per active light: a tile of the cookie atlas, or −1 for none. */
|
|
432
461
|
lightCookies: Float32Array;
|
|
462
|
+
/**
|
|
463
|
+
* One per active light: the lighting channels it lights, a mask from 1 to 255. Absent, short, or
|
|
464
|
+
* a light at 1 is the default channel every surface is on too. See `PointLightSet.lightChannels`
|
|
465
|
+
* and `SurfaceMaterial.lightChannels`.
|
|
466
|
+
*/
|
|
467
|
+
lightChannels?: Float32Array;
|
|
433
468
|
/**
|
|
434
469
|
* The rectangular emitters this frame, or null for none.
|
|
435
470
|
*
|
|
@@ -508,6 +543,8 @@ export function createEnvironment(overrides: Partial<Environment> = {}): Environ
|
|
|
508
543
|
lightIesAxes: new Float32Array(MAX_POINT_LIGHTS * 3),
|
|
509
544
|
/* −1, because tile 0 is a real cookie and no light here has asked for one. */
|
|
510
545
|
lightCookies: new Float32Array(MAX_POINT_LIGHTS).fill(-1),
|
|
546
|
+
/* Channel 1, the default every surface is on, so every light lights everything. */
|
|
547
|
+
lightChannels: new Float32Array(MAX_POINT_LIGHTS).fill(1),
|
|
511
548
|
/* None, so the shader's loop breaks before it reads anything. */
|
|
512
549
|
areaLights: null,
|
|
513
550
|
activeLightWorldIndices: new Int32Array(MAX_POINT_LIGHTS).fill(-1),
|
|
@@ -618,6 +655,9 @@ export interface SkyColors {
|
|
|
618
655
|
export { MAX_DEPTH_LAYER } from '../../depthConvention.ts';
|
|
619
656
|
|
|
620
657
|
/** The identity tint, handed back after any draw that asked for one. See `drawMesh`. */
|
|
658
|
+
/** The far corner of no highlight box: what an environment that names none reads. */
|
|
659
|
+
const NO_HIGHLIGHT: readonly number[] = [0, 0, 0];
|
|
660
|
+
|
|
621
661
|
const WHITE_TINT = new Float32Array([1, 1, 1]);
|
|
622
662
|
/** The lit stage's clip plane when there is no mirror: zero, which clips nothing. */
|
|
623
663
|
const NO_CLIP_PLANE = new Float32Array(4);
|
|
@@ -1364,6 +1404,23 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
1364
1404
|
private motionBlurScale = 1;
|
|
1365
1405
|
/** How much of the bloom ceiling this frame takes. See `setBloom`. */
|
|
1366
1406
|
private bloomScale = 1;
|
|
1407
|
+
/** The draw's own ambient, as `packAmbientSH` writes it; zeros are the frame's. */
|
|
1408
|
+
private readonly ambientSH = new Float32Array(AMBIENT_SH_FLOATS);
|
|
1409
|
+
/** Scratch for a sprite's depth conversion, so a particle draw allocates nothing. */
|
|
1410
|
+
private readonly spriteInverseProjection = mat4.create();
|
|
1411
|
+
/** Said once: a sprite asked for a soft edge where no depth copy could be had. */
|
|
1412
|
+
private warnedSpriteDepth = false;
|
|
1413
|
+
/** Said once: more rectangles than the fixed arm shades, and no froxel table for the rest. */
|
|
1414
|
+
private warnedAreaOverflow = false;
|
|
1415
|
+
/**
|
|
1416
|
+
* `uEnvironmentDials` as it stands: which chain the probe holds, the reflectivity, the gain. One
|
|
1417
|
+
* vector on the device, so written whole, and kept here so a setter moving one keeps the others.
|
|
1418
|
+
*/
|
|
1419
|
+
private readonly environmentDials = new Float32Array(3);
|
|
1420
|
+
/** `uEmission` as it stands: the emissive gain and the night factor. */
|
|
1421
|
+
private readonly emission = new Float32Array([1, 0]);
|
|
1422
|
+
/** The ramp and each level's tint, as `resolveBloomResponse` writes them; the subtraction and white. */
|
|
1423
|
+
private readonly bloomResponse = defaultBloomResponse();
|
|
1367
1424
|
/** In scene units, once `setBloom` has moved it; null reads the profile's. */
|
|
1368
1425
|
private bloomThresholdSet: number | null = null;
|
|
1369
1426
|
/**
|
|
@@ -1576,6 +1633,15 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
1576
1633
|
private warnedEnvironmentAmbient = false;
|
|
1577
1634
|
/** Whether a probe is being baked, so the passes that would recurse into one can tell. */
|
|
1578
1635
|
private probePassActive = false;
|
|
1636
|
+
/**
|
|
1637
|
+
* Whether a scene capture is being drawn (`captureScene`): an off-screen pass like a bake, so the
|
|
1638
|
+
* frame's own view, its refraction copy and skin's spread are left alone, but lit as the frame is.
|
|
1639
|
+
*/
|
|
1640
|
+
private capturePassActive = false;
|
|
1641
|
+
/** Each capture's framebuffer, beside the texture a material binds. See `createSceneCapture`. */
|
|
1642
|
+
private readonly captureTargets = new WeakMap<SurfaceTexture, GlSceneCaptureTarget>();
|
|
1643
|
+
/** Said once, where a driver refuses to draw into a capture. */
|
|
1644
|
+
private warnedCapture = false;
|
|
1579
1645
|
/** Whether the bake under way lights its faces by the grid. `ProbeBakeOptions.bounce`. */
|
|
1580
1646
|
private probeBounce = false;
|
|
1581
1647
|
/** The clip-control extension, or null where this context does not offer it. */
|
|
@@ -1647,12 +1713,14 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
1647
1713
|
* the camera's matrix unchanged and multiplies nothing.
|
|
1648
1714
|
*/
|
|
1649
1715
|
private viewProjFor(camera: { readonly viewProjection: ReadonlyMat4 }): ReadonlyMat4 {
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1716
|
+
/* A capture's rows are flipped as a probe face's are, so it reads as an image does. */
|
|
1717
|
+
const scene =
|
|
1718
|
+
this.probePassActive || this.capturePassActive
|
|
1719
|
+
? (cubeFaceProjection(
|
|
1720
|
+
this.cubeFaceViewProj,
|
|
1721
|
+
this.sceneMatrix(camera.viewProjection) as unknown as ArrayLike<number>,
|
|
1722
|
+
) as unknown as ReadonlyMat4)
|
|
1723
|
+
: this.sceneMatrix(camera.viewProjection);
|
|
1656
1724
|
/*
|
|
1657
1725
|
* **The jitter goes on here and nowhere else.** This is the one funnel every geometry upload
|
|
1658
1726
|
* passes through, so one line covers the mesh passes, the sky, the water and the effects
|
|
@@ -2460,9 +2528,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
2460
2528
|
gl.activeTexture(gl.TEXTURE0 + FIXTURE_ATLAS_TEXTURE_UNIT);
|
|
2461
2529
|
gl.bindTexture(gl.TEXTURE_2D, this.fixtureTexture);
|
|
2462
2530
|
gl.uniform1i(u['uFixtureAtlas'] ?? null, FIXTURE_ATLAS_TEXTURE_UNIT);
|
|
2463
|
-
gl.
|
|
2464
|
-
gl.uniform1f(u['uIesPlaneCount'] ?? null, this.iesPlanes);
|
|
2465
|
-
gl.uniform1f(u['uCookieTiles'] ?? null, this.cookieTiles);
|
|
2531
|
+
gl.uniform3f(u['uFixtureShape'] ?? null, this.iesRows, this.iesPlanes, this.cookieTiles);
|
|
2466
2532
|
gl.activeTexture(gl.TEXTURE0);
|
|
2467
2533
|
}
|
|
2468
2534
|
|
|
@@ -2516,7 +2582,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
2516
2582
|
gl.uniformMatrix4fv(u['uView'] ?? null, false, camera.view);
|
|
2517
2583
|
|
|
2518
2584
|
this.clusterTable ??= createClusterTable();
|
|
2519
|
-
|
|
2585
|
+
/* The rectangles past the fixed arm's go into the table. See `ClusterLightSet.areas`. */
|
|
2586
|
+
const lights = fillClusterLightSet(env, this.clusterLights, this.shadedAreaLights);
|
|
2520
2587
|
/* A slot is read only under `POINT_SHADOWS`; see `lightHasFixture`. */
|
|
2521
2588
|
const shadowSlots = this.quality.pointShadows ? this.lightBudget.maxLights : 0;
|
|
2522
2589
|
buildLightClusters(
|
|
@@ -2536,7 +2603,10 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
2536
2603
|
*/
|
|
2537
2604
|
shadowSlots,
|
|
2538
2605
|
);
|
|
2539
|
-
gl.uniform1i(
|
|
2606
|
+
gl.uniform1i(
|
|
2607
|
+
u['uClustered'] ?? null,
|
|
2608
|
+
clusteredMode(lights, clusteredLightTotal(lights), shadowSlots),
|
|
2609
|
+
);
|
|
2540
2610
|
gl.texSubImage2D(
|
|
2541
2611
|
gl.TEXTURE_2D,
|
|
2542
2612
|
0,
|
|
@@ -3252,8 +3322,9 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
3252
3322
|
* one. Measured on hardware: WebGL2 drew neither the bend nor the bulge while WebGPU drew
|
|
3253
3323
|
* both, which is the two-backend comparison doing exactly what it is for.
|
|
3254
3324
|
*/
|
|
3255
|
-
|
|
3256
|
-
|
|
3325
|
+
const drawn = withLightmapUvs(data);
|
|
3326
|
+
this.warmVariants(drawn);
|
|
3327
|
+
return new Mesh(this.gl, drawn, options.dynamic === true);
|
|
3257
3328
|
}
|
|
3258
3329
|
|
|
3259
3330
|
/**
|
|
@@ -3266,8 +3337,9 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
3266
3337
|
* the signal that it was abandoned.
|
|
3267
3338
|
*/
|
|
3268
3339
|
createMeshIncremental(data: MeshData, options: MeshOptions = {}): IncrementalMesh {
|
|
3269
|
-
|
|
3270
|
-
|
|
3340
|
+
const drawn = withLightmapUvs(data);
|
|
3341
|
+
this.warmVariants(drawn);
|
|
3342
|
+
const { mesh, upload } = createMeshIncremental(this.gl, drawn, options.dynamic === true);
|
|
3271
3343
|
return {
|
|
3272
3344
|
mesh,
|
|
3273
3345
|
upload: {
|
|
@@ -3445,6 +3517,97 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
3445
3517
|
return new SurfaceTexture(this.gl, sources, options, this.compressedFormats);
|
|
3446
3518
|
}
|
|
3447
3519
|
|
|
3520
|
+
/** A baked lightmap page, as a `lightmapModel` material's `modelMap`. See `RendererApi`. */
|
|
3521
|
+
createLightmap(page: LightmapPage): SurfaceTexture {
|
|
3522
|
+
return new SurfaceTexture(this.gl, lightmapTexels(page), { wrap: 'clamp', mipmap: false });
|
|
3523
|
+
}
|
|
3524
|
+
|
|
3525
|
+
/**
|
|
3526
|
+
* A texture the scene can be drawn into, for a material to show. See `RendererApi` and
|
|
3527
|
+
* `sceneCapture.ts`. Half floats where the frame keeps range, as a probe does.
|
|
3528
|
+
*/
|
|
3529
|
+
createSceneCapture(width: number, height: number): SurfaceTexture {
|
|
3530
|
+
const float = this.quality.hdrScene && this.gl.getExtension('EXT_color_buffer_float') !== null;
|
|
3531
|
+
const texture = new SurfaceTexture(this.gl, sceneCaptureTexels(width, height, float), {
|
|
3532
|
+
wrap: 'clamp',
|
|
3533
|
+
mipmap: false,
|
|
3534
|
+
});
|
|
3535
|
+
this.captureTargets.set(
|
|
3536
|
+
texture,
|
|
3537
|
+
new GlSceneCaptureTarget(
|
|
3538
|
+
this.gl,
|
|
3539
|
+
texture,
|
|
3540
|
+
width,
|
|
3541
|
+
height,
|
|
3542
|
+
this.sceneTarget?.sampleCount ?? 1,
|
|
3543
|
+
float,
|
|
3544
|
+
),
|
|
3545
|
+
);
|
|
3546
|
+
return texture;
|
|
3547
|
+
}
|
|
3548
|
+
|
|
3549
|
+
/**
|
|
3550
|
+
* Draw the scene into a capture from `camera`: `draw` is handed the camera, its matrices updated
|
|
3551
|
+
* for the capture's shape, and submits the scene as it would to the screen. See `RendererApi`.
|
|
3552
|
+
*
|
|
3553
|
+
* **Radiance, as a probe bake stores it**: the output transform and exposure are held off for the
|
|
3554
|
+
* pass, so the frame grades a screen showing it once. No temporal jitter, which a capture has no
|
|
3555
|
+
* resolve to undo. Returns whether it drew: false for a texture that is not a capture, or one the
|
|
3556
|
+
* driver will not draw into.
|
|
3557
|
+
*/
|
|
3558
|
+
captureScene(
|
|
3559
|
+
capture: SurfaceTexture,
|
|
3560
|
+
camera: Camera,
|
|
3561
|
+
clearColor: Vec3,
|
|
3562
|
+
draw: (camera: Camera) => void,
|
|
3563
|
+
): boolean {
|
|
3564
|
+
if (this.contextLost) return false;
|
|
3565
|
+
const target = this.captureTargets.get(capture);
|
|
3566
|
+
if (target === undefined) return false;
|
|
3567
|
+
if (!target.usable) {
|
|
3568
|
+
if (!this.warnedCapture) {
|
|
3569
|
+
this.warnedCapture = true;
|
|
3570
|
+
console.warn(
|
|
3571
|
+
'Renderer: this driver will not draw into a scene capture, so it keeps its clear. ' +
|
|
3572
|
+
'Nothing else is affected.',
|
|
3573
|
+
);
|
|
3574
|
+
}
|
|
3575
|
+
return false;
|
|
3576
|
+
}
|
|
3577
|
+
camera.updateMatrices(target.width / target.height);
|
|
3578
|
+
const { gl } = this;
|
|
3579
|
+
const jittering = this.temporalJittering;
|
|
3580
|
+
this.temporalJittering = false;
|
|
3581
|
+
this.capturePassActive = true;
|
|
3582
|
+
this.useFlatProgram();
|
|
3583
|
+
gl.uniform1i(this.flatUniforms['uOutputTransform'] ?? null, 0);
|
|
3584
|
+
gl.uniform1f(this.flatUniforms['uOutputExposure'] ?? null, 1);
|
|
3585
|
+
this.materials.dirty();
|
|
3586
|
+
target.begin(gl, clearColor, this.depthClear);
|
|
3587
|
+
/*
|
|
3588
|
+
* **Rows flipped, as a probe face's are.** A GL framebuffer's first row is its bottom and an
|
|
3589
|
+
* image's is its top, so a capture drawn upright would be shown upside down by every material
|
|
3590
|
+
* reading it as the image it is; `viewProjFor` negates clip y for the pass, and negating one axis
|
|
3591
|
+
* reverses every triangle's winding, which the front face follows. WebGPU's rows already run
|
|
3592
|
+
* the image's way.
|
|
3593
|
+
*/
|
|
3594
|
+
gl.frontFace(gl.CW);
|
|
3595
|
+
try {
|
|
3596
|
+
draw(camera);
|
|
3597
|
+
} finally {
|
|
3598
|
+
target.end(gl);
|
|
3599
|
+
gl.frontFace(gl.CCW);
|
|
3600
|
+
this.useFlatProgram();
|
|
3601
|
+
gl.uniform1i(this.flatUniforms['uOutputTransform'] ?? null, this.gradeCode());
|
|
3602
|
+
gl.uniform1f(this.flatUniforms['uOutputExposure'] ?? null, this.gradeExposure());
|
|
3603
|
+
this.materials.dirty();
|
|
3604
|
+
this.capturePassActive = false;
|
|
3605
|
+
this.temporalJittering = jittering;
|
|
3606
|
+
this.restoreViewport();
|
|
3607
|
+
}
|
|
3608
|
+
return true;
|
|
3609
|
+
}
|
|
3610
|
+
|
|
3448
3611
|
/**
|
|
3449
3612
|
* Replace a surface texture's pixels, keeping the GPU object and its sampler state.
|
|
3450
3613
|
*
|
|
@@ -3465,6 +3628,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
3465
3628
|
disposeSurfaceTexture(texture: SurfaceTexture): void {
|
|
3466
3629
|
if (this.contextLost) return;
|
|
3467
3630
|
texture.dispose(this.gl);
|
|
3631
|
+
this.captureTargets.get(texture)?.dispose(this.gl);
|
|
3632
|
+
this.captureTargets.delete(texture);
|
|
3468
3633
|
}
|
|
3469
3634
|
|
|
3470
3635
|
/**
|
|
@@ -3522,7 +3687,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
3522
3687
|
const label = `flat.${variant}.${kind}.${half}`;
|
|
3523
3688
|
const program = compileProgram(
|
|
3524
3689
|
gl,
|
|
3525
|
-
|
|
3690
|
+
modelVertexOf(variant, kind),
|
|
3526
3691
|
skinHalfBound(modelBound(this.flatFragSource, kind), half),
|
|
3527
3692
|
label,
|
|
3528
3693
|
);
|
|
@@ -3693,12 +3858,13 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
3693
3858
|
this.currentMaterial = material;
|
|
3694
3859
|
/* What the blur spreads this skin by: the last material to name a profile says. */
|
|
3695
3860
|
const model = material?.model;
|
|
3861
|
+
warnUnpagedLightmap(model, material?.modelMap);
|
|
3696
3862
|
if (model?.kind === 'skin' && this.skinScatter !== null) {
|
|
3697
3863
|
this.skinScatter.setProfile(
|
|
3698
3864
|
model.profile,
|
|
3699
|
-
model
|
|
3700
|
-
model
|
|
3701
|
-
model
|
|
3865
|
+
skinProfileDistance(model, 0),
|
|
3866
|
+
skinProfileDistance(model, 1),
|
|
3867
|
+
skinProfileDistance(model, 2),
|
|
3702
3868
|
);
|
|
3703
3869
|
}
|
|
3704
3870
|
/*
|
|
@@ -3738,7 +3904,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
3738
3904
|
v[0] = material?.cutout ?? 0;
|
|
3739
3905
|
v[1] = CUTOUT_RESOLVE_CODE[this.cutoutResolveNow(material, translucent)];
|
|
3740
3906
|
v[2] = this.cutoutFrame;
|
|
3741
|
-
|
|
3907
|
+
/* The material's lighting channels, which the lamps' are compared with. See `uCutout`. */
|
|
3908
|
+
v[3] = lightChannelsOf(material?.lightChannels);
|
|
3742
3909
|
return v;
|
|
3743
3910
|
}
|
|
3744
3911
|
|
|
@@ -3845,7 +4012,14 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
3845
4012
|
gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.emptyTexture2DArray);
|
|
3846
4013
|
if (modelMap !== null) modelMap.bind(gl, MODEL_MAP_TEXTURE_UNIT);
|
|
3847
4014
|
gl.uniform1i(u['uModelMap'] ?? null, MODEL_MAP_TEXTURE_UNIT);
|
|
3848
|
-
packModel(
|
|
4015
|
+
packModel(
|
|
4016
|
+
material?.model ?? null,
|
|
4017
|
+
modelMap !== null,
|
|
4018
|
+
this.modelParams,
|
|
4019
|
+
material?.physicalSpecular === true,
|
|
4020
|
+
);
|
|
4021
|
+
/* The first physical highlight asked for is when the lit programs start carrying one. */
|
|
4022
|
+
if (material?.physicalSpecular === true) this.askLit('PHYSICAL_SPECULAR');
|
|
3849
4023
|
gl.uniform4fv(u['uModelParams'] ?? null, this.modelParams);
|
|
3850
4024
|
|
|
3851
4025
|
/* The albedo's effects table, or the stand-in the shader reads as none. Before the early
|
|
@@ -3866,6 +4040,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
3866
4040
|
gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.emptyTexture2DArray);
|
|
3867
4041
|
this.materialFlags[0] = 0;
|
|
3868
4042
|
gl.uniform4iv(u['uMaterialFlags'] ?? null, this.materialFlags);
|
|
4043
|
+
/* No image to cut, and the channels all the same: a material needs no albedo to name them. */
|
|
4044
|
+
gl.uniform4f(u['uCutout'] ?? null, 0, 0, 0, lightChannelsOf(material?.lightChannels));
|
|
3869
4045
|
return;
|
|
3870
4046
|
}
|
|
3871
4047
|
albedo.bind(gl, SURFACE_TEXTURE_UNIT);
|
|
@@ -3906,17 +4082,27 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
3906
4082
|
}
|
|
3907
4083
|
|
|
3908
4084
|
/**
|
|
3909
|
-
*
|
|
4085
|
+
* The ambient the following draws take, as nine spherical-harmonic coefficients of incoming
|
|
4086
|
+
* radiance — red, green and blue of each, twenty-seven numbers — or `null` for the frame's own:
|
|
4087
|
+
* the sky's gradient, or the probes'. See `ambientHarmonics.ts` for the basis and the axes.
|
|
3910
4088
|
*
|
|
3911
|
-
*
|
|
3912
|
-
*
|
|
3913
|
-
*
|
|
3914
|
-
*
|
|
3915
|
-
*
|
|
3916
|
-
* Pass state, like `setSurfaceTexture`, because a material covers many draws. The default
|
|
3917
|
-
* is 1 and `bindMeshPass` restores it, so it scales geometry that declared grain and
|
|
3918
|
-
* leaves geometry that did not exactly as it is.
|
|
4089
|
+
* **For a thing lit by where it stands**: a consumer sampling a stage's baked volume of indirect
|
|
4090
|
+
* light at a character every frame sets the result around that character's draws, and the
|
|
4091
|
+
* character takes the light of its spot rather than the frame's. It replaces the diffuse ambient
|
|
4092
|
+
* only — a glossy surface still reflects the probes — and it is pass state like the material,
|
|
4093
|
+
* which `bindMeshPass` clears, so a mirror or a probe bake drawn after does not inherit it.
|
|
3919
4094
|
*/
|
|
4095
|
+
setAmbientSH(coefficients: ArrayLike<number> | null): void {
|
|
4096
|
+
if (this.contextLost) return;
|
|
4097
|
+
packAmbientSH(coefficients, this.ambientSH);
|
|
4098
|
+
this.materials.dirty();
|
|
4099
|
+
for (const target of this.flatTargets) {
|
|
4100
|
+
this.gl.useProgram(target.program);
|
|
4101
|
+
this.gl.uniform4fv(target.uniforms['uAmbientSH[0]'] ?? null, this.ambientSH);
|
|
4102
|
+
}
|
|
4103
|
+
this.useFlatProgram();
|
|
4104
|
+
}
|
|
4105
|
+
|
|
3920
4106
|
/**
|
|
3921
4107
|
* How much of the environment the following draws mirror, 0 to 1.
|
|
3922
4108
|
*
|
|
@@ -3930,7 +4116,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
3930
4116
|
setSurfaceReflectivity(amount: number): void {
|
|
3931
4117
|
if (this.contextLost) return;
|
|
3932
4118
|
this.materials.dirty();
|
|
3933
|
-
this.
|
|
4119
|
+
this.environmentDials[1] = Math.min(1, Math.max(0, amount));
|
|
4120
|
+
this.writeFlatVector('uEnvironmentDials', this.environmentDials);
|
|
3934
4121
|
}
|
|
3935
4122
|
|
|
3936
4123
|
/**
|
|
@@ -3948,7 +4135,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
3948
4135
|
setEnvironmentGain(gain: number): void {
|
|
3949
4136
|
if (this.contextLost) return;
|
|
3950
4137
|
this.materials.dirty();
|
|
3951
|
-
this.
|
|
4138
|
+
this.environmentDials[2] = Math.max(0, gain);
|
|
4139
|
+
this.writeFlatVector('uEnvironmentDials', this.environmentDials);
|
|
3952
4140
|
}
|
|
3953
4141
|
|
|
3954
4142
|
/**
|
|
@@ -3974,6 +4162,18 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
3974
4162
|
);
|
|
3975
4163
|
}
|
|
3976
4164
|
|
|
4165
|
+
/**
|
|
4166
|
+
* A pass-level scale over the grain the geometry itself declared, 0 to 1.
|
|
4167
|
+
*
|
|
4168
|
+
* Which surfaces are mineral is now `MeshData.grain`, stated per vertex through
|
|
4169
|
+
* `MeshBuilder.setGrain` — because it is a property of a material, and absent means none.
|
|
4170
|
+
* This stays as the override for the case that has no answer to state: an imported model
|
|
4171
|
+
* arrives with no grain information at all, and a caller drawing one decides for it.
|
|
4172
|
+
*
|
|
4173
|
+
* Pass state, like `setSurfaceTexture`, because a material covers many draws. The default
|
|
4174
|
+
* is 1 and `bindMeshPass` restores it, so it scales geometry that declared grain and
|
|
4175
|
+
* leaves geometry that did not exactly as it is.
|
|
4176
|
+
*/
|
|
3977
4177
|
setSurfaceGrain(amount: number): void {
|
|
3978
4178
|
if (this.contextLost) return;
|
|
3979
4179
|
this.materials.dirty();
|
|
@@ -4019,6 +4219,18 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4019
4219
|
this.useFlatProgram();
|
|
4020
4220
|
}
|
|
4021
4221
|
|
|
4222
|
+
/** A vector of material state, two to four floats, into every flat program. */
|
|
4223
|
+
private writeFlatVector(name: string, value: Float32Array): void {
|
|
4224
|
+
for (const target of this.flatTargets) {
|
|
4225
|
+
this.gl.useProgram(target.program);
|
|
4226
|
+
const at = target.uniforms[name] ?? null;
|
|
4227
|
+
if (value.length === 2) this.gl.uniform2fv(at, value);
|
|
4228
|
+
else if (value.length === 3) this.gl.uniform3fv(at, value);
|
|
4229
|
+
else this.gl.uniform4fv(at, value);
|
|
4230
|
+
}
|
|
4231
|
+
this.useFlatProgram();
|
|
4232
|
+
}
|
|
4233
|
+
|
|
4022
4234
|
/** One int of material state, into every flat program, for `writeFlatFloat`'s reason. */
|
|
4023
4235
|
private writeFlatInt(name: string, value: number): void {
|
|
4024
4236
|
for (const target of this.flatTargets) {
|
|
@@ -4083,8 +4295,13 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4083
4295
|
setSurfaceRelief(amount: number, cyclesPerMetre = 60): void {
|
|
4084
4296
|
if (this.contextLost) return;
|
|
4085
4297
|
this.materials.dirty();
|
|
4086
|
-
|
|
4087
|
-
|
|
4298
|
+
const strength = Math.min(1, Math.max(0, amount));
|
|
4299
|
+
const cycles = Math.max(0.01, cyclesPerMetre);
|
|
4300
|
+
for (const target of this.flatTargets) {
|
|
4301
|
+
this.gl.useProgram(target.program);
|
|
4302
|
+
this.gl.uniform2f(target.uniforms['uRelief'] ?? null, strength, cycles);
|
|
4303
|
+
}
|
|
4304
|
+
this.useFlatProgram();
|
|
4088
4305
|
}
|
|
4089
4306
|
|
|
4090
4307
|
/**
|
|
@@ -4142,7 +4359,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4142
4359
|
setEmissiveGain(gain: number): void {
|
|
4143
4360
|
if (this.contextLost) return;
|
|
4144
4361
|
this.materials.dirty();
|
|
4145
|
-
this.
|
|
4362
|
+
this.emission[0] = gain;
|
|
4363
|
+
this.writeFlatVector('uEmission', this.emission);
|
|
4146
4364
|
}
|
|
4147
4365
|
|
|
4148
4366
|
/* -- Instanced meshes --------------------------------------------------------------- */
|
|
@@ -4254,6 +4472,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4254
4472
|
options: TranslucentMeshOptions,
|
|
4255
4473
|
): void {
|
|
4256
4474
|
if (this.contextLost) return;
|
|
4475
|
+
warnRegionsWithoutPage(this.currentMaterial?.model, data.lightmapRegions);
|
|
4257
4476
|
const mesh = batch.mesh;
|
|
4258
4477
|
/* Nor a batch of a mesh with nothing in it: see `Mesh.indexCount`. */
|
|
4259
4478
|
if (!mesh.complete || mesh.indexCount === 0) return;
|
|
@@ -4384,7 +4603,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4384
4603
|
const refraction = options.refraction ?? 0;
|
|
4385
4604
|
const glassy = resolveGlass(options.glass, this.glassScratch);
|
|
4386
4605
|
if (refraction <= 0 && !glassy) return false;
|
|
4387
|
-
if (this.probePassActive || this.reflectionPassActive) return false;
|
|
4606
|
+
if (this.probePassActive || this.reflectionPassActive || this.capturePassActive) return false;
|
|
4388
4607
|
const snapshot = this.sceneTarget?.snapshotColor(false, glassy) ?? null;
|
|
4389
4608
|
if (snapshot === null) return false;
|
|
4390
4609
|
gl.activeTexture(gl.TEXTURE0 + REFRACT_SCENE_TEXTURE_UNIT);
|
|
@@ -4746,6 +4965,29 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4746
4965
|
return new ParticleBatch(this.gl, capacity, options);
|
|
4747
4966
|
}
|
|
4748
4967
|
|
|
4968
|
+
/**
|
|
4969
|
+
* Particles a caller's own compute wrote: WebGPU's alone, since this backend has no compute stage
|
|
4970
|
+
* to have written them (`computeSupported`). Said once, and nothing is drawn.
|
|
4971
|
+
*/
|
|
4972
|
+
drawDeviceParticles(
|
|
4973
|
+
_batch: ParticleBatch,
|
|
4974
|
+
_particles: DeviceParticles,
|
|
4975
|
+
_camera: Camera,
|
|
4976
|
+
_env: Environment,
|
|
4977
|
+
_timeSeconds: number,
|
|
4978
|
+
): void {
|
|
4979
|
+
if (this.warnedDeviceParticles) return;
|
|
4980
|
+
this.warnedDeviceParticles = true;
|
|
4981
|
+
console.warn(
|
|
4982
|
+
'Renderer: drawDeviceParticles draws particles a compute shader wrote, and WebGL2 has no ' +
|
|
4983
|
+
'compute stage, so nothing is drawn. Check `computeSupported`, and draw a pool with ' +
|
|
4984
|
+
'`drawParticles` where it is false.',
|
|
4985
|
+
);
|
|
4986
|
+
}
|
|
4987
|
+
|
|
4988
|
+
/** Said once: see `drawDeviceParticles`. */
|
|
4989
|
+
private warnedDeviceParticles = false;
|
|
4990
|
+
|
|
4749
4991
|
/**
|
|
4750
4992
|
* Draw a live particle pool.
|
|
4751
4993
|
*
|
|
@@ -4767,8 +5009,9 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4767
5009
|
const { gl } = this;
|
|
4768
5010
|
const u = batch.uniforms;
|
|
4769
5011
|
gl.useProgram(batch.program);
|
|
4770
|
-
batch.upload(gl, data);
|
|
5012
|
+
batch.upload(gl, data, camera.position);
|
|
4771
5013
|
batch.bindMaterial(gl);
|
|
5014
|
+
if (batch.material === 'sprite') this.bindSpriteFades(batch, camera);
|
|
4772
5015
|
gl.uniformMatrix4fv(u['uViewProj'] ?? null, false, this.viewProjFor(camera));
|
|
4773
5016
|
gl.uniform3fv(u['uCameraPos'] ?? null, camera.position);
|
|
4774
5017
|
gl.uniform1f(u['uTime'] ?? null, timeSeconds);
|
|
@@ -4800,6 +5043,53 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4800
5043
|
if (this.materialDoubleSided) gl.enable(gl.CULL_FACE);
|
|
4801
5044
|
}
|
|
4802
5045
|
|
|
5046
|
+
/**
|
|
5047
|
+
* A sprite's two fades and the depth copy the soft one reads. The copy is the frame's opaque
|
|
5048
|
+
* depth, so it is taken only in the frame's own picture: in a mirror or a probe's face it would
|
|
5049
|
+
* be another view's, and the edge stays hard there. Without a scene target there is no copy at
|
|
5050
|
+
* all, and the renderer says so once rather than drawing a hard edge that was asked to be soft.
|
|
5051
|
+
*/
|
|
5052
|
+
private bindSpriteFades(batch: ParticleBatch, camera: Camera): void {
|
|
5053
|
+
const { gl } = this;
|
|
5054
|
+
const u = batch.uniforms;
|
|
5055
|
+
const scene = this.sceneTarget;
|
|
5056
|
+
let depth: WebGLTexture | null = null;
|
|
5057
|
+
if (
|
|
5058
|
+
batch.softDepth > 0 &&
|
|
5059
|
+
!this.reflectionPassActive &&
|
|
5060
|
+
!this.probePassActive &&
|
|
5061
|
+
!this.capturePassActive
|
|
5062
|
+
) {
|
|
5063
|
+
depth = scene?.snapshotDepth(true) ?? null;
|
|
5064
|
+
if (depth === null && !this.warnedSpriteDepth) {
|
|
5065
|
+
this.warnedSpriteDepth = true;
|
|
5066
|
+
console.warn(
|
|
5067
|
+
"WebGL2: a 'sprite' asked for a soft edge, which reads a copy of the frame's depth, and " +
|
|
5068
|
+
'this profile keeps none — screenEffects is off, or the driver refused the copy. The ' +
|
|
5069
|
+
'edge is hard.',
|
|
5070
|
+
);
|
|
5071
|
+
}
|
|
5072
|
+
}
|
|
5073
|
+
gl.activeTexture(gl.TEXTURE0 + PARTICLE_DEPTH_UNIT);
|
|
5074
|
+
gl.bindTexture(gl.TEXTURE_2D, depth ?? this.emptyTexture2D);
|
|
5075
|
+
gl.uniform1i(u['uDepth'] ?? null, PARTICLE_DEPTH_UNIT);
|
|
5076
|
+
gl.uniform4f(u['uFade'] ?? null, batch.softDepth, batch.cameraFade, depth === null ? 0 : 1, 0);
|
|
5077
|
+
mat4.invert(this.spriteInverseProjection, camera.projection);
|
|
5078
|
+
const inverse = this.spriteInverseProjection;
|
|
5079
|
+
gl.uniform4f(
|
|
5080
|
+
u['uDepthToViewZ'] ?? null,
|
|
5081
|
+
inverse[10] ?? 0,
|
|
5082
|
+
inverse[14] ?? 0,
|
|
5083
|
+
inverse[11] ?? 0,
|
|
5084
|
+
inverse[15] ?? 1,
|
|
5085
|
+
);
|
|
5086
|
+
gl.uniform2f(
|
|
5087
|
+
u['uViewport'] ?? null,
|
|
5088
|
+
scene?.frameWidth ?? gl.drawingBufferWidth,
|
|
5089
|
+
scene?.frameHeight ?? gl.drawingBufferHeight,
|
|
5090
|
+
);
|
|
5091
|
+
}
|
|
5092
|
+
|
|
4803
5093
|
/** Build a batch for electrical arcs. `capacity` is in segments, not arcs. */
|
|
4804
5094
|
createBolts(segmentCapacity: number, label = 'bolts'): BoltBatch {
|
|
4805
5095
|
return new BoltBatch(this.gl, segmentCapacity, label);
|
|
@@ -5838,6 +6128,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
5838
6128
|
lightFixtures: this.litOn.LIGHT_FIXTURES,
|
|
5839
6129
|
surfaceEffects: this.litOn.SURFACE_EFFECTS,
|
|
5840
6130
|
driftLight: this.litOn.DRIFT_LIGHT,
|
|
6131
|
+
physicalSpecular: this.litOn.PHYSICAL_SPECULAR,
|
|
5841
6132
|
maxLights: budget.maxLights,
|
|
5842
6133
|
maxAreaLights: budget.maxAreaLights,
|
|
5843
6134
|
});
|
|
@@ -5918,7 +6209,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
5918
6209
|
const label = `flat.${variant}.${kind}.${half}`;
|
|
5919
6210
|
const program = compileProgram(
|
|
5920
6211
|
gl,
|
|
5921
|
-
|
|
6212
|
+
modelVertexOf(variant, kind),
|
|
5922
6213
|
skinHalfBound(modelBound(this.flatFragSource, kind), half),
|
|
5923
6214
|
label,
|
|
5924
6215
|
);
|
|
@@ -6800,10 +7091,17 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
6800
7091
|
* and wrong for a day whose exposure spans 2.5 to 14: a courtyard blooming at noon, or candles
|
|
6801
7092
|
* never blooming at night. A caller whose exposure moves passes what it wants on screen over the
|
|
6802
7093
|
* exposure. Omitted leaves it where it was, so a caller that never passes one keeps the profile's.
|
|
7094
|
+
*
|
|
7095
|
+
* **`response` is how a colour comes in past the threshold and what colour each octave of the
|
|
7096
|
+
* halo takes** — a ramp in place of the subtraction, and a tint a level — held until moved like
|
|
7097
|
+
* the threshold; `null` goes back to the subtraction and white. See `BloomResponse`: it is what
|
|
7098
|
+
* matches a stage built for another engine's bloom, whose tints are how a strength past 1 is
|
|
7099
|
+
* asked for.
|
|
6803
7100
|
*/
|
|
6804
|
-
setBloom(scale: number, threshold?: number): void {
|
|
7101
|
+
setBloom(scale: number, threshold?: number, response?: BloomResponse | null): void {
|
|
6805
7102
|
this.bloomScale = Math.min(Math.max(scale, 0), 1);
|
|
6806
7103
|
if (threshold !== undefined) this.bloomThresholdSet = bloomThresholdOf(threshold);
|
|
7104
|
+
if (response !== undefined) resolveBloomResponse(response, this.bloomResponse);
|
|
6807
7105
|
}
|
|
6808
7106
|
|
|
6809
7107
|
/**
|
|
@@ -7364,6 +7662,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
7364
7662
|
? {
|
|
7365
7663
|
strength: bloomStrength,
|
|
7366
7664
|
threshold: this.bloomThresholdSet ?? this.quality.bloomThreshold,
|
|
7665
|
+
response: this.bloomResponse,
|
|
7367
7666
|
}
|
|
7368
7667
|
: undefined;
|
|
7369
7668
|
|
|
@@ -7699,6 +7998,19 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
7699
7998
|
bindMeshPass(camera: Camera, env: Environment): void {
|
|
7700
7999
|
/* The pass's own material is reopened, so the next draw opens one. See `materialChanges.ts`. */
|
|
7701
8000
|
this.materials.dirty();
|
|
8001
|
+
/* Rectangles past the fixed arm's are the froxel table's, and without it nobody shades them. */
|
|
8002
|
+
if (
|
|
8003
|
+
!this.warnedAreaOverflow &&
|
|
8004
|
+
!this.quality.clusteredLights &&
|
|
8005
|
+
(env.areaLights?.count ?? 0) > this.shadedAreaLights
|
|
8006
|
+
) {
|
|
8007
|
+
this.warnedAreaOverflow = true;
|
|
8008
|
+
console.warn(
|
|
8009
|
+
`WebGL2: ${env.areaLights?.count ?? 0} rectangular lights and ${this.shadedAreaLights} ` +
|
|
8010
|
+
'shaded without clusteredLights: the rest are not drawn. Turn clusteredLights on, and the ' +
|
|
8011
|
+
'froxel table shades every one past the fixed ones, unshadowed.',
|
|
8012
|
+
);
|
|
8013
|
+
}
|
|
7702
8014
|
/*
|
|
7703
8015
|
* Captured here rather than taken as an argument to `endFrame`, because this is the
|
|
7704
8016
|
* camera the world was drawn with and a caller should not have to hand it over twice.
|
|
@@ -7712,7 +8024,12 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
7712
8024
|
* other position, and the anti-flicker's period never started.
|
|
7713
8025
|
*/
|
|
7714
8026
|
if (this.framePresented) this.temporalJittering = false;
|
|
7715
|
-
if (
|
|
8027
|
+
if (
|
|
8028
|
+
!this.reflectionPassActive &&
|
|
8029
|
+
!this.probePassActive &&
|
|
8030
|
+
!this.capturePassActive &&
|
|
8031
|
+
!this.framePresented
|
|
8032
|
+
) {
|
|
7716
8033
|
this.frameViewProj = camera.viewProjection;
|
|
7717
8034
|
/* Copied rather than referenced: a caller moves its camera in place, and the decal pass
|
|
7718
8035
|
reads this at the end of the frame. */
|
|
@@ -7810,6 +8127,13 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
7810
8127
|
this.materialHasMaps = false;
|
|
7811
8128
|
this.currentMaterial = null;
|
|
7812
8129
|
this.currentSurfaceTexture = null;
|
|
8130
|
+
/* Nor an ambient of its own: the frame's, until a draw asks. See `setAmbientSH`. */
|
|
8131
|
+
this.ambientSH.fill(0);
|
|
8132
|
+
/* No reflection and an environment at its own brightness; the emission the world asks for. */
|
|
8133
|
+
this.environmentDials[1] = 0;
|
|
8134
|
+
this.environmentDials[2] = 1;
|
|
8135
|
+
this.emission[0] = env.emissiveGain;
|
|
8136
|
+
this.emission[1] = env.nightFactor;
|
|
7813
8137
|
for (const target of this.flatTargets) {
|
|
7814
8138
|
this.gl.useProgram(target.program);
|
|
7815
8139
|
this.writeMeshPassState(target.uniforms, camera, env);
|
|
@@ -8036,13 +8360,18 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
8036
8360
|
this.oitWeight = 0;
|
|
8037
8361
|
this.ditherFade = 0;
|
|
8038
8362
|
gl.uniform2f(u['uWriteMode'] ?? null, 0, 0);
|
|
8039
|
-
/*
|
|
8040
|
-
|
|
8041
|
-
|
|
8042
|
-
|
|
8363
|
+
/* The draw's own ambient as it stands, which `bindMeshPass` has just cleared: a program built
|
|
8364
|
+
in the middle of a pass takes what the caller set. See `setAmbientSH`. */
|
|
8365
|
+
gl.uniform4fv(u['uAmbientSH[0]'] ?? null, this.ambientSH);
|
|
8366
|
+
/*
|
|
8367
|
+
* Which chain the probe holds, no environment reflection, and an environment at its own
|
|
8368
|
+
* brightness — the reflectivity's identity is 0 and the gain's is 1 — as `bindMeshPass` set
|
|
8369
|
+
* them, or as a draw since has: a program built in the middle of a pass takes what stands.
|
|
8370
|
+
* See `setSurfaceReflectivity` and `setEnvironmentGain`.
|
|
8371
|
+
*/
|
|
8372
|
+
gl.uniform3fv(u['uEnvironmentDials'] ?? null, this.environmentDials);
|
|
8043
8373
|
/* And no relief unless a caller asks, for the same reason. */
|
|
8044
|
-
gl.
|
|
8045
|
-
gl.uniform1f(u['uReliefCycles'] ?? null, 60);
|
|
8374
|
+
gl.uniform2f(u['uRelief'] ?? null, 0, 60);
|
|
8046
8375
|
/* And no relief read off a surface texture either, for the same reason. */
|
|
8047
8376
|
gl.uniform1f(u['uTextureRelief'] ?? null, 0);
|
|
8048
8377
|
/* The mirror's plane in a reflection pass, and zero — which clips nothing — in every other. */
|
|
@@ -8060,8 +8389,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
8060
8389
|
this.reflectionPassActive ? this.reflectionAtmosphereY : (camera.position[1] ?? 0),
|
|
8061
8390
|
this.quality.underwaterAtmosphere,
|
|
8062
8391
|
);
|
|
8063
|
-
|
|
8064
|
-
gl.
|
|
8392
|
+
/* The emissive gain and the night factor, as the pass set them. See `uEmission`. */
|
|
8393
|
+
gl.uniform2fv(u['uEmission'] ?? null, this.emission);
|
|
8065
8394
|
/* Absent means zero, so a world written before this term existed adds nothing. */
|
|
8066
8395
|
gl.uniform1f(u['uNightEmissive'] ?? null, env.nightEmissive ?? 0);
|
|
8067
8396
|
/*
|
|
@@ -8083,8 +8412,16 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
8083
8412
|
gl.uniform1f(u['uShadowMaxSlope'] ?? null, this.quality.directionalShadowMaxSlope);
|
|
8084
8413
|
gl.uniform1i(u['uShadowFilterTaps'] ?? null, this.quality.shadowFilterTaps);
|
|
8085
8414
|
gl.uniform3fv(u['uHighlightMin'] ?? null, env.highlightMin);
|
|
8086
|
-
|
|
8087
|
-
|
|
8415
|
+
/* The far corner, and the gain in its w. See `uHighlightMax`. */
|
|
8416
|
+
/* An environment written by hand may leave the box out; then there is none, as before. */
|
|
8417
|
+
const max = env.highlightMax ?? NO_HIGHLIGHT;
|
|
8418
|
+
gl.uniform4f(
|
|
8419
|
+
u['uHighlightMax'] ?? null,
|
|
8420
|
+
max[0] ?? 0,
|
|
8421
|
+
max[1] ?? 0,
|
|
8422
|
+
max[2] ?? 0,
|
|
8423
|
+
env.highlightGain ?? 0,
|
|
8424
|
+
);
|
|
8088
8425
|
|
|
8089
8426
|
bindPointLights(gl, u, env, this.quality.pointLightFalloff);
|
|
8090
8427
|
this.bindClusters(u, camera, env);
|
|
@@ -8184,7 +8521,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
8184
8521
|
* between them; there is one array, so `RenderQuality.environmentPrefilter` decides what the
|
|
8185
8522
|
* convolution writes and a profile that changes it has to bake again.
|
|
8186
8523
|
*/
|
|
8187
|
-
|
|
8524
|
+
this.environmentDials[0] = this.quality.environmentPrefilter ? 1 : 0;
|
|
8525
|
+
gl.uniform3fv(u['uEnvironmentDials'] ?? null, this.environmentDials);
|
|
8188
8526
|
gl.uniform1f(u['uEnvironmentMaxLod'] ?? null, array?.ggxMaxLevel ?? 0);
|
|
8189
8527
|
/*
|
|
8190
8528
|
* The map's edge, which used to be `exp2(maxLod)` and is not any more: the chain stops one
|
|
@@ -8383,12 +8721,17 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
8383
8721
|
if (this.quality.cullDraws && this.occluded(mesh.bounds, model)) return;
|
|
8384
8722
|
this.drawBudget.ask();
|
|
8385
8723
|
this.takeMaterial();
|
|
8386
|
-
/*
|
|
8724
|
+
/*
|
|
8725
|
+
* Skin under the screen-space blur draws its halves: the frame's, then at once its diffuse's
|
|
8726
|
+
* light and its colour, each into the blur's own target.
|
|
8727
|
+
*/
|
|
8387
8728
|
const splits = this.skinSplits();
|
|
8388
8729
|
this.drawMeshHalf(mesh, model, depthLayer, tint, splits ? 'scene' : 'whole');
|
|
8389
8730
|
if (splits && this.skinScatter !== null) {
|
|
8390
8731
|
this.skinScatter.begin();
|
|
8391
8732
|
this.drawMeshHalf(mesh, model, depthLayer, tint, 'diffuse');
|
|
8733
|
+
this.skinScatter.beginAlbedo();
|
|
8734
|
+
this.drawMeshHalf(mesh, model, depthLayer, tint, 'albedo');
|
|
8392
8735
|
this.sceneTarget?.bind();
|
|
8393
8736
|
}
|
|
8394
8737
|
}
|
|
@@ -8408,6 +8751,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
8408
8751
|
!this.skinScattered &&
|
|
8409
8752
|
!this.reflectionPassActive &&
|
|
8410
8753
|
!this.probePassActive &&
|
|
8754
|
+
!this.capturePassActive &&
|
|
8411
8755
|
!this.framePresented &&
|
|
8412
8756
|
skin.ready(scene)
|
|
8413
8757
|
);
|
|
@@ -8428,7 +8772,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
8428
8772
|
/* Latched whether or not skin was drawn: a skin drawn after this point is the whole surface. */
|
|
8429
8773
|
this.skinScattered = true;
|
|
8430
8774
|
if (!skin.pending) return;
|
|
8431
|
-
if (this.reflectionPassActive || this.probePassActive ||
|
|
8775
|
+
if (this.reflectionPassActive || this.probePassActive || this.capturePassActive) return;
|
|
8776
|
+
if (projection === null) return;
|
|
8432
8777
|
const depth = scene.snapshotDepth(true);
|
|
8433
8778
|
mat4.invert(this.skinInverseProjection, projection);
|
|
8434
8779
|
skin.spread(scene, depth, this.emptyTexture2D, projection, this.skinInverseProjection);
|
|
@@ -8574,8 +8919,9 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
8574
8919
|
* depth 1.0 against a buffer cleared to 1.0, and under `LESS` it would not draw at
|
|
8575
8920
|
* all. Film, caustics and text lie flush on surfaces for the same reason.
|
|
8576
8921
|
*/
|
|
8577
|
-
/* A skin's diffuse
|
|
8578
|
-
|
|
8922
|
+
/* A skin's diffuse and colour halves are the same surface again: each finds the depth its
|
|
8923
|
+
frame half wrote. */
|
|
8924
|
+
const again = half === 'diffuse' || half === 'albedo';
|
|
8579
8925
|
gl.depthFunc(again ? gl.EQUAL : this.reversedDepth ? gl.GREATER : gl.LESS);
|
|
8580
8926
|
if (again) gl.depthMask(false);
|
|
8581
8927
|
/* A dithered cutout in a multisampled frame with no temporal resolve: its share becomes the
|
|
@@ -9030,6 +9376,62 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
9030
9376
|
return true;
|
|
9031
9377
|
}
|
|
9032
9378
|
|
|
9379
|
+
/**
|
|
9380
|
+
* One layer of the declared probe grid from an image, the way `setEnvironmentImage` fills a
|
|
9381
|
+
* single probe: the same equirectangular image of linear RGB floats, projected onto the capture
|
|
9382
|
+
* cube and prefiltered into that layer, the others left as they are. Returns false — and says
|
|
9383
|
+
* why — where there is no probe, no grid, or no such layer.
|
|
9384
|
+
*
|
|
9385
|
+
* **What it is for**: a scene baked somewhere else. Another engine's reflection captures are
|
|
9386
|
+
* images taken where each one stands; a consumer fits them to a lattice — for each point, the
|
|
9387
|
+
* capture whose reach holds it — and hands each point its image here, where `bakeProbe` would
|
|
9388
|
+
* have drawn this engine's own world into it.
|
|
9389
|
+
*
|
|
9390
|
+
* `options.irradiance` decides, at grid scope as a bake's does, whether the grid also lights the
|
|
9391
|
+
* scene's diffuse ambient. A grid that crossfades writes the set being swept, as a bake does.
|
|
9392
|
+
*/
|
|
9393
|
+
setProbeLayerImage(
|
|
9394
|
+
layer: number,
|
|
9395
|
+
image: {
|
|
9396
|
+
readonly width: number;
|
|
9397
|
+
readonly height: number;
|
|
9398
|
+
readonly data: Float32Array;
|
|
9399
|
+
},
|
|
9400
|
+
options?: ProbeBakeOptions,
|
|
9401
|
+
): boolean {
|
|
9402
|
+
if (this.contextLost) return false;
|
|
9403
|
+
const probe = this.probeCapture;
|
|
9404
|
+
const array = this.probeArray;
|
|
9405
|
+
const grid = this.probes;
|
|
9406
|
+
const sweeps = this.probeSweeps;
|
|
9407
|
+
if (probe === null || array === null || grid === null || sweeps === null) {
|
|
9408
|
+
console.warn(
|
|
9409
|
+
'Renderer: setProbeLayerImage needs a reflection probe (`reflectionProbeSize`) and a grid ' +
|
|
9410
|
+
'declared with setProbeGrid; the image was ignored.',
|
|
9411
|
+
);
|
|
9412
|
+
return false;
|
|
9413
|
+
}
|
|
9414
|
+
if (!Number.isInteger(layer) || layer < 0 || layer >= grid.layers) {
|
|
9415
|
+
console.warn(
|
|
9416
|
+
`Renderer: setProbeLayerImage was given layer ${layer}, and the grid has ${grid.layers}.`,
|
|
9417
|
+
);
|
|
9418
|
+
return false;
|
|
9419
|
+
}
|
|
9420
|
+
if (!probe.upload(this.gl, equirectToCubeFaces(image, probe.size))) return false;
|
|
9421
|
+
this.probeAmbient = options?.irradiance ?? true;
|
|
9422
|
+
this.environmentPrefilter?.run(
|
|
9423
|
+
this.gl,
|
|
9424
|
+
probe,
|
|
9425
|
+
array,
|
|
9426
|
+
sweeps.writeLayer(layer),
|
|
9427
|
+
ENVIRONMENT_TEXTURE_UNIT,
|
|
9428
|
+
this.quality.environmentPrefilterSamples,
|
|
9429
|
+
this.quality.environmentPrefilter,
|
|
9430
|
+
);
|
|
9431
|
+
sweeps.baked(layer);
|
|
9432
|
+
return true;
|
|
9433
|
+
}
|
|
9434
|
+
|
|
9033
9435
|
/**
|
|
9034
9436
|
* Draw a volume of light: a beam from a lamp, a shaft through a window, the cone under a
|
|
9035
9437
|
* street light.
|
|
@@ -9420,6 +9822,15 @@ const NO_MODEL_PARAMS = new Float32Array(MODEL_PARAM_FLOATS);
|
|
|
9420
9822
|
* The lit vertex stage a program of this variant compiles: the standard programs' and the cloth's,
|
|
9421
9823
|
* which `modelProgram` pairs a model's fragment stage with.
|
|
9422
9824
|
*/
|
|
9825
|
+
/**
|
|
9826
|
+
* The vertex stage a model's program draws with: the variant's own, but for a lightmapped instanced
|
|
9827
|
+
* batch, whose tint lane is each instance's region of the page. See `lightmap.ts`.
|
|
9828
|
+
*/
|
|
9829
|
+
function modelVertexOf(variant: string, kind: SurfaceModelKind): string {
|
|
9830
|
+
const vertex = litVertexOf(variant);
|
|
9831
|
+
return kind === 'lightmap' && variant === 'instanced' ? lightmapRegionsBound(vertex) : vertex;
|
|
9832
|
+
}
|
|
9833
|
+
|
|
9423
9834
|
function litVertexOf(variant: string): string {
|
|
9424
9835
|
if (variant.startsWith('cloth')) {
|
|
9425
9836
|
const eight = variant[5] === '8';
|