@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
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
* whose drama is the contrast itself: a single shaft into a dark room wants the room dark, and the
|
|
27
27
|
* strength is the caller's for that reason.
|
|
28
28
|
*/
|
|
29
|
-
import { METER_GRID, METER_MAX_LOG2, METER_MIN_LOG2, METER_TAPS } from './exposure.ts';
|
|
29
|
+
import { ADAPTED_KEY, METER_GRID, METER_MAX_LOG2, METER_MIN_LOG2, METER_TAPS } from './exposure.ts';
|
|
30
30
|
|
|
31
31
|
/** Bands of log luminance, and how many stops each is wide. */
|
|
32
32
|
export const LOCAL_BINS = 10;
|
|
@@ -82,15 +82,27 @@ void main() {
|
|
|
82
82
|
`;
|
|
83
83
|
|
|
84
84
|
/**
|
|
85
|
-
* The composite's half. Needs `AUTO_EXPOSURE_GLSL` before it, for the held
|
|
85
|
+
* The composite's half. Needs `AUTO_EXPOSURE_GLSL` and `uOutputExposure` before it, for the held
|
|
86
|
+
* brightness and the frame's exposure.
|
|
86
87
|
*
|
|
87
88
|
* A block is read clamped half a texel inside its own tiles, so the filter never reaches into the
|
|
88
89
|
* neighbouring band's block. Every fetch is at level zero: the grid has one level, and the branch
|
|
89
90
|
* on the strength above them is uniform but the pixel's band is not.
|
|
90
91
|
*/
|
|
91
92
|
export const LOCAL_EXPOSURE_GLSL = `
|
|
92
|
-
/**
|
|
93
|
+
/** Above 0 when regions move at all: 0 is off and the whole of the off path. */
|
|
93
94
|
uniform float uLocalExposure;
|
|
95
|
+
/**
|
|
96
|
+
* How regions move, as \`resolveLocalExposure\` packs it: the share of a region's stops from the
|
|
97
|
+
* pivot it keeps above the pivot and below it, each less one; the share of a pixel's own stops
|
|
98
|
+
* from its region it keeps, less one; and how far a region's level leans toward its tile's mean.
|
|
99
|
+
*/
|
|
100
|
+
uniform vec4 uLocalShape;
|
|
101
|
+
/**
|
|
102
|
+
* What they move about: 1 for the frame's held brightness, 0 for middle grey at the frame's
|
|
103
|
+
* exposure; stops added to it; and the furthest a region moves either way.
|
|
104
|
+
*/
|
|
105
|
+
uniform vec4 uLocalPivot;
|
|
94
106
|
/** The bilateral grid: a block of tiles per band of brightness, and one of the tiles' means. */
|
|
95
107
|
uniform highp sampler2D uExposureLocal;
|
|
96
108
|
|
|
@@ -109,9 +121,15 @@ float localExposureGain(vec2 uv, vec3 light) {
|
|
|
109
121
|
vec2 banded = mix(localBlock(uv, lower), localBlock(uv, upper), clamp(b - lower, 0.0, 1.0));
|
|
110
122
|
float tileMean = localBlock(uv, ${LOCAL_BINS.toFixed(1)}).r;
|
|
111
123
|
float bilateral = banded.y > 1e-3 ? banded.x / banded.y : tileMean;
|
|
112
|
-
float local = mix(bilateral, tileMean,
|
|
113
|
-
|
|
114
|
-
|
|
124
|
+
float local = mix(bilateral, tileMean, uLocalShape.w);
|
|
125
|
+
/* Middle grey at the frame's exposure, in the scene light the grid measured. */
|
|
126
|
+
float pivot = uLocalPivot.x > 0.5
|
|
127
|
+
? held
|
|
128
|
+
: ${Math.log2(ADAPTED_KEY).toFixed(6)} - log2(max(autoExposureGain() * uOutputExposure, 1e-8));
|
|
129
|
+
pivot += uLocalPivot.y;
|
|
130
|
+
float keep = local > pivot ? uLocalShape.x : uLocalShape.y;
|
|
131
|
+
float stops = (local - pivot) * keep + (pixel - local) * uLocalShape.z;
|
|
132
|
+
return exp2(clamp(stops, -uLocalPivot.z, uLocalPivot.z));
|
|
115
133
|
}
|
|
116
134
|
`;
|
|
117
135
|
|
|
@@ -148,6 +166,97 @@ export function localExposureGain(localLog2: number, heldLog2: number, strength:
|
|
|
148
166
|
return 2 ** clamp(stops, -LOCAL_RANGE_STOPS, LOCAL_RANGE_STOPS);
|
|
149
167
|
}
|
|
150
168
|
|
|
169
|
+
/**
|
|
170
|
+
* Local exposure as a shape rather than one strength: each region turned about middle grey at the
|
|
171
|
+
* frame's exposure, the real-time form most engines expose.
|
|
172
|
+
*
|
|
173
|
+
* A region's level is its stops from that grey: it keeps `highlightContrast` of them where it is
|
|
174
|
+
* brighter and `shadowContrast` where it is darker, so 1 leaves it where it is and 0 brings it all
|
|
175
|
+
* the way to grey. A pixel keeps `detail` of its own stops from its region, so above 1 sharpens the
|
|
176
|
+
* texture inside a region a curve would have flattened. **What it gives up** is the held level the
|
|
177
|
+
* one-number form turns about: a frame metered far from grey, a night, is pulled toward grey rather
|
|
178
|
+
* than toward itself, which is what the form is for and what a caller holding a night dark leaves
|
|
179
|
+
* off.
|
|
180
|
+
*/
|
|
181
|
+
export interface LocalExposureOptions {
|
|
182
|
+
/** The share of a region's stops above middle grey it keeps. 1 is none moved. */
|
|
183
|
+
readonly highlightContrast: number;
|
|
184
|
+
/** The share it keeps below. */
|
|
185
|
+
readonly shadowContrast: number;
|
|
186
|
+
/** The share of a pixel's own stops from its region it keeps. Default 1. */
|
|
187
|
+
readonly detail?: number;
|
|
188
|
+
/** How far a region's level leans from its bilateral band toward its tile's mean. Default 0.4. */
|
|
189
|
+
readonly blurredBlend?: number;
|
|
190
|
+
/** Stops added to middle grey, the level regions turn about. Default 0. */
|
|
191
|
+
readonly middleGreyBias?: number;
|
|
192
|
+
/** The furthest a region moves either way, in stops. Default 8. */
|
|
193
|
+
readonly range?: number;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/** Floats `resolveLocalExposure` fills: `uLocalShape`, then `uLocalPivot`. */
|
|
197
|
+
export const LOCAL_EXPOSURE_FLOATS = 8;
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Either form of `setLocalExposure` as the composite reads it, into `out`, and whether regions move
|
|
201
|
+
* at all. The one number is the shape it always was, both shares `1 − strength` about the held
|
|
202
|
+
* level, at most three stops: so a strength draws what it drew, to the bit.
|
|
203
|
+
*/
|
|
204
|
+
export function resolveLocalExposure(
|
|
205
|
+
setting: number | LocalExposureOptions,
|
|
206
|
+
out: Float32Array,
|
|
207
|
+
): boolean {
|
|
208
|
+
if (typeof setting === 'number') {
|
|
209
|
+
const strength = clampLocalExposure(setting);
|
|
210
|
+
out[0] = -strength;
|
|
211
|
+
out[1] = -strength;
|
|
212
|
+
out[2] = 0;
|
|
213
|
+
out[3] = LOCAL_BLURRED_BLEND;
|
|
214
|
+
out[4] = 1;
|
|
215
|
+
out[5] = 0;
|
|
216
|
+
out[6] = LOCAL_RANGE_STOPS;
|
|
217
|
+
out[7] = 0;
|
|
218
|
+
return strength > 0;
|
|
219
|
+
}
|
|
220
|
+
const share = (value: number | undefined, fallback: number): number =>
|
|
221
|
+
value !== undefined && Number.isFinite(value) ? Math.max(0, value) : fallback;
|
|
222
|
+
const highlight = share(setting.highlightContrast, 1);
|
|
223
|
+
const shadow = share(setting.shadowContrast, 1);
|
|
224
|
+
const detail = share(setting.detail, 1);
|
|
225
|
+
out[0] = highlight - 1;
|
|
226
|
+
out[1] = shadow - 1;
|
|
227
|
+
out[2] = detail - 1;
|
|
228
|
+
out[3] = clamp(share(setting.blurredBlend, LOCAL_BLURRED_BLEND), 0, 1);
|
|
229
|
+
out[4] = 0;
|
|
230
|
+
const bias = setting.middleGreyBias;
|
|
231
|
+
out[5] = bias !== undefined && Number.isFinite(bias) ? bias : 0;
|
|
232
|
+
out[6] = share(setting.range, 8);
|
|
233
|
+
out[7] = 0;
|
|
234
|
+
return highlight !== 1 || shadow !== 1 || detail !== 1;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* A pixel's gain from either form, as the composite computes it: `pixelLog2` its own level,
|
|
239
|
+
* `localLog2` its region's before the blend, `tileMean` its tile's, `heldLog2` the frame's held
|
|
240
|
+
* brightness and `exposure` the frame's exposure, gain and all; `shape` is `resolveLocalExposure`'s.
|
|
241
|
+
*/
|
|
242
|
+
export function localExposureShapedGain(
|
|
243
|
+
pixelLog2: number,
|
|
244
|
+
localLog2: number,
|
|
245
|
+
tileMean: number,
|
|
246
|
+
heldLog2: number,
|
|
247
|
+
exposure: number,
|
|
248
|
+
shape: ArrayLike<number>,
|
|
249
|
+
): number {
|
|
250
|
+
const local = localLog2 + (tileMean - localLog2) * (shape[3] as number);
|
|
251
|
+
const pivot =
|
|
252
|
+
((shape[4] as number) > 0.5 ? heldLog2 : Math.log2(ADAPTED_KEY) - Math.log2(exposure)) +
|
|
253
|
+
(shape[5] as number);
|
|
254
|
+
const keep = local > pivot ? (shape[0] as number) : (shape[1] as number);
|
|
255
|
+
const stops = (local - pivot) * keep + (pixelLog2 - local) * (shape[2] as number);
|
|
256
|
+
const range = shape[6] as number;
|
|
257
|
+
return 2 ** clamp(stops, -range, range);
|
|
258
|
+
}
|
|
259
|
+
|
|
151
260
|
/** A local exposure strength as the shader takes it: 0 to 1, anything else off. */
|
|
152
261
|
export function clampLocalExposure(strength: number): number {
|
|
153
262
|
return Number.isFinite(strength) ? clamp(strength, 0, 1) : 0;
|
|
@@ -334,8 +334,10 @@ void main() {
|
|
|
334
334
|
}
|
|
335
335
|
|
|
336
336
|
vec3 shaded = vColor * lit;
|
|
337
|
-
float
|
|
338
|
-
|
|
337
|
+
float dist = distance(vWorldPos, uCameraPos);
|
|
338
|
+
float fog = mediumFog(dist, vWorldPos.y);
|
|
339
|
+
vec3 glow = mediumGlow(dist, vWorldPos.y, normalize(vWorldPos - uCameraPos));
|
|
340
|
+
outColor = vec4(applyOutputTransform(mix(shaded, mediumColor(), fog) + glow), mask * vAlpha);
|
|
339
341
|
}
|
|
340
342
|
`;
|
|
341
343
|
|
|
@@ -463,8 +465,9 @@ void main() {
|
|
|
463
465
|
|
|
464
466
|
vec3 color = vColor;
|
|
465
467
|
if (uFogEnabled != 0) {
|
|
466
|
-
float
|
|
467
|
-
|
|
468
|
+
float dist = distance(vWorldPos, uCameraPos);
|
|
469
|
+
float fog = mediumFog(dist, vWorldPos.y);
|
|
470
|
+
color = mix(color, mediumColor(), fog) + mediumGlow(dist, vWorldPos.y, normalize(vWorldPos - uCameraPos));
|
|
468
471
|
}
|
|
469
472
|
/*
|
|
470
473
|
* A real alpha blend (SRC_ALPHA, ONE_MINUS_SRC_ALPHA) rather than spark's energy-in-colour
|
|
@@ -563,8 +566,9 @@ void main() {
|
|
|
563
566
|
if (alpha <= 0.001) discard;
|
|
564
567
|
vec3 color = vColor * image.rgb;
|
|
565
568
|
if (uFogEnabled != 0) {
|
|
566
|
-
float
|
|
567
|
-
|
|
569
|
+
float dist = distance(vWorldPos, uCameraPos);
|
|
570
|
+
float fog = mediumFog(dist, vWorldPos.y);
|
|
571
|
+
color = mix(color, mediumColor(), fog) + mediumGlow(dist, vWorldPos.y, normalize(vWorldPos - uCameraPos));
|
|
568
572
|
}
|
|
569
573
|
outColor = vec4(applyOutputTransform(color), alpha);
|
|
570
574
|
}
|
|
@@ -206,9 +206,11 @@ void main() {
|
|
|
206
206
|
float ndl = abs(dot(n, uDirectionalDir));
|
|
207
207
|
vec3 lit = vColor * (uAmbient + uDirectionalColor * ndl);
|
|
208
208
|
|
|
209
|
-
float
|
|
209
|
+
float dist = distance(vWorldPos, uCameraPos);
|
|
210
|
+
float fog = mediumFog(dist, vWorldPos.y);
|
|
211
|
+
vec3 glow = mediumGlow(dist, vWorldPos.y, normalize(vWorldPos - uCameraPos));
|
|
210
212
|
|
|
211
213
|
/* Graded where nothing after this will grade it, as every pass is: the same light as the ground. */
|
|
212
|
-
outColor = vec4(applyOutputTransform(mix(lit, mediumColor(), fog)), 1.0);
|
|
214
|
+
outColor = vec4(applyOutputTransform(mix(lit, mediumColor(), fog) + glow), 1.0);
|
|
213
215
|
}
|
|
214
216
|
`;
|
|
@@ -277,7 +277,8 @@ void main() {
|
|
|
277
277
|
|
|
278
278
|
float dist = distance(vWorldPos, uCameraPos);
|
|
279
279
|
float fog = mediumFog(dist, vWorldPos.y);
|
|
280
|
-
|
|
280
|
+
vec3 glow = mediumGlow(dist, vWorldPos.y, normalize(vWorldPos - uCameraPos));
|
|
281
|
+
col = mix(col, atmosphereColor, fog) + glow;
|
|
281
282
|
/*
|
|
282
283
|
* **Graded here, before the mirror is mixed in**, as every pass grades what it draws when
|
|
283
284
|
* nothing after it will: the mirror's own passes graded what it holds, so mixing first would put
|
|
@@ -28,12 +28,20 @@ export interface AnisotropicModel {
|
|
|
28
28
|
readonly strength: number;
|
|
29
29
|
/** Radians, turning the stretch's direction from the tangent toward the bitangent. */
|
|
30
30
|
readonly rotation: number;
|
|
31
|
+
/**
|
|
32
|
+
* How far the highlight is lifted where it is bright: the distribution `D` becomes
|
|
33
|
+
* `D + min(3, D^1.5) · lift`, which is how a brushed finish that reads as polished along its
|
|
34
|
+
* streak is authored. Where a model map is bound its alpha scales it texel by texel, so a map
|
|
35
|
+
* storing a tenth of a region's lift takes `lift: 10`. 0, the default, is none.
|
|
36
|
+
*/
|
|
37
|
+
readonly lift: number;
|
|
31
38
|
}
|
|
32
39
|
|
|
33
40
|
/**
|
|
34
41
|
* Hair: three lobes along the strand, which is the mesh's tangent, running from root to tip
|
|
35
|
-
* (`hairLobes.ts`). A model map's
|
|
36
|
-
* its
|
|
42
|
+
* (`hairLobes.ts`). A model map's red is the strand's specular, twice it scaling the reflected lobe
|
|
43
|
+
* (0.5 is the lobe as drawn), its green turns each strand's tilt by up to half of it either way and
|
|
44
|
+
* its blue is occlusion; `directionOffset` names a layer of it holding each strand's direction.
|
|
37
45
|
*/
|
|
38
46
|
export interface HairModel {
|
|
39
47
|
readonly kind: 'hair';
|
|
@@ -47,6 +55,15 @@ export interface HairModel {
|
|
|
47
55
|
readonly scatter: number;
|
|
48
56
|
/** How strongly light from behind glows through: the transmitted lobe. */
|
|
49
57
|
readonly backlit: number;
|
|
58
|
+
/**
|
|
59
|
+
* Where the model map holds each strand's direction: this many layers past the layer the texel's
|
|
60
|
+
* other channels are read at, so a model map of one layer with its directions in the next takes
|
|
61
|
+
* 1. The direction is in the surface's tangent frame, red along the tangent, green along the
|
|
62
|
+
* bitangent and blue out of the surface along its normal, each −1 to 1 packed as a normal map
|
|
63
|
+
* packs them, and the strand runs along it rather than along the tangent: a card's strands can
|
|
64
|
+
* lean out of its plane. 0, the default, is none.
|
|
65
|
+
*/
|
|
66
|
+
readonly directionOffset: number;
|
|
50
67
|
}
|
|
51
68
|
|
|
52
69
|
/** Skin: light scattered beneath the surface, reddening its terminator and its thin parts. */
|
|
@@ -137,10 +154,11 @@ function unit(model: string, name: string, v: Vec3): Vec3 {
|
|
|
137
154
|
|
|
138
155
|
/** Brushed metal and the like, after glTF's `KHR_materials_anisotropy`: a strength and a turn. */
|
|
139
156
|
export function anisotropicModel(
|
|
140
|
-
options: { strength?: number; rotation?: number } = {},
|
|
157
|
+
options: { strength?: number; rotation?: number; lift?: number } = {},
|
|
141
158
|
): AnisotropicModel {
|
|
142
159
|
return Object.freeze({
|
|
143
160
|
kind: 'anisotropic',
|
|
161
|
+
lift: check('anisotropicModel', 'lift', options.lift ?? 0, 0, 100),
|
|
144
162
|
strength: check('anisotropicModel', 'strength', options.strength ?? 1, 0, 1),
|
|
145
163
|
rotation: check(
|
|
146
164
|
'anisotropicModel',
|
|
@@ -154,13 +172,26 @@ export function anisotropicModel(
|
|
|
154
172
|
|
|
155
173
|
/** Hair, along the mesh's tangent from root to tip. A tilt of 3°, within the 2.5° to 5° measured. */
|
|
156
174
|
export function hairModel(
|
|
157
|
-
options: { shift?: number; scatter?: number; backlit?: number } = {},
|
|
175
|
+
options: { shift?: number; scatter?: number; backlit?: number; directionOffset?: number } = {},
|
|
158
176
|
): HairModel {
|
|
177
|
+
const directionOffset = check(
|
|
178
|
+
'hairModel',
|
|
179
|
+
'directionOffset',
|
|
180
|
+
options.directionOffset ?? 0,
|
|
181
|
+
0,
|
|
182
|
+
255,
|
|
183
|
+
);
|
|
184
|
+
if (!Number.isInteger(directionOffset)) {
|
|
185
|
+
throw new Error(
|
|
186
|
+
`hairModel: directionOffset must be a whole number of layers, not ${directionOffset}`,
|
|
187
|
+
);
|
|
188
|
+
}
|
|
159
189
|
return Object.freeze({
|
|
160
190
|
kind: 'hair',
|
|
161
191
|
shift: check('hairModel', 'shift', options.shift ?? (3 * Math.PI) / 180, -0.5, 0.5),
|
|
162
192
|
scatter: check('hairModel', 'scatter', options.scatter ?? 0.7, 0, 1),
|
|
163
193
|
backlit: check('hairModel', 'backlit', options.backlit ?? 1, 0, 4),
|
|
194
|
+
directionOffset,
|
|
164
195
|
});
|
|
165
196
|
}
|
|
166
197
|
|
|
@@ -246,8 +277,8 @@ export function lightmapModel(options: { region?: LightmapRegion } = {}): Lightm
|
|
|
246
277
|
* models that read it leave free; skin, hair and the eye shade their own highlights and never do.
|
|
247
278
|
*
|
|
248
279
|
* - standard (null): − · −, −, physical, −
|
|
249
|
-
* - anisotropic: strength, cos and sin of the rotation · −, −, physical, map
|
|
250
|
-
* - hair: shift, scatter, backlit,
|
|
280
|
+
* - anisotropic: strength, cos and sin of the rotation, lift · −, −, physical, map
|
|
281
|
+
* - hair: shift, scatter, backlit, direction offset · −, −, −, map
|
|
251
282
|
* - skin: the scatter colour, radius / `SKIN_MEAN_EXIT` · transmission, profile, −, map
|
|
252
283
|
* - eye: iris radius, iris depth, ior, cornea roughness · the axis, map
|
|
253
284
|
* - lightmap: the region's scale and bias · −, −, physical, map
|
|
@@ -270,12 +301,16 @@ export function packModel(
|
|
|
270
301
|
out[0] = model.strength;
|
|
271
302
|
out[1] = Math.cos(model.rotation);
|
|
272
303
|
out[2] = Math.sin(model.rotation);
|
|
304
|
+
/* The lift, which the map's alpha scales texel by texel; 0, and a missing field, is none. */
|
|
305
|
+
out[3] = model.lift ?? 0;
|
|
273
306
|
out[6] = highlight;
|
|
274
307
|
return;
|
|
275
308
|
case 'hair':
|
|
276
309
|
out[0] = model.shift;
|
|
277
310
|
out[1] = model.scatter;
|
|
278
311
|
out[2] = model.backlit;
|
|
312
|
+
/* A layer offset the shader adds to the texel's own; 0 is none, and so is a missing field. */
|
|
313
|
+
out[3] = model.directionOffset ?? 0;
|
|
279
314
|
return;
|
|
280
315
|
case 'skin':
|
|
281
316
|
out[0] = model.scatterColor[0];
|