@plasius/gpu-lighting 0.2.5 → 0.2.7

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.cjs CHANGED
@@ -33,9 +33,18 @@ __export(index_exports, {
33
33
  createLightingBandPlan: () => createLightingBandPlan,
34
34
  createLightingProfileModeLadder: () => createLightingProfileModeLadder,
35
35
  createWavefrontEnvironmentLightingOptions: () => createWavefrontEnvironmentLightingOptions,
36
+ createWavefrontLightingPlan: () => createWavefrontLightingPlan,
37
+ createWavefrontRayPayload: () => createWavefrontRayPayload,
38
+ createWavefrontReferenceFixture: () => createWavefrontReferenceFixture,
39
+ createWavefrontVisibilityProbeRay: () => createWavefrontVisibilityProbeRay,
36
40
  defaultAdaptiveLightingProfilePolicy: () => defaultAdaptiveLightingProfilePolicy,
37
41
  defaultLightingProfile: () => defaultLightingProfile,
38
42
  defaultLightingTechnique: () => defaultLightingTechnique,
43
+ evaluateWavefrontContinuationEvent: () => evaluateWavefrontContinuationEvent,
44
+ evaluateWavefrontMaterialReference: () => evaluateWavefrontMaterialReference,
45
+ evaluateWavefrontMediumState: () => evaluateWavefrontMediumState,
46
+ evaluateWavefrontTerminalRadiance: () => evaluateWavefrontTerminalRadiance,
47
+ evaluateWavefrontVisibilityProbe: () => evaluateWavefrontVisibilityProbe,
39
48
  getLightingProfile: () => getLightingProfile,
40
49
  getLightingProfileWorkerManifest: () => getLightingProfileWorkerManifest,
41
50
  getLightingTechnique: () => getLightingTechnique,
@@ -54,8 +63,19 @@ __export(index_exports, {
54
63
  lightingProfileModeOrder: () => lightingProfileModeOrder,
55
64
  lightingProfileNames: () => lightingProfileNames,
56
65
  lightingProfiles: () => lightingProfiles,
66
+ lightingRequiredRendererWavefrontPassOrder: () => lightingRequiredRendererWavefrontPassOrder,
57
67
  lightingTechniqueNames: () => lightingTechniqueNames,
58
68
  lightingTechniques: () => lightingTechniques,
69
+ lightingWavefrontBufferContracts: () => lightingWavefrontBufferContracts,
70
+ lightingWavefrontContinuationHitTypes: () => lightingWavefrontContinuationHitTypes,
71
+ lightingWavefrontHitTypes: () => lightingWavefrontHitTypes,
72
+ lightingWavefrontPassOrder: () => lightingWavefrontPassOrder,
73
+ lightingWavefrontQueuePairStrategy: () => lightingWavefrontQueuePairStrategy,
74
+ lightingWavefrontRayKinds: () => lightingWavefrontRayKinds,
75
+ lightingWavefrontSchemaVersion: () => lightingWavefrontSchemaVersion,
76
+ lightingWavefrontTerminalHitTypes: () => lightingWavefrontTerminalHitTypes,
77
+ lightingWavefrontTerminationPolicy: () => lightingWavefrontTerminationPolicy,
78
+ lightingWavefrontVisibilityProbeModes: () => lightingWavefrontVisibilityProbeModes,
59
79
  lightingWorkerManifests: () => lightingWorkerManifests,
60
80
  lightingWorkerQueueClass: () => lightingWorkerQueueClass,
61
81
  loadLightingJobs: () => loadLightingJobs,
@@ -100,6 +120,14 @@ var techniqueSpecs = {
100
120
  denoise: "denoise.job.wgsl"
101
121
  }
102
122
  },
123
+ wavefront: {
124
+ description: "Renderer-aligned wavefront lighting jobs for terminal radiance and continuation scattering.",
125
+ prelude: "prelude.wgsl",
126
+ jobs: {
127
+ accumulateTerminalRadiance: "accumulate-terminal-radiance.job.wgsl",
128
+ scatterContinuations: "scatter-continuations.job.wgsl"
129
+ }
130
+ },
103
131
  volumetrics: {
104
132
  description: "Froxel volumetric lighting for fog, shafts, and participating media shadows.",
105
133
  prelude: "prelude.wgsl",
@@ -1108,6 +1136,684 @@ function createWavefrontEnvironmentLightingOptions(options = {}) {
1108
1136
  lightingEnvironment: config
1109
1137
  });
1110
1138
  }
1139
+ var lightingWavefrontSchemaVersion = 1;
1140
+ var lightingWavefrontQueuePairStrategy = "ping-pong-active-next";
1141
+ var lightingWavefrontHitTypes = Object.freeze([
1142
+ "surface",
1143
+ "emissive",
1144
+ "environment",
1145
+ "transparent",
1146
+ "miss"
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
+ ]);
1157
+ var lightingWavefrontTerminalHitTypes = Object.freeze([
1158
+ "emissive",
1159
+ "environment",
1160
+ "miss"
1161
+ ]);
1162
+ var lightingWavefrontContinuationHitTypes = Object.freeze([
1163
+ "surface",
1164
+ "transparent"
1165
+ ]);
1166
+ var lightingWavefrontPassOrder = Object.freeze([
1167
+ "accumulateTerminalRadiance",
1168
+ "scatterContinuations"
1169
+ ]);
1170
+ var lightingRequiredRendererWavefrontPassOrder = Object.freeze([
1171
+ "generatePrimaryRays",
1172
+ "intersectActiveQueue",
1173
+ "resolveSurfaceRecords",
1174
+ "accumulateTerminalRadiance",
1175
+ "scatterContinuations",
1176
+ "compactAndSwapQueues"
1177
+ ]);
1178
+ function createLightingWavefrontField(name, type, description) {
1179
+ return Object.freeze({
1180
+ name,
1181
+ type,
1182
+ description
1183
+ });
1184
+ }
1185
+ function createLightingWavefrontRecordContract(recordName, fields) {
1186
+ return Object.freeze({
1187
+ schemaVersion: lightingWavefrontSchemaVersion,
1188
+ recordName,
1189
+ fields: Object.freeze(fields)
1190
+ });
1191
+ }
1192
+ var lightingWavefrontBufferContracts = Object.freeze({
1193
+ ray: createLightingWavefrontRecordContract(
1194
+ "RayRecord",
1195
+ [
1196
+ createLightingWavefrontField("rayId", "u32", "Stable ray identifier."),
1197
+ createLightingWavefrontField("parentRayId", "u32", "Parent ray identifier."),
1198
+ createLightingWavefrontField("sourcePixelId", "u32", "Source pixel/sample owner."),
1199
+ createLightingWavefrontField("sampleId", "u32", "Per-pixel sample slot."),
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
+ ),
1227
+ createLightingWavefrontField("origin", "vec3<f32>", "Ray origin."),
1228
+ createLightingWavefrontField("direction", "vec3<f32>", "Normalized ray direction."),
1229
+ createLightingWavefrontField("throughput", "vec3<f32>", "Accumulated path throughput.")
1230
+ ]
1231
+ ),
1232
+ hit: createLightingWavefrontRecordContract(
1233
+ "HitRecord",
1234
+ [
1235
+ createLightingWavefrontField("rayId", "u32", "Stable ray identifier."),
1236
+ createLightingWavefrontField("sourcePixelId", "u32", "Source pixel/sample owner."),
1237
+ createLightingWavefrontField("hitType", "u32", "Surface, emissive, environment, transparent, or miss."),
1238
+ createLightingWavefrontField("distance", "f32", "Nearest-hit distance."),
1239
+ createLightingWavefrontField("entityId", "u32", "Owning entity identifier."),
1240
+ createLightingWavefrontField("instanceId", "u32", "Owning instance identifier."),
1241
+ createLightingWavefrontField("primitiveId", "u32", "Primitive identifier."),
1242
+ createLightingWavefrontField("materialId", "u32", "Resolved material identifier."),
1243
+ createLightingWavefrontField("barycentrics", "vec3<f32>", "Triangle barycentrics."),
1244
+ createLightingWavefrontField("uv", "vec2<f32>", "Resolved UV coordinates."),
1245
+ createLightingWavefrontField("geometricNormal", "vec3<f32>", "Geometric surface normal."),
1246
+ createLightingWavefrontField("shadingNormal", "vec3<f32>", "Interpolated shading normal."),
1247
+ createLightingWavefrontField("frontFace", "u32", "Front-face classification.")
1248
+ ]
1249
+ ),
1250
+ surface: createLightingWavefrontRecordContract(
1251
+ "SurfaceRecord",
1252
+ [
1253
+ createLightingWavefrontField("rayId", "u32", "Stable ray identifier."),
1254
+ createLightingWavefrontField("entityId", "u32", "Owning entity identifier."),
1255
+ createLightingWavefrontField("materialRefId", "u32", "Renderer material reference id."),
1256
+ createLightingWavefrontField("mediumRefId", "u32", "Renderer medium reference id."),
1257
+ createLightingWavefrontField("geometricNormal", "vec3<f32>", "Geometric surface normal."),
1258
+ createLightingWavefrontField("shadingNormal", "vec3<f32>", "Interpolated shading normal."),
1259
+ createLightingWavefrontField("uv", "vec2<f32>", "Resolved UV coordinates."),
1260
+ createLightingWavefrontField("tangentFrame", "mat3x3<f32>", "Resolved tangent frame.")
1261
+ ]
1262
+ ),
1263
+ materialReference: createLightingWavefrontRecordContract(
1264
+ "MaterialReferenceRecord",
1265
+ [
1266
+ createLightingWavefrontField("materialRefId", "u32", "Lighting-visible material reference id."),
1267
+ createLightingWavefrontField("materialId", "u32", "Source material identifier."),
1268
+ createLightingWavefrontField("shadingModel", "u32", "Renderer shading-model discriminator."),
1269
+ createLightingWavefrontField("textureSetId", "u32", "Resolved texture-set identifier."),
1270
+ createLightingWavefrontField("flags", "u32", "Renderer material flags.")
1271
+ ]
1272
+ ),
1273
+ mediumReference: createLightingWavefrontRecordContract(
1274
+ "MediumReferenceRecord",
1275
+ [
1276
+ createLightingWavefrontField("mediumRefId", "u32", "Lighting-visible medium reference id."),
1277
+ createLightingWavefrontField("mediumId", "u32", "Source medium identifier."),
1278
+ createLightingWavefrontField("phaseModel", "u32", "Phase-function discriminator."),
1279
+ createLightingWavefrontField("absorption", "vec3<f32>", "Medium absorption coefficients."),
1280
+ createLightingWavefrontField("scattering", "vec3<f32>", "Medium scattering coefficients.")
1281
+ ]
1282
+ ),
1283
+ accumulation: createLightingWavefrontRecordContract(
1284
+ "AccumulationRecord",
1285
+ [
1286
+ createLightingWavefrontField("sourcePixelId", "u32", "Source pixel/sample owner."),
1287
+ createLightingWavefrontField("sampleCount", "u32", "Accumulated sample count."),
1288
+ createLightingWavefrontField("radiance", "vec3<f32>", "Accumulated radiance."),
1289
+ createLightingWavefrontField("throughput", "vec3<f32>", "Last surviving throughput."),
1290
+ createLightingWavefrontField("resetEpoch", "u32", "Renderer accumulation reset epoch.")
1291
+ ]
1292
+ )
1293
+ });
1294
+ var lightingWavefrontTerminationPolicy = Object.freeze({
1295
+ terminalHitTypes: lightingWavefrontTerminalHitTypes,
1296
+ continuationHitTypes: lightingWavefrontContinuationHitTypes,
1297
+ emissive: Object.freeze({
1298
+ action: "accumulate-and-stop",
1299
+ contributesRadiance: true
1300
+ }),
1301
+ environment: Object.freeze({
1302
+ action: "accumulate-and-stop",
1303
+ contributesRadiance: true
1304
+ }),
1305
+ miss: Object.freeze({
1306
+ action: "accumulate-environment-or-dark-stop",
1307
+ contributesRadiance: true
1308
+ })
1309
+ });
1310
+ var defaultWavefrontDarkRadiance = Object.freeze([1e-4, 1e-4, 1e-4]);
1311
+ var wavefrontEventKinds = Object.freeze([
1312
+ "diffuse",
1313
+ "reflection",
1314
+ "refraction",
1315
+ "transparency",
1316
+ "terminate"
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
+ }
1329
+ function normalizeWavefrontHitType(value) {
1330
+ return lightingWavefrontHitTypes.includes(value) ? value : "surface";
1331
+ }
1332
+ function normalizeWavefrontEventKind(value) {
1333
+ return wavefrontEventKinds.includes(value) ? value : null;
1334
+ }
1335
+ function normalizeVec3(value, fallback = [0, 0, 0]) {
1336
+ if (!Array.isArray(value) || value.length < 3) {
1337
+ return [...fallback];
1338
+ }
1339
+ return [
1340
+ Number.isFinite(value[0]) ? value[0] : fallback[0],
1341
+ Number.isFinite(value[1]) ? value[1] : fallback[1],
1342
+ Number.isFinite(value[2]) ? value[2] : fallback[2]
1343
+ ];
1344
+ }
1345
+ function clampUnit(value, fallback = 0) {
1346
+ return Math.max(0, Math.min(1, readFinite(value, fallback)));
1347
+ }
1348
+ function saturateVec3(value) {
1349
+ return value.map((component) => Math.max(0, component));
1350
+ }
1351
+ function scaleVec3(value, scalar) {
1352
+ return value.map((component) => component * scalar);
1353
+ }
1354
+ function addVec3(left, right) {
1355
+ return left.map((component, index) => component + right[index]);
1356
+ }
1357
+ function multiplyVec3(left, right) {
1358
+ return left.map((component, index) => component * right[index]);
1359
+ }
1360
+ function dotVec3(left, right) {
1361
+ return left[0] * right[0] + left[1] * right[1] + left[2] * right[2];
1362
+ }
1363
+ function lengthVec3(value) {
1364
+ return Math.hypot(value[0], value[1], value[2]);
1365
+ }
1366
+ function normalizeDirection(value, fallback = [0, 1, 0]) {
1367
+ const vector = normalizeVec3(value, fallback);
1368
+ const length = lengthVec3(vector);
1369
+ if (!Number.isFinite(length) || length <= 1e-6) {
1370
+ return [...fallback];
1371
+ }
1372
+ return vector.map((component) => component / length);
1373
+ }
1374
+ function mixVec3(left, right, factor) {
1375
+ return left.map(
1376
+ (component, index) => component * (1 - factor) + right[index] * factor
1377
+ );
1378
+ }
1379
+ function reflectDirection(direction, normal) {
1380
+ const scale = 2 * dotVec3(direction, normal);
1381
+ return normalizeDirection(
1382
+ [
1383
+ direction[0] - scale * normal[0],
1384
+ direction[1] - scale * normal[1],
1385
+ direction[2] - scale * normal[2]
1386
+ ],
1387
+ normal
1388
+ );
1389
+ }
1390
+ function refractDirection(direction, normal, etaRatio) {
1391
+ const cosTheta = Math.min(-dotVec3(direction, normal), 1);
1392
+ const rOutPerp = scaleVec3(addVec3(direction, scaleVec3(normal, cosTheta)), etaRatio);
1393
+ const rOutPerpLengthSquared = dotVec3(rOutPerp, rOutPerp);
1394
+ const parallelFactor = 1 - rOutPerpLengthSquared;
1395
+ if (parallelFactor <= 0) {
1396
+ return null;
1397
+ }
1398
+ const rOutParallel = scaleVec3(normal, -Math.sqrt(parallelFactor));
1399
+ return normalizeDirection(addVec3(rOutPerp, rOutParallel), direction);
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
+ }
1513
+ function createWavefrontLightingPlan(options = {}) {
1514
+ const maxDepth = Math.max(1, Math.trunc(readFinite(options.maxDepth, 4)));
1515
+ const queueCapacity = Math.max(
1516
+ 1,
1517
+ Math.trunc(readFinite(options.queueCapacity, 4096))
1518
+ );
1519
+ const explicitLightSampling = Boolean(options.explicitLightSampling);
1520
+ const visibilityProbeMode = normalizeWavefrontVisibilityProbeMode(
1521
+ options.visibilityProbeMode ?? (explicitLightSampling ? "mis-balanced" : "disabled")
1522
+ );
1523
+ const accumulationResetEpoch = Math.max(
1524
+ 0,
1525
+ Math.trunc(readFinite(options.accumulationResetEpoch, 0))
1526
+ );
1527
+ return Object.freeze({
1528
+ schemaVersion: lightingWavefrontSchemaVersion,
1529
+ maxDepth,
1530
+ queueCapacity,
1531
+ explicitLightSampling,
1532
+ visibilityProbeMode,
1533
+ rayKinds: lightingWavefrontRayKinds,
1534
+ accumulationResetEpoch,
1535
+ queueLayout: Object.freeze({
1536
+ strategy: lightingWavefrontQueuePairStrategy,
1537
+ compactAfterScatter: true,
1538
+ queues: Object.freeze([
1539
+ Object.freeze({ name: "active", role: "current-bounce" }),
1540
+ Object.freeze({ name: "next", role: "next-bounce" })
1541
+ ])
1542
+ }),
1543
+ bufferContracts: lightingWavefrontBufferContracts,
1544
+ terminationPolicy: lightingWavefrontTerminationPolicy,
1545
+ requiredRendererPassOrder: lightingRequiredRendererWavefrontPassOrder,
1546
+ lightingPasses: Object.freeze([
1547
+ Object.freeze({
1548
+ key: "accumulateTerminalRadiance",
1549
+ stage: "accumulateTerminalRadiance",
1550
+ reads: Object.freeze([
1551
+ "ray",
1552
+ "hit",
1553
+ "surface",
1554
+ "materialReference",
1555
+ "mediumReference"
1556
+ ]),
1557
+ writes: Object.freeze(["accumulation"]),
1558
+ terminalHitTypes: lightingWavefrontTerminalHitTypes
1559
+ }),
1560
+ Object.freeze({
1561
+ key: "scatterContinuations",
1562
+ stage: "scatterContinuations",
1563
+ reads: Object.freeze([
1564
+ "ray",
1565
+ "hit",
1566
+ "surface",
1567
+ "materialReference",
1568
+ "mediumReference"
1569
+ ]),
1570
+ writes: Object.freeze(["ray"]),
1571
+ continuationHitTypes: lightingWavefrontContinuationHitTypes,
1572
+ explicitLightSampling,
1573
+ visibilityProbeMode
1574
+ })
1575
+ ])
1576
+ });
1577
+ }
1578
+ function evaluateWavefrontTerminalRadiance(options = {}) {
1579
+ const hitType = normalizeWavefrontHitType(options.hitType);
1580
+ const throughput = saturateVec3(normalizeVec3(options.throughput, [1, 1, 1]));
1581
+ const emission = saturateVec3(normalizeVec3(options.emission, [0, 0, 0]));
1582
+ const environmentRadiance = saturateVec3(
1583
+ normalizeVec3(options.environmentRadiance, [0, 0, 0])
1584
+ );
1585
+ const missRadiance = saturateVec3(
1586
+ normalizeVec3(options.missRadiance, defaultWavefrontDarkRadiance)
1587
+ );
1588
+ const environmentLuminance = colorLuminance(environmentRadiance);
1589
+ let source = "none";
1590
+ let rawRadiance = [0, 0, 0];
1591
+ if (hitType === "emissive") {
1592
+ source = "emissive";
1593
+ rawRadiance = emission;
1594
+ } else if (hitType === "environment") {
1595
+ source = "environment";
1596
+ rawRadiance = environmentRadiance;
1597
+ } else if (hitType === "miss") {
1598
+ source = environmentLuminance > 1e-6 ? "environment" : "dark";
1599
+ rawRadiance = environmentLuminance > 1e-6 ? environmentRadiance : missRadiance;
1600
+ }
1601
+ const radiance = multiplyVec3(throughput, rawRadiance);
1602
+ const terminated = lightingWavefrontTerminalHitTypes.includes(hitType);
1603
+ return Object.freeze({
1604
+ hitType,
1605
+ source,
1606
+ terminated,
1607
+ radiance: Object.freeze(radiance),
1608
+ nearDarkSample: source === "dark" && colorLuminance(radiance) <= colorLuminance(defaultWavefrontDarkRadiance)
1609
+ });
1610
+ }
1611
+ function evaluateWavefrontContinuationEvent(options = {}) {
1612
+ const hitType = normalizeWavefrontHitType(options.hitType);
1613
+ const bounceIndex = Math.max(0, Math.trunc(readFinite(options.bounceIndex, 0)));
1614
+ const maxDepth = Math.max(1, Math.trunc(readFinite(options.maxDepth, 4)));
1615
+ const throughput = saturateVec3(normalizeVec3(options.throughput, [1, 1, 1]));
1616
+ const albedo = saturateVec3(normalizeVec3(options.albedo, [0.8, 0.8, 0.8]));
1617
+ const transmission = saturateVec3(
1618
+ normalizeVec3(options.transmission, [0, 0, 0])
1619
+ );
1620
+ const shadingNormal = normalizeDirection(options.shadingNormal, [0, 1, 0]);
1621
+ const viewDirection = normalizeDirection(options.viewDirection, [0, 0, 1]);
1622
+ const incomingDirection = normalizeDirection(
1623
+ scaleVec3(viewDirection, -1),
1624
+ [0, 0, -1]
1625
+ );
1626
+ const frontFace = options.frontFace !== false;
1627
+ const orientedNormal = frontFace ? shadingNormal : scaleVec3(shadingNormal, -1);
1628
+ const metalness = clampUnit(options.metalness, 0);
1629
+ const roughness = clampUnit(options.roughness, 0.5);
1630
+ const opacity = clampUnit(options.opacity, 1);
1631
+ const refractiveIndex = Math.max(1, readFinite(options.refractiveIndex ?? options.ior, 1.45));
1632
+ const transmissionStrength = Math.max(...transmission);
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;
1636
+ let nextDirection = incomingDirection;
1637
+ let attenuation;
1638
+ if (!lightingWavefrontContinuationHitTypes.includes(hitType) || bounceIndex >= maxDepth - 1) {
1639
+ eventKind = "terminate";
1640
+ attenuation = [0, 0, 0];
1641
+ } else if (eventKind === "reflection") {
1642
+ nextDirection = reflectDirection(incomingDirection, orientedNormal);
1643
+ attenuation = mixVec3([0.04, 0.04, 0.04], albedo, metalness);
1644
+ } else if (eventKind === "refraction") {
1645
+ const etaRatio = frontFace ? 1 / refractiveIndex : refractiveIndex;
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
+ }
1660
+ } else if (eventKind === "transparency") {
1661
+ nextDirection = incomingDirection;
1662
+ const transparencyWeight = Math.max(1 - opacity, transmissionStrength, 0.05);
1663
+ attenuation = transmissionStrength > 1e-3 ? transmission : [transparencyWeight, transparencyWeight, transparencyWeight];
1664
+ } else {
1665
+ nextDirection = normalizeDirection(
1666
+ addVec3(orientedNormal, albedo.map((component) => component - 0.5)),
1667
+ orientedNormal
1668
+ );
1669
+ attenuation = scaleVec3(albedo, Math.max(0.05, 1 - metalness));
1670
+ }
1671
+ const nextThroughput = multiplyVec3(throughput, saturateVec3(attenuation));
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
+ });
1680
+ return Object.freeze({
1681
+ hitType,
1682
+ requestedEventKind,
1683
+ eventKind,
1684
+ continueTracing,
1685
+ totalInternalReflection,
1686
+ nextDirection: Object.freeze(nextDirection),
1687
+ attenuation: Object.freeze(saturateVec3(attenuation)),
1688
+ nextThroughput: Object.freeze(nextThroughput),
1689
+ mediumState,
1690
+ explicitLightSamplingEnabled: Boolean(options.explicitLightSampling)
1691
+ });
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
+ }
1111
1817
  var lightingImportanceLevels = Object.freeze([
1112
1818
  "low",
1113
1819
  "medium",
@@ -1673,6 +2379,91 @@ var lightingWorkerSpecPresets = {
1673
2379
  }
1674
2380
  }
1675
2381
  },
2382
+ wavefront: {
2383
+ suggestedAllocationIds: [
2384
+ "lighting.wavefront.active-queue",
2385
+ "lighting.wavefront.next-queue",
2386
+ "lighting.wavefront.accumulation"
2387
+ ],
2388
+ jobs: {
2389
+ accumulateTerminalRadiance: {
2390
+ domain: "lighting",
2391
+ importance: "critical",
2392
+ levels: buildWorkerBudgetLevels(
2393
+ "lighting.wavefront.accumulateTerminalRadiance",
2394
+ lightingWorkerQueueClass,
2395
+ {
2396
+ low: {
2397
+ estimatedCostMs: 0.7,
2398
+ maxDispatchesPerFrame: 1,
2399
+ maxJobsPerDispatch: 32,
2400
+ cadenceDivisor: 2,
2401
+ workgroupScale: 0.5,
2402
+ maxQueueDepth: 96
2403
+ },
2404
+ medium: {
2405
+ estimatedCostMs: 1.2,
2406
+ maxDispatchesPerFrame: 1,
2407
+ maxJobsPerDispatch: 64,
2408
+ cadenceDivisor: 1,
2409
+ workgroupScale: 0.75,
2410
+ maxQueueDepth: 192
2411
+ },
2412
+ high: {
2413
+ estimatedCostMs: 1.8,
2414
+ maxDispatchesPerFrame: 2,
2415
+ maxJobsPerDispatch: 128,
2416
+ cadenceDivisor: 1,
2417
+ workgroupScale: 1,
2418
+ maxQueueDepth: 256
2419
+ }
2420
+ }
2421
+ ),
2422
+ suggestedAllocationIds: [
2423
+ "lighting.wavefront.accumulation",
2424
+ "lighting.wavefront.active-queue"
2425
+ ]
2426
+ },
2427
+ scatterContinuations: {
2428
+ domain: "lighting",
2429
+ importance: "critical",
2430
+ levels: buildWorkerBudgetLevels(
2431
+ "lighting.wavefront.scatterContinuations",
2432
+ lightingWorkerQueueClass,
2433
+ {
2434
+ low: {
2435
+ estimatedCostMs: 0.8,
2436
+ maxDispatchesPerFrame: 1,
2437
+ maxJobsPerDispatch: 32,
2438
+ cadenceDivisor: 2,
2439
+ workgroupScale: 0.5,
2440
+ maxQueueDepth: 96
2441
+ },
2442
+ medium: {
2443
+ estimatedCostMs: 1.4,
2444
+ maxDispatchesPerFrame: 1,
2445
+ maxJobsPerDispatch: 64,
2446
+ cadenceDivisor: 1,
2447
+ workgroupScale: 0.75,
2448
+ maxQueueDepth: 192
2449
+ },
2450
+ high: {
2451
+ estimatedCostMs: 2.1,
2452
+ maxDispatchesPerFrame: 2,
2453
+ maxJobsPerDispatch: 128,
2454
+ cadenceDivisor: 1,
2455
+ workgroupScale: 1,
2456
+ maxQueueDepth: 256
2457
+ }
2458
+ }
2459
+ ),
2460
+ suggestedAllocationIds: [
2461
+ "lighting.wavefront.active-queue",
2462
+ "lighting.wavefront.next-queue"
2463
+ ]
2464
+ }
2465
+ }
2466
+ },
1676
2467
  volumetrics: {
1677
2468
  suggestedAllocationIds: [
1678
2469
  "lighting.volumetrics.froxel-grid",
@@ -1882,6 +2673,13 @@ var lightingWorkerDagSpecs = {
1882
2673
  accumulate: { priority: 3, dependencies: ["pathTrace"] },
1883
2674
  denoise: { priority: 2, dependencies: ["accumulate"] }
1884
2675
  },
2676
+ wavefront: {
2677
+ accumulateTerminalRadiance: { priority: 3, dependencies: [] },
2678
+ scatterContinuations: {
2679
+ priority: 2,
2680
+ dependencies: ["accumulateTerminalRadiance"]
2681
+ }
2682
+ },
1885
2683
  volumetrics: {
1886
2684
  volumetricShadow: { priority: 3, dependencies: [] },
1887
2685
  froxelIntegrate: { priority: 2, dependencies: ["volumetricShadow"] }
@@ -1915,6 +2713,16 @@ function resolveLightingQualityDimensions(techniqueName, jobKey) {
1915
2713
  "pathtracer.pathTrace": { rayTracing: 1, lightingSamples: 1 },
1916
2714
  "pathtracer.accumulate": { temporalReuse: 1, updateCadence: 0.4 },
1917
2715
  "pathtracer.denoise": { temporalReuse: 1, shading: 0.4 },
2716
+ "wavefront.accumulateTerminalRadiance": {
2717
+ rayTracing: 1,
2718
+ lightingSamples: 1,
2719
+ temporalReuse: 0.4
2720
+ },
2721
+ "wavefront.scatterContinuations": {
2722
+ rayTracing: 1,
2723
+ shading: 0.7,
2724
+ updateCadence: 0.5
2725
+ },
1918
2726
  "volumetrics.froxelIntegrate": {
1919
2727
  lightingSamples: 0.6,
1920
2728
  shading: 0.4,
@@ -1959,6 +2767,15 @@ function resolveLightingImportanceSignals(techniqueName, jobKey) {
1959
2767
  },
1960
2768
  "pathtracer.accumulate": { visible: true },
1961
2769
  "pathtracer.denoise": { visible: true },
2770
+ "wavefront.accumulateTerminalRadiance": {
2771
+ visible: true,
2772
+ shadowSignificance: "high",
2773
+ reflectionSignificance: "high"
2774
+ },
2775
+ "wavefront.scatterContinuations": {
2776
+ visible: true,
2777
+ reflectionSignificance: "high"
2778
+ },
1962
2779
  "volumetrics.froxelIntegrate": { visible: true },
1963
2780
  "volumetrics.volumetricShadow": { visible: true, shadowSignificance: "high" },
1964
2781
  "hdri.irradianceConvolution": { visible: false },
@@ -2229,9 +3046,18 @@ async function loadLightingProfileWorkerPlan(profileName = defaultLightingProfil
2229
3046
  createLightingBandPlan,
2230
3047
  createLightingProfileModeLadder,
2231
3048
  createWavefrontEnvironmentLightingOptions,
3049
+ createWavefrontLightingPlan,
3050
+ createWavefrontRayPayload,
3051
+ createWavefrontReferenceFixture,
3052
+ createWavefrontVisibilityProbeRay,
2232
3053
  defaultAdaptiveLightingProfilePolicy,
2233
3054
  defaultLightingProfile,
2234
3055
  defaultLightingTechnique,
3056
+ evaluateWavefrontContinuationEvent,
3057
+ evaluateWavefrontMaterialReference,
3058
+ evaluateWavefrontMediumState,
3059
+ evaluateWavefrontTerminalRadiance,
3060
+ evaluateWavefrontVisibilityProbe,
2235
3061
  getLightingProfile,
2236
3062
  getLightingProfileWorkerManifest,
2237
3063
  getLightingTechnique,
@@ -2250,8 +3076,19 @@ async function loadLightingProfileWorkerPlan(profileName = defaultLightingProfil
2250
3076
  lightingProfileModeOrder,
2251
3077
  lightingProfileNames,
2252
3078
  lightingProfiles,
3079
+ lightingRequiredRendererWavefrontPassOrder,
2253
3080
  lightingTechniqueNames,
2254
3081
  lightingTechniques,
3082
+ lightingWavefrontBufferContracts,
3083
+ lightingWavefrontContinuationHitTypes,
3084
+ lightingWavefrontHitTypes,
3085
+ lightingWavefrontPassOrder,
3086
+ lightingWavefrontQueuePairStrategy,
3087
+ lightingWavefrontRayKinds,
3088
+ lightingWavefrontSchemaVersion,
3089
+ lightingWavefrontTerminalHitTypes,
3090
+ lightingWavefrontTerminationPolicy,
3091
+ lightingWavefrontVisibilityProbeModes,
2255
3092
  lightingWorkerManifests,
2256
3093
  lightingWorkerQueueClass,
2257
3094
  loadLightingJobs,