@driftengine/core 4.8.4 → 4.8.6
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 +4332 -4250
- package/dist/build/shaderComments.js +0 -0
- package/dist/index.d.ts +10 -2
- package/dist/index.js +11 -1
- package/dist/render/ambientHarmonics.d.ts +46 -0
- package/dist/render/ambientHarmonics.js +80 -0
- package/dist/render/areaLights.d.ts +19 -0
- package/dist/render/areaLights.js +20 -0
- package/dist/render/backend/api.d.ts +13 -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 +74 -1
- package/dist/render/backend/webgl2/renderer.js +229 -37
- 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 +80 -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 +58 -7
- package/dist/render/backend/webgpu/renderer.js +329 -78
- 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 +2 -1
- package/dist/render/backend/webgpu/surfaceTexturePass.js +18 -4
- 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 +55 -1
- package/dist/render/clusteredLights.js +182 -8
- package/dist/render/fixtureAtlas.js +1 -1
- package/dist/render/instanceCull.js +7 -0
- package/dist/render/instances.d.ts +32 -10
- package/dist/render/instances.js +17 -8
- package/dist/render/lightBudget.d.ts +26 -1
- package/dist/render/lightBudget.js +26 -3
- package/dist/render/lightmap.d.ts +102 -0
- package/dist/render/lightmap.js +118 -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 +11 -0
- package/dist/render/particlePool.js +2 -0
- package/dist/render/particleSort.d.ts +34 -0
- package/dist/render/particleSort.js +96 -0
- package/dist/render/sceneTarget.d.ts +5 -0
- package/dist/render/sceneTarget.js +8 -1
- package/dist/render/shaders/bloom.d.ts +8 -4
- package/dist/render/shaders/bloom.js +20 -3
- package/dist/render/shaders/flat/areaLight.d.ts +10 -0
- package/dist/render/shaders/flat/areaLight.js +96 -0
- package/dist/render/shaders/flat/index.js +30 -4
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +107 -105
- package/dist/render/shaders/flat/models.d.ts +16 -4
- package/dist/render/shaders/flat/models.js +64 -4
- 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 +74 -37
- 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/flat.wgsl.d.ts +1631 -2044
- package/dist/render/shaders/generated/flat.wgsl.js +1669 -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/particle.d.ts +17 -1
- package/dist/render/shaders/particle.js +133 -1
- package/dist/render/shaders/skinBlur.js +25 -3
- package/dist/render/skinBlur.d.ts +8 -5
- package/dist/render/skinBlur.js +8 -5
- package/dist/render/surfaceModel.d.ts +21 -1
- package/dist/render/surfaceModel.js +22 -0
- package/dist/render/surfaceTexture.d.ts +14 -1
- package/dist/render/surfaceTexture.js +15 -2
- package/package.json +6 -6
- package/src/build/shaderComments.ts +0 -0
- package/src/index.ts +20 -1
- package/src/render/ambientHarmonics.ts +92 -0
- package/src/render/areaLights.ts +39 -0
- package/src/render/backend/api.ts +14 -1
- package/src/render/backend/webgl2/instanced.ts +12 -2
- package/src/render/backend/webgl2/renderer.ts +292 -35
- 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 +118 -12
- package/src/render/backend/webgpu/postPass.ts +7 -2
- package/src/render/backend/webgpu/renderer.ts +396 -95
- 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 +26 -5
- package/src/render/bloomChain.ts +69 -0
- package/src/render/bloomPass.ts +26 -2
- package/src/render/clusteredLights.ts +221 -7
- package/src/render/fixtureAtlas.ts +1 -1
- package/src/render/instanceCull.ts +5 -0
- package/src/render/instances.ts +40 -11
- package/src/render/lightBudget.ts +48 -2
- package/src/render/lightmap.ts +202 -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 +13 -0
- package/src/render/particleSort.ts +133 -0
- package/src/render/sceneTarget.ts +18 -1
- package/src/render/shaders/bloom.ts +20 -3
- package/src/render/shaders/flat/areaLight.ts +96 -0
- package/src/render/shaders/flat/index.ts +30 -4
- package/src/render/shaders/flat/main.ts +107 -105
- package/src/render/shaders/flat/models.ts +73 -8
- package/src/render/shaders/flat/normalMap.ts +25 -0
- package/src/render/shaders/flat/preamble.ts +74 -37
- package/src/render/shaders/flat/skin.ts +6 -5
- package/src/render/shaders/generated/bloom.wgsl.ts +13 -3
- package/src/render/shaders/generated/flat.wgsl.ts +1669 -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/particle.ts +134 -1
- package/src/render/shaders/skinBlur.ts +25 -3
- package/src/render/skinBlur.ts +8 -5
- package/src/render/surfaceModel.ts +36 -1
- package/src/render/surfaceTexture.ts +41 -3
|
@@ -22,7 +22,7 @@ import { batchBoxVisible, cullsInstances } from '../../instanceCull.js';
|
|
|
22
22
|
import { OcclusionBuffer } from '../../occlusion.js';
|
|
23
23
|
import { boundsVisible } from '../../visibility.js';
|
|
24
24
|
import { createPassRegistry, drainRegistry, passAt, registerIn, unregisterIn } from '../../pass.js';
|
|
25
|
-
import { TABLE_HEIGHT, TABLE_WIDTH, buildLightClusters, clusteredMode, createClusterLightSet, createClusterTable, fillClusterLightSet, } from '../../clusteredLights.js';
|
|
25
|
+
import { TABLE_HEIGHT, TABLE_WIDTH, buildLightClusters, clusteredMode, createClusterLightSet, createClusterTable, clusteredLightTotal, fillClusterLightSet, lightChannelsOf, } from '../../clusteredLights.js';
|
|
26
26
|
import { clamp } from '../../../math/scalar.js';
|
|
27
27
|
import { attachContextLoss } from '../../contextLoss.js';
|
|
28
28
|
import { isWeakGpuFamily, webgl2RendererName } from '../../gpuCapability.js';
|
|
@@ -32,7 +32,8 @@ import { FULL_LIGHT_BUDGET, nextLightBudget, planLightBudget, } from '../../unif
|
|
|
32
32
|
import { FILM_FRAG, FILM_VERT } from '../../shaders/film.js';
|
|
33
33
|
import { SceneTarget } from '../../sceneTarget.js';
|
|
34
34
|
import { FILM_LOOK_WITHOUT_COMPOSITE } from '../../shaders/filmLook.js';
|
|
35
|
-
import {
|
|
35
|
+
import { AMBIENT_SH_FLOATS, packAmbientSH } from '../../ambientHarmonics.js';
|
|
36
|
+
import { bloomProfileWarning, bloomThresholdOf, defaultBloomResponse, resolveBloomResponse, } from '../../bloomChain.js';
|
|
36
37
|
import { createEmptyTexture2D, createEmptyTexture2DArray, createEmptyTexture3D, createEmptyTextureCube, } from '../../emptyTexture.js';
|
|
37
38
|
import { LightField } from '../../driftLight/lightField.js';
|
|
38
39
|
import { WorldLightField } from '../../driftLight/worldLightField.js';
|
|
@@ -41,7 +42,8 @@ import { flatFrag, flatVert } from '../../shaders/flat/index.js';
|
|
|
41
42
|
import { skinEight } from '../../shaders/skinning.js';
|
|
42
43
|
import { MODEL_MAP_TEXTURE_UNIT, MORPH_DELTA_TEXTURE_UNIT, SKIN_PALETTE_TEXTURE_UNIT, } from '../../lightBudget.js';
|
|
43
44
|
import { MODEL_PARAM_FLOATS, eyeAxisInWorld, packModel } from '../../surfaceModel.js';
|
|
44
|
-
import { modelBound, skinHalfBound } from '../../shaders/flat/models.js';
|
|
45
|
+
import { lightmapRegionsBound, modelBound, skinHalfBound } from '../../shaders/flat/models.js';
|
|
46
|
+
import { lightmapTexels, warnRegionsWithoutPage, warnUnpagedLightmap, withLightmapUvs, } from '../../lightmap.js';
|
|
45
47
|
import { GlSkinScatter } from './skinScatter.js';
|
|
46
48
|
import { CLOTH_BINDING_TEXTURE_UNIT, CLOTH_PARTICLES_TEXTURE_UNIT, CLOTH_REST_TEXTURE_UNIT, } from '../../lightBudget.js';
|
|
47
49
|
import { SkinPaletteTexture } from './skinPaletteTexture.js';
|
|
@@ -79,7 +81,7 @@ import { GlassTintPrograms } from './glassTintPrograms.js';
|
|
|
79
81
|
import { FACE_COUNT, PointShadowMap } from '../../pointShadowMap.js';
|
|
80
82
|
import { createResolvedPointShadows, DEFAULT_SOURCE_RADIUS } from '../../pointShadowImage.js';
|
|
81
83
|
import { createBakeScratch, runPointShadowBakes, selectCastingLights, } from '../../pointShadowBudget.js';
|
|
82
|
-
import { DIRECTIONAL_SHADOW_UNITS, SUN_GLASS_TINT_TEXTURE_UNIT, POINT_GLASS_TINT_TEXTURE_UNIT, LIVE_POINT_SHADOW_MAPS, MAX_POINT_LIGHTS, NORMAL_TEXTURE_UNIT, ORM_TEXTURE_UNIT, POINT_SHADOW_POOL, POINT_SHADOW_UNITS, CLUSTER_TABLE_TEXTURE_UNIT, EMISSIVE_TEXTURE_UNIT, ENVIRONMENT_TEXTURE_UNIT, SURFACE_TEXTURE_UNIT, bindPointLights, REFRACT_SCENE_TEXTURE_UNIT, DRIFT_LIGHT_ATLAS_TEXTURE_UNIT, DRIFT_LIGHT_INDEX_TEXTURE_UNIT, COOKIE_TILE, FIXTURE_ATLAS_TEXTURE_UNIT, SURFACE_EFFECTS_TEXTURE_UNIT, bindAreaLights, bindAreaShadows, openCones, } from '../../lightBudget.js';
|
|
84
|
+
import { DIRECTIONAL_SHADOW_UNITS, SUN_GLASS_TINT_TEXTURE_UNIT, POINT_GLASS_TINT_TEXTURE_UNIT, LIVE_POINT_SHADOW_MAPS, MAX_POINT_LIGHTS, NORMAL_TEXTURE_UNIT, ORM_TEXTURE_UNIT, POINT_SHADOW_POOL, POINT_SHADOW_UNITS, CLUSTER_TABLE_TEXTURE_UNIT, EMISSIVE_TEXTURE_UNIT, ENVIRONMENT_TEXTURE_UNIT, SURFACE_TEXTURE_UNIT, bindPointLights, REFRACT_SCENE_TEXTURE_UNIT, DRIFT_LIGHT_ATLAS_TEXTURE_UNIT, DRIFT_LIGHT_INDEX_TEXTURE_UNIT, COOKIE_TILE, FIXTURE_ATLAS_TEXTURE_UNIT, SURFACE_EFFECTS_TEXTURE_UNIT, bindAreaLights, bindAreaShadows, openCones, PARTICLE_DEPTH_UNIT, } from '../../lightBudget.js';
|
|
83
85
|
import { resolveRenderQuality } from '../../renderQuality.js';
|
|
84
86
|
import { drawingBufferSize } from '../../drawingBuffer.js';
|
|
85
87
|
import { Mesh, createMeshIncremental } from '../../mesh.js';
|
|
@@ -180,6 +182,8 @@ export function createEnvironment(overrides = {}) {
|
|
|
180
182
|
lightIesAxes: new Float32Array(MAX_POINT_LIGHTS * 3),
|
|
181
183
|
/* −1, because tile 0 is a real cookie and no light here has asked for one. */
|
|
182
184
|
lightCookies: new Float32Array(MAX_POINT_LIGHTS).fill(-1),
|
|
185
|
+
/* Channel 1, the default every surface is on, so every light lights everything. */
|
|
186
|
+
lightChannels: new Float32Array(MAX_POINT_LIGHTS).fill(1),
|
|
183
187
|
/* None, so the shader's loop breaks before it reads anything. */
|
|
184
188
|
areaLights: null,
|
|
185
189
|
activeLightWorldIndices: new Int32Array(MAX_POINT_LIGHTS).fill(-1),
|
|
@@ -257,6 +261,8 @@ const IDENTITY_MAT4 = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
|
|
|
257
261
|
*/
|
|
258
262
|
export { MAX_DEPTH_LAYER } from '../../depthConvention.js';
|
|
259
263
|
/** The identity tint, handed back after any draw that asked for one. See `drawMesh`. */
|
|
264
|
+
/** The far corner of no highlight box: what an environment that names none reads. */
|
|
265
|
+
const NO_HIGHLIGHT = [0, 0, 0];
|
|
260
266
|
const WHITE_TINT = new Float32Array([1, 1, 1]);
|
|
261
267
|
/** The lit stage's clip plane when there is no mirror: zero, which clips nothing. */
|
|
262
268
|
const NO_CLIP_PLANE = new Float32Array(4);
|
|
@@ -640,6 +646,23 @@ export class WebGL2Renderer {
|
|
|
640
646
|
motionBlurScale = 1;
|
|
641
647
|
/** How much of the bloom ceiling this frame takes. See `setBloom`. */
|
|
642
648
|
bloomScale = 1;
|
|
649
|
+
/** The draw's own ambient, as `packAmbientSH` writes it; zeros are the frame's. */
|
|
650
|
+
ambientSH = new Float32Array(AMBIENT_SH_FLOATS);
|
|
651
|
+
/** Scratch for a sprite's depth conversion, so a particle draw allocates nothing. */
|
|
652
|
+
spriteInverseProjection = mat4.create();
|
|
653
|
+
/** Said once: a sprite asked for a soft edge where no depth copy could be had. */
|
|
654
|
+
warnedSpriteDepth = false;
|
|
655
|
+
/** Said once: more rectangles than the fixed arm shades, and no froxel table for the rest. */
|
|
656
|
+
warnedAreaOverflow = false;
|
|
657
|
+
/**
|
|
658
|
+
* `uEnvironmentDials` as it stands: which chain the probe holds, the reflectivity, the gain. One
|
|
659
|
+
* vector on the device, so written whole, and kept here so a setter moving one keeps the others.
|
|
660
|
+
*/
|
|
661
|
+
environmentDials = new Float32Array(3);
|
|
662
|
+
/** `uEmission` as it stands: the emissive gain and the night factor. */
|
|
663
|
+
emission = new Float32Array([1, 0]);
|
|
664
|
+
/** The ramp and each level's tint, as `resolveBloomResponse` writes them; the subtraction and white. */
|
|
665
|
+
bloomResponse = defaultBloomResponse();
|
|
643
666
|
/** In scene units, once `setBloom` has moved it; null reads the profile's. */
|
|
644
667
|
bloomThresholdSet = null;
|
|
645
668
|
/**
|
|
@@ -1675,9 +1698,7 @@ export class WebGL2Renderer {
|
|
|
1675
1698
|
gl.activeTexture(gl.TEXTURE0 + FIXTURE_ATLAS_TEXTURE_UNIT);
|
|
1676
1699
|
gl.bindTexture(gl.TEXTURE_2D, this.fixtureTexture);
|
|
1677
1700
|
gl.uniform1i(u['uFixtureAtlas'] ?? null, FIXTURE_ATLAS_TEXTURE_UNIT);
|
|
1678
|
-
gl.
|
|
1679
|
-
gl.uniform1f(u['uIesPlaneCount'] ?? null, this.iesPlanes);
|
|
1680
|
-
gl.uniform1f(u['uCookieTiles'] ?? null, this.cookieTiles);
|
|
1701
|
+
gl.uniform3f(u['uFixtureShape'] ?? null, this.iesRows, this.iesPlanes, this.cookieTiles);
|
|
1681
1702
|
gl.activeTexture(gl.TEXTURE0);
|
|
1682
1703
|
}
|
|
1683
1704
|
bindClusters(u, camera, env) {
|
|
@@ -1723,7 +1744,8 @@ export class WebGL2Renderer {
|
|
|
1723
1744
|
gl.uniform4fv(u['uClusterFrustum'] ?? null, this.clusterFrustum);
|
|
1724
1745
|
gl.uniformMatrix4fv(u['uView'] ?? null, false, camera.view);
|
|
1725
1746
|
this.clusterTable ??= createClusterTable();
|
|
1726
|
-
|
|
1747
|
+
/* The rectangles past the fixed arm's go into the table. See `ClusterLightSet.areas`. */
|
|
1748
|
+
const lights = fillClusterLightSet(env, this.clusterLights, this.shadedAreaLights);
|
|
1727
1749
|
/* A slot is read only under `POINT_SHADOWS`; see `lightHasFixture`. */
|
|
1728
1750
|
const shadowSlots = this.quality.pointShadows ? this.lightBudget.maxLights : 0;
|
|
1729
1751
|
buildLightClusters(lights, camera.view, near, far, tanHalfFovY, aspect, this.clusterTable,
|
|
@@ -1735,7 +1757,7 @@ export class WebGL2Renderer {
|
|
|
1735
1757
|
* guards the read as well, for a build that reaches it another way.
|
|
1736
1758
|
*/
|
|
1737
1759
|
shadowSlots);
|
|
1738
|
-
gl.uniform1i(u['uClustered'] ?? null, clusteredMode(lights, lights
|
|
1760
|
+
gl.uniform1i(u['uClustered'] ?? null, clusteredMode(lights, clusteredLightTotal(lights), shadowSlots));
|
|
1739
1761
|
gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, TABLE_WIDTH, TABLE_HEIGHT, gl.RGBA_INTEGER, gl.UNSIGNED_INT, this.clusterTable);
|
|
1740
1762
|
}
|
|
1741
1763
|
/**
|
|
@@ -2340,8 +2362,9 @@ export class WebGL2Renderer {
|
|
|
2340
2362
|
* one. Measured on hardware: WebGL2 drew neither the bend nor the bulge while WebGPU drew
|
|
2341
2363
|
* both, which is the two-backend comparison doing exactly what it is for.
|
|
2342
2364
|
*/
|
|
2343
|
-
|
|
2344
|
-
|
|
2365
|
+
const drawn = withLightmapUvs(data);
|
|
2366
|
+
this.warmVariants(drawn);
|
|
2367
|
+
return new Mesh(this.gl, drawn, options.dynamic === true);
|
|
2345
2368
|
}
|
|
2346
2369
|
/**
|
|
2347
2370
|
* Geometry in, handle out — but the geometry lands over as many frames as the caller gives it.
|
|
@@ -2353,8 +2376,9 @@ export class WebGL2Renderer {
|
|
|
2353
2376
|
* the signal that it was abandoned.
|
|
2354
2377
|
*/
|
|
2355
2378
|
createMeshIncremental(data, options = {}) {
|
|
2356
|
-
|
|
2357
|
-
|
|
2379
|
+
const drawn = withLightmapUvs(data);
|
|
2380
|
+
this.warmVariants(drawn);
|
|
2381
|
+
const { mesh, upload } = createMeshIncremental(this.gl, drawn, options.dynamic === true);
|
|
2358
2382
|
return {
|
|
2359
2383
|
mesh,
|
|
2360
2384
|
upload: {
|
|
@@ -2517,6 +2541,10 @@ export class WebGL2Renderer {
|
|
|
2517
2541
|
createSurfaceTextureArray(sources, options = {}) {
|
|
2518
2542
|
return new SurfaceTexture(this.gl, sources, options, this.compressedFormats);
|
|
2519
2543
|
}
|
|
2544
|
+
/** A baked lightmap page, as a `lightmapModel` material's `modelMap`. See `RendererApi`. */
|
|
2545
|
+
createLightmap(page) {
|
|
2546
|
+
return new SurfaceTexture(this.gl, lightmapTexels(page), { wrap: 'clamp', mipmap: false });
|
|
2547
|
+
}
|
|
2520
2548
|
/**
|
|
2521
2549
|
* Replace a surface texture's pixels, keeping the GPU object and its sampler state.
|
|
2522
2550
|
*
|
|
@@ -2590,7 +2618,7 @@ export class WebGL2Renderer {
|
|
|
2590
2618
|
return held;
|
|
2591
2619
|
const { gl } = this;
|
|
2592
2620
|
const label = `flat.${variant}.${kind}.${half}`;
|
|
2593
|
-
const program = compileProgram(gl,
|
|
2621
|
+
const program = compileProgram(gl, modelVertexOf(variant, kind), skinHalfBound(modelBound(this.flatFragSource, kind), half), label);
|
|
2594
2622
|
const made = { program, uniforms: uniformLocations(gl, program, label) };
|
|
2595
2623
|
this.modelFlat.set(key, made);
|
|
2596
2624
|
this.flatTargets.push(made);
|
|
@@ -2738,6 +2766,7 @@ export class WebGL2Renderer {
|
|
|
2738
2766
|
this.currentMaterial = material;
|
|
2739
2767
|
/* What the blur spreads this skin by: the last material to name a profile says. */
|
|
2740
2768
|
const model = material?.model;
|
|
2769
|
+
warnUnpagedLightmap(model, material?.modelMap);
|
|
2741
2770
|
if (model?.kind === 'skin' && this.skinScatter !== null) {
|
|
2742
2771
|
this.skinScatter.setProfile(model.profile, model.scatterColor[0] * model.radius, model.scatterColor[1] * model.radius, model.scatterColor[2] * model.radius);
|
|
2743
2772
|
}
|
|
@@ -2773,7 +2802,8 @@ export class WebGL2Renderer {
|
|
|
2773
2802
|
v[0] = material?.cutout ?? 0;
|
|
2774
2803
|
v[1] = CUTOUT_RESOLVE_CODE[this.cutoutResolveNow(material, translucent)];
|
|
2775
2804
|
v[2] = this.cutoutFrame;
|
|
2776
|
-
|
|
2805
|
+
/* The material's lighting channels, which the lamps' are compared with. See `uCutout`. */
|
|
2806
|
+
v[3] = lightChannelsOf(material?.lightChannels);
|
|
2777
2807
|
return v;
|
|
2778
2808
|
}
|
|
2779
2809
|
/**
|
|
@@ -2884,6 +2914,8 @@ export class WebGL2Renderer {
|
|
|
2884
2914
|
gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.emptyTexture2DArray);
|
|
2885
2915
|
this.materialFlags[0] = 0;
|
|
2886
2916
|
gl.uniform4iv(u['uMaterialFlags'] ?? null, this.materialFlags);
|
|
2917
|
+
/* No image to cut, and the channels all the same: a material needs no albedo to name them. */
|
|
2918
|
+
gl.uniform4f(u['uCutout'] ?? null, 0, 0, 0, lightChannelsOf(material?.lightChannels));
|
|
2887
2919
|
return;
|
|
2888
2920
|
}
|
|
2889
2921
|
albedo.bind(gl, SURFACE_TEXTURE_UNIT);
|
|
@@ -2932,6 +2964,17 @@ export class WebGL2Renderer {
|
|
|
2932
2964
|
* is 1 and `bindMeshPass` restores it, so it scales geometry that declared grain and
|
|
2933
2965
|
* leaves geometry that did not exactly as it is.
|
|
2934
2966
|
*/
|
|
2967
|
+
/**
|
|
2968
|
+
* The ambient the following draws take, as nine spherical-harmonic coefficients of incoming
|
|
2969
|
+
* radiance — red, green and blue of each, twenty-seven numbers — or `null` for the frame's own:
|
|
2970
|
+
* the sky's gradient, or the probes'. See `ambientHarmonics.ts` for the basis and the axes.
|
|
2971
|
+
*
|
|
2972
|
+
* **For a thing lit by where it stands**: a consumer sampling a stage's baked volume of indirect
|
|
2973
|
+
* light at a character every frame sets the result around that character's draws, and the
|
|
2974
|
+
* character takes the light of its spot rather than the frame's. It replaces the diffuse ambient
|
|
2975
|
+
* only — a glossy surface still reflects the probes — and it is pass state like the material,
|
|
2976
|
+
* which `bindMeshPass` clears, so a mirror or a probe bake drawn after does not inherit it.
|
|
2977
|
+
*/
|
|
2935
2978
|
/**
|
|
2936
2979
|
* How much of the environment the following draws mirror, 0 to 1.
|
|
2937
2980
|
*
|
|
@@ -2942,11 +2985,23 @@ export class WebGL2Renderer {
|
|
|
2942
2985
|
* Defaults to zero and bindMeshPass restores it, so a scene that never calls this looks
|
|
2943
2986
|
* exactly as it did.
|
|
2944
2987
|
*/
|
|
2988
|
+
setAmbientSH(coefficients) {
|
|
2989
|
+
if (this.contextLost)
|
|
2990
|
+
return;
|
|
2991
|
+
packAmbientSH(coefficients, this.ambientSH);
|
|
2992
|
+
this.materials.dirty();
|
|
2993
|
+
for (const target of this.flatTargets) {
|
|
2994
|
+
this.gl.useProgram(target.program);
|
|
2995
|
+
this.gl.uniform4fv(target.uniforms['uAmbientSH[0]'] ?? null, this.ambientSH);
|
|
2996
|
+
}
|
|
2997
|
+
this.useFlatProgram();
|
|
2998
|
+
}
|
|
2945
2999
|
setSurfaceReflectivity(amount) {
|
|
2946
3000
|
if (this.contextLost)
|
|
2947
3001
|
return;
|
|
2948
3002
|
this.materials.dirty();
|
|
2949
|
-
this.
|
|
3003
|
+
this.environmentDials[1] = Math.min(1, Math.max(0, amount));
|
|
3004
|
+
this.writeFlatVector('uEnvironmentDials', this.environmentDials);
|
|
2950
3005
|
}
|
|
2951
3006
|
/**
|
|
2952
3007
|
* How bright the environment the surfaces drawn next reflect is. 1 is the default and identity.
|
|
@@ -2964,7 +3019,8 @@ export class WebGL2Renderer {
|
|
|
2964
3019
|
if (this.contextLost)
|
|
2965
3020
|
return;
|
|
2966
3021
|
this.materials.dirty();
|
|
2967
|
-
this.
|
|
3022
|
+
this.environmentDials[2] = Math.max(0, gain);
|
|
3023
|
+
this.writeFlatVector('uEnvironmentDials', this.environmentDials);
|
|
2968
3024
|
}
|
|
2969
3025
|
/**
|
|
2970
3026
|
* Kept as a no-op that says so once, because it was a stand-in for a term that now exists.
|
|
@@ -3031,6 +3087,20 @@ export class WebGL2Renderer {
|
|
|
3031
3087
|
}
|
|
3032
3088
|
this.useFlatProgram();
|
|
3033
3089
|
}
|
|
3090
|
+
/** A vector of material state, two to four floats, into every flat program. */
|
|
3091
|
+
writeFlatVector(name, value) {
|
|
3092
|
+
for (const target of this.flatTargets) {
|
|
3093
|
+
this.gl.useProgram(target.program);
|
|
3094
|
+
const at = target.uniforms[name] ?? null;
|
|
3095
|
+
if (value.length === 2)
|
|
3096
|
+
this.gl.uniform2fv(at, value);
|
|
3097
|
+
else if (value.length === 3)
|
|
3098
|
+
this.gl.uniform3fv(at, value);
|
|
3099
|
+
else
|
|
3100
|
+
this.gl.uniform4fv(at, value);
|
|
3101
|
+
}
|
|
3102
|
+
this.useFlatProgram();
|
|
3103
|
+
}
|
|
3034
3104
|
/** One int of material state, into every flat program, for `writeFlatFloat`'s reason. */
|
|
3035
3105
|
writeFlatInt(name, value) {
|
|
3036
3106
|
for (const target of this.flatTargets) {
|
|
@@ -3094,8 +3164,13 @@ export class WebGL2Renderer {
|
|
|
3094
3164
|
if (this.contextLost)
|
|
3095
3165
|
return;
|
|
3096
3166
|
this.materials.dirty();
|
|
3097
|
-
|
|
3098
|
-
|
|
3167
|
+
const strength = Math.min(1, Math.max(0, amount));
|
|
3168
|
+
const cycles = Math.max(0.01, cyclesPerMetre);
|
|
3169
|
+
for (const target of this.flatTargets) {
|
|
3170
|
+
this.gl.useProgram(target.program);
|
|
3171
|
+
this.gl.uniform2f(target.uniforms['uRelief'] ?? null, strength, cycles);
|
|
3172
|
+
}
|
|
3173
|
+
this.useFlatProgram();
|
|
3099
3174
|
}
|
|
3100
3175
|
/**
|
|
3101
3176
|
* How hard the bound surface texture's own luminance turns the shading normal, 0 for not at all.
|
|
@@ -3153,7 +3228,8 @@ export class WebGL2Renderer {
|
|
|
3153
3228
|
if (this.contextLost)
|
|
3154
3229
|
return;
|
|
3155
3230
|
this.materials.dirty();
|
|
3156
|
-
this.
|
|
3231
|
+
this.emission[0] = gain;
|
|
3232
|
+
this.writeFlatVector('uEmission', this.emission);
|
|
3157
3233
|
}
|
|
3158
3234
|
/* -- Instanced meshes --------------------------------------------------------------- */
|
|
3159
3235
|
/** The instanced program, compiled on first use. See `ensureInstancedProgram`. */
|
|
@@ -3241,6 +3317,7 @@ export class WebGL2Renderer {
|
|
|
3241
3317
|
submitInstanced(batch, data, opacity, blend, options) {
|
|
3242
3318
|
if (this.contextLost)
|
|
3243
3319
|
return;
|
|
3320
|
+
warnRegionsWithoutPage(this.currentMaterial?.model, data.lightmapRegions);
|
|
3244
3321
|
const mesh = batch.mesh;
|
|
3245
3322
|
/* Nor a batch of a mesh with nothing in it: see `Mesh.indexCount`. */
|
|
3246
3323
|
if (!mesh.complete || mesh.indexCount === 0)
|
|
@@ -3702,8 +3779,10 @@ export class WebGL2Renderer {
|
|
|
3702
3779
|
const { gl } = this;
|
|
3703
3780
|
const u = batch.uniforms;
|
|
3704
3781
|
gl.useProgram(batch.program);
|
|
3705
|
-
batch.upload(gl, data);
|
|
3782
|
+
batch.upload(gl, data, camera.position);
|
|
3706
3783
|
batch.bindMaterial(gl);
|
|
3784
|
+
if (batch.material === 'sprite')
|
|
3785
|
+
this.bindSpriteFades(batch, camera);
|
|
3707
3786
|
gl.uniformMatrix4fv(u['uViewProj'] ?? null, false, this.viewProjFor(camera));
|
|
3708
3787
|
gl.uniform3fv(u['uCameraPos'] ?? null, camera.position);
|
|
3709
3788
|
gl.uniform1f(u['uTime'] ?? null, timeSeconds);
|
|
@@ -3730,6 +3809,35 @@ export class WebGL2Renderer {
|
|
|
3730
3809
|
if (this.materialDoubleSided)
|
|
3731
3810
|
gl.enable(gl.CULL_FACE);
|
|
3732
3811
|
}
|
|
3812
|
+
/**
|
|
3813
|
+
* A sprite's two fades and the depth copy the soft one reads. The copy is the frame's opaque
|
|
3814
|
+
* depth, so it is taken only in the frame's own picture: in a mirror or a probe's face it would
|
|
3815
|
+
* be another view's, and the edge stays hard there. Without a scene target there is no copy at
|
|
3816
|
+
* all, and the renderer says so once rather than drawing a hard edge that was asked to be soft.
|
|
3817
|
+
*/
|
|
3818
|
+
bindSpriteFades(batch, camera) {
|
|
3819
|
+
const { gl } = this;
|
|
3820
|
+
const u = batch.uniforms;
|
|
3821
|
+
const scene = this.sceneTarget;
|
|
3822
|
+
let depth = null;
|
|
3823
|
+
if (batch.softDepth > 0 && !this.reflectionPassActive && !this.probePassActive) {
|
|
3824
|
+
depth = scene?.snapshotDepth(true) ?? null;
|
|
3825
|
+
if (depth === null && !this.warnedSpriteDepth) {
|
|
3826
|
+
this.warnedSpriteDepth = true;
|
|
3827
|
+
console.warn("WebGL2: a 'sprite' asked for a soft edge, which reads a copy of the frame's depth, and " +
|
|
3828
|
+
'this profile keeps none — screenEffects is off, or the driver refused the copy. The ' +
|
|
3829
|
+
'edge is hard.');
|
|
3830
|
+
}
|
|
3831
|
+
}
|
|
3832
|
+
gl.activeTexture(gl.TEXTURE0 + PARTICLE_DEPTH_UNIT);
|
|
3833
|
+
gl.bindTexture(gl.TEXTURE_2D, depth ?? this.emptyTexture2D);
|
|
3834
|
+
gl.uniform1i(u['uDepth'] ?? null, PARTICLE_DEPTH_UNIT);
|
|
3835
|
+
gl.uniform4f(u['uFade'] ?? null, batch.softDepth, batch.cameraFade, depth === null ? 0 : 1, 0);
|
|
3836
|
+
mat4.invert(this.spriteInverseProjection, camera.projection);
|
|
3837
|
+
const inverse = this.spriteInverseProjection;
|
|
3838
|
+
gl.uniform4f(u['uDepthToViewZ'] ?? null, inverse[10] ?? 0, inverse[14] ?? 0, inverse[11] ?? 0, inverse[15] ?? 1);
|
|
3839
|
+
gl.uniform2f(u['uViewport'] ?? null, scene?.frameWidth ?? gl.drawingBufferWidth, scene?.frameHeight ?? gl.drawingBufferHeight);
|
|
3840
|
+
}
|
|
3733
3841
|
/** Build a batch for electrical arcs. `capacity` is in segments, not arcs. */
|
|
3734
3842
|
createBolts(segmentCapacity, label = 'bolts') {
|
|
3735
3843
|
return new BoltBatch(this.gl, segmentCapacity, label);
|
|
@@ -4643,7 +4751,7 @@ export class WebGL2Renderer {
|
|
|
4643
4751
|
for (const [key, entry] of this.modelFlat) {
|
|
4644
4752
|
const [variant, kind, half] = key.split('|');
|
|
4645
4753
|
const label = `flat.${variant}.${kind}.${half}`;
|
|
4646
|
-
const program = compileProgram(gl,
|
|
4754
|
+
const program = compileProgram(gl, modelVertexOf(variant, kind), skinHalfBound(modelBound(this.flatFragSource, kind), half), label);
|
|
4647
4755
|
gl.deleteProgram(entry.program);
|
|
4648
4756
|
this.modelFlat.set(key, { program, uniforms: uniformLocations(gl, program, label) });
|
|
4649
4757
|
}
|
|
@@ -5386,11 +5494,19 @@ export class WebGL2Renderer {
|
|
|
5386
5494
|
* and wrong for a day whose exposure spans 2.5 to 14: a courtyard blooming at noon, or candles
|
|
5387
5495
|
* never blooming at night. A caller whose exposure moves passes what it wants on screen over the
|
|
5388
5496
|
* exposure. Omitted leaves it where it was, so a caller that never passes one keeps the profile's.
|
|
5497
|
+
*
|
|
5498
|
+
* **`response` is how a colour comes in past the threshold and what colour each octave of the
|
|
5499
|
+
* halo takes** — a ramp in place of the subtraction, and a tint a level — held until moved like
|
|
5500
|
+
* the threshold; `null` goes back to the subtraction and white. See `BloomResponse`: it is what
|
|
5501
|
+
* matches a stage built for another engine's bloom, whose tints are how a strength past 1 is
|
|
5502
|
+
* asked for.
|
|
5389
5503
|
*/
|
|
5390
|
-
setBloom(scale, threshold) {
|
|
5504
|
+
setBloom(scale, threshold, response) {
|
|
5391
5505
|
this.bloomScale = Math.min(Math.max(scale, 0), 1);
|
|
5392
5506
|
if (threshold !== undefined)
|
|
5393
5507
|
this.bloomThresholdSet = bloomThresholdOf(threshold);
|
|
5508
|
+
if (response !== undefined)
|
|
5509
|
+
resolveBloomResponse(response, this.bloomResponse);
|
|
5394
5510
|
}
|
|
5395
5511
|
/**
|
|
5396
5512
|
* How thick the air is, this frame: a global participating medium filling the whole frustum.
|
|
@@ -5906,6 +6022,7 @@ export class WebGL2Renderer {
|
|
|
5906
6022
|
? {
|
|
5907
6023
|
strength: bloomStrength,
|
|
5908
6024
|
threshold: this.bloomThresholdSet ?? this.quality.bloomThreshold,
|
|
6025
|
+
response: this.bloomResponse,
|
|
5909
6026
|
}
|
|
5910
6027
|
: undefined;
|
|
5911
6028
|
/* The grade travels to the resolve only where the mesh pass gave it up. See `bindMeshPass`. */
|
|
@@ -6188,6 +6305,15 @@ export class WebGL2Renderer {
|
|
|
6188
6305
|
bindMeshPass(camera, env) {
|
|
6189
6306
|
/* The pass's own material is reopened, so the next draw opens one. See `materialChanges.ts`. */
|
|
6190
6307
|
this.materials.dirty();
|
|
6308
|
+
/* Rectangles past the fixed arm's are the froxel table's, and without it nobody shades them. */
|
|
6309
|
+
if (!this.warnedAreaOverflow &&
|
|
6310
|
+
!this.quality.clusteredLights &&
|
|
6311
|
+
(env.areaLights?.count ?? 0) > this.shadedAreaLights) {
|
|
6312
|
+
this.warnedAreaOverflow = true;
|
|
6313
|
+
console.warn(`WebGL2: ${env.areaLights?.count ?? 0} rectangular lights and ${this.shadedAreaLights} ` +
|
|
6314
|
+
'shaded without clusteredLights: the rest are not drawn. Turn clusteredLights on, and the ' +
|
|
6315
|
+
'froxel table shades every one past the fixed ones, unshadowed.');
|
|
6316
|
+
}
|
|
6191
6317
|
/*
|
|
6192
6318
|
* Captured here rather than taken as an argument to `endFrame`, because this is the
|
|
6193
6319
|
* camera the world was drawn with and a caller should not have to hand it over twice.
|
|
@@ -6297,6 +6423,13 @@ export class WebGL2Renderer {
|
|
|
6297
6423
|
this.materialHasMaps = false;
|
|
6298
6424
|
this.currentMaterial = null;
|
|
6299
6425
|
this.currentSurfaceTexture = null;
|
|
6426
|
+
/* Nor an ambient of its own: the frame's, until a draw asks. See `setAmbientSH`. */
|
|
6427
|
+
this.ambientSH.fill(0);
|
|
6428
|
+
/* No reflection and an environment at its own brightness; the emission the world asks for. */
|
|
6429
|
+
this.environmentDials[1] = 0;
|
|
6430
|
+
this.environmentDials[2] = 1;
|
|
6431
|
+
this.emission[0] = env.emissiveGain;
|
|
6432
|
+
this.emission[1] = env.nightFactor;
|
|
6300
6433
|
for (const target of this.flatTargets) {
|
|
6301
6434
|
this.gl.useProgram(target.program);
|
|
6302
6435
|
this.writeMeshPassState(target.uniforms, camera, env);
|
|
@@ -6492,13 +6625,18 @@ export class WebGL2Renderer {
|
|
|
6492
6625
|
this.oitWeight = 0;
|
|
6493
6626
|
this.ditherFade = 0;
|
|
6494
6627
|
gl.uniform2f(u['uWriteMode'] ?? null, 0, 0);
|
|
6495
|
-
/*
|
|
6496
|
-
|
|
6497
|
-
|
|
6498
|
-
|
|
6628
|
+
/* The draw's own ambient as it stands, which `bindMeshPass` has just cleared: a program built
|
|
6629
|
+
in the middle of a pass takes what the caller set. See `setAmbientSH`. */
|
|
6630
|
+
gl.uniform4fv(u['uAmbientSH[0]'] ?? null, this.ambientSH);
|
|
6631
|
+
/*
|
|
6632
|
+
* Which chain the probe holds, no environment reflection, and an environment at its own
|
|
6633
|
+
* brightness — the reflectivity's identity is 0 and the gain's is 1 — as `bindMeshPass` set
|
|
6634
|
+
* them, or as a draw since has: a program built in the middle of a pass takes what stands.
|
|
6635
|
+
* See `setSurfaceReflectivity` and `setEnvironmentGain`.
|
|
6636
|
+
*/
|
|
6637
|
+
gl.uniform3fv(u['uEnvironmentDials'] ?? null, this.environmentDials);
|
|
6499
6638
|
/* And no relief unless a caller asks, for the same reason. */
|
|
6500
|
-
gl.
|
|
6501
|
-
gl.uniform1f(u['uReliefCycles'] ?? null, 60);
|
|
6639
|
+
gl.uniform2f(u['uRelief'] ?? null, 0, 60);
|
|
6502
6640
|
/* And no relief read off a surface texture either, for the same reason. */
|
|
6503
6641
|
gl.uniform1f(u['uTextureRelief'] ?? null, 0);
|
|
6504
6642
|
/* The mirror's plane in a reflection pass, and zero — which clips nothing — in every other. */
|
|
@@ -6507,8 +6645,8 @@ export class WebGL2Renderer {
|
|
|
6507
6645
|
resolveSurfaceScene(env, this.surfaceScene);
|
|
6508
6646
|
gl.uniform4fv(u['uSurfaceScene'] ?? null, this.surfaceScene);
|
|
6509
6647
|
bindAtmosphere(gl, u, env, this.reflectionPassActive ? this.reflectionAtmosphereY : (camera.position[1] ?? 0), this.quality.underwaterAtmosphere);
|
|
6510
|
-
|
|
6511
|
-
gl.
|
|
6648
|
+
/* The emissive gain and the night factor, as the pass set them. See `uEmission`. */
|
|
6649
|
+
gl.uniform2fv(u['uEmission'] ?? null, this.emission);
|
|
6512
6650
|
/* Absent means zero, so a world written before this term existed adds nothing. */
|
|
6513
6651
|
gl.uniform1f(u['uNightEmissive'] ?? null, env.nightEmissive ?? 0);
|
|
6514
6652
|
/*
|
|
@@ -6530,8 +6668,10 @@ export class WebGL2Renderer {
|
|
|
6530
6668
|
gl.uniform1f(u['uShadowMaxSlope'] ?? null, this.quality.directionalShadowMaxSlope);
|
|
6531
6669
|
gl.uniform1i(u['uShadowFilterTaps'] ?? null, this.quality.shadowFilterTaps);
|
|
6532
6670
|
gl.uniform3fv(u['uHighlightMin'] ?? null, env.highlightMin);
|
|
6533
|
-
|
|
6534
|
-
|
|
6671
|
+
/* The far corner, and the gain in its w. See `uHighlightMax`. */
|
|
6672
|
+
/* An environment written by hand may leave the box out; then there is none, as before. */
|
|
6673
|
+
const max = env.highlightMax ?? NO_HIGHLIGHT;
|
|
6674
|
+
gl.uniform4f(u['uHighlightMax'] ?? null, max[0] ?? 0, max[1] ?? 0, max[2] ?? 0, env.highlightGain ?? 0);
|
|
6535
6675
|
bindPointLights(gl, u, env, this.quality.pointLightFalloff);
|
|
6536
6676
|
this.bindClusters(u, camera, env);
|
|
6537
6677
|
this.bindIesAtlas(u);
|
|
@@ -6627,7 +6767,8 @@ export class WebGL2Renderer {
|
|
|
6627
6767
|
* between them; there is one array, so `RenderQuality.environmentPrefilter` decides what the
|
|
6628
6768
|
* convolution writes and a profile that changes it has to bake again.
|
|
6629
6769
|
*/
|
|
6630
|
-
|
|
6770
|
+
this.environmentDials[0] = this.quality.environmentPrefilter ? 1 : 0;
|
|
6771
|
+
gl.uniform3fv(u['uEnvironmentDials'] ?? null, this.environmentDials);
|
|
6631
6772
|
gl.uniform1f(u['uEnvironmentMaxLod'] ?? null, array?.ggxMaxLevel ?? 0);
|
|
6632
6773
|
/*
|
|
6633
6774
|
* The map's edge, which used to be `exp2(maxLod)` and is not any more: the chain stops one
|
|
@@ -6817,12 +6958,17 @@ export class WebGL2Renderer {
|
|
|
6817
6958
|
return;
|
|
6818
6959
|
this.drawBudget.ask();
|
|
6819
6960
|
this.takeMaterial();
|
|
6820
|
-
/*
|
|
6961
|
+
/*
|
|
6962
|
+
* Skin under the screen-space blur draws its halves: the frame's, then at once its diffuse's
|
|
6963
|
+
* light and its colour, each into the blur's own target.
|
|
6964
|
+
*/
|
|
6821
6965
|
const splits = this.skinSplits();
|
|
6822
6966
|
this.drawMeshHalf(mesh, model, depthLayer, tint, splits ? 'scene' : 'whole');
|
|
6823
6967
|
if (splits && this.skinScatter !== null) {
|
|
6824
6968
|
this.skinScatter.begin();
|
|
6825
6969
|
this.drawMeshHalf(mesh, model, depthLayer, tint, 'diffuse');
|
|
6970
|
+
this.skinScatter.beginAlbedo();
|
|
6971
|
+
this.drawMeshHalf(mesh, model, depthLayer, tint, 'albedo');
|
|
6826
6972
|
this.sceneTarget?.bind();
|
|
6827
6973
|
}
|
|
6828
6974
|
}
|
|
@@ -6996,8 +7142,9 @@ export class WebGL2Renderer {
|
|
|
6996
7142
|
* depth 1.0 against a buffer cleared to 1.0, and under `LESS` it would not draw at
|
|
6997
7143
|
* all. Film, caustics and text lie flush on surfaces for the same reason.
|
|
6998
7144
|
*/
|
|
6999
|
-
/* A skin's diffuse
|
|
7000
|
-
|
|
7145
|
+
/* A skin's diffuse and colour halves are the same surface again: each finds the depth its
|
|
7146
|
+
frame half wrote. */
|
|
7147
|
+
const again = half === 'diffuse' || half === 'albedo';
|
|
7001
7148
|
gl.depthFunc(again ? gl.EQUAL : this.reversedDepth ? gl.GREATER : gl.LESS);
|
|
7002
7149
|
if (again)
|
|
7003
7150
|
gl.depthMask(false);
|
|
@@ -7441,6 +7588,43 @@ export class WebGL2Renderer {
|
|
|
7441
7588
|
this.probeSweeps?.baked(0);
|
|
7442
7589
|
return true;
|
|
7443
7590
|
}
|
|
7591
|
+
/**
|
|
7592
|
+
* One layer of the declared probe grid from an image, the way `setEnvironmentImage` fills a
|
|
7593
|
+
* single probe: the same equirectangular image of linear RGB floats, projected onto the capture
|
|
7594
|
+
* cube and prefiltered into that layer, the others left as they are. Returns false — and says
|
|
7595
|
+
* why — where there is no probe, no grid, or no such layer.
|
|
7596
|
+
*
|
|
7597
|
+
* **What it is for**: a scene baked somewhere else. Another engine's reflection captures are
|
|
7598
|
+
* images taken where each one stands; a consumer fits them to a lattice — for each point, the
|
|
7599
|
+
* capture whose reach holds it — and hands each point its image here, where `bakeProbe` would
|
|
7600
|
+
* have drawn this engine's own world into it.
|
|
7601
|
+
*
|
|
7602
|
+
* `options.irradiance` decides, at grid scope as a bake's does, whether the grid also lights the
|
|
7603
|
+
* scene's diffuse ambient. A grid that crossfades writes the set being swept, as a bake does.
|
|
7604
|
+
*/
|
|
7605
|
+
setProbeLayerImage(layer, image, options) {
|
|
7606
|
+
if (this.contextLost)
|
|
7607
|
+
return false;
|
|
7608
|
+
const probe = this.probeCapture;
|
|
7609
|
+
const array = this.probeArray;
|
|
7610
|
+
const grid = this.probes;
|
|
7611
|
+
const sweeps = this.probeSweeps;
|
|
7612
|
+
if (probe === null || array === null || grid === null || sweeps === null) {
|
|
7613
|
+
console.warn('Renderer: setProbeLayerImage needs a reflection probe (`reflectionProbeSize`) and a grid ' +
|
|
7614
|
+
'declared with setProbeGrid; the image was ignored.');
|
|
7615
|
+
return false;
|
|
7616
|
+
}
|
|
7617
|
+
if (!Number.isInteger(layer) || layer < 0 || layer >= grid.layers) {
|
|
7618
|
+
console.warn(`Renderer: setProbeLayerImage was given layer ${layer}, and the grid has ${grid.layers}.`);
|
|
7619
|
+
return false;
|
|
7620
|
+
}
|
|
7621
|
+
if (!probe.upload(this.gl, equirectToCubeFaces(image, probe.size)))
|
|
7622
|
+
return false;
|
|
7623
|
+
this.probeAmbient = options?.irradiance ?? true;
|
|
7624
|
+
this.environmentPrefilter?.run(this.gl, probe, array, sweeps.writeLayer(layer), ENVIRONMENT_TEXTURE_UNIT, this.quality.environmentPrefilterSamples, this.quality.environmentPrefilter);
|
|
7625
|
+
sweeps.baked(layer);
|
|
7626
|
+
return true;
|
|
7627
|
+
}
|
|
7444
7628
|
/**
|
|
7445
7629
|
* Draw a volume of light: a beam from a lamp, a shaft through a window, the cone under a
|
|
7446
7630
|
* street light.
|
|
@@ -7758,6 +7942,14 @@ const NO_MODEL_PARAMS = new Float32Array(MODEL_PARAM_FLOATS);
|
|
|
7758
7942
|
* The lit vertex stage a program of this variant compiles: the standard programs' and the cloth's,
|
|
7759
7943
|
* which `modelProgram` pairs a model's fragment stage with.
|
|
7760
7944
|
*/
|
|
7945
|
+
/**
|
|
7946
|
+
* The vertex stage a model's program draws with: the variant's own, but for a lightmapped instanced
|
|
7947
|
+
* batch, whose tint lane is each instance's region of the page. See `lightmap.ts`.
|
|
7948
|
+
*/
|
|
7949
|
+
function modelVertexOf(variant, kind) {
|
|
7950
|
+
const vertex = litVertexOf(variant);
|
|
7951
|
+
return kind === 'lightmap' && variant === 'instanced' ? lightmapRegionsBound(vertex) : vertex;
|
|
7952
|
+
}
|
|
7761
7953
|
function litVertexOf(variant) {
|
|
7762
7954
|
if (variant.startsWith('cloth')) {
|
|
7763
7955
|
const eight = variant[5] === '8';
|
|
@@ -10,9 +10,13 @@ export declare class GlSkinScatter {
|
|
|
10
10
|
private readonly depthToViewZ;
|
|
11
11
|
private diffuse;
|
|
12
12
|
private across;
|
|
13
|
+
private albedo;
|
|
13
14
|
private multisampled;
|
|
15
|
+
private albedoMultisampled;
|
|
14
16
|
private drawFramebuffer;
|
|
15
17
|
private resolveFramebuffer;
|
|
18
|
+
private albedoFramebuffer;
|
|
19
|
+
private albedoResolveFramebuffer;
|
|
16
20
|
private acrossFramebuffer;
|
|
17
21
|
private width;
|
|
18
22
|
private height;
|
|
@@ -21,6 +25,7 @@ export declare class GlSkinScatter {
|
|
|
21
25
|
/** Refused once and for good: a driver that will not render to half floats. */
|
|
22
26
|
private unavailable;
|
|
23
27
|
private cleared;
|
|
28
|
+
private albedoCleared;
|
|
24
29
|
private drawn;
|
|
25
30
|
constructor(gl: WebGL2RenderingContext);
|
|
26
31
|
/** Whether any skin's diffuse was drawn this frame. */
|
|
@@ -34,6 +39,8 @@ export declare class GlSkinScatter {
|
|
|
34
39
|
ready(scene: SceneTarget): boolean;
|
|
35
40
|
/** Bind the target for one skin's diffuse half, cleared the first time a frame does. */
|
|
36
41
|
begin(): void;
|
|
42
|
+
/** Bind the target for the same skin's colour half, cleared the first time a frame does. */
|
|
43
|
+
beginAlbedo(): void;
|
|
37
44
|
/**
|
|
38
45
|
* Across into the second target, then down and added into the frame, which `scene` binds. `depth`
|
|
39
46
|
* is the frame's depth as a single-sampled texture, or null where none could be had — and then
|
|
@@ -42,8 +49,15 @@ export declare class GlSkinScatter {
|
|
|
42
49
|
spread(scene: SceneTarget, depth: WebGLTexture | null, empty: WebGLTexture, projection: ArrayLike<number>, inverse: ArrayLike<number>): void;
|
|
43
50
|
/** Spent at the frame's start: what was drawn into the target was last frame's. */
|
|
44
51
|
reset(): void;
|
|
52
|
+
/** A multisampled target into its single-sampled texture. */
|
|
53
|
+
private resolve;
|
|
45
54
|
dispose(): void;
|
|
46
55
|
private allocate;
|
|
56
|
+
/**
|
|
57
|
+
* A framebuffer a skin half draws into against the frame's depth: the texture itself, or a
|
|
58
|
+
* multisampled renderbuffer and the framebuffer that resolves it into the texture.
|
|
59
|
+
*/
|
|
60
|
+
private drawTarget;
|
|
47
61
|
private refuse;
|
|
48
62
|
private halfFloatTexture;
|
|
49
63
|
private release;
|