@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.
@@ -13,6 +13,10 @@ const EVENT_KIND_REFRACTION: u32 = 2u;
13
13
  const EVENT_KIND_TRANSPARENCY: u32 = 3u;
14
14
  const EVENT_KIND_TERMINATE: u32 = 4u;
15
15
 
16
+ const RAY_KIND_PATH: u32 = 0u;
17
+ const RAY_KIND_VISIBILITY_PROBE: u32 = 1u;
18
+ const RAY_KIND_MASK: u32 = 0x3u;
19
+
16
20
  const LIGHTING_EPSILON: f32 = 0.0001;
17
21
  const LIGHTING_INV_PI: f32 = 0.3183098861837907;
18
22
 
@@ -147,6 +151,10 @@ fn is_terminal_hit_type(hit_type: u32) -> bool {
147
151
  hit_type == HIT_TYPE_MISS;
148
152
  }
149
153
 
154
+ fn ray_kind(flags: u32) -> u32 {
155
+ return flags & RAY_KIND_MASK;
156
+ }
157
+
150
158
  fn environment_radiance(direction: vec3<f32>) -> vec3<f32> {
151
159
  let upward = saturate_scalar(direction.y * 0.5 + 0.5);
152
160
  let directional = mix(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plasius/gpu-lighting",
3
- "version": "0.2.6",
3
+ "version": "0.2.8",
4
4
  "description": "Advanced lighting WGSL modules and planning profiles for @plasius/gpu-worker.",
5
5
  "type": "module",
6
6
  "sideEffects": false,
package/src/index.js CHANGED
@@ -1146,6 +1146,15 @@ export const lightingWavefrontHitTypes = Object.freeze([
1146
1146
  "transparent",
1147
1147
  "miss",
1148
1148
  ]);
1149
+ export const lightingWavefrontRayKinds = Object.freeze([
1150
+ "path",
1151
+ "visibility-probe",
1152
+ ]);
1153
+ export const lightingWavefrontVisibilityProbeModes = Object.freeze([
1154
+ "disabled",
1155
+ "mis-balanced",
1156
+ "exclusive-emissive",
1157
+ ]);
1149
1158
  export const lightingWavefrontTerminalHitTypes = Object.freeze([
1150
1159
  "emissive",
1151
1160
  "environment",
@@ -1193,11 +1202,35 @@ export const lightingWavefrontBufferContracts = Object.freeze({
1193
1202
  createLightingWavefrontField("sourcePixelId", "u32", "Source pixel/sample owner."),
1194
1203
  createLightingWavefrontField("sampleId", "u32", "Per-pixel sample slot."),
1195
1204
  createLightingWavefrontField("bounce", "u32", "Current bounce depth."),
1205
+ createLightingWavefrontField("mediumRefId", "u32", "Current medium reference."),
1206
+ createLightingWavefrontField(
1207
+ "mediumStackDepth",
1208
+ "u32",
1209
+ "Depth of the bounded nested medium stack."
1210
+ ),
1211
+ createLightingWavefrontField(
1212
+ "flags",
1213
+ "u32",
1214
+ "Renderer-owned ray flags. Low bits may encode ray kind metadata."
1215
+ ),
1216
+ createLightingWavefrontField(
1217
+ "mediumStack0",
1218
+ "vec4<u32>",
1219
+ "Lower half of the bounded nested medium stack."
1220
+ ),
1221
+ createLightingWavefrontField(
1222
+ "mediumStack1",
1223
+ "vec4<u32>",
1224
+ "Upper half of the bounded nested medium stack."
1225
+ ),
1226
+ createLightingWavefrontField(
1227
+ "spectralState",
1228
+ "vec4<f32>",
1229
+ "Spectral transport payload for wavelength-driven reference validation."
1230
+ ),
1196
1231
  createLightingWavefrontField("origin", "vec3<f32>", "Ray origin."),
1197
1232
  createLightingWavefrontField("direction", "vec3<f32>", "Normalized ray direction."),
1198
1233
  createLightingWavefrontField("throughput", "vec3<f32>", "Accumulated path throughput."),
1199
- createLightingWavefrontField("mediumRefId", "u32", "Current medium reference."),
1200
- createLightingWavefrontField("flags", "u32", "Renderer-owned ray flags."),
1201
1234
  ]
1202
1235
  ),
1203
1236
  hit: createLightingWavefrontRecordContract(
@@ -1288,6 +1321,21 @@ const wavefrontEventKinds = Object.freeze([
1288
1321
  "transparency",
1289
1322
  "terminate",
1290
1323
  ]);
1324
+ const wavefrontRayKindFlagMask = 0x3;
1325
+ const wavefrontRayKindFlagValues = Object.freeze({
1326
+ path: 0,
1327
+ "visibility-probe": 1,
1328
+ });
1329
+
1330
+ function normalizeWavefrontRayKind(value) {
1331
+ return lightingWavefrontRayKinds.includes(value) ? value : "path";
1332
+ }
1333
+
1334
+ function normalizeWavefrontVisibilityProbeMode(value) {
1335
+ return lightingWavefrontVisibilityProbeModes.includes(value)
1336
+ ? value
1337
+ : "disabled";
1338
+ }
1291
1339
 
1292
1340
  function normalizeWavefrontHitType(value) {
1293
1341
  return lightingWavefrontHitTypes.includes(value) ? value : "surface";
@@ -1375,6 +1423,137 @@ function refractDirection(direction, normal, etaRatio) {
1375
1423
  return normalizeDirection(addVec3(rOutPerp, rOutParallel), direction);
1376
1424
  }
1377
1425
 
1426
+ function normalizeMediumRefId(value) {
1427
+ const mediumRefId = Math.max(0, Math.trunc(readFinite(value, 0)));
1428
+ return Number.isFinite(mediumRefId) ? mediumRefId : 0;
1429
+ }
1430
+
1431
+ function normalizeMediumStack(value) {
1432
+ if (!Array.isArray(value)) {
1433
+ return [];
1434
+ }
1435
+
1436
+ return value
1437
+ .map((entry) => normalizeMediumRefId(entry))
1438
+ .filter((entry, index, stack) => entry > 0 && stack.indexOf(entry) === index)
1439
+ .slice(0, 4);
1440
+ }
1441
+
1442
+ function createWavefrontMediumStatePayload(currentMediumRefId, stack) {
1443
+ const normalizedStack = normalizeMediumStack(stack);
1444
+ const stackSlots = [0, 0, 0, 0];
1445
+ normalizedStack.forEach((entry, index) => {
1446
+ stackSlots[index] = entry;
1447
+ });
1448
+ return Object.freeze({
1449
+ currentMediumRefId,
1450
+ stackDepth: normalizedStack.length,
1451
+ stack: Object.freeze(normalizedStack),
1452
+ stackSlots: Object.freeze(stackSlots),
1453
+ });
1454
+ }
1455
+
1456
+ export function evaluateWavefrontMediumState(options = {}) {
1457
+ const currentMediumRefId = normalizeMediumRefId(
1458
+ options.currentMediumRefId ?? options.mediumRefId
1459
+ );
1460
+ const surfaceMediumRefId = normalizeMediumRefId(options.surfaceMediumRefId);
1461
+ const stack = normalizeMediumStack(options.mediumStack);
1462
+ const frontFace = options.frontFace !== false;
1463
+ const eventKind =
1464
+ normalizeWavefrontEventKind(options.eventKind) ?? "transparency";
1465
+
1466
+ if (
1467
+ surfaceMediumRefId === 0 ||
1468
+ (eventKind !== "refraction" && eventKind !== "transparency")
1469
+ ) {
1470
+ return Object.freeze({
1471
+ ...createWavefrontMediumStatePayload(currentMediumRefId, stack),
1472
+ enteredMediumRefId: 0,
1473
+ exitedMediumRefId: 0,
1474
+ });
1475
+ }
1476
+
1477
+ let nextStack = [...stack];
1478
+ let nextMediumRefId = currentMediumRefId;
1479
+ let enteredMediumRefId = 0;
1480
+ let exitedMediumRefId = 0;
1481
+ const stackTop = nextStack.at(-1) ?? 0;
1482
+
1483
+ if (frontFace) {
1484
+ if (stackTop !== surfaceMediumRefId) {
1485
+ nextStack.push(surfaceMediumRefId);
1486
+ nextStack = nextStack.slice(-4);
1487
+ }
1488
+ nextMediumRefId = surfaceMediumRefId;
1489
+ enteredMediumRefId = surfaceMediumRefId;
1490
+ } else if (stackTop === surfaceMediumRefId) {
1491
+ nextStack.pop();
1492
+ nextMediumRefId = nextStack.at(-1) ?? 0;
1493
+ exitedMediumRefId = surfaceMediumRefId;
1494
+ }
1495
+
1496
+ return Object.freeze({
1497
+ ...createWavefrontMediumStatePayload(nextMediumRefId, nextStack),
1498
+ enteredMediumRefId,
1499
+ exitedMediumRefId,
1500
+ });
1501
+ }
1502
+
1503
+ function encodeWavefrontRayFlags(flags, rayKind) {
1504
+ const normalizedFlags = Math.max(0, Math.trunc(readFinite(flags, 0)));
1505
+ const rayKindValue = wavefrontRayKindFlagValues[rayKind];
1506
+ return (normalizedFlags & ~wavefrontRayKindFlagMask) | rayKindValue;
1507
+ }
1508
+
1509
+ export function createWavefrontRayPayload(options = {}) {
1510
+ const rayKind = normalizeWavefrontRayKind(options.rayKind);
1511
+ const mediumState = evaluateWavefrontMediumState({
1512
+ currentMediumRefId: options.mediumRefId,
1513
+ mediumStack: options.mediumStack,
1514
+ });
1515
+ const spectralState = Object.freeze(
1516
+ normalizeVec3(options.spectralState, [550, 1, 0]).concat(
1517
+ readFinite(options.spectralWeight, 0)
1518
+ )
1519
+ );
1520
+ const mediumStack0 = Object.freeze([
1521
+ mediumState.stackSlots[0],
1522
+ mediumState.stackSlots[1],
1523
+ mediumState.stackSlots[2],
1524
+ mediumState.stackSlots[3],
1525
+ ]);
1526
+ const mediumStack1 = Object.freeze([0, 0, 0, 0]);
1527
+ return Object.freeze({
1528
+ rayId: Math.max(0, Math.trunc(readFinite(options.rayId, 0))),
1529
+ parentRayId: Math.max(0, Math.trunc(readFinite(options.parentRayId, 0))),
1530
+ sourcePixelId: Math.max(0, Math.trunc(readFinite(options.sourcePixelId, 0))),
1531
+ sampleId: Math.max(0, Math.trunc(readFinite(options.sampleId, 0))),
1532
+ bounce: Math.max(0, Math.trunc(readFinite(options.bounce, 0))),
1533
+ origin: Object.freeze(normalizeVec3(options.origin, [0, 0, 0])),
1534
+ direction: Object.freeze(normalizeDirection(options.direction, [0, 0, -1])),
1535
+ throughput: Object.freeze(
1536
+ saturateVec3(normalizeVec3(options.throughput, [1, 1, 1]))
1537
+ ),
1538
+ mediumRefId: mediumState.currentMediumRefId,
1539
+ mediumStackDepth: mediumState.stackDepth,
1540
+ mediumStack: mediumState.stack,
1541
+ mediumStackSlots: mediumState.stackSlots,
1542
+ mediumStack0,
1543
+ mediumStack1,
1544
+ spectralState,
1545
+ rayKind,
1546
+ flags: encodeWavefrontRayFlags(options.flags, rayKind),
1547
+ });
1548
+ }
1549
+
1550
+ export function createWavefrontVisibilityProbeRay(options = {}) {
1551
+ return createWavefrontRayPayload({
1552
+ ...options,
1553
+ rayKind: "visibility-probe",
1554
+ });
1555
+ }
1556
+
1378
1557
  export function createWavefrontLightingPlan(options = {}) {
1379
1558
  const maxDepth = Math.max(1, Math.trunc(readFinite(options.maxDepth, 4)));
1380
1559
  const queueCapacity = Math.max(
@@ -1382,6 +1561,10 @@ export function createWavefrontLightingPlan(options = {}) {
1382
1561
  Math.trunc(readFinite(options.queueCapacity, 4096))
1383
1562
  );
1384
1563
  const explicitLightSampling = Boolean(options.explicitLightSampling);
1564
+ const visibilityProbeMode = normalizeWavefrontVisibilityProbeMode(
1565
+ options.visibilityProbeMode ??
1566
+ (explicitLightSampling ? "mis-balanced" : "disabled")
1567
+ );
1385
1568
  const accumulationResetEpoch = Math.max(
1386
1569
  0,
1387
1570
  Math.trunc(readFinite(options.accumulationResetEpoch, 0))
@@ -1392,6 +1575,8 @@ export function createWavefrontLightingPlan(options = {}) {
1392
1575
  maxDepth,
1393
1576
  queueCapacity,
1394
1577
  explicitLightSampling,
1578
+ visibilityProbeMode,
1579
+ rayKinds: lightingWavefrontRayKinds,
1395
1580
  accumulationResetEpoch,
1396
1581
  queueLayout: Object.freeze({
1397
1582
  strategy: lightingWavefrontQueuePairStrategy,
@@ -1431,6 +1616,7 @@ export function createWavefrontLightingPlan(options = {}) {
1431
1616
  writes: Object.freeze(["ray"]),
1432
1617
  continuationHitTypes: lightingWavefrontContinuationHitTypes,
1433
1618
  explicitLightSampling,
1619
+ visibilityProbeMode,
1434
1620
  }),
1435
1621
  ]),
1436
1622
  });
@@ -1498,7 +1684,7 @@ export function evaluateWavefrontContinuationEvent(options = {}) {
1498
1684
  const opacity = clampUnit(options.opacity, 1);
1499
1685
  const refractiveIndex = Math.max(1, readFinite(options.refractiveIndex ?? options.ior, 1.45));
1500
1686
  const transmissionStrength = Math.max(...transmission);
1501
- let eventKind =
1687
+ const requestedEventKind =
1502
1688
  normalizeWavefrontEventKind(options.eventKind) ??
1503
1689
  (hitType === "transparent" || opacity < 0.999
1504
1690
  ? "transparency"
@@ -1507,6 +1693,8 @@ export function evaluateWavefrontContinuationEvent(options = {}) {
1507
1693
  : metalness >= 0.5 || roughness <= 0.2
1508
1694
  ? "reflection"
1509
1695
  : "diffuse");
1696
+ let eventKind = requestedEventKind;
1697
+ let totalInternalReflection = false;
1510
1698
 
1511
1699
  let nextDirection = incomingDirection;
1512
1700
  let attenuation;
@@ -1519,10 +1707,20 @@ export function evaluateWavefrontContinuationEvent(options = {}) {
1519
1707
  attenuation = mixVec3([0.04, 0.04, 0.04], albedo, metalness);
1520
1708
  } else if (eventKind === "refraction") {
1521
1709
  const etaRatio = frontFace ? 1 / refractiveIndex : refractiveIndex;
1522
- nextDirection =
1523
- refractDirection(incomingDirection, orientedNormal, etaRatio) ??
1524
- reflectDirection(incomingDirection, orientedNormal);
1525
- attenuation = transmissionStrength > 0.001 ? transmission : [1, 1, 1];
1710
+ const refractedDirection = refractDirection(
1711
+ incomingDirection,
1712
+ orientedNormal,
1713
+ etaRatio
1714
+ );
1715
+ if (refractedDirection) {
1716
+ nextDirection = refractedDirection;
1717
+ attenuation = transmissionStrength > 0.001 ? transmission : [1, 1, 1];
1718
+ } else {
1719
+ totalInternalReflection = true;
1720
+ eventKind = "reflection";
1721
+ nextDirection = reflectDirection(incomingDirection, orientedNormal);
1722
+ attenuation = mixVec3([0.04, 0.04, 0.04], albedo, metalness);
1723
+ }
1526
1724
  } else if (eventKind === "transparency") {
1527
1725
  nextDirection = incomingDirection;
1528
1726
  const transparencyWeight = Math.max(1 - opacity, transmissionStrength, 0.05);
@@ -1540,18 +1738,174 @@ export function evaluateWavefrontContinuationEvent(options = {}) {
1540
1738
  const nextThroughput = multiplyVec3(throughput, saturateVec3(attenuation));
1541
1739
  const continueTracing =
1542
1740
  eventKind !== "terminate" && colorLuminance(nextThroughput) > 0.0001;
1741
+ const mediumState = evaluateWavefrontMediumState({
1742
+ currentMediumRefId: options.currentMediumRefId ?? options.mediumRefId,
1743
+ surfaceMediumRefId: options.surfaceMediumRefId,
1744
+ mediumStack: options.mediumStack,
1745
+ frontFace,
1746
+ eventKind,
1747
+ });
1543
1748
 
1544
1749
  return Object.freeze({
1545
1750
  hitType,
1751
+ requestedEventKind,
1546
1752
  eventKind,
1547
1753
  continueTracing,
1754
+ totalInternalReflection,
1548
1755
  nextDirection: Object.freeze(nextDirection),
1549
1756
  attenuation: Object.freeze(saturateVec3(attenuation)),
1550
1757
  nextThroughput: Object.freeze(nextThroughput),
1758
+ mediumState,
1551
1759
  explicitLightSamplingEnabled: Boolean(options.explicitLightSampling),
1552
1760
  });
1553
1761
  }
1554
1762
 
1763
+ export function evaluateWavefrontVisibilityProbe(options = {}) {
1764
+ const probeMode = normalizeWavefrontVisibilityProbeMode(
1765
+ options.probeMode ??
1766
+ (options.explicitLightSampling ? "mis-balanced" : "disabled")
1767
+ );
1768
+ const probeRay = createWavefrontVisibilityProbeRay({
1769
+ ...options.probeRay,
1770
+ throughput: options.throughput ?? options.probeRay?.throughput,
1771
+ direction: options.direction ?? options.probeRay?.direction,
1772
+ mediumRefId: options.currentMediumRefId ?? options.probeRay?.mediumRefId,
1773
+ mediumStack: options.mediumStack ?? options.probeRay?.mediumStack,
1774
+ });
1775
+ const transparentSegments = Array.isArray(options.transparentSegments)
1776
+ ? options.transparentSegments
1777
+ : [];
1778
+ const transmittance = transparentSegments.reduce(
1779
+ (current, segment) =>
1780
+ multiplyVec3(current, saturateVec3(normalizeVec3(segment, [1, 1, 1]))),
1781
+ [1, 1, 1]
1782
+ );
1783
+ const emissiveRadiance = saturateVec3(
1784
+ normalizeVec3(options.emissiveRadiance, [0, 0, 0])
1785
+ );
1786
+ const environmentRadiance = saturateVec3(
1787
+ normalizeVec3(options.environmentRadiance, [0, 0, 0])
1788
+ );
1789
+ const activeEmissiveRadiance = saturateVec3(
1790
+ normalizeVec3(options.activeEmissiveRadiance, [0, 0, 0])
1791
+ );
1792
+ const prefersEnvironment = Boolean(options.prefersEnvironment);
1793
+ const sourceRadiance =
1794
+ prefersEnvironment || colorLuminance(emissiveRadiance) <= 0.000001
1795
+ ? environmentRadiance
1796
+ : emissiveRadiance;
1797
+ const rawContribution = multiplyVec3(
1798
+ probeRay.throughput,
1799
+ multiplyVec3(sourceRadiance, transmittance)
1800
+ );
1801
+ const activeEmissiveVisible =
1802
+ colorLuminance(activeEmissiveRadiance) > 0.000001;
1803
+ const misWeight =
1804
+ probeMode === "mis-balanced" && activeEmissiveVisible ? 0.5 : 1;
1805
+ const contribution =
1806
+ probeMode === "exclusive-emissive" && activeEmissiveVisible
1807
+ ? [0, 0, 0]
1808
+ : scaleVec3(rawContribution, misWeight);
1809
+
1810
+ return Object.freeze({
1811
+ probeMode,
1812
+ probeRay,
1813
+ transmittance: Object.freeze(transmittance),
1814
+ misWeight,
1815
+ doubleCountPrevented:
1816
+ activeEmissiveVisible && probeMode !== "disabled",
1817
+ contribution: Object.freeze(contribution),
1818
+ });
1819
+ }
1820
+
1821
+ export function evaluateWavefrontMaterialReference(options = {}) {
1822
+ const material = Object.freeze({
1823
+ albedo: Object.freeze(
1824
+ saturateVec3(normalizeVec3(options.albedo, [0.8, 0.8, 0.8]))
1825
+ ),
1826
+ emission: Object.freeze(
1827
+ saturateVec3(normalizeVec3(options.emission, [0, 0, 0]))
1828
+ ),
1829
+ roughness: clampUnit(options.roughness, 0.5),
1830
+ metalness: clampUnit(options.metalness, 0),
1831
+ opacity: clampUnit(options.opacity, 1),
1832
+ transmission: Object.freeze(
1833
+ saturateVec3(normalizeVec3(options.transmission, [0, 0, 0]))
1834
+ ),
1835
+ refractiveIndex: Math.max(
1836
+ 1,
1837
+ readFinite(options.refractiveIndex ?? options.ior, 1.45)
1838
+ ),
1839
+ });
1840
+ const continuation = evaluateWavefrontContinuationEvent({
1841
+ ...options,
1842
+ albedo: material.albedo,
1843
+ roughness: material.roughness,
1844
+ metalness: material.metalness,
1845
+ opacity: material.opacity,
1846
+ transmission: material.transmission,
1847
+ refractiveIndex: material.refractiveIndex,
1848
+ });
1849
+ const terminal = evaluateWavefrontTerminalRadiance({
1850
+ ...options,
1851
+ emission: material.emission,
1852
+ });
1853
+
1854
+ return Object.freeze({
1855
+ material,
1856
+ terminal,
1857
+ continuation,
1858
+ throughputUpdate: continuation.nextThroughput,
1859
+ });
1860
+ }
1861
+
1862
+ export function createWavefrontReferenceFixture(options = {}) {
1863
+ const tolerance = Math.max(0.0001, readFinite(options.tolerance, 0.0005));
1864
+ const ray = createWavefrontRayPayload({
1865
+ rayId: options.rayId,
1866
+ parentRayId: options.parentRayId,
1867
+ sourcePixelId: options.sourcePixelId,
1868
+ sampleId: options.sampleId,
1869
+ bounce: options.bounceIndex ?? options.bounce,
1870
+ origin: options.origin,
1871
+ direction: options.direction ?? scaleVec3(options.viewDirection ?? [0, 0, 1], -1),
1872
+ throughput: options.throughput,
1873
+ mediumRefId: options.currentMediumRefId ?? options.mediumRefId,
1874
+ mediumStack: options.mediumStack,
1875
+ });
1876
+ const reference = evaluateWavefrontMaterialReference(options);
1877
+ const visibilityProbe =
1878
+ options.visibilityProbe === undefined
1879
+ ? null
1880
+ : evaluateWavefrontVisibilityProbe({
1881
+ ...options.visibilityProbe,
1882
+ throughput: options.visibilityProbe.throughput ?? ray.throughput,
1883
+ });
1884
+ const accumulationRadiance = addVec3(
1885
+ reference.terminal.radiance,
1886
+ visibilityProbe?.contribution ?? [0, 0, 0]
1887
+ );
1888
+
1889
+ return Object.freeze({
1890
+ tolerance,
1891
+ ray,
1892
+ material: reference.material,
1893
+ continuation: reference.continuation,
1894
+ terminal: reference.terminal,
1895
+ visibilityProbe,
1896
+ accumulation: Object.freeze({
1897
+ sourcePixelId: ray.sourcePixelId,
1898
+ sampleCount: reference.terminal.terminated ? 1 : 0,
1899
+ radiance: Object.freeze(accumulationRadiance),
1900
+ throughput: reference.continuation.nextThroughput,
1901
+ resetEpoch: Math.max(
1902
+ 0,
1903
+ Math.trunc(readFinite(options.accumulationResetEpoch, 0))
1904
+ ),
1905
+ }),
1906
+ });
1907
+ }
1908
+
1555
1909
  const lightingImportanceLevels = Object.freeze([
1556
1910
  "low",
1557
1911
  "medium",
@@ -13,6 +13,10 @@ const EVENT_KIND_REFRACTION: u32 = 2u;
13
13
  const EVENT_KIND_TRANSPARENCY: u32 = 3u;
14
14
  const EVENT_KIND_TERMINATE: u32 = 4u;
15
15
 
16
+ const RAY_KIND_PATH: u32 = 0u;
17
+ const RAY_KIND_VISIBILITY_PROBE: u32 = 1u;
18
+ const RAY_KIND_MASK: u32 = 0x3u;
19
+
16
20
  const LIGHTING_EPSILON: f32 = 0.0001;
17
21
  const LIGHTING_INV_PI: f32 = 0.3183098861837907;
18
22
 
@@ -147,6 +151,10 @@ fn is_terminal_hit_type(hit_type: u32) -> bool {
147
151
  hit_type == HIT_TYPE_MISS;
148
152
  }
149
153
 
154
+ fn ray_kind(flags: u32) -> u32 {
155
+ return flags & RAY_KIND_MASK;
156
+ }
157
+
150
158
  fn environment_radiance(direction: vec3<f32>) -> vec3<f32> {
151
159
  let upward = saturate_scalar(direction.y * 0.5 + 0.5);
152
160
  let directional = mix(