@genex-ai/cli-demo 0.6.0 → 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.
- package/dist/index.js +5 -0
- package/package.json +1 -1
- package/templates/skills/genex-threejs-atmosphere-aerial-perspective/SKILL.md +30 -18
- package/templates/skills/genex-threejs-atmosphere-aerial-perspective/references/atmosphere.md +204 -20
- package/templates/skills/genex-threejs-bloom/SKILL.md +29 -18
- package/templates/skills/genex-threejs-bloom/references/bloom.md +176 -20
- package/templates/skills/genex-threejs-camera-direction/SKILL.md +38 -26
- package/templates/skills/genex-threejs-camera-direction/references/camera-rigs.md +359 -27
- package/templates/skills/genex-threejs-exposure-color-grading/SKILL.md +27 -18
- package/templates/skills/genex-threejs-exposure-color-grading/references/exposure-grading.md +196 -21
- package/templates/skills/genex-threejs-image-pipeline/SKILL.md +38 -17
- package/templates/skills/genex-threejs-image-pipeline/references/image-pipeline.md +185 -29
- package/templates/skills/genex-threejs-procedural-animation/SKILL.md +34 -21
- package/templates/skills/genex-threejs-procedural-animation/references/procedural-motion.md +353 -24
- package/templates/skills/genex-threejs-procedural-architecture/SKILL.md +36 -17
- package/templates/skills/genex-threejs-procedural-architecture/references/architecture-systems.md +500 -22
- package/templates/skills/genex-threejs-procedural-fields/SKILL.md +59 -24
- package/templates/skills/genex-threejs-procedural-fields/references/field-systems.md +222 -25
- package/templates/skills/genex-threejs-procedural-geometry/SKILL.md +34 -20
- package/templates/skills/genex-threejs-procedural-geometry/references/mesh-systems.md +192 -26
- package/templates/skills/genex-threejs-procedural-materials/SKILL.md +55 -18
- package/templates/skills/genex-threejs-procedural-materials/references/material-systems.md +189 -22
- package/templates/skills/genex-threejs-procedural-planets/SKILL.md +36 -18
- package/templates/skills/genex-threejs-procedural-planets/references/planet-systems.md +489 -21
- package/templates/skills/genex-threejs-procedural-vegetation/SKILL.md +35 -25
- package/templates/skills/genex-threejs-procedural-vegetation/references/vegetation-systems.md +304 -27
- package/templates/skills/genex-threejs-procedural-vfx/SKILL.md +26 -18
- package/templates/skills/genex-threejs-procedural-vfx/references/vfx-systems.md +208 -20
- package/templates/skills/genex-threejs-raymarched-space-effects/SKILL.md +25 -18
- package/templates/skills/genex-threejs-raymarched-space-effects/references/space-effects.md +185 -21
- package/templates/skills/genex-threejs-screen-space-ambient-occlusion/SKILL.md +23 -17
- package/templates/skills/genex-threejs-screen-space-ambient-occlusion/references/ambient-occlusion.md +430 -20
- package/templates/skills/genex-threejs-shadow-systems/SKILL.md +29 -18
- package/templates/skills/genex-threejs-shadow-systems/references/shadow-systems.md +420 -21
- package/templates/skills/genex-threejs-skill-router/SKILL.md +21 -21
- package/templates/skills/genex-threejs-spectral-ocean/SKILL.md +30 -20
- package/templates/skills/genex-threejs-spectral-ocean/references/spectral-ocean.md +462 -22
- package/templates/skills/genex-threejs-temporal-surfaces/SKILL.md +26 -17
- package/templates/skills/genex-threejs-temporal-surfaces/references/temporal-surfaces.md +198 -20
- package/templates/skills/genex-threejs-visual-validation/SKILL.md +34 -18
- package/templates/skills/genex-threejs-visual-validation/references/visual-validation.md +396 -32
- package/templates/skills/genex-threejs-volumetric-clouds/SKILL.md +33 -17
- package/templates/skills/genex-threejs-volumetric-clouds/references/volumetric-clouds.md +570 -21
- package/templates/skills/genex-threejs-water-optics/SKILL.md +33 -18
- package/templates/skills/genex-threejs-water-optics/references/water-optics.md +184 -20
|
@@ -1,38 +1,370 @@
|
|
|
1
|
-
# Camera
|
|
1
|
+
# Camera rig and cinematic systems
|
|
2
2
|
|
|
3
|
-
Use
|
|
3
|
+
Use this reference for scale-aware chase, side, orbit, authored-shot, pointer-look, floating-origin, projection, and lifecycle camera systems.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Contents
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
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
|
-
|
|
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
|
-
|
|
21
|
+
Record before implementation:
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
36
|
+
Do not combine modes until each can produce a valid position and quaternion
|
|
37
|
+
independently.
|
|
28
38
|
|
|
29
|
-
-
|
|
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
|
-
|
|
41
|
+
`CameraRigSystem` derives the chase mount from ship length:
|
|
34
42
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
|
|
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
|
-
|
|
12
|
-
metering, tone mapping, LUTs, and validation.
|
|
10
|
+
## Order
|
|
13
11
|
|
|
14
|
-
|
|
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
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
##
|
|
25
|
+
## Failure conditions
|
|
24
26
|
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
30
|
-
-
|
|
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.
|
package/templates/skills/genex-threejs-exposure-color-grading/references/exposure-grading.md
CHANGED
|
@@ -1,30 +1,205 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Scene-referred exposure and color pipeline
|
|
2
2
|
|
|
3
|
-
Use this reference for
|
|
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
|
-
##
|
|
5
|
+
## Contents
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
|
|
11
|
-
-
|
|
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
|
-
|
|
16
|
-
- Smooth by `deltaTime`.
|
|
17
|
-
- Reset or seed exposure intentionally on scene transitions.
|
|
17
|
+
## Exact pipeline order
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
The pipeline computes:
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
+
```
|