@driftengine/core 4.13.3 → 4.13.5
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 +52 -0
- package/README.md +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/render/anisotropicLobe.d.ts +5 -0
- package/dist/render/anisotropicLobe.js +13 -0
- package/dist/render/atmosphere.d.ts +79 -1
- package/dist/render/atmosphere.js +66 -19
- package/dist/render/backend/webgl2/renderer.d.ts +34 -2
- package/dist/render/backend/webgl2/renderer.js +44 -12
- 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/clothSetPack.js +1 -0
- package/dist/render/backend/webgpu/clothSolvePack.d.ts +5 -2
- package/dist/render/backend/webgpu/clothSolvePack.js +2 -1
- package/dist/render/backend/webgpu/filmPass.d.ts +1 -1
- package/dist/render/backend/webgpu/gpuClothSet.js +1 -1
- package/dist/render/backend/webgpu/gpuDrivenPass.js +20 -3
- package/dist/render/backend/webgpu/linePass.d.ts +1 -1
- package/dist/render/backend/webgpu/mediumFields.d.ts +17 -0
- package/dist/render/backend/webgpu/mediumFields.js +74 -0
- package/dist/render/backend/webgpu/plumePass.d.ts +3 -3
- package/dist/render/backend/webgpu/postPass.d.ts +7 -2
- package/dist/render/backend/webgpu/renderer.d.ts +13 -3
- package/dist/render/backend/webgpu/renderer.js +86 -150
- package/dist/render/backend/webgpu/scatterPass.d.ts +1 -1
- package/dist/render/backend/webgpu/shaders/clothSolve.wgsl.d.ts +1 -1
- package/dist/render/backend/webgpu/shaders/clothSolve.wgsl.js +7 -5
- package/dist/render/backend/webgpu/waterPass.d.ts +1 -1
- package/dist/render/environmentMixing.d.ts +18 -0
- package/dist/render/environmentMixing.js +40 -0
- package/dist/render/fog.d.ts +43 -1
- package/dist/render/fog.js +87 -22
- package/dist/render/globalMedium.d.ts +112 -10
- package/dist/render/globalMedium.js +96 -1
- package/dist/render/globalMediumPass.d.ts +7 -3
- package/dist/render/globalMediumPass.js +22 -1
- package/dist/render/hairLobes.d.ts +27 -2
- package/dist/render/hairLobes.js +57 -4
- package/dist/render/lightBudget.d.ts +12 -0
- package/dist/render/lightBudget.js +24 -0
- package/dist/render/sceneTarget.d.ts +5 -3
- package/dist/render/sceneTarget.js +14 -8
- package/dist/render/shaders/arcane.d.ts +1 -1
- package/dist/render/shaders/bolt.d.ts +1 -1
- package/dist/render/shaders/caustics.d.ts +1 -1
- package/dist/render/shaders/film.d.ts +1 -1
- package/dist/render/shaders/film.js +4 -1
- package/dist/render/shaders/fire.d.ts +1 -1
- package/dist/render/shaders/flat/anisotropic.d.ts +8 -1
- package/dist/render/shaders/flat/anisotropic.js +25 -3
- package/dist/render/shaders/flat/hair.js +77 -27
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +23 -3
- package/dist/render/shaders/flat/models.js +12 -5
- package/dist/render/shaders/flat/preamble.js +7 -0
- package/dist/render/shaders/fog.d.ts +4 -4
- package/dist/render/shaders/fog.js +81 -32
- package/dist/render/shaders/generated/arcane.wgsl.d.ts +29 -39
- package/dist/render/shaders/generated/arcane.wgsl.js +29 -39
- package/dist/render/shaders/generated/bolt.wgsl.d.ts +26 -36
- package/dist/render/shaders/generated/bolt.wgsl.js +26 -36
- package/dist/render/shaders/generated/caustics.wgsl.d.ts +25 -35
- package/dist/render/shaders/generated/caustics.wgsl.js +25 -35
- package/dist/render/shaders/generated/film.wgsl.d.ts +28 -38
- package/dist/render/shaders/generated/film.wgsl.js +28 -38
- package/dist/render/shaders/generated/fire.wgsl.d.ts +28 -38
- package/dist/render/shaders/generated/fire.wgsl.js +28 -38
- package/dist/render/shaders/generated/flat.wgsl.d.ts +1466 -1546
- package/dist/render/shaders/generated/flat.wgsl.js +1489 -1569
- package/dist/render/shaders/generated/globalMedium.wgsl.d.ts +98 -12
- package/dist/render/shaders/generated/globalMedium.wgsl.js +98 -12
- package/dist/render/shaders/generated/line.wgsl.d.ts +25 -35
- package/dist/render/shaders/generated/line.wgsl.js +25 -35
- package/dist/render/shaders/generated/particle.wgsl.d.ts +100 -140
- package/dist/render/shaders/generated/particle.wgsl.js +100 -140
- package/dist/render/shaders/generated/rush.wgsl.d.ts +20 -10
- package/dist/render/shaders/generated/rush.wgsl.js +20 -10
- package/dist/render/shaders/generated/scatter.wgsl.d.ts +25 -35
- package/dist/render/shaders/generated/scatter.wgsl.js +25 -35
- package/dist/render/shaders/generated/smoke.wgsl.d.ts +28 -38
- package/dist/render/shaders/generated/smoke.wgsl.js +28 -38
- package/dist/render/shaders/generated/water.wgsl.d.ts +29 -39
- package/dist/render/shaders/generated/water.wgsl.js +29 -39
- package/dist/render/shaders/globalMedium.js +114 -10
- package/dist/render/shaders/gpudriven/blendRaster.wgsl.js +2 -1
- package/dist/render/shaders/gpudriven/fog.wgsl.d.ts +10 -4
- package/dist/render/shaders/gpudriven/fog.wgsl.js +77 -19
- package/dist/render/shaders/gpudriven/frameBlock.wgsl.d.ts +1 -1
- package/dist/render/shaders/gpudriven/frameBlock.wgsl.js +12 -1
- package/dist/render/shaders/gpudriven/shade.wgsl.js +3 -1
- package/dist/render/shaders/line.d.ts +1 -1
- package/dist/render/shaders/line.js +4 -2
- package/dist/render/shaders/localExposure.d.ts +42 -1
- package/dist/render/shaders/localExposure.js +73 -6
- package/dist/render/shaders/particle.d.ts +3 -3
- package/dist/render/shaders/particle.js +10 -6
- package/dist/render/shaders/scatter.d.ts +1 -1
- package/dist/render/shaders/scatter.js +4 -2
- package/dist/render/shaders/smoke.d.ts +1 -1
- package/dist/render/shaders/water.d.ts +1 -1
- package/dist/render/shaders/water.js +2 -1
- package/dist/render/surfaceModel.d.ts +23 -4
- package/dist/render/surfaceModel.js +12 -2
- package/package.json +6 -6
- package/src/index.ts +12 -2
- package/src/render/anisotropicLobe.ts +20 -0
- package/src/render/atmosphere.ts +153 -20
- package/src/render/backend/webgl2/renderer.ts +63 -13
- package/src/render/backend/webgpu/clothSetPack.ts +1 -0
- package/src/render/backend/webgpu/clothSolvePack.ts +6 -2
- package/src/render/backend/webgpu/gpuClothSet.ts +1 -1
- package/src/render/backend/webgpu/gpuDrivenPass.ts +20 -3
- package/src/render/backend/webgpu/mediumFields.ts +87 -0
- package/src/render/backend/webgpu/renderer.ts +109 -157
- package/src/render/backend/webgpu/shaders/clothSolve.wgsl.ts +7 -5
- package/src/render/environmentMixing.ts +48 -0
- package/src/render/fog.ts +125 -25
- package/src/render/globalMedium.ts +214 -11
- package/src/render/globalMediumPass.ts +29 -4
- package/src/render/hairLobes.ts +109 -3
- package/src/render/lightBudget.ts +32 -0
- package/src/render/sceneTarget.ts +15 -11
- package/src/render/shaders/film.ts +4 -1
- package/src/render/shaders/flat/anisotropic.ts +25 -3
- package/src/render/shaders/flat/hair.ts +77 -27
- package/src/render/shaders/flat/main.ts +23 -3
- package/src/render/shaders/flat/models.ts +12 -5
- package/src/render/shaders/flat/preamble.ts +7 -0
- package/src/render/shaders/fog.ts +81 -32
- package/src/render/shaders/generated/arcane.wgsl.ts +29 -39
- package/src/render/shaders/generated/bolt.wgsl.ts +26 -36
- package/src/render/shaders/generated/caustics.wgsl.ts +25 -35
- package/src/render/shaders/generated/film.wgsl.ts +28 -38
- package/src/render/shaders/generated/fire.wgsl.ts +28 -38
- package/src/render/shaders/generated/flat.wgsl.ts +1489 -1569
- package/src/render/shaders/generated/globalMedium.wgsl.ts +98 -12
- package/src/render/shaders/generated/line.wgsl.ts +25 -35
- package/src/render/shaders/generated/particle.wgsl.ts +100 -140
- package/src/render/shaders/generated/rush.wgsl.ts +20 -10
- package/src/render/shaders/generated/scatter.wgsl.ts +25 -35
- package/src/render/shaders/generated/smoke.wgsl.ts +28 -38
- package/src/render/shaders/generated/water.wgsl.ts +29 -39
- package/src/render/shaders/globalMedium.ts +114 -10
- package/src/render/shaders/gpudriven/blendRaster.wgsl.ts +2 -1
- package/src/render/shaders/gpudriven/fog.wgsl.ts +78 -19
- package/src/render/shaders/gpudriven/frameBlock.wgsl.ts +12 -1
- package/src/render/shaders/gpudriven/shade.wgsl.ts +3 -1
- package/src/render/shaders/line.ts +4 -2
- package/src/render/shaders/localExposure.ts +115 -6
- package/src/render/shaders/particle.ts +10 -6
- package/src/render/shaders/scatter.ts +4 -2
- package/src/render/shaders/water.ts +2 -1
- package/src/render/surfaceModel.ts +41 -6
package/src/render/hairLobes.ts
CHANGED
|
@@ -50,6 +50,17 @@ export const HAIR_MIN_BETA = 0.05;
|
|
|
50
50
|
*/
|
|
51
51
|
export const HAIR_SCATTER_SPHERE = 1.33;
|
|
52
52
|
|
|
53
|
+
/**
|
|
54
|
+
* How far every lobe is widened for light from every side, in radians: the angle a source would
|
|
55
|
+
* subtend to widen them as much, `sourceRadius / 2·dist` in `hairLight`'s terms.
|
|
56
|
+
*
|
|
57
|
+
* **0.2, the real-time form's own value**, which stands one direction in for the sphere: a lobe as
|
|
58
|
+
* narrow as a lamp's would put the whole sky's light into one highlight wherever the eye's
|
|
59
|
+
* direction happens to meet it. What it gives up is the sky's shape: a sun low in a clear sky and
|
|
60
|
+
* an even overcast light hair through the same widened lobes.
|
|
61
|
+
*/
|
|
62
|
+
export const HAIR_AREA = 0.2;
|
|
63
|
+
|
|
53
64
|
/** How much the scatter term leans toward the strand's own direction rather than the pseudo-normal. */
|
|
54
65
|
const KAJIYA_SHARE = 0.33;
|
|
55
66
|
|
|
@@ -80,7 +91,9 @@ export const HAIR_OUT = { r: 0, tt: 1, trt: 4, scatter: 7, length: 10 } as const
|
|
|
80
91
|
/**
|
|
81
92
|
* One light on a strand, in lamp units and unshadowed, into `out` (`HAIR_OUT`'s layout: R is grey,
|
|
82
93
|
* the rest colours). `tangent`, `toLight` and `toEye` are unit vectors but for a tangent that could
|
|
83
|
-
* not be made one, which comes in as zeros; `fibre` is the base colour
|
|
94
|
+
* not be made one, which comes in as zeros; `fibre` is the base colour; `specular` is the strand's,
|
|
95
|
+
* the model map's red, which scales the reflected lobe by twice it, so 0.5 draws the lobe as the
|
|
96
|
+
* fit gives it and 0 is a strand that reflects nothing off its surface.
|
|
84
97
|
*
|
|
85
98
|
* **A lamp with a size widens every lobe by the angle it subtends**, `sourceRadius / 2·dist` as
|
|
86
99
|
* `sphereLobe` takes it, and the lobes are normalised Gaussians, so a wider one carries the same
|
|
@@ -93,11 +106,103 @@ export function hairLight(
|
|
|
93
106
|
fibre: Vec3,
|
|
94
107
|
roughness: number,
|
|
95
108
|
shift: number,
|
|
109
|
+
specular: number,
|
|
96
110
|
scatter: number,
|
|
97
111
|
backlit: number,
|
|
98
112
|
sourceRadius: number,
|
|
99
113
|
dist: number,
|
|
100
114
|
out: Float64Array,
|
|
115
|
+
): void {
|
|
116
|
+
const grow = sourceRadius / Math.max(2 * dist, 1e-3);
|
|
117
|
+
hairLobes(
|
|
118
|
+
tangent,
|
|
119
|
+
toLight,
|
|
120
|
+
toEye,
|
|
121
|
+
fibre,
|
|
122
|
+
roughness,
|
|
123
|
+
shift,
|
|
124
|
+
specular,
|
|
125
|
+
scatter,
|
|
126
|
+
backlit,
|
|
127
|
+
grow,
|
|
128
|
+
out,
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** The flattened eye `hairIndirect` lights a strand along, as the shader finds it. */
|
|
133
|
+
const flattened = new Float64Array(3);
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* The share of light from every side a strand returns, a colour into `out`, with the one
|
|
137
|
+
* direction's lobes it is made of into `lobes` (`HAIR_OUT`'s layout). `normal` is the surface's,
|
|
138
|
+
* which stands in for the flattened eye where the eye runs along the strand and there is none.
|
|
139
|
+
*
|
|
140
|
+
* **One direction for the sphere**: the eye flattened across the strand, `V − T(V·T)`, unshadowed,
|
|
141
|
+
* with no light from behind and so no transmitted lobe, every lobe widened by `HAIR_AREA`. Twice
|
|
142
|
+
* the sum, and at most all of what arrives. **Why twice**: the scatter's half is then
|
|
143
|
+
* `HAIR_SCATTER_SPHERE`, its own answer to an even sky — along the flattened eye its shape is
|
|
144
|
+
* `mix(1/2, 1, 0.33)`, 0.665, which doubled is that constant's 1.33 — so the lobes ride on a
|
|
145
|
+
* scatter the sphere's quadrature already holds to, and R and TRT are what the direction adds.
|
|
146
|
+
*/
|
|
147
|
+
export function hairIndirect(
|
|
148
|
+
tangent: ArrayLike<number>,
|
|
149
|
+
toEye: ArrayLike<number>,
|
|
150
|
+
normal: ArrayLike<number>,
|
|
151
|
+
fibre: Vec3,
|
|
152
|
+
roughness: number,
|
|
153
|
+
shift: number,
|
|
154
|
+
specular: number,
|
|
155
|
+
scatter: number,
|
|
156
|
+
lobes: Float64Array,
|
|
157
|
+
out: Float64Array,
|
|
158
|
+
): void {
|
|
159
|
+
const along = dot(tangent, toEye);
|
|
160
|
+
let length2 = 0;
|
|
161
|
+
for (let c = 0; c < 3; c++) {
|
|
162
|
+
const across = (toEye[c] as number) - (tangent[c] as number) * along;
|
|
163
|
+
flattened[c] = across;
|
|
164
|
+
length2 += across * across;
|
|
165
|
+
}
|
|
166
|
+
for (let c = 0; c < 3; c++) {
|
|
167
|
+
flattened[c] =
|
|
168
|
+
length2 > 1e-8 ? (flattened[c] as number) / Math.sqrt(length2) : (normal[c] as number);
|
|
169
|
+
}
|
|
170
|
+
hairLobes(
|
|
171
|
+
tangent,
|
|
172
|
+
flattened,
|
|
173
|
+
toEye,
|
|
174
|
+
fibre,
|
|
175
|
+
roughness,
|
|
176
|
+
shift,
|
|
177
|
+
specular,
|
|
178
|
+
scatter,
|
|
179
|
+
0,
|
|
180
|
+
HAIR_AREA,
|
|
181
|
+
lobes,
|
|
182
|
+
);
|
|
183
|
+
for (let c = 0; c < 3; c++) {
|
|
184
|
+
const sum =
|
|
185
|
+
(lobes[HAIR_OUT.r] as number) +
|
|
186
|
+
(lobes[HAIR_OUT.tt + c] as number) +
|
|
187
|
+
(lobes[HAIR_OUT.trt + c] as number) +
|
|
188
|
+
(lobes[HAIR_OUT.scatter + c] as number);
|
|
189
|
+
out[c] = Math.min(1, 2 * sum);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/** `hairLight`'s arithmetic, every lobe widened by `grow` radians. */
|
|
194
|
+
function hairLobes(
|
|
195
|
+
tangent: ArrayLike<number>,
|
|
196
|
+
toLight: ArrayLike<number>,
|
|
197
|
+
toEye: ArrayLike<number>,
|
|
198
|
+
fibre: Vec3,
|
|
199
|
+
roughness: number,
|
|
200
|
+
shift: number,
|
|
201
|
+
specular: number,
|
|
202
|
+
scatter: number,
|
|
203
|
+
backlit: number,
|
|
204
|
+
grow: number,
|
|
205
|
+
out: Float64Array,
|
|
101
206
|
): void {
|
|
102
207
|
const sinI = clamp(dot(tangent, toLight), -1, 1);
|
|
103
208
|
const sinO = clamp(dot(tangent, toEye), -1, 1);
|
|
@@ -124,14 +229,15 @@ export function hairLight(
|
|
|
124
229
|
const cosPhi = lpvp / Math.sqrt(lp2 * vp2 + 1e-4);
|
|
125
230
|
const cosHalfPhi = Math.sqrt(clamp(0.5 + 0.5 * cosPhi, 0, 1));
|
|
126
231
|
const beta = Math.max(roughness * roughness, HAIR_MIN_BETA);
|
|
127
|
-
const grow = sourceRadius / Math.max(2 * dist, 1e-3);
|
|
128
232
|
const x = sinI + sinO;
|
|
129
233
|
|
|
130
234
|
const r =
|
|
131
235
|
hairLongitudinal(beta + grow, x + 2 * shift) *
|
|
132
236
|
0.25 *
|
|
133
237
|
cosHalfPhi *
|
|
134
|
-
hairFresnel(Math.sqrt(clamp(0.5 + 0.5 * dot(toLight, toEye), 0, 1)))
|
|
238
|
+
hairFresnel(Math.sqrt(clamp(0.5 + 0.5 * dot(toLight, toEye), 0, 1))) *
|
|
239
|
+
2 *
|
|
240
|
+
specular;
|
|
135
241
|
|
|
136
242
|
const a = 1 / (1.19 / cosD + 0.36 * cosD);
|
|
137
243
|
const h = cosHalfPhi * (1 + a * (0.6 - 0.8 * cosPhi));
|
|
@@ -427,6 +427,13 @@ export interface PointLightSet {
|
|
|
427
427
|
* falls off as it did. See `PointLightSource.falloffExponent`.
|
|
428
428
|
*/
|
|
429
429
|
readonly lightFalloffExponents?: Float32Array;
|
|
430
|
+
/**
|
|
431
|
+
* One per light: how strongly it scatters in the global medium, as a share of how strongly it
|
|
432
|
+
* lights a surface, beside `setGlobalMedium`'s `lampScatter` for every lamp at once. **Absent, or
|
|
433
|
+
* a short array, is 1 for every light.** A lamp turned up here lights a fog bank brighter than it
|
|
434
|
+
* lights the floor in it, which is how a lamp authored for its shaft rather than its pool reads.
|
|
435
|
+
*/
|
|
436
|
+
readonly lightMediumScatter?: Float32Array;
|
|
430
437
|
}
|
|
431
438
|
|
|
432
439
|
/** Weights are optional per consumer; a short array means every light is fully present. */
|
|
@@ -637,6 +644,31 @@ export interface ResolvedPointLights {
|
|
|
637
644
|
readonly heldWeights: Float32Array;
|
|
638
645
|
}
|
|
639
646
|
|
|
647
|
+
/**
|
|
648
|
+
* A target for `resolvePointLights`, made once outside the frame loop. Its arrays are references
|
|
649
|
+
* the resolve points at the caller's own or at the defaults, but for the four it owns.
|
|
650
|
+
*/
|
|
651
|
+
export function createResolvedPointLights(): ResolvedPointLights {
|
|
652
|
+
return {
|
|
653
|
+
count: 0,
|
|
654
|
+
falloff: 0,
|
|
655
|
+
positions: NO_POSITIONS,
|
|
656
|
+
colors: NO_COLORS,
|
|
657
|
+
radii: POINT_SOURCES,
|
|
658
|
+
sourceRadii: POINT_SOURCES,
|
|
659
|
+
weights: FULLY_PRESENT,
|
|
660
|
+
directions: NO_DIRECTIONS,
|
|
661
|
+
coneCos: OPEN_CONES,
|
|
662
|
+
iesProfiles: NO_PROFILES,
|
|
663
|
+
iesAxes: NO_IES_AXES,
|
|
664
|
+
cookies: NO_COOKIES,
|
|
665
|
+
axesAndChannels: new Float32Array(MAX_POINT_LIGHTS * 4),
|
|
666
|
+
conesAndFalloffs: new Float32Array(MAX_POINT_LIGHTS * 4),
|
|
667
|
+
heldSourceRadii: new Float32Array(MAX_POINT_LIGHTS),
|
|
668
|
+
heldWeights: new Float32Array(MAX_POINT_LIGHTS),
|
|
669
|
+
};
|
|
670
|
+
}
|
|
671
|
+
|
|
640
672
|
/** One per GL binder, refilled per call: holds references, allocates nothing. */
|
|
641
673
|
const glLights: ResolvedPointLights = {
|
|
642
674
|
count: 0,
|
|
@@ -4,7 +4,8 @@ import { FRINGE_FLOATS, resolveFringe } from './shaders/fringe.ts';
|
|
|
4
4
|
import { AmbientOcclusionPass } from './ambientOcclusionPass.ts';
|
|
5
5
|
import { ExposurePass } from './exposurePass.ts';
|
|
6
6
|
import { clampAutoExposure } from './shaders/exposure.ts';
|
|
7
|
-
import {
|
|
7
|
+
import { LOCAL_EXPOSURE_FLOATS, resolveLocalExposure } from './shaders/localExposure.ts';
|
|
8
|
+
import type { LocalExposureOptions } from './shaders/localExposure.ts';
|
|
8
9
|
import { BloomPass } from './bloomPass.ts';
|
|
9
10
|
import {
|
|
10
11
|
type ColourGradeLut,
|
|
@@ -107,8 +108,9 @@ export class SceneTarget {
|
|
|
107
108
|
/** Eye adaptation: how far, and this frame's time step. See `Renderer.setAutoExposure`. */
|
|
108
109
|
private autoExposure = 0;
|
|
109
110
|
private autoExposureDt = 0;
|
|
110
|
-
/**
|
|
111
|
-
private localExposure =
|
|
111
|
+
/** Whether regions move, and how, as `resolveLocalExposure` packs it. See `setLocalExposure`. */
|
|
112
|
+
private localExposure = false;
|
|
113
|
+
private readonly localExposureShape = new Float32Array(LOCAL_EXPOSURE_FLOATS);
|
|
112
114
|
private exposure: ExposurePass | null = null;
|
|
113
115
|
private width = 0;
|
|
114
116
|
private height = 0;
|
|
@@ -722,9 +724,9 @@ export class SceneTarget {
|
|
|
722
724
|
this.autoExposureDt = dtSec;
|
|
723
725
|
}
|
|
724
726
|
|
|
725
|
-
/** How
|
|
726
|
-
setLocalExposure(
|
|
727
|
-
this.localExposure =
|
|
727
|
+
/** How regions are brought toward the frame. See `Renderer.setLocalExposure`. */
|
|
728
|
+
setLocalExposure(setting: number | LocalExposureOptions): void {
|
|
729
|
+
this.localExposure = resolveLocalExposure(setting, this.localExposureShape);
|
|
728
730
|
}
|
|
729
731
|
|
|
730
732
|
/** A new shot: the eye meters it afresh rather than easing in from the last one. */
|
|
@@ -1011,7 +1013,7 @@ export class SceneTarget {
|
|
|
1011
1013
|
* reason: the composite consumes it. Off at zero strength, where nothing is allocated or drawn.
|
|
1012
1014
|
*/
|
|
1013
1015
|
let heldExposure: WebGLTexture | null = null;
|
|
1014
|
-
const local = this.localExposure
|
|
1016
|
+
const local = this.localExposure;
|
|
1015
1017
|
if ((this.autoExposure > 0 || local) && scene !== null) {
|
|
1016
1018
|
this.exposure ??= new ExposurePass(gl);
|
|
1017
1019
|
heldExposure = this.exposure.run(scene, this.autoExposureDt, local);
|
|
@@ -1136,10 +1138,12 @@ export class SceneTarget {
|
|
|
1136
1138
|
gl.activeTexture(gl.TEXTURE6);
|
|
1137
1139
|
gl.bindTexture(gl.TEXTURE_2D, localGrid ?? scene);
|
|
1138
1140
|
gl.uniform1i(this.uniforms['uExposureLocal'] ?? null, 6);
|
|
1139
|
-
gl.uniform1f(
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1141
|
+
gl.uniform1f(this.uniforms['uLocalExposure'] ?? null, localGrid === null ? 0 : 1);
|
|
1142
|
+
/* Its shape only where it is on: off, the shader returns before reading it. */
|
|
1143
|
+
if (localGrid !== null) {
|
|
1144
|
+
gl.uniform4fv(this.uniforms['uLocalShape'] ?? null, this.localExposureShape, 0, 4);
|
|
1145
|
+
gl.uniform4fv(this.uniforms['uLocalPivot'] ?? null, this.localExposureShape, 4, 4);
|
|
1146
|
+
}
|
|
1143
1147
|
/* Each object's own motion is WebGPU's under a reconstruction; here the scene stands in on
|
|
1144
1148
|
unit 7 and the flag says not to read it, so the camera's motion alone blurs. */
|
|
1145
1149
|
gl.activeTexture(gl.TEXTURE7);
|
|
@@ -211,8 +211,11 @@ void main() {
|
|
|
211
211
|
// it was not: this ran its own curve while claiming otherwise, so a slick receded
|
|
212
212
|
// at a different rate than the deck it was lying on.
|
|
213
213
|
float fog = mediumFog(length(uCameraPos - vWorld), vWorld.y);
|
|
214
|
+
vec3 glow = mediumGlow(length(uCameraPos - vWorld), vWorld.y, normalize(vWorld - uCameraPos));
|
|
215
|
+
/* The air the mirror recedes into takes no glow: the mirror is mixed toward it by the fog, which
|
|
216
|
+
would lay the glow by the fog's share a second time over the surface's own. */
|
|
214
217
|
vec3 gradedAir = applyOutputTransform(mediumColor());
|
|
215
|
-
vec3 graded = applyOutputTransform(mix(color, mediumColor(), fog));
|
|
218
|
+
vec3 graded = applyOutputTransform(mix(color, mediumColor(), fog) + glow);
|
|
216
219
|
|
|
217
220
|
/*
|
|
218
221
|
* The mirrored scene, under the sheen rather than instead of it.
|
|
@@ -11,20 +11,30 @@
|
|
|
11
11
|
*
|
|
12
12
|
* Lamps widen both of the lobe's widths by the source's angle, as `sphereLobe` widens the standard
|
|
13
13
|
* lobe, with the energy kept; at strength 0 that is `sphereLobe` exactly.
|
|
14
|
+
*
|
|
15
|
+
* **A lift**, where the model asks for one: the distribution raised where it is bright, `D +
|
|
16
|
+
* min(3, D^1.5) · k`, which is how a brushed finish that reads as polished in its streak is
|
|
17
|
+
* authored. `k` is the model's `lift`, times the model map's alpha where one is bound, so it can
|
|
18
|
+
* vary texel by texel. Applied as a factor of the distribution at the half vector, so the
|
|
19
|
+
* peak-normalised lobe and the physical one lift alike. **What it gives up** is energy: a lifted
|
|
20
|
+
* lobe returns more than arrives, which is the look it is for.
|
|
14
21
|
*/
|
|
15
22
|
export const ANISOTROPIC_GLSL = /* glsl */ `
|
|
16
|
-
/* The stretch's frame and
|
|
23
|
+
/* The stretch's frame, strength and lift, gathered once with the surface. */
|
|
17
24
|
vec3 aAlong;
|
|
18
25
|
vec3 aAcross;
|
|
19
26
|
float aStrength;
|
|
27
|
+
float aLift;
|
|
20
28
|
|
|
21
29
|
void anisotropicSurface() {
|
|
22
30
|
vec2 direction = vec2(1.0, 0.0);
|
|
23
31
|
float strength = uModelParams[0].x;
|
|
32
|
+
aLift = uModelParams[0].w;
|
|
24
33
|
if (uModelParams[1].w > 0.0) {
|
|
25
34
|
vec2 mapped = mMap.rg * 2.0 - 1.0;
|
|
26
35
|
direction = dot(mapped, mapped) > 1e-8 ? normalize(mapped) : direction;
|
|
27
36
|
strength *= mMap.b;
|
|
37
|
+
aLift *= mMap.a;
|
|
28
38
|
}
|
|
29
39
|
vec2 turned = vec2(
|
|
30
40
|
direction.x * uModelParams[0].y - direction.y * uModelParams[0].z,
|
|
@@ -44,6 +54,17 @@ void anisotropicSurface() {
|
|
|
44
54
|
aStrength = strength;
|
|
45
55
|
}
|
|
46
56
|
|
|
57
|
+
/**
|
|
58
|
+
* How much the lift brightens the lobe at a half vector, from the stretched distribution there, D,
|
|
59
|
+
* at widths \`along\` and \`across\` and \`d\` its denominator's root: \`(D + min(3, D^1.5) k) / D\`.
|
|
60
|
+
* anisotropicLiftFactor in anisotropicLobe.ts.
|
|
61
|
+
*/
|
|
62
|
+
float anisotropicLift(float along, float across, float d) {
|
|
63
|
+
if (aLift <= 0.0) return 1.0;
|
|
64
|
+
float distribution = 1.0 / max(3.14159265 * along * across * d * d, 1e-8);
|
|
65
|
+
return 1.0 + aLift * min(3.0 / distribution, sqrt(distribution));
|
|
66
|
+
}
|
|
67
|
+
|
|
47
68
|
/** The stretched lobe at a half vector, 1 at the mirror direction: anisotropicLobe.ts. */
|
|
48
69
|
float anisotropicLobe(vec3 h, float sourceRadius, float dist) {
|
|
49
70
|
float alpha = max(mRoughness * mRoughness, MIN_LOBE_ALPHA);
|
|
@@ -56,7 +77,7 @@ float anisotropicLobe(vec3 h, float sourceRadius, float dist) {
|
|
|
56
77
|
float y = dot(aAcross, h) / acrossWide;
|
|
57
78
|
float z = dot(mNormal, h);
|
|
58
79
|
float d = x * x + y * y + z * z;
|
|
59
|
-
return energy / max(d * d, 1e-8);
|
|
80
|
+
return energy / max(d * d, 1e-8) * anisotropicLift(alongWide, acrossWide, d);
|
|
60
81
|
}
|
|
61
82
|
|
|
62
83
|
/**
|
|
@@ -74,7 +95,8 @@ float anisotropicPhysical(vec3 l, vec3 h, float ndl, float sourceRadius, float d
|
|
|
74
95
|
float y = dot(aAcross, h) / across;
|
|
75
96
|
float z = dot(mNormal, h);
|
|
76
97
|
float d = x * x + y * y + z * z;
|
|
77
|
-
float distribution =
|
|
98
|
+
float distribution =
|
|
99
|
+
1.0 / max(3.14159265 * along * across * d * d, 1e-8) * anisotropicLift(along, across, d);
|
|
78
100
|
float ndv = max(dot(mNormal, mToEye), 1e-4);
|
|
79
101
|
float viewed = ndl * length(vec3(along * dot(aAlong, mToEye), across * dot(aAcross, mToEye), ndv));
|
|
80
102
|
float lit = ndv * length(vec3(along * dot(aAlong, l), across * dot(aAcross, l), ndl));
|
|
@@ -3,30 +3,47 @@
|
|
|
3
3
|
* their tilted peaks, the transmitted lobe to the backlight, the sum to what a white strand
|
|
4
4
|
* receives, and every term to a finite answer where the geometry degenerates.
|
|
5
5
|
*
|
|
6
|
-
* **The strand is the tangent, root to tip
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
6
|
+
* **The strand is the tangent, root to tip**, unless the model map says otherwise. Its red is the
|
|
7
|
+
* strand's specular, which scales the reflected lobe by twice it, so 0.5 is the lobe as drawn and 0
|
|
8
|
+
* a root that reflects nothing; its green turns each strand's tilt by up to half of it either way
|
|
9
|
+
* (0.5 is none); and its blue is occlusion, which light from every side takes and the lamps do not
|
|
10
|
+
* — their shadows are theirs to cast. A colour from root to tip is the albedo map's to carry.
|
|
10
11
|
*
|
|
11
|
-
* **
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
12
|
+
* **A strand's direction, texel by texel**, where the model has a \`directionOffset\`: that many
|
|
13
|
+
* layers past the one the other channels are read at, a direction in the surface's tangent frame —
|
|
14
|
+
* red along the tangent, green along the bitangent and blue out of the surface along its normal,
|
|
15
|
+
* each −1 to 1 as a normal map packs them — which the strand runs along instead of the tangent. So
|
|
16
|
+
* a card's strands can lean out of its plane, each its own way, as the strands it stands for do.
|
|
17
|
+
* **What it gives up** is the frame's own precision: eight bits a component turns a strand in
|
|
18
|
+
* steps of about half a degree, which the lobes, a few degrees wide, do not resolve.
|
|
19
|
+
*
|
|
20
|
+
* **Light from every side** — the sky, the probes, DriftLight — goes through the lobes rather than
|
|
21
|
+
* past them: they are evaluated once, for a light along the eye flattened across the strand, every
|
|
22
|
+
* lobe widened by \`HAIR_AREA\` as a source that large would widen it, and their sum taken as the
|
|
23
|
+
* share of that light the strand returns (\`hairIndirect\`). That is how a real-time hair model
|
|
24
|
+
* stands one direction in for the sphere: a strand returns most toward the eye what arrives from
|
|
25
|
+
* the eye's side of it, and the R and TRT lobes are what make a fibre gold rather than a wash of
|
|
26
|
+
* its scatter. The environment's reflection leaves hair out, because R already took its share.
|
|
27
|
+
*
|
|
28
|
+
* **What it gives up**: light from behind. The direction stands for the eye's side only, so there
|
|
29
|
+
* is no transmitted lobe in it, and a head of hair lit from behind by an open sky shows no rim
|
|
30
|
+
* from the sky; the lamps carry one. And the share is one direction's, not the sphere's integral,
|
|
31
|
+
* so it moves with the eye as the lobes do rather than holding still as an average would. **What
|
|
32
|
+
* would make it wrong** is hair seen mostly against its light, a head against a bright window,
|
|
33
|
+
* whose sky arrives through the strands rather than off them and wants the transmitted lobe back.
|
|
16
34
|
*/
|
|
17
|
-
import { HAIR_F0, HAIR_MIN_BETA
|
|
35
|
+
import { HAIR_AREA, HAIR_F0, HAIR_MIN_BETA } from '../../hairLobes.ts';
|
|
18
36
|
|
|
19
37
|
export const HAIR_GLSL = /* glsl */ `
|
|
20
38
|
const float HAIR_F0 = ${HAIR_F0.toFixed(9)};
|
|
21
39
|
const float HAIR_MIN_BETA = ${HAIR_MIN_BETA.toFixed(3)};
|
|
22
|
-
const float
|
|
40
|
+
const float HAIR_AREA = ${HAIR_AREA.toFixed(3)};
|
|
23
41
|
|
|
24
|
-
/* This texel's tilt,
|
|
42
|
+
/* This texel's tilt, its specular, and the share of light from every side it returns: gathered
|
|
43
|
+
with the surface. */
|
|
25
44
|
float hShift;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
hShift = uModelParams[0].x * (0.5 + mMap.g);
|
|
29
|
-
}
|
|
45
|
+
float hSpecular;
|
|
46
|
+
vec3 hAround;
|
|
30
47
|
|
|
31
48
|
/** A longitudinal lobe: a normalised Gaussian of width \`beta\` at \`x\` from its centre. */
|
|
32
49
|
float hairLongitudinal(float beta, float x) {
|
|
@@ -38,14 +55,18 @@ float hairFresnel(float cosine) {
|
|
|
38
55
|
return HAIR_F0 + (1.0 - HAIR_F0) * c * c * c * c * c;
|
|
39
56
|
}
|
|
40
57
|
|
|
41
|
-
/** The normal turned toward the eye across the strand: the scatter's and
|
|
58
|
+
/** The normal turned toward the eye across the strand: the scatter's, and light from every side's. */
|
|
42
59
|
vec3 hairAcross() {
|
|
43
60
|
vec3 across = mToEye - mTangent * dot(mTangent, mToEye);
|
|
44
61
|
float length2 = dot(across, across);
|
|
45
62
|
return length2 > 1e-8 ? across * inversesqrt(length2) : mNormal;
|
|
46
63
|
}
|
|
47
64
|
|
|
48
|
-
|
|
65
|
+
/**
|
|
66
|
+
* The lobes for light arriving along \`l\`, into \`mSpecular\` and \`mDiffuse\`: every lobe widened by
|
|
67
|
+
* \`grow\` radians, and the transmitted one scaled by \`backlit\`.
|
|
68
|
+
*/
|
|
69
|
+
void hairLobes(vec3 l, float grow, float backlit) {
|
|
49
70
|
vec3 t = mTangent;
|
|
50
71
|
float sinI = clamp(dot(t, l), -1.0, 1.0);
|
|
51
72
|
float sinO = clamp(dot(t, mToEye), -1.0, 1.0);
|
|
@@ -56,17 +77,16 @@ void hairLight(vec3 l, float sourceRadius, float dist) {
|
|
|
56
77
|
float cosPhi = dot(lp, vp) * inversesqrt(dot(lp, lp) * dot(vp, vp) + 1e-4);
|
|
57
78
|
float cosHalfPhi = sqrt(clamp(0.5 + 0.5 * cosPhi, 0.0, 1.0));
|
|
58
79
|
float beta = max(mRoughness * mRoughness, HAIR_MIN_BETA);
|
|
59
|
-
float grow = sourceRadius / max(2.0 * dist, 1e-3);
|
|
60
80
|
float x = sinI + sinO;
|
|
61
81
|
|
|
62
82
|
float r = hairLongitudinal(beta + grow, x + 2.0 * hShift) * 0.25 * cosHalfPhi
|
|
63
|
-
* hairFresnel(sqrt(clamp(0.5 + 0.5 * dot(l, mToEye), 0.0, 1.0)));
|
|
83
|
+
* hairFresnel(sqrt(clamp(0.5 + 0.5 * dot(l, mToEye), 0.0, 1.0))) * hSpecular;
|
|
64
84
|
|
|
65
85
|
float a = 1.0 / (1.19 / cosD + 0.36 * cosD);
|
|
66
86
|
float h = cosHalfPhi * (1.0 + a * (0.6 - 0.8 * cosPhi));
|
|
67
87
|
float fTT = hairFresnel(cosD * sqrt(clamp(1.0 - h * h, 0.0, 1.0)));
|
|
68
88
|
float tt = hairLongitudinal(0.5 * beta + grow, x - hShift) * exp(-3.65 * cosPhi - 3.98)
|
|
69
|
-
* (1.0 - fTT) * (1.0 - fTT) *
|
|
89
|
+
* (1.0 - fTT) * (1.0 - fTT) * backlit;
|
|
70
90
|
float ttPower = 0.5 * sqrt(clamp(1.0 - h * h * a * a, 0.0, 1.0)) / cosD;
|
|
71
91
|
|
|
72
92
|
float fTRT = hairFresnel(0.5 * cosD);
|
|
@@ -81,6 +101,11 @@ void hairLight(vec3 l, float sourceRadius, float dist) {
|
|
|
81
101
|
mDiffuse = sqrt(mAlbedo) * uModelParams[0].y * mix(wrapped, 1.0 - abs(sinI), 0.33);
|
|
82
102
|
}
|
|
83
103
|
|
|
104
|
+
/** A lamp, or the sun at no size: widened by the angle it subtends, as \`sphereLobe\` takes it. */
|
|
105
|
+
void hairLight(vec3 l, float sourceRadius, float dist) {
|
|
106
|
+
hairLobes(l, sourceRadius / max(2.0 * dist, 1e-3), uModelParams[0].z);
|
|
107
|
+
}
|
|
108
|
+
|
|
84
109
|
/**
|
|
85
110
|
* A shadow on the scatter: light that reaches a strand in another's shadow has passed through
|
|
86
111
|
* hair to get there, so a penumbra deepens toward the fibre's own hue.
|
|
@@ -90,13 +115,38 @@ vec3 hairShade(float shadow) {
|
|
|
90
115
|
return shadow * pow(max(hue, vec3(1e-4)), vec3(1.0 - shadow));
|
|
91
116
|
}
|
|
92
117
|
|
|
93
|
-
/**
|
|
94
|
-
|
|
95
|
-
|
|
118
|
+
/**
|
|
119
|
+
* The share of light from every side a strand returns: the lobes for a light along the eye
|
|
120
|
+
* flattened across the strand, unshadowed, with nothing from behind and every lobe widened by
|
|
121
|
+
* \`HAIR_AREA\`, twice their sum and at most all of it. Twice, in this engine's units, because the
|
|
122
|
+
* lobes are per unit irradiance and the scatter's half is then exactly \`HAIR_SCATTER_SPHERE\`, its
|
|
123
|
+
* own average over the sphere: \`hairLobes.ts\` says why, and holds the arithmetic to it.
|
|
124
|
+
*/
|
|
125
|
+
vec3 hairIndirect() {
|
|
126
|
+
hairLobes(hairAcross(), HAIR_AREA, 0.0);
|
|
127
|
+
return min(vec3(1.0), 2.0 * (mSpecular + mDiffuse));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* The strand at this texel: its tilt and specular from the model map, its direction from the layer
|
|
132
|
+
* \`directionOffset\` names where there is one, and then its share of light from every side, which
|
|
133
|
+
* reads all three. On a branch of uniforms alone, so the read's derivatives are in uniform flow; a
|
|
134
|
+
* direction that decodes to nothing keeps the tangent.
|
|
135
|
+
*/
|
|
136
|
+
void hairSurface(vec3 at) {
|
|
137
|
+
hShift = uModelParams[0].x * (0.5 + mMap.g);
|
|
138
|
+
hSpecular = 2.0 * mMap.r;
|
|
139
|
+
if (uModelParams[0].w > 0.0 && uModelParams[1].w > 0.0) {
|
|
140
|
+
vec3 d = texture(uModelMap, vec3(at.xy, at.z + uModelParams[0].w)).rgb * 2.0 - 1.0;
|
|
141
|
+
vec3 strand = mTangent * d.x + mBitangent * d.y + mNormal * d.z;
|
|
142
|
+
float length2 = dot(strand, strand);
|
|
143
|
+
if (length2 > 1e-4) mTangent = strand * inversesqrt(length2);
|
|
144
|
+
}
|
|
145
|
+
hAround = hairIndirect();
|
|
96
146
|
}
|
|
97
147
|
|
|
98
|
-
/**
|
|
99
|
-
|
|
100
|
-
return
|
|
148
|
+
/** Light from every side, through the lobes, under the map's occlusion. */
|
|
149
|
+
vec3 hairAround(vec3 light) {
|
|
150
|
+
return hAround * (mMap.b * light);
|
|
101
151
|
}
|
|
102
152
|
`;
|
|
@@ -695,8 +695,9 @@ export const MAIN_GLSL = `void main() {
|
|
|
695
695
|
* branch it did not take before.
|
|
696
696
|
*/
|
|
697
697
|
/* A model reflects its environment whatever the pass asked of the standard model: see
|
|
698
|
-
modelReflectance. On the switches alone, so the derivatives
|
|
699
|
-
|
|
698
|
+
modelReflectance. Hair takes none: modelReflects. On the switches alone, so the derivatives
|
|
699
|
+
below stay in uniform flow. */
|
|
700
|
+
if ((uEnvironmentDials.y > 0.0 || uMaterialFlags.y != 0 || modelled) && modelReflects()) {
|
|
700
701
|
vec3 toEye = normalize(uCameraPos - vWorldPos);
|
|
701
702
|
vec3 mirrored = reflect(-toEye, modelled ? modelReflectNormal(n) : n);
|
|
702
703
|
vec3 environment = mix(uAmbientGround, uAmbient, mirrored.y * 0.5 + 0.5);
|
|
@@ -834,6 +835,19 @@ export const MAIN_GLSL = `void main() {
|
|
|
834
835
|
* caller sets this.
|
|
835
836
|
*/
|
|
836
837
|
environment *= mix(1.0, uEnvironmentDials.z, metal);
|
|
838
|
+
/*
|
|
839
|
+
* **And brought to the light where the surface stands**, metal or not: a probe is captured
|
|
840
|
+
* at one place and seen from many, so a rough surface standing in light twice what the probe
|
|
841
|
+
* saw takes twice its reflection, and a mirror keeps the capture as it is. The weight is the
|
|
842
|
+
* caller's, the place's indirect light over the probe's average; mixed in by roughness, with
|
|
843
|
+
* the smoothstep written out because GLSL leaves one over an empty range undefined. 0, the
|
|
844
|
+
* weight less one, is exactly the reflection there was.
|
|
845
|
+
*/
|
|
846
|
+
if (uEnvironmentMixing.x != 0.0) {
|
|
847
|
+
float span = max(uEnvironmentMixing.z - uEnvironmentMixing.y, 1e-4);
|
|
848
|
+
float rough = clamp((surfaceRoughness - uEnvironmentMixing.y) / span, 0.0, 1.0);
|
|
849
|
+
environment *= 1.0 + uEnvironmentMixing.x * rough * rough * (3.0 - 2.0 * rough);
|
|
850
|
+
}
|
|
837
851
|
float facing = 1.0 - max(dot(n, toEye), 0.0);
|
|
838
852
|
/*
|
|
839
853
|
* **A metal's Fresnel base is close to its albedo, not a dielectric's four percent.** Schlick
|
|
@@ -2089,8 +2103,14 @@ export const MAIN_GLSL = `void main() {
|
|
|
2089
2103
|
* the medium's colour and adding it puts the haze into the frame twice — a glow by day drew as
|
|
2090
2104
|
* a grey shape of itself, greyer the thicker the air.
|
|
2091
2105
|
*/
|
|
2106
|
+
/* And the sun's glow in the haze, which a surface takes after its fog and an added light never
|
|
2107
|
+
does: what is behind it already has it. */
|
|
2108
|
+
vec3 glow = vec3(0.0);
|
|
2109
|
+
if (uFogEnabled == 1) {
|
|
2110
|
+
glow = mediumGlow(distance(vWorldPos, uCameraPos), vWorldPos.y, normalize(vWorldPos - uCameraPos));
|
|
2111
|
+
}
|
|
2092
2112
|
vec3 shaded = applyOutputTransform(
|
|
2093
|
-
uFogEnabled == 2 ? lit * (1.0 - fog) : mix(lit, mediumColor(), fog));
|
|
2113
|
+
uFogEnabled == 2 ? lit * (1.0 - fog) : mix(lit, mediumColor(), fog) + glow);
|
|
2094
2114
|
|
|
2095
2115
|
/*
|
|
2096
2116
|
* **Refraction: the scene behind this surface, bent and absorbed.**
|
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
* **Where the models plug in.** `main` gathers the surface once (`modelSurface`), then asks at each
|
|
13
13
|
* place light arrives: `modelLight` for the sun, each lamp and each rectangle, `modelAmbient` for
|
|
14
14
|
* the sky and the probe's irradiance, `modelIrradiance` for DriftLight, `modelReflectNormal` and
|
|
15
|
-
* `modelReflectance` for the environment's reflection
|
|
16
|
-
* model, and each model's file is where its own answers are.
|
|
15
|
+
* `modelReflectance` for the environment's reflection, which `modelReflects` keeps from hair. Each
|
|
16
|
+
* returns today's term for the standard model, and each model's file is where its own answers are.
|
|
17
17
|
*
|
|
18
18
|
* **What a model returns from a light** is the two halves the standard terms are: a diffuse already
|
|
19
19
|
* weighted by the angle of arrival, and a specular lobe in the engine's peak-normalised units (see
|
|
@@ -95,6 +95,15 @@ bool modelSeesBehind() {
|
|
|
95
95
|
return false;
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
+
/**
|
|
99
|
+
* Whether the environment's reflection reaches this surface: every model's but hair's, whose share
|
|
100
|
+
* of it is in its light from every side (hair.ts).
|
|
101
|
+
*/
|
|
102
|
+
bool modelReflects() {
|
|
103
|
+
if (MODEL_HAIR) return false;
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
|
|
98
107
|
/* The surface a model shades, gathered once in main before any light reaches it. */
|
|
99
108
|
vec3 mNormal;
|
|
100
109
|
vec3 mToEye;
|
|
@@ -227,7 +236,7 @@ void modelSurface(vec3 n, vec3 albedo, float roughness, float metal, vec3 at) {
|
|
|
227
236
|
mMap = vec4(0.5, 0.5, 1.0, 1.0);
|
|
228
237
|
if (uModelParams[1].w > 0.0) mMap = texture(uModelMap, at);
|
|
229
238
|
if (MODEL_ANISOTROPIC) anisotropicSurface();
|
|
230
|
-
if (MODEL_HAIR) hairSurface();
|
|
239
|
+
if (MODEL_HAIR) hairSurface(at);
|
|
231
240
|
if (MODEL_SKIN) skinSurface();
|
|
232
241
|
if (MODEL_EYE) eyeSurface(at);
|
|
233
242
|
}
|
|
@@ -285,7 +294,6 @@ vec3 modelIrradiance(vec3 irradiance) {
|
|
|
285
294
|
/** The normal the environment is reflected about: the surface's own, but for a stretched lobe. */
|
|
286
295
|
vec3 modelReflectNormal(vec3 n) {
|
|
287
296
|
if (MODEL_ANISOTROPIC) return anisotropicReflectNormal(n);
|
|
288
|
-
if (MODEL_HAIR) return hairAcross();
|
|
289
297
|
return n;
|
|
290
298
|
}
|
|
291
299
|
|
|
@@ -361,7 +369,6 @@ vec3 modelDiffuseOut(vec3 diffuse, float fog) {
|
|
|
361
369
|
* what the standard model would.
|
|
362
370
|
*/
|
|
363
371
|
float modelReflectance(float standard) {
|
|
364
|
-
if (MODEL_HAIR) return hairReflectance();
|
|
365
372
|
if (MODEL_SKIN) return skinReflectance();
|
|
366
373
|
if (MODEL_EYE) return eyeReflectance();
|
|
367
374
|
return standard;
|
|
@@ -289,6 +289,13 @@ uniform vec3 uOrmScale; // wgsl:material
|
|
|
289
289
|
*/
|
|
290
290
|
uniform vec3 uEnvironmentDials; // wgsl:material
|
|
291
291
|
|
|
292
|
+
/**
|
|
293
|
+
* The environment a surface reflects brought to the light where it stands, for every surface:
|
|
294
|
+
* \`setEnvironmentMixing\`. x is the weight less one, so a program nothing wrote reflects as it did;
|
|
295
|
+
* y and z the roughnesses the weight is mixed in over, smooth to rough.
|
|
296
|
+
*/
|
|
297
|
+
uniform vec4 uEnvironmentMixing; // wgsl:material
|
|
298
|
+
|
|
292
299
|
/**
|
|
293
300
|
* Where a surface glows, and in what colour.
|
|
294
301
|
*
|