@driftengine/core 4.12.0 → 4.13.1
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 +77 -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 +3 -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 +17 -4
- package/dist/render/backend/webgl2/renderer.js +82 -47
- 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 +19 -8
- package/dist/render/backend/webgpu/renderer.js +94 -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/layerLooks.js +11 -19
- package/dist/render/shaders/flat/layered.js +33 -18
- 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.js +23 -23
- 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 +16 -4
- package/dist/render/surfaceLayerLooks.js +33 -14
- package/dist/render/surfaceLayers.d.ts +2 -2
- 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/src/geometry/meshBuilder.ts +21 -2
- package/src/index.ts +11 -2
- package/src/math/color.ts +39 -0
- package/src/render/backend/webgl2/renderer.ts +134 -44
- 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 +104 -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/layerLooks.ts +11 -19
- package/src/render/shaders/flat/layered.ts +33 -18
- 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 +23 -23
- 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 +49 -18
- package/src/render/surfaceLayers.ts +2 -2
- 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
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { GrowableF32, GrowableU32 } from './growable.ts';
|
|
2
2
|
import type { MeshData } from '../render/mesh.ts';
|
|
3
3
|
import type { Vec3 } from '../math/color.ts';
|
|
4
|
+
import { decodeSrgbInPlace } from '../math/color.ts';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* The emissive colour of a vertex that has not been given one: negative, which no real
|
|
@@ -23,6 +24,16 @@ export interface MeshBuildOptions {
|
|
|
23
24
|
* sample them. On, for a mesh that will be drawn with a `SurfaceTexture`.
|
|
24
25
|
*/
|
|
25
26
|
planarUvs?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* The space the builder's colours were given in. `'linear'`, the default, is the renderer's own:
|
|
29
|
+
* light as a surface reflects it. `'srgb'` is a colour picked by eye, on a screen or in a paint
|
|
30
|
+
* program, and decodes every vertex's colour and emissive colour to linear here, once — as a
|
|
31
|
+
* texture of colours uploaded as sRGB is decoded — so a world built from hand-picked colours draws
|
|
32
|
+
* them as picked under the default `outputTransform`, where the same numbers taken as linear come
|
|
33
|
+
* out pale. What it gives up: nothing at the colour picked; a colour meant as light rather than as
|
|
34
|
+
* a look wants a linear number of its own.
|
|
35
|
+
*/
|
|
36
|
+
colorSpace?: 'linear' | 'srgb';
|
|
26
37
|
}
|
|
27
38
|
|
|
28
39
|
/**
|
|
@@ -1083,14 +1094,22 @@ export class MeshBuilder {
|
|
|
1083
1094
|
/* Same promise as `specular`: a mesh nothing bound uploads no skinning buffers, and
|
|
1084
1095
|
`MeshData` requires both arrays or neither. */
|
|
1085
1096
|
const skinned = this.anyJoint;
|
|
1097
|
+
/* Colours picked by eye, decoded once here: see `MeshBuildOptions.colorSpace`. */
|
|
1098
|
+
const picked = options.colorSpace === 'srgb';
|
|
1086
1099
|
return {
|
|
1087
1100
|
positions: this.positions.toTyped(),
|
|
1088
1101
|
normals: this.normals.toTyped(),
|
|
1089
|
-
colors: this.colors.toTyped(),
|
|
1102
|
+
colors: picked ? decodeSrgbInPlace(this.colors.toTyped()) : this.colors.toTyped(),
|
|
1090
1103
|
emissive: this.emissive.toTyped(),
|
|
1091
1104
|
...(shines ? { specular: this.specular.toTyped() } : {}),
|
|
1092
1105
|
...(textured ? { relief: this.relief.toTyped() } : {}),
|
|
1093
|
-
...(tinted
|
|
1106
|
+
...(tinted
|
|
1107
|
+
? {
|
|
1108
|
+
emissiveColor: picked
|
|
1109
|
+
? decodeSrgbInPlace(this.emissiveColor.toTyped())
|
|
1110
|
+
: this.emissiveColor.toTyped(),
|
|
1111
|
+
}
|
|
1112
|
+
: {}),
|
|
1094
1113
|
...(rough ? { roughness: this.roughness.toTyped() } : {}),
|
|
1095
1114
|
...(mineral ? { grain: this.grain.toTyped() } : {}),
|
|
1096
1115
|
...(skinned ? { joints: this.expandedJoints(), weights: this.expandedWeights() } : {}),
|
package/src/index.ts
CHANGED
|
@@ -96,7 +96,15 @@ export {
|
|
|
96
96
|
wrapAngle,
|
|
97
97
|
lerpAngle,
|
|
98
98
|
} from './math/scalar.ts';
|
|
99
|
-
export {
|
|
99
|
+
export {
|
|
100
|
+
decodeSrgbInPlace,
|
|
101
|
+
hslToRgb,
|
|
102
|
+
mixColor,
|
|
103
|
+
mixColorInto,
|
|
104
|
+
scaleColor,
|
|
105
|
+
srgbColor,
|
|
106
|
+
srgbToLinear,
|
|
107
|
+
} from './math/color.ts';
|
|
100
108
|
export type { Vec3 } from './math/color.ts';
|
|
101
109
|
|
|
102
110
|
export { Camera } from './render/camera.ts';
|
|
@@ -707,7 +715,7 @@ export type { LightGrid } from './render/lightGrid.ts';
|
|
|
707
715
|
export type { PointLightBuffer, PointLightSource } from './render/pointLightSelection.ts';
|
|
708
716
|
export { createFlame, flameFrequencyHz, updateFlame } from './render/flameLight.ts';
|
|
709
717
|
export type { Flame, FlameOptions } from './render/flameLight.ts';
|
|
710
|
-
export { computeLightMatrix } from './render/lightMatrix.ts';
|
|
718
|
+
export { computeLightMatrix, computeLightMatrixForBounds } from './render/lightMatrix.ts';
|
|
711
719
|
export { PlumeRenderer } from './render/plumeRenderer.ts';
|
|
712
720
|
export { createEnvironment } from './render/backend/webgl2/renderer.ts';
|
|
713
721
|
export type { InsetRect } from './render/backend/webgl2/renderer.ts';
|
|
@@ -750,6 +758,7 @@ export type { ShellStation, ShellFillOptions } from './geometry/shellFill.ts';
|
|
|
750
758
|
* this line the only way for it to comply was to copy, which is what the rule forbids.
|
|
751
759
|
*/
|
|
752
760
|
export { OUTPUT_TRANSFORM_GLSL } from './render/shaders/outputTransform.ts';
|
|
761
|
+
export { interfaceGrade } from './render/passGrade.ts';
|
|
753
762
|
export { PLUME_VERT, FIRE_FRAG } from './render/shaders/fire.ts';
|
|
754
763
|
export { ARCANE_FRAG } from './render/shaders/arcane.ts';
|
|
755
764
|
export { SMOKE_FRAG } from './render/shaders/smoke.ts';
|
package/src/math/color.ts
CHANGED
|
@@ -35,3 +35,42 @@ export function mixColorInto(out: Vec3, a: Vec3, b: Vec3, t: number): void {
|
|
|
35
35
|
export function scaleColor(color: Vec3, factor: number): Vec3 {
|
|
36
36
|
return [color[0] * factor, color[1] * factor, color[2] * factor];
|
|
37
37
|
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* A colour picked by eye — on a screen, in a paint program, as a CSS colour — as the linear value
|
|
41
|
+
* the renderer lights, blends, fogs and grades: each channel through the sRGB curve, which the
|
|
42
|
+
* default `outputTransform`, `'srgb'`, applies the other way at the end, so a colour drawn as it is,
|
|
43
|
+
* a sky, a clear or a surface lit at exactly 1, comes out as the colour picked.
|
|
44
|
+
*
|
|
45
|
+
* **Why a colour needs it**: everything the renderer draws is linear and encoded at the end, so a
|
|
46
|
+
* colour typed as it looks comes out too light — a brown picked as `(0.55, 0.3, 0.15)` draws as
|
|
47
|
+
* tan. **What it gives up**: nothing at the colour picked. **What would make it wrong** is a number
|
|
48
|
+
* that is an amount rather than a look: a light's intensity, a multiplier, a strength, which are
|
|
49
|
+
* linear already. Allocates, so it is for building a world rather than for a frame.
|
|
50
|
+
*/
|
|
51
|
+
export function srgbColor(r: number, g: number, b: number): Vec3 {
|
|
52
|
+
return [srgbToLinear(r), srgbToLinear(g), srgbToLinear(b)];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Every value of `values` decoded from sRGB in place, but a negative one, which is a sentinel and
|
|
57
|
+
* not a colour (`MeshBuilder`'s emissive colour left to the albedo). For whole arrays of colours
|
|
58
|
+
* picked by eye; see `MeshBuildOptions.colorSpace`.
|
|
59
|
+
*/
|
|
60
|
+
export function decodeSrgbInPlace(values: Float32Array): Float32Array {
|
|
61
|
+
for (let i = 0; i < values.length; i++) {
|
|
62
|
+
const value = values[i] as number;
|
|
63
|
+
if (value >= 0) values[i] = srgbToLinear(value);
|
|
64
|
+
}
|
|
65
|
+
return values;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* One channel, 0 to 1, through the sRGB decode: the straight segment below 0.04045. For a colour
|
|
70
|
+
* that arrives as numbers per channel rather than as a triple, a packed hex token being the usual
|
|
71
|
+
* one; allocates nothing.
|
|
72
|
+
*/
|
|
73
|
+
export function srgbToLinear(value: number): number {
|
|
74
|
+
// determinism: build-time — a colour for drawing, which no tick reads
|
|
75
|
+
return value <= 0.04045 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4;
|
|
76
|
+
}
|
|
@@ -340,7 +340,9 @@ import {
|
|
|
340
340
|
RUSH_REACH_UV,
|
|
341
341
|
clampMotionShare,
|
|
342
342
|
} from '../../vertexDefaults.ts';
|
|
343
|
-
import { OUTPUT_TRANSFORM_CODE
|
|
343
|
+
import { OUTPUT_TRANSFORM_CODE } from '../../vertexDefaults.ts';
|
|
344
|
+
import { gradeColorInto } from '../../gradeColor.ts';
|
|
345
|
+
import { interfaceGrade, passGradeCode, passGradeExposure } from '../../passGrade.ts';
|
|
344
346
|
import type { DisplayRange } from '../../displayRange.ts';
|
|
345
347
|
import {
|
|
346
348
|
DEFAULT_FILMIC_CURVE,
|
|
@@ -1798,6 +1800,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
1798
1800
|
private warnedOverlayBlocks = false;
|
|
1799
1801
|
/** Whether a probe is being baked, so the passes that would recurse into one can tell. */
|
|
1800
1802
|
private probePassActive = false;
|
|
1803
|
+
/** `clearOf`'s scratch: a clear colour as the open target takes it. */
|
|
1804
|
+
private readonly gradedClear: Vec3 = [0, 0, 0];
|
|
1801
1805
|
/**
|
|
1802
1806
|
* Whether a scene capture is being drawn (`captureScene`): an off-screen pass like a bake, so the
|
|
1803
1807
|
* frame's own view, its refraction copy and skin's spread are left alone, but lit as the frame is.
|
|
@@ -2563,8 +2567,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
2563
2567
|
sceneDepth: null,
|
|
2564
2568
|
};
|
|
2565
2569
|
const context = this.passContext;
|
|
2566
|
-
context.outputTransform = this.
|
|
2567
|
-
context.outputExposure = this.
|
|
2570
|
+
context.outputTransform = this.passGrade();
|
|
2571
|
+
context.outputExposure = this.passGradeExposure();
|
|
2568
2572
|
/*
|
|
2569
2573
|
* The copies a pass declared it reads, taken here with everything drawn before it: colour
|
|
2570
2574
|
* afresh at each reading pass, as WebGPU's `takePassColour` does, so a full-screen effect drawn
|
|
@@ -3893,11 +3897,11 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
3893
3897
|
} finally {
|
|
3894
3898
|
target.end(gl);
|
|
3895
3899
|
gl.frontFace(gl.CCW);
|
|
3900
|
+
this.capturePassActive = false;
|
|
3896
3901
|
this.useFlatProgram();
|
|
3897
|
-
gl.uniform1i(this.flatUniforms['uOutputTransform'] ?? null, this.
|
|
3898
|
-
gl.uniform1f(this.flatUniforms['uOutputExposure'] ?? null, this.
|
|
3902
|
+
gl.uniform1i(this.flatUniforms['uOutputTransform'] ?? null, this.passGrade());
|
|
3903
|
+
gl.uniform1f(this.flatUniforms['uOutputExposure'] ?? null, this.passGradeExposure());
|
|
3899
3904
|
this.materials.dirty();
|
|
3900
|
-
this.capturePassActive = false;
|
|
3901
3905
|
this.temporalJittering = jittering;
|
|
3902
3906
|
this.restoreViewport();
|
|
3903
3907
|
}
|
|
@@ -5289,7 +5293,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
5289
5293
|
*/
|
|
5290
5294
|
if (!lit) gl.uniform1i(u['uLightingEnabled'] ?? null, 0);
|
|
5291
5295
|
if (fog !== this.surfaceFog) gl.uniform1i(u['uFogEnabled'] ?? null, fog);
|
|
5292
|
-
if (!toneMapped) gl.uniform1i(u['uOutputTransform'] ?? null, Math.min(this.
|
|
5296
|
+
if (!toneMapped) gl.uniform1i(u['uOutputTransform'] ?? null, Math.min(this.passGrade(), 1));
|
|
5293
5297
|
|
|
5294
5298
|
if (blend) {
|
|
5295
5299
|
gl.enable(gl.BLEND);
|
|
@@ -5347,7 +5351,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
5347
5351
|
if (refracting) this.unbindSeeThrough(u);
|
|
5348
5352
|
if (!lit) gl.uniform1i(u['uLightingEnabled'] ?? null, 1);
|
|
5349
5353
|
if (fog !== this.surfaceFog) gl.uniform1i(u['uFogEnabled'] ?? null, this.surfaceFog);
|
|
5350
|
-
if (!toneMapped) gl.uniform1i(u['uOutputTransform'] ?? null, this.
|
|
5354
|
+
if (!toneMapped) gl.uniform1i(u['uOutputTransform'] ?? null, this.passGrade());
|
|
5351
5355
|
if (own) this.materials.dirty();
|
|
5352
5356
|
this.useFlatProgram();
|
|
5353
5357
|
}
|
|
@@ -5523,7 +5527,18 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
5523
5527
|
// raining dust into the sea.
|
|
5524
5528
|
const surface = env.underwater?.surfaceY;
|
|
5525
5529
|
const submerged = surface !== undefined && (camera.position[1] ?? 0) < surface;
|
|
5526
|
-
if (
|
|
5530
|
+
if (
|
|
5531
|
+
streaks.draw(
|
|
5532
|
+
this.gl,
|
|
5533
|
+
this.frameViewFor(camera),
|
|
5534
|
+
wind,
|
|
5535
|
+
timeSeconds,
|
|
5536
|
+
tint,
|
|
5537
|
+
submerged,
|
|
5538
|
+
this.passGrade(),
|
|
5539
|
+
this.passGradeExposure(),
|
|
5540
|
+
)
|
|
5541
|
+
) {
|
|
5527
5542
|
this.windStreakBudget.ask();
|
|
5528
5543
|
}
|
|
5529
5544
|
}
|
|
@@ -5563,7 +5578,18 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
5563
5578
|
timeSec: number,
|
|
5564
5579
|
): void {
|
|
5565
5580
|
this.scatterSkin();
|
|
5566
|
-
if (
|
|
5581
|
+
if (
|
|
5582
|
+
text.draw(
|
|
5583
|
+
viewportWidth,
|
|
5584
|
+
viewportHeight,
|
|
5585
|
+
originX,
|
|
5586
|
+
originY,
|
|
5587
|
+
style,
|
|
5588
|
+
timeSec,
|
|
5589
|
+
interfaceGrade(this.passGrade()),
|
|
5590
|
+
1,
|
|
5591
|
+
)
|
|
5592
|
+
) {
|
|
5567
5593
|
this.textBudget.ask();
|
|
5568
5594
|
}
|
|
5569
5595
|
}
|
|
@@ -5642,7 +5668,9 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
5642
5668
|
* real draw call touches the unit in the first place.
|
|
5643
5669
|
*/
|
|
5644
5670
|
/**
|
|
5645
|
-
* The
|
|
5671
|
+
* The grade a pass applies to what it draws into the target open now, and the exposure it
|
|
5672
|
+
* applies it at. The decision is `passGrade.ts`'s, for both backends; this only gathers the
|
|
5673
|
+
* state it is taken from.
|
|
5646
5674
|
*
|
|
5647
5675
|
* **Only where that pass is genuinely the last one to touch the frame.** With a scene target
|
|
5648
5676
|
* the resolve grades, so a pass that graded as well would be applying a curve to its own
|
|
@@ -5650,21 +5678,37 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
5650
5678
|
* `screenEffects` off there is no resolve at all and every pass writes straight to the canvas,
|
|
5651
5679
|
* so each one has to grade itself or the frame comes out half converted: linear values in an
|
|
5652
5680
|
* eight-bit buffer read as display values, which is a saturated, clipped version of the colour
|
|
5653
|
-
* the caller asked for rather than a slightly different one.
|
|
5681
|
+
* the caller asked for rather than a slightly different one. A probe's face or a capture is
|
|
5682
|
+
* never graded, and past the present every pass grades itself, composite or not.
|
|
5654
5683
|
*
|
|
5655
5684
|
* One pair of accessors rather than the expression written out at each site, because that is
|
|
5656
5685
|
* how the mesh pass and the particle pass came to disagree in the first place: the mesh pass
|
|
5657
5686
|
* gated, and the particle pass uploaded the transform unconditionally to a stage that did not
|
|
5658
5687
|
* yet declare it.
|
|
5659
5688
|
*/
|
|
5660
|
-
private
|
|
5661
|
-
|
|
5662
|
-
|
|
5663
|
-
|
|
5689
|
+
private passGrade(): number {
|
|
5690
|
+
const storing = this.probePassActive || this.capturePassActive;
|
|
5691
|
+
const transform = this.quality.outputTransform;
|
|
5692
|
+
return passGradeCode(storing, this.framePresented, this.resolveGrades(), transform);
|
|
5693
|
+
}
|
|
5694
|
+
|
|
5695
|
+
private passGradeExposure(): number {
|
|
5696
|
+
const storing = this.probePassActive || this.capturePassActive;
|
|
5697
|
+
return passGradeExposure(storing, this.framePresented, this.resolveGrades(), this.exposure);
|
|
5664
5698
|
}
|
|
5665
5699
|
|
|
5666
|
-
|
|
5667
|
-
|
|
5700
|
+
/** Whether the frame's resolve applies the transform, so the passes before it draw linear. */
|
|
5701
|
+
private resolveGrades(): boolean {
|
|
5702
|
+
return this.sceneTarget !== null && this.quality.hdrScene;
|
|
5703
|
+
}
|
|
5704
|
+
|
|
5705
|
+
/**
|
|
5706
|
+
* A clear colour as the target open now takes it: graded as a pass would grade it, so a colour
|
|
5707
|
+
* handed to the renderer is linear wherever it lands — the clear too, which the resolve grades
|
|
5708
|
+
* with everything else under `hdrScene`. See `gradeColor.ts`. Into one scratch, read at once.
|
|
5709
|
+
*/
|
|
5710
|
+
private clearOf(color: Readonly<Vec3>): Vec3 {
|
|
5711
|
+
return gradeColorInto(this.passGrade(), this.passGradeExposure(), color, this.gradedClear);
|
|
5668
5712
|
}
|
|
5669
5713
|
|
|
5670
5714
|
drawSdfText(handle: SdfTextRenderer, model: Float32Array, color: Vec3, opacity: number): void {
|
|
@@ -5679,8 +5723,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
5679
5723
|
model,
|
|
5680
5724
|
color,
|
|
5681
5725
|
opacity,
|
|
5682
|
-
this.
|
|
5683
|
-
this.
|
|
5726
|
+
this.passGrade(),
|
|
5727
|
+
this.passGradeExposure(),
|
|
5684
5728
|
)
|
|
5685
5729
|
) {
|
|
5686
5730
|
this.sdfTextBudget.ask();
|
|
@@ -5707,7 +5751,17 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
5707
5751
|
windZ = 0,
|
|
5708
5752
|
): void {
|
|
5709
5753
|
this.flockBudget.ask();
|
|
5710
|
-
flock.draw(
|
|
5754
|
+
flock.draw(
|
|
5755
|
+
this.gl,
|
|
5756
|
+
this.frameViewFor(camera),
|
|
5757
|
+
timeSeconds,
|
|
5758
|
+
params,
|
|
5759
|
+
tint,
|
|
5760
|
+
windX,
|
|
5761
|
+
windZ,
|
|
5762
|
+
this.passGrade(),
|
|
5763
|
+
this.passGradeExposure(),
|
|
5764
|
+
);
|
|
5711
5765
|
}
|
|
5712
5766
|
|
|
5713
5767
|
/**
|
|
@@ -5808,8 +5862,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
5808
5862
|
the uniform at all, so it was a write to a location that did not exist; now that the
|
|
5809
5863
|
stage grades itself it has to obey the same "only where this pass is last" rule the mesh
|
|
5810
5864
|
pass does, or a scene with a composite grades its particles twice. */
|
|
5811
|
-
gl.uniform1i(u['uOutputTransform'] ?? null, this.
|
|
5812
|
-
gl.uniform1f(u['uOutputExposure'] ?? null, this.
|
|
5865
|
+
gl.uniform1i(u['uOutputTransform'] ?? null, this.passGrade());
|
|
5866
|
+
gl.uniform1f(u['uOutputExposure'] ?? null, this.passGradeExposure());
|
|
5813
5867
|
gl.uniform3fv(u['uAmbient'] ?? null, env.ambient);
|
|
5814
5868
|
gl.uniform3fv(u['uAmbientGround'] ?? null, env.ambientGround ?? env.ambient);
|
|
5815
5869
|
gl.uniform1i(u['uNoiseOctaves'] ?? null, this.quality.plumeNoiseOctaves);
|
|
@@ -5917,6 +5971,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
5917
5971
|
gl.uniform3fv(u['uCoreColor'] ?? null, core);
|
|
5918
5972
|
gl.uniform3fv(u['uEdgeColor'] ?? null, edge);
|
|
5919
5973
|
gl.uniform1f(u['uCoreGain'] ?? null, coreGain);
|
|
5974
|
+
gl.uniform1i(u['uOutputTransform'] ?? null, this.passGrade());
|
|
5975
|
+
gl.uniform1f(u['uOutputExposure'] ?? null, this.passGradeExposure());
|
|
5920
5976
|
bindAtmosphere(
|
|
5921
5977
|
gl,
|
|
5922
5978
|
u,
|
|
@@ -5992,8 +6048,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
5992
6048
|
gl.uniform3fv(u['uColor'] ?? null, color);
|
|
5993
6049
|
gl.uniform1f(u['uOpacity'] ?? null, opacity);
|
|
5994
6050
|
gl.uniform1f(u['uSoftness'] ?? null, softness);
|
|
5995
|
-
gl.uniform1i(u['uOutputTransform'] ?? null, this.
|
|
5996
|
-
gl.uniform1f(u['uOutputExposure'] ?? null, this.
|
|
6051
|
+
gl.uniform1i(u['uOutputTransform'] ?? null, this.passGrade());
|
|
6052
|
+
gl.uniform1f(u['uOutputExposure'] ?? null, this.passGradeExposure());
|
|
5997
6053
|
bindAtmosphere(
|
|
5998
6054
|
gl,
|
|
5999
6055
|
u,
|
|
@@ -6042,6 +6098,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
6042
6098
|
gl.uniform3fv(u['uDirectionalColor'] ?? null, env.directionalColor);
|
|
6043
6099
|
gl.uniform3fv(u['uAmbient'] ?? null, env.ambient);
|
|
6044
6100
|
gl.uniform3fv(u['uCameraPos'] ?? null, camera.position);
|
|
6101
|
+
gl.uniform1i(u['uOutputTransform'] ?? null, this.passGrade());
|
|
6102
|
+
gl.uniform1f(u['uOutputExposure'] ?? null, this.passGradeExposure());
|
|
6045
6103
|
bindAtmosphere(
|
|
6046
6104
|
gl,
|
|
6047
6105
|
u,
|
|
@@ -6236,6 +6294,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
6236
6294
|
originX,
|
|
6237
6295
|
originY,
|
|
6238
6296
|
originZ,
|
|
6297
|
+
this.passGrade(),
|
|
6298
|
+
this.passGradeExposure(),
|
|
6239
6299
|
);
|
|
6240
6300
|
}
|
|
6241
6301
|
|
|
@@ -6265,6 +6325,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
6265
6325
|
this.quality.pointLightFalloff,
|
|
6266
6326
|
windX,
|
|
6267
6327
|
windZ,
|
|
6328
|
+
this.passGrade(),
|
|
6329
|
+
this.passGradeExposure(),
|
|
6268
6330
|
);
|
|
6269
6331
|
}
|
|
6270
6332
|
|
|
@@ -6295,6 +6357,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
6295
6357
|
windX,
|
|
6296
6358
|
windZ,
|
|
6297
6359
|
strength,
|
|
6360
|
+
this.passGrade(),
|
|
6361
|
+
this.passGradeExposure(),
|
|
6298
6362
|
);
|
|
6299
6363
|
if (drew) this.causticsBudget.ask();
|
|
6300
6364
|
}
|
|
@@ -6337,7 +6401,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
6337
6401
|
this.canvas.height,
|
|
6338
6402
|
source,
|
|
6339
6403
|
planeY,
|
|
6340
|
-
clearColor,
|
|
6404
|
+
this.clearOf(clearColor),
|
|
6341
6405
|
);
|
|
6342
6406
|
this.gpuTimer.begin('reflection');
|
|
6343
6407
|
this.reflectionPassActive = true;
|
|
@@ -7651,7 +7715,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
7651
7715
|
// scene rather than over a rectangle of flat colour.
|
|
7652
7716
|
gl.clear(gl.DEPTH_BUFFER_BIT);
|
|
7653
7717
|
} else {
|
|
7654
|
-
|
|
7718
|
+
const clear = this.clearOf(clearColor);
|
|
7719
|
+
gl.clearColor(clear[0], clear[1], clear[2], 1);
|
|
7655
7720
|
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
|
7656
7721
|
}
|
|
7657
7722
|
/*
|
|
@@ -7761,6 +7826,9 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
7761
7826
|
gl.uniformMatrix4fv(this.panelUniforms['uClipCorrection'] ?? null, false, IDENTITY_MAT4);
|
|
7762
7827
|
gl.uniform3fv(this.panelUniforms['uColor'] ?? null, color);
|
|
7763
7828
|
gl.uniform1f(this.panelUniforms['uAlpha'] ?? null, alpha);
|
|
7829
|
+
/* An interface: the screen encode, never the curve. See `interfaceGrade`. */
|
|
7830
|
+
gl.uniform1i(this.panelUniforms['uOutputTransform'] ?? null, interfaceGrade(this.passGrade()));
|
|
7831
|
+
gl.uniform1f(this.panelUniforms['uOutputExposure'] ?? null, 1);
|
|
7764
7832
|
|
|
7765
7833
|
gl.drawArrays(gl.TRIANGLES, 0, 6);
|
|
7766
7834
|
|
|
@@ -8487,7 +8555,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
8487
8555
|
this.sceneTarget?.begin(this.canvas.width, this.canvas.height);
|
|
8488
8556
|
/* The materials' maps on the scene's framebuffer, cleared and disabled: `reflectionKeeps`. */
|
|
8489
8557
|
this.reflectionTargets?.begin(this.canvas.width, this.canvas.height);
|
|
8490
|
-
|
|
8558
|
+
const clear = this.clearOf(clearColor);
|
|
8559
|
+
gl.clearColor(clear[0], clear[1], clear[2], 1);
|
|
8491
8560
|
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
|
8492
8561
|
}
|
|
8493
8562
|
|
|
@@ -8642,7 +8711,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
8642
8711
|
exposure: this.exposure,
|
|
8643
8712
|
film: this.filmConstants,
|
|
8644
8713
|
}
|
|
8645
|
-
: { transform: 0, exposure: 1 };
|
|
8714
|
+
: { transform: 0, exposure: 1, sceneGraded: this.passGrade() !== 0 };
|
|
8646
8715
|
/*
|
|
8647
8716
|
* Depth of field, on the same ceiling-and-dial split as bloom above: `depthOfField` decides
|
|
8648
8717
|
* how far a defocused point may spread and this frame's `setDepthOfField` decides where the
|
|
@@ -8705,6 +8774,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
8705
8774
|
this.frameEye,
|
|
8706
8775
|
this.canvas.width,
|
|
8707
8776
|
this.canvas.height,
|
|
8777
|
+
this.passGrade(),
|
|
8708
8778
|
);
|
|
8709
8779
|
}
|
|
8710
8780
|
}
|
|
@@ -8902,7 +8972,13 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
8902
8972
|
/* The march finished on the default framebuffer, which is not where the frame is being
|
|
8903
8973
|
drawn. See `resolveReflections`, which is the same two-step for the same reason. */
|
|
8904
8974
|
this.sceneTarget.bind();
|
|
8905
|
-
this.medium.compositeMedium(
|
|
8975
|
+
this.medium.compositeMedium(
|
|
8976
|
+
depthCopy,
|
|
8977
|
+
this.canvas.width,
|
|
8978
|
+
this.canvas.height,
|
|
8979
|
+
this.passGrade(),
|
|
8980
|
+
this.passGradeExposure(),
|
|
8981
|
+
);
|
|
8906
8982
|
}
|
|
8907
8983
|
}
|
|
8908
8984
|
}
|
|
@@ -9381,12 +9457,11 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
9381
9457
|
* exists to keep. With `screenEffects` off there is no resolve at all and the mesh pass
|
|
9382
9458
|
* writes straight to the canvas, so it stays where it always was.
|
|
9383
9459
|
*
|
|
9384
|
-
*
|
|
9385
|
-
*
|
|
9386
|
-
* inconsistency that existed everywhere before this moved.
|
|
9460
|
+
* Every other pass grades through the same gate, `passGrade`, so a frame without a composite
|
|
9461
|
+
* is graded pass by pass to the curve a composite would have applied once.
|
|
9387
9462
|
*/
|
|
9388
|
-
gl.uniform1i(u['uOutputTransform'] ?? null, this.
|
|
9389
|
-
gl.uniform1f(u['uOutputExposure'] ?? null, this.
|
|
9463
|
+
gl.uniform1i(u['uOutputTransform'] ?? null, this.passGrade());
|
|
9464
|
+
gl.uniform1f(u['uOutputExposure'] ?? null, this.passGradeExposure());
|
|
9390
9465
|
gl.uniform3fv(u['uCameraPos'] ?? null, camera.position);
|
|
9391
9466
|
/*
|
|
9392
9467
|
* Solid, every frame, before anything asks otherwise. A GL uniform starts at
|
|
@@ -9875,7 +9950,15 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
9875
9950
|
if (projection === null) return;
|
|
9876
9951
|
const depth = scene.snapshotDepth(true);
|
|
9877
9952
|
mat4.invert(this.skinInverseProjection, projection);
|
|
9878
|
-
skin.spread(
|
|
9953
|
+
skin.spread(
|
|
9954
|
+
scene,
|
|
9955
|
+
depth,
|
|
9956
|
+
this.emptyTexture2D,
|
|
9957
|
+
projection,
|
|
9958
|
+
this.skinInverseProjection,
|
|
9959
|
+
this.passGrade(),
|
|
9960
|
+
this.passGradeExposure(),
|
|
9961
|
+
);
|
|
9879
9962
|
this.useFlatProgram();
|
|
9880
9963
|
}
|
|
9881
9964
|
|
|
@@ -10188,7 +10271,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
10188
10271
|
/* `1` is sRGB alone: the conversion without the curve, which is what dropping the tone map
|
|
10189
10272
|
means rather than dropping the whole transform. `Math.min` rather than a literal, so a
|
|
10190
10273
|
renderer asked for `none` stays at none and one asked for `srgb` is already there. */
|
|
10191
|
-
if (!toneMapped) gl.uniform1i(u['uOutputTransform'] ?? null, Math.min(this.
|
|
10274
|
+
if (!toneMapped) gl.uniform1i(u['uOutputTransform'] ?? null, Math.min(this.passGrade(), 1));
|
|
10192
10275
|
/* Scoped and handed back like every other state here, so a tinted draw cannot leak its
|
|
10193
10276
|
colour onto the next one. See `TranslucentMeshOptions.tint`. */
|
|
10194
10277
|
const tint = options.tint ?? null;
|
|
@@ -10259,7 +10342,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
10259
10342
|
cannot inherit a material from the draw before it. */
|
|
10260
10343
|
if (!lit) gl.uniform1i(u['uLightingEnabled'] ?? null, 1);
|
|
10261
10344
|
if (fog !== this.surfaceFog) gl.uniform1i(u['uFogEnabled'] ?? null, this.surfaceFog);
|
|
10262
|
-
if (!toneMapped) gl.uniform1i(u['uOutputTransform'] ?? null, this.
|
|
10345
|
+
if (!toneMapped) gl.uniform1i(u['uOutputTransform'] ?? null, this.passGrade());
|
|
10263
10346
|
if (own) this.materials.dirty();
|
|
10264
10347
|
}
|
|
10265
10348
|
|
|
@@ -10362,7 +10445,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
10362
10445
|
/*
|
|
10363
10446
|
* **A probe stores radiance, not display pixels.**
|
|
10364
10447
|
*
|
|
10365
|
-
* `
|
|
10448
|
+
* `passGrade()` is the ACES curve and the sRGB encode whenever the frame has no HDR target,
|
|
10366
10449
|
* so every face was baked through the whole output transform and the shader then sampled the
|
|
10367
10450
|
* result as though it were light. Two things came of that and both read as "the reflection is
|
|
10368
10451
|
* dull": the curve pulls everything bright toward 1 *before* it reaches the cube, so no
|
|
@@ -10418,16 +10501,17 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
10418
10501
|
}
|
|
10419
10502
|
} finally {
|
|
10420
10503
|
this.gl.frontFace(this.gl.CCW);
|
|
10421
|
-
this.useFlatProgram();
|
|
10422
|
-
this.gl.uniform1i(this.flatUniforms['uOutputTransform'] ?? null, this.gradeCode());
|
|
10423
|
-
this.gl.uniform1f(this.flatUniforms['uOutputExposure'] ?? null, this.gradeExposure());
|
|
10424
|
-
this.materials.dirty();
|
|
10425
10504
|
/*
|
|
10426
10505
|
* Cleared even if a caller's own draw threw, because leaving this set would keep every
|
|
10427
|
-
* later frame reading the placeholder and quietly reflecting nothing.
|
|
10506
|
+
* later frame reading the placeholder and quietly reflecting nothing. Before the grade is
|
|
10507
|
+
* put back, which asks it.
|
|
10428
10508
|
*/
|
|
10429
10509
|
this.probePassActive = false;
|
|
10430
10510
|
this.probeBounce = false;
|
|
10511
|
+
this.useFlatProgram();
|
|
10512
|
+
this.gl.uniform1i(this.flatUniforms['uOutputTransform'] ?? null, this.passGrade());
|
|
10513
|
+
this.gl.uniform1f(this.flatUniforms['uOutputExposure'] ?? null, this.passGradeExposure());
|
|
10514
|
+
this.materials.dirty();
|
|
10431
10515
|
/* Back to the canvas, or to the frame's own target if one is bound. See `SceneTarget`. */
|
|
10432
10516
|
if (this.sceneTarget !== null && !this.framePresented) this.sceneTarget.bind();
|
|
10433
10517
|
else {
|
|
@@ -10655,6 +10739,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
10655
10739
|
gl.uniformMatrix4fv(u['uModel'] ?? null, false, model);
|
|
10656
10740
|
gl.uniform3fv(u['uCameraPos'] ?? null, camera.position);
|
|
10657
10741
|
gl.uniform1f(u['uStrength'] ?? null, Math.min(shown, 1));
|
|
10742
|
+
gl.uniform1i(u['uOutputTransform'] ?? null, this.passGrade());
|
|
10743
|
+
gl.uniform1f(u['uOutputExposure'] ?? null, this.passGradeExposure());
|
|
10658
10744
|
gl.uniform1f(u['uLength'] ?? null, length);
|
|
10659
10745
|
gl.uniform1f(u['uSpread'] ?? null, spread);
|
|
10660
10746
|
gl.uniform1f(u['uDust'] ?? null, clamp(options.dust ?? 0, 0, 1));
|
|
@@ -10820,6 +10906,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
10820
10906
|
gl.uniform3fv(u['uCameraPos'] ?? null, camera.position);
|
|
10821
10907
|
gl.uniform1f(u['uTime'] ?? null, timeSeconds);
|
|
10822
10908
|
gl.uniform1f(u['uSheen'] ?? null, sheen);
|
|
10909
|
+
gl.uniform1i(u['uOutputTransform'] ?? null, this.passGrade());
|
|
10910
|
+
gl.uniform1f(u['uOutputExposure'] ?? null, this.passGradeExposure());
|
|
10823
10911
|
gl.uniform1f(u['uFilmRoughness'] ?? null, Math.min(1, Math.max(0, options.roughness ?? 0)));
|
|
10824
10912
|
gl.uniform1f(
|
|
10825
10913
|
u['uFilmRoughnessCycles'] ?? null,
|
|
@@ -10875,6 +10963,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
10875
10963
|
gl.uniform1f(u['uMoonPhase'] ?? null, sky.moonPhase);
|
|
10876
10964
|
gl.uniform1f(u['uNightFactor'] ?? null, sky.nightFactor);
|
|
10877
10965
|
gl.uniform2f(u['uCloudOffset'] ?? null, sky.cloudOffsetX, sky.cloudOffsetZ);
|
|
10966
|
+
gl.uniform1i(u['uOutputTransform'] ?? null, this.passGrade());
|
|
10967
|
+
gl.uniform1f(u['uOutputExposure'] ?? null, this.passGradeExposure());
|
|
10878
10968
|
bindAtmosphere(
|
|
10879
10969
|
gl,
|
|
10880
10970
|
u,
|
|
@@ -147,6 +147,9 @@ export class GlSkinScatter {
|
|
|
147
147
|
empty: WebGLTexture,
|
|
148
148
|
projection: ArrayLike<number>,
|
|
149
149
|
inverse: ArrayLike<number>,
|
|
150
|
+
/** The frame's grade, which the second axis applies to the light it adds. */
|
|
151
|
+
outputTransform = 0,
|
|
152
|
+
outputExposure = 1,
|
|
150
153
|
): void {
|
|
151
154
|
const { gl } = this;
|
|
152
155
|
if (this.multisampled !== null) {
|
|
@@ -163,6 +166,8 @@ export class GlSkinScatter {
|
|
|
163
166
|
gl.uniform1i(u['uSkin'] ?? null, SKIN_BLUR_TEXTURE_UNIT);
|
|
164
167
|
gl.uniform1i(u['uDepth'] ?? null, SKIN_BLUR_DEPTH_UNIT);
|
|
165
168
|
gl.uniform1i(u['uAlbedo'] ?? null, SKIN_BLUR_ALBEDO_UNIT);
|
|
169
|
+
gl.uniform1i(u['uOutputTransform'] ?? null, outputTransform);
|
|
170
|
+
gl.uniform1f(u['uOutputExposure'] ?? null, outputExposure);
|
|
166
171
|
gl.activeTexture(gl.TEXTURE0 + SKIN_BLUR_ALBEDO_UNIT);
|
|
167
172
|
gl.bindTexture(gl.TEXTURE_2D, this.albedo ?? empty);
|
|
168
173
|
/* The four terms that carry a depth back to view-space metres: the occlusion blur's own. */
|
|
@@ -41,7 +41,9 @@ const STORAGE = 0x0080;
|
|
|
41
41
|
* can look at, and looking is what a number cannot replace: if the geometry is in the right places
|
|
42
42
|
* at the right sizes the composition is right, and if a cascade boundary shows as a seam the fade
|
|
43
43
|
* is wrong. Wave 4A's own recorded failure — a phantom surface that passed 111,907 parity samples
|
|
44
|
-
* and was found by looking at a picture — is why this exists at all.
|
|
44
|
+
* and was found by looking at a picture — is why this exists at all. **So it is not graded**, alone
|
|
45
|
+
* of the passes that draw into a frame: what it shows are the field's values, which a curve would
|
|
46
|
+
* bend into a picture of something else.
|
|
45
47
|
*
|
|
46
48
|
* **The field it marches is one frame old**, because the renderer composes at `endFrame` and
|
|
47
49
|
* `prepare` runs at `beginFrame`. `PrepareContext.distanceField` says why that is right rather
|
|
@@ -962,7 +962,16 @@ export class GpuDrivenPass implements PassDefinition {
|
|
|
962
962
|
shadowed: true,
|
|
963
963
|
blended: false,
|
|
964
964
|
};
|
|
965
|
-
|
|
965
|
+
/**
|
|
966
|
+
* The frame's grade for the blit, `PassContext.outputTransform` and its exposure, and the code it
|
|
967
|
+
* was last written with, so a frame writes it only when it changes. See `present.wgsl.ts`.
|
|
968
|
+
*/
|
|
969
|
+
private gradeBuffer: GPUBuffer | null = null;
|
|
970
|
+
private readonly gradeStaging = new ArrayBuffer(16);
|
|
971
|
+
private readonly gradeInts = new Int32Array(this.gradeStaging);
|
|
972
|
+
private readonly gradeFloats = new Float32Array(this.gradeStaging);
|
|
973
|
+
private gradeWrittenCode = Number.NaN;
|
|
974
|
+
private gradeWrittenExposure = Number.NaN;
|
|
966
975
|
|
|
967
976
|
constructor(
|
|
968
977
|
scene: StreamingScene,
|
|
@@ -2410,10 +2419,17 @@ export class GpuDrivenPass implements PassDefinition {
|
|
|
2410
2419
|
],
|
|
2411
2420
|
});
|
|
2412
2421
|
|
|
2422
|
+
this.gradeBuffer ??= device.createBuffer({
|
|
2423
|
+
label: 'gpu-driven blit grade',
|
|
2424
|
+
size: this.gradeStaging.byteLength,
|
|
2425
|
+
usage: UNIFORM | COPY_DST,
|
|
2426
|
+
});
|
|
2427
|
+
this.gradeWrittenCode = Number.NaN;
|
|
2413
2428
|
this.blitGroup = device.createBindGroup({
|
|
2414
2429
|
layout: (this.blitPipeline as GPURenderPipeline).getBindGroupLayout(0),
|
|
2415
2430
|
entries: [
|
|
2416
2431
|
{ binding: 0, resource: this.colourView as GPUTextureView },
|
|
2432
|
+
{ binding: 2, resource: { buffer: this.gradeBuffer } },
|
|
2417
2433
|
/* The depth the raster wrote, sampled so the blit can hand it to the frame. */
|
|
2418
2434
|
...(this.presentDepth
|
|
2419
2435
|
? [
|
|
@@ -3429,18 +3445,16 @@ export class GpuDrivenPass implements PassDefinition {
|
|
|
3429
3445
|
/* The same gate as `prepare`: an unanswered or refused pipeline set encodes nothing at all. */
|
|
3430
3446
|
if (!this.usable) return;
|
|
3431
3447
|
if (this.blitPipeline === null || this.blitGroup === null) return;
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
'to grade it, so the picture will be flat. Use a quality profile with a scene target.',
|
|
3443
|
-
);
|
|
3448
|
+
/* The frame's grade, written when it changes: 0 where the resolve grades. See present.wgsl.ts. */
|
|
3449
|
+
const changed =
|
|
3450
|
+
ctx.outputTransform !== this.gradeWrittenCode ||
|
|
3451
|
+
ctx.outputExposure !== this.gradeWrittenExposure;
|
|
3452
|
+
if (changed && this.device !== null && this.gradeBuffer !== null) {
|
|
3453
|
+
this.gradeWrittenCode = ctx.outputTransform;
|
|
3454
|
+
this.gradeWrittenExposure = ctx.outputExposure;
|
|
3455
|
+
this.gradeInts[0] = ctx.outputTransform;
|
|
3456
|
+
this.gradeFloats[1] = ctx.outputExposure;
|
|
3457
|
+
this.device.queue.writeBuffer(this.gradeBuffer, 0, this.gradeStaging);
|
|
3444
3458
|
}
|
|
3445
3459
|
ctx.pass.setPipeline(this.blitPipeline);
|
|
3446
3460
|
ctx.pass.setBindGroup(0, this.blitGroup);
|
|
@@ -3452,6 +3466,8 @@ export class GpuDrivenPass implements PassDefinition {
|
|
|
3452
3466
|
this.scene.detach();
|
|
3453
3467
|
if (device.backend !== 'webgpu') return;
|
|
3454
3468
|
this.disposeTargets();
|
|
3469
|
+
this.gradeBuffer?.destroy();
|
|
3470
|
+
this.gradeBuffer = null;
|
|
3455
3471
|
this.shadowTexture?.destroy();
|
|
3456
3472
|
this.glassTintTexture?.destroy();
|
|
3457
3473
|
this.glassTintNone?.destroy();
|