@plasius/gpu-lighting 0.2.6 → 0.2.8
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.md +32 -1
- package/README.md +61 -0
- package/dist/index.cjs +333 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +325 -7
- package/dist/index.js.map +1 -1
- package/dist/techniques/techniques/wavefront/prelude.wgsl +8 -0
- package/package.json +1 -1
- package/src/index.js +361 -7
- package/src/techniques/wavefront/prelude.wgsl +8 -0
package/CHANGELOG.md
CHANGED
|
@@ -20,6 +20,36 @@ The format is based on **[Keep a Changelog](https://keepachangelog.com/en/1.1.0/
|
|
|
20
20
|
- **Security**
|
|
21
21
|
- (placeholder)
|
|
22
22
|
|
|
23
|
+
## [0.2.8] - 2026-06-20
|
|
24
|
+
|
|
25
|
+
- **Added**
|
|
26
|
+
- Extended the wavefront lighting contract with compact medium-state carry,
|
|
27
|
+
visibility-probe ray helpers, MIS/exclusive-emissive probe controls, and
|
|
28
|
+
deterministic CPU reference fixtures for continuation validation.
|
|
29
|
+
|
|
30
|
+
- **Changed**
|
|
31
|
+
- Wavefront ray-record documentation now mirrors the current renderer payload
|
|
32
|
+
shape, including medium-stack and spectral-state fields used for transport
|
|
33
|
+
validation.
|
|
34
|
+
- The Eames validation page now defaults display-quality captures to
|
|
35
|
+
`accelerationBuildMode=cpu-upload` while still allowing explicit GPU BVH
|
|
36
|
+
validation through the query parameter.
|
|
37
|
+
|
|
38
|
+
- **Fixed**
|
|
39
|
+
- Wavefront continuation helpers now report total internal reflection
|
|
40
|
+
explicitly and keep refraction/transparency medium transitions stable in the
|
|
41
|
+
published reference contract.
|
|
42
|
+
- The Eames validation mesh transform now floor-aligns scaled product meshes
|
|
43
|
+
by their actual lower bound instead of centering them through the analytic
|
|
44
|
+
floor plane, preventing chair geometry from rendering mostly below ground.
|
|
45
|
+
- Eames validation captures at 4 SPP and 8 SPP no longer depend on the
|
|
46
|
+
corrupted display-quality CPU-upload material path or the broken high-SPP
|
|
47
|
+
tile scheduling order, so browser-driven runtime screenshots now render the
|
|
48
|
+
chair coherently instead of producing striped/blocked artifact regions.
|
|
49
|
+
|
|
50
|
+
- **Security**
|
|
51
|
+
- (placeholder)
|
|
52
|
+
|
|
23
53
|
## [0.2.6] - 2026-06-16
|
|
24
54
|
|
|
25
55
|
- **Added**
|
|
@@ -475,7 +505,8 @@ The format is based on **[Keep a Changelog](https://keepachangelog.com/en/1.1.0/
|
|
|
475
505
|
[0.1.16]: https://github.com/Plasius-LTD/gpu-lighting/releases/tag/v0.1.16
|
|
476
506
|
[0.1.17]: https://github.com/Plasius-LTD/gpu-lighting/releases/tag/v0.1.17
|
|
477
507
|
[0.1.19]: https://github.com/Plasius-LTD/gpu-lighting/releases/tag/v0.1.19
|
|
478
|
-
[Unreleased]: https://github.com/Plasius-LTD/gpu-lighting/compare/v0.2.
|
|
508
|
+
[Unreleased]: https://github.com/Plasius-LTD/gpu-lighting/compare/v0.2.8...HEAD
|
|
479
509
|
[0.2.0]: https://github.com/Plasius-LTD/gpu-lighting/releases/tag/v0.2.0
|
|
480
510
|
[0.2.2]: https://github.com/Plasius-LTD/gpu-lighting/releases/tag/v0.2.2
|
|
481
511
|
[0.2.6]: https://github.com/Plasius-LTD/gpu-lighting/releases/tag/v0.2.6
|
|
512
|
+
[0.2.8]: https://github.com/Plasius-LTD/gpu-lighting/releases/tag/v0.2.8
|
package/README.md
CHANGED
|
@@ -173,7 +173,11 @@ radiance accumulation and continuation scattering.
|
|
|
173
173
|
```js
|
|
174
174
|
import {
|
|
175
175
|
createWavefrontLightingPlan,
|
|
176
|
+
createWavefrontReferenceFixture,
|
|
177
|
+
createWavefrontVisibilityProbeRay,
|
|
178
|
+
evaluateWavefrontMaterialReference,
|
|
176
179
|
evaluateWavefrontTerminalRadiance,
|
|
180
|
+
evaluateWavefrontVisibilityProbe,
|
|
177
181
|
loadLightingTechniqueWorkerBundle,
|
|
178
182
|
} from "@plasius/gpu-lighting";
|
|
179
183
|
|
|
@@ -181,6 +185,7 @@ const plan = createWavefrontLightingPlan({
|
|
|
181
185
|
maxDepth: 6,
|
|
182
186
|
queueCapacity: 4096,
|
|
183
187
|
explicitLightSampling: true,
|
|
188
|
+
visibilityProbeMode: "mis-balanced",
|
|
184
189
|
});
|
|
185
190
|
|
|
186
191
|
const bundle = await loadLightingTechniqueWorkerBundle("wavefront");
|
|
@@ -189,16 +194,72 @@ const emissive = evaluateWavefrontTerminalRadiance({
|
|
|
189
194
|
throughput: [0.5, 0.5, 0.5],
|
|
190
195
|
emission: [8, 6, 4],
|
|
191
196
|
});
|
|
197
|
+
const material = evaluateWavefrontMaterialReference({
|
|
198
|
+
hitType: "surface",
|
|
199
|
+
eventKind: "refraction",
|
|
200
|
+
throughput: [1, 1, 1],
|
|
201
|
+
transmission: [0.92, 0.95, 0.98],
|
|
202
|
+
ior: 1.45,
|
|
203
|
+
shadingNormal: [0, 1, 0],
|
|
204
|
+
viewDirection: [0, 1, 0],
|
|
205
|
+
currentMediumRefId: 0,
|
|
206
|
+
surfaceMediumRefId: 7,
|
|
207
|
+
});
|
|
208
|
+
const probeRay = createWavefrontVisibilityProbeRay({
|
|
209
|
+
rayId: 12,
|
|
210
|
+
parentRayId: 4,
|
|
211
|
+
sourcePixelId: 9,
|
|
212
|
+
sampleId: 2,
|
|
213
|
+
bounce: 1,
|
|
214
|
+
origin: [0, 1, 0],
|
|
215
|
+
direction: [0.25, -1, 0.15],
|
|
216
|
+
throughput: [0.8, 0.7, 0.6],
|
|
217
|
+
mediumRefId: 7,
|
|
218
|
+
mediumStack: [7],
|
|
219
|
+
});
|
|
220
|
+
const probe = evaluateWavefrontVisibilityProbe({
|
|
221
|
+
probeRay,
|
|
222
|
+
probeMode: "exclusive-emissive",
|
|
223
|
+
activeEmissiveRadiance: [4, 3, 2],
|
|
224
|
+
emissiveRadiance: [4, 3, 2],
|
|
225
|
+
transparentSegments: [[0.8, 0.8, 0.8]],
|
|
226
|
+
});
|
|
227
|
+
const fixture = createWavefrontReferenceFixture({
|
|
228
|
+
hitType: "emissive",
|
|
229
|
+
throughput: [0.8, 0.7, 0.6],
|
|
230
|
+
emission: [4, 3, 2],
|
|
231
|
+
visibilityProbe: {
|
|
232
|
+
probeMode: "mis-balanced",
|
|
233
|
+
emissiveRadiance: [0.6, 0.3, 0.1],
|
|
234
|
+
transparentSegments: [[0.7, 0.8, 0.9]],
|
|
235
|
+
},
|
|
236
|
+
});
|
|
192
237
|
|
|
193
238
|
console.log(plan.requiredRendererPassOrder);
|
|
239
|
+
console.log(plan.visibilityProbeMode);
|
|
194
240
|
console.log(bundle.jobs.map((job) => job.label));
|
|
195
241
|
console.log(emissive.radiance);
|
|
242
|
+
console.log(material.continuation.mediumState);
|
|
243
|
+
console.log(probe.doubleCountPrevented);
|
|
244
|
+
console.log(fixture.tolerance);
|
|
196
245
|
```
|
|
197
246
|
|
|
198
247
|
This slice keeps emissive hits, environment hits, and environment-miss dark
|
|
199
248
|
fallbacks on the lighting package surface without reintroducing a depth-first
|
|
200
249
|
shader dependency into the renderer-owned wavefront queue model.
|
|
201
250
|
|
|
251
|
+
The continuation/reference surface now also exposes:
|
|
252
|
+
|
|
253
|
+
- compact medium-state carry for refraction/transparency events, including
|
|
254
|
+
total-internal-reflection fallback reporting
|
|
255
|
+
- shared-ray payload helpers where visibility probes reuse the base ray layout
|
|
256
|
+
and encode their kind through the low bits of `flags`
|
|
257
|
+
- optional probe contribution helpers with `mis-balanced` and
|
|
258
|
+
`exclusive-emissive` modes so active emissive hits remain correct even when
|
|
259
|
+
explicit light sampling is enabled
|
|
260
|
+
- deterministic reference fixtures that publish buffer-like accumulation outputs
|
|
261
|
+
with a documented default tolerance of `0.0005` for CPU-vs-GPU comparisons
|
|
262
|
+
|
|
202
263
|
## Distance-Banded Lighting
|
|
203
264
|
|
|
204
265
|
```js
|
package/dist/index.cjs
CHANGED
|
@@ -34,11 +34,17 @@ __export(index_exports, {
|
|
|
34
34
|
createLightingProfileModeLadder: () => createLightingProfileModeLadder,
|
|
35
35
|
createWavefrontEnvironmentLightingOptions: () => createWavefrontEnvironmentLightingOptions,
|
|
36
36
|
createWavefrontLightingPlan: () => createWavefrontLightingPlan,
|
|
37
|
+
createWavefrontRayPayload: () => createWavefrontRayPayload,
|
|
38
|
+
createWavefrontReferenceFixture: () => createWavefrontReferenceFixture,
|
|
39
|
+
createWavefrontVisibilityProbeRay: () => createWavefrontVisibilityProbeRay,
|
|
37
40
|
defaultAdaptiveLightingProfilePolicy: () => defaultAdaptiveLightingProfilePolicy,
|
|
38
41
|
defaultLightingProfile: () => defaultLightingProfile,
|
|
39
42
|
defaultLightingTechnique: () => defaultLightingTechnique,
|
|
40
43
|
evaluateWavefrontContinuationEvent: () => evaluateWavefrontContinuationEvent,
|
|
44
|
+
evaluateWavefrontMaterialReference: () => evaluateWavefrontMaterialReference,
|
|
45
|
+
evaluateWavefrontMediumState: () => evaluateWavefrontMediumState,
|
|
41
46
|
evaluateWavefrontTerminalRadiance: () => evaluateWavefrontTerminalRadiance,
|
|
47
|
+
evaluateWavefrontVisibilityProbe: () => evaluateWavefrontVisibilityProbe,
|
|
42
48
|
getLightingProfile: () => getLightingProfile,
|
|
43
49
|
getLightingProfileWorkerManifest: () => getLightingProfileWorkerManifest,
|
|
44
50
|
getLightingTechnique: () => getLightingTechnique,
|
|
@@ -65,9 +71,11 @@ __export(index_exports, {
|
|
|
65
71
|
lightingWavefrontHitTypes: () => lightingWavefrontHitTypes,
|
|
66
72
|
lightingWavefrontPassOrder: () => lightingWavefrontPassOrder,
|
|
67
73
|
lightingWavefrontQueuePairStrategy: () => lightingWavefrontQueuePairStrategy,
|
|
74
|
+
lightingWavefrontRayKinds: () => lightingWavefrontRayKinds,
|
|
68
75
|
lightingWavefrontSchemaVersion: () => lightingWavefrontSchemaVersion,
|
|
69
76
|
lightingWavefrontTerminalHitTypes: () => lightingWavefrontTerminalHitTypes,
|
|
70
77
|
lightingWavefrontTerminationPolicy: () => lightingWavefrontTerminationPolicy,
|
|
78
|
+
lightingWavefrontVisibilityProbeModes: () => lightingWavefrontVisibilityProbeModes,
|
|
71
79
|
lightingWorkerManifests: () => lightingWorkerManifests,
|
|
72
80
|
lightingWorkerQueueClass: () => lightingWorkerQueueClass,
|
|
73
81
|
loadLightingJobs: () => loadLightingJobs,
|
|
@@ -1137,6 +1145,15 @@ var lightingWavefrontHitTypes = Object.freeze([
|
|
|
1137
1145
|
"transparent",
|
|
1138
1146
|
"miss"
|
|
1139
1147
|
]);
|
|
1148
|
+
var lightingWavefrontRayKinds = Object.freeze([
|
|
1149
|
+
"path",
|
|
1150
|
+
"visibility-probe"
|
|
1151
|
+
]);
|
|
1152
|
+
var lightingWavefrontVisibilityProbeModes = Object.freeze([
|
|
1153
|
+
"disabled",
|
|
1154
|
+
"mis-balanced",
|
|
1155
|
+
"exclusive-emissive"
|
|
1156
|
+
]);
|
|
1140
1157
|
var lightingWavefrontTerminalHitTypes = Object.freeze([
|
|
1141
1158
|
"emissive",
|
|
1142
1159
|
"environment",
|
|
@@ -1181,11 +1198,35 @@ var lightingWavefrontBufferContracts = Object.freeze({
|
|
|
1181
1198
|
createLightingWavefrontField("sourcePixelId", "u32", "Source pixel/sample owner."),
|
|
1182
1199
|
createLightingWavefrontField("sampleId", "u32", "Per-pixel sample slot."),
|
|
1183
1200
|
createLightingWavefrontField("bounce", "u32", "Current bounce depth."),
|
|
1201
|
+
createLightingWavefrontField("mediumRefId", "u32", "Current medium reference."),
|
|
1202
|
+
createLightingWavefrontField(
|
|
1203
|
+
"mediumStackDepth",
|
|
1204
|
+
"u32",
|
|
1205
|
+
"Depth of the bounded nested medium stack."
|
|
1206
|
+
),
|
|
1207
|
+
createLightingWavefrontField(
|
|
1208
|
+
"flags",
|
|
1209
|
+
"u32",
|
|
1210
|
+
"Renderer-owned ray flags. Low bits may encode ray kind metadata."
|
|
1211
|
+
),
|
|
1212
|
+
createLightingWavefrontField(
|
|
1213
|
+
"mediumStack0",
|
|
1214
|
+
"vec4<u32>",
|
|
1215
|
+
"Lower half of the bounded nested medium stack."
|
|
1216
|
+
),
|
|
1217
|
+
createLightingWavefrontField(
|
|
1218
|
+
"mediumStack1",
|
|
1219
|
+
"vec4<u32>",
|
|
1220
|
+
"Upper half of the bounded nested medium stack."
|
|
1221
|
+
),
|
|
1222
|
+
createLightingWavefrontField(
|
|
1223
|
+
"spectralState",
|
|
1224
|
+
"vec4<f32>",
|
|
1225
|
+
"Spectral transport payload for wavelength-driven reference validation."
|
|
1226
|
+
),
|
|
1184
1227
|
createLightingWavefrontField("origin", "vec3<f32>", "Ray origin."),
|
|
1185
1228
|
createLightingWavefrontField("direction", "vec3<f32>", "Normalized ray direction."),
|
|
1186
|
-
createLightingWavefrontField("throughput", "vec3<f32>", "Accumulated path throughput.")
|
|
1187
|
-
createLightingWavefrontField("mediumRefId", "u32", "Current medium reference."),
|
|
1188
|
-
createLightingWavefrontField("flags", "u32", "Renderer-owned ray flags.")
|
|
1229
|
+
createLightingWavefrontField("throughput", "vec3<f32>", "Accumulated path throughput.")
|
|
1189
1230
|
]
|
|
1190
1231
|
),
|
|
1191
1232
|
hit: createLightingWavefrontRecordContract(
|
|
@@ -1274,6 +1315,17 @@ var wavefrontEventKinds = Object.freeze([
|
|
|
1274
1315
|
"transparency",
|
|
1275
1316
|
"terminate"
|
|
1276
1317
|
]);
|
|
1318
|
+
var wavefrontRayKindFlagMask = 3;
|
|
1319
|
+
var wavefrontRayKindFlagValues = Object.freeze({
|
|
1320
|
+
path: 0,
|
|
1321
|
+
"visibility-probe": 1
|
|
1322
|
+
});
|
|
1323
|
+
function normalizeWavefrontRayKind(value) {
|
|
1324
|
+
return lightingWavefrontRayKinds.includes(value) ? value : "path";
|
|
1325
|
+
}
|
|
1326
|
+
function normalizeWavefrontVisibilityProbeMode(value) {
|
|
1327
|
+
return lightingWavefrontVisibilityProbeModes.includes(value) ? value : "disabled";
|
|
1328
|
+
}
|
|
1277
1329
|
function normalizeWavefrontHitType(value) {
|
|
1278
1330
|
return lightingWavefrontHitTypes.includes(value) ? value : "surface";
|
|
1279
1331
|
}
|
|
@@ -1346,6 +1398,118 @@ function refractDirection(direction, normal, etaRatio) {
|
|
|
1346
1398
|
const rOutParallel = scaleVec3(normal, -Math.sqrt(parallelFactor));
|
|
1347
1399
|
return normalizeDirection(addVec3(rOutPerp, rOutParallel), direction);
|
|
1348
1400
|
}
|
|
1401
|
+
function normalizeMediumRefId(value) {
|
|
1402
|
+
const mediumRefId = Math.max(0, Math.trunc(readFinite(value, 0)));
|
|
1403
|
+
return Number.isFinite(mediumRefId) ? mediumRefId : 0;
|
|
1404
|
+
}
|
|
1405
|
+
function normalizeMediumStack(value) {
|
|
1406
|
+
if (!Array.isArray(value)) {
|
|
1407
|
+
return [];
|
|
1408
|
+
}
|
|
1409
|
+
return value.map((entry) => normalizeMediumRefId(entry)).filter((entry, index, stack) => entry > 0 && stack.indexOf(entry) === index).slice(0, 4);
|
|
1410
|
+
}
|
|
1411
|
+
function createWavefrontMediumStatePayload(currentMediumRefId, stack) {
|
|
1412
|
+
const normalizedStack = normalizeMediumStack(stack);
|
|
1413
|
+
const stackSlots = [0, 0, 0, 0];
|
|
1414
|
+
normalizedStack.forEach((entry, index) => {
|
|
1415
|
+
stackSlots[index] = entry;
|
|
1416
|
+
});
|
|
1417
|
+
return Object.freeze({
|
|
1418
|
+
currentMediumRefId,
|
|
1419
|
+
stackDepth: normalizedStack.length,
|
|
1420
|
+
stack: Object.freeze(normalizedStack),
|
|
1421
|
+
stackSlots: Object.freeze(stackSlots)
|
|
1422
|
+
});
|
|
1423
|
+
}
|
|
1424
|
+
function evaluateWavefrontMediumState(options = {}) {
|
|
1425
|
+
const currentMediumRefId = normalizeMediumRefId(
|
|
1426
|
+
options.currentMediumRefId ?? options.mediumRefId
|
|
1427
|
+
);
|
|
1428
|
+
const surfaceMediumRefId = normalizeMediumRefId(options.surfaceMediumRefId);
|
|
1429
|
+
const stack = normalizeMediumStack(options.mediumStack);
|
|
1430
|
+
const frontFace = options.frontFace !== false;
|
|
1431
|
+
const eventKind = normalizeWavefrontEventKind(options.eventKind) ?? "transparency";
|
|
1432
|
+
if (surfaceMediumRefId === 0 || eventKind !== "refraction" && eventKind !== "transparency") {
|
|
1433
|
+
return Object.freeze({
|
|
1434
|
+
...createWavefrontMediumStatePayload(currentMediumRefId, stack),
|
|
1435
|
+
enteredMediumRefId: 0,
|
|
1436
|
+
exitedMediumRefId: 0
|
|
1437
|
+
});
|
|
1438
|
+
}
|
|
1439
|
+
let nextStack = [...stack];
|
|
1440
|
+
let nextMediumRefId = currentMediumRefId;
|
|
1441
|
+
let enteredMediumRefId = 0;
|
|
1442
|
+
let exitedMediumRefId = 0;
|
|
1443
|
+
const stackTop = nextStack.at(-1) ?? 0;
|
|
1444
|
+
if (frontFace) {
|
|
1445
|
+
if (stackTop !== surfaceMediumRefId) {
|
|
1446
|
+
nextStack.push(surfaceMediumRefId);
|
|
1447
|
+
nextStack = nextStack.slice(-4);
|
|
1448
|
+
}
|
|
1449
|
+
nextMediumRefId = surfaceMediumRefId;
|
|
1450
|
+
enteredMediumRefId = surfaceMediumRefId;
|
|
1451
|
+
} else if (stackTop === surfaceMediumRefId) {
|
|
1452
|
+
nextStack.pop();
|
|
1453
|
+
nextMediumRefId = nextStack.at(-1) ?? 0;
|
|
1454
|
+
exitedMediumRefId = surfaceMediumRefId;
|
|
1455
|
+
}
|
|
1456
|
+
return Object.freeze({
|
|
1457
|
+
...createWavefrontMediumStatePayload(nextMediumRefId, nextStack),
|
|
1458
|
+
enteredMediumRefId,
|
|
1459
|
+
exitedMediumRefId
|
|
1460
|
+
});
|
|
1461
|
+
}
|
|
1462
|
+
function encodeWavefrontRayFlags(flags, rayKind) {
|
|
1463
|
+
const normalizedFlags = Math.max(0, Math.trunc(readFinite(flags, 0)));
|
|
1464
|
+
const rayKindValue = wavefrontRayKindFlagValues[rayKind];
|
|
1465
|
+
return normalizedFlags & ~wavefrontRayKindFlagMask | rayKindValue;
|
|
1466
|
+
}
|
|
1467
|
+
function createWavefrontRayPayload(options = {}) {
|
|
1468
|
+
const rayKind = normalizeWavefrontRayKind(options.rayKind);
|
|
1469
|
+
const mediumState = evaluateWavefrontMediumState({
|
|
1470
|
+
currentMediumRefId: options.mediumRefId,
|
|
1471
|
+
mediumStack: options.mediumStack
|
|
1472
|
+
});
|
|
1473
|
+
const spectralState = Object.freeze(
|
|
1474
|
+
normalizeVec3(options.spectralState, [550, 1, 0]).concat(
|
|
1475
|
+
readFinite(options.spectralWeight, 0)
|
|
1476
|
+
)
|
|
1477
|
+
);
|
|
1478
|
+
const mediumStack0 = Object.freeze([
|
|
1479
|
+
mediumState.stackSlots[0],
|
|
1480
|
+
mediumState.stackSlots[1],
|
|
1481
|
+
mediumState.stackSlots[2],
|
|
1482
|
+
mediumState.stackSlots[3]
|
|
1483
|
+
]);
|
|
1484
|
+
const mediumStack1 = Object.freeze([0, 0, 0, 0]);
|
|
1485
|
+
return Object.freeze({
|
|
1486
|
+
rayId: Math.max(0, Math.trunc(readFinite(options.rayId, 0))),
|
|
1487
|
+
parentRayId: Math.max(0, Math.trunc(readFinite(options.parentRayId, 0))),
|
|
1488
|
+
sourcePixelId: Math.max(0, Math.trunc(readFinite(options.sourcePixelId, 0))),
|
|
1489
|
+
sampleId: Math.max(0, Math.trunc(readFinite(options.sampleId, 0))),
|
|
1490
|
+
bounce: Math.max(0, Math.trunc(readFinite(options.bounce, 0))),
|
|
1491
|
+
origin: Object.freeze(normalizeVec3(options.origin, [0, 0, 0])),
|
|
1492
|
+
direction: Object.freeze(normalizeDirection(options.direction, [0, 0, -1])),
|
|
1493
|
+
throughput: Object.freeze(
|
|
1494
|
+
saturateVec3(normalizeVec3(options.throughput, [1, 1, 1]))
|
|
1495
|
+
),
|
|
1496
|
+
mediumRefId: mediumState.currentMediumRefId,
|
|
1497
|
+
mediumStackDepth: mediumState.stackDepth,
|
|
1498
|
+
mediumStack: mediumState.stack,
|
|
1499
|
+
mediumStackSlots: mediumState.stackSlots,
|
|
1500
|
+
mediumStack0,
|
|
1501
|
+
mediumStack1,
|
|
1502
|
+
spectralState,
|
|
1503
|
+
rayKind,
|
|
1504
|
+
flags: encodeWavefrontRayFlags(options.flags, rayKind)
|
|
1505
|
+
});
|
|
1506
|
+
}
|
|
1507
|
+
function createWavefrontVisibilityProbeRay(options = {}) {
|
|
1508
|
+
return createWavefrontRayPayload({
|
|
1509
|
+
...options,
|
|
1510
|
+
rayKind: "visibility-probe"
|
|
1511
|
+
});
|
|
1512
|
+
}
|
|
1349
1513
|
function createWavefrontLightingPlan(options = {}) {
|
|
1350
1514
|
const maxDepth = Math.max(1, Math.trunc(readFinite(options.maxDepth, 4)));
|
|
1351
1515
|
const queueCapacity = Math.max(
|
|
@@ -1353,6 +1517,9 @@ function createWavefrontLightingPlan(options = {}) {
|
|
|
1353
1517
|
Math.trunc(readFinite(options.queueCapacity, 4096))
|
|
1354
1518
|
);
|
|
1355
1519
|
const explicitLightSampling = Boolean(options.explicitLightSampling);
|
|
1520
|
+
const visibilityProbeMode = normalizeWavefrontVisibilityProbeMode(
|
|
1521
|
+
options.visibilityProbeMode ?? (explicitLightSampling ? "mis-balanced" : "disabled")
|
|
1522
|
+
);
|
|
1356
1523
|
const accumulationResetEpoch = Math.max(
|
|
1357
1524
|
0,
|
|
1358
1525
|
Math.trunc(readFinite(options.accumulationResetEpoch, 0))
|
|
@@ -1362,6 +1529,8 @@ function createWavefrontLightingPlan(options = {}) {
|
|
|
1362
1529
|
maxDepth,
|
|
1363
1530
|
queueCapacity,
|
|
1364
1531
|
explicitLightSampling,
|
|
1532
|
+
visibilityProbeMode,
|
|
1533
|
+
rayKinds: lightingWavefrontRayKinds,
|
|
1365
1534
|
accumulationResetEpoch,
|
|
1366
1535
|
queueLayout: Object.freeze({
|
|
1367
1536
|
strategy: lightingWavefrontQueuePairStrategy,
|
|
@@ -1400,7 +1569,8 @@ function createWavefrontLightingPlan(options = {}) {
|
|
|
1400
1569
|
]),
|
|
1401
1570
|
writes: Object.freeze(["ray"]),
|
|
1402
1571
|
continuationHitTypes: lightingWavefrontContinuationHitTypes,
|
|
1403
|
-
explicitLightSampling
|
|
1572
|
+
explicitLightSampling,
|
|
1573
|
+
visibilityProbeMode
|
|
1404
1574
|
})
|
|
1405
1575
|
])
|
|
1406
1576
|
});
|
|
@@ -1460,7 +1630,9 @@ function evaluateWavefrontContinuationEvent(options = {}) {
|
|
|
1460
1630
|
const opacity = clampUnit(options.opacity, 1);
|
|
1461
1631
|
const refractiveIndex = Math.max(1, readFinite(options.refractiveIndex ?? options.ior, 1.45));
|
|
1462
1632
|
const transmissionStrength = Math.max(...transmission);
|
|
1463
|
-
|
|
1633
|
+
const requestedEventKind = normalizeWavefrontEventKind(options.eventKind) ?? (hitType === "transparent" || opacity < 0.999 ? "transparency" : transmissionStrength > 1e-3 ? "refraction" : metalness >= 0.5 || roughness <= 0.2 ? "reflection" : "diffuse");
|
|
1634
|
+
let eventKind = requestedEventKind;
|
|
1635
|
+
let totalInternalReflection = false;
|
|
1464
1636
|
let nextDirection = incomingDirection;
|
|
1465
1637
|
let attenuation;
|
|
1466
1638
|
if (!lightingWavefrontContinuationHitTypes.includes(hitType) || bounceIndex >= maxDepth - 1) {
|
|
@@ -1471,8 +1643,20 @@ function evaluateWavefrontContinuationEvent(options = {}) {
|
|
|
1471
1643
|
attenuation = mixVec3([0.04, 0.04, 0.04], albedo, metalness);
|
|
1472
1644
|
} else if (eventKind === "refraction") {
|
|
1473
1645
|
const etaRatio = frontFace ? 1 / refractiveIndex : refractiveIndex;
|
|
1474
|
-
|
|
1475
|
-
|
|
1646
|
+
const refractedDirection = refractDirection(
|
|
1647
|
+
incomingDirection,
|
|
1648
|
+
orientedNormal,
|
|
1649
|
+
etaRatio
|
|
1650
|
+
);
|
|
1651
|
+
if (refractedDirection) {
|
|
1652
|
+
nextDirection = refractedDirection;
|
|
1653
|
+
attenuation = transmissionStrength > 1e-3 ? transmission : [1, 1, 1];
|
|
1654
|
+
} else {
|
|
1655
|
+
totalInternalReflection = true;
|
|
1656
|
+
eventKind = "reflection";
|
|
1657
|
+
nextDirection = reflectDirection(incomingDirection, orientedNormal);
|
|
1658
|
+
attenuation = mixVec3([0.04, 0.04, 0.04], albedo, metalness);
|
|
1659
|
+
}
|
|
1476
1660
|
} else if (eventKind === "transparency") {
|
|
1477
1661
|
nextDirection = incomingDirection;
|
|
1478
1662
|
const transparencyWeight = Math.max(1 - opacity, transmissionStrength, 0.05);
|
|
@@ -1486,16 +1670,150 @@ function evaluateWavefrontContinuationEvent(options = {}) {
|
|
|
1486
1670
|
}
|
|
1487
1671
|
const nextThroughput = multiplyVec3(throughput, saturateVec3(attenuation));
|
|
1488
1672
|
const continueTracing = eventKind !== "terminate" && colorLuminance(nextThroughput) > 1e-4;
|
|
1673
|
+
const mediumState = evaluateWavefrontMediumState({
|
|
1674
|
+
currentMediumRefId: options.currentMediumRefId ?? options.mediumRefId,
|
|
1675
|
+
surfaceMediumRefId: options.surfaceMediumRefId,
|
|
1676
|
+
mediumStack: options.mediumStack,
|
|
1677
|
+
frontFace,
|
|
1678
|
+
eventKind
|
|
1679
|
+
});
|
|
1489
1680
|
return Object.freeze({
|
|
1490
1681
|
hitType,
|
|
1682
|
+
requestedEventKind,
|
|
1491
1683
|
eventKind,
|
|
1492
1684
|
continueTracing,
|
|
1685
|
+
totalInternalReflection,
|
|
1493
1686
|
nextDirection: Object.freeze(nextDirection),
|
|
1494
1687
|
attenuation: Object.freeze(saturateVec3(attenuation)),
|
|
1495
1688
|
nextThroughput: Object.freeze(nextThroughput),
|
|
1689
|
+
mediumState,
|
|
1496
1690
|
explicitLightSamplingEnabled: Boolean(options.explicitLightSampling)
|
|
1497
1691
|
});
|
|
1498
1692
|
}
|
|
1693
|
+
function evaluateWavefrontVisibilityProbe(options = {}) {
|
|
1694
|
+
const probeMode = normalizeWavefrontVisibilityProbeMode(
|
|
1695
|
+
options.probeMode ?? (options.explicitLightSampling ? "mis-balanced" : "disabled")
|
|
1696
|
+
);
|
|
1697
|
+
const probeRay = createWavefrontVisibilityProbeRay({
|
|
1698
|
+
...options.probeRay,
|
|
1699
|
+
throughput: options.throughput ?? options.probeRay?.throughput,
|
|
1700
|
+
direction: options.direction ?? options.probeRay?.direction,
|
|
1701
|
+
mediumRefId: options.currentMediumRefId ?? options.probeRay?.mediumRefId,
|
|
1702
|
+
mediumStack: options.mediumStack ?? options.probeRay?.mediumStack
|
|
1703
|
+
});
|
|
1704
|
+
const transparentSegments = Array.isArray(options.transparentSegments) ? options.transparentSegments : [];
|
|
1705
|
+
const transmittance = transparentSegments.reduce(
|
|
1706
|
+
(current, segment) => multiplyVec3(current, saturateVec3(normalizeVec3(segment, [1, 1, 1]))),
|
|
1707
|
+
[1, 1, 1]
|
|
1708
|
+
);
|
|
1709
|
+
const emissiveRadiance = saturateVec3(
|
|
1710
|
+
normalizeVec3(options.emissiveRadiance, [0, 0, 0])
|
|
1711
|
+
);
|
|
1712
|
+
const environmentRadiance = saturateVec3(
|
|
1713
|
+
normalizeVec3(options.environmentRadiance, [0, 0, 0])
|
|
1714
|
+
);
|
|
1715
|
+
const activeEmissiveRadiance = saturateVec3(
|
|
1716
|
+
normalizeVec3(options.activeEmissiveRadiance, [0, 0, 0])
|
|
1717
|
+
);
|
|
1718
|
+
const prefersEnvironment = Boolean(options.prefersEnvironment);
|
|
1719
|
+
const sourceRadiance = prefersEnvironment || colorLuminance(emissiveRadiance) <= 1e-6 ? environmentRadiance : emissiveRadiance;
|
|
1720
|
+
const rawContribution = multiplyVec3(
|
|
1721
|
+
probeRay.throughput,
|
|
1722
|
+
multiplyVec3(sourceRadiance, transmittance)
|
|
1723
|
+
);
|
|
1724
|
+
const activeEmissiveVisible = colorLuminance(activeEmissiveRadiance) > 1e-6;
|
|
1725
|
+
const misWeight = probeMode === "mis-balanced" && activeEmissiveVisible ? 0.5 : 1;
|
|
1726
|
+
const contribution = probeMode === "exclusive-emissive" && activeEmissiveVisible ? [0, 0, 0] : scaleVec3(rawContribution, misWeight);
|
|
1727
|
+
return Object.freeze({
|
|
1728
|
+
probeMode,
|
|
1729
|
+
probeRay,
|
|
1730
|
+
transmittance: Object.freeze(transmittance),
|
|
1731
|
+
misWeight,
|
|
1732
|
+
doubleCountPrevented: activeEmissiveVisible && probeMode !== "disabled",
|
|
1733
|
+
contribution: Object.freeze(contribution)
|
|
1734
|
+
});
|
|
1735
|
+
}
|
|
1736
|
+
function evaluateWavefrontMaterialReference(options = {}) {
|
|
1737
|
+
const material = Object.freeze({
|
|
1738
|
+
albedo: Object.freeze(
|
|
1739
|
+
saturateVec3(normalizeVec3(options.albedo, [0.8, 0.8, 0.8]))
|
|
1740
|
+
),
|
|
1741
|
+
emission: Object.freeze(
|
|
1742
|
+
saturateVec3(normalizeVec3(options.emission, [0, 0, 0]))
|
|
1743
|
+
),
|
|
1744
|
+
roughness: clampUnit(options.roughness, 0.5),
|
|
1745
|
+
metalness: clampUnit(options.metalness, 0),
|
|
1746
|
+
opacity: clampUnit(options.opacity, 1),
|
|
1747
|
+
transmission: Object.freeze(
|
|
1748
|
+
saturateVec3(normalizeVec3(options.transmission, [0, 0, 0]))
|
|
1749
|
+
),
|
|
1750
|
+
refractiveIndex: Math.max(
|
|
1751
|
+
1,
|
|
1752
|
+
readFinite(options.refractiveIndex ?? options.ior, 1.45)
|
|
1753
|
+
)
|
|
1754
|
+
});
|
|
1755
|
+
const continuation = evaluateWavefrontContinuationEvent({
|
|
1756
|
+
...options,
|
|
1757
|
+
albedo: material.albedo,
|
|
1758
|
+
roughness: material.roughness,
|
|
1759
|
+
metalness: material.metalness,
|
|
1760
|
+
opacity: material.opacity,
|
|
1761
|
+
transmission: material.transmission,
|
|
1762
|
+
refractiveIndex: material.refractiveIndex
|
|
1763
|
+
});
|
|
1764
|
+
const terminal = evaluateWavefrontTerminalRadiance({
|
|
1765
|
+
...options,
|
|
1766
|
+
emission: material.emission
|
|
1767
|
+
});
|
|
1768
|
+
return Object.freeze({
|
|
1769
|
+
material,
|
|
1770
|
+
terminal,
|
|
1771
|
+
continuation,
|
|
1772
|
+
throughputUpdate: continuation.nextThroughput
|
|
1773
|
+
});
|
|
1774
|
+
}
|
|
1775
|
+
function createWavefrontReferenceFixture(options = {}) {
|
|
1776
|
+
const tolerance = Math.max(1e-4, readFinite(options.tolerance, 5e-4));
|
|
1777
|
+
const ray = createWavefrontRayPayload({
|
|
1778
|
+
rayId: options.rayId,
|
|
1779
|
+
parentRayId: options.parentRayId,
|
|
1780
|
+
sourcePixelId: options.sourcePixelId,
|
|
1781
|
+
sampleId: options.sampleId,
|
|
1782
|
+
bounce: options.bounceIndex ?? options.bounce,
|
|
1783
|
+
origin: options.origin,
|
|
1784
|
+
direction: options.direction ?? scaleVec3(options.viewDirection ?? [0, 0, 1], -1),
|
|
1785
|
+
throughput: options.throughput,
|
|
1786
|
+
mediumRefId: options.currentMediumRefId ?? options.mediumRefId,
|
|
1787
|
+
mediumStack: options.mediumStack
|
|
1788
|
+
});
|
|
1789
|
+
const reference = evaluateWavefrontMaterialReference(options);
|
|
1790
|
+
const visibilityProbe = options.visibilityProbe === void 0 ? null : evaluateWavefrontVisibilityProbe({
|
|
1791
|
+
...options.visibilityProbe,
|
|
1792
|
+
throughput: options.visibilityProbe.throughput ?? ray.throughput
|
|
1793
|
+
});
|
|
1794
|
+
const accumulationRadiance = addVec3(
|
|
1795
|
+
reference.terminal.radiance,
|
|
1796
|
+
visibilityProbe?.contribution ?? [0, 0, 0]
|
|
1797
|
+
);
|
|
1798
|
+
return Object.freeze({
|
|
1799
|
+
tolerance,
|
|
1800
|
+
ray,
|
|
1801
|
+
material: reference.material,
|
|
1802
|
+
continuation: reference.continuation,
|
|
1803
|
+
terminal: reference.terminal,
|
|
1804
|
+
visibilityProbe,
|
|
1805
|
+
accumulation: Object.freeze({
|
|
1806
|
+
sourcePixelId: ray.sourcePixelId,
|
|
1807
|
+
sampleCount: reference.terminal.terminated ? 1 : 0,
|
|
1808
|
+
radiance: Object.freeze(accumulationRadiance),
|
|
1809
|
+
throughput: reference.continuation.nextThroughput,
|
|
1810
|
+
resetEpoch: Math.max(
|
|
1811
|
+
0,
|
|
1812
|
+
Math.trunc(readFinite(options.accumulationResetEpoch, 0))
|
|
1813
|
+
)
|
|
1814
|
+
})
|
|
1815
|
+
});
|
|
1816
|
+
}
|
|
1499
1817
|
var lightingImportanceLevels = Object.freeze([
|
|
1500
1818
|
"low",
|
|
1501
1819
|
"medium",
|
|
@@ -2729,11 +3047,17 @@ async function loadLightingProfileWorkerPlan(profileName = defaultLightingProfil
|
|
|
2729
3047
|
createLightingProfileModeLadder,
|
|
2730
3048
|
createWavefrontEnvironmentLightingOptions,
|
|
2731
3049
|
createWavefrontLightingPlan,
|
|
3050
|
+
createWavefrontRayPayload,
|
|
3051
|
+
createWavefrontReferenceFixture,
|
|
3052
|
+
createWavefrontVisibilityProbeRay,
|
|
2732
3053
|
defaultAdaptiveLightingProfilePolicy,
|
|
2733
3054
|
defaultLightingProfile,
|
|
2734
3055
|
defaultLightingTechnique,
|
|
2735
3056
|
evaluateWavefrontContinuationEvent,
|
|
3057
|
+
evaluateWavefrontMaterialReference,
|
|
3058
|
+
evaluateWavefrontMediumState,
|
|
2736
3059
|
evaluateWavefrontTerminalRadiance,
|
|
3060
|
+
evaluateWavefrontVisibilityProbe,
|
|
2737
3061
|
getLightingProfile,
|
|
2738
3062
|
getLightingProfileWorkerManifest,
|
|
2739
3063
|
getLightingTechnique,
|
|
@@ -2760,9 +3084,11 @@ async function loadLightingProfileWorkerPlan(profileName = defaultLightingProfil
|
|
|
2760
3084
|
lightingWavefrontHitTypes,
|
|
2761
3085
|
lightingWavefrontPassOrder,
|
|
2762
3086
|
lightingWavefrontQueuePairStrategy,
|
|
3087
|
+
lightingWavefrontRayKinds,
|
|
2763
3088
|
lightingWavefrontSchemaVersion,
|
|
2764
3089
|
lightingWavefrontTerminalHitTypes,
|
|
2765
3090
|
lightingWavefrontTerminationPolicy,
|
|
3091
|
+
lightingWavefrontVisibilityProbeModes,
|
|
2766
3092
|
lightingWorkerManifests,
|
|
2767
3093
|
lightingWorkerQueueClass,
|
|
2768
3094
|
loadLightingJobs,
|