@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
|
@@ -781,8 +781,8 @@ export declare class WebGL2Renderer implements RendererApi {
|
|
|
781
781
|
*/
|
|
782
782
|
get presentedFrames(): number;
|
|
783
783
|
private presentedFrameCount;
|
|
784
|
-
private
|
|
785
|
-
private
|
|
784
|
+
private flatProgram;
|
|
785
|
+
private flatUniforms;
|
|
786
786
|
/**
|
|
787
787
|
* The lit fragment source every flat program is built from, and the budget it was built at.
|
|
788
788
|
*
|
|
@@ -793,7 +793,7 @@ export declare class WebGL2Renderer implements RendererApi {
|
|
|
793
793
|
* for a lazily compiled variant to end up at a different light budget from the one the renderer
|
|
794
794
|
* uploads for, which is a picture nobody could explain.
|
|
795
795
|
*/
|
|
796
|
-
private
|
|
796
|
+
private flatFragSource;
|
|
797
797
|
/**
|
|
798
798
|
* How many lights this renderer's shader has room for, after the device had its say.
|
|
799
799
|
*
|
|
@@ -936,6 +936,11 @@ export declare class WebGL2Renderer implements RendererApi {
|
|
|
936
936
|
private readonly surfaceScene;
|
|
937
937
|
/** The two halves of `uWriteMode`, kept here because a `vec2` is written whole. */
|
|
938
938
|
private oitWeight;
|
|
939
|
+
/**
|
|
940
|
+
* How the surfaces drawn next meet the medium: what `setSurfaceFog` set, and `FOG_RECEDE` from
|
|
941
|
+
* each `bindMeshPass`. A translucent draw's own fog is measured against this and puts it back.
|
|
942
|
+
*/
|
|
943
|
+
private surfaceFog;
|
|
939
944
|
private ditherFade;
|
|
940
945
|
/** Said once, not per frame, where the context cannot give the float target the sum needs. */
|
|
941
946
|
private oitRefused;
|
|
@@ -1294,6 +1299,27 @@ export declare class WebGL2Renderer implements RendererApi {
|
|
|
1294
1299
|
* each such draw and handed back, where the other backend bakes it into a pipeline.
|
|
1295
1300
|
*/
|
|
1296
1301
|
private materialDoubleSided;
|
|
1302
|
+
/** Whether the bound material has a map, for the warning a mesh with no coordinates earns. */
|
|
1303
|
+
private materialHasMaps;
|
|
1304
|
+
/** The material last set, so a lit program rebuilt for glass can be given it. */
|
|
1305
|
+
private currentMaterial;
|
|
1306
|
+
/**
|
|
1307
|
+
* The lit stage's switches: what the lit programs were compiled with (`litOn`), and what the
|
|
1308
|
+
* profile and the content have asked for since (`litWanted`).
|
|
1309
|
+
*
|
|
1310
|
+
* **Each is off until it is used** — glass until a pane is offered, a fixture until one is loaded,
|
|
1311
|
+
* the effects until a material carries a table, DriftLight until a volume is set — and then on for
|
|
1312
|
+
* good, rebuilt at the next `beginFrame`. Clustering is the profile's. The code costs the lit pass
|
|
1313
|
+
* registers on every surface whether it finds anything or not, and a world without the feature
|
|
1314
|
+
* draws the same picture without it. `PipelineCache.litSwitches` makes the same decision on the
|
|
1315
|
+
* other backend. The frame a feature is first used in draws without it; the rebuild is a
|
|
1316
|
+
* synchronous compile, and the next frame waits for it.
|
|
1317
|
+
*/
|
|
1318
|
+
private readonly litOn;
|
|
1319
|
+
private readonly litWanted;
|
|
1320
|
+
private litAsked;
|
|
1321
|
+
/** Ask for a switch, which the next `beginFrame` compiles in. Never glass with it turned off. */
|
|
1322
|
+
private askLit;
|
|
1297
1323
|
/**
|
|
1298
1324
|
* The one sink handed to every caster enumeration.
|
|
1299
1325
|
*
|
|
@@ -1845,6 +1871,22 @@ export declare class WebGL2Renderer implements RendererApi {
|
|
|
1845
1871
|
*/
|
|
1846
1872
|
setEnvironmentAmbient(_amount: number): void;
|
|
1847
1873
|
setSurfaceGrain(amount: number): void;
|
|
1874
|
+
/**
|
|
1875
|
+
* Whether the surfaces drawn next meet the medium: distance fog and the global medium's haze.
|
|
1876
|
+
* True is the default, and what `bindMeshPass` restores.
|
|
1877
|
+
*
|
|
1878
|
+
* **The opaque counterpart of `TranslucentMeshOptions.fog`**, which a translucent draw has had
|
|
1879
|
+
* all along; the shader's per-draw switch was there, and nothing reached it for an ordinary mesh.
|
|
1880
|
+
* What it is for is a medium that darkens one thing and not another — a floor receding into the
|
|
1881
|
+
* distance under figures that must keep their full value however far from the camera they stand.
|
|
1882
|
+
* Material state like `setSurfaceGrain`: instanced and skinned draws read it too, and a
|
|
1883
|
+
* translucent draw's own `fog` is measured against it and puts it back.
|
|
1884
|
+
*
|
|
1885
|
+
* **What it gives up** is the medium's honesty: a surface out of the fog is drawn as though the
|
|
1886
|
+
* air between it and the eye were empty, so it should be a surface the scene means to stand
|
|
1887
|
+
* apart, not one whose distance is the point.
|
|
1888
|
+
*/
|
|
1889
|
+
setSurfaceFog(enabled: boolean): void;
|
|
1848
1890
|
/**
|
|
1849
1891
|
* One float of material state, into every flat program.
|
|
1850
1892
|
*
|
|
@@ -1855,6 +1897,8 @@ export declare class WebGL2Renderer implements RendererApi {
|
|
|
1855
1897
|
* reads, drew what was asked.
|
|
1856
1898
|
*/
|
|
1857
1899
|
private writeFlatFloat;
|
|
1900
|
+
/** One int of material state, into every flat program, for `writeFlatFloat`'s reason. */
|
|
1901
|
+
private writeFlatInt;
|
|
1858
1902
|
/**
|
|
1859
1903
|
* How much of the surfaces drawn next survives a screen-door dither, for crossfading two levels
|
|
1860
1904
|
* of detail of one thing.
|
|
@@ -2523,6 +2567,17 @@ export declare class WebGL2Renderer implements RendererApi {
|
|
|
2523
2567
|
* rather than propagating. Only a shader that will not link at *any* budget throws, and it
|
|
2524
2568
|
* throws a sentence with the device's numbers in it rather than a bare driver string.
|
|
2525
2569
|
*/
|
|
2570
|
+
/** The lit fragment source this renderer's profile compiles, at a light budget. */
|
|
2571
|
+
private flatFragAt;
|
|
2572
|
+
/**
|
|
2573
|
+
* Every lit program that exists, compiled again with the switches asked for since, at the budget
|
|
2574
|
+
* the renderer already uploads for, and given the pass and the material the old one held.
|
|
2575
|
+
*
|
|
2576
|
+
* At `beginFrame`, not where a feature was first used: a pane is offered inside a shadow pass
|
|
2577
|
+
* with the depth program bound, and a program swapped under it would draw the next caster with
|
|
2578
|
+
* the lit one.
|
|
2579
|
+
*/
|
|
2580
|
+
private rebuildLit;
|
|
2526
2581
|
private fitFlatProgram;
|
|
2527
2582
|
/**
|
|
2528
2583
|
* How many point lights this renderer's shader shades at once.
|
|
@@ -2716,6 +2771,23 @@ export declare class WebGL2Renderer implements RendererApi {
|
|
|
2716
2771
|
fillPanel(rect: InsetRect, color: Vec3, alpha: number): void;
|
|
2717
2772
|
/** Give the whole canvas back. Safe to call without a matching `beginInset`. */
|
|
2718
2773
|
endInset(): void;
|
|
2774
|
+
/**
|
|
2775
|
+
* Draw what follows as a **view model**, in the nearest sliver of depth, until `endViewModel`.
|
|
2776
|
+
*
|
|
2777
|
+
* A first-person view model — arms and a held weapon — drawn with the frame's camera, or with one
|
|
2778
|
+
* of its own through `bindMeshPass`, lands in front of anything past the near plane without the
|
|
2779
|
+
* world's depth being cleared, so every pass that reads depth afterwards still sees the world.
|
|
2780
|
+
* `share` is how much of the range it takes, 1% by default. See `viewModel.ts` for what it gives
|
|
2781
|
+
* up, which includes temporal reconstruction: draw a view model with multisampling.
|
|
2782
|
+
*
|
|
2783
|
+
* Not inside an inset: an inset has its own depth, cleared for it, and needs no squeeze.
|
|
2784
|
+
*/
|
|
2785
|
+
beginViewModel(share?: number): void;
|
|
2786
|
+
/** Give the whole depth range back. Safe to call without a matching `beginViewModel`. */
|
|
2787
|
+
endViewModel(): void;
|
|
2788
|
+
/** Whether a view model is being drawn, so `endFrame` can close one the caller left open. */
|
|
2789
|
+
private viewModelActive;
|
|
2790
|
+
private readonly viewModelRange;
|
|
2719
2791
|
/**
|
|
2720
2792
|
* How much speed blur the frame about to be drawn should resolve with, 0 to 1.
|
|
2721
2793
|
*
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { FrameBudget } from '../budget.js';
|
|
2
2
|
import { MaterialChanges, ownsMaterial } from '../materialChanges.js';
|
|
3
|
-
import { FOG_RECEDE, fogModeOf } from '../drawFog.js';
|
|
3
|
+
import { FOG_OFF, FOG_RECEDE, fogModeOf } from '../drawFog.js';
|
|
4
|
+
import { blendCovering, blendKeeping } from '../../sceneCoverage.js';
|
|
4
5
|
import { mat4 } from 'gl-matrix';
|
|
5
6
|
import { GL_DEPTH_REMAP, MAX_DEPTH_LAYER, depthClearFor, depthOffsetForLayer, GL_SHADOW_REMAP, REVERSED_DEPTH, SHADOW_DEPTH_CLEAR, glDepthFuncEqual, glShadowDepthFunc, } from '../../depthConvention.js';
|
|
6
7
|
import { resolveGlass } from '../../glass.js';
|
|
@@ -93,6 +94,9 @@ import { deviceSnappedCellSize } from '../../textLayout.js';
|
|
|
93
94
|
import { WindStreakRenderer } from '../../windStreakRenderer.js';
|
|
94
95
|
import { InstancedBatch } from './instanced.js';
|
|
95
96
|
import { cutoutOf } from '../../cutoutCaster.js';
|
|
97
|
+
import { materialHasMaps, noteMapsWithoutUvs } from '../../mapsWithoutUvs.js';
|
|
98
|
+
import { VIEW_MODEL_DEPTH_SHARE, viewModelDepthRange } from '../../viewModel.js';
|
|
99
|
+
import { noLitSwitches } from '../../shaders/flat/glassTint.js';
|
|
96
100
|
/**
|
|
97
101
|
* How many frames `firstFrameSettled` will ask before it stops asking.
|
|
98
102
|
*
|
|
@@ -557,6 +561,11 @@ export class WebGL2Renderer {
|
|
|
557
561
|
surfaceScene = new Float32Array(4);
|
|
558
562
|
/** The two halves of `uWriteMode`, kept here because a `vec2` is written whole. */
|
|
559
563
|
oitWeight = 0;
|
|
564
|
+
/**
|
|
565
|
+
* How the surfaces drawn next meet the medium: what `setSurfaceFog` set, and `FOG_RECEDE` from
|
|
566
|
+
* each `bindMeshPass`. A translucent draw's own fog is measured against this and puts it back.
|
|
567
|
+
*/
|
|
568
|
+
surfaceFog = FOG_RECEDE;
|
|
560
569
|
ditherFade = 0;
|
|
561
570
|
/** Said once, not per frame, where the context cannot give the float target the sum needs. */
|
|
562
571
|
oitRefused = false;
|
|
@@ -961,6 +970,34 @@ export class WebGL2Renderer {
|
|
|
961
970
|
* each such draw and handed back, where the other backend bakes it into a pipeline.
|
|
962
971
|
*/
|
|
963
972
|
materialDoubleSided = false;
|
|
973
|
+
/** Whether the bound material has a map, for the warning a mesh with no coordinates earns. */
|
|
974
|
+
materialHasMaps = false;
|
|
975
|
+
/** The material last set, so a lit program rebuilt for glass can be given it. */
|
|
976
|
+
currentMaterial = null;
|
|
977
|
+
/**
|
|
978
|
+
* The lit stage's switches: what the lit programs were compiled with (`litOn`), and what the
|
|
979
|
+
* profile and the content have asked for since (`litWanted`).
|
|
980
|
+
*
|
|
981
|
+
* **Each is off until it is used** — glass until a pane is offered, a fixture until one is loaded,
|
|
982
|
+
* the effects until a material carries a table, DriftLight until a volume is set — and then on for
|
|
983
|
+
* good, rebuilt at the next `beginFrame`. Clustering is the profile's. The code costs the lit pass
|
|
984
|
+
* registers on every surface whether it finds anything or not, and a world without the feature
|
|
985
|
+
* draws the same picture without it. `PipelineCache.litSwitches` makes the same decision on the
|
|
986
|
+
* other backend. The frame a feature is first used in draws without it; the rebuild is a
|
|
987
|
+
* synchronous compile, and the next frame waits for it.
|
|
988
|
+
*/
|
|
989
|
+
litOn = noLitSwitches();
|
|
990
|
+
litWanted = noLitSwitches();
|
|
991
|
+
litAsked = false;
|
|
992
|
+
/** Ask for a switch, which the next `beginFrame` compiles in. Never glass with it turned off. */
|
|
993
|
+
askLit(feature) {
|
|
994
|
+
if (this.litWanted[feature])
|
|
995
|
+
return;
|
|
996
|
+
if (feature === 'GLASS_SHADOWS' && this.quality.glassShadows === 'off')
|
|
997
|
+
return;
|
|
998
|
+
this.litWanted[feature] = true;
|
|
999
|
+
this.litAsked = true;
|
|
1000
|
+
}
|
|
964
1001
|
/**
|
|
965
1002
|
* The one sink handed to every caster enumeration.
|
|
966
1003
|
*
|
|
@@ -992,6 +1029,7 @@ export class WebGL2Renderer {
|
|
|
992
1029
|
/* Glass: kept aside for its own two passes rather than dropped. See `glassCasters.ts`. */
|
|
993
1030
|
if (this.quality.glassShadows !== 'off' && glassOf(material, this.glassCasterScratch)) {
|
|
994
1031
|
this.glassCasters.recordMesh(mesh, model, material, this.glassCasterScratch);
|
|
1032
|
+
this.askLit('GLASS_SHADOWS');
|
|
995
1033
|
}
|
|
996
1034
|
return;
|
|
997
1035
|
}
|
|
@@ -1037,6 +1075,7 @@ export class WebGL2Renderer {
|
|
|
1037
1075
|
if (!this.drawingGlassDepth && !castsDepth(material)) {
|
|
1038
1076
|
if (this.quality.glassShadows !== 'off' && glassOf(material, this.glassCasterScratch)) {
|
|
1039
1077
|
this.glassCasters.recordInstanced(batch, data, material, this.glassCasterScratch);
|
|
1078
|
+
this.askLit('GLASS_SHADOWS');
|
|
1040
1079
|
}
|
|
1041
1080
|
return;
|
|
1042
1081
|
}
|
|
@@ -1075,6 +1114,7 @@ export class WebGL2Renderer {
|
|
|
1075
1114
|
if (!this.drawingGlassDepth && !castsDepth(material)) {
|
|
1076
1115
|
if (this.quality.glassShadows !== 'off' && glassOf(material, this.glassCasterScratch)) {
|
|
1077
1116
|
this.glassCasters.recordSkinned(mesh, model, palette, material, this.glassCasterScratch);
|
|
1117
|
+
this.askLit('GLASS_SHADOWS');
|
|
1078
1118
|
}
|
|
1079
1119
|
return;
|
|
1080
1120
|
}
|
|
@@ -1135,6 +1175,7 @@ export class WebGL2Renderer {
|
|
|
1135
1175
|
if (this.activeDepthPeel)
|
|
1136
1176
|
gl.uniform1i(u['uPreviousShadowMap'] ?? null, 0);
|
|
1137
1177
|
gl.uniform2f(u['uUvScale'] ?? null, cutout.u, cutout.v);
|
|
1178
|
+
gl.uniform2f(u['uUvOffset'] ?? null, cutout.uOffset, cutout.vOffset);
|
|
1138
1179
|
gl.uniform1f(u['uAlphaCutout'] ?? null, cutout.cutoff);
|
|
1139
1180
|
/* Narrowed for the reason the sink's own `mesh` gives: this renderer only hands out its own. */
|
|
1140
1181
|
cutout.albedo.bind(gl, SURFACE_TEXTURE_UNIT);
|
|
@@ -1538,6 +1579,9 @@ export class WebGL2Renderer {
|
|
|
1538
1579
|
this.iesRows = this.iesAtlas.height;
|
|
1539
1580
|
this.iesPlanes = this.iesAtlas.planes;
|
|
1540
1581
|
this.cookieTiles = this.cookieImages.length;
|
|
1582
|
+
/* The first profile or cookie is when the lit programs start reading them. See `litOn`. */
|
|
1583
|
+
if (this.iesRows > 1 || this.cookieTiles > 0)
|
|
1584
|
+
this.askLit('LIGHT_FIXTURES');
|
|
1541
1585
|
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
1542
1586
|
gl.activeTexture(gl.TEXTURE0);
|
|
1543
1587
|
}
|
|
@@ -1896,6 +1940,9 @@ export class WebGL2Renderer {
|
|
|
1896
1940
|
* Built for the profile this renderer resolved, not for every profile at once — so a
|
|
1897
1941
|
* world with shadows off compiles none of the shadow path. See `shaders/flat.ts`.
|
|
1898
1942
|
*/
|
|
1943
|
+
/* The one switch the profile decides; the rest wait for their content. See `litOn`. */
|
|
1944
|
+
this.litOn.CLUSTERED_LIGHTS = this.quality.clusteredLights;
|
|
1945
|
+
this.litWanted.CLUSTERED_LIGHTS = this.quality.clusteredLights;
|
|
1899
1946
|
const fitted = this.fitFlatProgram(gl);
|
|
1900
1947
|
this.flatProgram = fitted.program;
|
|
1901
1948
|
this.flatFragSource = fitted.source;
|
|
@@ -2410,6 +2457,8 @@ export class WebGL2Renderer {
|
|
|
2410
2457
|
this.materials.dirty();
|
|
2411
2458
|
this.currentSurfaceTexture = material?.albedo ?? null;
|
|
2412
2459
|
this.materialDoubleSided = material?.doubleSided === true;
|
|
2460
|
+
this.materialHasMaps = materialHasMaps(material);
|
|
2461
|
+
this.currentMaterial = material;
|
|
2413
2462
|
/*
|
|
2414
2463
|
* Written to every flat program, because material state persists across draws and a skinned
|
|
2415
2464
|
* draw is entitled to the material the caller set before it. A second program holding none of
|
|
@@ -2499,6 +2548,7 @@ export class WebGL2Renderer {
|
|
|
2499
2548
|
* how big a dome was — WebGPU writes it before its own early return and WebGL2 did not.
|
|
2500
2549
|
*/
|
|
2501
2550
|
gl.uniform2f(u['uUvScale'] ?? null, material?.uScale ?? 1, material?.vScale ?? 1);
|
|
2551
|
+
gl.uniform2f(u['uUvOffset'] ?? null, material?.uOffset ?? 0, material?.vOffset ?? 0);
|
|
2502
2552
|
gl.uniform1i(u['uDoubleSided'] ?? null, material?.doubleSided === true ? 1 : 0);
|
|
2503
2553
|
/* The albedo's effects table, or the stand-in the shader reads as none. Before the early
|
|
2504
2554
|
return, so a material with no albedo still leaves a complete texture on the unit. */
|
|
@@ -2508,6 +2558,9 @@ export class WebGL2Renderer {
|
|
|
2508
2558
|
}
|
|
2509
2559
|
else {
|
|
2510
2560
|
albedo.bindEffects(gl, SURFACE_EFFECTS_TEXTURE_UNIT, this.emptyTexture2D);
|
|
2561
|
+
/* The first table bound is when the lit programs start reading one. See `litOn`. */
|
|
2562
|
+
if (albedo.hasEffects)
|
|
2563
|
+
this.askLit('SURFACE_EFFECTS');
|
|
2511
2564
|
}
|
|
2512
2565
|
gl.uniform1i(u['uSurfaceEffects'] ?? null, SURFACE_EFFECTS_TEXTURE_UNIT);
|
|
2513
2566
|
if (albedo === null) {
|
|
@@ -2624,6 +2677,28 @@ export class WebGL2Renderer {
|
|
|
2624
2677
|
this.materials.dirty();
|
|
2625
2678
|
this.writeFlatFloat('uGrain', Math.min(1, Math.max(0, amount)));
|
|
2626
2679
|
}
|
|
2680
|
+
/**
|
|
2681
|
+
* Whether the surfaces drawn next meet the medium: distance fog and the global medium's haze.
|
|
2682
|
+
* True is the default, and what `bindMeshPass` restores.
|
|
2683
|
+
*
|
|
2684
|
+
* **The opaque counterpart of `TranslucentMeshOptions.fog`**, which a translucent draw has had
|
|
2685
|
+
* all along; the shader's per-draw switch was there, and nothing reached it for an ordinary mesh.
|
|
2686
|
+
* What it is for is a medium that darkens one thing and not another — a floor receding into the
|
|
2687
|
+
* distance under figures that must keep their full value however far from the camera they stand.
|
|
2688
|
+
* Material state like `setSurfaceGrain`: instanced and skinned draws read it too, and a
|
|
2689
|
+
* translucent draw's own `fog` is measured against it and puts it back.
|
|
2690
|
+
*
|
|
2691
|
+
* **What it gives up** is the medium's honesty: a surface out of the fog is drawn as though the
|
|
2692
|
+
* air between it and the eye were empty, so it should be a surface the scene means to stand
|
|
2693
|
+
* apart, not one whose distance is the point.
|
|
2694
|
+
*/
|
|
2695
|
+
setSurfaceFog(enabled) {
|
|
2696
|
+
if (this.contextLost)
|
|
2697
|
+
return;
|
|
2698
|
+
this.materials.dirty();
|
|
2699
|
+
this.surfaceFog = enabled ? FOG_RECEDE : FOG_OFF;
|
|
2700
|
+
this.writeFlatInt('uFogEnabled', this.surfaceFog);
|
|
2701
|
+
}
|
|
2627
2702
|
/**
|
|
2628
2703
|
* One float of material state, into every flat program.
|
|
2629
2704
|
*
|
|
@@ -2640,6 +2715,14 @@ export class WebGL2Renderer {
|
|
|
2640
2715
|
}
|
|
2641
2716
|
this.useFlatProgram();
|
|
2642
2717
|
}
|
|
2718
|
+
/** One int of material state, into every flat program, for `writeFlatFloat`'s reason. */
|
|
2719
|
+
writeFlatInt(name, value) {
|
|
2720
|
+
for (const target of this.flatTargets) {
|
|
2721
|
+
this.gl.useProgram(target.program);
|
|
2722
|
+
this.gl.uniform1i(target.uniforms[name] ?? null, value);
|
|
2723
|
+
}
|
|
2724
|
+
this.useFlatProgram();
|
|
2725
|
+
}
|
|
2643
2726
|
/**
|
|
2644
2727
|
* How much of the surfaces drawn next survives a screen-door dither, for crossfading two levels
|
|
2645
2728
|
* of detail of one thing.
|
|
@@ -2868,7 +2951,7 @@ export class WebGL2Renderer {
|
|
|
2868
2951
|
/* A blended batch shows what is behind it — refraction or glass — as a single draw does, and
|
|
2869
2952
|
counts a material of its own for it by the rule both backends share (`materialChanges.ts`). */
|
|
2870
2953
|
const refracting = blend && this.bindSeeThrough(u, options);
|
|
2871
|
-
const own = ownsMaterial({ opacity, lit, fog, toneMapped, refracting });
|
|
2954
|
+
const own = ownsMaterial({ opacity, lit, fog, toneMapped, refracting }, this.surfaceFog);
|
|
2872
2955
|
if (own)
|
|
2873
2956
|
this.materials.dirty();
|
|
2874
2957
|
this.takeMaterial();
|
|
@@ -2894,16 +2977,16 @@ export class WebGL2Renderer {
|
|
|
2894
2977
|
*/
|
|
2895
2978
|
if (!lit)
|
|
2896
2979
|
gl.uniform1i(u['uLightingEnabled'] ?? null, 0);
|
|
2897
|
-
if (fog !==
|
|
2980
|
+
if (fog !== this.surfaceFog)
|
|
2898
2981
|
gl.uniform1i(u['uFogEnabled'] ?? null, fog);
|
|
2899
2982
|
if (!toneMapped)
|
|
2900
2983
|
gl.uniform1i(u['uOutputTransform'] ?? null, Math.min(this.gradeCode(), 1));
|
|
2901
2984
|
if (blend) {
|
|
2902
2985
|
gl.enable(gl.BLEND);
|
|
2903
2986
|
if (options.additive === true)
|
|
2904
|
-
|
|
2987
|
+
blendKeeping(gl, gl.SRC_ALPHA, gl.ONE);
|
|
2905
2988
|
else
|
|
2906
|
-
|
|
2989
|
+
blendCovering(gl, gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
|
2907
2990
|
if (!depthWrite)
|
|
2908
2991
|
gl.depthMask(false);
|
|
2909
2992
|
}
|
|
@@ -2943,8 +3026,8 @@ export class WebGL2Renderer {
|
|
|
2943
3026
|
this.unbindSeeThrough(u);
|
|
2944
3027
|
if (!lit)
|
|
2945
3028
|
gl.uniform1i(u['uLightingEnabled'] ?? null, 1);
|
|
2946
|
-
if (fog !==
|
|
2947
|
-
gl.uniform1i(u['uFogEnabled'] ?? null,
|
|
3029
|
+
if (fog !== this.surfaceFog)
|
|
3030
|
+
gl.uniform1i(u['uFogEnabled'] ?? null, this.surfaceFog);
|
|
2948
3031
|
if (!toneMapped)
|
|
2949
3032
|
gl.uniform1i(u['uOutputTransform'] ?? null, this.gradeCode());
|
|
2950
3033
|
if (own)
|
|
@@ -4023,6 +4106,7 @@ export class WebGL2Renderer {
|
|
|
4023
4106
|
gl.uniform4fv(u['uGlassLight'] ?? null, this.glassLight);
|
|
4024
4107
|
if (cutout !== null) {
|
|
4025
4108
|
gl.uniform2f(u['uUvScale'] ?? null, cutout.u, cutout.v);
|
|
4109
|
+
gl.uniform2f(u['uUvOffset'] ?? null, cutout.uOffset, cutout.vOffset);
|
|
4026
4110
|
gl.uniform1f(u['uAlphaCutout'] ?? null, cutout.cutoff);
|
|
4027
4111
|
cutout.albedo.bind(gl, SURFACE_TEXTURE_UNIT);
|
|
4028
4112
|
gl.uniform1i(u['uCutoutMap'] ?? null, SURFACE_TEXTURE_UNIT);
|
|
@@ -4132,16 +4216,76 @@ export class WebGL2Renderer {
|
|
|
4132
4216
|
* rather than propagating. Only a shader that will not link at *any* budget throws, and it
|
|
4133
4217
|
* throws a sentence with the device's numbers in it rather than a bare driver string.
|
|
4134
4218
|
*/
|
|
4135
|
-
|
|
4136
|
-
|
|
4219
|
+
/** The lit fragment source this renderer's profile compiles, at a light budget. */
|
|
4220
|
+
flatFragAt(budget) {
|
|
4221
|
+
return flatFrag({
|
|
4137
4222
|
pointShadows: this.quality.pointShadows,
|
|
4138
4223
|
directionalShadows: this.quality.directionalShadows,
|
|
4139
4224
|
environmentProbe: this.probeCapture !== null,
|
|
4140
4225
|
nightEmissive: this.quality.nightEmissive,
|
|
4141
|
-
/*
|
|
4142
|
-
glassShadows: this.
|
|
4226
|
+
/* Each off until it is used: see `litOn`. */
|
|
4227
|
+
glassShadows: this.litOn.GLASS_SHADOWS,
|
|
4228
|
+
clusteredLights: this.litOn.CLUSTERED_LIGHTS,
|
|
4229
|
+
lightFixtures: this.litOn.LIGHT_FIXTURES,
|
|
4230
|
+
surfaceEffects: this.litOn.SURFACE_EFFECTS,
|
|
4231
|
+
driftLight: this.litOn.DRIFT_LIGHT,
|
|
4232
|
+
maxLights: budget.maxLights,
|
|
4233
|
+
maxAreaLights: budget.maxAreaLights,
|
|
4234
|
+
});
|
|
4235
|
+
}
|
|
4236
|
+
/**
|
|
4237
|
+
* Every lit program that exists, compiled again with the switches asked for since, at the budget
|
|
4238
|
+
* the renderer already uploads for, and given the pass and the material the old one held.
|
|
4239
|
+
*
|
|
4240
|
+
* At `beginFrame`, not where a feature was first used: a pane is offered inside a shadow pass
|
|
4241
|
+
* with the depth program bound, and a program swapped under it would draw the next caster with
|
|
4242
|
+
* the lit one.
|
|
4243
|
+
*/
|
|
4244
|
+
rebuildLit() {
|
|
4245
|
+
const { gl } = this;
|
|
4246
|
+
Object.assign(this.litOn, this.litWanted);
|
|
4247
|
+
this.flatFragSource = this.flatFragAt(this.lightBudget);
|
|
4248
|
+
const recompile = (old, skinned, morphed, instanced, label) => {
|
|
4249
|
+
if (old === null)
|
|
4250
|
+
return null;
|
|
4251
|
+
const program = compileProgram(gl, flatVert({ skinned, morphed, instanced }), this.flatFragSource, label);
|
|
4252
|
+
gl.deleteProgram(old);
|
|
4253
|
+
return { program, uniforms: uniformLocations(gl, program, label) };
|
|
4143
4254
|
};
|
|
4144
|
-
const
|
|
4255
|
+
const plain = recompile(this.flatProgram, false, false, false, 'flat');
|
|
4256
|
+
if (plain !== null) {
|
|
4257
|
+
this.flatProgram = plain.program;
|
|
4258
|
+
this.flatUniforms = plain.uniforms;
|
|
4259
|
+
}
|
|
4260
|
+
const skinned = recompile(this.flatSkinnedProgram, true, false, false, 'flat.skinned');
|
|
4261
|
+
this.flatSkinnedProgram = skinned?.program ?? null;
|
|
4262
|
+
this.flatSkinnedUniforms = skinned?.uniforms ?? null;
|
|
4263
|
+
const morphed = recompile(this.flatMorphedProgram, false, true, false, 'flat.morphed');
|
|
4264
|
+
this.flatMorphedProgram = morphed?.program ?? null;
|
|
4265
|
+
this.flatMorphedUniforms = morphed?.uniforms ?? null;
|
|
4266
|
+
const both = recompile(this.flatBothProgram, true, true, false, 'flat.morphedskinned');
|
|
4267
|
+
this.flatBothProgram = both?.program ?? null;
|
|
4268
|
+
this.flatBothUniforms = both?.uniforms ?? null;
|
|
4269
|
+
const instanced = recompile(this.flatInstancedProgram, false, false, true, 'flat.instanced');
|
|
4270
|
+
this.flatInstancedProgram = instanced?.program ?? null;
|
|
4271
|
+
this.flatInstancedUniforms = instanced?.uniforms ?? null;
|
|
4272
|
+
this.flatTargets.length = 0;
|
|
4273
|
+
for (const target of [plain, skinned, morphed, both, instanced]) {
|
|
4274
|
+
if (target !== null)
|
|
4275
|
+
this.flatTargets.push(target);
|
|
4276
|
+
}
|
|
4277
|
+
const camera = this.lastPassCamera;
|
|
4278
|
+
const env = this.lastPassEnv;
|
|
4279
|
+
for (const target of this.flatTargets) {
|
|
4280
|
+
gl.useProgram(target.program);
|
|
4281
|
+
if (camera !== null && env !== null)
|
|
4282
|
+
this.writeMeshPassState(target.uniforms, camera, env);
|
|
4283
|
+
this.writeMaterialState(target.uniforms, this.currentMaterial);
|
|
4284
|
+
}
|
|
4285
|
+
this.useFlatProgram();
|
|
4286
|
+
}
|
|
4287
|
+
fitFlatProgram(gl) {
|
|
4288
|
+
const build = (budget) => this.flatFragAt(budget);
|
|
4145
4289
|
const vert = flatVert({ skinned: false, morphed: false, instanced: false });
|
|
4146
4290
|
const limit = gl.getParameter(gl.MAX_FRAGMENT_UNIFORM_VECTORS);
|
|
4147
4291
|
const ceiling = {
|
|
@@ -4652,7 +4796,7 @@ export class WebGL2Renderer {
|
|
|
4652
4796
|
*/
|
|
4653
4797
|
gl.disable(gl.CULL_FACE);
|
|
4654
4798
|
gl.enable(gl.BLEND);
|
|
4655
|
-
|
|
4799
|
+
blendCovering(gl, gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
|
4656
4800
|
gl.uniform4f(this.panelUniforms['uRect'] ?? null, rect.left, rect.top, rect.width, rect.height);
|
|
4657
4801
|
gl.uniform2f(this.panelUniforms['uViewport'] ?? null, Math.max(this.canvas.clientWidth, 1), Math.max(this.canvas.clientHeight, 1));
|
|
4658
4802
|
/* Identity: this backend's clip space is the one the shader was written against. */
|
|
@@ -4673,6 +4817,34 @@ export class WebGL2Renderer {
|
|
|
4673
4817
|
gl.disable(gl.SCISSOR_TEST);
|
|
4674
4818
|
this.restoreViewport();
|
|
4675
4819
|
}
|
|
4820
|
+
/**
|
|
4821
|
+
* Draw what follows as a **view model**, in the nearest sliver of depth, until `endViewModel`.
|
|
4822
|
+
*
|
|
4823
|
+
* A first-person view model — arms and a held weapon — drawn with the frame's camera, or with one
|
|
4824
|
+
* of its own through `bindMeshPass`, lands in front of anything past the near plane without the
|
|
4825
|
+
* world's depth being cleared, so every pass that reads depth afterwards still sees the world.
|
|
4826
|
+
* `share` is how much of the range it takes, 1% by default. See `viewModel.ts` for what it gives
|
|
4827
|
+
* up, which includes temporal reconstruction: draw a view model with multisampling.
|
|
4828
|
+
*
|
|
4829
|
+
* Not inside an inset: an inset has its own depth, cleared for it, and needs no squeeze.
|
|
4830
|
+
*/
|
|
4831
|
+
beginViewModel(share = VIEW_MODEL_DEPTH_SHARE) {
|
|
4832
|
+
if (this.contextLost)
|
|
4833
|
+
return;
|
|
4834
|
+
viewModelDepthRange(this.reversedDepth, share, this.viewModelRange);
|
|
4835
|
+
this.gl.depthRange(this.viewModelRange[0], this.viewModelRange[1]);
|
|
4836
|
+
this.viewModelActive = true;
|
|
4837
|
+
}
|
|
4838
|
+
/** Give the whole depth range back. Safe to call without a matching `beginViewModel`. */
|
|
4839
|
+
endViewModel() {
|
|
4840
|
+
this.viewModelActive = false;
|
|
4841
|
+
if (this.contextLost)
|
|
4842
|
+
return;
|
|
4843
|
+
this.gl.depthRange(0, 1);
|
|
4844
|
+
}
|
|
4845
|
+
/** Whether a view model is being drawn, so `endFrame` can close one the caller left open. */
|
|
4846
|
+
viewModelActive = false;
|
|
4847
|
+
viewModelRange = [0, 1];
|
|
4676
4848
|
/**
|
|
4677
4849
|
* How much speed blur the frame about to be drawn should resolve with, 0 to 1.
|
|
4678
4850
|
*
|
|
@@ -5162,6 +5334,10 @@ export class WebGL2Renderer {
|
|
|
5162
5334
|
beginFrame(clearColor) {
|
|
5163
5335
|
if (this.contextLost)
|
|
5164
5336
|
return;
|
|
5337
|
+
if (this.litAsked) {
|
|
5338
|
+
this.litAsked = false;
|
|
5339
|
+
this.rebuildLit();
|
|
5340
|
+
}
|
|
5165
5341
|
this.budget.reset();
|
|
5166
5342
|
this.materials.dirty();
|
|
5167
5343
|
/*
|
|
@@ -5235,6 +5411,9 @@ export class WebGL2Renderer {
|
|
|
5235
5411
|
endFrame() {
|
|
5236
5412
|
if (this.contextLost)
|
|
5237
5413
|
return;
|
|
5414
|
+
/* A view model left open would squeeze every pass after the world, the composite included. */
|
|
5415
|
+
if (this.viewModelActive)
|
|
5416
|
+
this.endViewModel();
|
|
5238
5417
|
/*
|
|
5239
5418
|
* Camera motion blur, built from the camera the frame was *rendered* with.
|
|
5240
5419
|
*
|
|
@@ -5797,6 +5976,8 @@ export class WebGL2Renderer {
|
|
|
5797
5976
|
gl.bindTexture(gl.TEXTURE_3D, null);
|
|
5798
5977
|
this.driftIndex = index;
|
|
5799
5978
|
this.driftAtlas = atlas;
|
|
5979
|
+
/* The first volume is when the lit programs start reading one. See `litOn`. */
|
|
5980
|
+
this.askLit('DRIFT_LIGHT');
|
|
5800
5981
|
}
|
|
5801
5982
|
/** The field's uniforms and volumes for this pass, or the stand-ins that switch it off. */
|
|
5802
5983
|
bindDriftLight(u, eye) {
|
|
@@ -5906,7 +6087,8 @@ export class WebGL2Renderer {
|
|
|
5906
6087
|
* shape `uOpacity` and `uTint` already use.
|
|
5907
6088
|
*/
|
|
5908
6089
|
gl.uniform1i(u['uLightingEnabled'] ?? null, 1);
|
|
5909
|
-
gl.uniform1i(u['uFogEnabled'] ?? null,
|
|
6090
|
+
gl.uniform1i(u['uFogEnabled'] ?? null, FOG_RECEDE);
|
|
6091
|
+
this.surfaceFog = FOG_RECEDE;
|
|
5910
6092
|
/* Grain on by default, so every scene written before it was a choice looks unchanged. */
|
|
5911
6093
|
gl.uniform1f(u['uGrain'] ?? null, 1);
|
|
5912
6094
|
/* No dither and no weighting: every draw whole, until a crossfade asks. See `setDitherFade`. */
|
|
@@ -6091,9 +6273,12 @@ export class WebGL2Renderer {
|
|
|
6091
6273
|
gl.bindTexture(gl.TEXTURE_2D, this.emptyTexture2D);
|
|
6092
6274
|
gl.uniform1i(u['uSurfaceEffects'] ?? null, SURFACE_EFFECTS_TEXTURE_UNIT);
|
|
6093
6275
|
gl.uniform2f(u['uUvScale'] ?? null, 1, 1);
|
|
6276
|
+
gl.uniform2f(u['uUvOffset'] ?? null, 0, 0);
|
|
6094
6277
|
gl.uniform1f(u['uAlbedoCutout'] ?? null, 0);
|
|
6095
6278
|
gl.uniform1i(u['uDoubleSided'] ?? null, 0);
|
|
6096
6279
|
this.materialDoubleSided = false;
|
|
6280
|
+
this.materialHasMaps = false;
|
|
6281
|
+
this.currentMaterial = null;
|
|
6097
6282
|
this.currentSurfaceTexture = null;
|
|
6098
6283
|
/*
|
|
6099
6284
|
* **And the normal map, which this did not reset and had to.**
|
|
@@ -6213,6 +6398,7 @@ export class WebGL2Renderer {
|
|
|
6213
6398
|
and `Mesh.indexCount`. */
|
|
6214
6399
|
if (!mesh.complete || mesh.indexCount === 0)
|
|
6215
6400
|
return;
|
|
6401
|
+
noteMapsWithoutUvs(mesh, mesh.hasUvs, this.materialHasMaps);
|
|
6216
6402
|
/*
|
|
6217
6403
|
* Skipped where the flag asks and the bounds say so.
|
|
6218
6404
|
*
|
|
@@ -6405,6 +6591,7 @@ export class WebGL2Renderer {
|
|
|
6405
6591
|
and `Mesh.indexCount`. */
|
|
6406
6592
|
if (!mesh.complete || mesh.indexCount === 0)
|
|
6407
6593
|
return;
|
|
6594
|
+
noteMapsWithoutUvs(mesh, mesh.hasUvs, this.materialHasMaps);
|
|
6408
6595
|
if (opacity <= 0)
|
|
6409
6596
|
return;
|
|
6410
6597
|
/*
|
|
@@ -6435,7 +6622,7 @@ export class WebGL2Renderer {
|
|
|
6435
6622
|
const toneMapped = options.toneMapped ?? true;
|
|
6436
6623
|
if (!lit)
|
|
6437
6624
|
gl.uniform1i(u['uLightingEnabled'] ?? null, 0);
|
|
6438
|
-
if (fog !==
|
|
6625
|
+
if (fog !== this.surfaceFog)
|
|
6439
6626
|
gl.uniform1i(u['uFogEnabled'] ?? null, fog);
|
|
6440
6627
|
/* `1` is sRGB alone: the conversion without the curve, which is what dropping the tone map
|
|
6441
6628
|
means rather than dropping the whole transform. `Math.min` rather than a literal, so a
|
|
@@ -6470,7 +6657,7 @@ export class WebGL2Renderer {
|
|
|
6470
6657
|
*/
|
|
6471
6658
|
/* Taken for itself when any option differs from the pass, and put back below: the rule both
|
|
6472
6659
|
backends count material changes by. See `materialChanges.ts`. */
|
|
6473
|
-
const own = ownsMaterial({ opacity, lit, fog, toneMapped, refracting });
|
|
6660
|
+
const own = ownsMaterial({ opacity, lit, fog, toneMapped, refracting }, this.surfaceFog);
|
|
6474
6661
|
if (own)
|
|
6475
6662
|
this.materials.dirty();
|
|
6476
6663
|
this.takeMaterial();
|
|
@@ -6479,9 +6666,9 @@ export class WebGL2Renderer {
|
|
|
6479
6666
|
gl.enable(gl.BLEND);
|
|
6480
6667
|
/* Added, where asked: colour times alpha onto what is there, and the target's alpha kept. */
|
|
6481
6668
|
if (options.additive === true)
|
|
6482
|
-
|
|
6669
|
+
blendKeeping(gl, gl.SRC_ALPHA, gl.ONE);
|
|
6483
6670
|
else
|
|
6484
|
-
|
|
6671
|
+
blendCovering(gl, gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
|
6485
6672
|
}
|
|
6486
6673
|
/*
|
|
6487
6674
|
* The two depth options, both scoped to this draw and handed straight back — the same
|
|
@@ -6521,8 +6708,8 @@ export class WebGL2Renderer {
|
|
|
6521
6708
|
cannot inherit a material from the draw before it. */
|
|
6522
6709
|
if (!lit)
|
|
6523
6710
|
gl.uniform1i(u['uLightingEnabled'] ?? null, 1);
|
|
6524
|
-
if (fog !==
|
|
6525
|
-
gl.uniform1i(u['uFogEnabled'] ?? null,
|
|
6711
|
+
if (fog !== this.surfaceFog)
|
|
6712
|
+
gl.uniform1i(u['uFogEnabled'] ?? null, this.surfaceFog);
|
|
6526
6713
|
if (!toneMapped)
|
|
6527
6714
|
gl.uniform1i(u['uOutputTransform'] ?? null, this.gradeCode());
|
|
6528
6715
|
if (own)
|
|
@@ -6890,7 +7077,7 @@ export class WebGL2Renderer {
|
|
|
6890
7077
|
gl.uniform1i(u['uSceneDepth'] ?? null, 3);
|
|
6891
7078
|
}
|
|
6892
7079
|
gl.enable(gl.BLEND);
|
|
6893
|
-
|
|
7080
|
+
blendKeeping(gl, gl.SRC_ALPHA, gl.ONE);
|
|
6894
7081
|
gl.depthMask(false);
|
|
6895
7082
|
gl.cullFace(inside ? gl.FRONT : gl.BACK);
|
|
6896
7083
|
mesh.draw(gl);
|
|
@@ -6958,7 +7145,7 @@ export class WebGL2Renderer {
|
|
|
6958
7145
|
}
|
|
6959
7146
|
bindAtmosphere(gl, u, env, this.reflectionPassActive ? this.reflectionAtmosphereY : (camera.position[1] ?? 0), this.quality.underwaterAtmosphere);
|
|
6960
7147
|
gl.enable(gl.BLEND);
|
|
6961
|
-
|
|
7148
|
+
blendKeeping(gl, gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
|
6962
7149
|
gl.depthMask(false);
|
|
6963
7150
|
mesh.draw(gl);
|
|
6964
7151
|
gl.depthMask(true);
|
|
@@ -114,6 +114,8 @@ export interface GpuMesh {
|
|
|
114
114
|
* the key is built before the present map is consulted.
|
|
115
115
|
*/
|
|
116
116
|
readonly isSkinned: boolean;
|
|
117
|
+
/** Whether it has texture coordinates, without which a material's maps read one texel. */
|
|
118
|
+
readonly hasUvs: boolean;
|
|
117
119
|
/**
|
|
118
120
|
* Whether this mesh carries the per-vertex channel, which an instanced draw cannot.
|
|
119
121
|
*
|
|
@@ -257,6 +257,7 @@ export function createGpuMeshIncremental(device, data, dynamic = false) {
|
|
|
257
257
|
progress,
|
|
258
258
|
hasTangents: data.tangents !== undefined,
|
|
259
259
|
isSkinned: data.joints !== undefined,
|
|
260
|
+
hasUvs: data.uvs !== undefined,
|
|
260
261
|
skinOffsets: skinOffsetsOf(supplied),
|
|
261
262
|
hasChannel: data.channel !== undefined,
|
|
262
263
|
morph: data.morphTargets === undefined || data.morphTargetCount === undefined
|
|
@@ -392,6 +393,7 @@ function gpuMeshOf(parts) {
|
|
|
392
393
|
},
|
|
393
394
|
hasTangents: parts.hasTangents,
|
|
394
395
|
isSkinned: parts.isSkinned,
|
|
396
|
+
hasUvs: parts.hasUvs,
|
|
395
397
|
hasChannel: parts.hasChannel,
|
|
396
398
|
morph: parts.morph,
|
|
397
399
|
update: parts.update,
|
|
@@ -2,6 +2,7 @@ import { DEPTH_COMPARE } from '../../depthConvention.js';
|
|
|
2
2
|
import { CAUSTICS_BINDINGS, CAUSTICS_FRAG_WGSL, CAUSTICS_VERT_WGSL, } from '../../shaders/generated/caustics.wgsl.js';
|
|
3
3
|
import { DEPTH_FORMAT } from './flatPass.js';
|
|
4
4
|
import { shaderModule } from './shaderModules.js';
|
|
5
|
+
import { SCENE_ALPHA_KEEPS } from '../../sceneCoverage.js';
|
|
5
6
|
/**
|
|
6
7
|
* The caustics pass: a surface lit by nearby water.
|
|
7
8
|
*
|
|
@@ -126,7 +127,7 @@ export function causticsPipeline(cache, device, layout) {
|
|
|
126
127
|
format: cache.format,
|
|
127
128
|
blend: {
|
|
128
129
|
color: { srcFactor: 'one', dstFactor: 'one', operation: 'add' },
|
|
129
|
-
alpha:
|
|
130
|
+
alpha: SCENE_ALPHA_KEEPS,
|
|
130
131
|
},
|
|
131
132
|
},
|
|
132
133
|
],
|