@meimu/mutracer 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/README.md +61 -16
  2. package/dist/assets/bvh.worker.js +1 -1
  3. package/dist/mutracer.umd.js +668 -658
  4. package/docs/materials.md +27 -7
  5. package/docs/progressive-rendering.md +24 -8
  6. package/docs/threejs.md +15 -3
  7. package/docs/viewer.md +53 -0
  8. package/lib/Display.d.ts +1 -1
  9. package/lib/Display.js +4 -2
  10. package/lib/Display.js.map +1 -1
  11. package/lib/Render.d.ts +9 -0
  12. package/lib/Render.js +84 -13
  13. package/lib/Render.js.map +1 -1
  14. package/lib/RenderLoop.d.ts +5 -2
  15. package/lib/RenderLoop.js +47 -15
  16. package/lib/RenderLoop.js.map +1 -1
  17. package/lib/oidn.d.ts +1 -1
  18. package/lib/oidnBundledWeights.cdn.d.ts +2 -2
  19. package/lib/renderer/MaterialTable.d.ts +1 -0
  20. package/lib/renderer/MaterialTable.js +2 -1
  21. package/lib/renderer/MaterialTable.js.map +1 -1
  22. package/lib/renderer/WGPUBloomPass.js +8 -8
  23. package/lib/renderer/WGPUJNLMDenoiser.js +2 -2
  24. package/lib/renderer/WGPURayTraceOutput.js +5 -5
  25. package/lib/renderer/WGPURayTracePass.js +1 -1
  26. package/lib/renderer/WGPURayTracePass.js.map +1 -1
  27. package/lib/renderer/WGPURenderingPipeline.js +8 -6
  28. package/lib/renderer/WGPURenderingPipeline.js.map +1 -1
  29. package/lib/renderer/WebGLJNLMDenoiser.js +2 -2
  30. package/lib/renderer/WebGLRayTracePass.js +3 -1
  31. package/lib/renderer/WebGLRayTracePass.js.map +1 -1
  32. package/lib/renderer/committedScene.js +11 -6
  33. package/lib/renderer/committedScene.js.map +1 -1
  34. package/lib/renderer/glsl/chunks/envMap.glsl.js +5 -5
  35. package/lib/renderer/glsl/chunks/intersect.glsl.js +32 -32
  36. package/lib/renderer/glsl/chunks/materialBuffer.glsl.js +9 -9
  37. package/lib/renderer/glsl/chunks/openPbrBsdf.glsl.js +30 -30
  38. package/lib/renderer/glsl/chunks/rand.glsl.js +2 -2
  39. package/lib/renderer/glsl/chunks/rayTraceCore.glsl.js +4 -4
  40. package/lib/renderer/glsl/chunks/sampleLight.glsl.js +3 -3
  41. package/lib/renderer/glsl/chunks/sampleMaterial.glsl.js +4 -4
  42. package/lib/renderer/glsl/chunks/sampling.glsl.js +3 -3
  43. package/lib/renderer/glsl/chunks/sobol.glsl.js +3 -3
  44. package/lib/renderer/glsl/rayTrace.frag.js +7 -3
  45. package/lib/renderer/glsl/rayTrace.frag.js.map +1 -1
  46. package/lib/renderer/glsl/webglPipeline.frag.js +2 -2
  47. package/lib/renderer/materialComparison.generated.js +5 -0
  48. package/lib/renderer/materialComparison.generated.js.map +1 -1
  49. package/lib/renderer/materialLayout.js +2 -1
  50. package/lib/renderer/materialLayout.js.map +1 -1
  51. package/lib/renderer/prepareMeshBVH.d.ts +4 -2
  52. package/lib/renderer/prepareMeshBVH.js +44 -20
  53. package/lib/renderer/prepareMeshBVH.js.map +1 -1
  54. package/lib/renderer/util.d.ts +2 -2
  55. package/lib/renderer/util.js +3 -3
  56. package/lib/renderer/util.js.map +1 -1
  57. package/lib/renderer/wgsl/chunks/common.wgsl.js +2 -2
  58. package/lib/renderer/wgsl/chunks/envMap.wgsl.js +14 -14
  59. package/lib/renderer/wgsl/chunks/intersect.wgsl.js +85 -88
  60. package/lib/renderer/wgsl/chunks/intersect.wgsl.js.map +1 -1
  61. package/lib/renderer/wgsl/chunks/openPbrBsdf.wgsl.js +34 -34
  62. package/lib/renderer/wgsl/chunks/pdm_displacement.wgsl.d.ts +1 -1
  63. package/lib/renderer/wgsl/chunks/pdm_displacement.wgsl.js +51 -46
  64. package/lib/renderer/wgsl/chunks/pdm_displacement.wgsl.js.map +1 -1
  65. package/lib/renderer/wgsl/chunks/rand.wgsl.js +2 -2
  66. package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.js +3 -3
  67. package/lib/renderer/wgsl/chunks/sampleLight.wgsl.js +4 -4
  68. package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.js +4 -4
  69. package/lib/renderer/wgsl/chunks/sampling.wgsl.js +3 -3
  70. package/lib/renderer/wgsl/chunks/sobol.wgsl.js +3 -3
  71. package/lib/renderer/wgsl/compose.frag.wgsl.js +2 -2
  72. package/lib/renderer/wgsl/customDenoise.wgsl.js +4 -4
  73. package/lib/renderer/wgsl/offlineAdaptive.compute.js +10 -10
  74. package/lib/renderer/wgsl/rayTrace.compute.js +13 -9
  75. package/lib/renderer/wgsl/rayTrace.compute.js.map +1 -1
  76. package/lib/scene/OpenPBRMaterial.d.ts +4 -2
  77. package/lib/scene/OpenPBRMaterial.js +4 -2
  78. package/lib/scene/OpenPBRMaterial.js.map +1 -1
  79. package/lib/scene/openPbrSchema.d.ts +7 -2
  80. package/lib/scene/openPbrSchema.js +2 -1
  81. package/lib/scene/openPbrSchema.js.map +1 -1
  82. package/lib/util/fromTHREE.d.ts +6 -0
  83. package/lib/util/fromTHREE.js +85 -19
  84. package/lib/util/fromTHREE.js.map +1 -1
  85. package/lib/version.d.ts +1 -1
  86. package/lib/version.js +1 -1
  87. package/lib/version.js.map +1 -1
  88. package/package.json +1 -1
@@ -39,6 +39,7 @@ displacementMapIndex,
39
39
  nX(displacementMapIndex),
40
40
  minH, maxH,
41
41
  hScale,
42
+ matData.displacementBias,
42
43
  matData.displacementStepSize,
43
44
  i32(matData.displacementUvProjection),
44
45
  displacementUvProjectionScale,
@@ -52,24 +53,20 @@ const displacementIntersectCode = function (anyHit, hasAnisotropyDirections) {
52
53
 
53
54
  let hScale = od.w;
54
55
  let th = bitcast<i32>(r3.w);
55
- if (abs(hScale) > 0.0 && th > 0) {
56
56
 
57
+ let materialIndex = i32(ae(bitcast<i32>(od.z)).x);
58
+ let matData = materialsBuffer[materialIndex];
57
59
  let n0 = normalize(attrBuffers[index.x * ATTR_STRIDE + NORMAL_OFF]${hasAnisotropyDirections ? '.xyz' : ''});
58
60
  let n1 = normalize(attrBuffers[index.y * ATTR_STRIDE + NORMAL_OFF]${hasAnisotropyDirections ? '.xyz' : ''});
59
61
  let n2 = normalize(attrBuffers[index.z * ATTR_STRIDE + NORMAL_OFF]${hasAnisotropyDirections ? '.xyz' : ''});
60
-
62
+ if (matData.displacementMapIndex >= 0.0 && matData.displacementBias != 0.0) {
63
+ tri.p0 += n0 * matData.displacementBias;
64
+ tri.p1 += n1 * matData.displacementBias;
65
+ tri.p2 += n2 * matData.displacementBias;
66
+ }
67
+ if (abs(hScale) > 0.0 && th > 0) {
61
68
  let minH = f32(th >> 8) / 255.0;
62
69
  let maxH = f32(th & 0xff) / 255.0;
63
-
64
- let pA = tri.p0 + n0 * hScale * minH;
65
- let pB = tri.p1 + n1 * hScale * minH;
66
- let pC = tri.p2 + n2 * hScale * minH;
67
- let pD = tri.p0 + n0 * hScale * maxH;
68
- let pE = tri.p1 + n1 * hScale * maxH;
69
- let pF = tri.p2 + n2 * hScale * maxH;
70
-
71
- let materialIndex = i32(ae(bitcast<i32>(od.z)).x);
72
- let matData = materialsBuffer[materialIndex];
73
70
  let displacementUvProjectionScale = vec2f(matData.displacementUvProjectionScale);
74
71
  let displacementMapIndex = i32(matData.displacementMapIndex);
75
72
 
@@ -147,59 +144,59 @@ bitcast<f32>(u32(rs + 127) << 23u)
147
144
  let bJ = eV(gv, o);
148
145
  let eH = (n0.xyz - o) * gv;
149
146
  let da = hG * gv;
150
- let wl = bp(bitcast<u32>(n2.x));
151
- let wm = bp(bitcast<u32>(n2.y));
152
- let wn = bp(bitcast<u32>(n2.z));
153
- let wo = bp(bitcast<u32>(n2.w));
154
- let wp = bp(bitcast<u32>(n3.x));
155
- let wq = bp(bitcast<u32>(n3.y));
156
- let wf = bp(bitcast<u32>(n3.z));
157
- let wg = bp(bitcast<u32>(n3.w));
158
- let wh = bp(bitcast<u32>(n4.x));
159
- let wi = bp(bitcast<u32>(n4.y));
160
- let wj = bp(bitcast<u32>(n4.z));
161
- let wk = bp(bitcast<u32>(n4.w));
162
-
163
- let uZ = wl * da.x + eH.x;
164
- let vb = wn * da.y + eH.y;
165
- let vd = wp * da.z + eH.z;
166
- let uT = wf * da.x + eH.x;
167
- let uV = wh * da.y + eH.y;
168
- let uX = wj * da.z + eH.z;
169
- let wr = max(
170
- max((min(uZ, uT) - vec4f(bJ.x)), (min(vb, uV) - vec4f(bJ.y))),
171
- (min(vd, uX) - vec4f(bJ.z))
147
+ let wm = bp(bitcast<u32>(n2.x));
148
+ let wn = bp(bitcast<u32>(n2.y));
149
+ let wo = bp(bitcast<u32>(n2.z));
150
+ let wp = bp(bitcast<u32>(n2.w));
151
+ let wq = bp(bitcast<u32>(n3.x));
152
+ let wr = bp(bitcast<u32>(n3.y));
153
+ let wg = bp(bitcast<u32>(n3.z));
154
+ let wh = bp(bitcast<u32>(n3.w));
155
+ let wi = bp(bitcast<u32>(n4.x));
156
+ let wj = bp(bitcast<u32>(n4.y));
157
+ let wk = bp(bitcast<u32>(n4.z));
158
+ let wl = bp(bitcast<u32>(n4.w));
159
+
160
+ let vb = wm * da.x + eH.x;
161
+ let vd = wo * da.y + eH.y;
162
+ let vg = wq * da.z + eH.z;
163
+ let uV = wg * da.x + eH.x;
164
+ let uX = wi * da.y + eH.y;
165
+ let uZ = wk * da.z + eH.z;
166
+ let ws = max(
167
+ max((min(vb, uV) - vec4f(bJ.x)), (min(vd, uX) - vec4f(bJ.y))),
168
+ (min(vg, uZ) - vec4f(bJ.z))
172
169
  );
173
- let vZ = min(
174
- min((max(uZ, uT) + vec4f(bJ.x)), (max(vb, uV) + vec4f(bJ.y))),
175
- (max(vd, uX) + vec4f(bJ.z))
170
+ let wa = min(
171
+ min((max(vb, uV) + vec4f(bJ.x)), (max(vd, uX) + vec4f(bJ.y))),
172
+ (max(vg, uZ) + vec4f(bJ.z))
176
173
  );
177
174
 
178
- let va = wm * da.x + eH.x;
179
- let vc = wo * da.y + eH.y;
180
- let ve = wq * da.z + eH.z;
181
- let uU = wg * da.x + eH.x;
182
- let uW = wi * da.y + eH.y;
183
- let uY = wk * da.z + eH.z;
184
- let ws = max(
185
- max((min(va, uU) - vec4f(bJ.x)), (min(vc, uW) - vec4f(bJ.y))),
186
- (min(ve, uY) - vec4f(bJ.z))
175
+ let vc = wn * da.x + eH.x;
176
+ let ve = wp * da.y + eH.y;
177
+ let vh = wr * da.z + eH.z;
178
+ let uW = wh * da.x + eH.x;
179
+ let uY = wj * da.y + eH.y;
180
+ let va = wl * da.z + eH.z;
181
+ let wt = max(
182
+ max((min(vc, uW) - vec4f(bJ.x)), (min(ve, uY) - vec4f(bJ.y))),
183
+ (min(vh, va) - vec4f(bJ.z))
187
184
  );
188
- let wa = min(
189
- min((max(va, uU) + vec4f(bJ.x)), (max(vc, uW) + vec4f(bJ.y))),
190
- (max(ve, uY) + vec4f(bJ.z))
185
+ let wb = min(
186
+ min((max(vc, uW) + vec4f(bJ.x)), (max(ve, uY) + vec4f(bJ.y))),
187
+ (max(vh, va) + vec4f(bJ.z))
191
188
  );
192
189
 
193
- let uw = (wr <= min(vZ, vec4f(tMax))) & (vZ > vec4f(0.0));
194
- let ux = (ws <= min(wa, vec4f(tMax))) & (wa > vec4f(0.0));
195
- return select(0u, 1u, uw.x)
196
- | select(0u, 2u, uw.y)
197
- | select(0u, 4u, uw.z)
198
- | select(0u, 8u, uw.w)
199
- | select(0u, 16u, ux.x)
200
- | select(0u, 32u, ux.y)
201
- | select(0u, 64u, ux.z)
202
- | select(0u, 128u, ux.w);
190
+ let uy = (ws <= min(wa, vec4f(tMax))) & (wa > vec4f(0.0));
191
+ let uz = (wt <= min(wb, vec4f(tMax))) & (wb > vec4f(0.0));
192
+ return select(0u, 1u, uy.x)
193
+ | select(0u, 2u, uy.y)
194
+ | select(0u, 4u, uy.z)
195
+ | select(0u, 8u, uy.w)
196
+ | select(0u, 16u, uz.x)
197
+ | select(0u, 32u, uz.y)
198
+ | select(0u, 64u, uz.z)
199
+ | select(0u, 128u, uz.w);
203
200
  }
204
201
 
205
202
  `;
@@ -255,8 +252,8 @@ o = ray.o;
255
252
  d = ray.d;
256
253
  gv = eX(d);
257
254
 
258
- let vR = bvhBuffer[sI];
259
- let eR = bitcast<i32>(vR.w);
255
+ let vS = bvhBuffer[sI];
256
+ let eR = bitcast<i32>(vS.w);
260
257
  if (eR >= 0) {
261
258
  let left = sI + 2;
262
259
  let right = eR;
@@ -295,7 +292,7 @@ mi--
295
292
  ) {
296
293
  let pm = bvhBuffer[sI + 2 + mi];
297
294
  let cf = bitcast<i32>(pm.x);
298
- let uG = bitcast<i32>(pm.y);
295
+ let uI = bitcast<i32>(pm.y);
299
296
  let od = instancesBuffer[cf * ${INSTANCE_VEC4_STRIDE}];
300
297
  let w2l = mat4x4<f32>(
301
298
  instancesBuffer[cf * ${INSTANCE_VEC4_STRIDE} + 1],
@@ -326,10 +323,10 @@ var rK = 0u;
326
323
  var kN = 0u;
327
324
 
328
325
  var ep = 0x80000000u;
329
- let uf = select(0u, 4u, d.x < 0.0)
326
+ let ug = select(0u, 4u, d.x < 0.0)
330
327
  | select(0u, 2u, d.y < 0.0)
331
328
  | select(0u, 1u, d.z < 0.0);
332
- let pZ = 7u - uf;
329
+ let pZ = 7u - ug;
333
330
 
334
331
 
335
332
 
@@ -375,13 +372,13 @@ rm >= 4u
375
372
  let sR =
376
373
  (sn >> ((rm & 3u) * 8u)) & 0xffu;
377
374
  let ju = sR & 0x1fu;
378
- let uF = select(
375
+ let uH = select(
379
376
  ju,
380
377
  ju ^ pZ,
381
378
  ju >= 24u
382
379
  );
383
- let uA = (sR >> 5u) & 0x7u;
384
- tf |= uA << uF;
380
+ let uC = (sR >> 5u) & 0x7u;
381
+ tf |= uC << uH;
385
382
  }
386
383
 
387
384
  kN = bitcast<u32>(fC.x);
@@ -415,7 +412,7 @@ var dR = -1;
415
412
  if (gL != 3u) {
416
413
  dR = 0;
417
414
 
418
- gn[0] = uG;
415
+ gn[0] = uI;
419
416
  }
420
417
 
421
418
  while (dR >= 0) {
@@ -428,23 +425,23 @@ var primitiveOffset = 0;
428
425
  ${(blasLayoutMask & 4) !== 0 ? /* wgsl */ `
429
426
  if (gL == 2u) {
430
427
  let i = node + bo;
431
- let uK = bvhBuffer[i];
432
- let uL = bvhBuffer[i + 1];
433
- let uM = bvhBuffer[i + 2];
434
- let uN = bvhBuffer[i + 3];
435
- bh = i32(bitcast<u32>(uM.w));
428
+ let uM = bvhBuffer[i];
429
+ let uN = bvhBuffer[i + 1];
430
+ let uO = bvhBuffer[i + 2];
431
+ let uP = bvhBuffer[i + 3];
432
+ bh = i32(bitcast<u32>(uO.w));
436
433
  if (bh > 0) {
437
434
  primitiveOffset = bo + blasOffset
438
- + i32(bitcast<u32>(uN.w));
435
+ + i32(bitcast<u32>(uP.w));
439
436
  }
440
437
  else {
441
- let left = blasOffset + i32(bitcast<u32>(uK.w) * 4u);
442
- let right = blasOffset + i32(bitcast<u32>(uL.w) * 4u);
438
+ let left = blasOffset + i32(bitcast<u32>(uM.w) * 4u);
439
+ let right = blasOffset + i32(bitcast<u32>(uN.w) * 4u);
443
440
  let oi = cM(
444
- o, uK.xyz, uL.xyz, gv, tMax
441
+ o, uM.xyz, uN.xyz, gv, tMax
445
442
  );
446
443
  let nd = cM(
447
- o, uM.xyz, uN.xyz, gv, tMax
444
+ o, uO.xyz, uP.xyz, gv, tMax
448
445
  );
449
446
  if (oi > nd) {
450
447
  if (oi > 0.0) {
@@ -475,12 +472,12 @@ primitiveOffset = bo + blasOffset
475
472
  }
476
473
  else {
477
474
  let i = node + bo;
478
- let tV = bvhBuffer[i];
475
+ let tW = bvhBuffer[i];
479
476
  let ra = bvhBuffer[i + 1];
480
477
  let rb = bvhBuffer[i + 2];
481
478
  let rc = bvhBuffer[i + 3];
482
- let nodeMin = tV.xyz;
483
- let ec = vec3f(tV.w, ra.x, ra.y);
479
+ let nodeMin = tW.xyz;
480
+ let ec = vec3f(tW.w, ra.x, ra.y);
484
481
  let lS = array<u32, 4>(
485
482
  bitcast<u32>(ra.z), bitcast<u32>(ra.w),
486
483
  bitcast<u32>(rb.x), bitcast<u32>(rb.y)
@@ -777,8 +774,8 @@ d = ht(light.radiusOrSpread, light.position, r);
777
774
  (*lightSample).isDelta = false;
778
775
  }
779
776
  if (d != INF) {
780
- let uQ = r.o + d * r.d;
781
- let mo = bL(light, normalize(r.o - uQ));
777
+ let uS = r.o + d * r.d;
778
+ let mo = bL(light, normalize(r.o - uS));
782
779
  if (mo <= 0.0) { return INF; }
783
780
  if (light.radiusOrSpread > 0.0) {
784
781
  (*lightSample).direction = r.d;
@@ -1175,12 +1172,12 @@ return abs(gv) * max(vec3f(1.0), abs(o)) * 1e-6;
1175
1172
 
1176
1173
 
1177
1174
 
1178
- fn cM(o: vec3f, vw: vec3f, vu: vec3f, gv: vec3f, tMax: f32) -> f32 {
1179
- let uD = (vw - o) * gv;
1180
- let uE = (vu - o) * gv;
1175
+ fn cM(o: vec3f, vx: vec3f, vw: vec3f, gv: vec3f, tMax: f32) -> f32 {
1176
+ let uF = (vx - o) * gv;
1177
+ let uG = (vw - o) * gv;
1181
1178
  let bJ = eV(gv, o);
1182
- let my = min(uD, uE) - bJ;
1183
- let rH = max(uD, uE) + bJ;
1179
+ let my = min(uF, uG) - bJ;
1180
+ let rH = max(uF, uG) + bJ;
1184
1181
  let t0 = max(my.x, max(my.y, my.z));
1185
1182
  let t1 = min(rH.x, min(rH.y, rH.z));
1186
1183
  return select(select(t1, t0, t0 > 0.0), -1.0, (t0 > t1 || t0 > tMax));
@@ -1 +1 @@
1
- {"version":3,"file":"intersect.wgsl.js","sourceRoot":"","sources":["../../../../src/renderer/wgsl/chunks/intersect.wgsl.ts"],"names":[],"mappings":"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","sourcesContent":["// Portions adapted from ray-tracing-renderer@0.10.15\n// (https://github.com/hoverinc/ray-tracing-renderer).\n// Copyright (c) 2019 HOVER, MIT license; see LICENSES/RayTracingRenderer-MIT.txt.\n\nimport { uvProjectionConstants } from '../../MaterialTable.js';\nimport { INSTANCE_VEC4_STRIDE } from '../../bvhAccel.js';\nimport pdm_displacementWgsl from './pdm_displacement.wgsl.js';\nimport { createIntersectionAlphaWgsl } from './intersectionAlpha.wgsl.js';\n\nconst displacementWgsl = pdm_displacementWgsl;\n\nconst flatIntersectCode = function () {\n return /* wgsl */ `\nlet hit = intersectTriangle(o, d, tri, maxDim, shear, tMax);\nif (hit.t > 0.0) {\n let e1 = tri.p1 - tri.p0;\n let e2 = tri.p2 - tri.p0;\n hitNormal = normalize(cross(e1, e2));\n let sideMaterialIndex = i32(unpackMaterialIndex(bitcast<i32>(insData.z)).x);\n let materialDoubleSided = materialsBuffer[sideMaterialIndex].doubleSided > 0.5;\n if (!doubleSided && !materialDoubleSided && dot(hitNormal, d) > 0.0) {\n hitNormal = vec3f(0.0);\n continue;\n }\n\n tMax = hit.t;\n isHit = true;\n isHitCurrent = true;\n hitPosition = o + tMax * d;\n hitBarycentric = hit.barycentric;\n}\n`;\n};\n\nfunction getPDMDisplacmentCode(anyHit: boolean) {\n return /*wgsl */ `\n let microTriangle = intersectDisplacement(\n o, d, tMax,\n tri.p0, tri.p1, tri.p2,\n n0, n1, n2,\n uv0, uv1, uv2,\n displacementMapIndex,\n getMapSize(displacementMapIndex),\n minH, maxH,\n hScale,\n matData.displacementStepSize,\n i32(matData.displacementUvProjection),\n displacementUvProjectionScale,\n ${anyHit ? 'true' : 'false'},\n isRefractionRay\n );\n`;\n}\n\nconst displacementIntersectCode = function (anyHit: boolean, hasAnisotropyDirections: boolean) {\n return /* wgsl */ `\n\nlet hScale = insData.w;\nlet minMaxH = bitcast<i32>(r3.w);\nif (abs(hScale) > 0.0 && minMaxH > 0) {\n // Scale normal by displacement scale\n let n0 = normalize(attrBuffers[index.x * ATTR_STRIDE + NORMAL_OFF]${hasAnisotropyDirections ? '.xyz' : ''});\n let n1 = normalize(attrBuffers[index.y * ATTR_STRIDE + NORMAL_OFF]${hasAnisotropyDirections ? '.xyz' : ''});\n let n2 = normalize(attrBuffers[index.z * ATTR_STRIDE + NORMAL_OFF]${hasAnisotropyDirections ? '.xyz' : ''});\n\n let minH = f32(minMaxH >> 8) / 255.0;\n let maxH = f32(minMaxH & 0xff) / 255.0;\n\n let pA = tri.p0 + n0 * hScale * minH;\n let pB = tri.p1 + n1 * hScale * minH;\n let pC = tri.p2 + n2 * hScale * minH;\n let pD = tri.p0 + n0 * hScale * maxH;\n let pE = tri.p1 + n1 * hScale * maxH;\n let pF = tri.p2 + n2 * hScale * maxH;\n // TODO not support material blending\n let materialIndex = i32(unpackMaterialIndex(bitcast<i32>(insData.z)).x);\n let matData = materialsBuffer[materialIndex];\n let displacementUvProjectionScale = vec2f(matData.displacementUvProjectionScale);\n let displacementMapIndex = i32(matData.displacementMapIndex);\n\n // TODO duplicate reading of normal and uv\n var uv0 = attrBuffers[index.x * ATTR_STRIDE + UV_OFF].xy;\n var uv1 = attrBuffers[index.y * ATTR_STRIDE + UV_OFF].xy;\n var uv2 = attrBuffers[index.z * ATTR_STRIDE + UV_OFF].xy;\n\n ${getPDMDisplacmentCode(anyHit)}\n\n if (microTriangle.t < tMax) {\n hitNormal = microTriangle.normal;\n // TODO support double sided to improve non watertight triangles.\n\n tMax = microTriangle.t;\n // TODO barycentric is for micro triangle\n hitBarycentric = microTriangle.barycentric;\n hitPosition = o + tMax * d;\n\n isHitMicroTriangle = true;\n isHit = true;\n isHitCurrent = true;\n }\n}\nelse {\n ${flatIntersectCode()}\n}\n`;\n};\n\nconst cwbvhTraversalWgsl = /* wgsl */ `\nfn unpackCwbvhBytes(value: u32) -> vec4f {\n return vec4f(\n f32(value & 0xffu),\n f32((value >> 8u) & 0xffu),\n f32((value >> 16u) & 0xffu),\n f32(value >> 24u)\n );\n}\n\n// CWBVH calls these helpers only with nonzero 8/24-bit masks. Every such\n// integer is exactly representable as f32, whose unbiased IEEE-754 exponent\n// gives the highest set bit. This avoids both the full-width firstLeadingBit\n// polyfill on some shader backends and a serial bit-spread/popcount chain.\n// Do not use this conversion for full 32-bit masks: rounding near a power of\n// two can change their highest bit. Zero is also outside this contract.\nfn cwbvhHighestBitIndex8(value: u32) -> u32 {\n return (bitcast<u32>(f32(value)) >> 23u) - 127u;\n}\n\nfn cwbvhHighestBitIndex24(value: u32) -> u32 {\n return (bitcast<u32>(f32(value)) >> 23u) - 127u;\n}\n\nfn intersectCwbvhInterior(\n node: i32,\n bufferOffset: i32,\n n0: vec4f,\n o: vec3f,\n invD: vec3f,\n tMax: f32\n) -> u32 {\n let i = node + bufferOffset;\n let n2 = bvhBuffer[i + 2];\n let n3 = bvhBuffer[i + 3];\n let n4 = bvhBuffer[i + 4];\n let header = bitcast<u32>(n0.w);\n let exponentX = bitcast<i32>((header & 0xffu) << 24u) >> 24;\n let exponentY = bitcast<i32>(((header >> 8u) & 0xffu) << 24u) >> 24;\n let exponentZ = bitcast<i32>(((header >> 16u) & 0xffu) << 24u) >> 24;\n let quantizationScale = vec3f(\n bitcast<f32>(u32(exponentX + 127) << 23u),\n bitcast<f32>(u32(exponentY + 127) << 23u),\n bitcast<f32>(u32(exponentZ + 127) << 23u)\n );\n let padding = depthSlabPadding(invD, o);\n let originTerm = (n0.xyz - o) * invD;\n let adjustedInvD = quantizationScale * invD;\n let minX0 = unpackCwbvhBytes(bitcast<u32>(n2.x));\n let minX1 = unpackCwbvhBytes(bitcast<u32>(n2.y));\n let minY0 = unpackCwbvhBytes(bitcast<u32>(n2.z));\n let minY1 = unpackCwbvhBytes(bitcast<u32>(n2.w));\n let minZ0 = unpackCwbvhBytes(bitcast<u32>(n3.x));\n let minZ1 = unpackCwbvhBytes(bitcast<u32>(n3.y));\n let maxX0 = unpackCwbvhBytes(bitcast<u32>(n3.z));\n let maxX1 = unpackCwbvhBytes(bitcast<u32>(n3.w));\n let maxY0 = unpackCwbvhBytes(bitcast<u32>(n4.x));\n let maxY1 = unpackCwbvhBytes(bitcast<u32>(n4.y));\n let maxZ0 = unpackCwbvhBytes(bitcast<u32>(n4.z));\n let maxZ1 = unpackCwbvhBytes(bitcast<u32>(n4.w));\n\n let minTx0 = minX0 * adjustedInvD.x + originTerm.x;\n let minTy0 = minY0 * adjustedInvD.y + originTerm.y;\n let minTz0 = minZ0 * adjustedInvD.z + originTerm.z;\n let maxTx0 = maxX0 * adjustedInvD.x + originTerm.x;\n let maxTy0 = maxY0 * adjustedInvD.y + originTerm.y;\n let maxTz0 = maxZ0 * adjustedInvD.z + originTerm.z;\n let near0 = max(\n max((min(minTx0, maxTx0) - vec4f(padding.x)), (min(minTy0, maxTy0) - vec4f(padding.y))),\n (min(minTz0, maxTz0) - vec4f(padding.z))\n );\n let far0 = min(\n min((max(minTx0, maxTx0) + vec4f(padding.x)), (max(minTy0, maxTy0) + vec4f(padding.y))),\n (max(minTz0, maxTz0) + vec4f(padding.z))\n );\n\n let minTx1 = minX1 * adjustedInvD.x + originTerm.x;\n let minTy1 = minY1 * adjustedInvD.y + originTerm.y;\n let minTz1 = minZ1 * adjustedInvD.z + originTerm.z;\n let maxTx1 = maxX1 * adjustedInvD.x + originTerm.x;\n let maxTy1 = maxY1 * adjustedInvD.y + originTerm.y;\n let maxTz1 = maxZ1 * adjustedInvD.z + originTerm.z;\n let near1 = max(\n max((min(minTx1, maxTx1) - vec4f(padding.x)), (min(minTy1, maxTy1) - vec4f(padding.y))),\n (min(minTz1, maxTz1) - vec4f(padding.z))\n );\n let far1 = min(\n min((max(minTx1, maxTx1) + vec4f(padding.x)), (max(minTy1, maxTy1) + vec4f(padding.y))),\n (max(minTz1, maxTz1) + vec4f(padding.z))\n );\n\n let hits0 = (near0 <= min(far0, vec4f(tMax))) & (far0 > vec4f(0.0));\n let hits1 = (near1 <= min(far1, vec4f(tMax))) & (far1 > vec4f(0.0));\n return select(0u, 1u, hits0.x)\n | select(0u, 2u, hits0.y)\n | select(0u, 4u, hits0.z)\n | select(0u, 8u, hits0.w)\n | select(0u, 16u, hits1.x)\n | select(0u, 32u, hits1.y)\n | select(0u, 64u, hits1.z)\n | select(0u, 128u, hits1.w);\n}\n\n`;\n\nfunction getBVHTraverseCode(\n handleHitCode: string,\n hasDisplacement: boolean,\n anyHit: boolean,\n blasLayoutMask: number,\n hasAnisotropyDirections: boolean,\n skipTriangleCode = ''\n) {\n const intersectTriangles = /* wgsl */ `\n let insData = instancesBuffer[insIdx * ${INSTANCE_VEC4_STRIDE}];\n let vertexOffset = vec3i(bitcast<i32>(insData.y));\n for (var k = 0; k < primitiveCount; k++) {\n let triangleIndex = primitiveOffset + k;\n ${skipTriangleCode}\n if (bounces > 0 && triangleIndex == currentTriangleIndex) {\n continue;\n }\n \n let r3 = bvhBuffer[triangleIndex];\n let index = bitcast<vec3i>(r3.xyz) + vertexOffset;\n var tri = Triangle(\n attrBuffers[index.x * ATTR_STRIDE + POSITION_OFF]${hasAnisotropyDirections ? '.xyz' : ''},\n attrBuffers[index.y * ATTR_STRIDE + POSITION_OFF]${hasAnisotropyDirections ? '.xyz' : ''},\n attrBuffers[index.z * ATTR_STRIDE + POSITION_OFF]${hasAnisotropyDirections ? '.xyz' : ''}\n );\n var isHitCurrent = false;\n\n ${hasDisplacement\n ? displacementIntersectCode(anyHit, hasAnisotropyDirections)\n : flatIntersectCode()}\n if (isHitCurrent) {\n ${handleHitCode}\n }\n }\n`;\n return /* wgsl */ `\nvar d = ray.d;\nvar o = ray.o;\nvar tlasStack: array<i32, TLAS_STACK_SIZE>;\nvar tlasTop = -1;\nvar invD = traversalInverse(d);\nvar isHitMicroTriangle = false;\nvar isHit = false;\nvar hitPosition: vec3f;\nvar hitNormal: vec3f;\nvar hitBarycentric: vec3f;\n\n\nif (intersectBox(o, bvhBuffer[0].xyz, bvhBuffer[1].xyz, invD, tMax) > 0.0) {\n tlasTop = 0;\n tlasStack[0] = 0;\n}\nwhile (tlasTop >= 0) {\n let tlasNode = tlasStack[tlasTop];\n tlasTop--;\n o = ray.o;\n d = ray.d;\n invD = traversalInverse(d);\n\n let tlasR1 = bvhBuffer[tlasNode];\n let tlasPointerOrPrimitiveCount = bitcast<i32>(tlasR1.w);\n if (tlasPointerOrPrimitiveCount >= 0) {\n let left = tlasNode + 2;\n let right = tlasPointerOrPrimitiveCount;\n let d0 = intersectBox(\n o,\n bvhBuffer[left].xyz,\n bvhBuffer[left + 1].xyz,\n invD,\n tMax\n );\n let d1 = intersectBox(\n o,\n bvhBuffer[right].xyz,\n bvhBuffer[right + 1].xyz,\n invD,\n tMax\n );\n\n if (d0 > d1) {\n if (d0 > 0.0) { tlasTop++; tlasStack[tlasTop] = left; }\n if (d1 > 0.0) { tlasTop++; tlasStack[tlasTop] = right; }\n }\n else {\n if (d1 > 0.0) { tlasTop++; tlasStack[tlasTop] = right; }\n if (d0 > 0.0) { tlasTop++; tlasStack[tlasTop] = left; }\n }\n }\n else {\n let tlasPrimitiveCount = -tlasPointerOrPrimitiveCount;\n // The previous unified LIFO stack visited leaf instances in reverse order.\n // Preserve that order for alpha-tested any-hit rays and deterministic RNG.\n for (\n var instanceOffset = tlasPrimitiveCount - 1;\n instanceOffset >= 0;\n instanceOffset--\n ) {\n let instanceRecord = bvhBuffer[tlasNode + 2 + instanceOffset];\n let insIdx = bitcast<i32>(instanceRecord.x);\n let blasRoot = bitcast<i32>(instanceRecord.y);\n let insData = instancesBuffer[insIdx * ${INSTANCE_VEC4_STRIDE}];\n let w2l = mat4x4<f32>(\n instancesBuffer[insIdx * ${INSTANCE_VEC4_STRIDE} + 1],\n instancesBuffer[insIdx * ${INSTANCE_VEC4_STRIDE} + 2],\n instancesBuffer[insIdx * ${INSTANCE_VEC4_STRIDE} + 3],\n instancesBuffer[insIdx * ${INSTANCE_VEC4_STRIDE} + 4]\n );\n o = (w2l * vec4f(ray.o, 1.0)).xyz;\n d = (w2l * vec4f(ray.d, 0.0)).xyz;\n invD = traversalInverse(d);\n maxDim = maxDimension(abs(d));\n shear = getShear(d, maxDim);\n\n let encodedBlasOffset = bitcast<u32>(insData.x);\n let blasLayout = encodedBlasOffset >> 30u;\n let blasOffset = i32(encodedBlasOffset & 0x3fffffffu);\n let bufferOff = rtUniform.blasOffset;\n\n ${(blasLayoutMask & 8) !== 0 ? /* wgsl */ `\n if (blasLayout == 3u) {\n // TinyBVH's official CWBVH traversal keeps compact node groups on the\n // stack. The upper 8 bits identify intersected interior slots and the\n // lower 8 bits preserve the node's interior-slot mask for popcount\n // addressing. Triangle leaves are merged into a separate 24-bit mask.\n var cwbvhStack: array<vec2u, BLAS_STACK_SIZE>;\n var cwbvhTop = -1;\n var cwbvhVisits = 0u;\n var nodeGroupBase = 0u;\n // A synthetic slot-7 hit selects relative node zero for the root.\n var nodeGroupMask = 0x80000000u;\n let octant = select(0u, 4u, d.x < 0.0)\n | select(0u, 2u, d.y < 0.0)\n | select(0u, 1u, d.z < 0.0);\n let octantInverse = 7u - octant;\n\n // Keep a statically visible trip bound. DXC can otherwise miscompile\n // the dynamic stack-top loop into a non-terminating shader on D3D12.\n // Validated scene rays stay far below this conservative safety cap.\n while (cwbvhVisits < 4096u) {\n cwbvhVisits++;\n \n let internalMask = nodeGroupMask & 0xffu;\n let childBitIndex =\n cwbvhHighestBitIndex8(nodeGroupMask >> 24u) + 24u;\n nodeGroupMask &= ~(1u << childBitIndex);\n if ((nodeGroupMask & 0xff000000u) != 0u) {\n cwbvhTop++;\n \n cwbvhStack[cwbvhTop] = vec2u(nodeGroupBase, nodeGroupMask);\n }\n\n let slot = (childBitIndex - 24u) ^ octantInverse;\n let precedingMask = (1u << slot) - 1u;\n let relativeIndex = countOneBits(internalMask & precedingMask);\n let childNodeIndex = nodeGroupBase + relativeIndex;\n let node = blasOffset + i32(childNodeIndex * 5u);\n let compressed0 = bvhBuffer[node + bufferOff];\n let slotHits = intersectCwbvhInterior(\n node, bufferOff, compressed0, o, invD, tMax\n );\n // Empty child hits need neither metadata nor leaf processing. Clear\n // the current group so the common tail restores pending siblings.\n nodeGroupMask = 0u;\n if (slotHits != 0u) {\n let compressed1 = bvhBuffer[node + bufferOff + 1];\n let nodeInternalMask = bitcast<u32>(compressed0.w) >> 24u;\n var hitMask = 0u;\n var remainingSlotHits = slotHits;\n while (remainingSlotHits != 0u) {\n let childSlot = cwbvhHighestBitIndex8(remainingSlotHits);\n remainingSlotHits &= ~(1u << childSlot);\n let metaWord = select(\n bitcast<u32>(compressed1.z),\n bitcast<u32>(compressed1.w),\n childSlot >= 4u\n );\n let childMeta =\n (metaWord >> ((childSlot & 3u) * 8u)) & 0xffu;\n let localOffset = childMeta & 0x1fu;\n let bitIndex = select(\n localOffset,\n localOffset ^ octantInverse,\n localOffset >= 24u\n );\n let childBits = (childMeta >> 5u) & 0x7u;\n hitMask |= childBits << bitIndex;\n }\n\n nodeGroupBase = bitcast<u32>(compressed1.x);\n nodeGroupMask = (hitMask & 0xff000000u) | nodeInternalMask;\n let triangleBase = bitcast<u32>(compressed1.y);\n var triangleMask = hitMask & 0x00ffffffu;\n while (triangleMask != 0u) {\n let triangleBitIndex = cwbvhHighestBitIndex24(triangleMask);\n triangleMask &= ~(1u << triangleBitIndex);\n let primitiveCount = 1;\n let primitiveOffset = bufferOff + blasOffset\n + i32(triangleBase + triangleBitIndex);\n ${intersectTriangles}\n }\n }\n\n if ((nodeGroupMask & 0xff000000u) != 0u) { continue; }\n if (cwbvhTop >= 0) {\n let restoredGroup = cwbvhStack[cwbvhTop];\n nodeGroupBase = restoredGroup.x;\n nodeGroupMask = restoredGroup.y;\n cwbvhTop--;\n }\n else { break; }\n }\n }\n ` : ''}\n\n var blasStack: array<i32, BLAS_STACK_SIZE>;\n var blasTop = -1;\n if (blasLayout != 3u) {\n blasTop = 0;\n \n blasStack[0] = blasRoot;\n }\n\n while (blasTop >= 0) {\n \n let node = blasStack[blasTop];\n blasTop--;\n var primitiveCount = 0;\n var primitiveOffset = 0;\n\n ${(blasLayoutMask & 4) !== 0 ? /* wgsl */ `\n if (blasLayout == 2u) {\n let i = node + bufferOff;\n let gpu0 = bvhBuffer[i];\n let gpu1 = bvhBuffer[i + 1];\n let gpu2 = bvhBuffer[i + 2];\n let gpu3 = bvhBuffer[i + 3];\n primitiveCount = i32(bitcast<u32>(gpu2.w));\n if (primitiveCount > 0) {\n primitiveOffset = bufferOff + blasOffset\n + i32(bitcast<u32>(gpu3.w));\n }\n else {\n let left = blasOffset + i32(bitcast<u32>(gpu0.w) * 4u);\n let right = blasOffset + i32(bitcast<u32>(gpu1.w) * 4u);\n let leftDistance = intersectBox(\n o, gpu0.xyz, gpu1.xyz, invD, tMax\n );\n let rightDistance = intersectBox(\n o, gpu2.xyz, gpu3.xyz, invD, tMax\n );\n if (leftDistance > rightDistance) {\n if (leftDistance > 0.0) {\n blasTop++; blasStack[blasTop] = left;\n }\n if (rightDistance > 0.0) {\n blasTop++; blasStack[blasTop] = right;\n }\n }\n else {\n if (rightDistance > 0.0) {\n blasTop++; blasStack[blasTop] = right;\n }\n if (leftDistance > 0.0) {\n blasTop++; blasStack[blasTop] = left;\n }\n }\n }\n }\n ` : ''}\n ${(blasLayoutMask & 2) !== 0 ? /* wgsl */ `\n if (blasLayout == 1u) {\n let nodeInfo = bitcast<u32>(node);\n if ((nodeInfo & 0x80000000u) != 0u) {\n primitiveCount = i32((nodeInfo >> 23u) & 0xffu);\n primitiveOffset = bufferOff + blasOffset\n + i32(nodeInfo & 0x007fffffu);\n }\n else {\n let i = node + bufferOff;\n let wide0 = bvhBuffer[i];\n let wide1 = bvhBuffer[i + 1];\n let wide2 = bvhBuffer[i + 2];\n let wide3 = bvhBuffer[i + 3];\n let nodeMin = wide0.xyz;\n let nodeExtent = vec3f(wide0.w, wide1.x, wide1.y);\n let childInfos = array<u32, 4>(\n bitcast<u32>(wide1.z), bitcast<u32>(wide1.w),\n bitcast<u32>(wide2.x), bitcast<u32>(wide2.y)\n );\n let childMinX = unpack4x8unorm(bitcast<u32>(wide2.z));\n let childMinY = unpack4x8unorm(bitcast<u32>(wide2.w));\n let childMinZ = unpack4x8unorm(bitcast<u32>(wide3.x));\n let childMaxX = unpack4x8unorm(bitcast<u32>(wide3.y));\n let childMaxY = unpack4x8unorm(bitcast<u32>(wide3.z));\n let childMaxZ = unpack4x8unorm(bitcast<u32>(wide3.w));\n let tx0 = (nodeMin.x + childMinX * nodeExtent.x - o.x) * invD.x;\n let tx1 = (nodeMin.x + childMaxX * nodeExtent.x - o.x) * invD.x;\n let ty0 = (nodeMin.y + childMinY * nodeExtent.y - o.y) * invD.y;\n let ty1 = (nodeMin.y + childMaxY * nodeExtent.y - o.y) * invD.y;\n let tz0 = (nodeMin.z + childMinZ * nodeExtent.z - o.z) * invD.z;\n let tz1 = (nodeMin.z + childMaxZ * nodeExtent.z - o.z) * invD.z;\n let padding = depthSlabPadding(invD, o);\n let tNear = max(\n max((min(tx0, tx1) - vec4f(padding.x)), (min(ty0, ty1) - vec4f(padding.y))),\n (min(tz0, tz1) - vec4f(padding.z))\n );\n let tFar = min(\n min((max(tx0, tx1) + vec4f(padding.x)), (max(ty0, ty1) + vec4f(padding.y))),\n (max(tz0, tz1) + vec4f(padding.z))\n );\n var distance = select(tFar, tNear, tNear > vec4f(0.0));\n distance = select(\n distance,\n vec4f(-1.0),\n tNear > min(tFar, vec4f(tMax))\n );\n var childDistances = array<f32, 4>(\n distance.x,\n distance.y,\n distance.z,\n distance.w\n );\n\n // Push far-to-near so that the closest child is popped first.\n for (var order = 0; order < 4; order++) {\n var farthestDistance = -1.0;\n var farthestChild = -1;\n for (var child = 0; child < 4; child++) {\n if (\n childInfos[child] != 0u &&\n childDistances[child] > farthestDistance\n ) {\n farthestDistance = childDistances[child];\n farthestChild = child;\n }\n }\n if (farthestChild >= 0 && farthestDistance > 0.0) {\n let childInfo = childInfos[farthestChild];\n var childAddress = bitcast<i32>(childInfo);\n if ((childInfo & 0x80000000u) == 0u) {\n childAddress += blasOffset;\n }\n blasTop++;\n blasStack[blasTop] = childAddress;\n childDistances[farthestChild] = -1.0;\n }\n }\n }\n }\n ` : ''}\n ${(blasLayoutMask & 1) !== 0 ? /* wgsl */ `\n if (blasLayout == 0u) {\n let i = node + bufferOff;\n let r1 = bvhBuffer[i];\n let pointerToRightOrNumPrims = bitcast<i32>(r1.w);\n\n if (pointerToRightOrNumPrims >= 0) {\n let left = node + 2;\n let right = pointerToRightOrNumPrims + blasOffset;\n let d0 = intersectBox(\n o,\n bvhBuffer[left + bufferOff].xyz,\n bvhBuffer[left + 1 + bufferOff].xyz,\n invD,\n tMax\n );\n let d1 = intersectBox(\n o,\n bvhBuffer[right + bufferOff].xyz,\n bvhBuffer[right + 1 + bufferOff].xyz,\n invD,\n tMax\n );\n\n if (d0 > d1) {\n if (d0 > 0.0) {\n blasTop++; blasStack[blasTop] = left;\n \n }\n if (d1 > 0.0) {\n blasTop++; blasStack[blasTop] = right;\n \n }\n }\n else {\n if (d1 > 0.0) {\n blasTop++; blasStack[blasTop] = right;\n \n }\n if (d0 > 0.0) {\n blasTop++; blasStack[blasTop] = left;\n \n }\n }\n }\n else {\n primitiveCount = -pointerToRightOrNumPrims;\n primitiveOffset = i + 2;\n }\n }\n ` : ''}\n if (primitiveCount > 0) {\n ${intersectTriangles}\n }\n }\n }\n }\n}\n\n`;\n}\nexport default function getIntersectCode({\n hasDisplacement,\n hasAnisotropyDirections = true,\n hasCoatNormals = false,\n blasLayoutMask = 1\n}: {\n hasDisplacement: boolean;\n hasAnisotropyDirections?: boolean;\n hasCoatNormals?: boolean;\n blasLayoutMask?: number;\n}) {\n return /*wgsl*/ `\n\n${hasDisplacement ? displacementWgsl : ''}\n${(blasLayoutMask & 8) !== 0 ? cwbvhTraversalWgsl : ''}\n\nstruct Triangle {\n p0: vec3f,\n p1: vec3f,\n p2: vec3f,\n};\n\nfn intersectSphere(rad: f32, pos: vec3f, r: Ray) -> f32 {\n let op: vec3f = pos - r.o;\n let eps: f32 = 0.001;\n let b: f32 = dot(op, r.d);\n var det: f32 = b * b - dot(op, op) + rad * rad;\n if (det < 0.0) {\n return INF;\n }\n\n det = sqrt(det);\n let t1: f32 = b - det;\n if (t1 > eps) {\n return t1;\n }\n\n let t2: f32 = b + det;\n if (t2 > eps) {\n return t2;\n }\n\n return INF;\n}\n\nfn intersectRect(pos: vec3f, u: vec3f, v: vec3f, n: vec3f, r: Ray) -> f32 {\n let dt: f32 = dot(r.d, n);\n let t: f32 = (dot(n, pos) - dot(n, r.o)) / dt;\n\n if (t > EPS) {\n let p: vec3f = r.o + r.d * t;\n let vi: vec3f = p - pos;\n let a1: f32 = dot(u / dot(u, u), vi);\n if (a1 >= 0.0 && a1 <= 1.0) {\n let a2: f32 = dot(v / dot(v, v), vi);\n if (a2 >= 0.0 && a2 <= 1.0) {\n return t;\n }\n }\n }\n\n return INF;\n}\n\nfn sphereSolidAnglePdf(origin: vec3f, center: vec3f, radius: f32) -> f32 {\n let centerDistance = distance(origin, center);\n if (centerDistance <= radius) { return INV4PI; }\n let sinThetaMaxSquared = min(1.0, radius * radius / (centerDistance * centerDistance));\n let cosThetaMax = sqrt(max(0.0, 1.0 - sinThetaMaxSquared));\n return 1.0 / max(TWOPI * (1.0 - cosThetaMax), 1e-8);\n}\n\nfn intersectLight(r: Ray, light: Light, lightSample: ptr<function, LightSampleRec>) -> f32 {\n // Analytic continuation hits use the same conditional light density as NEE.\n // Initialize every field because this record is reused across the light loop.\n var d: f32 = INF;\n (*lightSample).direction = r.d;\n (*lightSample).normal = -r.d;\n (*lightSample).dist = INF;\n (*lightSample).pdf = 0.0;\n (*lightSample).emission = vec3f(0.0);\n (*lightSample).isDelta = false;\n var u = light.u;\n var v = light.v;\n var lightType = i32(light.litType);\n\n if (lightType == RECT_LIGHT) {\n // Adjust the position from center to start corner\n var pos = light.position - u * 0.5 - v * 0.5;\n var normal = normalize(cross(u, v));\n if (dot(normal, r.d) > 0.0) { // Hide backfacing quad light\n return INF;\n }\n\n d = intersectRect(pos, u, v, normal, r);\n if (d < 0.0) {\n d = INF;\n }\n else {\n var cosTheta: f32 = dot(-r.d, normal);\n let spread = light.radiusOrSpread;\n if (cosTheta >= cos(spread * PI / 2.0)) {\n (*lightSample).direction = r.d;\n (*lightSample).normal = normal;\n (*lightSample).dist = d;\n (*lightSample).pdf = (d * d) / (light.area * cosTheta);\n (*lightSample).emission = light.emission;\n (*lightSample).isDelta = false;\n }\n else {\n d = INF;\n }\n }\n }\n\n else if (lightType == SPHERE_LIGHT) {\n if (light.radiusOrSpread <= 0.0) {\n let toPoint = light.position - r.o;\n let projection = dot(toPoint, r.d);\n let perpendicular = toPoint - projection * r.d;\n if (projection <= EPS || dot(perpendicular, perpendicular) > 1e-8) {\n return INF;\n }\n d = projection;\n (*lightSample).direction = r.d;\n (*lightSample).normal = -r.d;\n (*lightSample).dist = d;\n (*lightSample).pdf = 1.0;\n (*lightSample).isDelta = true;\n }\n else {\n d = intersectSphere(light.radiusOrSpread, light.position, r);\n (*lightSample).isDelta = false;\n if (d > 0.0 && d != INF) {\n (*lightSample).direction = r.d;\n (*lightSample).dist = d;\n (*lightSample).normal = normalize(\n r.o + r.d * d - light.position\n );\n (*lightSample).pdf = sphereSolidAnglePdf(\n r.o,\n light.position,\n light.radiusOrSpread\n );\n }\n }\n if (d < 0.0) {\n d = INF;\n }\n else {\n (*lightSample).direction = r.d;\n (*lightSample).dist = d;\n (*lightSample).emission = select(\n light.emission,\n light.emission / max(d * d, 1e-8),\n light.radiusOrSpread <= 0.0\n );\n }\n }\n\n else if (lightType == SPOT_LIGHT) {\n if (light.radiusOrSpread <= 0.0) {\n let toPoint = light.position - r.o;\n let projection = dot(toPoint, r.d);\n let perpendicular = toPoint - projection * r.d;\n if (projection <= EPS || dot(perpendicular, perpendicular) > 1e-8) {\n return INF;\n }\n d = projection;\n (*lightSample).direction = r.d;\n (*lightSample).normal = -r.d;\n (*lightSample).dist = d;\n (*lightSample).pdf = 1.0;\n (*lightSample).isDelta = true;\n }\n else {\n d = intersectSphere(light.radiusOrSpread, light.position, r);\n (*lightSample).isDelta = false;\n }\n if (d != INF) {\n let hitPt = r.o + d * r.d;\n let multiplier = evaluateSpotEmissionMultiplier(light, normalize(r.o - hitPt));\n if (multiplier <= 0.0) { return INF; }\n if (light.radiusOrSpread > 0.0) {\n (*lightSample).direction = r.d;\n (*lightSample).dist = d;\n (*lightSample).normal = normalize(\n r.o + r.d * d - light.position\n );\n (*lightSample).pdf = sphereSolidAnglePdf(r.o, light.position, light.radiusOrSpread);\n }\n (*lightSample).emission = light.emission * multiplier * select(\n 1.0,\n 1.0 / max(d * d, 1e-8),\n light.radiusOrSpread <= 0.0\n );\n }\n }\n\n else if (lightType != RECT_LIGHT && lightType != SPHERE_LIGHT && lightType != SPOT_LIGHT) {\n // Unsupported light\n d = INF;\n }\n\n return d;\n}\n\nfn randomPickMaterial(m0: vec3i, m1: vec3i, m2: vec3i, bary: vec3f) -> i32 {\n let m00 = unpackMaterialIndex(m0.x);\n let m01 = unpackMaterialIndex(m0.y);\n let m02 = unpackMaterialIndex(m0.z);\n let m10 = unpackMaterialIndex(m1.x);\n let m11 = unpackMaterialIndex(m1.y);\n let m12 = unpackMaterialIndex(m1.z);\n let m20 = unpackMaterialIndex(m2.x);\n let m21 = unpackMaterialIndex(m2.y);\n let m22 = unpackMaterialIndex(m2.z);\n var w00 = m00.y * bary.x;\n var w01 = m01.y * bary.x;\n var w02 = m02.y * bary.x;\n var w10 = m10.y * bary.y;\n var w11 = m11.y * bary.y;\n var w12 = m12.y * bary.y;\n var w20 = m20.y * bary.z;\n var w21 = m21.y * bary.z;\n var w22 = m22.y * bary.z;\n\n var wTotal = w00 + w01 + w02 + w10 + w11 + w12 + w20 + w21 + w22;\n // Accumulate weights\n w00 = w00 / wTotal;\n w01 = w01 / wTotal + w00;\n w02 = w02 / wTotal + w01;\n w10 = w10 / wTotal + w02;\n w11 = w11 / wTotal + w10;\n w12 = w12 / wTotal + w11;\n w20 = w20 / wTotal + w12;\n w21 = w21 / wTotal + w20;\n w22 = w22 / wTotal + w21;\n\n let rand = hashedSample(0u);\n var mIdx: f32;\n if (rand < w00) {\n mIdx = m00.x;\n } else if (rand < w01) {\n mIdx = m01.x;\n } else if (rand < w02) {\n mIdx = m02.x;\n } else if (rand < w10) {\n mIdx = m10.x;\n } else if (rand < w11) {\n mIdx = m11.x;\n } else if (rand < w12) {\n mIdx = m12.x;\n } else if (rand < w20) {\n mIdx = m20.x;\n } else if (rand < w21) {\n mIdx = m21.x;\n } else {\n mIdx = m22.x;\n }\n return i32(mIdx);\n}\n\nfn resolveSurfaceMaterialIndex(\n fallbackMaterialIndex: i32,\n index: vec3i,\n bary: vec3f\n) -> i32 {\n let m0 = vec3i(attrBuffers[index.x * ATTR_STRIDE + MATERIALINDEX_OFF]${hasAnisotropyDirections ? '.xyz' : ''});\n let m1 = vec3i(attrBuffers[index.y * ATTR_STRIDE + MATERIALINDEX_OFF]${hasAnisotropyDirections ? '.xyz' : ''});\n let m2 = vec3i(attrBuffers[index.z * ATTR_STRIDE + MATERIALINDEX_OFF]${hasAnisotropyDirections ? '.xyz' : ''});\n if (m0.x > 0 || m1.x > 0 || m2.x > 0) {\n return randomPickMaterial(m0, m1, m2, bary);\n }\n return fallbackMaterialIndex;\n}\n\n\n\n${hasAnisotropyDirections ? /* wgsl */ `\nfn anisotropy_axes(normal: vec3f, dp1: vec3f, dp2: vec3f, duv1: vec2f, duv2: vec2f, input: vec4f) -> mat2x3f {\n let determinant = duv1.x * duv2.y - duv1.y * duv2.x;\n var t = input.xyz;\n var b = vec3f(0.0);\n // Internal W=2 marks an authored XYZ tangent whose sign is derived from UVs.\n var sign = select(input.w, 0.0, abs(input.w) > 1.5);\n if (abs(determinant) > 1e-12) {\n let dt = (dp1 * duv2.y - dp2 * duv1.y) / determinant;\n let db = (dp2 * duv1.x - dp1 * duv2.x) / determinant;\n if (dot(t, t) < 1e-8) { t = dt; }\n if (sign == 0.0) { sign = select(1.0, -1.0, dot(cross(normal, dt), db) < 0.0); }\n }\n t -= normal * dot(normal, t);\n if (dot(t, t) < 1e-8) {\n let signZ = select(-1.0, 1.0, normal.z >= 0.0);\n let factor = -1.0 / (signZ + normal.z);\n let xy = normal.x * normal.y * factor;\n t = vec3f(1.0 + signZ * normal.x * normal.x * factor, signZ * xy, -signZ * normal.x);\n b = vec3f(xy, signZ + normal.y * normal.y * factor, -normal.y);\n }\n else { t = normalize(t); b = normalize(cross(normal, t)); }\n return mat2x3f(t, b * select(1.0, -1.0, sign < 0.0));\n}\n\n` : /* wgsl */ ``}\nfn fetchAttributes(\n si: ptr<function, SurfaceInteraction>,\n tri: Triangle,\n bary: vec3f,\n index: vec3i,\n isHitMicroTriangle: bool,\n resolvedMaterialIndex: i32\n) {\n (*si).hit = true;\n // Get projected uv\n let material = materialsBuffer[(*si).materialIndex];\n let uvProjection = i32(material.uvProjection);\n\n let i0 = index.x;\n let i1 = index.y;\n let i2 = index.z;\n let isSphericalProj = uvProjection == ${uvProjectionConstants.spherical};\n\n var normal: vec3f = (*si).normal;\n var normalForSphericalProj: vec3f;\n var n0 = vec3f(0.0);\n var n1 = vec3f(0.0);\n var n2 = vec3f(0.0);\n // Otherwise use the normal from the microtriangle\n // The exception is spherical projection, we need the original normal to project the uv. Or the case like earth has wrong normal.\n if (!isHitMicroTriangle || isSphericalProj) {\n n0 = attrBuffers[i0 * ATTR_STRIDE + NORMAL_OFF]${hasAnisotropyDirections ? '.xyz' : ''};\n n1 = attrBuffers[i1 * ATTR_STRIDE + NORMAL_OFF]${hasAnisotropyDirections ? '.xyz' : ''};\n n2 = attrBuffers[i2 * ATTR_STRIDE + NORMAL_OFF]${hasAnisotropyDirections ? '.xyz' : ''};\n normalForSphericalProj = normalize(bary.x * n0 + bary.y * n1 + bary.z * n2);\n if (!isHitMicroTriangle) {\n normal = normalForSphericalProj;\n }\n }\n\n var uv: vec2f;\n var uv0: vec2f;\n var uv1: vec2f;\n var uv2: vec2f;\n var secondaryUv = vec2f(0.0);\n if (USE_UV) {\n uv0 = attrBuffers[i0 * ATTR_STRIDE + UV_OFF].xy;\n uv1 = attrBuffers[i1 * ATTR_STRIDE + UV_OFF].xy;\n uv2 = attrBuffers[i2 * ATTR_STRIDE + UV_OFF].xy;\n uv = bary.x * uv0 + bary.y * uv1 + bary.z * uv2;\n if (USE_UV1) {\n let secondaryUv0 = attrBuffers[i0 * ATTR_STRIDE + UV1_OFF].xy;\n let secondaryUv1 = attrBuffers[i1 * ATTR_STRIDE + UV1_OFF].xy;\n let secondaryUv2 = attrBuffers[i2 * ATTR_STRIDE + UV1_OFF].xy;\n secondaryUv = bary.x * secondaryUv0\n + bary.y * secondaryUv1\n + bary.z * secondaryUv2;\n }\n }\n\n if (USE_TANGENT) {\n let t0 = attrBuffers[i0 * ATTR_STRIDE + TANGENT_OFF]${hasAnisotropyDirections ? '.xyz' : ''};\n let t1 = attrBuffers[i1 * ATTR_STRIDE + TANGENT_OFF]${hasAnisotropyDirections ? '.xyz' : ''};\n let t2 = attrBuffers[i2 * ATTR_STRIDE + TANGENT_OFF]${hasAnisotropyDirections ? '.xyz' : ''};\n (*si).tangent = normalize(bary.x * t0 + bary.y * t1 + bary.z * t2);\n }\n\n if (resolvedMaterialIndex >= 0) {\n (*si).materialIndex = resolvedMaterialIndex;\n }\n else {\n (*si).materialIndex = resolveSurfaceMaterialIndex(\n (*si).materialIndex,\n index,\n bary\n );\n }\n\n if (uvProjection > 0) {\n // NOTE: position and normal are not transformed to world space yet.\n uv = projectUV(uvProjection, (*si).position, select(normal, normalForSphericalProj, isSphericalProj));\n secondaryUv = uv;\n }\n\n uv = uv * material.uvScale;\n secondaryUv = secondaryUv * material.uvScale;\n\n // Force repeating UVs for triplanar mapping\n if (uvProjection == ${uvProjectionConstants.triplanar}) {\n uv = fract(uv);\n }\n\n${hasAnisotropyDirections ? /* wgsl */ `\n${hasCoatNormals ? /* wgsl */ ` (*si).coatNormal = normal;` : ''}\n let directionMaterial = materialsBuffer[(*si).materialIndex];\n if (directionMaterial.anisotropyMapIndex >= 0.0 || directionMaterial.specularAnisotropyRotation != 0.0 || directionMaterial.coatAnisotropyRotation != 0.0${hasCoatNormals ? \" || (USE_COAT_NORMAL_MAPS && directionMaterial.coatNormalMapIndex >= 0.0)\" : \"\"}) {\n let dp1 = tri.p0 - tri.p2;\n let dp2 = tri.p1 - tri.p2;\n var inputTangent = vec4f(0.0);\n if (USE_TANGENT) {\n inputTangent = bary.x * attrBuffers[i0 * ATTR_STRIDE + TANGENT_OFF]\n + bary.y * attrBuffers[i1 * ATTR_STRIDE + TANGENT_OFF]\n + bary.z * attrBuffers[i2 * ATTR_STRIDE + TANGENT_OFF];\n }\n let geometryAxes = anisotropy_axes(normal, dp1, dp2, uv0-uv2, uv1-uv2, inputTangent);\n (*si).geometryTangent = geometryAxes[0];\n (*si).geometryBitangent = geometryAxes[1];\n var directionAxes = geometryAxes;\n if (USE_UV1 && directionMaterial.anisotropyMapIndex >= 0.0 && mapInfo[i32(directionMaterial.anisotropyMapIndex) + NUM_MAPS * 2].x > 0.5) {\n let a = attrBuffers[i0 * ATTR_STRIDE + UV1_OFF].xy;\n let b = attrBuffers[i1 * ATTR_STRIDE + UV1_OFF].xy;\n let c = attrBuffers[i2 * ATTR_STRIDE + UV1_OFF].xy;\n directionAxes = anisotropy_axes(normal, dp1, dp2, a-c, b-c, select(inputTangent, vec4f(0.0), inputTangent.w == 0.0));\n }\n (*si).directionTangent = directionAxes[0];\n (*si).directionBitangent = directionAxes[1];\n${hasCoatNormals ? /* wgsl */ `\n if (USE_COAT_NORMAL_MAPS && directionMaterial.coatNormalMapIndex >= 0.0) {\n let coatIndex = i32(directionMaterial.coatNormalMapIndex);\n var coatAxes = geometryAxes;\n if (USE_UV1 && mapInfo[coatIndex + NUM_MAPS * 2].x > 0.5) {\n let a = attrBuffers[i0 * ATTR_STRIDE + UV1_OFF].xy;\n let b = attrBuffers[i1 * ATTR_STRIDE + UV1_OFF].xy;\n let c = attrBuffers[i2 * ATTR_STRIDE + UV1_OFF].xy;\n coatAxes = anisotropy_axes(normal, dp1, dp2, a-c, b-c,\n select(inputTangent, vec4f(0.0), inputTangent.w == 0.0));\n }\n // Procedural projection overrides both UV sets. UV0 has already applied\n // triplanar wrapping, so use it for either texture channel in that case.\n let decoded = sampleMap(coatIndex, getMapUvSet(coatIndex, uv,\n select(secondaryUv, uv, uvProjection > 0))).rgb * 2.0 - 1.0;\n let mapped = coatAxes[0] * decoded.x * directionMaterial.coatNormalScale.x\n + coatAxes[1] * decoded.y * directionMaterial.coatNormalScale.y + normal * decoded.z;\n if (dot(mapped, mapped) > 1e-12) { (*si).coatNormal = normalize(mapped); }\n }\n` : ''}\n }\n\n` : /* wgsl */ ``}\n (*si).normal = normal;\n (*si).vertexNormal = normal;\n (*si).shadowOffset = vec3f(0.0);\n if (!isHitMicroTriangle) {\n // Smooth-limit geometry offset for direct-light shadow rays. This follows\n // the Cycles shadow terminator construction while keeping the result in\n // local space until the instance transform is available.\n let geometricNormal = normalize(cross(tri.p1 - tri.p0, tri.p2 - tri.p0));\n let smoothNormal = bary.x * n0 + bary.y * n1 + bary.z * n2;\n let a = dot(n2 - n0, tri.p0 - tri.p2);\n let b = dot(n2 - n1, tri.p1 - tri.p2);\n let c = dot(n1 - n0, tri.p1 - tri.p0);\n var h = a * bary.x * (bary.x - 1.0)\n + (a + b + c) * bary.x * bary.y\n + b * bary.y * (bary.y - 1.0);\n if (dot(smoothNormal, geometricNormal) > 0.0) {\n var h0 = max(max(dot(tri.p1 - tri.p0, n0), dot(tri.p2 - tri.p0, n0)), 0.0);\n var h1 = max(max(dot(tri.p0 - tri.p1, n1), dot(tri.p2 - tri.p1, n1)), 0.0);\n var h2 = max(max(dot(tri.p0 - tri.p2, n2), dot(tri.p1 - tri.p2, n2)), 0.0);\n h0 = max(dot(tri.p0 - (*si).position, n0) + h0, 0.0);\n h1 = max(dot(tri.p1 - (*si).position, n1) + h1, 0.0);\n h2 = max(dot(tri.p2 - (*si).position, n2) + h2, 0.0);\n h = max(min(min(h0, h1), h2), h * 0.5);\n }\n else {\n var h0 = max(max(dot(tri.p0 - tri.p1, n0), dot(tri.p0 - tri.p2, n0)), 0.0);\n var h1 = max(max(dot(tri.p1 - tri.p0, n1), dot(tri.p1 - tri.p2, n1)), 0.0);\n var h2 = max(max(dot(tri.p2 - tri.p0, n2), dot(tri.p2 - tri.p1, n2)), 0.0);\n h0 = max(dot((*si).position - tri.p0, n0) + h0, 0.0);\n h1 = max(dot((*si).position - tri.p1, n1) + h1, 0.0);\n h2 = max(dot((*si).position - tri.p2, n2) + h2, 0.0);\n h = min(-min(min(h0, h1), h2), h * 0.5);\n }\n (*si).shadowOffset = smoothNormal * h;\n }\n (*si).uv = uv;\n (*si).uv1 = secondaryUv;\n if (USE_NORMAL_MAPS) {\n let dp1 = tri.p0 - tri.p2;\n let dp2 = tri.p1 - tri.p2;\n let duv1 = uv0 - uv2;\n let duv2 = uv1 - uv2;\n (*si).normal = getMatNormal(\n (*si).materialIndex, uv, normal, dp1, dp2, duv1, duv2\n );\n }\n}\n\nstruct TriangleIntersect {\n t: f32,\n barycentric: vec3f,\n};\n\n\n// Triangle-ray intersection\n// Faster than the classic Möller–Trumbore intersection algorithm\n// http://www.pbr-book.org/3ed-2018/Shapes/Triangle_Meshes.html#TriangleIntersection\nfn intersectTriangle(o: vec3f, d: vec3f, tri: Triangle, maxDim: i32, shear: vec3f, tMax: f32) -> TriangleIntersect {\n var ti: TriangleIntersect;\n\n // translate vertices based on ray origin\n var p0t = tri.p0 - o;\n var p1t = tri.p1 - o;\n var p2t = tri.p2 - o;\n\n // permute components of triangle vertices\n if (maxDim == 0) {\n p0t = p0t.yzx;\n p1t = p1t.yzx;\n p2t = p2t.yzx;\n } else if (maxDim == 1) {\n p0t = p0t.zxy;\n p1t = p1t.zxy;\n p2t = p2t.zxy;\n }\n\n // apply shear transformation to translated vertex positions\n // TODO swizzled assignment is not supported in wgsl\n p0t = vec3f(p0t.xy + shear.xy * p0t.z, p0t.z);\n p1t = vec3f(p1t.xy + shear.xy * p1t.z, p1t.z);\n p2t = vec3f(p2t.xy + shear.xy * p2t.z, p2t.z);\n\n // compute edge function coefficients\n var e = vec3f(\n p1t.x * p2t.y - p1t.y * p2t.x,\n p2t.x * p0t.y - p2t.y * p0t.x,\n p0t.x * p1t.y - p0t.y * p1t.x\n );\n\n if (DEPTH_QUERY_MODE) {\n // Exact grid rays can land on shared edges. Roundoff in the two edge\n // products must not turn the edge into a crack between both triangles.\n let edgeScale = abs(vec3f(p1t.x * p2t.y, p2t.x * p0t.y, p0t.x * p1t.y))\n + abs(vec3f(p1t.y * p2t.x, p2t.y * p0t.x, p0t.y * p1t.x));\n e = select(e, vec3f(0.0), abs(e) <= edgeScale * 1e-6);\n }\n\n // check if intersection is inside triangle\n if (any(e < vec3f(0)) && any(e > vec3f(0))) {\n return ti;\n }\n\n let det = e.x + e.y + e.z;\n if (DEPTH_QUERY_MODE && det == 0.0) { return ti; }\n\n p0t.z *= shear.z;\n p1t.z *= shear.z;\n p2t.z *= shear.z;\n let tScaled = (e.x * p0t.z + e.y * p1t.z + e.z * p2t.z);\n\n // check if closer intersection already exists\n if (abs(tScaled) > abs(tMax * det)) {\n return ti;\n }\n\n let invDet = 1. / det;\n ti.t = tScaled * invDet;\n ti.barycentric = e * invDet;\n\n return ti;\n}\n\n// Pinhole depth queries can lie exactly on a grid vertex or a flat slab.\n// Specialize conservative slabs for that entry point; beauty retains its\n// original traversal arithmetic and random streams.\noverride DEPTH_QUERY_MODE: bool = false;\n\nfn traversalInverse(d: vec3f) -> vec3f {\n if (!DEPTH_QUERY_MODE) { return 1.0 / d; }\n return 1.0 / select(d, select(vec3f(1e-20), vec3f(-1e-20), d < vec3f(0.0)), abs(d) < vec3f(1e-20));\n}\n\nfn depthSlabPadding(invD: vec3f, o: vec3f) -> vec3f {\n if (!DEPTH_QUERY_MODE) { return vec3f(0.0); }\n return abs(invD) * max(vec3f(1.0), abs(o)) * 1e-6;\n}\n\n// Branchless ray/box intersection\n// https://tavianator.com/fast-branchless-raybounding-box-intersections/\nfn intersectBox(o: vec3f, bmin: vec3f, bmax: vec3f, invD: vec3f, tMax: f32) -> f32 {\n let tBot = (bmin - o) * invD;\n let tTop = (bmax - o) * invD;\n let padding = depthSlabPadding(invD, o);\n let tNear = min(tBot, tTop) - padding;\n let tFar = max(tBot, tTop) + padding;\n let t0 = max(tNear.x, max(tNear.y, tNear.z));\n let t1 = min(tFar.x, min(tFar.y, tFar.z));\n return select(select(t1, t0, t0 > 0.0), -1.0, (t0 > t1 || t0 > tMax));\n}\n\nfn maxDimension(v: vec3f) -> i32 {\n return select(select(2, 1, v.y > v.z), select(2, 0, v.x > v.z), v.x > v.y);\n}\n\nfn getShear(d: vec3f, maxDim: i32) -> vec3f {\n // Permute space so that the z dimension is the one where the absolute value of the ray's direction is largest.\n // Then create a shear transformation that aligns ray direction with the +z axis\n if (maxDim == 0) {\n return vec3f(-d.y, -d.z, 1.0) / d.x;\n } else if (maxDim == 1) {\n return vec3f(-d.z, -d.x, 1.0) / d.y;\n } else {\n return vec3f(-d.x, -d.y, 1.0) / d.z;\n }\n}\n\nfn completeSurfaceInteraction(\n si: ptr<function, SurfaceInteraction>,\n hitTri: Triangle,\n hitBarycentric: vec3f,\n hitIndex: vec3i,\n isHitMicroTriangle: bool,\n hitInsIdx: i32,\n hitNormal: vec3f,\n hitPosition: vec3f,\n resolvedMaterialIndex: i32\n) {\n (*si).instanceIndex = hitInsIdx;\n (*si).barycentrics = hitBarycentric.xy;\n (*si).normal = hitNormal;\n (*si).position = hitPosition;\n (*si).materialIndex = i32(unpackMaterialIndex(\n bitcast<i32>(instancesBuffer[hitInsIdx * ${INSTANCE_VEC4_STRIDE}].z)\n ).x);\n // NOTE: Must be called before transforming normal/position to world space.\n // UV projection depends on the local position.\n fetchAttributes(\n si,\n hitTri,\n hitBarycentric,\n hitIndex,\n isHitMicroTriangle,\n resolvedMaterialIndex\n );\n\n let l2w = mat4x4<f32>(\n instancesBuffer[hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 5],\n instancesBuffer[hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 6],\n instancesBuffer[hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 7],\n instancesBuffer[hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 8]\n );\n let w2l = mat4x4<f32>(\n instancesBuffer[hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 1],\n instancesBuffer[hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 2],\n instancesBuffer[hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 3],\n instancesBuffer[hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 4]\n );\n let normalTransform = transpose(mat3x3<f32>(\n w2l[0].xyz,\n w2l[1].xyz,\n w2l[2].xyz\n ));\n\n (*si).faceNormal = normalize(normalTransform * hitNormal);\n (*si).vertexNormal = normalize(normalTransform * (*si).vertexNormal);\n (*si).normal = normalize(normalTransform * (*si).normal);\n${hasCoatNormals ? /* wgsl */ ` (*si).coatNormal = normalize(normalTransform * (*si).coatNormal);` : ''}\n if (USE_TANGENT && dot((*si).tangent, (*si).tangent) > 0.0) {\n (*si).tangent = normalize((l2w * vec4f((*si).tangent, 0.0)).xyz);\n }\n${hasAnisotropyDirections ? /* wgsl */ `\n (*si).directionTangent = (l2w * vec4f((*si).directionTangent, 0.0)).xyz;\n (*si).directionBitangent = (l2w * vec4f((*si).directionBitangent, 0.0)).xyz;\n (*si).geometryTangent = (l2w * vec4f((*si).geometryTangent, 0.0)).xyz;\n (*si).geometryBitangent = (l2w * vec4f((*si).geometryBitangent, 0.0)).xyz;\n` : /* wgsl */ ``}\n (*si).shadowOffset = (l2w * vec4f((*si).shadowOffset, 0.0)).xyz;\n (*si).position = (l2w * vec4((*si).position, 1.0)).xyz;\n}\n\n\n${createIntersectionAlphaWgsl(hasAnisotropyDirections)}\n\n\n\n// Traverse BVH, find closest triangle intersection, and return surface information\nfn intersectScene(ray: Ray, si: ptr<function, SurfaceInteraction>, bounces: i32, doubleSided: bool, isRefractionRay: bool) {\n\n (*si).hit = false;\n\n if (rtUniform.isEmpty > 0) {\n return;\n }\n\n var tMax = RAY_MAX_DISTANCE;\n var maxDim: i32;\n var shear: vec3f;\n\n var hitIndex: vec3i;\n var hitTri: Triangle;\n var hitInsIdx: i32;\n var currentTriangleIndex: i32 = (*si).triangleIndex;\n\n ${getBVHTraverseCode(\n `\n(*si).triangleIndex = triangleIndex;\nhitTri = tri;\nhitIndex = index;\nhitInsIdx = insIdx;\n `,\n hasDisplacement,\n false,\n blasLayoutMask,\n hasAnisotropyDirections\n )}\n\n if (isHit) {\n completeSurfaceInteraction(\n si,\n hitTri,\n hitBarycentric,\n hitIndex,\n isHitMicroTriangle,\n hitInsIdx,\n hitNormal,\n hitPosition,\n -1\n );\n }\n}\n\n// Local intersection helper for shader bevel.\nfn intersectSceneBevel(ray: Ray, si: ptr<function, SurfaceInteraction>, instanceIndex: i32, maxDistance: f32) {\n (*si).hit = false;\n\n if (rtUniform.isEmpty > 0) {\n return;\n }\n\n let bounces = 0;\n let doubleSided = true;\n let isRefractionRay = false;\n var tMax = maxDistance;\n var maxDim: i32;\n var shear: vec3f;\n var hitIndex: vec3i;\n var hitTri: Triangle;\n var hitInsIdx: i32;\n var currentTriangleIndex: i32 = -1;\n\n ${getBVHTraverseCode(\n `\n(*si).triangleIndex = triangleIndex;\nhitTri = tri;\nhitIndex = index;\nhitInsIdx = insIdx;\n `,\n hasDisplacement,\n false,\n blasLayoutMask,\n hasAnisotropyDirections,\n `\n if (insIdx != instanceIndex) {\n continue;\n }\n `\n )}\n\n if (isHit) {\n (*si).instanceIndex = hitInsIdx;\n (*si).normal = hitNormal;\n (*si).position = hitPosition;\n (*si).materialIndex = i32(unpackMaterialIndex(bitcast<i32>(instancesBuffer[hitInsIdx * ${INSTANCE_VEC4_STRIDE}].z)).x);\n fetchAttributes(\n si,\n hitTri,\n hitBarycentric,\n hitIndex,\n isHitMicroTriangle,\n -1\n );\n\n let l2w = mat4x4<f32>(\n instancesBuffer[hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 5],\n instancesBuffer[hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 6],\n instancesBuffer[hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 7],\n instancesBuffer[hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 8]\n );\n let w2l = mat4x4<f32>(\n instancesBuffer[hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 1],\n instancesBuffer[hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 2],\n instancesBuffer[hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 3],\n instancesBuffer[hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 4]\n );\n let normalTransform = transpose(mat3x3<f32>(\n w2l[0].xyz,\n w2l[1].xyz,\n w2l[2].xyz\n ));\n (*si).faceNormal = normalize(normalTransform * hitNormal);\n (*si).vertexNormal = normalize(normalTransform * (*si).vertexNormal);\n (*si).normal = normalize(normalTransform * (*si).normal);\n${hasCoatNormals ? /* wgsl */ ` (*si).coatNormal = normalize(normalTransform * (*si).coatNormal);` : ''}\n if (USE_TANGENT && dot((*si).tangent, (*si).tangent) > 0.0) {\n (*si).tangent = normalize((l2w * vec4f((*si).tangent, 0.0)).xyz);\n }\n${hasAnisotropyDirections ? /* wgsl */ `\n (*si).directionTangent = (l2w * vec4f((*si).directionTangent, 0.0)).xyz;\n (*si).directionBitangent = (l2w * vec4f((*si).directionBitangent, 0.0)).xyz;\n (*si).geometryTangent = (l2w * vec4f((*si).geometryTangent, 0.0)).xyz;\n (*si).geometryBitangent = (l2w * vec4f((*si).geometryBitangent, 0.0)).xyz;\n` : /* wgsl */ ``}\n (*si).shadowOffset = (l2w * vec4f((*si).shadowOffset, 0.0)).xyz;\n (*si).position = (l2w * vec4((*si).position, 1.0)).xyz;\n }\n}\n\nfn intersectSceneShadow(ray: Ray, lightDistance: f32, currTriangleIndex: i32) -> bool {\n if (rtUniform.isEmpty > 0) {\n return false;\n }\n\n let doubleSided = true;\n let shadowRayTMax = min(max(lightDistance, 0.0), RAY_MAX_DISTANCE);\n var tMax = shadowRayTMax;\n var maxDim: i32;\n var shear: vec3f;\n\n let bounces = 1;\n var isRefractionRay = false;\n var currentTriangleIndex: i32 = currTriangleIndex;\n\n ${getBVHTraverseCode(\n /*wgsl */ `\n// Traversal is already clipped to the sampled light. Affine instance\n// transforms preserve the ray parameter because local directions are not\n// renormalized, so no local-to-world hit transform or distance() is needed.\nvar isOccluded = true;\n\nif (isOccluded) {\n if (HAS_ALPHA) {\n let materialIndex = i32(unpackMaterialIndex(bitcast<i32>(insData.z)).x);\n if (!intersectionAlphaPasses(\n materialIndex,\n index,\n hitBarycentric,\n hitPosition,\n hitNormal\n )) {\n // Continue looking for an opaque hit, but never traverse beyond the light.\n tMax = shadowRayTMax;\n isOccluded = false;\n }\n }\n}\nif (isOccluded) {\n return true;\n}\n `,\n hasDisplacement,\n true,\n blasLayoutMask,\n hasAnisotropyDirections\n )}\n\n return false;\n}\n\n`;\n}\n"]}
1
+ {"version":3,"file":"intersect.wgsl.js","sourceRoot":"","sources":["../../../../src/renderer/wgsl/chunks/intersect.wgsl.ts"],"names":[],"mappings":"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","sourcesContent":["// Portions adapted from ray-tracing-renderer@0.10.15\n// (https://github.com/hoverinc/ray-tracing-renderer).\n// Copyright (c) 2019 HOVER, MIT license; see LICENSES/RayTracingRenderer-MIT.txt.\n\nimport { uvProjectionConstants } from '../../MaterialTable.js';\nimport { INSTANCE_VEC4_STRIDE } from '../../bvhAccel.js';\nimport pdm_displacementWgsl from './pdm_displacement.wgsl.js';\nimport { createIntersectionAlphaWgsl } from './intersectionAlpha.wgsl.js';\n\nconst displacementWgsl = pdm_displacementWgsl;\n\nconst flatIntersectCode = function () {\n return /* wgsl */ `\nlet hit = intersectTriangle(o, d, tri, maxDim, shear, tMax);\nif (hit.t > 0.0) {\n let e1 = tri.p1 - tri.p0;\n let e2 = tri.p2 - tri.p0;\n hitNormal = normalize(cross(e1, e2));\n let sideMaterialIndex = i32(unpackMaterialIndex(bitcast<i32>(insData.z)).x);\n let materialDoubleSided = materialsBuffer[sideMaterialIndex].doubleSided > 0.5;\n if (!doubleSided && !materialDoubleSided && dot(hitNormal, d) > 0.0) {\n hitNormal = vec3f(0.0);\n continue;\n }\n\n tMax = hit.t;\n isHit = true;\n isHitCurrent = true;\n hitPosition = o + tMax * d;\n hitBarycentric = hit.barycentric;\n}\n`;\n};\n\nfunction getPDMDisplacmentCode(anyHit: boolean) {\n return /*wgsl */ `\n let microTriangle = intersectDisplacement(\n o, d, tMax,\n tri.p0, tri.p1, tri.p2,\n n0, n1, n2,\n uv0, uv1, uv2,\n displacementMapIndex,\n getMapSize(displacementMapIndex),\n minH, maxH,\n hScale,\n matData.displacementBias,\n matData.displacementStepSize,\n i32(matData.displacementUvProjection),\n displacementUvProjectionScale,\n ${anyHit ? 'true' : 'false'},\n isRefractionRay\n );\n`;\n}\n\nconst displacementIntersectCode = function (anyHit: boolean, hasAnisotropyDirections: boolean) {\n return /* wgsl */ `\n\nlet hScale = insData.w;\nlet minMaxH = bitcast<i32>(r3.w);\n// Displacement currently uses the first material of the instance.\nlet materialIndex = i32(unpackMaterialIndex(bitcast<i32>(insData.z)).x);\nlet matData = materialsBuffer[materialIndex];\nlet n0 = normalize(attrBuffers[index.x * ATTR_STRIDE + NORMAL_OFF]${hasAnisotropyDirections ? '.xyz' : ''});\nlet n1 = normalize(attrBuffers[index.y * ATTR_STRIDE + NORMAL_OFF]${hasAnisotropyDirections ? '.xyz' : ''});\nlet n2 = normalize(attrBuffers[index.z * ATTR_STRIDE + NORMAL_OFF]${hasAnisotropyDirections ? '.xyz' : ''});\nif (matData.displacementMapIndex >= 0.0 && matData.displacementBias != 0.0) {\n tri.p0 += n0 * matData.displacementBias;\n tri.p1 += n1 * matData.displacementBias;\n tri.p2 += n2 * matData.displacementBias;\n}\nif (abs(hScale) > 0.0 && minMaxH > 0) {\n let minH = f32(minMaxH >> 8) / 255.0;\n let maxH = f32(minMaxH & 0xff) / 255.0;\n let displacementUvProjectionScale = vec2f(matData.displacementUvProjectionScale);\n let displacementMapIndex = i32(matData.displacementMapIndex);\n\n // TODO duplicate reading of normal and uv\n var uv0 = attrBuffers[index.x * ATTR_STRIDE + UV_OFF].xy;\n var uv1 = attrBuffers[index.y * ATTR_STRIDE + UV_OFF].xy;\n var uv2 = attrBuffers[index.z * ATTR_STRIDE + UV_OFF].xy;\n\n ${getPDMDisplacmentCode(anyHit)}\n\n if (microTriangle.t < tMax) {\n hitNormal = microTriangle.normal;\n // TODO support double sided to improve non watertight triangles.\n\n tMax = microTriangle.t;\n // TODO barycentric is for micro triangle\n hitBarycentric = microTriangle.barycentric;\n hitPosition = o + tMax * d;\n\n isHitMicroTriangle = true;\n isHit = true;\n isHitCurrent = true;\n }\n}\nelse {\n ${flatIntersectCode()}\n}\n`;\n};\n\nconst cwbvhTraversalWgsl = /* wgsl */ `\nfn unpackCwbvhBytes(value: u32) -> vec4f {\n return vec4f(\n f32(value & 0xffu),\n f32((value >> 8u) & 0xffu),\n f32((value >> 16u) & 0xffu),\n f32(value >> 24u)\n );\n}\n\n// CWBVH calls these helpers only with nonzero 8/24-bit masks. Every such\n// integer is exactly representable as f32, whose unbiased IEEE-754 exponent\n// gives the highest set bit. This avoids both the full-width firstLeadingBit\n// polyfill on some shader backends and a serial bit-spread/popcount chain.\n// Do not use this conversion for full 32-bit masks: rounding near a power of\n// two can change their highest bit. Zero is also outside this contract.\nfn cwbvhHighestBitIndex8(value: u32) -> u32 {\n return (bitcast<u32>(f32(value)) >> 23u) - 127u;\n}\n\nfn cwbvhHighestBitIndex24(value: u32) -> u32 {\n return (bitcast<u32>(f32(value)) >> 23u) - 127u;\n}\n\nfn intersectCwbvhInterior(\n node: i32,\n bufferOffset: i32,\n n0: vec4f,\n o: vec3f,\n invD: vec3f,\n tMax: f32\n) -> u32 {\n let i = node + bufferOffset;\n let n2 = bvhBuffer[i + 2];\n let n3 = bvhBuffer[i + 3];\n let n4 = bvhBuffer[i + 4];\n let header = bitcast<u32>(n0.w);\n let exponentX = bitcast<i32>((header & 0xffu) << 24u) >> 24;\n let exponentY = bitcast<i32>(((header >> 8u) & 0xffu) << 24u) >> 24;\n let exponentZ = bitcast<i32>(((header >> 16u) & 0xffu) << 24u) >> 24;\n let quantizationScale = vec3f(\n bitcast<f32>(u32(exponentX + 127) << 23u),\n bitcast<f32>(u32(exponentY + 127) << 23u),\n bitcast<f32>(u32(exponentZ + 127) << 23u)\n );\n let padding = depthSlabPadding(invD, o);\n let originTerm = (n0.xyz - o) * invD;\n let adjustedInvD = quantizationScale * invD;\n let minX0 = unpackCwbvhBytes(bitcast<u32>(n2.x));\n let minX1 = unpackCwbvhBytes(bitcast<u32>(n2.y));\n let minY0 = unpackCwbvhBytes(bitcast<u32>(n2.z));\n let minY1 = unpackCwbvhBytes(bitcast<u32>(n2.w));\n let minZ0 = unpackCwbvhBytes(bitcast<u32>(n3.x));\n let minZ1 = unpackCwbvhBytes(bitcast<u32>(n3.y));\n let maxX0 = unpackCwbvhBytes(bitcast<u32>(n3.z));\n let maxX1 = unpackCwbvhBytes(bitcast<u32>(n3.w));\n let maxY0 = unpackCwbvhBytes(bitcast<u32>(n4.x));\n let maxY1 = unpackCwbvhBytes(bitcast<u32>(n4.y));\n let maxZ0 = unpackCwbvhBytes(bitcast<u32>(n4.z));\n let maxZ1 = unpackCwbvhBytes(bitcast<u32>(n4.w));\n\n let minTx0 = minX0 * adjustedInvD.x + originTerm.x;\n let minTy0 = minY0 * adjustedInvD.y + originTerm.y;\n let minTz0 = minZ0 * adjustedInvD.z + originTerm.z;\n let maxTx0 = maxX0 * adjustedInvD.x + originTerm.x;\n let maxTy0 = maxY0 * adjustedInvD.y + originTerm.y;\n let maxTz0 = maxZ0 * adjustedInvD.z + originTerm.z;\n let near0 = max(\n max((min(minTx0, maxTx0) - vec4f(padding.x)), (min(minTy0, maxTy0) - vec4f(padding.y))),\n (min(minTz0, maxTz0) - vec4f(padding.z))\n );\n let far0 = min(\n min((max(minTx0, maxTx0) + vec4f(padding.x)), (max(minTy0, maxTy0) + vec4f(padding.y))),\n (max(minTz0, maxTz0) + vec4f(padding.z))\n );\n\n let minTx1 = minX1 * adjustedInvD.x + originTerm.x;\n let minTy1 = minY1 * adjustedInvD.y + originTerm.y;\n let minTz1 = minZ1 * adjustedInvD.z + originTerm.z;\n let maxTx1 = maxX1 * adjustedInvD.x + originTerm.x;\n let maxTy1 = maxY1 * adjustedInvD.y + originTerm.y;\n let maxTz1 = maxZ1 * adjustedInvD.z + originTerm.z;\n let near1 = max(\n max((min(minTx1, maxTx1) - vec4f(padding.x)), (min(minTy1, maxTy1) - vec4f(padding.y))),\n (min(minTz1, maxTz1) - vec4f(padding.z))\n );\n let far1 = min(\n min((max(minTx1, maxTx1) + vec4f(padding.x)), (max(minTy1, maxTy1) + vec4f(padding.y))),\n (max(minTz1, maxTz1) + vec4f(padding.z))\n );\n\n let hits0 = (near0 <= min(far0, vec4f(tMax))) & (far0 > vec4f(0.0));\n let hits1 = (near1 <= min(far1, vec4f(tMax))) & (far1 > vec4f(0.0));\n return select(0u, 1u, hits0.x)\n | select(0u, 2u, hits0.y)\n | select(0u, 4u, hits0.z)\n | select(0u, 8u, hits0.w)\n | select(0u, 16u, hits1.x)\n | select(0u, 32u, hits1.y)\n | select(0u, 64u, hits1.z)\n | select(0u, 128u, hits1.w);\n}\n\n`;\n\nfunction getBVHTraverseCode(\n handleHitCode: string,\n hasDisplacement: boolean,\n anyHit: boolean,\n blasLayoutMask: number,\n hasAnisotropyDirections: boolean,\n skipTriangleCode = ''\n) {\n const intersectTriangles = /* wgsl */ `\n let insData = instancesBuffer[insIdx * ${INSTANCE_VEC4_STRIDE}];\n let vertexOffset = vec3i(bitcast<i32>(insData.y));\n for (var k = 0; k < primitiveCount; k++) {\n let triangleIndex = primitiveOffset + k;\n ${skipTriangleCode}\n if (bounces > 0 && triangleIndex == currentTriangleIndex) {\n continue;\n }\n \n let r3 = bvhBuffer[triangleIndex];\n let index = bitcast<vec3i>(r3.xyz) + vertexOffset;\n var tri = Triangle(\n attrBuffers[index.x * ATTR_STRIDE + POSITION_OFF]${hasAnisotropyDirections ? '.xyz' : ''},\n attrBuffers[index.y * ATTR_STRIDE + POSITION_OFF]${hasAnisotropyDirections ? '.xyz' : ''},\n attrBuffers[index.z * ATTR_STRIDE + POSITION_OFF]${hasAnisotropyDirections ? '.xyz' : ''}\n );\n var isHitCurrent = false;\n\n ${hasDisplacement\n ? displacementIntersectCode(anyHit, hasAnisotropyDirections)\n : flatIntersectCode()}\n if (isHitCurrent) {\n ${handleHitCode}\n }\n }\n`;\n return /* wgsl */ `\nvar d = ray.d;\nvar o = ray.o;\nvar tlasStack: array<i32, TLAS_STACK_SIZE>;\nvar tlasTop = -1;\nvar invD = traversalInverse(d);\nvar isHitMicroTriangle = false;\nvar isHit = false;\nvar hitPosition: vec3f;\nvar hitNormal: vec3f;\nvar hitBarycentric: vec3f;\n\n\nif (intersectBox(o, bvhBuffer[0].xyz, bvhBuffer[1].xyz, invD, tMax) > 0.0) {\n tlasTop = 0;\n tlasStack[0] = 0;\n}\nwhile (tlasTop >= 0) {\n let tlasNode = tlasStack[tlasTop];\n tlasTop--;\n o = ray.o;\n d = ray.d;\n invD = traversalInverse(d);\n\n let tlasR1 = bvhBuffer[tlasNode];\n let tlasPointerOrPrimitiveCount = bitcast<i32>(tlasR1.w);\n if (tlasPointerOrPrimitiveCount >= 0) {\n let left = tlasNode + 2;\n let right = tlasPointerOrPrimitiveCount;\n let d0 = intersectBox(\n o,\n bvhBuffer[left].xyz,\n bvhBuffer[left + 1].xyz,\n invD,\n tMax\n );\n let d1 = intersectBox(\n o,\n bvhBuffer[right].xyz,\n bvhBuffer[right + 1].xyz,\n invD,\n tMax\n );\n\n if (d0 > d1) {\n if (d0 > 0.0) { tlasTop++; tlasStack[tlasTop] = left; }\n if (d1 > 0.0) { tlasTop++; tlasStack[tlasTop] = right; }\n }\n else {\n if (d1 > 0.0) { tlasTop++; tlasStack[tlasTop] = right; }\n if (d0 > 0.0) { tlasTop++; tlasStack[tlasTop] = left; }\n }\n }\n else {\n let tlasPrimitiveCount = -tlasPointerOrPrimitiveCount;\n // The previous unified LIFO stack visited leaf instances in reverse order.\n // Preserve that order for alpha-tested any-hit rays and deterministic RNG.\n for (\n var instanceOffset = tlasPrimitiveCount - 1;\n instanceOffset >= 0;\n instanceOffset--\n ) {\n let instanceRecord = bvhBuffer[tlasNode + 2 + instanceOffset];\n let insIdx = bitcast<i32>(instanceRecord.x);\n let blasRoot = bitcast<i32>(instanceRecord.y);\n let insData = instancesBuffer[insIdx * ${INSTANCE_VEC4_STRIDE}];\n let w2l = mat4x4<f32>(\n instancesBuffer[insIdx * ${INSTANCE_VEC4_STRIDE} + 1],\n instancesBuffer[insIdx * ${INSTANCE_VEC4_STRIDE} + 2],\n instancesBuffer[insIdx * ${INSTANCE_VEC4_STRIDE} + 3],\n instancesBuffer[insIdx * ${INSTANCE_VEC4_STRIDE} + 4]\n );\n o = (w2l * vec4f(ray.o, 1.0)).xyz;\n d = (w2l * vec4f(ray.d, 0.0)).xyz;\n invD = traversalInverse(d);\n maxDim = maxDimension(abs(d));\n shear = getShear(d, maxDim);\n\n let encodedBlasOffset = bitcast<u32>(insData.x);\n let blasLayout = encodedBlasOffset >> 30u;\n let blasOffset = i32(encodedBlasOffset & 0x3fffffffu);\n let bufferOff = rtUniform.blasOffset;\n\n ${(blasLayoutMask & 8) !== 0 ? /* wgsl */ `\n if (blasLayout == 3u) {\n // TinyBVH's official CWBVH traversal keeps compact node groups on the\n // stack. The upper 8 bits identify intersected interior slots and the\n // lower 8 bits preserve the node's interior-slot mask for popcount\n // addressing. Triangle leaves are merged into a separate 24-bit mask.\n var cwbvhStack: array<vec2u, BLAS_STACK_SIZE>;\n var cwbvhTop = -1;\n var cwbvhVisits = 0u;\n var nodeGroupBase = 0u;\n // A synthetic slot-7 hit selects relative node zero for the root.\n var nodeGroupMask = 0x80000000u;\n let octant = select(0u, 4u, d.x < 0.0)\n | select(0u, 2u, d.y < 0.0)\n | select(0u, 1u, d.z < 0.0);\n let octantInverse = 7u - octant;\n\n // Keep a statically visible trip bound. DXC can otherwise miscompile\n // the dynamic stack-top loop into a non-terminating shader on D3D12.\n // Validated scene rays stay far below this conservative safety cap.\n while (cwbvhVisits < 4096u) {\n cwbvhVisits++;\n \n let internalMask = nodeGroupMask & 0xffu;\n let childBitIndex =\n cwbvhHighestBitIndex8(nodeGroupMask >> 24u) + 24u;\n nodeGroupMask &= ~(1u << childBitIndex);\n if ((nodeGroupMask & 0xff000000u) != 0u) {\n cwbvhTop++;\n \n cwbvhStack[cwbvhTop] = vec2u(nodeGroupBase, nodeGroupMask);\n }\n\n let slot = (childBitIndex - 24u) ^ octantInverse;\n let precedingMask = (1u << slot) - 1u;\n let relativeIndex = countOneBits(internalMask & precedingMask);\n let childNodeIndex = nodeGroupBase + relativeIndex;\n let node = blasOffset + i32(childNodeIndex * 5u);\n let compressed0 = bvhBuffer[node + bufferOff];\n let slotHits = intersectCwbvhInterior(\n node, bufferOff, compressed0, o, invD, tMax\n );\n // Empty child hits need neither metadata nor leaf processing. Clear\n // the current group so the common tail restores pending siblings.\n nodeGroupMask = 0u;\n if (slotHits != 0u) {\n let compressed1 = bvhBuffer[node + bufferOff + 1];\n let nodeInternalMask = bitcast<u32>(compressed0.w) >> 24u;\n var hitMask = 0u;\n var remainingSlotHits = slotHits;\n while (remainingSlotHits != 0u) {\n let childSlot = cwbvhHighestBitIndex8(remainingSlotHits);\n remainingSlotHits &= ~(1u << childSlot);\n let metaWord = select(\n bitcast<u32>(compressed1.z),\n bitcast<u32>(compressed1.w),\n childSlot >= 4u\n );\n let childMeta =\n (metaWord >> ((childSlot & 3u) * 8u)) & 0xffu;\n let localOffset = childMeta & 0x1fu;\n let bitIndex = select(\n localOffset,\n localOffset ^ octantInverse,\n localOffset >= 24u\n );\n let childBits = (childMeta >> 5u) & 0x7u;\n hitMask |= childBits << bitIndex;\n }\n\n nodeGroupBase = bitcast<u32>(compressed1.x);\n nodeGroupMask = (hitMask & 0xff000000u) | nodeInternalMask;\n let triangleBase = bitcast<u32>(compressed1.y);\n var triangleMask = hitMask & 0x00ffffffu;\n while (triangleMask != 0u) {\n let triangleBitIndex = cwbvhHighestBitIndex24(triangleMask);\n triangleMask &= ~(1u << triangleBitIndex);\n let primitiveCount = 1;\n let primitiveOffset = bufferOff + blasOffset\n + i32(triangleBase + triangleBitIndex);\n ${intersectTriangles}\n }\n }\n\n if ((nodeGroupMask & 0xff000000u) != 0u) { continue; }\n if (cwbvhTop >= 0) {\n let restoredGroup = cwbvhStack[cwbvhTop];\n nodeGroupBase = restoredGroup.x;\n nodeGroupMask = restoredGroup.y;\n cwbvhTop--;\n }\n else { break; }\n }\n }\n ` : ''}\n\n var blasStack: array<i32, BLAS_STACK_SIZE>;\n var blasTop = -1;\n if (blasLayout != 3u) {\n blasTop = 0;\n \n blasStack[0] = blasRoot;\n }\n\n while (blasTop >= 0) {\n \n let node = blasStack[blasTop];\n blasTop--;\n var primitiveCount = 0;\n var primitiveOffset = 0;\n\n ${(blasLayoutMask & 4) !== 0 ? /* wgsl */ `\n if (blasLayout == 2u) {\n let i = node + bufferOff;\n let gpu0 = bvhBuffer[i];\n let gpu1 = bvhBuffer[i + 1];\n let gpu2 = bvhBuffer[i + 2];\n let gpu3 = bvhBuffer[i + 3];\n primitiveCount = i32(bitcast<u32>(gpu2.w));\n if (primitiveCount > 0) {\n primitiveOffset = bufferOff + blasOffset\n + i32(bitcast<u32>(gpu3.w));\n }\n else {\n let left = blasOffset + i32(bitcast<u32>(gpu0.w) * 4u);\n let right = blasOffset + i32(bitcast<u32>(gpu1.w) * 4u);\n let leftDistance = intersectBox(\n o, gpu0.xyz, gpu1.xyz, invD, tMax\n );\n let rightDistance = intersectBox(\n o, gpu2.xyz, gpu3.xyz, invD, tMax\n );\n if (leftDistance > rightDistance) {\n if (leftDistance > 0.0) {\n blasTop++; blasStack[blasTop] = left;\n }\n if (rightDistance > 0.0) {\n blasTop++; blasStack[blasTop] = right;\n }\n }\n else {\n if (rightDistance > 0.0) {\n blasTop++; blasStack[blasTop] = right;\n }\n if (leftDistance > 0.0) {\n blasTop++; blasStack[blasTop] = left;\n }\n }\n }\n }\n ` : ''}\n ${(blasLayoutMask & 2) !== 0 ? /* wgsl */ `\n if (blasLayout == 1u) {\n let nodeInfo = bitcast<u32>(node);\n if ((nodeInfo & 0x80000000u) != 0u) {\n primitiveCount = i32((nodeInfo >> 23u) & 0xffu);\n primitiveOffset = bufferOff + blasOffset\n + i32(nodeInfo & 0x007fffffu);\n }\n else {\n let i = node + bufferOff;\n let wide0 = bvhBuffer[i];\n let wide1 = bvhBuffer[i + 1];\n let wide2 = bvhBuffer[i + 2];\n let wide3 = bvhBuffer[i + 3];\n let nodeMin = wide0.xyz;\n let nodeExtent = vec3f(wide0.w, wide1.x, wide1.y);\n let childInfos = array<u32, 4>(\n bitcast<u32>(wide1.z), bitcast<u32>(wide1.w),\n bitcast<u32>(wide2.x), bitcast<u32>(wide2.y)\n );\n let childMinX = unpack4x8unorm(bitcast<u32>(wide2.z));\n let childMinY = unpack4x8unorm(bitcast<u32>(wide2.w));\n let childMinZ = unpack4x8unorm(bitcast<u32>(wide3.x));\n let childMaxX = unpack4x8unorm(bitcast<u32>(wide3.y));\n let childMaxY = unpack4x8unorm(bitcast<u32>(wide3.z));\n let childMaxZ = unpack4x8unorm(bitcast<u32>(wide3.w));\n let tx0 = (nodeMin.x + childMinX * nodeExtent.x - o.x) * invD.x;\n let tx1 = (nodeMin.x + childMaxX * nodeExtent.x - o.x) * invD.x;\n let ty0 = (nodeMin.y + childMinY * nodeExtent.y - o.y) * invD.y;\n let ty1 = (nodeMin.y + childMaxY * nodeExtent.y - o.y) * invD.y;\n let tz0 = (nodeMin.z + childMinZ * nodeExtent.z - o.z) * invD.z;\n let tz1 = (nodeMin.z + childMaxZ * nodeExtent.z - o.z) * invD.z;\n let padding = depthSlabPadding(invD, o);\n let tNear = max(\n max((min(tx0, tx1) - vec4f(padding.x)), (min(ty0, ty1) - vec4f(padding.y))),\n (min(tz0, tz1) - vec4f(padding.z))\n );\n let tFar = min(\n min((max(tx0, tx1) + vec4f(padding.x)), (max(ty0, ty1) + vec4f(padding.y))),\n (max(tz0, tz1) + vec4f(padding.z))\n );\n var distance = select(tFar, tNear, tNear > vec4f(0.0));\n distance = select(\n distance,\n vec4f(-1.0),\n tNear > min(tFar, vec4f(tMax))\n );\n var childDistances = array<f32, 4>(\n distance.x,\n distance.y,\n distance.z,\n distance.w\n );\n\n // Push far-to-near so that the closest child is popped first.\n for (var order = 0; order < 4; order++) {\n var farthestDistance = -1.0;\n var farthestChild = -1;\n for (var child = 0; child < 4; child++) {\n if (\n childInfos[child] != 0u &&\n childDistances[child] > farthestDistance\n ) {\n farthestDistance = childDistances[child];\n farthestChild = child;\n }\n }\n if (farthestChild >= 0 && farthestDistance > 0.0) {\n let childInfo = childInfos[farthestChild];\n var childAddress = bitcast<i32>(childInfo);\n if ((childInfo & 0x80000000u) == 0u) {\n childAddress += blasOffset;\n }\n blasTop++;\n blasStack[blasTop] = childAddress;\n childDistances[farthestChild] = -1.0;\n }\n }\n }\n }\n ` : ''}\n ${(blasLayoutMask & 1) !== 0 ? /* wgsl */ `\n if (blasLayout == 0u) {\n let i = node + bufferOff;\n let r1 = bvhBuffer[i];\n let pointerToRightOrNumPrims = bitcast<i32>(r1.w);\n\n if (pointerToRightOrNumPrims >= 0) {\n let left = node + 2;\n let right = pointerToRightOrNumPrims + blasOffset;\n let d0 = intersectBox(\n o,\n bvhBuffer[left + bufferOff].xyz,\n bvhBuffer[left + 1 + bufferOff].xyz,\n invD,\n tMax\n );\n let d1 = intersectBox(\n o,\n bvhBuffer[right + bufferOff].xyz,\n bvhBuffer[right + 1 + bufferOff].xyz,\n invD,\n tMax\n );\n\n if (d0 > d1) {\n if (d0 > 0.0) {\n blasTop++; blasStack[blasTop] = left;\n \n }\n if (d1 > 0.0) {\n blasTop++; blasStack[blasTop] = right;\n \n }\n }\n else {\n if (d1 > 0.0) {\n blasTop++; blasStack[blasTop] = right;\n \n }\n if (d0 > 0.0) {\n blasTop++; blasStack[blasTop] = left;\n \n }\n }\n }\n else {\n primitiveCount = -pointerToRightOrNumPrims;\n primitiveOffset = i + 2;\n }\n }\n ` : ''}\n if (primitiveCount > 0) {\n ${intersectTriangles}\n }\n }\n }\n }\n}\n\n`;\n}\nexport default function getIntersectCode({\n hasDisplacement,\n hasAnisotropyDirections = true,\n hasCoatNormals = false,\n blasLayoutMask = 1\n}: {\n hasDisplacement: boolean;\n hasAnisotropyDirections?: boolean;\n hasCoatNormals?: boolean;\n blasLayoutMask?: number;\n}) {\n return /*wgsl*/ `\n\n${hasDisplacement ? displacementWgsl : ''}\n${(blasLayoutMask & 8) !== 0 ? cwbvhTraversalWgsl : ''}\n\nstruct Triangle {\n p0: vec3f,\n p1: vec3f,\n p2: vec3f,\n};\n\nfn intersectSphere(rad: f32, pos: vec3f, r: Ray) -> f32 {\n let op: vec3f = pos - r.o;\n let eps: f32 = 0.001;\n let b: f32 = dot(op, r.d);\n var det: f32 = b * b - dot(op, op) + rad * rad;\n if (det < 0.0) {\n return INF;\n }\n\n det = sqrt(det);\n let t1: f32 = b - det;\n if (t1 > eps) {\n return t1;\n }\n\n let t2: f32 = b + det;\n if (t2 > eps) {\n return t2;\n }\n\n return INF;\n}\n\nfn intersectRect(pos: vec3f, u: vec3f, v: vec3f, n: vec3f, r: Ray) -> f32 {\n let dt: f32 = dot(r.d, n);\n let t: f32 = (dot(n, pos) - dot(n, r.o)) / dt;\n\n if (t > EPS) {\n let p: vec3f = r.o + r.d * t;\n let vi: vec3f = p - pos;\n let a1: f32 = dot(u / dot(u, u), vi);\n if (a1 >= 0.0 && a1 <= 1.0) {\n let a2: f32 = dot(v / dot(v, v), vi);\n if (a2 >= 0.0 && a2 <= 1.0) {\n return t;\n }\n }\n }\n\n return INF;\n}\n\nfn sphereSolidAnglePdf(origin: vec3f, center: vec3f, radius: f32) -> f32 {\n let centerDistance = distance(origin, center);\n if (centerDistance <= radius) { return INV4PI; }\n let sinThetaMaxSquared = min(1.0, radius * radius / (centerDistance * centerDistance));\n let cosThetaMax = sqrt(max(0.0, 1.0 - sinThetaMaxSquared));\n return 1.0 / max(TWOPI * (1.0 - cosThetaMax), 1e-8);\n}\n\nfn intersectLight(r: Ray, light: Light, lightSample: ptr<function, LightSampleRec>) -> f32 {\n // Analytic continuation hits use the same conditional light density as NEE.\n // Initialize every field because this record is reused across the light loop.\n var d: f32 = INF;\n (*lightSample).direction = r.d;\n (*lightSample).normal = -r.d;\n (*lightSample).dist = INF;\n (*lightSample).pdf = 0.0;\n (*lightSample).emission = vec3f(0.0);\n (*lightSample).isDelta = false;\n var u = light.u;\n var v = light.v;\n var lightType = i32(light.litType);\n\n if (lightType == RECT_LIGHT) {\n // Adjust the position from center to start corner\n var pos = light.position - u * 0.5 - v * 0.5;\n var normal = normalize(cross(u, v));\n if (dot(normal, r.d) > 0.0) { // Hide backfacing quad light\n return INF;\n }\n\n d = intersectRect(pos, u, v, normal, r);\n if (d < 0.0) {\n d = INF;\n }\n else {\n var cosTheta: f32 = dot(-r.d, normal);\n let spread = light.radiusOrSpread;\n if (cosTheta >= cos(spread * PI / 2.0)) {\n (*lightSample).direction = r.d;\n (*lightSample).normal = normal;\n (*lightSample).dist = d;\n (*lightSample).pdf = (d * d) / (light.area * cosTheta);\n (*lightSample).emission = light.emission;\n (*lightSample).isDelta = false;\n }\n else {\n d = INF;\n }\n }\n }\n\n else if (lightType == SPHERE_LIGHT) {\n if (light.radiusOrSpread <= 0.0) {\n let toPoint = light.position - r.o;\n let projection = dot(toPoint, r.d);\n let perpendicular = toPoint - projection * r.d;\n if (projection <= EPS || dot(perpendicular, perpendicular) > 1e-8) {\n return INF;\n }\n d = projection;\n (*lightSample).direction = r.d;\n (*lightSample).normal = -r.d;\n (*lightSample).dist = d;\n (*lightSample).pdf = 1.0;\n (*lightSample).isDelta = true;\n }\n else {\n d = intersectSphere(light.radiusOrSpread, light.position, r);\n (*lightSample).isDelta = false;\n if (d > 0.0 && d != INF) {\n (*lightSample).direction = r.d;\n (*lightSample).dist = d;\n (*lightSample).normal = normalize(\n r.o + r.d * d - light.position\n );\n (*lightSample).pdf = sphereSolidAnglePdf(\n r.o,\n light.position,\n light.radiusOrSpread\n );\n }\n }\n if (d < 0.0) {\n d = INF;\n }\n else {\n (*lightSample).direction = r.d;\n (*lightSample).dist = d;\n (*lightSample).emission = select(\n light.emission,\n light.emission / max(d * d, 1e-8),\n light.radiusOrSpread <= 0.0\n );\n }\n }\n\n else if (lightType == SPOT_LIGHT) {\n if (light.radiusOrSpread <= 0.0) {\n let toPoint = light.position - r.o;\n let projection = dot(toPoint, r.d);\n let perpendicular = toPoint - projection * r.d;\n if (projection <= EPS || dot(perpendicular, perpendicular) > 1e-8) {\n return INF;\n }\n d = projection;\n (*lightSample).direction = r.d;\n (*lightSample).normal = -r.d;\n (*lightSample).dist = d;\n (*lightSample).pdf = 1.0;\n (*lightSample).isDelta = true;\n }\n else {\n d = intersectSphere(light.radiusOrSpread, light.position, r);\n (*lightSample).isDelta = false;\n }\n if (d != INF) {\n let hitPt = r.o + d * r.d;\n let multiplier = evaluateSpotEmissionMultiplier(light, normalize(r.o - hitPt));\n if (multiplier <= 0.0) { return INF; }\n if (light.radiusOrSpread > 0.0) {\n (*lightSample).direction = r.d;\n (*lightSample).dist = d;\n (*lightSample).normal = normalize(\n r.o + r.d * d - light.position\n );\n (*lightSample).pdf = sphereSolidAnglePdf(r.o, light.position, light.radiusOrSpread);\n }\n (*lightSample).emission = light.emission * multiplier * select(\n 1.0,\n 1.0 / max(d * d, 1e-8),\n light.radiusOrSpread <= 0.0\n );\n }\n }\n\n else if (lightType != RECT_LIGHT && lightType != SPHERE_LIGHT && lightType != SPOT_LIGHT) {\n // Unsupported light\n d = INF;\n }\n\n return d;\n}\n\nfn randomPickMaterial(m0: vec3i, m1: vec3i, m2: vec3i, bary: vec3f) -> i32 {\n let m00 = unpackMaterialIndex(m0.x);\n let m01 = unpackMaterialIndex(m0.y);\n let m02 = unpackMaterialIndex(m0.z);\n let m10 = unpackMaterialIndex(m1.x);\n let m11 = unpackMaterialIndex(m1.y);\n let m12 = unpackMaterialIndex(m1.z);\n let m20 = unpackMaterialIndex(m2.x);\n let m21 = unpackMaterialIndex(m2.y);\n let m22 = unpackMaterialIndex(m2.z);\n var w00 = m00.y * bary.x;\n var w01 = m01.y * bary.x;\n var w02 = m02.y * bary.x;\n var w10 = m10.y * bary.y;\n var w11 = m11.y * bary.y;\n var w12 = m12.y * bary.y;\n var w20 = m20.y * bary.z;\n var w21 = m21.y * bary.z;\n var w22 = m22.y * bary.z;\n\n var wTotal = w00 + w01 + w02 + w10 + w11 + w12 + w20 + w21 + w22;\n // Accumulate weights\n w00 = w00 / wTotal;\n w01 = w01 / wTotal + w00;\n w02 = w02 / wTotal + w01;\n w10 = w10 / wTotal + w02;\n w11 = w11 / wTotal + w10;\n w12 = w12 / wTotal + w11;\n w20 = w20 / wTotal + w12;\n w21 = w21 / wTotal + w20;\n w22 = w22 / wTotal + w21;\n\n let rand = hashedSample(0u);\n var mIdx: f32;\n if (rand < w00) {\n mIdx = m00.x;\n } else if (rand < w01) {\n mIdx = m01.x;\n } else if (rand < w02) {\n mIdx = m02.x;\n } else if (rand < w10) {\n mIdx = m10.x;\n } else if (rand < w11) {\n mIdx = m11.x;\n } else if (rand < w12) {\n mIdx = m12.x;\n } else if (rand < w20) {\n mIdx = m20.x;\n } else if (rand < w21) {\n mIdx = m21.x;\n } else {\n mIdx = m22.x;\n }\n return i32(mIdx);\n}\n\nfn resolveSurfaceMaterialIndex(\n fallbackMaterialIndex: i32,\n index: vec3i,\n bary: vec3f\n) -> i32 {\n let m0 = vec3i(attrBuffers[index.x * ATTR_STRIDE + MATERIALINDEX_OFF]${hasAnisotropyDirections ? '.xyz' : ''});\n let m1 = vec3i(attrBuffers[index.y * ATTR_STRIDE + MATERIALINDEX_OFF]${hasAnisotropyDirections ? '.xyz' : ''});\n let m2 = vec3i(attrBuffers[index.z * ATTR_STRIDE + MATERIALINDEX_OFF]${hasAnisotropyDirections ? '.xyz' : ''});\n if (m0.x > 0 || m1.x > 0 || m2.x > 0) {\n return randomPickMaterial(m0, m1, m2, bary);\n }\n return fallbackMaterialIndex;\n}\n\n\n\n${hasAnisotropyDirections ? /* wgsl */ `\nfn anisotropy_axes(normal: vec3f, dp1: vec3f, dp2: vec3f, duv1: vec2f, duv2: vec2f, input: vec4f) -> mat2x3f {\n let determinant = duv1.x * duv2.y - duv1.y * duv2.x;\n var t = input.xyz;\n var b = vec3f(0.0);\n // Internal W=2 marks an authored XYZ tangent whose sign is derived from UVs.\n var sign = select(input.w, 0.0, abs(input.w) > 1.5);\n if (abs(determinant) > 1e-12) {\n let dt = (dp1 * duv2.y - dp2 * duv1.y) / determinant;\n let db = (dp2 * duv1.x - dp1 * duv2.x) / determinant;\n if (dot(t, t) < 1e-8) { t = dt; }\n if (sign == 0.0) { sign = select(1.0, -1.0, dot(cross(normal, dt), db) < 0.0); }\n }\n t -= normal * dot(normal, t);\n if (dot(t, t) < 1e-8) {\n let signZ = select(-1.0, 1.0, normal.z >= 0.0);\n let factor = -1.0 / (signZ + normal.z);\n let xy = normal.x * normal.y * factor;\n t = vec3f(1.0 + signZ * normal.x * normal.x * factor, signZ * xy, -signZ * normal.x);\n b = vec3f(xy, signZ + normal.y * normal.y * factor, -normal.y);\n }\n else { t = normalize(t); b = normalize(cross(normal, t)); }\n return mat2x3f(t, b * select(1.0, -1.0, sign < 0.0));\n}\n\n` : /* wgsl */ ``}\nfn fetchAttributes(\n si: ptr<function, SurfaceInteraction>,\n tri: Triangle,\n bary: vec3f,\n index: vec3i,\n isHitMicroTriangle: bool,\n resolvedMaterialIndex: i32\n) {\n (*si).hit = true;\n // Get projected uv\n let material = materialsBuffer[(*si).materialIndex];\n let uvProjection = i32(material.uvProjection);\n\n let i0 = index.x;\n let i1 = index.y;\n let i2 = index.z;\n let isSphericalProj = uvProjection == ${uvProjectionConstants.spherical};\n\n var normal: vec3f = (*si).normal;\n var normalForSphericalProj: vec3f;\n var n0 = vec3f(0.0);\n var n1 = vec3f(0.0);\n var n2 = vec3f(0.0);\n // Otherwise use the normal from the microtriangle\n // The exception is spherical projection, we need the original normal to project the uv. Or the case like earth has wrong normal.\n if (!isHitMicroTriangle || isSphericalProj) {\n n0 = attrBuffers[i0 * ATTR_STRIDE + NORMAL_OFF]${hasAnisotropyDirections ? '.xyz' : ''};\n n1 = attrBuffers[i1 * ATTR_STRIDE + NORMAL_OFF]${hasAnisotropyDirections ? '.xyz' : ''};\n n2 = attrBuffers[i2 * ATTR_STRIDE + NORMAL_OFF]${hasAnisotropyDirections ? '.xyz' : ''};\n normalForSphericalProj = normalize(bary.x * n0 + bary.y * n1 + bary.z * n2);\n if (!isHitMicroTriangle) {\n normal = normalForSphericalProj;\n }\n }\n\n var uv: vec2f;\n var uv0: vec2f;\n var uv1: vec2f;\n var uv2: vec2f;\n var secondaryUv = vec2f(0.0);\n if (USE_UV) {\n uv0 = attrBuffers[i0 * ATTR_STRIDE + UV_OFF].xy;\n uv1 = attrBuffers[i1 * ATTR_STRIDE + UV_OFF].xy;\n uv2 = attrBuffers[i2 * ATTR_STRIDE + UV_OFF].xy;\n uv = bary.x * uv0 + bary.y * uv1 + bary.z * uv2;\n if (USE_UV1) {\n let secondaryUv0 = attrBuffers[i0 * ATTR_STRIDE + UV1_OFF].xy;\n let secondaryUv1 = attrBuffers[i1 * ATTR_STRIDE + UV1_OFF].xy;\n let secondaryUv2 = attrBuffers[i2 * ATTR_STRIDE + UV1_OFF].xy;\n secondaryUv = bary.x * secondaryUv0\n + bary.y * secondaryUv1\n + bary.z * secondaryUv2;\n }\n }\n\n if (USE_TANGENT) {\n let t0 = attrBuffers[i0 * ATTR_STRIDE + TANGENT_OFF]${hasAnisotropyDirections ? '.xyz' : ''};\n let t1 = attrBuffers[i1 * ATTR_STRIDE + TANGENT_OFF]${hasAnisotropyDirections ? '.xyz' : ''};\n let t2 = attrBuffers[i2 * ATTR_STRIDE + TANGENT_OFF]${hasAnisotropyDirections ? '.xyz' : ''};\n (*si).tangent = normalize(bary.x * t0 + bary.y * t1 + bary.z * t2);\n }\n\n if (resolvedMaterialIndex >= 0) {\n (*si).materialIndex = resolvedMaterialIndex;\n }\n else {\n (*si).materialIndex = resolveSurfaceMaterialIndex(\n (*si).materialIndex,\n index,\n bary\n );\n }\n\n if (uvProjection > 0) {\n // NOTE: position and normal are not transformed to world space yet.\n uv = projectUV(uvProjection, (*si).position, select(normal, normalForSphericalProj, isSphericalProj));\n secondaryUv = uv;\n }\n\n uv = uv * material.uvScale;\n secondaryUv = secondaryUv * material.uvScale;\n\n // Force repeating UVs for triplanar mapping\n if (uvProjection == ${uvProjectionConstants.triplanar}) {\n uv = fract(uv);\n }\n\n${hasAnisotropyDirections ? /* wgsl */ `\n${hasCoatNormals ? /* wgsl */ ` (*si).coatNormal = normal;` : ''}\n let directionMaterial = materialsBuffer[(*si).materialIndex];\n if (directionMaterial.anisotropyMapIndex >= 0.0 || directionMaterial.specularAnisotropyRotation != 0.0 || directionMaterial.coatAnisotropyRotation != 0.0${hasCoatNormals ? \" || (USE_COAT_NORMAL_MAPS && directionMaterial.coatNormalMapIndex >= 0.0)\" : \"\"}) {\n let dp1 = tri.p0 - tri.p2;\n let dp2 = tri.p1 - tri.p2;\n var inputTangent = vec4f(0.0);\n if (USE_TANGENT) {\n inputTangent = bary.x * attrBuffers[i0 * ATTR_STRIDE + TANGENT_OFF]\n + bary.y * attrBuffers[i1 * ATTR_STRIDE + TANGENT_OFF]\n + bary.z * attrBuffers[i2 * ATTR_STRIDE + TANGENT_OFF];\n }\n let geometryAxes = anisotropy_axes(normal, dp1, dp2, uv0-uv2, uv1-uv2, inputTangent);\n (*si).geometryTangent = geometryAxes[0];\n (*si).geometryBitangent = geometryAxes[1];\n var directionAxes = geometryAxes;\n if (USE_UV1 && directionMaterial.anisotropyMapIndex >= 0.0 && mapInfo[i32(directionMaterial.anisotropyMapIndex) + NUM_MAPS * 2].x > 0.5) {\n let a = attrBuffers[i0 * ATTR_STRIDE + UV1_OFF].xy;\n let b = attrBuffers[i1 * ATTR_STRIDE + UV1_OFF].xy;\n let c = attrBuffers[i2 * ATTR_STRIDE + UV1_OFF].xy;\n directionAxes = anisotropy_axes(normal, dp1, dp2, a-c, b-c, select(inputTangent, vec4f(0.0), inputTangent.w == 0.0));\n }\n (*si).directionTangent = directionAxes[0];\n (*si).directionBitangent = directionAxes[1];\n${hasCoatNormals ? /* wgsl */ `\n if (USE_COAT_NORMAL_MAPS && directionMaterial.coatNormalMapIndex >= 0.0) {\n let coatIndex = i32(directionMaterial.coatNormalMapIndex);\n var coatAxes = geometryAxes;\n if (USE_UV1 && mapInfo[coatIndex + NUM_MAPS * 2].x > 0.5) {\n let a = attrBuffers[i0 * ATTR_STRIDE + UV1_OFF].xy;\n let b = attrBuffers[i1 * ATTR_STRIDE + UV1_OFF].xy;\n let c = attrBuffers[i2 * ATTR_STRIDE + UV1_OFF].xy;\n coatAxes = anisotropy_axes(normal, dp1, dp2, a-c, b-c,\n select(inputTangent, vec4f(0.0), inputTangent.w == 0.0));\n }\n // Procedural projection overrides both UV sets. UV0 has already applied\n // triplanar wrapping, so use it for either texture channel in that case.\n let decoded = sampleMap(coatIndex, getMapUvSet(coatIndex, uv,\n select(secondaryUv, uv, uvProjection > 0))).rgb * 2.0 - 1.0;\n let mapped = coatAxes[0] * decoded.x * directionMaterial.coatNormalScale.x\n + coatAxes[1] * decoded.y * directionMaterial.coatNormalScale.y + normal * decoded.z;\n if (dot(mapped, mapped) > 1e-12) { (*si).coatNormal = normalize(mapped); }\n }\n` : ''}\n }\n\n` : /* wgsl */ ``}\n (*si).normal = normal;\n (*si).vertexNormal = normal;\n (*si).shadowOffset = vec3f(0.0);\n if (!isHitMicroTriangle) {\n // Smooth-limit geometry offset for direct-light shadow rays. This follows\n // the Cycles shadow terminator construction while keeping the result in\n // local space until the instance transform is available.\n let geometricNormal = normalize(cross(tri.p1 - tri.p0, tri.p2 - tri.p0));\n let smoothNormal = bary.x * n0 + bary.y * n1 + bary.z * n2;\n let a = dot(n2 - n0, tri.p0 - tri.p2);\n let b = dot(n2 - n1, tri.p1 - tri.p2);\n let c = dot(n1 - n0, tri.p1 - tri.p0);\n var h = a * bary.x * (bary.x - 1.0)\n + (a + b + c) * bary.x * bary.y\n + b * bary.y * (bary.y - 1.0);\n if (dot(smoothNormal, geometricNormal) > 0.0) {\n var h0 = max(max(dot(tri.p1 - tri.p0, n0), dot(tri.p2 - tri.p0, n0)), 0.0);\n var h1 = max(max(dot(tri.p0 - tri.p1, n1), dot(tri.p2 - tri.p1, n1)), 0.0);\n var h2 = max(max(dot(tri.p0 - tri.p2, n2), dot(tri.p1 - tri.p2, n2)), 0.0);\n h0 = max(dot(tri.p0 - (*si).position, n0) + h0, 0.0);\n h1 = max(dot(tri.p1 - (*si).position, n1) + h1, 0.0);\n h2 = max(dot(tri.p2 - (*si).position, n2) + h2, 0.0);\n h = max(min(min(h0, h1), h2), h * 0.5);\n }\n else {\n var h0 = max(max(dot(tri.p0 - tri.p1, n0), dot(tri.p0 - tri.p2, n0)), 0.0);\n var h1 = max(max(dot(tri.p1 - tri.p0, n1), dot(tri.p1 - tri.p2, n1)), 0.0);\n var h2 = max(max(dot(tri.p2 - tri.p0, n2), dot(tri.p2 - tri.p1, n2)), 0.0);\n h0 = max(dot((*si).position - tri.p0, n0) + h0, 0.0);\n h1 = max(dot((*si).position - tri.p1, n1) + h1, 0.0);\n h2 = max(dot((*si).position - tri.p2, n2) + h2, 0.0);\n h = min(-min(min(h0, h1), h2), h * 0.5);\n }\n (*si).shadowOffset = smoothNormal * h;\n }\n (*si).uv = uv;\n (*si).uv1 = secondaryUv;\n if (USE_NORMAL_MAPS) {\n let dp1 = tri.p0 - tri.p2;\n let dp2 = tri.p1 - tri.p2;\n let duv1 = uv0 - uv2;\n let duv2 = uv1 - uv2;\n (*si).normal = getMatNormal(\n (*si).materialIndex, uv, normal, dp1, dp2, duv1, duv2\n );\n }\n}\n\nstruct TriangleIntersect {\n t: f32,\n barycentric: vec3f,\n};\n\n\n// Triangle-ray intersection\n// Faster than the classic Möller–Trumbore intersection algorithm\n// http://www.pbr-book.org/3ed-2018/Shapes/Triangle_Meshes.html#TriangleIntersection\nfn intersectTriangle(o: vec3f, d: vec3f, tri: Triangle, maxDim: i32, shear: vec3f, tMax: f32) -> TriangleIntersect {\n var ti: TriangleIntersect;\n\n // translate vertices based on ray origin\n var p0t = tri.p0 - o;\n var p1t = tri.p1 - o;\n var p2t = tri.p2 - o;\n\n // permute components of triangle vertices\n if (maxDim == 0) {\n p0t = p0t.yzx;\n p1t = p1t.yzx;\n p2t = p2t.yzx;\n } else if (maxDim == 1) {\n p0t = p0t.zxy;\n p1t = p1t.zxy;\n p2t = p2t.zxy;\n }\n\n // apply shear transformation to translated vertex positions\n // TODO swizzled assignment is not supported in wgsl\n p0t = vec3f(p0t.xy + shear.xy * p0t.z, p0t.z);\n p1t = vec3f(p1t.xy + shear.xy * p1t.z, p1t.z);\n p2t = vec3f(p2t.xy + shear.xy * p2t.z, p2t.z);\n\n // compute edge function coefficients\n var e = vec3f(\n p1t.x * p2t.y - p1t.y * p2t.x,\n p2t.x * p0t.y - p2t.y * p0t.x,\n p0t.x * p1t.y - p0t.y * p1t.x\n );\n\n if (DEPTH_QUERY_MODE) {\n // Exact grid rays can land on shared edges. Roundoff in the two edge\n // products must not turn the edge into a crack between both triangles.\n let edgeScale = abs(vec3f(p1t.x * p2t.y, p2t.x * p0t.y, p0t.x * p1t.y))\n + abs(vec3f(p1t.y * p2t.x, p2t.y * p0t.x, p0t.y * p1t.x));\n e = select(e, vec3f(0.0), abs(e) <= edgeScale * 1e-6);\n }\n\n // check if intersection is inside triangle\n if (any(e < vec3f(0)) && any(e > vec3f(0))) {\n return ti;\n }\n\n let det = e.x + e.y + e.z;\n if (DEPTH_QUERY_MODE && det == 0.0) { return ti; }\n\n p0t.z *= shear.z;\n p1t.z *= shear.z;\n p2t.z *= shear.z;\n let tScaled = (e.x * p0t.z + e.y * p1t.z + e.z * p2t.z);\n\n // check if closer intersection already exists\n if (abs(tScaled) > abs(tMax * det)) {\n return ti;\n }\n\n let invDet = 1. / det;\n ti.t = tScaled * invDet;\n ti.barycentric = e * invDet;\n\n return ti;\n}\n\n// Pinhole depth queries can lie exactly on a grid vertex or a flat slab.\n// Specialize conservative slabs for that entry point; beauty retains its\n// original traversal arithmetic and random streams.\noverride DEPTH_QUERY_MODE: bool = false;\n\nfn traversalInverse(d: vec3f) -> vec3f {\n if (!DEPTH_QUERY_MODE) { return 1.0 / d; }\n return 1.0 / select(d, select(vec3f(1e-20), vec3f(-1e-20), d < vec3f(0.0)), abs(d) < vec3f(1e-20));\n}\n\nfn depthSlabPadding(invD: vec3f, o: vec3f) -> vec3f {\n if (!DEPTH_QUERY_MODE) { return vec3f(0.0); }\n return abs(invD) * max(vec3f(1.0), abs(o)) * 1e-6;\n}\n\n// Branchless ray/box intersection\n// https://tavianator.com/fast-branchless-raybounding-box-intersections/\nfn intersectBox(o: vec3f, bmin: vec3f, bmax: vec3f, invD: vec3f, tMax: f32) -> f32 {\n let tBot = (bmin - o) * invD;\n let tTop = (bmax - o) * invD;\n let padding = depthSlabPadding(invD, o);\n let tNear = min(tBot, tTop) - padding;\n let tFar = max(tBot, tTop) + padding;\n let t0 = max(tNear.x, max(tNear.y, tNear.z));\n let t1 = min(tFar.x, min(tFar.y, tFar.z));\n return select(select(t1, t0, t0 > 0.0), -1.0, (t0 > t1 || t0 > tMax));\n}\n\nfn maxDimension(v: vec3f) -> i32 {\n return select(select(2, 1, v.y > v.z), select(2, 0, v.x > v.z), v.x > v.y);\n}\n\nfn getShear(d: vec3f, maxDim: i32) -> vec3f {\n // Permute space so that the z dimension is the one where the absolute value of the ray's direction is largest.\n // Then create a shear transformation that aligns ray direction with the +z axis\n if (maxDim == 0) {\n return vec3f(-d.y, -d.z, 1.0) / d.x;\n } else if (maxDim == 1) {\n return vec3f(-d.z, -d.x, 1.0) / d.y;\n } else {\n return vec3f(-d.x, -d.y, 1.0) / d.z;\n }\n}\n\nfn completeSurfaceInteraction(\n si: ptr<function, SurfaceInteraction>,\n hitTri: Triangle,\n hitBarycentric: vec3f,\n hitIndex: vec3i,\n isHitMicroTriangle: bool,\n hitInsIdx: i32,\n hitNormal: vec3f,\n hitPosition: vec3f,\n resolvedMaterialIndex: i32\n) {\n (*si).instanceIndex = hitInsIdx;\n (*si).barycentrics = hitBarycentric.xy;\n (*si).normal = hitNormal;\n (*si).position = hitPosition;\n (*si).materialIndex = i32(unpackMaterialIndex(\n bitcast<i32>(instancesBuffer[hitInsIdx * ${INSTANCE_VEC4_STRIDE}].z)\n ).x);\n // NOTE: Must be called before transforming normal/position to world space.\n // UV projection depends on the local position.\n fetchAttributes(\n si,\n hitTri,\n hitBarycentric,\n hitIndex,\n isHitMicroTriangle,\n resolvedMaterialIndex\n );\n\n let l2w = mat4x4<f32>(\n instancesBuffer[hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 5],\n instancesBuffer[hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 6],\n instancesBuffer[hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 7],\n instancesBuffer[hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 8]\n );\n let w2l = mat4x4<f32>(\n instancesBuffer[hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 1],\n instancesBuffer[hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 2],\n instancesBuffer[hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 3],\n instancesBuffer[hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 4]\n );\n let normalTransform = transpose(mat3x3<f32>(\n w2l[0].xyz,\n w2l[1].xyz,\n w2l[2].xyz\n ));\n\n (*si).faceNormal = normalize(normalTransform * hitNormal);\n (*si).vertexNormal = normalize(normalTransform * (*si).vertexNormal);\n (*si).normal = normalize(normalTransform * (*si).normal);\n${hasCoatNormals ? /* wgsl */ ` (*si).coatNormal = normalize(normalTransform * (*si).coatNormal);` : ''}\n if (USE_TANGENT && dot((*si).tangent, (*si).tangent) > 0.0) {\n (*si).tangent = normalize((l2w * vec4f((*si).tangent, 0.0)).xyz);\n }\n${hasAnisotropyDirections ? /* wgsl */ `\n (*si).directionTangent = (l2w * vec4f((*si).directionTangent, 0.0)).xyz;\n (*si).directionBitangent = (l2w * vec4f((*si).directionBitangent, 0.0)).xyz;\n (*si).geometryTangent = (l2w * vec4f((*si).geometryTangent, 0.0)).xyz;\n (*si).geometryBitangent = (l2w * vec4f((*si).geometryBitangent, 0.0)).xyz;\n` : /* wgsl */ ``}\n (*si).shadowOffset = (l2w * vec4f((*si).shadowOffset, 0.0)).xyz;\n (*si).position = (l2w * vec4((*si).position, 1.0)).xyz;\n}\n\n\n${createIntersectionAlphaWgsl(hasAnisotropyDirections)}\n\n\n\n// Traverse BVH, find closest triangle intersection, and return surface information\nfn intersectScene(ray: Ray, si: ptr<function, SurfaceInteraction>, bounces: i32, doubleSided: bool, isRefractionRay: bool) {\n\n (*si).hit = false;\n\n if (rtUniform.isEmpty > 0) {\n return;\n }\n\n var tMax = RAY_MAX_DISTANCE;\n var maxDim: i32;\n var shear: vec3f;\n\n var hitIndex: vec3i;\n var hitTri: Triangle;\n var hitInsIdx: i32;\n var currentTriangleIndex: i32 = (*si).triangleIndex;\n\n ${getBVHTraverseCode(\n `\n(*si).triangleIndex = triangleIndex;\nhitTri = tri;\nhitIndex = index;\nhitInsIdx = insIdx;\n `,\n hasDisplacement,\n false,\n blasLayoutMask,\n hasAnisotropyDirections\n )}\n\n if (isHit) {\n completeSurfaceInteraction(\n si,\n hitTri,\n hitBarycentric,\n hitIndex,\n isHitMicroTriangle,\n hitInsIdx,\n hitNormal,\n hitPosition,\n -1\n );\n }\n}\n\n// Local intersection helper for shader bevel.\nfn intersectSceneBevel(ray: Ray, si: ptr<function, SurfaceInteraction>, instanceIndex: i32, maxDistance: f32) {\n (*si).hit = false;\n\n if (rtUniform.isEmpty > 0) {\n return;\n }\n\n let bounces = 0;\n let doubleSided = true;\n let isRefractionRay = false;\n var tMax = maxDistance;\n var maxDim: i32;\n var shear: vec3f;\n var hitIndex: vec3i;\n var hitTri: Triangle;\n var hitInsIdx: i32;\n var currentTriangleIndex: i32 = -1;\n\n ${getBVHTraverseCode(\n `\n(*si).triangleIndex = triangleIndex;\nhitTri = tri;\nhitIndex = index;\nhitInsIdx = insIdx;\n `,\n hasDisplacement,\n false,\n blasLayoutMask,\n hasAnisotropyDirections,\n `\n if (insIdx != instanceIndex) {\n continue;\n }\n `\n )}\n\n if (isHit) {\n (*si).instanceIndex = hitInsIdx;\n (*si).normal = hitNormal;\n (*si).position = hitPosition;\n (*si).materialIndex = i32(unpackMaterialIndex(bitcast<i32>(instancesBuffer[hitInsIdx * ${INSTANCE_VEC4_STRIDE}].z)).x);\n fetchAttributes(\n si,\n hitTri,\n hitBarycentric,\n hitIndex,\n isHitMicroTriangle,\n -1\n );\n\n let l2w = mat4x4<f32>(\n instancesBuffer[hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 5],\n instancesBuffer[hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 6],\n instancesBuffer[hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 7],\n instancesBuffer[hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 8]\n );\n let w2l = mat4x4<f32>(\n instancesBuffer[hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 1],\n instancesBuffer[hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 2],\n instancesBuffer[hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 3],\n instancesBuffer[hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 4]\n );\n let normalTransform = transpose(mat3x3<f32>(\n w2l[0].xyz,\n w2l[1].xyz,\n w2l[2].xyz\n ));\n (*si).faceNormal = normalize(normalTransform * hitNormal);\n (*si).vertexNormal = normalize(normalTransform * (*si).vertexNormal);\n (*si).normal = normalize(normalTransform * (*si).normal);\n${hasCoatNormals ? /* wgsl */ ` (*si).coatNormal = normalize(normalTransform * (*si).coatNormal);` : ''}\n if (USE_TANGENT && dot((*si).tangent, (*si).tangent) > 0.0) {\n (*si).tangent = normalize((l2w * vec4f((*si).tangent, 0.0)).xyz);\n }\n${hasAnisotropyDirections ? /* wgsl */ `\n (*si).directionTangent = (l2w * vec4f((*si).directionTangent, 0.0)).xyz;\n (*si).directionBitangent = (l2w * vec4f((*si).directionBitangent, 0.0)).xyz;\n (*si).geometryTangent = (l2w * vec4f((*si).geometryTangent, 0.0)).xyz;\n (*si).geometryBitangent = (l2w * vec4f((*si).geometryBitangent, 0.0)).xyz;\n` : /* wgsl */ ``}\n (*si).shadowOffset = (l2w * vec4f((*si).shadowOffset, 0.0)).xyz;\n (*si).position = (l2w * vec4((*si).position, 1.0)).xyz;\n }\n}\n\nfn intersectSceneShadow(ray: Ray, lightDistance: f32, currTriangleIndex: i32) -> bool {\n if (rtUniform.isEmpty > 0) {\n return false;\n }\n\n let doubleSided = true;\n let shadowRayTMax = min(max(lightDistance, 0.0), RAY_MAX_DISTANCE);\n var tMax = shadowRayTMax;\n var maxDim: i32;\n var shear: vec3f;\n\n let bounces = 1;\n var isRefractionRay = false;\n var currentTriangleIndex: i32 = currTriangleIndex;\n\n ${getBVHTraverseCode(\n /*wgsl */ `\n// Traversal is already clipped to the sampled light. Affine instance\n// transforms preserve the ray parameter because local directions are not\n// renormalized, so no local-to-world hit transform or distance() is needed.\nvar isOccluded = true;\n\nif (isOccluded) {\n if (HAS_ALPHA) {\n let materialIndex = i32(unpackMaterialIndex(bitcast<i32>(insData.z)).x);\n if (!intersectionAlphaPasses(\n materialIndex,\n index,\n hitBarycentric,\n hitPosition,\n hitNormal\n )) {\n // Continue looking for an opaque hit, but never traverse beyond the light.\n tMax = shadowRayTMax;\n isOccluded = false;\n }\n }\n}\nif (isOccluded) {\n return true;\n}\n `,\n hasDisplacement,\n true,\n blasLayoutMask,\n hasAnisotropyDirections\n )}\n\n return false;\n}\n\n`;\n}\n"]}