@driftengine/core 4.8.1 → 4.8.3
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 +142 -0
- package/README.md +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +8 -0
- package/dist/input/actionMap.d.ts +6 -1
- package/dist/input/actionMap.js +20 -7
- package/dist/input/input.d.ts +28 -0
- package/dist/input/input.js +71 -9
- package/dist/input/inputHarness.d.ts +9 -0
- package/dist/input/inputHarness.js +34 -2
- package/dist/render/backend/materialChanges.d.ts +7 -2
- package/dist/render/backend/materialChanges.js +8 -3
- package/dist/render/backend/webgl2/renderer.d.ts +75 -3
- package/dist/render/backend/webgl2/renderer.js +209 -22
- package/dist/render/backend/webgpu/buffers.d.ts +2 -0
- package/dist/render/backend/webgpu/buffers.js +2 -0
- package/dist/render/backend/webgpu/causticsPass.js +2 -1
- package/dist/render/backend/webgpu/depthPass.d.ts +13 -3
- package/dist/render/backend/webgpu/depthPass.js +4 -3
- package/dist/render/backend/webgpu/filmPass.js +2 -1
- package/dist/render/backend/webgpu/flatPass.d.ts +5 -0
- package/dist/render/backend/webgpu/flatPass.js +18 -8
- package/dist/render/backend/webgpu/linePass.js +2 -1
- package/dist/render/backend/webgpu/panelPass.js +2 -1
- package/dist/render/backend/webgpu/particlePass.js +2 -1
- package/dist/render/backend/webgpu/pipelineCache.d.ts +40 -8
- package/dist/render/backend/webgpu/pipelineCache.js +76 -9
- package/dist/render/backend/webgpu/plumePass.js +2 -1
- package/dist/render/backend/webgpu/postPass.d.ts +12 -2
- package/dist/render/backend/webgpu/postPass.js +19 -5
- package/dist/render/backend/webgpu/renderer.d.ts +66 -2
- package/dist/render/backend/webgpu/renderer.js +189 -38
- package/dist/render/backend/webgpu/sdfTextPass.js +2 -1
- package/dist/render/backend/webgpu/textPass.js +2 -1
- package/dist/render/backend/webgpu/waterPass.js +2 -1
- package/dist/render/backend/webgpu/windStreakPass.js +2 -1
- package/dist/render/boltBatch.js +2 -1
- package/dist/render/causticsRenderer.js +2 -1
- package/dist/render/cutoutCaster.d.ts +3 -1
- package/dist/render/cutoutCaster.js +4 -0
- package/dist/render/decalPass.js +2 -1
- package/dist/render/globalMediumPass.js +3 -2
- package/dist/render/lineBatch.js +5 -1
- package/dist/render/mapsWithoutUvs.d.ts +24 -0
- package/dist/render/mapsWithoutUvs.js +24 -0
- package/dist/render/mesh.d.ts +2 -0
- package/dist/render/mesh.js +3 -0
- package/dist/render/oitPass.js +2 -1
- package/dist/render/particleBatch.js +3 -2
- package/dist/render/particlePool.d.ts +21 -2
- package/dist/render/particlePool.js +72 -37
- package/dist/render/pass.d.ts +12 -0
- package/dist/render/plumeRenderer.js +3 -2
- package/dist/render/renderQuality.d.ts +16 -8
- package/dist/render/sceneCoverage.d.ts +54 -0
- package/dist/render/sceneCoverage.js +72 -0
- package/dist/render/sdfTextRenderer.js +2 -1
- package/dist/render/shaders/depth.js +3 -2
- package/dist/render/shaders/flat/glassTint.d.ts +28 -0
- package/dist/render/shaders/flat/glassTint.js +31 -0
- package/dist/render/shaders/flat/index.d.ts +12 -0
- package/dist/render/shaders/flat/index.js +13 -2
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +60 -25
- package/dist/render/shaders/flat/surfaceEffects.d.ts +1 -1
- package/dist/render/shaders/flat/surfaceEffects.js +6 -2
- package/dist/render/shaders/generated/depth.wgsl.d.ts +32 -12
- package/dist/render/shaders/generated/depth.wgsl.js +32 -12
- package/dist/render/shaders/generated/flat.wgsl.d.ts +89 -0
- package/dist/render/shaders/generated/flat.wgsl.js +127 -38
- package/dist/render/shaders/generated/line.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/line.wgsl.js +1 -1
- package/dist/render/shaders/generated/particle.wgsl.d.ts +3 -3
- package/dist/render/shaders/generated/particle.wgsl.js +3 -3
- package/dist/render/shaders/generated/rush.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/rush.wgsl.js +1 -1
- package/dist/render/shaders/generated/sdfText.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/sdfText.wgsl.js +1 -1
- package/dist/render/shaders/generated/temporalResolve.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/temporalResolve.wgsl.js +1 -1
- package/dist/render/shaders/line.d.ts +1 -1
- package/dist/render/shaders/outputTransform.d.ts +14 -1
- package/dist/render/shaders/outputTransform.js +23 -1
- package/dist/render/shaders/particle.d.ts +3 -3
- package/dist/render/shaders/recon/resolve.wgsl.js +15 -1
- package/dist/render/shaders/rush.js +13 -2
- package/dist/render/shaders/sdfText.d.ts +1 -1
- package/dist/render/shaders/temporalResolve.js +12 -5
- package/dist/render/ssrPass.js +2 -1
- package/dist/render/surfaceTexture.d.ts +13 -0
- package/dist/render/surfaceTexture.js +4 -0
- package/dist/render/textRenderer.js +2 -1
- package/dist/render/vertexDefaults.js +1 -0
- package/dist/render/viewModel.d.ts +29 -0
- package/dist/render/viewModel.js +35 -0
- package/dist/render/waterRenderer.js +2 -1
- package/dist/render/windStreakRenderer.js +2 -1
- package/package.json +6 -6
- package/src/index.ts +22 -1
- package/src/input/actionMap.ts +23 -8
- package/src/input/input.ts +79 -9
- package/src/input/inputHarness.ts +27 -2
- package/src/render/backend/materialChanges.ts +8 -3
- package/src/render/backend/webgl2/renderer.ts +223 -29
- package/src/render/backend/webgpu/buffers.ts +5 -0
- package/src/render/backend/webgpu/causticsPass.ts +2 -1
- package/src/render/backend/webgpu/depthPass.ts +4 -3
- package/src/render/backend/webgpu/filmPass.ts +2 -1
- package/src/render/backend/webgpu/flatPass.ts +43 -26
- package/src/render/backend/webgpu/linePass.ts +2 -1
- package/src/render/backend/webgpu/panelPass.ts +2 -1
- package/src/render/backend/webgpu/particlePass.ts +2 -1
- package/src/render/backend/webgpu/pipelineCache.ts +82 -8
- package/src/render/backend/webgpu/plumePass.ts +2 -1
- package/src/render/backend/webgpu/postPass.ts +20 -5
- package/src/render/backend/webgpu/renderer.ts +191 -37
- package/src/render/backend/webgpu/sdfTextPass.ts +2 -1
- package/src/render/backend/webgpu/textPass.ts +2 -1
- package/src/render/backend/webgpu/waterPass.ts +2 -1
- package/src/render/backend/webgpu/windStreakPass.ts +2 -1
- package/src/render/boltBatch.ts +2 -1
- package/src/render/causticsRenderer.ts +2 -1
- package/src/render/cutoutCaster.ts +15 -2
- package/src/render/decalPass.ts +2 -1
- package/src/render/globalMediumPass.ts +3 -2
- package/src/render/lineBatch.ts +3 -1
- package/src/render/mapsWithoutUvs.ts +43 -0
- package/src/render/mesh.ts +3 -0
- package/src/render/oitPass.ts +2 -1
- package/src/render/particleBatch.ts +3 -2
- package/src/render/particlePool.ts +107 -56
- package/src/render/pass.ts +12 -0
- package/src/render/plumeRenderer.ts +3 -2
- package/src/render/renderQuality.ts +16 -8
- package/src/render/sceneCoverage.ts +78 -0
- package/src/render/sdfTextRenderer.ts +2 -1
- package/src/render/shaders/depth.ts +3 -2
- package/src/render/shaders/flat/glassTint.ts +48 -0
- package/src/render/shaders/flat/index.ts +25 -2
- package/src/render/shaders/flat/main.ts +60 -25
- package/src/render/shaders/flat/surfaceEffects.ts +6 -2
- package/src/render/shaders/generated/depth.wgsl.ts +32 -12
- package/src/render/shaders/generated/flat.wgsl.ts +127 -38
- package/src/render/shaders/generated/line.wgsl.ts +1 -1
- package/src/render/shaders/generated/particle.wgsl.ts +3 -3
- package/src/render/shaders/generated/rush.wgsl.ts +1 -1
- package/src/render/shaders/generated/sdfText.wgsl.ts +1 -1
- package/src/render/shaders/generated/temporalResolve.wgsl.ts +1 -1
- package/src/render/shaders/outputTransform.ts +24 -1
- package/src/render/shaders/recon/resolve.wgsl.ts +15 -1
- package/src/render/shaders/rush.ts +13 -2
- package/src/render/shaders/temporalResolve.ts +12 -5
- package/src/render/ssrPass.ts +2 -1
- package/src/render/surfaceTexture.ts +16 -0
- package/src/render/textRenderer.ts +2 -1
- package/src/render/vertexDefaults.ts +1 -0
- package/src/render/viewModel.ts +41 -0
- package/src/render/waterRenderer.ts +2 -1
- package/src/render/windStreakRenderer.ts +2 -1
|
@@ -254,6 +254,7 @@ import {
|
|
|
254
254
|
DECAL_TEXTURES,
|
|
255
255
|
DECAL_UNIFORMS,
|
|
256
256
|
PREMULTIPLIED_OVER_BLEND,
|
|
257
|
+
REFLECTION_OVER_BLEND,
|
|
257
258
|
SSR_FRAG_FIELDS,
|
|
258
259
|
SSR_FRAG_SIZE,
|
|
259
260
|
SSR_RESOLVE_FRAG_WGSL,
|
|
@@ -337,6 +338,8 @@ import {
|
|
|
337
338
|
import type { GlobalMediumOptions } from '../../globalMedium.ts';
|
|
338
339
|
import type { MeshData } from '../../mesh.ts';
|
|
339
340
|
import { castsDepth, glassOf } from '../../shadowCasters.ts';
|
|
341
|
+
import { materialHasMaps, noteMapsWithoutUvs } from '../../mapsWithoutUvs.ts';
|
|
342
|
+
import { VIEW_MODEL_DEPTH_SHARE, viewModelDepthRange } from '../../viewModel.ts';
|
|
340
343
|
import { GlassCasterList, type GlassReplaySink } from '../../glassCasters.ts';
|
|
341
344
|
import { GpuGlassTint } from './glassTintPass.ts';
|
|
342
345
|
import {
|
|
@@ -379,7 +382,7 @@ import {
|
|
|
379
382
|
createDepthBindGroupLayout,
|
|
380
383
|
depthPipeline,
|
|
381
384
|
} from './depthPass.ts';
|
|
382
|
-
import { PipelineCache } from './pipelineCache.ts';
|
|
385
|
+
import { PipelineCache, type LitSwitch } from './pipelineCache.ts';
|
|
383
386
|
import {
|
|
384
387
|
SKY_FIELDS,
|
|
385
388
|
SKY_UNIFORM_SIZE,
|
|
@@ -490,7 +493,7 @@ import {
|
|
|
490
493
|
import { aimReflection, createGpuReflection, type GpuReflection } from './reflectionPass.ts';
|
|
491
494
|
import { FrameBudget } from '../budget.ts';
|
|
492
495
|
import { MaterialChanges, ownsMaterial } from '../materialChanges.ts';
|
|
493
|
-
import { FOG_RECEDE, fogModeOf } from '../drawFog.ts';
|
|
496
|
+
import { FOG_OFF, FOG_RECEDE, fogModeOf } from '../drawFog.ts';
|
|
494
497
|
import { DYNAMIC_ALIGNMENT as DYNAMIC_UNIFORM_ALIGNMENT, UniformRing } from './uniformRing.ts';
|
|
495
498
|
import { DecalQueue, MAX_DRAWN_DECALS } from '../../decalQueue.ts';
|
|
496
499
|
import { DistanceFieldScene } from '../../gi/fieldScene.ts';
|
|
@@ -2803,8 +2806,17 @@ export class WebGPURenderer implements RendererApi {
|
|
|
2803
2806
|
private blankAlbedoBindGroup: GPUBindGroup;
|
|
2804
2807
|
/** Repeats across the mesh's own UV range. Per draw, because `uUvScale` is a vertex uniform. */
|
|
2805
2808
|
private readonly uvScale = new Float32Array([1, 1]);
|
|
2809
|
+
/** Where the texture starts, after the scale. Per draw, beside `uvScale`. */
|
|
2810
|
+
private readonly uvOffset = new Float32Array([0, 0]);
|
|
2811
|
+
/**
|
|
2812
|
+
* How the surfaces drawn next meet the medium: what `setSurfaceFog` set, and `FOG_RECEDE` from
|
|
2813
|
+
* each `bindMeshPass`. A translucent draw's own fog is measured against this and puts it back.
|
|
2814
|
+
*/
|
|
2815
|
+
private surfaceFog = FOG_RECEDE;
|
|
2806
2816
|
/** Whether the bound material is seen from both faces. See `SurfaceMaterial.doubleSided`. */
|
|
2807
2817
|
private materialDoubleSided = false;
|
|
2818
|
+
/** Whether the bound material has a map, for the warning a mesh with no coordinates earns. */
|
|
2819
|
+
private materialHasMaps = false;
|
|
2808
2820
|
|
|
2809
2821
|
createSurfaceTexture(
|
|
2810
2822
|
source: TexImageSource,
|
|
@@ -2936,7 +2948,10 @@ export class WebGPURenderer implements RendererApi {
|
|
|
2936
2948
|
const albedo = material?.albedo ?? null;
|
|
2937
2949
|
this.uvScale[0] = material?.uScale ?? 1;
|
|
2938
2950
|
this.uvScale[1] = material?.vScale ?? 1;
|
|
2951
|
+
this.uvOffset[0] = material?.uOffset ?? 0;
|
|
2952
|
+
this.uvOffset[1] = material?.vOffset ?? 0;
|
|
2939
2953
|
this.materialDoubleSided = material?.doubleSided === true;
|
|
2954
|
+
this.materialHasMaps = materialHasMaps(material);
|
|
2940
2955
|
const normal = (material?.normal ?? null) as GpuSurfaceTexture | null;
|
|
2941
2956
|
const orm = (material?.orm ?? null) as GpuSurfaceTexture | null;
|
|
2942
2957
|
const emissiveMap = (material?.emissive ?? null) as GpuSurfaceTexture | null;
|
|
@@ -3099,6 +3114,14 @@ export class WebGPURenderer implements RendererApi {
|
|
|
3099
3114
|
});
|
|
3100
3115
|
}
|
|
3101
3116
|
|
|
3117
|
+
/** Whether the surfaces drawn next meet the medium. See `renderer.ts`. */
|
|
3118
|
+
setSurfaceFog(enabled: boolean): void {
|
|
3119
|
+
this.surfaceFog = enabled ? FOG_RECEDE : FOG_OFF;
|
|
3120
|
+
this.material((_f, i) => {
|
|
3121
|
+
i[this.materialField('uFogEnabled')] = this.surfaceFog;
|
|
3122
|
+
});
|
|
3123
|
+
}
|
|
3124
|
+
|
|
3102
3125
|
/**
|
|
3103
3126
|
* How much of the surfaces drawn next survives a screen-door dither. See `renderer.ts`.
|
|
3104
3127
|
*
|
|
@@ -5387,17 +5410,20 @@ export class WebGPURenderer implements RendererApi {
|
|
|
5387
5410
|
return group;
|
|
5388
5411
|
}
|
|
5389
5412
|
|
|
5390
|
-
/** The cutoff and UV scale into a cutout draw's slot, at
|
|
5413
|
+
/** The cutoff and UV scale and offset into a cutout draw's slot, at its own variant's offsets. */
|
|
5391
5414
|
private writeCutout(
|
|
5392
5415
|
slot: number,
|
|
5393
5416
|
cutout: CutoutCaster<unknown>,
|
|
5394
5417
|
fields: {
|
|
5395
5418
|
readonly uUvScale: { readonly offset: number };
|
|
5419
|
+
readonly uUvOffset: { readonly offset: number };
|
|
5396
5420
|
readonly uAlphaCutout: { readonly offset: number };
|
|
5397
5421
|
},
|
|
5398
5422
|
): void {
|
|
5399
5423
|
this.shadowDraws.writeFloat(slot, fields.uUvScale.offset, cutout.u);
|
|
5400
5424
|
this.shadowDraws.writeFloat(slot, fields.uUvScale.offset + 4, cutout.v);
|
|
5425
|
+
this.shadowDraws.writeFloat(slot, fields.uUvOffset.offset, cutout.uOffset);
|
|
5426
|
+
this.shadowDraws.writeFloat(slot, fields.uUvOffset.offset + 4, cutout.vOffset);
|
|
5401
5427
|
this.shadowDraws.writeFloat(slot, fields.uAlphaCutout.offset, cutout.cutoff);
|
|
5402
5428
|
}
|
|
5403
5429
|
|
|
@@ -5450,6 +5476,27 @@ export class WebGPURenderer implements RendererApi {
|
|
|
5450
5476
|
*/
|
|
5451
5477
|
/** The glass the open shadow pass was offered, kept for its own passes: `glassCasters.ts`. */
|
|
5452
5478
|
private readonly glassCasters = new GlassCasterList();
|
|
5479
|
+
/** Whether a glass caster has ever been offered, which is when lit pipelines start reading it. */
|
|
5480
|
+
private glassSeen = false;
|
|
5481
|
+
|
|
5482
|
+
/**
|
|
5483
|
+
* The first glass caster switches every lit pipeline to reading glass. See
|
|
5484
|
+
* `PipelineCache.litSwitches`: until a pane exists the lookups would find nothing, and they cost
|
|
5485
|
+
* the lit pass on every surface whether they find anything or not.
|
|
5486
|
+
*/
|
|
5487
|
+
private noteGlassCaster(): void {
|
|
5488
|
+
if (this.glassSeen) return;
|
|
5489
|
+
this.glassSeen = true;
|
|
5490
|
+
this.enableLit('GLASS_SHADOWS');
|
|
5491
|
+
}
|
|
5492
|
+
|
|
5493
|
+
/** One of the lit stage's switches on, in every cache a lit pipeline can come from. */
|
|
5494
|
+
private enableLit(feature: LitSwitch): void {
|
|
5495
|
+
if (this.pipelines.litSwitches[feature]) return;
|
|
5496
|
+
void this.pipelines.enable(feature);
|
|
5497
|
+
if (this.overlayPipelines !== this.pipelines) void this.overlayPipelines.enable(feature);
|
|
5498
|
+
if (this.latePipelines !== this.pipelines) void this.latePipelines.enable(feature);
|
|
5499
|
+
}
|
|
5453
5500
|
/** One resolution every glass caster is read through, so recording one allocates nothing. */
|
|
5454
5501
|
private readonly glassCasterScratch: ResolvedGlass = {
|
|
5455
5502
|
transmission: 0,
|
|
@@ -5462,6 +5509,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
5462
5509
|
/* Glass: kept aside for its own two passes rather than dropped. See `glassCasters.ts`. */
|
|
5463
5510
|
if (this.quality.glassShadows !== 'off' && glassOf(material, this.glassCasterScratch)) {
|
|
5464
5511
|
this.glassCasters.recordMesh(mesh, model, material, this.glassCasterScratch);
|
|
5512
|
+
this.noteGlassCaster();
|
|
5465
5513
|
}
|
|
5466
5514
|
return;
|
|
5467
5515
|
}
|
|
@@ -5517,6 +5565,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
5517
5565
|
if (!this.drawingGlassDepth && !castsDepth(material)) {
|
|
5518
5566
|
if (this.quality.glassShadows !== 'off' && glassOf(material, this.glassCasterScratch)) {
|
|
5519
5567
|
this.glassCasters.recordInstanced(batch, data, material, this.glassCasterScratch);
|
|
5568
|
+
this.noteGlassCaster();
|
|
5520
5569
|
}
|
|
5521
5570
|
return;
|
|
5522
5571
|
}
|
|
@@ -5572,6 +5621,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
5572
5621
|
if (!this.drawingGlassDepth && !castsDepth(material)) {
|
|
5573
5622
|
if (this.quality.glassShadows !== 'off' && glassOf(material, this.glassCasterScratch)) {
|
|
5574
5623
|
this.glassCasters.recordSkinned(mesh, model, palette, material, this.glassCasterScratch);
|
|
5624
|
+
this.noteGlassCaster();
|
|
5575
5625
|
}
|
|
5576
5626
|
return;
|
|
5577
5627
|
}
|
|
@@ -5968,6 +6018,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
5968
6018
|
quality.screenEffects ? sceneColorFormat(quality.hdrScene) : surface.format,
|
|
5969
6019
|
this.samples,
|
|
5970
6020
|
glassShadows,
|
|
6021
|
+
quality.clusteredLights,
|
|
5971
6022
|
);
|
|
5972
6023
|
/*
|
|
5973
6024
|
* The canvas' own state, for the pass `ensurePass` reopens after `endFrame`.
|
|
@@ -5980,11 +6031,11 @@ export class WebGPURenderer implements RendererApi {
|
|
|
5980
6031
|
this.overlayPipelines =
|
|
5981
6032
|
this.pipelines.format === surface.format && this.samples === 1
|
|
5982
6033
|
? this.pipelines
|
|
5983
|
-
: new PipelineCache(device, surface.format, 1, glassShadows);
|
|
6034
|
+
: new PipelineCache(device, surface.format, 1, glassShadows, quality.clusteredLights);
|
|
5984
6035
|
this.latePipelines =
|
|
5985
6036
|
this.pipelines.format === RECON_HISTORY_FORMAT && this.samples === 1
|
|
5986
6037
|
? this.pipelines
|
|
5987
|
-
: new PipelineCache(device, RECON_HISTORY_FORMAT, 1, glassShadows);
|
|
6038
|
+
: new PipelineCache(device, RECON_HISTORY_FORMAT, 1, glassShadows, quality.clusteredLights);
|
|
5988
6039
|
this.issueBound = this.issueCommand.bind(this);
|
|
5989
6040
|
this.pipelineTargets =
|
|
5990
6041
|
this.overlayPipelines === this.pipelines
|
|
@@ -6288,7 +6339,10 @@ export class WebGPURenderer implements RendererApi {
|
|
|
6288
6339
|
/* The albedo's effects table, or a one-texel stand-in the shader's size test reads as none.
|
|
6289
6340
|
The albedo is the group's key, so its table needs no key of its own. */
|
|
6290
6341
|
if (name === 'uSurfaceEffects') {
|
|
6291
|
-
|
|
6342
|
+
const effects = this.albedo?.effectsView ?? null;
|
|
6343
|
+
/* The first table bound is when the lit pipelines start reading one. See `litSwitches`. */
|
|
6344
|
+
if (effects !== null) this.enableLit('SURFACE_EFFECTS');
|
|
6345
|
+
return { view: effects ?? blankView, sampler: blankSampler };
|
|
6292
6346
|
}
|
|
6293
6347
|
/*
|
|
6294
6348
|
* The room, once a bake has filled it. Unbaked reads as the blank cube: nothing to mirror.
|
|
@@ -7750,8 +7804,10 @@ export class WebGPURenderer implements RendererApi {
|
|
|
7750
7804
|
* these per draw through the material block, the same shape `uOpacity` already uses.
|
|
7751
7805
|
*/
|
|
7752
7806
|
i[at('uLightingEnabled')] = 1;
|
|
7753
|
-
i[at('uFogEnabled')] =
|
|
7807
|
+
i[at('uFogEnabled')] = FOG_RECEDE;
|
|
7808
|
+
this.surfaceFog = FOG_RECEDE;
|
|
7754
7809
|
i[at('uAlbedoEnabled')] = 0;
|
|
7810
|
+
this.materialHasMaps = false;
|
|
7755
7811
|
|
|
7756
7812
|
/*
|
|
7757
7813
|
* **The camera medium, which this pass did not upload until 2026-08-29.**
|
|
@@ -8201,6 +8257,8 @@ export class WebGPURenderer implements RendererApi {
|
|
|
8201
8257
|
this.bindGroup = this.blankAlbedoBindGroup;
|
|
8202
8258
|
this.uvScale[0] = 1;
|
|
8203
8259
|
this.uvScale[1] = 1;
|
|
8260
|
+
this.uvOffset[0] = 0;
|
|
8261
|
+
this.uvOffset[1] = 0;
|
|
8204
8262
|
}
|
|
8205
8263
|
|
|
8206
8264
|
/**
|
|
@@ -8284,7 +8342,11 @@ export class WebGPURenderer implements RendererApi {
|
|
|
8284
8342
|
* where it happened to pass, write motion for a surface no pixel of the frame belongs to.
|
|
8285
8343
|
*/
|
|
8286
8344
|
if (this.tracksMotionNow()) this.recordDrawMotion(mesh, model, previousModel);
|
|
8287
|
-
|
|
8345
|
+
noteMapsWithoutUvs(mesh, mesh.hasUvs, this.materialHasMaps);
|
|
8346
|
+
/* An opaque draw meets the medium as the dial says: `submitMesh` reads an absent `fog` as
|
|
8347
|
+
true, which is a translucent draw's default and would put every opaque draw back in the fog
|
|
8348
|
+
`setSurfaceFog(false)` took it out of. */
|
|
8349
|
+
this.submitMesh(mesh, model, tint, 1, false, { depthLayer, fog: this.surfaceFog !== FOG_OFF });
|
|
8288
8350
|
}
|
|
8289
8351
|
|
|
8290
8352
|
/**
|
|
@@ -8389,6 +8451,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
8389
8451
|
return;
|
|
8390
8452
|
}
|
|
8391
8453
|
|
|
8454
|
+
noteMapsWithoutUvs(mesh, mesh.hasUvs, this.materialHasMaps);
|
|
8392
8455
|
/* The tint travels in the options here, where `drawMesh` takes it positionally — see
|
|
8393
8456
|
`TranslucentMeshOptions.tint` for why, and for the fade it was added for. */
|
|
8394
8457
|
this.submitMesh(
|
|
@@ -8465,6 +8528,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
8465
8528
|
/* The material's repeats, not a constant: geometry authored in metres is textured at
|
|
8466
8529
|
whatever density the material asks for without rebuilding it. */
|
|
8467
8530
|
this.perDraw.writeFloats(slot, FLAT_VERT_FIELDS.uUvScale.offset, this.uvScale);
|
|
8531
|
+
this.perDraw.writeFloats(slot, FLAT_VERT_FIELDS.uUvOffset.offset, this.uvOffset);
|
|
8468
8532
|
this.perDraw.writeFloats(slot, FLAT_VERT_FIELDS.uLightViewProj.offset, this.lightViewProj);
|
|
8469
8533
|
/*
|
|
8470
8534
|
* Whether this mesh's tangent frame is real. WebGPU has no disabled attribute, so location 10
|
|
@@ -8498,7 +8562,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
8498
8562
|
/* Added light fades in the medium rather than receding into it. See `drawFog.ts`. */
|
|
8499
8563
|
const fog = fogModeOf(options.fog ?? true, blend && options.additive === true);
|
|
8500
8564
|
const toneMapped = options.toneMapped ?? true;
|
|
8501
|
-
const unlitOrUnfogged = !lit || fog !==
|
|
8565
|
+
const unlitOrUnfogged = !lit || fog !== this.surfaceFog;
|
|
8502
8566
|
if (unlitOrUnfogged) {
|
|
8503
8567
|
if (!lit) this.perFrameInts[this.materialField('uLightingEnabled')] = 0;
|
|
8504
8568
|
this.perFrameInts[this.materialField('uFogEnabled')] = fog;
|
|
@@ -8520,7 +8584,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
8520
8584
|
const refracting = this.bindSeeThrough(options);
|
|
8521
8585
|
/* Taken for itself when any of those differs from the pass, and put back below: the rule both
|
|
8522
8586
|
backends count material changes by. See `materialChanges.ts`. */
|
|
8523
|
-
const own = ownsMaterial({ opacity, lit, fog, toneMapped, refracting });
|
|
8587
|
+
const own = ownsMaterial({ opacity, lit, fog, toneMapped, refracting }, this.surfaceFog);
|
|
8524
8588
|
if (own) this.materials.dirty();
|
|
8525
8589
|
const base = (mesh as GpuMesh & { key?: string }).key ?? 'flat:s0:u0';
|
|
8526
8590
|
/*
|
|
@@ -8664,7 +8728,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
8664
8728
|
}
|
|
8665
8729
|
if (unlitOrUnfogged) {
|
|
8666
8730
|
if (!lit) this.perFrameInts[this.materialField('uLightingEnabled')] = 1;
|
|
8667
|
-
this.perFrameInts[this.materialField('uFogEnabled')] =
|
|
8731
|
+
this.perFrameInts[this.materialField('uFogEnabled')] = this.surfaceFog;
|
|
8668
8732
|
}
|
|
8669
8733
|
/* Back to off for every other draw in the frame, exactly as `uOpacity` is: this scratch is
|
|
8670
8734
|
shared, so a strength left set is worn by everything drawn after it. */
|
|
@@ -8911,6 +8975,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
8911
8975
|
lands ? this.correctedViewProj : this.viewProj,
|
|
8912
8976
|
);
|
|
8913
8977
|
this.perDraw.writeFloats(slot, FLAT_VERT_FIELDS.uUvScale.offset, this.uvScale);
|
|
8978
|
+
this.perDraw.writeFloats(slot, FLAT_VERT_FIELDS.uUvOffset.offset, this.uvOffset);
|
|
8914
8979
|
this.perDraw.writeFloats(slot, FLAT_VERT_FIELDS.uLightViewProj.offset, this.lightViewProj);
|
|
8915
8980
|
this.perDraw.writeInt(slot, FLAT_VERT_FIELDS.uHasTangents.offset, mesh.hasTangents ? 1 : 0);
|
|
8916
8981
|
|
|
@@ -8921,7 +8986,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
8921
8986
|
const lit = options.lit ?? true;
|
|
8922
8987
|
const fog = fogModeOf(options.fog ?? true, blend && options.additive === true);
|
|
8923
8988
|
const toneMapped = options.toneMapped ?? true;
|
|
8924
|
-
const unlitOrUnfogged = !lit || fog !==
|
|
8989
|
+
const unlitOrUnfogged = !lit || fog !== this.surfaceFog;
|
|
8925
8990
|
if (unlitOrUnfogged) {
|
|
8926
8991
|
if (!lit) this.perFrameInts[this.materialField('uLightingEnabled')] = 0;
|
|
8927
8992
|
this.perFrameInts[this.materialField('uFogEnabled')] = fog;
|
|
@@ -8934,7 +8999,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
8934
8999
|
const layer = Math.min(Math.max(Math.round(options.depthLayer ?? 0), 0), MAX_DEPTH_LAYER);
|
|
8935
9000
|
/* A blended batch shows what is behind it — refraction or glass — as a single draw does. */
|
|
8936
9001
|
const refracting = blend && this.bindSeeThrough(options);
|
|
8937
|
-
const own = ownsMaterial({ opacity, lit, fog, toneMapped, refracting });
|
|
9002
|
+
const own = ownsMaterial({ opacity, lit, fog, toneMapped, refracting }, this.surfaceFog);
|
|
8938
9003
|
if (own) this.materials.dirty();
|
|
8939
9004
|
const base = (mesh as GpuMesh & { key?: string }).key ?? 'flat:s0:u0';
|
|
8940
9005
|
/* `|inst` in the key, for the reason every other suffix is there: an instanced pipeline binds
|
|
@@ -9016,7 +9081,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
9016
9081
|
}
|
|
9017
9082
|
if (unlitOrUnfogged) {
|
|
9018
9083
|
this.perFrameInts[this.materialField('uLightingEnabled')] = 1;
|
|
9019
|
-
this.perFrameInts[this.materialField('uFogEnabled')] =
|
|
9084
|
+
this.perFrameInts[this.materialField('uFogEnabled')] = this.surfaceFog;
|
|
9020
9085
|
}
|
|
9021
9086
|
if (!toneMapped) {
|
|
9022
9087
|
this.perFrameInts[this.materialField('uOutputTransform')] = this.gradeCode();
|
|
@@ -11578,7 +11643,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
11578
11643
|
'post.ssrResolve',
|
|
11579
11644
|
SSR_RESOLVE_FRAG_WGSL,
|
|
11580
11645
|
this.pipelines.format,
|
|
11581
|
-
|
|
11646
|
+
REFLECTION_OVER_BLEND,
|
|
11582
11647
|
),
|
|
11583
11648
|
);
|
|
11584
11649
|
resolve.setBindGroup(0, resolveGroup);
|
|
@@ -12598,6 +12663,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
12598
12663
|
command.instances = 1;
|
|
12599
12664
|
command.indirect = null;
|
|
12600
12665
|
recordNode(this.arena, VERB_DRAW, reads, writes, at, 0);
|
|
12666
|
+
if (this.framePresented) this.queueOverlayFlush();
|
|
12601
12667
|
return command;
|
|
12602
12668
|
}
|
|
12603
12669
|
|
|
@@ -12613,11 +12679,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
12613
12679
|
if (verb === VERB_PASS) {
|
|
12614
12680
|
const definition = passAt(this.passes, nodeState(this.arena, at));
|
|
12615
12681
|
if (definition === undefined) return;
|
|
12616
|
-
this.
|
|
12617
|
-
/* The frame's grade, so a contributed pass can obey the 2026-08-17 rule. See `PassContext`. */
|
|
12618
|
-
this.passContext.outputTransform = this.gradeCode();
|
|
12619
|
-
this.passContext.outputExposure = this.gradeExposure();
|
|
12620
|
-
definition.draw(this.passContext as PassContext);
|
|
12682
|
+
definition.draw(this.fillPassContext(pass));
|
|
12621
12683
|
return;
|
|
12622
12684
|
}
|
|
12623
12685
|
if (verb !== VERB_DRAW) return;
|
|
@@ -12704,6 +12766,16 @@ export class WebGPURenderer implements RendererApi {
|
|
|
12704
12766
|
}
|
|
12705
12767
|
|
|
12706
12768
|
private openPass(): GPURenderPassEncoder | null {
|
|
12769
|
+
const was = this.pass;
|
|
12770
|
+
const pass = this.openAnyPass();
|
|
12771
|
+
/* A pass opened while a view model is drawn starts with the whole depth range: squeeze it. */
|
|
12772
|
+
if (this.viewModelActive && pass !== null && pass !== was && !this.reflectionPassActive) {
|
|
12773
|
+
this.squeezeViewport(pass);
|
|
12774
|
+
}
|
|
12775
|
+
return pass;
|
|
12776
|
+
}
|
|
12777
|
+
|
|
12778
|
+
private openAnyPass(): GPURenderPassEncoder | null {
|
|
12707
12779
|
/* While the replay is running this is the only pass there is. See `oitEncoder`. */
|
|
12708
12780
|
if (this.oitEncoder !== null) return this.oitEncoder;
|
|
12709
12781
|
/*
|
|
@@ -12797,19 +12869,31 @@ export class WebGPURenderer implements RendererApi {
|
|
|
12797
12869
|
});
|
|
12798
12870
|
this.overlayActive = true;
|
|
12799
12871
|
|
|
12800
|
-
|
|
12801
|
-
* **Submitted on a microtask, which runs before the browser presents.** There is no second
|
|
12802
|
-
* `endFrame` to hang this on — a consumer thinks the frame is over — and presentation happens
|
|
12803
|
-
* when the task ends, after the microtask checkpoint. Anything that needs the pixels sooner
|
|
12804
|
-
* flushes synchronously; `copyRegionTo` is the one that does.
|
|
12805
|
-
*/
|
|
12806
|
-
if (!this.overlayFlushQueued) {
|
|
12807
|
-
this.overlayFlushQueued = true;
|
|
12808
|
-
queueMicrotask(() => this.flushOverlay());
|
|
12809
|
-
}
|
|
12872
|
+
this.queueOverlayFlush();
|
|
12810
12873
|
return this.pass;
|
|
12811
12874
|
}
|
|
12812
12875
|
|
|
12876
|
+
/**
|
|
12877
|
+
* Submit what is drawn after the present on a microtask, which runs before the browser presents.
|
|
12878
|
+
*
|
|
12879
|
+
* There is no second `endFrame` to hang this on — a consumer thinks the frame is over — and
|
|
12880
|
+
* presentation happens when the task ends, after the microtask checkpoint. Anything that needs
|
|
12881
|
+
* the pixels sooner flushes synchronously; `copyRegionTo` is the one that does.
|
|
12882
|
+
*
|
|
12883
|
+
* **Queued wherever such work begins: where the overlay pass opens, and where a verb records.**
|
|
12884
|
+
* With the graph on a verb records rather than opening a pass, and until 4.8.2 only an opening
|
|
12885
|
+
* queued this, so `fillPanel` and a contributed pass drawn after `endFrame` waited for the next
|
|
12886
|
+
* `beginFrame` — whose replay drew onto a canvas texture already presented and destroyed.
|
|
12887
|
+
*/
|
|
12888
|
+
private queueOverlayFlush(): void {
|
|
12889
|
+
if (this.overlayFlushQueued) return;
|
|
12890
|
+
this.overlayFlushQueued = true;
|
|
12891
|
+
queueMicrotask(this.flushOverlayBound);
|
|
12892
|
+
}
|
|
12893
|
+
|
|
12894
|
+
/** `flushOverlay` bound once, so queueing it each frame allocates nothing. */
|
|
12895
|
+
private readonly flushOverlayBound = (): void => this.flushOverlay();
|
|
12896
|
+
|
|
12813
12897
|
/**
|
|
12814
12898
|
* The cache whose pipelines match the pass this draw will land in.
|
|
12815
12899
|
*
|
|
@@ -12873,6 +12957,8 @@ export class WebGPURenderer implements RendererApi {
|
|
|
12873
12957
|
}
|
|
12874
12958
|
|
|
12875
12959
|
endFrame(): void {
|
|
12960
|
+
/* A view model left open would squeeze every pass after the world, the composite included. */
|
|
12961
|
+
if (this.viewModelActive) this.endViewModel();
|
|
12876
12962
|
this.inFrame = false;
|
|
12877
12963
|
if (this.surface.lost || this.encoder === null) return;
|
|
12878
12964
|
/*
|
|
@@ -13341,14 +13427,40 @@ export class WebGPURenderer implements RendererApi {
|
|
|
13341
13427
|
outputTransform: number;
|
|
13342
13428
|
outputExposure: number;
|
|
13343
13429
|
jitter: Float32Array;
|
|
13430
|
+
format: GPUTextureFormat;
|
|
13431
|
+
depthFormat: GPUTextureFormat;
|
|
13432
|
+
samples: number;
|
|
13344
13433
|
} = {
|
|
13345
13434
|
backend: 'webgpu',
|
|
13346
13435
|
pass: null,
|
|
13347
13436
|
outputTransform: 0,
|
|
13348
13437
|
outputExposure: 1,
|
|
13349
13438
|
jitter: this.passJitter,
|
|
13439
|
+
format: 'rgba8unorm',
|
|
13440
|
+
depthFormat: DEPTH_FORMAT,
|
|
13441
|
+
samples: 1,
|
|
13350
13442
|
};
|
|
13351
13443
|
|
|
13444
|
+
/**
|
|
13445
|
+
* Fill the context a contributed pass draws with, for the pass it is about to draw into.
|
|
13446
|
+
*
|
|
13447
|
+
* **The attachments as well as the encoder**: a pass drawn after `endFrame` lands on the canvas
|
|
13448
|
+
* at one sample where the frame's own is the scene target at the scene's samples, and a pipeline
|
|
13449
|
+
* built for the one is refused by the other. The cache a mesh drawn here would use is the answer,
|
|
13450
|
+
* so the two cannot disagree.
|
|
13451
|
+
*/
|
|
13452
|
+
private fillPassContext(pass: GPURenderPassEncoder): PassContext {
|
|
13453
|
+
const target = this.targetPipelines();
|
|
13454
|
+
const context = this.passContext;
|
|
13455
|
+
context.pass = pass;
|
|
13456
|
+
/* The frame's grade, so a contributed pass can obey the 2026-08-17 rule. See `PassContext`. */
|
|
13457
|
+
context.outputTransform = this.gradeCode();
|
|
13458
|
+
context.outputExposure = this.gradeExposure();
|
|
13459
|
+
context.format = target.format;
|
|
13460
|
+
context.samples = target.sampleCount;
|
|
13461
|
+
return context as PassContext;
|
|
13462
|
+
}
|
|
13463
|
+
|
|
13352
13464
|
/**
|
|
13353
13465
|
* Let something outside this file draw into the frame. See `Renderer.registerPass`.
|
|
13354
13466
|
*
|
|
@@ -13502,11 +13614,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
13502
13614
|
if (!this.quality.frameGraph) {
|
|
13503
13615
|
const pass = this.openPass();
|
|
13504
13616
|
if (pass === null) return;
|
|
13505
|
-
this.
|
|
13506
|
-
/* The frame's grade, so a contributed pass can obey the 2026-08-17 rule. See `PassContext`. */
|
|
13507
|
-
this.passContext.outputTransform = this.gradeCode();
|
|
13508
|
-
this.passContext.outputExposure = this.gradeExposure();
|
|
13509
|
-
definition.draw(this.passContext as PassContext);
|
|
13617
|
+
definition.draw(this.fillPassContext(pass));
|
|
13510
13618
|
return;
|
|
13511
13619
|
}
|
|
13512
13620
|
recordNode(
|
|
@@ -13517,6 +13625,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
13517
13625
|
handle,
|
|
13518
13626
|
0,
|
|
13519
13627
|
);
|
|
13628
|
+
if (this.framePresented) this.queueOverlayFlush();
|
|
13520
13629
|
}
|
|
13521
13630
|
|
|
13522
13631
|
/** Let go of a pass. A handle kept past this draws nothing; see `PassHandle`. */
|
|
@@ -13728,7 +13837,10 @@ export class WebGPURenderer implements RendererApi {
|
|
|
13728
13837
|
* **The clear is a drawn quad rather than a clear**, because WebGPU clears whole attachments
|
|
13729
13838
|
* and cannot confine one to a scissor. See `insetPass.ts`.
|
|
13730
13839
|
*
|
|
13731
|
-
* Returns the
|
|
13840
|
+
* Returns the **aspect** of the inset, width over height in the pixels its viewport got, as the
|
|
13841
|
+
* other backend does — for `camera.updateMatrices`. With no pass to draw into, a lost device,
|
|
13842
|
+
* it is the aspect of the box asked for: there are no pixels to measure, and a zero would be a
|
|
13843
|
+
* projection of infinities in the caller's camera.
|
|
13732
13844
|
*/
|
|
13733
13845
|
beginInset(rect: InsetRect, clearColor: Vec3 | null): number {
|
|
13734
13846
|
/*
|
|
@@ -13738,7 +13850,8 @@ export class WebGPURenderer implements RendererApi {
|
|
|
13738
13850
|
*/
|
|
13739
13851
|
if (this.quality.frameGraph) this.flushGraph();
|
|
13740
13852
|
const pass = this.openPass();
|
|
13741
|
-
if (this.surface.lost || pass === null)
|
|
13853
|
+
if (this.surface.lost || pass === null)
|
|
13854
|
+
return Math.max(rect.width, 1) / Math.max(rect.height, 1);
|
|
13742
13855
|
const canvas = this.surface.context.canvas as HTMLCanvasElement;
|
|
13743
13856
|
const cssWidth = Math.max(canvas.clientWidth, 1);
|
|
13744
13857
|
const cssHeight = Math.max(canvas.clientHeight, 1);
|
|
@@ -13843,6 +13956,43 @@ export class WebGPURenderer implements RendererApi {
|
|
|
13843
13956
|
return w / h;
|
|
13844
13957
|
}
|
|
13845
13958
|
|
|
13959
|
+
/**
|
|
13960
|
+
* Draw what follows as a **view model**, in the nearest sliver of depth, until `endViewModel`.
|
|
13961
|
+
*
|
|
13962
|
+
* The twin of the WebGL2 method, which carries the reasons. Here the squeeze is the viewport's
|
|
13963
|
+
* depth bounds, which are pass state applied when a draw is issued, so both ends flush the graph
|
|
13964
|
+
* as an inset's do, and a pass opened in between is given the bounds again in `openPass`.
|
|
13965
|
+
*/
|
|
13966
|
+
beginViewModel(share = VIEW_MODEL_DEPTH_SHARE): void {
|
|
13967
|
+
if (this.quality.frameGraph) this.flushGraph();
|
|
13968
|
+
viewModelDepthRange(this.reversedDepth, share, this.viewModelRange);
|
|
13969
|
+
this.viewModelActive = true;
|
|
13970
|
+
const pass = this.openPass();
|
|
13971
|
+
if (this.surface.lost || pass === null) return;
|
|
13972
|
+
this.squeezeViewport(pass);
|
|
13973
|
+
}
|
|
13974
|
+
|
|
13975
|
+
/** Give the whole depth range back. Safe to call without a matching `beginViewModel`. */
|
|
13976
|
+
endViewModel(): void {
|
|
13977
|
+
if (!this.viewModelActive) return;
|
|
13978
|
+
if (this.quality.frameGraph) this.flushGraph();
|
|
13979
|
+
this.viewModelActive = false;
|
|
13980
|
+
const pass = this.pass;
|
|
13981
|
+
if (this.surface.lost || pass === null) return;
|
|
13982
|
+
const [width, height] = this.passSize();
|
|
13983
|
+
pass.setViewport(0, 0, width, height, 0, 1);
|
|
13984
|
+
}
|
|
13985
|
+
|
|
13986
|
+
/** The view model's depth bounds on the whole of the pass. */
|
|
13987
|
+
private squeezeViewport(pass: GPURenderPassEncoder): void {
|
|
13988
|
+
const [width, height] = this.passSize();
|
|
13989
|
+
pass.setViewport(0, 0, width, height, this.viewModelRange[0], this.viewModelRange[1]);
|
|
13990
|
+
}
|
|
13991
|
+
|
|
13992
|
+
/** Whether a view model is being drawn: `openPass` re-applies it, `endFrame` closes it. */
|
|
13993
|
+
private viewModelActive = false;
|
|
13994
|
+
private readonly viewModelRange: [number, number] = [0, 1];
|
|
13995
|
+
|
|
13846
13996
|
/** Give the whole canvas back. Safe to call without a matching `beginInset`. */
|
|
13847
13997
|
endInset(): void {
|
|
13848
13998
|
/*
|
|
@@ -15030,6 +15180,8 @@ export class WebGPURenderer implements RendererApi {
|
|
|
15030
15180
|
this.iesRows = this.iesAtlas.height;
|
|
15031
15181
|
this.iesPlanes = this.iesAtlas.planes;
|
|
15032
15182
|
this.cookieTiles = this.cookieImages.length;
|
|
15183
|
+
/* The first profile or cookie is when the lit pipelines start reading them. See `litSwitches`. */
|
|
15184
|
+
if (this.iesRows > 1 || this.cookieTiles > 0) this.enableLit('LIGHT_FIXTURES');
|
|
15033
15185
|
return this.fixtureView;
|
|
15034
15186
|
}
|
|
15035
15187
|
|
|
@@ -15125,6 +15277,8 @@ export class WebGPURenderer implements RendererApi {
|
|
|
15125
15277
|
);
|
|
15126
15278
|
this.driftIndex = index;
|
|
15127
15279
|
this.driftAtlas = atlas;
|
|
15280
|
+
/* The first volume is when the lit pipelines start reading one. See `litSwitches`. */
|
|
15281
|
+
this.enableLit('DRIFT_LIGHT');
|
|
15128
15282
|
this.driftIndexView = index.createView({ dimension: '3d' });
|
|
15129
15283
|
this.driftAtlasView = atlas.createView({ dimension: '3d' });
|
|
15130
15284
|
this.bakeLightField = {
|
|
@@ -11,6 +11,7 @@ import type { SdfFont } from '../../sdfFont.ts';
|
|
|
11
11
|
import { SdfTextLayout, type SdfTextStyle } from '../../sdfTextLayout.ts';
|
|
12
12
|
import type { GpuSurfaceTexture } from './surfaceTexturePass.ts';
|
|
13
13
|
import { shaderModule } from './shaderModules.ts';
|
|
14
|
+
import { SCENE_ALPHA_COVERS } from '../../sceneCoverage.ts';
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* The SDF text pass: a string as flat, textured quads, drawn into the scene.
|
|
@@ -213,7 +214,7 @@ export function sdfTextPipeline(
|
|
|
213
214
|
format: cache.format,
|
|
214
215
|
blend: {
|
|
215
216
|
color: { srcFactor: 'src-alpha', dstFactor: 'one-minus-src-alpha', operation: 'add' },
|
|
216
|
-
alpha:
|
|
217
|
+
alpha: SCENE_ALPHA_COVERS,
|
|
217
218
|
},
|
|
218
219
|
},
|
|
219
220
|
],
|
|
@@ -9,6 +9,7 @@ import type { UniformFields } from './scatterPass.ts';
|
|
|
9
9
|
import { DEPTH_FORMAT } from './flatPass.ts';
|
|
10
10
|
import type { PipelineCache } from './pipelineCache.ts';
|
|
11
11
|
import { shaderModule } from './shaderModules.ts';
|
|
12
|
+
import { SCENE_ALPHA_COVERS } from '../../sceneCoverage.ts';
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* The text pass: a string as instanced glyph cubes, drawn over the scene.
|
|
@@ -208,7 +209,7 @@ export function textPipeline(
|
|
|
208
209
|
format: cache.format,
|
|
209
210
|
blend: {
|
|
210
211
|
color: { srcFactor: 'src-alpha', dstFactor: 'one-minus-src-alpha', operation: 'add' },
|
|
211
|
-
alpha:
|
|
212
|
+
alpha: SCENE_ALPHA_COVERS,
|
|
212
213
|
},
|
|
213
214
|
},
|
|
214
215
|
],
|
|
@@ -9,6 +9,7 @@ import { DEPTH_FORMAT } from './flatPass.ts';
|
|
|
9
9
|
import type { PipelineCache } from './pipelineCache.ts';
|
|
10
10
|
import type { UniformFields } from './scatterPass.ts';
|
|
11
11
|
import { shaderModule } from './shaderModules.ts';
|
|
12
|
+
import { SCENE_ALPHA_COVERS } from '../../sceneCoverage.ts';
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* The water pass: two sheets and a patch, displaced by a wave field in the vertex stage.
|
|
@@ -199,7 +200,7 @@ export function waterPipeline(
|
|
|
199
200
|
format: cache.format,
|
|
200
201
|
blend: {
|
|
201
202
|
color: { srcFactor: 'src-alpha', dstFactor: 'one-minus-src-alpha', operation: 'add' },
|
|
202
|
-
alpha:
|
|
203
|
+
alpha: SCENE_ALPHA_COVERS,
|
|
203
204
|
},
|
|
204
205
|
},
|
|
205
206
|
],
|
|
@@ -9,6 +9,7 @@ import type { UniformFields } from './scatterPass.ts';
|
|
|
9
9
|
import { DEPTH_FORMAT } from './flatPass.ts';
|
|
10
10
|
import type { PipelineCache } from './pipelineCache.ts';
|
|
11
11
|
import { shaderModule } from './shaderModules.ts';
|
|
12
|
+
import { SCENE_ALPHA_COVERS } from '../../sceneCoverage.ts';
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* The wind-streak pass: a lattice of debris that follows the camera and wraps.
|
|
@@ -184,7 +185,7 @@ export function windStreakPipeline(
|
|
|
184
185
|
dstFactor: 'one-minus-src-alpha',
|
|
185
186
|
operation: 'add',
|
|
186
187
|
},
|
|
187
|
-
alpha:
|
|
188
|
+
alpha: SCENE_ALPHA_COVERS,
|
|
188
189
|
},
|
|
189
190
|
},
|
|
190
191
|
],
|
package/src/render/boltBatch.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { compileProgram, uniformLocations } from './shader.ts';
|
|
|
2
2
|
import { BOLT_FRAG, BOLT_VERT } from './shaders/bolt.ts';
|
|
3
3
|
import { buildSegmentQuads, expandBoltSegments } from './segmentQuads.ts';
|
|
4
4
|
import type { BoltSegments } from './boltPool.ts';
|
|
5
|
+
import { blendKeeping } from './sceneCoverage.ts';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* One draw call for a pool of electrical arcs.
|
|
@@ -86,7 +87,7 @@ export class BoltBatch {
|
|
|
86
87
|
drawTo(gl: WebGL2RenderingContext, segmentCount: number): void {
|
|
87
88
|
if (segmentCount === 0) return;
|
|
88
89
|
gl.enable(gl.BLEND);
|
|
89
|
-
|
|
90
|
+
blendKeeping(gl, gl.SRC_ALPHA, gl.ONE);
|
|
90
91
|
gl.depthMask(false);
|
|
91
92
|
gl.disable(gl.CULL_FACE);
|
|
92
93
|
|
|
@@ -7,6 +7,7 @@ import type { Atmosphere } from './atmosphere.ts';
|
|
|
7
7
|
import { buildSheets } from './surfaceSheet.ts';
|
|
8
8
|
import type { SheetSpan } from './surfaceSheet.ts';
|
|
9
9
|
import { seaStateForWind } from './seaState.ts';
|
|
10
|
+
import { blendKeeping } from './sceneCoverage.ts';
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* A surface lit by nearby water: the floor of a pool, the bottom of a flooded
|
|
@@ -149,7 +150,7 @@ export class CausticsRenderer {
|
|
|
149
150
|
bindAtmosphere(gl, u, atmosphere, camera.position[1] ?? 0, underwaterEnabled);
|
|
150
151
|
|
|
151
152
|
gl.enable(gl.BLEND);
|
|
152
|
-
|
|
153
|
+
blendKeeping(gl, gl.ONE, gl.ONE);
|
|
153
154
|
/*
|
|
154
155
|
* No depth write, but depth *testing* stays on: the sheet sits a couple of
|
|
155
156
|
* centimetres under the surface it lights, so geometry in front of it — the
|