@genex-ai/cli-demo 0.6.1 → 0.6.2

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.
Files changed (44) hide show
  1. package/package.json +1 -1
  2. package/templates/skills/genex-threejs-atmosphere-aerial-perspective/SKILL.md +30 -18
  3. package/templates/skills/genex-threejs-atmosphere-aerial-perspective/references/atmosphere.md +204 -20
  4. package/templates/skills/genex-threejs-bloom/SKILL.md +29 -18
  5. package/templates/skills/genex-threejs-bloom/references/bloom.md +176 -20
  6. package/templates/skills/genex-threejs-camera-direction/SKILL.md +38 -26
  7. package/templates/skills/genex-threejs-camera-direction/references/camera-rigs.md +359 -27
  8. package/templates/skills/genex-threejs-exposure-color-grading/SKILL.md +27 -18
  9. package/templates/skills/genex-threejs-exposure-color-grading/references/exposure-grading.md +196 -21
  10. package/templates/skills/genex-threejs-image-pipeline/SKILL.md +38 -17
  11. package/templates/skills/genex-threejs-image-pipeline/references/image-pipeline.md +185 -29
  12. package/templates/skills/genex-threejs-procedural-animation/SKILL.md +34 -21
  13. package/templates/skills/genex-threejs-procedural-animation/references/procedural-motion.md +353 -24
  14. package/templates/skills/genex-threejs-procedural-architecture/SKILL.md +36 -17
  15. package/templates/skills/genex-threejs-procedural-architecture/references/architecture-systems.md +500 -22
  16. package/templates/skills/genex-threejs-procedural-fields/SKILL.md +59 -24
  17. package/templates/skills/genex-threejs-procedural-fields/references/field-systems.md +222 -25
  18. package/templates/skills/genex-threejs-procedural-geometry/SKILL.md +34 -20
  19. package/templates/skills/genex-threejs-procedural-geometry/references/mesh-systems.md +192 -26
  20. package/templates/skills/genex-threejs-procedural-materials/SKILL.md +55 -18
  21. package/templates/skills/genex-threejs-procedural-materials/references/material-systems.md +189 -22
  22. package/templates/skills/genex-threejs-procedural-planets/SKILL.md +36 -18
  23. package/templates/skills/genex-threejs-procedural-planets/references/planet-systems.md +489 -21
  24. package/templates/skills/genex-threejs-procedural-vegetation/SKILL.md +35 -25
  25. package/templates/skills/genex-threejs-procedural-vegetation/references/vegetation-systems.md +304 -27
  26. package/templates/skills/genex-threejs-procedural-vfx/SKILL.md +26 -18
  27. package/templates/skills/genex-threejs-procedural-vfx/references/vfx-systems.md +208 -20
  28. package/templates/skills/genex-threejs-raymarched-space-effects/SKILL.md +25 -18
  29. package/templates/skills/genex-threejs-raymarched-space-effects/references/space-effects.md +185 -21
  30. package/templates/skills/genex-threejs-screen-space-ambient-occlusion/SKILL.md +23 -17
  31. package/templates/skills/genex-threejs-screen-space-ambient-occlusion/references/ambient-occlusion.md +430 -20
  32. package/templates/skills/genex-threejs-shadow-systems/SKILL.md +29 -18
  33. package/templates/skills/genex-threejs-shadow-systems/references/shadow-systems.md +420 -21
  34. package/templates/skills/genex-threejs-skill-router/SKILL.md +21 -21
  35. package/templates/skills/genex-threejs-spectral-ocean/SKILL.md +30 -20
  36. package/templates/skills/genex-threejs-spectral-ocean/references/spectral-ocean.md +462 -22
  37. package/templates/skills/genex-threejs-temporal-surfaces/SKILL.md +26 -17
  38. package/templates/skills/genex-threejs-temporal-surfaces/references/temporal-surfaces.md +198 -20
  39. package/templates/skills/genex-threejs-visual-validation/SKILL.md +34 -18
  40. package/templates/skills/genex-threejs-visual-validation/references/visual-validation.md +396 -32
  41. package/templates/skills/genex-threejs-volumetric-clouds/SKILL.md +33 -17
  42. package/templates/skills/genex-threejs-volumetric-clouds/references/volumetric-clouds.md +570 -21
  43. package/templates/skills/genex-threejs-water-optics/SKILL.md +33 -18
  44. package/templates/skills/genex-threejs-water-optics/references/water-optics.md +184 -20
@@ -1,38 +1,370 @@
1
- # Camera Rigs
1
+ # Camera rig and cinematic systems
2
2
 
3
- Use these patterns when a Genex scene needs authored camera behavior.
3
+ Use this reference for scale-aware chase, side, orbit, authored-shot, pointer-look, floating-origin, projection, and lifecycle camera systems.
4
4
 
5
- ## Chase camera
5
+ ## Contents
6
6
 
7
- - Base the offset on the controlled object's bounding sphere.
8
- - Track a target point slightly ahead of the object, not only its origin.
9
- - Smooth position with an exponential factor based on `deltaTime`.
10
- - Smooth orientation separately so fast direction changes do not roll the view.
11
- - Clamp pitch and distance so the player cannot bury the camera in geometry.
7
+ - Camera contract
8
+ - planet-space implementation ship-scaled chase mount
9
+ - planet-space implementation thrust-lag spring
10
+ - planet-space implementation side and orbit camera
11
+ - Explicit camera handoffs
12
+ - cinematic implementation shot ownership
13
+ - Pointer-look and movement constraints
14
+ - Floating origin and background handling
15
+ - Projection and lifecycle ownership
16
+ - Failure modes and diagnostics
12
17
 
13
- ## Orbit camera
14
18
 
15
- - Store yaw, pitch, and distance as explicit state.
16
- - Clamp pitch before deriving the spherical offset.
17
- - Recompute the target from the game object each frame.
18
- - Disable or reduce damping during scripted handoffs.
19
+ ## Camera contract
19
20
 
20
- ## Side or authored shot
21
+ Record before implementation:
21
22
 
22
- - Define shot anchors in scene space or subject-relative space.
23
- - Keep screen occupancy stable across subject scale changes.
24
- - Blend only one transition source at a time.
25
- - Expose a debug view showing target point, camera point, and frustum.
23
+ ```ts
24
+ type CameraDirectionContract = {
25
+ subject: THREE.Object3D
26
+ subjectScale: number
27
+ projection: { fov: number; near: number; far: number }
28
+ positionMode: "authored" | "mount" | "body-relative" | "floating-origin"
29
+ upMode: "world" | "subject" | "dominant-body"
30
+ inputMode: "locked" | "pointer-look" | "orbit-offset"
31
+ handoffOwner: string
32
+ spatialConstraints: string[]
33
+ }
34
+ ```
26
35
 
27
- ## Pointer look
36
+ Do not combine modes until each can produce a valid position and quaternion
37
+ independently.
28
38
 
29
- - On lock acquisition, derive yaw and pitch from the current camera quaternion.
30
- - Apply pointer deltas to yaw/pitch state, then rebuild the quaternion.
31
- - Release cleanly on escape, tab changes, and scene disposal.
39
+ ## planet-space implementation ship-scaled chase mount
32
40
 
33
- ## Large worlds
41
+ `CameraRigSystem` derives the chase mount from ship length:
34
42
 
35
- - Use a floating origin or camera-relative background for scenes with very large
36
- coordinates.
37
- - Keep stars, sky domes, and infinite grids from translating in ways that imply
38
- false parallax.
43
+ ```text
44
+ height = shipLength * 0.5
45
+ back = shipLength * 1.3
46
+ mount position = (0, height, -back)
47
+
48
+ look target:
49
+ up = shipLength * 0.0001
50
+ forward = shipLength * 0.35
51
+ ```
52
+
53
+ It constructs a basis:
54
+
55
+ ```text
56
+ forward = normalize(target - mount)
57
+ right = normalize(cross(worldUp, forward))
58
+ up = normalize(cross(forward, right))
59
+ quaternion = quaternion(makeBasis(right, up, forward))
60
+ quaternion *= rotation(worldUp, π)
61
+ ```
62
+
63
+ The final `π` correction is model-convention specific. Verify the camera’s
64
+ local forward convention before retaining it.
65
+
66
+ The mount is parented to `ShipRoot`, so its world position/quaternion follows
67
+ the ship without recomputing the authored chase pose.
68
+
69
+ ## planet-space implementation thrust-lag spring
70
+
71
+ planet-space implementation adds camera distance behind the ship only while manual thrust is
72
+ active. Throttle and boost own separate scalar spring states:
73
+
74
+ ```text
75
+ throttle max = 3.8
76
+ boost max = 5.8
77
+ combined max = 8.2
78
+
79
+ drive acceleration:
80
+ throttle 12
81
+ boost 22.8
82
+
83
+ held stiffness:
84
+ throttle 6
85
+ boost 7.5
86
+
87
+ return stiffness = 34
88
+ held damping ratio = 1.04
89
+ return damping ratio = 1.30
90
+ ```
91
+
92
+ Per component:
93
+
94
+ ```text
95
+ damping = 2 * dampingRatio * sqrt(stiffness)
96
+ acceleration =
97
+ activeDrive
98
+ - stiffness * distance
99
+ - damping * velocity
100
+
101
+ velocity += acceleration * dt
102
+ distance += velocity * dt
103
+ distance = clamp(distance, 0, maxDistance)
104
+ ```
105
+
106
+ If clamping blocks velocity in the same direction, zero it. Apply total lag
107
+ along negative ship forward after reading the chase mount’s world pose.
108
+
109
+ This gives acceleration weight without adding camera rotation lag.
110
+
111
+ ## planet-space implementation side and orbit camera
112
+
113
+ Scale-aware offsets after the ship model loads:
114
+
115
+ ```text
116
+ side = (
117
+ shipLength * 3.2,
118
+ shipLength * 1.0,
119
+ -shipLength * 1.35
120
+ )
121
+
122
+ orbit = (
123
+ shipLength * 4.85,
124
+ shipLength * 1.35,
125
+ -shipLength * 2.15
126
+ )
127
+ ```
128
+
129
+ The camera uses the dominant-body radial vector as up:
130
+
131
+ ```text
132
+ bodyUp = normalize(shipPosition - bodyPosition)
133
+ ```
134
+
135
+ For orbit lock, forward comes from relative velocity; otherwise it comes from
136
+ ship orientation. Project forward onto the body tangent plane:
137
+
138
+ ```text
139
+ tangent = forward - bodyUp * dot(forward, bodyUp)
140
+ ```
141
+
142
+ Frame-rate-independent smoothing:
143
+
144
+ ```text
145
+ side forward response = 1 - exp(-6.5 * dt)
146
+ offset response = 1 - exp(-3.6 * dt)
147
+ mode blend lambda = 3.2
148
+ ```
149
+
150
+ Rebuild an orthonormal frame:
151
+
152
+ ```text
153
+ right = normalize(cross(bodyUp, tangent))
154
+ tangent = normalize(cross(right, bodyUp))
155
+ offset =
156
+ right * offset.x
157
+ + bodyUp * offset.y
158
+ + tangent * offset.z
159
+ ```
160
+
161
+ Yaw rotates the offset around `bodyUp`. Pitch rotates around
162
+ `cross(bodyUp, offset)`. Pointer input scales:
163
+
164
+ ```text
165
+ yaw -= mouseDeltaX * 0.0022
166
+ pitch -= mouseDeltaY * 0.0018
167
+ ```
168
+
169
+ Pitch bounds vary by flight mode. The implementation also enforces camera height above
170
+ the ship:
171
+
172
+ ```text
173
+ landed minimum = shipLength * 0.42
174
+ other side-camera minimum = shipLength * 0.20
175
+ ```
176
+
177
+ If tangent becomes nearly parallel to up (`abs(dot) > 0.985`), rebuild it from
178
+ `cross(bodyUp, worldUp)` and then X as a final fallback.
179
+
180
+ Look target:
181
+
182
+ ```text
183
+ target = bodyUp * shipLength * 0.12
184
+ quaternion = lookAt(cameraPosition, target, bodyUp)
185
+ ```
186
+
187
+ This camera is positioned in ship-root-local coordinates. Preserve that
188
+ coordinate ownership when adapting the rig.
189
+
190
+ ## Explicit camera handoffs
191
+
192
+ planet-space implementation captures position and quaternion at transition start. Launch handoff
193
+ begins at progress `0.68`; orbit-exit duration varies from `1.1` to `2.6`
194
+ seconds based on the current side-camera blend.
195
+
196
+ Ease:
197
+
198
+ ```text
199
+ eased = 1 - (1 - t)^1.8
200
+ position = lerp(startPosition, chasePosition, eased)
201
+ orientation = slerp(startQuaternion, chaseQuaternion, eased)
202
+ ```
203
+
204
+ Critical transition invariant:
205
+
206
+ ```text
207
+ explicit transition active
208
+ -> write camera directly from one lerp/slerp
209
+ -> return from camera update
210
+ ```
211
+
212
+ Do not apply the normal follow smoother after this interpolation. Stacked
213
+ smoothing causes a mid-transition half-halt.
214
+
215
+ Outside explicit transitions, the final chase/side pose is followed with:
216
+
217
+ ```text
218
+ lambda 9.5 while side blend is active/transitioning
219
+ lambda 18 when pure chase
220
+ ```
221
+
222
+ At effectively zero blend, copy the chase pose exactly to prevent a permanent
223
+ subpixel tail.
224
+
225
+ ## cinematic implementation shot ownership
226
+
227
+ Each cinematic implementation scene owns its shot and projection values, for example:
228
+
229
+ ```text
230
+ Saturn approach:
231
+ FOV 40
232
+ near 12
233
+ far 360000
234
+
235
+ spin docking:
236
+ FOV 46
237
+ near 35
238
+ far 90000
239
+ ```
240
+
241
+ Scenes save prior FOV/near/far, update the projection, and restore all three on
242
+ dispose.
243
+
244
+ The spin-docking shot uses authored world anchors:
245
+
246
+ ```text
247
+ camera position = (6878.606, 4914.173, 6141.678)
248
+ look target = (6301.714, 4779.175, 5336.091)
249
+ ```
250
+
251
+ Ships are then staged in the camera frame:
252
+
253
+ ```text
254
+ forward = normalize(lookTarget - cameraPosition)
255
+ right = normalize(cross(forward, worldUp))
256
+ up = normalize(cross(right, forward))
257
+
258
+ staging center = cameraPosition + forward * 340
259
+ ```
260
+
261
+ Subject offsets are expressed in this shot basis. This is more robust than
262
+ tuning independent world coordinates after the camera is framed.
263
+
264
+ The launch shot instead hard-anchors to a rocket-relative orbit target every
265
+ frame. It intentionally avoids follow lag against a rapidly accelerating
266
+ subject.
267
+
268
+ ## Pointer-look and movement constraints
269
+
270
+ `PointerLookControls` uses Euler order `YXZ`, clamps pitch to
271
+ `±(π/2 - 0.01)`, and re-syncs yaw/pitch from the current camera quaternion
272
+ whenever pointer lock is acquired.
273
+
274
+ Movement:
275
+
276
+ ```text
277
+ forward = camera world direction
278
+ right = normalize(cross(forward, worldUp))
279
+ distance = movementSpeed * dt
280
+ ```
281
+
282
+ Default speed is `9`, sensitivity `0.0023`.
283
+
284
+ Keys are cleared on:
285
+
286
+ - pointer-lock exit;
287
+ - window blur;
288
+ - any update while unlocked.
289
+
290
+ Scene-specific constraints then run after controls:
291
+
292
+ - NASA room clamps X/Y/Z with floor, ceiling, and wall clearance.
293
+ - Mann’s Planet clamps camera Y above sampled terrain plus `0.2`.
294
+ - cinematic scenes block movement keys while retaining their authored camera.
295
+
296
+ Input control and spatial constraint are separate layers.
297
+
298
+ ## Floating origin and background handling
299
+
300
+ The Saturn scene first computes a virtual camera pose, stores its orientation
301
+ basis, then:
302
+
303
+ ```text
304
+ camera position = origin
305
+ Saturn group position = -virtualCameraPosition
306
+ atmosphere center uniform = Saturn group position
307
+ stars position = camera position
308
+ ```
309
+
310
+ The ship flyby is animated in the stored camera basis. This preserves the
311
+ authored composition while avoiding enormous camera coordinates.
312
+
313
+ Stars are tethered to the camera in multiple scenes to remove deep-space
314
+ parallax and prevent them from crossing the far envelope.
315
+
316
+ ## Projection and lifecycle ownership
317
+
318
+ planet-space implementation’s global camera uses:
319
+
320
+ ```text
321
+ FOV 38
322
+ near 0.2
323
+ far 3.0e7
324
+ ```
325
+
326
+ It prewarms pipelines by temporarily aiming at representative bodies, then
327
+ restores both position and quaternion in `finally`.
328
+
329
+ cinematic implementation’s scene manager:
330
+
331
+ ```text
332
+ dispose active scene
333
+ clear scene-root children
334
+ create next scene
335
+ await init
336
+ ```
337
+
338
+ Every scene that changes projection or background restores it on disposal.
339
+ This ownership prevents one shot’s lens from leaking into another.
340
+
341
+ ## Failure modes and diagnostics
342
+
343
+ Observed boundaries:
344
+
345
+ - planet-space implementation’s scalar spring is semi-implicit Euler; clamp `dt` during long frame
346
+ stalls.
347
+ - The chase mount’s final 180-degree correction depends on model conventions.
348
+ - Side-camera local/world ownership is easy to break when adapting the ship
349
+ hierarchy.
350
+ - Authored cinematic world coordinates are scene-specific; preserve the
351
+ camera-frame staging method, not literal positions.
352
+ - Hard camera anchoring is correct for launch composition but unsuitable when
353
+ inertial camera feel is the goal.
354
+ - Global-Y pointer movement is not valid for walking on a spherical planet.
355
+
356
+ Expose:
357
+
358
+ ```text
359
+ camera mode and owner
360
+ design-frame guides and subject screen bounds
361
+ camera local basis
362
+ body-up/tangent/right vectors
363
+ chase mount and thrust-lag distance/velocity
364
+ side/orbit target pose and blend
365
+ handoff start, target, t, and easing
366
+ FOV/near/far and depth precision
367
+ constraint contacts
368
+ floating-origin offset
369
+ camera-relative background state
370
+ ```
@@ -5,26 +5,35 @@ description: Build exposure and color grading for Genex Three.js games. Use for
5
5
 
6
6
  # Genex Three.js Exposure And Color Grading
7
7
 
8
- Exposure and grading are final image controls. Use them to preserve readability
9
- across gameplay, not to fix broken lighting.
8
+ Treat exposure, tone mapping, grading, and output conversion as distinct stages. Tune them from measured HDR signal, not by stacking compensating color operations.
10
9
 
11
- Read [references/exposure-grading.md](references/exposure-grading.md) for
12
- metering, tone mapping, LUTs, and validation.
10
+ ## Order
13
11
 
14
- ## Build order
12
+ ```text
13
+ HDR scene
14
+ → luminance meter
15
+ → adapted exposure
16
+ → tone map
17
+ → creative grade / 3D LUT
18
+ → final output conversion
19
+ ```
15
20
 
16
- 1. Confirm render color space, tone-mapping owner, and output transform.
17
- 2. Define target luminance range and adaptation speed.
18
- 3. Meter luminance from an intentionally downsampled or sampled source.
19
- 4. Apply exposure before tone mapping.
20
- 5. Apply grading after tone mapping if using a display-style LUT.
21
- 6. Expose debug views for luminance, exposure, tone map, LUT, and final output.
21
+ Read [references/exposure-grading.md](references/exposure-grading.md)
22
+ for the exact 64x36 meter, encoded readback, adaptation constants, 32-cube LUT,
23
+ and signal-ownership ambiguities.
22
24
 
23
- ## Rules
25
+ ## Failure conditions
24
26
 
25
- - Use one tone-mapping owner.
26
- - Keep adaptation asymmetric if bright-to-dark and dark-to-bright need different
27
- response.
28
- - Avoid grading that hides gameplay colors or UI states.
29
- - Validate bright, dark, indoor, outdoor, and VFX-heavy moments.
30
- - Document known artistic compromises.
27
+ - tone mapping occurs in both materials and post;
28
+ - exposure is used to repair physically inconsistent light ratios;
29
+ - meter weighting and scene framing are not inspected;
30
+ - adaptation speed is the same toward light and dark;
31
+ - LUT input/output spaces are undocumented;
32
+ - sRGB encoding happens twice;
33
+ - a display-domain LUT is moved before tone mapping without being rebuilt.
34
+
35
+ ## Routing boundary
36
+
37
+ Use `$genex-threejs-bloom` for HDR glow contribution and
38
+ `$genex-threejs-image-pipeline` when this color path must share ownership with AO,
39
+ atmosphere, or effect-local render targets.
@@ -1,30 +1,205 @@
1
- # Exposure And Color Grading
1
+ # Scene-referred exposure and color pipeline
2
2
 
3
- Use this reference for final image response.
3
+ Use this reference for a measured HDR-to-display path with encoded luminance readback, asymmetric adaptation, one tone-map owner, and a generated display-domain 3D LUT.
4
4
 
5
- ## Metering
5
+ ## Contents
6
6
 
7
- - Downsample luminance or sample a stable set of pixels.
8
- - Use log-average luminance for robust exposure.
9
- - Ignore or reduce weighting for UI and transient VFX when they should not drive
10
- exposure.
11
- - Clamp exposure range.
7
+ - Exact pipeline order
8
+ - Luminance meter
9
+ - Exposure target and adaptation
10
+ - 3D LUT construction
11
+ - LUT placement
12
+ - Tone mapping ownership
13
+ - Observed limitations
14
+ - Diagnostics
12
15
 
13
- ## Adaptation
14
16
 
15
- - Use different speeds for brightening and darkening when useful.
16
- - Smooth by `deltaTime`.
17
- - Reset or seed exposure intentionally on scene transitions.
17
+ ## Exact pipeline order
18
18
 
19
- ## Tone mapping
19
+ The pipeline computes:
20
20
 
21
- - Choose one tone mapping stage.
22
- - Place bloom before tone mapping.
23
- - Place display grading after tone mapping unless the project has a specific
24
- scene-referred grading path.
21
+ ```text
22
+ HDR scene after AO/atmosphere
23
+ -> bloom added in HDR
24
+ -> multiply by adapted exposure
25
+ -> renderOutput using renderer tone mapping
26
+ -> saturate to LUT domain
27
+ -> sample 3D LUT
28
+ -> blend LUT intensity
29
+ -> optional FXAA
30
+ ```
25
31
 
26
- ## Grading
32
+ `RenderPipeline.outputColorTransform` is disabled and one output node owns the
33
+ final conversion. Renderer tone-mapping mode and renderer exposure are still
34
+ configuration inputs to `renderOutput`.
27
35
 
28
- - Keep gameplay-critical colors distinguishable.
29
- - Provide a neutral grade toggle.
30
- - Validate captures on multiple scene lighting states.
36
+ ## Luminance meter
37
+
38
+ The implementation renders a `64 x 36` meter target using unsigned bytes. It encodes
39
+ unbounded luminance:
40
+
41
+ ```text
42
+ encoded = luminance / (luminance + 1)
43
+ decoded = encoded / max(0.0001, 1 - encoded)
44
+ ```
45
+
46
+ Readback occurs asynchronously every `12` frames by default. While one readback
47
+ is pending, another is not started.
48
+
49
+ CPU reduction uses weighted log average:
50
+
51
+ ```text
52
+ weight = 1.0 when luminance > 0.002
53
+ weight = 0.15 otherwise
54
+
55
+ average =
56
+ exp(sum(log(max(luminance, 0.0001)) * weight) / sum(weight))
57
+ ```
58
+
59
+ This suppresses black-pixel dominance without requiring a histogram.
60
+
61
+ ## Exposure target and adaptation
62
+
63
+ Defaults:
64
+
65
+ ```text
66
+ minimum exposure = 0.45
67
+ maximum exposure = 1.85
68
+ middle gray = 0.18
69
+ compensation = 0 EV
70
+ speed up = 3.2
71
+ speed down = 1.1
72
+ ```
73
+
74
+ Target:
75
+
76
+ ```text
77
+ target =
78
+ clamp(
79
+ middleGray / averageLuminance
80
+ * 2^exposureCompensation,
81
+ minExposure,
82
+ maxExposure
83
+ )
84
+ ```
85
+
86
+ Frame-rate-independent adaptation:
87
+
88
+ ```text
89
+ speed = target > current ? speedUp : speedDown
90
+ amount = 1 - exp(-max(deltaSeconds, 0) * speed)
91
+ current += (target - current) * amount
92
+ ```
93
+
94
+ When disabled, current and target reset to `1`.
95
+
96
+ ## 3D LUT construction
97
+
98
+ Build a `32^3` RGBA `Data3DTexture` with linear filtering, clamp wrapping, no
99
+ mipmaps, and unsigned-byte storage.
100
+
101
+ Each preset recipe owns:
102
+
103
+ ```text
104
+ contrast
105
+ saturation
106
+ vibrance
107
+ black/white point
108
+ per-channel gamma
109
+ shadow/midtone/highlight tint
110
+ strength for each tonal range
111
+ ```
112
+
113
+ Recipe order:
114
+
115
+ ```text
116
+ normalize black/white range
117
+ S-curve blend, fixed amount 0.44
118
+ contrast around 0.5
119
+ shadow tint
120
+ midtone tint
121
+ highlight tint
122
+ per-channel gamma
123
+ saturation
124
+ vibrance
125
+ small highlight glow bias
126
+ clamp to [0, 1]
127
+ ```
128
+
129
+ Tonal weights are calculated from pre-grade luminance:
130
+
131
+ ```text
132
+ shadow = 1 - smoothstep(0.12, 0.54, luma)
133
+ highlight = smoothstep(0.48, 0.92, luma)
134
+ midtone = max(0, 1 - abs(luma - 0.5) * 2)
135
+ ```
136
+
137
+ ## LUT placement
138
+
139
+ The LUT samples tone-mapped display-linear RGB after saturation:
140
+
141
+ ```text
142
+ uv = saturate(displayColor.rgb) * ((32 - 1) / 32) + 0.5 / 32
143
+ graded = texture3D(lut, uv)
144
+ final = mix(displayColor, graded, lutIntensity)
145
+ ```
146
+
147
+ This means the included recipes are authored for a bounded post-tone-map
148
+ domain. Do not move them before tone mapping without rebuilding the recipes and
149
+ documenting a scene-linear or log domain.
150
+
151
+ ## Tone mapping ownership
152
+
153
+ Available renderer modes include:
154
+
155
+ ```text
156
+ None, Linear, Reinhard, Cineon, ACES, AgX, Neutral
157
+ ```
158
+
159
+ Color defaults:
160
+
161
+ ```text
162
+ tone mapping = ACES
163
+ renderer exposure = 0.72
164
+ LUT = Real Daylight
165
+ LUT intensity = 1
166
+ ```
167
+
168
+ The feature factory initially disables LUT intensity and eye adaptation until
169
+ enabled through settings. Distinguish configuration defaults from active
170
+ feature state.
171
+
172
+ ## Observed limitations
173
+
174
+ - The meter has no center weighting, percentile clipping, sky mask, or UI mask.
175
+ - Unsigned-byte encoding loses precision near extreme luminance.
176
+ - Readback cadence is frame-count based, so wall-clock cadence changes with
177
+ frame rate.
178
+ - A failed readback resets target exposure to `1`, which can cause a visible
179
+ adaptation shift.
180
+ - LUT generation clamps every entry to `[0,1]`; it is display-domain grading,
181
+ not HDR scene-referred grading.
182
+ - The pipeline exposes both renderer `toneMappingExposure` and a separate
183
+ adapted exposure multiplier. Their combined ownership must be documented to
184
+ avoid accidental double exposure.
185
+ - FXAA is applied after grading, but dithering/gamut compression are absent.
186
+
187
+ ## Diagnostics
188
+
189
+ Expose:
190
+
191
+ ```text
192
+ meter source
193
+ encoded meter target
194
+ decoded luminance
195
+ weight mask
196
+ measured average
197
+ target/current exposure over time
198
+ readback pending and cadence
199
+ HDR before exposure
200
+ tone-mapped before LUT
201
+ neutral versus selected LUT
202
+ per-recipe tonal weights
203
+ clipped/out-of-domain mask
204
+ final with one exposure stage disabled at a time
205
+ ```