@driftengine/core 4.11.1 → 4.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.json +102 -0
- package/README.md +18 -9
- package/dist/geometry/meshBuilder.d.ts +10 -0
- package/dist/geometry/meshBuilder.js +11 -2
- package/dist/index.d.ts +5 -2
- package/dist/index.js +3 -2
- package/dist/math/color.d.ts +25 -0
- package/dist/math/color.js +37 -0
- package/dist/render/backend/webgl2/renderer.d.ts +33 -5
- package/dist/render/backend/webgl2/renderer.js +123 -49
- package/dist/render/backend/webgl2/skinScatter.d.ts +3 -1
- package/dist/render/backend/webgl2/skinScatter.js +5 -1
- package/dist/render/backend/webgpu/boltPass.d.ts +1 -1
- package/dist/render/backend/webgpu/causticsPass.d.ts +1 -1
- package/dist/render/backend/webgpu/filmPass.d.ts +1 -1
- package/dist/render/backend/webgpu/flockPass.d.ts +1 -1
- package/dist/render/backend/webgpu/giFieldPass.d.ts +3 -1
- package/dist/render/backend/webgpu/gpuDrivenPass.d.ts +10 -1
- package/dist/render/backend/webgpu/gpuDrivenPass.js +28 -11
- package/dist/render/backend/webgpu/panelPass.d.ts +1 -1
- package/dist/render/backend/webgpu/postPass.d.ts +2 -2
- package/dist/render/backend/webgpu/renderer.d.ts +21 -8
- package/dist/render/backend/webgpu/renderer.js +103 -38
- package/dist/render/backend/webgpu/scatterPass.d.ts +1 -1
- package/dist/render/backend/webgpu/skinScatterPass.d.ts +5 -1
- package/dist/render/backend/webgpu/skinScatterPass.js +9 -3
- package/dist/render/backend/webgpu/skyPass.d.ts +11 -1
- package/dist/render/backend/webgpu/waterPass.d.ts +1 -1
- package/dist/render/backend/webgpu/windStreakPass.d.ts +1 -1
- package/dist/render/causticsRenderer.d.ts +3 -1
- package/dist/render/causticsRenderer.js +5 -1
- package/dist/render/decalPass.d.ts +3 -1
- package/dist/render/decalPass.js +4 -1
- package/dist/render/flockRenderer.d.ts +1 -1
- package/dist/render/flockRenderer.js +4 -1
- package/dist/render/globalMediumPass.d.ts +3 -1
- package/dist/render/globalMediumPass.js +5 -1
- package/dist/render/gradeColor.d.ts +21 -0
- package/dist/render/gradeColor.js +64 -0
- package/dist/render/lightMatrix.d.ts +13 -0
- package/dist/render/lightMatrix.js +17 -0
- package/dist/render/passGrade.d.ts +13 -0
- package/dist/render/passGrade.js +47 -0
- package/dist/render/plumeRenderer.d.ts +1 -1
- package/dist/render/plumeRenderer.js +4 -1
- package/dist/render/renderQuality.d.ts +57 -18
- package/dist/render/renderQuality.js +3 -3
- package/dist/render/sceneTarget.d.ts +2 -0
- package/dist/render/sceneTarget.js +1 -0
- package/dist/render/sdfTextRenderer.d.ts +1 -1
- package/dist/render/sdfTextRenderer.js +1 -1
- package/dist/render/shaders/arcane.d.ts +1 -1
- package/dist/render/shaders/arcane.js +5 -1
- package/dist/render/shaders/bolt.d.ts +1 -1
- package/dist/render/shaders/bolt.js +4 -1
- package/dist/render/shaders/caustics.d.ts +1 -1
- package/dist/render/shaders/caustics.js +4 -1
- package/dist/render/shaders/decalProject.js +12 -2
- package/dist/render/shaders/film.d.ts +1 -1
- package/dist/render/shaders/film.js +22 -9
- package/dist/render/shaders/fire.d.ts +1 -1
- package/dist/render/shaders/fire.js +4 -1
- package/dist/render/shaders/flat/areaLight.d.ts +1 -1
- package/dist/render/shaders/flat/areaLight.js +1 -1
- package/dist/render/shaders/flat/glassTint.d.ts +1 -1
- package/dist/render/shaders/flat/glassTint.js +1 -0
- package/dist/render/shaders/flat/index.d.ts +5 -0
- package/dist/render/shaders/flat/index.js +1 -1
- package/dist/render/shaders/flat/layerLooks.d.ts +16 -0
- package/dist/render/shaders/flat/layerLooks.js +108 -0
- package/dist/render/shaders/flat/layered.d.ts +6 -4
- package/dist/render/shaders/flat/layered.js +77 -13
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +7 -2
- package/dist/render/shaders/flat/models.js +1 -1
- package/dist/render/shaders/flat/preamble.js +6 -0
- package/dist/render/shaders/flock.d.ts +1 -1
- package/dist/render/shaders/flock.js +4 -1
- package/dist/render/shaders/generated/arcane.wgsl.d.ts +11 -1
- package/dist/render/shaders/generated/arcane.wgsl.js +11 -1
- package/dist/render/shaders/generated/bolt.wgsl.d.ts +12 -2
- package/dist/render/shaders/generated/bolt.wgsl.js +12 -2
- package/dist/render/shaders/generated/caustics.wgsl.d.ts +12 -2
- package/dist/render/shaders/generated/caustics.wgsl.js +12 -2
- package/dist/render/shaders/generated/decalProject.wgsl.d.ts +6 -1
- package/dist/render/shaders/generated/decalProject.wgsl.js +6 -1
- package/dist/render/shaders/generated/film.wgsl.d.ts +12 -2
- package/dist/render/shaders/generated/film.wgsl.js +12 -2
- package/dist/render/shaders/generated/fire.wgsl.d.ts +11 -1
- package/dist/render/shaders/generated/fire.wgsl.js +11 -1
- package/dist/render/shaders/generated/flat.wgsl.d.ts +144 -16
- package/dist/render/shaders/generated/flat.wgsl.js +183 -55
- package/dist/render/shaders/generated/flock.wgsl.d.ts +12 -2
- package/dist/render/shaders/generated/flock.wgsl.js +12 -2
- package/dist/render/shaders/generated/globalMedium.wgsl.d.ts +12 -2
- package/dist/render/shaders/generated/globalMedium.wgsl.js +12 -2
- package/dist/render/shaders/generated/lightVolume.wgsl.d.ts +36 -16
- package/dist/render/shaders/generated/lightVolume.wgsl.js +39 -19
- package/dist/render/shaders/generated/panel.wgsl.d.ts +12 -2
- package/dist/render/shaders/generated/panel.wgsl.js +12 -2
- package/dist/render/shaders/generated/rush.wgsl.d.ts +31 -26
- package/dist/render/shaders/generated/rush.wgsl.js +31 -26
- package/dist/render/shaders/generated/scatter.wgsl.d.ts +12 -2
- package/dist/render/shaders/generated/scatter.wgsl.js +12 -2
- package/dist/render/shaders/generated/skinBlur.wgsl.d.ts +11 -1
- package/dist/render/shaders/generated/skinBlur.wgsl.js +11 -1
- package/dist/render/shaders/generated/sky.wgsl.d.ts +12 -2
- package/dist/render/shaders/generated/sky.wgsl.js +12 -2
- package/dist/render/shaders/generated/smoke.wgsl.d.ts +11 -1
- package/dist/render/shaders/generated/smoke.wgsl.js +11 -1
- package/dist/render/shaders/generated/text.wgsl.d.ts +11 -1
- package/dist/render/shaders/generated/text.wgsl.js +11 -1
- package/dist/render/shaders/generated/water.wgsl.d.ts +12 -2
- package/dist/render/shaders/generated/water.wgsl.js +12 -2
- package/dist/render/shaders/generated/windStreaks.wgsl.d.ts +12 -2
- package/dist/render/shaders/generated/windStreaks.wgsl.js +12 -2
- package/dist/render/shaders/globalMedium.js +14 -1
- package/dist/render/shaders/gpudriven/present.wgsl.d.ts +15 -6
- package/dist/render/shaders/gpudriven/present.wgsl.js +71 -8
- package/dist/render/shaders/lightVolume.js +4 -1
- package/dist/render/shaders/line.d.ts +1 -1
- package/dist/render/shaders/outputTransform.d.ts +8 -2
- package/dist/render/shaders/outputTransform.js +18 -11
- package/dist/render/shaders/panel.d.ts +1 -1
- package/dist/render/shaders/panel.js +5 -1
- package/dist/render/shaders/particle.d.ts +3 -3
- package/dist/render/shaders/rush.js +8 -0
- package/dist/render/shaders/scatter.d.ts +1 -1
- package/dist/render/shaders/scatter.js +4 -1
- package/dist/render/shaders/sdfText.d.ts +1 -1
- package/dist/render/shaders/skinBlur.js +5 -1
- package/dist/render/shaders/sky.d.ts +1 -1
- package/dist/render/shaders/sky.js +5 -1
- package/dist/render/shaders/smoke.d.ts +1 -1
- package/dist/render/shaders/smoke.js +4 -1
- package/dist/render/shaders/text.d.ts +1 -1
- package/dist/render/shaders/text.js +4 -1
- package/dist/render/shaders/water.d.ts +1 -1
- package/dist/render/shaders/water.js +12 -2
- package/dist/render/shaders/windStreaks.d.ts +1 -1
- package/dist/render/shaders/windStreaks.js +4 -1
- package/dist/render/surfaceLayerLooks.d.ts +92 -0
- package/dist/render/surfaceLayerLooks.js +102 -0
- package/dist/render/surfaceLayers.d.ts +47 -6
- package/dist/render/surfaceLayers.js +5 -1
- package/dist/render/textRenderer.d.ts +3 -1
- package/dist/render/textRenderer.js +7 -1
- package/dist/render/vertexDefaults.d.ts +1 -1
- package/dist/render/vertexDefaults.js +1 -1
- package/dist/render/waterRenderer.d.ts +1 -1
- package/dist/render/waterRenderer.js +4 -1
- package/dist/render/windStreakRenderer.d.ts +3 -1
- package/dist/render/windStreakRenderer.js +5 -1
- package/package.json +6 -6
- package/scripts/browser.d.mts +5 -0
- package/scripts/browser.mjs +68 -14
- package/scripts/look.d.mts +81 -0
- package/scripts/look.mjs +380 -0
- package/src/geometry/meshBuilder.ts +21 -2
- package/src/index.ts +13 -2
- package/src/math/color.ts +39 -0
- package/src/render/backend/webgl2/renderer.ts +174 -46
- package/src/render/backend/webgl2/skinScatter.ts +5 -0
- package/src/render/backend/webgpu/giFieldPass.ts +3 -1
- package/src/render/backend/webgpu/gpuDrivenPass.ts +29 -13
- package/src/render/backend/webgpu/renderer.ts +112 -37
- package/src/render/backend/webgpu/skinScatterPass.ts +17 -1
- package/src/render/causticsRenderer.ts +5 -0
- package/src/render/decalPass.ts +3 -0
- package/src/render/flockRenderer.ts +5 -0
- package/src/render/globalMediumPass.ts +10 -1
- package/src/render/gradeColor.ts +90 -0
- package/src/render/lightMatrix.ts +32 -0
- package/src/render/passGrade.ts +58 -0
- package/src/render/plumeRenderer.ts +5 -0
- package/src/render/renderQuality.ts +60 -21
- package/src/render/sceneTarget.ts +3 -0
- package/src/render/sdfTextRenderer.ts +1 -1
- package/src/render/shaders/arcane.ts +5 -1
- package/src/render/shaders/bolt.ts +4 -1
- package/src/render/shaders/caustics.ts +4 -1
- package/src/render/shaders/decalProject.ts +12 -2
- package/src/render/shaders/film.ts +22 -9
- package/src/render/shaders/fire.ts +4 -1
- package/src/render/shaders/flat/areaLight.ts +1 -1
- package/src/render/shaders/flat/glassTint.ts +3 -1
- package/src/render/shaders/flat/index.ts +6 -1
- package/src/render/shaders/flat/layerLooks.ts +108 -0
- package/src/render/shaders/flat/layered.ts +78 -13
- package/src/render/shaders/flat/main.ts +7 -2
- package/src/render/shaders/flat/models.ts +1 -1
- package/src/render/shaders/flat/preamble.ts +6 -0
- package/src/render/shaders/flock.ts +4 -1
- package/src/render/shaders/generated/arcane.wgsl.ts +11 -1
- package/src/render/shaders/generated/bolt.wgsl.ts +12 -2
- package/src/render/shaders/generated/caustics.wgsl.ts +12 -2
- package/src/render/shaders/generated/decalProject.wgsl.ts +6 -1
- package/src/render/shaders/generated/film.wgsl.ts +12 -2
- package/src/render/shaders/generated/fire.wgsl.ts +11 -1
- package/src/render/shaders/generated/flat.wgsl.ts +183 -55
- package/src/render/shaders/generated/flock.wgsl.ts +12 -2
- package/src/render/shaders/generated/globalMedium.wgsl.ts +12 -2
- package/src/render/shaders/generated/lightVolume.wgsl.ts +39 -19
- package/src/render/shaders/generated/panel.wgsl.ts +12 -2
- package/src/render/shaders/generated/rush.wgsl.ts +31 -26
- package/src/render/shaders/generated/scatter.wgsl.ts +12 -2
- package/src/render/shaders/generated/skinBlur.wgsl.ts +11 -1
- package/src/render/shaders/generated/sky.wgsl.ts +12 -2
- package/src/render/shaders/generated/smoke.wgsl.ts +11 -1
- package/src/render/shaders/generated/text.wgsl.ts +11 -1
- package/src/render/shaders/generated/water.wgsl.ts +12 -2
- package/src/render/shaders/generated/windStreaks.wgsl.ts +12 -2
- package/src/render/shaders/globalMedium.ts +14 -1
- package/src/render/shaders/gpudriven/present.wgsl.ts +72 -8
- package/src/render/shaders/lightVolume.ts +4 -1
- package/src/render/shaders/outputTransform.ts +19 -11
- package/src/render/shaders/panel.ts +5 -1
- package/src/render/shaders/rush.ts +8 -0
- package/src/render/shaders/scatter.ts +4 -1
- package/src/render/shaders/skinBlur.ts +5 -1
- package/src/render/shaders/sky.ts +5 -1
- package/src/render/shaders/smoke.ts +4 -1
- package/src/render/shaders/text.ts +4 -1
- package/src/render/shaders/water.ts +12 -2
- package/src/render/shaders/windStreaks.ts +4 -1
- package/src/render/surfaceLayerLooks.ts +186 -0
- package/src/render/surfaceLayers.ts +52 -7
- package/src/render/textRenderer.ts +6 -0
- package/src/render/vertexDefaults.ts +1 -1
- package/src/render/waterRenderer.ts +5 -0
- package/src/render/windStreakRenderer.ts +5 -0
|
@@ -208,6 +208,7 @@ import {
|
|
|
208
208
|
packSurfaceLayers,
|
|
209
209
|
} from '../../surfaceLayers.ts';
|
|
210
210
|
import type { SurfaceLayers } from '../../surfaceLayers.ts';
|
|
211
|
+
import { LOOK_FLOATS, asksLayerLooks, packLayerLooks } from '../../surfaceLayerLooks.ts';
|
|
211
212
|
import {
|
|
212
213
|
frameReflectionsRefused,
|
|
213
214
|
resolveFrameReflections,
|
|
@@ -252,8 +253,9 @@ import {
|
|
|
252
253
|
OUTPUT_TRANSFORM_CODE,
|
|
253
254
|
RUSH_REACH_UV,
|
|
254
255
|
clampMotionShare,
|
|
255
|
-
forwardTransformCode,
|
|
256
256
|
} from '../../vertexDefaults.ts';
|
|
257
|
+
import { gradeColorInto } from '../../gradeColor.ts';
|
|
258
|
+
import { interfaceGrade, passGradeCode, passGradeExposure } from '../../passGrade.ts';
|
|
257
259
|
import { FRINGE_FLOATS, resolveFringe } from '../../shaders/fringe.ts';
|
|
258
260
|
import { displayHeadroom, type DisplayRange } from '../../displayRange.ts';
|
|
259
261
|
import {
|
|
@@ -1511,7 +1513,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
1511
1513
|
* **Resolved by the caller and held, rather than defaulted per use.** Every shadow term
|
|
1512
1514
|
* below reads it, and the constants they replaced were `DEFAULT_RENDER_QUALITY` copies —
|
|
1513
1515
|
* which measured wrong the moment a scene overrode one. `demo/dayClock` resolves
|
|
1514
|
-
* `directionalShadowMaxSlope` to 9 where the default
|
|
1516
|
+
* `directionalShadowMaxSlope` to 9 where the default was 3 (60 since 4.13.0).
|
|
1515
1517
|
*/
|
|
1516
1518
|
readonly quality: Readonly<RenderQuality>;
|
|
1517
1519
|
|
|
@@ -2225,6 +2227,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
2225
2227
|
private readonly mediumUpsampleUniforms: GPUBuffer;
|
|
2226
2228
|
private readonly mediumUpsampleStaging = new ArrayBuffer(MEDIUM_UPSAMPLE_SIZE);
|
|
2227
2229
|
private readonly mediumUpsampleFloats = new Float32Array(this.mediumUpsampleStaging);
|
|
2230
|
+
private readonly mediumUpsampleInts = new Int32Array(this.mediumUpsampleStaging);
|
|
2228
2231
|
private mediumUpsampleGroup: GPUBindGroup | null = null;
|
|
2229
2232
|
/** The inverse projection the composite linearises a depth with, built once per frame. */
|
|
2230
2233
|
private readonly mediumInvProjection = mat4.create();
|
|
@@ -2475,6 +2478,12 @@ export class WebGPURenderer implements RendererApi {
|
|
|
2475
2478
|
*/
|
|
2476
2479
|
private readonly skyUniforms: UniformRing;
|
|
2477
2480
|
private readonly skyFloats = new Float32Array(SKY_UNIFORM_SIZE / 4);
|
|
2481
|
+
/** The same block's ints: the grade the sky draws with. */
|
|
2482
|
+
private readonly skyInts = new Int32Array(this.skyFloats.buffer);
|
|
2483
|
+
/** `clearOf`'s scratch: a clear colour as the open target takes it. */
|
|
2484
|
+
private readonly gradedClear: Vec3 = [0, 0, 0];
|
|
2485
|
+
/** An inset's clear as its quad's uniform, rewritten in place: the colour and an alpha of 1. */
|
|
2486
|
+
private readonly insetClear = new Float32Array([0, 0, 0, 1]);
|
|
2478
2487
|
private readonly skyBindGroup: GPUBindGroup;
|
|
2479
2488
|
/** The camera's inverse with the clip correction undone, rebuilt once a frame. */
|
|
2480
2489
|
private readonly skyInvViewProj = new Float32Array(16);
|
|
@@ -2559,6 +2568,8 @@ export class WebGPURenderer implements RendererApi {
|
|
|
2559
2568
|
private decalUniforms!: GPUBuffer;
|
|
2560
2569
|
private readonly decalStaging = new ArrayBuffer(DECAL_SLOT * MAX_DRAWN_DECALS);
|
|
2561
2570
|
private readonly decalFloats = new Float32Array(this.decalStaging);
|
|
2571
|
+
/** The same slots' ints: the grade the frame under the marks was drawn with. */
|
|
2572
|
+
private readonly decalInts = new Int32Array(this.decalStaging);
|
|
2562
2573
|
private decalBindGroup: GPUBindGroup | null = null;
|
|
2563
2574
|
/** Said once where a mark cannot be drawn at all, rather than dropped in silence. */
|
|
2564
2575
|
private decalsRefused = false;
|
|
@@ -2742,9 +2753,10 @@ export class WebGPURenderer implements RendererApi {
|
|
|
2742
2753
|
* still owed.
|
|
2743
2754
|
*/
|
|
2744
2755
|
this.pass = null;
|
|
2745
|
-
|
|
2746
|
-
this.mirrorClearColor[
|
|
2747
|
-
this.mirrorClearColor[
|
|
2756
|
+
const mirrorClear = this.clearOf(clearColor);
|
|
2757
|
+
this.mirrorClearColor[0] = mirrorClear[0];
|
|
2758
|
+
this.mirrorClearColor[1] = mirrorClear[1];
|
|
2759
|
+
this.mirrorClearColor[2] = mirrorClear[2];
|
|
2748
2760
|
this.pendingClearMask |= MIRROR_TARGET;
|
|
2749
2761
|
this.reflectionPassActive = true;
|
|
2750
2762
|
/* Closed in `endPlanarReflection`, matching `renderer.ts:2054`. */
|
|
@@ -2915,6 +2927,8 @@ export class WebGPURenderer implements RendererApi {
|
|
|
2915
2927
|
i[atF('uClipEnabled')] = this.reflectionPassActive ? 1 : 0;
|
|
2916
2928
|
f.set(this.reflection?.clipPlane ?? NO_CLIP_PLANE, atF('uClipPlane'));
|
|
2917
2929
|
f[atF('uSheen')] = sheen;
|
|
2930
|
+
i[atF('uOutputTransform')] = this.passGrade();
|
|
2931
|
+
f[atF('uOutputExposure')] = this.passGradeExposure();
|
|
2918
2932
|
f[atF('uFilmRoughness')] = Math.min(1, Math.max(0, options.roughness ?? 0));
|
|
2919
2933
|
f[atF('uFilmRoughnessCycles')] = Math.max(0.01, options.roughnessCyclesPerMetre ?? 60);
|
|
2920
2934
|
|
|
@@ -3015,6 +3029,8 @@ export class WebGPURenderer implements RendererApi {
|
|
|
3015
3029
|
private readonly projection = new Float32Array(PROJECTION_FLOATS);
|
|
3016
3030
|
/** `uLayers`, the material's layers packed by `packSurfaceLayers`. */
|
|
3017
3031
|
private readonly layerFloats = new Float32Array(LAYER_FLOATS);
|
|
3032
|
+
/** `uLayerLooks`, the material's layer picks and looks packed by `packLayerLooks`. */
|
|
3033
|
+
private readonly lookFloats = new Float32Array(LOOK_FLOATS);
|
|
3018
3034
|
/**
|
|
3019
3035
|
* `uGlassTint` as the material leaves it, its light from behind and that light's colour, which a
|
|
3020
3036
|
* pane overwrites for its own draw and `releaseSeeThrough` puts back. See `transmission.ts`.
|
|
@@ -3588,6 +3604,10 @@ export class WebGPURenderer implements RendererApi {
|
|
|
3588
3604
|
packSurfaceLayers(layers, this.layerFloats);
|
|
3589
3605
|
f.set(this.layerFloats, this.materialField('uLayers'));
|
|
3590
3606
|
if (layers !== null) this.enableLit('LAYERED');
|
|
3607
|
+
/* And which layer of shared arrays each reads, with its look: layerLooks.ts. */
|
|
3608
|
+
packLayerLooks(layers, this.lookFloats);
|
|
3609
|
+
f.set(this.lookFloats, this.materialField('uLayerLooks'));
|
|
3610
|
+
if (asksLayerLooks(layers)) this.enableLit('LAYER_LOOKS');
|
|
3591
3611
|
f.set(this.modelParams, this.materialField('uModelParams'));
|
|
3592
3612
|
});
|
|
3593
3613
|
/*
|
|
@@ -3881,6 +3901,13 @@ export class WebGPURenderer implements RendererApi {
|
|
|
3881
3901
|
const fragAt = (name: string): number => PANEL_FRAG_FIELDS[name]?.offset ?? 0;
|
|
3882
3902
|
this.panelFrags.writeFloats(fragmentSlot, fragAt('uColor'), color);
|
|
3883
3903
|
this.panelFrags.writeFloat(fragmentSlot, fragAt('uAlpha'), alpha);
|
|
3904
|
+
/* An interface: the screen encode, never the curve. See `interfaceGrade`. */
|
|
3905
|
+
this.panelFrags.writeInt(
|
|
3906
|
+
fragmentSlot,
|
|
3907
|
+
fragAt('uOutputTransform'),
|
|
3908
|
+
interfaceGrade(this.passGrade()),
|
|
3909
|
+
);
|
|
3910
|
+
this.panelFrags.writeFloat(fragmentSlot, fragAt('uOutputExposure'), 1);
|
|
3884
3911
|
|
|
3885
3912
|
const panelPipe = panelPipeline(this.targetPipelines(), this.surface.device, this.panelLayout);
|
|
3886
3913
|
/* `allocate` already returns a byte offset, not an index. Multiplying by the stride
|
|
@@ -4014,8 +4041,8 @@ export class WebGPURenderer implements RendererApi {
|
|
|
4014
4041
|
font.atlasWidth,
|
|
4015
4042
|
font.atlasHeight,
|
|
4016
4043
|
]);
|
|
4017
|
-
this.sdfTextFrags.writeInt(fragmentSlot, fragAt('uOutputTransform'), this.
|
|
4018
|
-
this.sdfTextFrags.writeFloat(fragmentSlot, fragAt('uOutputExposure'), this.
|
|
4044
|
+
this.sdfTextFrags.writeInt(fragmentSlot, fragAt('uOutputTransform'), this.passGrade());
|
|
4045
|
+
this.sdfTextFrags.writeFloat(fragmentSlot, fragAt('uOutputExposure'), this.passGradeExposure());
|
|
4019
4046
|
|
|
4020
4047
|
const sdfPipe = sdfTextPipeline(
|
|
4021
4048
|
this.blendedPipelines(),
|
|
@@ -4136,6 +4163,13 @@ export class WebGPURenderer implements RendererApi {
|
|
|
4136
4163
|
this.textFrags.writeFloats(fragmentSlot, fragAt('uColor'), style.color);
|
|
4137
4164
|
this.textFrags.writeFloat(fragmentSlot, fragAt('uGlow'), style.glow);
|
|
4138
4165
|
this.textFrags.writeFloat(fragmentSlot, fragAt('uAlpha'), style.alpha);
|
|
4166
|
+
/* An interface: the screen encode, never the curve. See `interfaceGrade`. */
|
|
4167
|
+
this.textFrags.writeInt(
|
|
4168
|
+
fragmentSlot,
|
|
4169
|
+
fragAt('uOutputTransform'),
|
|
4170
|
+
interfaceGrade(this.passGrade()),
|
|
4171
|
+
);
|
|
4172
|
+
this.textFrags.writeFloat(fragmentSlot, fragAt('uOutputExposure'), 1);
|
|
4139
4173
|
|
|
4140
4174
|
const pass = this.openTextPass();
|
|
4141
4175
|
if (pass === null) return;
|
|
@@ -4498,20 +4532,29 @@ export class WebGPURenderer implements RendererApi {
|
|
|
4498
4532
|
* array allocated with the batch, so the frame loop still allocates nothing.
|
|
4499
4533
|
*/
|
|
4500
4534
|
/**
|
|
4501
|
-
* The
|
|
4502
|
-
*
|
|
4503
|
-
* The
|
|
4504
|
-
* one verb with two implementations, and a frame that grades on one backend and not the other
|
|
4505
|
-
* is the silent kind of disagreement. See that method for what the gate is protecting.
|
|
4535
|
+
* The grade a pass applies to what it draws into the target open now, and the exposure it
|
|
4536
|
+
* applies it at: the WebGPU half of `Renderer.passGrade`, and it has to agree with it draw for
|
|
4537
|
+
* draw. The decision is `passGrade.ts`'s, so both halves only gather the state it is taken from.
|
|
4506
4538
|
*/
|
|
4507
|
-
private
|
|
4508
|
-
|
|
4509
|
-
|
|
4510
|
-
|
|
4539
|
+
private passGrade(): number {
|
|
4540
|
+
const storing = this.probePassActive || this.capturePassActive;
|
|
4541
|
+
const transform = this.quality.outputTransform;
|
|
4542
|
+
return passGradeCode(storing, this.framePresented, this.resolveGrades(), transform);
|
|
4543
|
+
}
|
|
4544
|
+
|
|
4545
|
+
private passGradeExposure(): number {
|
|
4546
|
+
const storing = this.probePassActive || this.capturePassActive;
|
|
4547
|
+
return passGradeExposure(storing, this.framePresented, this.resolveGrades(), this.exposure);
|
|
4548
|
+
}
|
|
4549
|
+
|
|
4550
|
+
/** Whether the frame's resolve applies the transform, so the passes before it draw linear. */
|
|
4551
|
+
private resolveGrades(): boolean {
|
|
4552
|
+
return this.hasComposite && this.quality.hdrScene;
|
|
4511
4553
|
}
|
|
4512
4554
|
|
|
4513
|
-
|
|
4514
|
-
|
|
4555
|
+
/** A clear colour as the open target takes it, graded as a pass would. See `gradeColor.ts`. */
|
|
4556
|
+
private clearOf(color: Readonly<Vec3>): Vec3 {
|
|
4557
|
+
return gradeColorInto(this.passGrade(), this.passGradeExposure(), color, this.gradedClear);
|
|
4515
4558
|
}
|
|
4516
4559
|
|
|
4517
4560
|
drawParticles(
|
|
@@ -4634,8 +4677,8 @@ export class WebGPURenderer implements RendererApi {
|
|
|
4634
4677
|
if (has('uFogEnabled')) i[atF('uFogEnabled')] = batch.fog ? 1 : 0;
|
|
4635
4678
|
/* Every material's fragment stage declares these, so they are unconditional like
|
|
4636
4679
|
`uCameraPos` rather than guarded like the material-specific fields above. */
|
|
4637
|
-
i[atF('uOutputTransform')] = this.
|
|
4638
|
-
f[atF('uOutputExposure')] = this.
|
|
4680
|
+
i[atF('uOutputTransform')] = this.passGrade();
|
|
4681
|
+
f[atF('uOutputExposure')] = this.passGradeExposure();
|
|
4639
4682
|
|
|
4640
4683
|
const spriteGroup = batch.material === 'sprite' ? this.spriteFades(batch, camera, f) : null;
|
|
4641
4684
|
|
|
@@ -4889,6 +4932,8 @@ export class WebGPURenderer implements RendererApi {
|
|
|
4889
4932
|
frags.writeInt(fragmentSlot, atF('uFogMode'), medium.fogMode);
|
|
4890
4933
|
frags.writeFloat(fragmentSlot, atF('uFogNear'), medium.fogNear);
|
|
4891
4934
|
frags.writeFloat(fragmentSlot, atF('uFogFar'), medium.fogFar);
|
|
4935
|
+
frags.writeInt(fragmentSlot, atF('uOutputTransform'), this.passGrade());
|
|
4936
|
+
frags.writeFloat(fragmentSlot, atF('uOutputExposure'), this.passGradeExposure());
|
|
4892
4937
|
/*
|
|
4893
4938
|
* **Written for every material, not only the one that reads it.** `arcane` is the only
|
|
4894
4939
|
* plume whose block declares `uTint`, and the slot it lands in is past the end of fire's
|
|
@@ -5089,6 +5134,8 @@ export class WebGPURenderer implements RendererApi {
|
|
|
5089
5134
|
/* No reflection pass yet: the shader's own "off" path, not an improvised one. */
|
|
5090
5135
|
i[atF('uReflectionEnabled')] = mirror === null ? 0 : 1;
|
|
5091
5136
|
i[atF('uReflectionFilterTaps')] = water.reflectionFilterTaps;
|
|
5137
|
+
i[atF('uOutputTransform')] = this.passGrade();
|
|
5138
|
+
f[atF('uOutputExposure')] = this.passGradeExposure();
|
|
5092
5139
|
f[atF('uReflectionTexelSize')] = mirror === null ? 1 : 1 / mirror.width;
|
|
5093
5140
|
f[atF('uReflectionTexelSize') + 1] = mirror === null ? 1 : 1 / mirror.height;
|
|
5094
5141
|
|
|
@@ -5241,6 +5288,8 @@ export class WebGPURenderer implements RendererApi {
|
|
|
5241
5288
|
ring.writeFloats(fragmentSlot, at('uDustOffset'), options.driftM ?? NO_DRIFT);
|
|
5242
5289
|
ring.writeFloat(fragmentSlot, at('uNear'), options.nearM ?? 0);
|
|
5243
5290
|
ring.writeInt(fragmentSlot, at('uSamples'), this.quality.lightVolumeSamples);
|
|
5291
|
+
ring.writeInt(fragmentSlot, at('uOutputTransform'), this.passGrade());
|
|
5292
|
+
ring.writeFloat(fragmentSlot, at('uOutputExposure'), this.passGradeExposure());
|
|
5244
5293
|
|
|
5245
5294
|
/*
|
|
5246
5295
|
* The clamp that makes a beam end on the floor, and the snapshot it reads.
|
|
@@ -5464,6 +5513,10 @@ export class WebGPURenderer implements RendererApi {
|
|
|
5464
5513
|
v[at('uTime')] = timeSeconds;
|
|
5465
5514
|
|
|
5466
5515
|
this.windStreakFragFloats.set(tint, (WIND_STREAK_FRAG_FIELDS.uTint.offset ?? 0) / 4);
|
|
5516
|
+
this.windStreakFragBlock[WIND_STREAK_FRAG_FIELDS.uOutputTransform.offset / 4] =
|
|
5517
|
+
this.passGrade();
|
|
5518
|
+
this.windStreakFragFloats[WIND_STREAK_FRAG_FIELDS.uOutputExposure.offset / 4] =
|
|
5519
|
+
this.passGradeExposure();
|
|
5467
5520
|
|
|
5468
5521
|
this.windStreakVerts.writeBlock(vertexSlot, this.windStreakVertBlock);
|
|
5469
5522
|
this.windStreakFrags.writeBlock(fragmentSlot, this.windStreakFragBlock);
|
|
@@ -5569,6 +5622,8 @@ export class WebGPURenderer implements RendererApi {
|
|
|
5569
5622
|
v[at('uWind') + 1] = windZ;
|
|
5570
5623
|
|
|
5571
5624
|
this.flockFragFloats.set(tint, (FLOCK_FRAG_FIELDS.uTint.offset ?? 0) / 4);
|
|
5625
|
+
this.flockFragBlock[FLOCK_FRAG_FIELDS.uOutputTransform.offset / 4] = this.passGrade();
|
|
5626
|
+
this.flockFragFloats[FLOCK_FRAG_FIELDS.uOutputExposure.offset / 4] = this.passGradeExposure();
|
|
5572
5627
|
|
|
5573
5628
|
this.flockVerts.writeBlock(vertexSlot, this.flockVertBlock);
|
|
5574
5629
|
this.flockFrags.writeBlock(fragmentSlot, this.flockFragBlock);
|
|
@@ -5680,6 +5735,8 @@ export class WebGPURenderer implements RendererApi {
|
|
|
5680
5735
|
f.set(core, atF('uCoreColor'));
|
|
5681
5736
|
f.set(edge, atF('uEdgeColor'));
|
|
5682
5737
|
f[atF('uCoreGain')] = coreGain;
|
|
5738
|
+
i[atF('uOutputTransform')] = this.passGrade();
|
|
5739
|
+
f[atF('uOutputExposure')] = this.passGradeExposure();
|
|
5683
5740
|
f.set(camera.position, atF('uCameraPos'));
|
|
5684
5741
|
/* The medium, through the same resolver every other pass here binds from. */
|
|
5685
5742
|
const medium = resolveAtmosphere(
|
|
@@ -5816,8 +5873,8 @@ export class WebGPURenderer implements RendererApi {
|
|
|
5816
5873
|
this.lineFrags.writeFloat(fragmentSlot, atF('uOpacity'), opacity);
|
|
5817
5874
|
this.lineFrags.writeFloats(fragmentSlot, atF('uCameraPos'), camera.position);
|
|
5818
5875
|
this.lineFrags.writeFloat(fragmentSlot, atF('uSoftness'), softness);
|
|
5819
|
-
this.lineFrags.writeInt(fragmentSlot, atF('uOutputTransform'), this.
|
|
5820
|
-
this.lineFrags.writeFloat(fragmentSlot, atF('uOutputExposure'), this.
|
|
5876
|
+
this.lineFrags.writeInt(fragmentSlot, atF('uOutputTransform'), this.passGrade());
|
|
5877
|
+
this.lineFrags.writeFloat(fragmentSlot, atF('uOutputExposure'), this.passGradeExposure());
|
|
5821
5878
|
/* The medium, through the same resolver every other pass here binds from. */
|
|
5822
5879
|
const medium = resolveAtmosphere(
|
|
5823
5880
|
env,
|
|
@@ -8475,9 +8532,10 @@ export class WebGPURenderer implements RendererApi {
|
|
|
8475
8532
|
* at 92 MB a frame at 824x1830. `ensurePass` opens it on the first draw that wants it, which
|
|
8476
8533
|
* for such a scene is after the last mirror, so it is opened once and cleared once.
|
|
8477
8534
|
*/
|
|
8478
|
-
|
|
8479
|
-
this.pendingClearColor[
|
|
8480
|
-
this.pendingClearColor[
|
|
8535
|
+
const frameClear = this.clearOf(clearColor);
|
|
8536
|
+
this.pendingClearColor[0] = frameClear[0];
|
|
8537
|
+
this.pendingClearColor[1] = frameClear[1];
|
|
8538
|
+
this.pendingClearColor[2] = frameClear[2];
|
|
8481
8539
|
this.pendingClearMask = SCENE_TARGET;
|
|
8482
8540
|
if (this.quality.deferFramePass) {
|
|
8483
8541
|
this.pass = null;
|
|
@@ -8976,6 +9034,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
8976
9034
|
f.set(this.transmission, at('uGlassTint'));
|
|
8977
9035
|
/* Nor layers: one layer until a material asks for more. */
|
|
8978
9036
|
f.fill(0, at('uLayers'), at('uLayers') + LAYER_FLOATS);
|
|
9037
|
+
f.fill(0, at('uLayerLooks'), at('uLayerLooks') + LOOK_FLOATS);
|
|
8979
9038
|
f[at('uCutout')] = 0;
|
|
8980
9039
|
f[at('uCutout') + 1] = 0;
|
|
8981
9040
|
this.cutoutResolveStaged = 'hard';
|
|
@@ -9233,8 +9292,8 @@ export class WebGPURenderer implements RendererApi {
|
|
|
9233
9292
|
* `screenEffects` off, or on without `hdrScene`, the mesh pass writes what reaches the
|
|
9234
9293
|
* canvas and grades as it always did.
|
|
9235
9294
|
*/
|
|
9236
|
-
i[at('uOutputTransform')] = this.
|
|
9237
|
-
f[at('uOutputExposure')] = this.
|
|
9295
|
+
i[at('uOutputTransform')] = this.passGrade();
|
|
9296
|
+
f[at('uOutputExposure')] = this.passGradeExposure();
|
|
9238
9297
|
/* The projection occlusion needs, settled by the pass that has a camera. */
|
|
9239
9298
|
this.frameProjection = camera.projection as Float32Array;
|
|
9240
9299
|
|
|
@@ -10088,7 +10147,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
10088
10147
|
/* `1` is sRGB alone: the conversion without the curve. `Math.min` rather than a literal, so
|
|
10089
10148
|
a renderer asked for `none` stays at none. See `TranslucentMeshOptions.toneMapped`. */
|
|
10090
10149
|
if (!toneMapped) {
|
|
10091
|
-
this.perFrameInts[this.materialField('uOutputTransform')] = Math.min(this.
|
|
10150
|
+
this.perFrameInts[this.materialField('uOutputTransform')] = Math.min(this.passGrade(), 1);
|
|
10092
10151
|
}
|
|
10093
10152
|
/*
|
|
10094
10153
|
* **Refraction, and the snapshot it reads is taken here or the draw does not refract.**
|
|
@@ -10260,7 +10319,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
10260
10319
|
shared, so a strength left set is worn by everything drawn after it. */
|
|
10261
10320
|
if (refracting) this.releaseSeeThrough();
|
|
10262
10321
|
if (!toneMapped) {
|
|
10263
|
-
this.perFrameInts[this.materialField('uOutputTransform')] = this.
|
|
10322
|
+
this.perFrameInts[this.materialField('uOutputTransform')] = this.passGrade();
|
|
10264
10323
|
}
|
|
10265
10324
|
if (ditheredBlend) {
|
|
10266
10325
|
this.perFrameFloats[this.materialField('uCutout') + 1] =
|
|
@@ -10728,7 +10787,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
10728
10787
|
this.perFrameInts[this.materialField('uFogEnabled')] = fog;
|
|
10729
10788
|
}
|
|
10730
10789
|
if (!toneMapped) {
|
|
10731
|
-
this.perFrameInts[this.materialField('uOutputTransform')] = Math.min(this.
|
|
10790
|
+
this.perFrameInts[this.materialField('uOutputTransform')] = Math.min(this.passGrade(), 1);
|
|
10732
10791
|
}
|
|
10733
10792
|
|
|
10734
10793
|
const depthWrite = options.depthWrite ?? true;
|
|
@@ -10838,7 +10897,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
10838
10897
|
this.perFrameInts[this.materialField('uFogEnabled')] = this.surfaceFog;
|
|
10839
10898
|
}
|
|
10840
10899
|
if (!toneMapped) {
|
|
10841
|
-
this.perFrameInts[this.materialField('uOutputTransform')] = this.
|
|
10900
|
+
this.perFrameInts[this.materialField('uOutputTransform')] = this.passGrade();
|
|
10842
10901
|
}
|
|
10843
10902
|
if (refracting) this.releaseSeeThrough();
|
|
10844
10903
|
if (ditheredBlend) {
|
|
@@ -10955,6 +11014,8 @@ export class WebGPURenderer implements RendererApi {
|
|
|
10955
11014
|
i[atF('uFogMode')] = medium.fogMode;
|
|
10956
11015
|
f[atF('uFogNear')] = medium.fogNear;
|
|
10957
11016
|
f[atF('uFogFar')] = medium.fogFar;
|
|
11017
|
+
i[atF('uOutputTransform')] = this.passGrade();
|
|
11018
|
+
f[atF('uOutputExposure')] = this.passGradeExposure();
|
|
10958
11019
|
|
|
10959
11020
|
const fragmentSlot = this.scatterFrags.allocate();
|
|
10960
11021
|
if (fragmentSlot === null) return;
|
|
@@ -12575,6 +12636,8 @@ export class WebGPURenderer implements RendererApi {
|
|
|
12575
12636
|
);
|
|
12576
12637
|
f.set(medium.underwaterColor, at('uUnderwaterColor'));
|
|
12577
12638
|
f[at('uUnderwaterFactor')] = medium.underwaterFactor;
|
|
12639
|
+
this.skyInts[at('uOutputTransform')] = this.passGrade();
|
|
12640
|
+
f[at('uOutputExposure')] = this.passGradeExposure();
|
|
12578
12641
|
|
|
12579
12642
|
const slot = this.skyUniforms.allocate();
|
|
12580
12643
|
if (slot === null) {
|
|
@@ -12739,6 +12802,8 @@ export class WebGPURenderer implements RendererApi {
|
|
|
12739
12802
|
? (OUTPUT_TRANSFORM_CODE[this.quality.outputTransform] ?? 0)
|
|
12740
12803
|
: 0;
|
|
12741
12804
|
f[at('uOutputExposure')] = graded ? this.exposure : 1;
|
|
12805
|
+
/* The occlusion multiplied in as an encoded pixel takes it, where the passes graded the scene. */
|
|
12806
|
+
i[at('uSceneGraded')] = this.passGrade() !== 0 ? 1 : 0;
|
|
12742
12807
|
f.set(this.filmA, at('uFilmA'));
|
|
12743
12808
|
f.set(this.filmB, at('uFilmB'));
|
|
12744
12809
|
f[at('uDisplayHeadroom')] = this.headroom;
|
|
@@ -13368,8 +13433,8 @@ export class WebGPURenderer implements RendererApi {
|
|
|
13368
13433
|
});
|
|
13369
13434
|
const context = this.passContext;
|
|
13370
13435
|
context.pass = pass;
|
|
13371
|
-
context.outputTransform = this.
|
|
13372
|
-
context.outputExposure = this.
|
|
13436
|
+
context.outputTransform = this.passGrade();
|
|
13437
|
+
context.outputExposure = this.passGradeExposure();
|
|
13373
13438
|
context.jitter = this.latePassJitter;
|
|
13374
13439
|
context.format = RECON_HISTORY_FORMAT;
|
|
13375
13440
|
context.depthFormat = DEPTH_FORMAT;
|
|
@@ -14018,6 +14083,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
14018
14083
|
f[base + at('uDecalOpacity')] = decal.opacity;
|
|
14019
14084
|
f[base + at('uDecalFacingCos')] = decal.facingCos;
|
|
14020
14085
|
f[base + at('uDecalSoftness')] = decal.softness;
|
|
14086
|
+
this.decalInts[base + at('uOutputTransform')] = this.passGrade();
|
|
14021
14087
|
slot++;
|
|
14022
14088
|
});
|
|
14023
14089
|
device.queue.writeBuffer(this.decalUniforms, 0, this.decalStaging, 0, slot * DECAL_SLOT);
|
|
@@ -14264,6 +14330,8 @@ export class WebGPURenderer implements RendererApi {
|
|
|
14264
14330
|
g[upsampleAt('uDepthToViewZ') + 1] = inv[14] ?? 0;
|
|
14265
14331
|
g[upsampleAt('uDepthToViewZ') + 2] = inv[11] ?? 0;
|
|
14266
14332
|
g[upsampleAt('uDepthToViewZ') + 3] = inv[15] ?? 1;
|
|
14333
|
+
this.mediumUpsampleInts[upsampleAt('uOutputTransform')] = this.passGrade();
|
|
14334
|
+
g[upsampleAt('uOutputExposure')] = this.passGradeExposure();
|
|
14267
14335
|
device.queue.writeBuffer(this.mediumUpsampleUniforms, 0, this.mediumUpsampleStaging);
|
|
14268
14336
|
|
|
14269
14337
|
const march = encoder.beginRenderPass({
|
|
@@ -14989,6 +15057,8 @@ export class WebGPURenderer implements RendererApi {
|
|
|
14989
15057
|
projection,
|
|
14990
15058
|
this.skinInverseProjection,
|
|
14991
15059
|
() => this.gpuTimer.writesFor(),
|
|
15060
|
+
this.passGrade(),
|
|
15061
|
+
this.passGradeExposure(),
|
|
14992
15062
|
);
|
|
14993
15063
|
this.pass = encoder.beginRenderPass({
|
|
14994
15064
|
label: 'skin.scattered',
|
|
@@ -16199,8 +16269,8 @@ export class WebGPURenderer implements RendererApi {
|
|
|
16199
16269
|
const context = this.passContext;
|
|
16200
16270
|
context.pass = pass;
|
|
16201
16271
|
/* The frame's grade, so a contributed pass can obey the 2026-08-17 rule. See `PassContext`. */
|
|
16202
|
-
context.outputTransform = this.
|
|
16203
|
-
context.outputExposure = this.
|
|
16272
|
+
context.outputTransform = this.passGrade();
|
|
16273
|
+
context.outputExposure = this.passGradeExposure();
|
|
16204
16274
|
context.format = target.format;
|
|
16205
16275
|
context.samples = target.sampleCount;
|
|
16206
16276
|
return context as PassContext;
|
|
@@ -16697,8 +16767,11 @@ export class WebGPURenderer implements RendererApi {
|
|
|
16697
16767
|
const slot = this.insetUniforms.allocate();
|
|
16698
16768
|
if (slot !== null) {
|
|
16699
16769
|
/* Only the colour: the quad covers the viewport set above, so it needs no geometry. */
|
|
16700
|
-
const color = clearColor ?? BLACK_CLEAR;
|
|
16701
|
-
this.
|
|
16770
|
+
const color = this.clearOf(clearColor ?? BLACK_CLEAR);
|
|
16771
|
+
this.insetClear[0] = color[0];
|
|
16772
|
+
this.insetClear[1] = color[1];
|
|
16773
|
+
this.insetClear[2] = color[2];
|
|
16774
|
+
this.insetUniforms.writeFloats(slot, 0, this.insetClear);
|
|
16702
16775
|
const insetPipe = insetPipeline(
|
|
16703
16776
|
this.targetPipelines(),
|
|
16704
16777
|
this.surface.device,
|
|
@@ -17360,6 +17433,8 @@ export class WebGPURenderer implements RendererApi {
|
|
|
17360
17433
|
f[atF('uTime')] = timeSeconds;
|
|
17361
17434
|
f[atF('uMaxDrop')] = CAUSTICS_MAX_DROP_M;
|
|
17362
17435
|
f[atF('uStrength')] = strength;
|
|
17436
|
+
i[atF('uOutputTransform')] = this.passGrade();
|
|
17437
|
+
f[atF('uOutputExposure')] = this.passGradeExposure();
|
|
17363
17438
|
f.set(env.directionalDir, atF('uLightDir'));
|
|
17364
17439
|
|
|
17365
17440
|
/*
|
|
@@ -41,6 +41,8 @@ export class SkinScatterPass {
|
|
|
41
41
|
private readonly layout: GPUBindGroupLayout;
|
|
42
42
|
private readonly uniforms: GPUBuffer;
|
|
43
43
|
private readonly staging = new Float32Array((SLOT * 2) / 4);
|
|
44
|
+
/** The same blocks' ints: the frame's grade, which the second axis applies. */
|
|
45
|
+
private readonly stagingInts = new Int32Array(this.staging.buffer);
|
|
44
46
|
private readonly linear: GPUSampler;
|
|
45
47
|
private readonly nearest: GPUSampler;
|
|
46
48
|
private readonly commands: CommandPool = createCommandPool(16);
|
|
@@ -212,6 +214,9 @@ export class SkinScatterPass {
|
|
|
212
214
|
projection: ArrayLike<number>,
|
|
213
215
|
inverseProjection: ArrayLike<number>,
|
|
214
216
|
timestampWrites: () => GPURenderPassTimestampWrites | undefined,
|
|
217
|
+
/** The frame's grade, which the second axis applies to the light it adds. */
|
|
218
|
+
outputTransform = 0,
|
|
219
|
+
outputExposure = 1,
|
|
215
220
|
): void {
|
|
216
221
|
const diffuse = this.diffuse;
|
|
217
222
|
if (diffuse === null || this.diffuseView === null || this.acrossView === null) return;
|
|
@@ -221,7 +226,14 @@ export class SkinScatterPass {
|
|
|
221
226
|
this.groupDown = this.group('skin.blurDown', this.acrossView, depth);
|
|
222
227
|
this.groupsDepth = depth;
|
|
223
228
|
}
|
|
224
|
-
this.writeUniforms(
|
|
229
|
+
this.writeUniforms(
|
|
230
|
+
diffuse.width,
|
|
231
|
+
diffuse.height,
|
|
232
|
+
projection,
|
|
233
|
+
inverseProjection,
|
|
234
|
+
outputTransform,
|
|
235
|
+
outputExposure,
|
|
236
|
+
);
|
|
225
237
|
|
|
226
238
|
const across = postPipeline(
|
|
227
239
|
pipelines,
|
|
@@ -316,6 +328,8 @@ export class SkinScatterPass {
|
|
|
316
328
|
height: number,
|
|
317
329
|
projection: ArrayLike<number>,
|
|
318
330
|
inverse: ArrayLike<number>,
|
|
331
|
+
outputTransform: number,
|
|
332
|
+
outputExposure: number,
|
|
319
333
|
): void {
|
|
320
334
|
const f = this.staging;
|
|
321
335
|
const fields = BINDINGS.fields;
|
|
@@ -335,6 +349,8 @@ export class SkinScatterPass {
|
|
|
335
349
|
f.set(this.profiles, base + fields.uProfiles.offset / 4);
|
|
336
350
|
/* The colour is applied once, on the axis that adds into the frame. */
|
|
337
351
|
f[base + fields.uApplyAlbedo.offset / 4] = axis;
|
|
352
|
+
this.stagingInts[base + fields.uOutputTransform.offset / 4] = outputTransform;
|
|
353
|
+
f[base + fields.uOutputExposure.offset / 4] = outputExposure;
|
|
338
354
|
}
|
|
339
355
|
this.device.queue.writeBuffer(this.uniforms, 0, f);
|
|
340
356
|
}
|
|
@@ -116,10 +116,15 @@ export class CausticsRenderer {
|
|
|
116
116
|
windX = 0,
|
|
117
117
|
windZ = 0,
|
|
118
118
|
strength = 1,
|
|
119
|
+
/** The renderer's `passGrade`: the frame's, but none in a probe's face or a capture. */
|
|
120
|
+
outputTransform = 0,
|
|
121
|
+
outputExposure = 1,
|
|
119
122
|
): boolean {
|
|
120
123
|
if (this.vertexCount === 0) return false;
|
|
121
124
|
const u = this.uniforms;
|
|
122
125
|
gl.useProgram(this.program);
|
|
126
|
+
gl.uniform1i(u['uOutputTransform'] ?? null, outputTransform);
|
|
127
|
+
gl.uniform1f(u['uOutputExposure'] ?? null, outputExposure);
|
|
123
128
|
gl.uniformMatrix4fv(u['uViewProj'] ?? null, false, camera.viewProjection);
|
|
124
129
|
gl.uniform3fv(u['uCameraPos'] ?? null, camera.position);
|
|
125
130
|
gl.uniform1f(u['uTime'] ?? null, timeSeconds);
|
package/src/render/decalPass.ts
CHANGED
|
@@ -53,6 +53,8 @@ export class DecalPass {
|
|
|
53
53
|
eye: ArrayLike<number>,
|
|
54
54
|
width: number,
|
|
55
55
|
height: number,
|
|
56
|
+
/** The renderer's `passGrade`: how the frame under the marks was graded, 0 for not yet. */
|
|
57
|
+
outputTransform = 0,
|
|
56
58
|
): void {
|
|
57
59
|
if (queue.length === 0) return;
|
|
58
60
|
const { gl } = this;
|
|
@@ -62,6 +64,7 @@ export class DecalPass {
|
|
|
62
64
|
mat4.multiply(this.depthToWorld, this.depthToWorld, DEPTH_01_TO_CLIP);
|
|
63
65
|
|
|
64
66
|
gl.useProgram(this.program);
|
|
67
|
+
gl.uniform1i(u['uOutputTransform'] ?? null, outputTransform);
|
|
65
68
|
gl.bindVertexArray(this.vao);
|
|
66
69
|
gl.disable(gl.DEPTH_TEST);
|
|
67
70
|
gl.depthMask(false);
|
|
@@ -74,9 +74,14 @@ export class FlockRenderer {
|
|
|
74
74
|
tint: Vec3,
|
|
75
75
|
windX = 0,
|
|
76
76
|
windZ = 0,
|
|
77
|
+
outputTransform = 0,
|
|
78
|
+
outputExposure = 1,
|
|
77
79
|
): void {
|
|
78
80
|
const u = this.uniforms;
|
|
79
81
|
gl.useProgram(this.program);
|
|
82
|
+
/* The renderer's `passGrade`: the frame's, but none in a probe's face or a capture. */
|
|
83
|
+
gl.uniform1i(u['uOutputTransform'] ?? null, outputTransform);
|
|
84
|
+
gl.uniform1f(u['uOutputExposure'] ?? null, outputExposure);
|
|
80
85
|
gl.uniformMatrix4fv(u['uViewProj'] ?? null, false, camera.viewProjection);
|
|
81
86
|
/*
|
|
82
87
|
* No `uCameraPos` here, and the shader no longer declares one. It fed the banking that
|
|
@@ -268,7 +268,14 @@ export class GlobalMediumPass {
|
|
|
268
268
|
* `(ONE, SRC_ALPHA)` on colour is `dst * transmittance + inscatter`; alpha is left alone with
|
|
269
269
|
* `(ZERO, ONE)`, because the frame's own alpha is not the medium's to spend.
|
|
270
270
|
*/
|
|
271
|
-
compositeMedium(
|
|
271
|
+
compositeMedium(
|
|
272
|
+
depth: WebGLTexture,
|
|
273
|
+
frameWidth: number,
|
|
274
|
+
frameHeight: number,
|
|
275
|
+
/** The renderer's `passGrade`: how the frame under the medium was graded. */
|
|
276
|
+
outputTransform = 0,
|
|
277
|
+
outputExposure = 1,
|
|
278
|
+
): void {
|
|
272
279
|
if (this.target === null || this.unavailable) return;
|
|
273
280
|
const { gl } = this;
|
|
274
281
|
const u = this.upsampleUniforms;
|
|
@@ -292,6 +299,8 @@ export class GlobalMediumPass {
|
|
|
292
299
|
this.mediumTexel[1] = 1 / Math.max(1, this.height);
|
|
293
300
|
gl.uniform2fv(u['uMediumTexel'] ?? null, this.mediumTexel);
|
|
294
301
|
gl.uniform4fv(u['uDepthToViewZ'] ?? null, this.depthToViewZ);
|
|
302
|
+
gl.uniform1i(u['uOutputTransform'] ?? null, outputTransform);
|
|
303
|
+
gl.uniform1f(u['uOutputExposure'] ?? null, outputExposure);
|
|
295
304
|
|
|
296
305
|
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
|
297
306
|
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The forward grade on the CPU: what `applyOutputTransform` in `shaders/outputTransform.ts` does to
|
|
3
|
+
* a colour, for a colour no shader draws — a clear colour, written to a target before any pass runs.
|
|
4
|
+
*
|
|
5
|
+
* **Why a clear colour is graded at all.** Every pass grades what it draws when nothing after it
|
|
6
|
+
* will, so a colour handed to the renderer means one thing wherever it lands: linear light. A clear
|
|
7
|
+
* left as it was given is the one colour in the frame read as a display value, so a scene whose fog
|
|
8
|
+
* fades to its background colour fades to a different colour than the background it meets; with
|
|
9
|
+
* `hdrScene` the resolve grades the clear with everything else, and a frame without it has to agree.
|
|
10
|
+
*
|
|
11
|
+
* **A second copy of the curve, and what holds the two together** is the hand-derived values in
|
|
12
|
+
* `gradeColor.test.ts`, the same ones the shader's own constants give: the sRGB encode's two
|
|
13
|
+
* segments, the ACES fit through its two matrices, and the highlight shoulder. Code 4, the filmic
|
|
14
|
+
* curve, is the composite's alone, so a forward code never carries it (`forwardTransformCode`).
|
|
15
|
+
*/
|
|
16
|
+
import type { Vec3 } from '../math/color.ts';
|
|
17
|
+
|
|
18
|
+
/* Columns as the shader's `mat3` reads them, written here as rows of the product. */
|
|
19
|
+
const IN_R = [0.59719, 0.35458, 0.04823];
|
|
20
|
+
const IN_G = [0.076, 0.90834, 0.01566];
|
|
21
|
+
const IN_B = [0.0284, 0.13383, 0.83777];
|
|
22
|
+
const OUT_R = [1.60475, -0.53108, -0.07367];
|
|
23
|
+
const OUT_G = [-0.10208, 1.10813, -0.00605];
|
|
24
|
+
const OUT_B = [-0.00327, -0.07276, 1.07602];
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* `color` graded by forward code `code` at `exposure` into `out`, which may be `color`: 0 leaves it
|
|
28
|
+
* as it is, 1 encodes it as sRGB, 2 runs ACES first and 3 the highlight shoulder. Allocates nothing.
|
|
29
|
+
*/
|
|
30
|
+
export function gradeColorInto(
|
|
31
|
+
code: number,
|
|
32
|
+
exposure: number,
|
|
33
|
+
color: Readonly<Vec3>,
|
|
34
|
+
out: Vec3,
|
|
35
|
+
): Vec3 {
|
|
36
|
+
let r = color[0];
|
|
37
|
+
let g = color[1];
|
|
38
|
+
let b = color[2];
|
|
39
|
+
if (code === 0) {
|
|
40
|
+
out[0] = r;
|
|
41
|
+
out[1] = g;
|
|
42
|
+
out[2] = b;
|
|
43
|
+
return out;
|
|
44
|
+
}
|
|
45
|
+
if (code === 2) {
|
|
46
|
+
const x = r * exposure;
|
|
47
|
+
const y = g * exposure;
|
|
48
|
+
const z = b * exposure;
|
|
49
|
+
const ir = fit(dot(IN_R, x, y, z));
|
|
50
|
+
const ig = fit(dot(IN_G, x, y, z));
|
|
51
|
+
const ib = fit(dot(IN_B, x, y, z));
|
|
52
|
+
r = clamp01(dot(OUT_R, ir, ig, ib));
|
|
53
|
+
g = clamp01(dot(OUT_G, ir, ig, ib));
|
|
54
|
+
b = clamp01(dot(OUT_B, ir, ig, ib));
|
|
55
|
+
} else if (code === 3) {
|
|
56
|
+
r *= exposure;
|
|
57
|
+
g *= exposure;
|
|
58
|
+
b *= exposure;
|
|
59
|
+
const m = Math.max(r, g, b);
|
|
60
|
+
if (m > 0.8) {
|
|
61
|
+
const e = m - 0.8;
|
|
62
|
+
const k = (0.8 + (0.2 * e) / (e + 0.2)) / m;
|
|
63
|
+
r *= k;
|
|
64
|
+
g *= k;
|
|
65
|
+
b *= k;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
out[0] = encode(r);
|
|
69
|
+
out[1] = encode(g);
|
|
70
|
+
out[2] = encode(b);
|
|
71
|
+
return out;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function dot(row: readonly number[], x: number, y: number, z: number): number {
|
|
75
|
+
return (row[0] as number) * x + (row[1] as number) * y + (row[2] as number) * z;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** The RRT and ODT fit, `rrtAndOdtFit` in the shader. */
|
|
79
|
+
function fit(v: number): number {
|
|
80
|
+
return (v * (v + 0.0245786) - 0.000090537) / (v * (0.983729 * v + 0.432951) + 0.238081);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Linear to sRGB, the piecewise form the shader uses. */
|
|
84
|
+
function encode(c: number): number {
|
|
85
|
+
return c <= 0.0031308 ? c * 12.92 : 1.055 * Math.max(c, 0) ** (1 / 2.4) - 0.055;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function clamp01(v: number): number {
|
|
89
|
+
return v < 0 ? 0 : v > 1 ? 1 : v;
|
|
90
|
+
}
|