@meimu/mutracer 0.1.3 → 0.1.5

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 (125) hide show
  1. package/dist/assets/bvh.worker.js +1 -1
  2. package/dist/mutracer.umd.js +4155 -4052
  3. package/docs/api.md +13 -10
  4. package/docs/materials.md +17 -26
  5. package/docs/threejs.md +35 -9
  6. package/docs/viewer.md +36 -3
  7. package/lib/Engine.js +18 -1
  8. package/lib/Engine.js.map +1 -1
  9. package/lib/main.d.ts +1 -1
  10. package/lib/main.js.map +1 -1
  11. package/lib/oidn.d.ts +1 -1
  12. package/lib/oidnBundledWeights.cdn.d.ts +2 -2
  13. package/lib/renderer/MaterialTable.d.ts +1 -0
  14. package/lib/renderer/MaterialTable.js +10 -4
  15. package/lib/renderer/MaterialTable.js.map +1 -1
  16. package/lib/renderer/WGPUBloomPass.js +18 -18
  17. package/lib/renderer/WGPUFullQuadPass.js +6 -6
  18. package/lib/renderer/WGPUJNLMDenoiser.js +69 -69
  19. package/lib/renderer/WGPURayTraceOutput.js +14 -14
  20. package/lib/renderer/WGPURayTracePass.d.ts +2 -0
  21. package/lib/renderer/WGPURayTracePass.js +17 -9
  22. package/lib/renderer/WGPURayTracePass.js.map +1 -1
  23. package/lib/renderer/WGPURenderingPipeline.js +15 -15
  24. package/lib/renderer/WebGLJNLMDenoiser.js +69 -69
  25. package/lib/renderer/WebGLRayTraceOutputPass.js +4 -4
  26. package/lib/renderer/WebGLRayTracePass.d.ts +1 -0
  27. package/lib/renderer/WebGLRayTracePass.js +8 -1
  28. package/lib/renderer/WebGLRayTracePass.js.map +1 -1
  29. package/lib/renderer/envMapCreation.d.ts +5 -1
  30. package/lib/renderer/envMapCreation.js +42 -5
  31. package/lib/renderer/envMapCreation.js.map +1 -1
  32. package/lib/renderer/geometryUpdates.js +1 -1
  33. package/lib/renderer/geometryUpdates.js.map +1 -1
  34. package/lib/renderer/glsl/chunks/cameraRay.glsl.js +12 -12
  35. package/lib/renderer/glsl/chunks/envMap.glsl.js +66 -54
  36. package/lib/renderer/glsl/chunks/envMap.glsl.js.map +1 -1
  37. package/lib/renderer/glsl/chunks/intersect.glsl.js +323 -315
  38. package/lib/renderer/glsl/chunks/intersect.glsl.js.map +1 -1
  39. package/lib/renderer/glsl/chunks/materialBuffer.glsl.js +154 -121
  40. package/lib/renderer/glsl/chunks/materialBuffer.glsl.js.map +1 -1
  41. package/lib/renderer/glsl/chunks/openPbrBsdf.glsl.js +434 -434
  42. package/lib/renderer/glsl/chunks/openPbrBsdf.glsl.js.map +1 -1
  43. package/lib/renderer/glsl/chunks/rand.glsl.js +3 -3
  44. package/lib/renderer/glsl/chunks/random.glsl.js +3 -3
  45. package/lib/renderer/glsl/chunks/rayTraceCore.glsl.js +42 -41
  46. package/lib/renderer/glsl/chunks/rayTraceCore.glsl.js.map +1 -1
  47. package/lib/renderer/glsl/chunks/sampleLight.glsl.js +69 -69
  48. package/lib/renderer/glsl/chunks/sampleMaterial.glsl.js +84 -84
  49. package/lib/renderer/glsl/chunks/sampleMaterial.glsl.js.map +1 -1
  50. package/lib/renderer/glsl/chunks/sampleShadowCatcher.glsl.js +12 -12
  51. package/lib/renderer/glsl/chunks/sampling.glsl.js +75 -75
  52. package/lib/renderer/glsl/chunks/sobol.glsl.js +61 -61
  53. package/lib/renderer/glsl/chunks/textureLinear.glsl.js +5 -5
  54. package/lib/renderer/glsl/chunks/uvProjection.glsl.js +11 -11
  55. package/lib/renderer/glsl/depthQuery.frag.js +5 -5
  56. package/lib/renderer/glsl/depthQuery.frag.js.map +1 -1
  57. package/lib/renderer/glsl/normalizeRayTraceOutput.frag.js +8 -8
  58. package/lib/renderer/glsl/rayTrace.frag.js +133 -133
  59. package/lib/renderer/glsl/rayTrace.frag.js.map +1 -1
  60. package/lib/renderer/glsl/shadowCatcher.frag.js +9 -9
  61. package/lib/renderer/glsl/shadowCatcher.frag.js.map +1 -1
  62. package/lib/renderer/glsl/webglPipeline.frag.js +52 -52
  63. package/lib/renderer/lightsUtil.js +6 -4
  64. package/lib/renderer/lightsUtil.js.map +1 -1
  65. package/lib/renderer/materialChanges.js +1 -1
  66. package/lib/renderer/materialChanges.js.map +1 -1
  67. package/lib/renderer/materialComparison.generated.js +36 -18
  68. package/lib/renderer/materialComparison.generated.js.map +1 -1
  69. package/lib/renderer/prepareMeshBVH.js +2 -2
  70. package/lib/renderer/prepareMeshBVH.js.map +1 -1
  71. package/lib/renderer/sceneChangeDetector.js +4 -1
  72. package/lib/renderer/sceneChangeDetector.js.map +1 -1
  73. package/lib/renderer/sceneLightChanges.js +3 -2
  74. package/lib/renderer/sceneLightChanges.js.map +1 -1
  75. package/lib/renderer/scratchSampling.js +18 -18
  76. package/lib/renderer/wgsl/chunks/common.wgsl.js +4 -4
  77. package/lib/renderer/wgsl/chunks/envMap.wgsl.d.ts +1 -1
  78. package/lib/renderer/wgsl/chunks/envMap.wgsl.js +39 -31
  79. package/lib/renderer/wgsl/chunks/envMap.wgsl.js.map +1 -1
  80. package/lib/renderer/wgsl/chunks/intersect.wgsl.js +476 -470
  81. package/lib/renderer/wgsl/chunks/intersect.wgsl.js.map +1 -1
  82. package/lib/renderer/wgsl/chunks/intersectionAlpha.wgsl.js +8 -8
  83. package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.js +95 -60
  84. package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.js.map +1 -1
  85. package/lib/renderer/wgsl/chunks/openPbrBsdf.wgsl.js +469 -469
  86. package/lib/renderer/wgsl/chunks/pdm_displacement.wgsl.d.ts +1 -1
  87. package/lib/renderer/wgsl/chunks/pdm_displacement.wgsl.js +117 -120
  88. package/lib/renderer/wgsl/chunks/pdm_displacement.wgsl.js.map +1 -1
  89. package/lib/renderer/wgsl/chunks/rand.wgsl.js +3 -3
  90. package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.js +25 -22
  91. package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.js.map +1 -1
  92. package/lib/renderer/wgsl/chunks/sampleLight.wgsl.js +127 -127
  93. package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.js +136 -136
  94. package/lib/renderer/wgsl/chunks/sampleShadowCatcher.wgsl.js +6 -6
  95. package/lib/renderer/wgsl/chunks/sampling.wgsl.js +73 -73
  96. package/lib/renderer/wgsl/chunks/sobol.wgsl.js +62 -62
  97. package/lib/renderer/wgsl/chunks/uvProjection.wgsl.js +11 -11
  98. package/lib/renderer/wgsl/compose.frag.wgsl.js +105 -105
  99. package/lib/renderer/wgsl/customDenoise.wgsl.js +10 -10
  100. package/lib/renderer/wgsl/depthQuery.wgsl.d.ts +1 -1
  101. package/lib/renderer/wgsl/depthQuery.wgsl.js +5 -5
  102. package/lib/renderer/wgsl/depthQuery.wgsl.js.map +1 -1
  103. package/lib/renderer/wgsl/offlineAdaptive.compute.js +28 -28
  104. package/lib/renderer/wgsl/rayTrace.compute.js +251 -248
  105. package/lib/renderer/wgsl/rayTrace.compute.js.map +1 -1
  106. package/lib/scene/EnvironmentLight.d.ts +9 -3
  107. package/lib/scene/EnvironmentLight.js +9 -2
  108. package/lib/scene/EnvironmentLight.js.map +1 -1
  109. package/lib/scene/OpenPBRMaterial.d.ts +1 -1
  110. package/lib/scene/OpenPBRMaterial.js +1 -1
  111. package/lib/scene/OpenPBRMaterial.js.map +1 -1
  112. package/lib/scene/Scene.d.ts +4 -0
  113. package/lib/scene/Scene.js.map +1 -1
  114. package/lib/scene/SpotLight.d.ts +2 -2
  115. package/lib/scene/SpotLight.js +1 -1
  116. package/lib/scene/SpotLight.js.map +1 -1
  117. package/lib/scene/Texture.d.ts +17 -6
  118. package/lib/scene/Texture.js +13 -4
  119. package/lib/scene/Texture.js.map +1 -1
  120. package/lib/util/fromTHREE.js +151 -29
  121. package/lib/util/fromTHREE.js.map +1 -1
  122. package/lib/version.d.ts +1 -1
  123. package/lib/version.js +1 -1
  124. package/lib/version.js.map +1 -1
  125. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"intersect.glsl.js","sourceRoot":"","sources":["../../../../src/renderer/glsl/chunks/intersect.glsl.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AACpE,qDAAqD;AACrD,sDAAsD;AACtD,kFAAkF;AAElF,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAElE,SAAS,kBAAkB,CACzB,aAAqB,EACrB,gBAAgB,GAAG,EAAE,EACrB,WAAW,GAAG,kBAAkB;IAEhC,MAAM,kBAAkB,GAAG,IAAI,CAAA;oDACqC,oBAAoB;;;EAG9E,gBAAgB;;;;;;;;;;;;;;;;yBAgBqB,oBAAoB;;;;;;;;;EASvD,aAAa;;;CAG1B,CAAC;IACA,OAAO,IAAI,CAAA;;;;eAII,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UA8BE,oBAAoB;UACpB,oBAAoB;UACpB,oBAAoB;UACpB,oBAAoB;;;;;;;;;;;kDAWiC,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAmN/F,kBAAkB;;;CAGzB,CAAC;AACF,CAAC;AAED,eAAe,iBAAiB,CAAC;IAC/B,QAAQ,EAAE;QACR,cAAc,EAAE,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC;QAC3C,YAAY,EAAE,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC;QACzC,aAAa,EAAE,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC;QAC1C,QAAQ,EAAE,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC;QACrC,SAAS,EAAE,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC;QACtC,WAAW,EAAE,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC;QACxC,mBAAmB,EAAE,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC;QAEhD,SAAS,EAAE,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC;QACtC,eAAe,EAAE,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC;QAE5C,UAAU,EAAE,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;QACpC,aAAa,EAAE,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;QAEvC,gBAAgB,EAAE,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;QAE1C,UAAU,EAAE,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;QAEjC,YAAY,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;KACrC;IACD,IAAI,EAAE,IAAI,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAmjBV,kBAAkB,CAAC,IAAI,CAAA;;;;;;;;CAQxB,CAAC;;;;mDAImF,oBAAoB;;;;UAI5E,oBAAoB;UACpB,oBAAoB;UACpB,oBAAoB;UACpB,oBAAoB;;;UAGpB,oBAAoB;UACpB,oBAAoB;UACpB,oBAAoB;UACpB,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAoC/C,kBAAkB,CAClB,IAAI,CAAA;;;;;;;;CAQL,EACC,IAAI,CAAA;;;;CAIL,EACC,aAAa,CACd;;;;mDAIoF,oBAAoB;;;UAG5E,oBAAoB;UACpB,oBAAoB;UACpB,oBAAoB;UACpB,oBAAoB;;;UAGpB,oBAAoB;UACpB,oBAAoB;UACpB,oBAAoB;UACpB,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiC/C,kBAAkB,CAClB,IAAI,CAAA;;;;;;;;;oDAS6E,oBAAoB;;;;;;;;;;;;;;;;uBAgB5E,sBAAsB,CAAC,KAAK,GAAG,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkD3D,EACC,IAAI,CAAA;;;;CAIL,EACC,gDAAgD,CACjD;;;CAGA;CACA,CAAC,CAAC","sourcesContent":["import { mapAlphaUsageConstants } from '../../materialConstants.js';\n// 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 { INSTANCE_VEC4_STRIDE } from '../../bvhAccel.js';\nimport { createShaderMixin, glsl, Shader } from '../../shader.js';\n\nfunction getBVHTraverseCode(\n handleHitCode: string,\n skipTriangleCode = '',\n initialTMax = 'RAY_MAX_DISTANCE'\n) {\n const intersectTriangles = glsl`\n ivec3 vertexOffset = ivec3(floatBitsToInt(fetchIns(insIdx * ${INSTANCE_VEC4_STRIDE}).y));\n for (int k = 0; k < primitiveCount; k++) {\n int triangleIndex = primitiveOffset + k;\n ${skipTriangleCode}\n vec4 r3 = fetchBVH(triangleIndex);\n ivec3 index = floatBitsToInt(r3.xyz) + vertexOffset;\n Triangle tri = Triangle(\n fetchData(positionBuffer, index.x, vertexColumns).xyz,\n fetchData(positionBuffer, index.y, vertexColumns).xyz,\n fetchData(positionBuffer, index.z, vertexColumns).xyz\n );\n TriangleIntersect hit = intersectTriangle(o, d, tri, maxDim, shear, tMax);\n\n if (hit.t > 0.0) {\n tMax = hit.t;\n vec3 e1 = tri.p1 - tri.p0;\n vec3 e2 = tri.p2 - tri.p0;\n vec3 faceNormal = normalize(cross(e1, e2));\n int sideMaterialIndex = int(unpackMaterialIndex(\n floatBitsToInt(fetchIns(insIdx * ${INSTANCE_VEC4_STRIDE}).z)\n ).x);\n bool materialDoubleSided = fetchMatData(\n sideMaterialIndex * MAT_DATA_SIZE + MAT_ROW_ALPHA\n ).z > 0.5;\n if (!doubleSided && !materialDoubleSided && dot(faceNormal, d) > 0.0) {\n continue;\n }\n\n ${handleHitCode}\n }\n }\n`;\n return glsl`\n ivec2 stack[STACK_SIZE];\n int top = -1;\n int prevInsIdx = -1;\n float tMax = ${initialTMax};\n\n vec3 o = ray.o;\n vec3 d = ray.d;\n\n bool isHit = false;\n vec3 hitFaceNormal;\n vec3 hitBarycentric;\n ivec3 hitIndex;\n\n Triangle hitTri;\n\n // TODO not sure why. but do root node check here will cause rendering wrong in windows.\n top = 0;\n stack[0] = ivec2(0, -1);\n\n int iter = 0;\n while(top >= 0 && iter < 5000) {\n iter++;\n ivec2 node = stack[top--];\n\n int insIdx = node.y;\n\n if (insIdx != prevInsIdx) {\n if (insIdx < 0) {\n o = ray.o;\n d = ray.d;\n }\n else {\n mat4 w2l = mat4(\n fetchIns(insIdx * ${INSTANCE_VEC4_STRIDE} + 1),\n fetchIns(insIdx * ${INSTANCE_VEC4_STRIDE} + 2),\n fetchIns(insIdx * ${INSTANCE_VEC4_STRIDE} + 3),\n fetchIns(insIdx * ${INSTANCE_VEC4_STRIDE} + 4)\n );\n o = (w2l * vec4(ray.o, 1.0)).xyz;\n d = (w2l * vec4(ray.d, 0.0)).xyz;\n }\n maxDim = maxDimension(abs(d));\n shear = getShear(d, maxDim);\n prevInsIdx = insIdx;\n }\n\n int bufferOff = insIdx < 0 ? 0 : blasOffset;\n uint encodedBlasOffset = insIdx < 0 ? 0u : floatBitsToUint(fetchIns(insIdx * ${INSTANCE_VEC4_STRIDE}).x);\n int blasLayout = int(encodedBlasOffset >> 30u);\n int blasInsOffset = int(encodedBlasOffset & 0x3fffffffu);\n int primitiveCount = 0;\n int primitiveOffset = 0;\n\n if (blasLayout == 3) {\n uint nodeInfo = uint(node.x);\n if ((nodeInfo & 0x80000000u) != 0u) {\n primitiveCount = int((nodeInfo >> 23u) & 0xffu);\n primitiveOffset = bufferOff + blasInsOffset + int(nodeInfo & 0x007fffffu);\n }\n else {\n int i = node.x + bufferOff;\n vec4 compressed0 = fetchBVH(i);\n vec4 compressed1 = fetchBVH(i + 1);\n vec4 compressed2 = fetchBVH(i + 2);\n vec4 compressed3 = fetchBVH(i + 3);\n vec4 compressed4 = fetchBVH(i + 4);\n uint header = floatBitsToUint(compressed0.w);\n int exponentX = int(header << 24u) >> 24;\n int exponentY = int((header >> 8u) << 24u) >> 24;\n int exponentZ = int((header >> 16u) << 24u) >> 24;\n vec3 quantizationScale = vec3(\n uintBitsToFloat(uint(exponentX + 127) << 23u),\n uintBitsToFloat(uint(exponentY + 127) << 23u),\n uintBitsToFloat(uint(exponentZ + 127) << 23u)\n );\n uint childBase = floatBitsToUint(compressed1.x);\n uint triangleBase = floatBitsToUint(compressed1.y);\n uint internalMask = header >> 24u;\n uint octant = (d.x < 0.0 ? 4u : 0u) |\n (d.y < 0.0 ? 2u : 0u) |\n (d.z < 0.0 ? 1u : 0u);\n uint quantizedWords[12] = uint[12](\n floatBitsToUint(compressed2.x), floatBitsToUint(compressed2.y),\n floatBitsToUint(compressed2.z), floatBitsToUint(compressed2.w),\n floatBitsToUint(compressed3.x), floatBitsToUint(compressed3.y),\n floatBitsToUint(compressed3.z), floatBitsToUint(compressed3.w),\n floatBitsToUint(compressed4.x), floatBitsToUint(compressed4.y),\n floatBitsToUint(compressed4.z), floatBitsToUint(compressed4.w)\n );\n\n for (int order = 7; order >= 0; order--) {\n uint slot = uint(order) ^ octant;\n uint metaWord = slot >= 4u\n ? floatBitsToUint(compressed1.w)\n : floatBitsToUint(compressed1.z);\n uint meta = (metaWord >> ((slot & 3u) * 8u)) & 0xffu;\n if (meta == 0u) continue;\n uint byteShift = (slot & 3u) * 8u;\n uint wordOffset = slot >> 2u;\n vec3 quantizedMin = vec3(\n float((quantizedWords[wordOffset] >> byteShift) & 0xffu),\n float((quantizedWords[2u + wordOffset] >> byteShift) & 0xffu),\n float((quantizedWords[4u + wordOffset] >> byteShift) & 0xffu)\n );\n vec3 quantizedMax = vec3(\n float((quantizedWords[6u + wordOffset] >> byteShift) & 0xffu),\n float((quantizedWords[8u + wordOffset] >> byteShift) & 0xffu),\n float((quantizedWords[10u + wordOffset] >> byteShift) & 0xffu)\n );\n vec3 childMin = compressed0.xyz + quantizedMin * quantizationScale;\n vec3 childMax = compressed0.xyz + quantizedMax * quantizationScale;\n if (intersectBox(o, d, childMin, childMax, tMax) <= 0.0) continue;\n uint localOffset = meta & 0x1fu;\n if (localOffset >= 24u) {\n uint precedingMask = internalMask & ((1u << slot) - 1u);\n uint internalIndex = childBase + countBvhBits(precedingMask);\n stack[++top] = ivec2(\n blasInsOffset + int(internalIndex * 5u), insIdx\n );\n }\n else {\n uint triangleCount = countBvhBits(meta >> 5u);\n uint primitiveAddress = triangleBase + localOffset;\n uint leafInfo = 0x80000000u |\n (triangleCount << 23u) |\n (primitiveAddress & 0x007fffffu);\n stack[++top] = ivec2(int(leafInfo), insIdx);\n }\n }\n }\n }\n else if (blasLayout == 2) {\n int i = node.x + bufferOff;\n vec4 gpu0 = fetchBVH(i);\n vec4 gpu1 = fetchBVH(i + 1);\n vec4 gpu2 = fetchBVH(i + 2);\n vec4 gpu3 = fetchBVH(i + 3);\n primitiveCount = int(floatBitsToUint(gpu2.w));\n if (primitiveCount > 0) {\n primitiveOffset = bufferOff + blasInsOffset + int(floatBitsToUint(gpu3.w));\n }\n else {\n int left = blasInsOffset + int(floatBitsToUint(gpu0.w) * 4u);\n int right = blasInsOffset + int(floatBitsToUint(gpu1.w) * 4u);\n float d0 = intersectBox(o, d, gpu0.xyz, gpu1.xyz, tMax);\n float d1 = intersectBox(o, d, gpu2.xyz, gpu3.xyz, tMax);\n if (d0 > d1) {\n if (d0 > 0.0) stack[++top] = ivec2(left, insIdx);\n if (d1 > 0.0) stack[++top] = ivec2(right, insIdx);\n }\n else {\n if (d1 > 0.0) stack[++top] = ivec2(right, insIdx);\n if (d0 > 0.0) stack[++top] = ivec2(left, insIdx);\n }\n }\n }\n else if (blasLayout == 1) {\n uint nodeInfo = uint(node.x);\n if ((nodeInfo & 0x80000000u) != 0u) {\n primitiveCount = int((nodeInfo >> 23u) & 0xffu);\n primitiveOffset = bufferOff + blasInsOffset + int(nodeInfo & 0x007fffffu);\n }\n else {\n int i = node.x + bufferOff;\n vec4 wide0 = fetchBVH(i);\n vec4 wide1 = fetchBVH(i + 1);\n vec4 wide2 = fetchBVH(i + 2);\n vec4 wide3 = fetchBVH(i + 3);\n vec3 nodeMin = wide0.xyz;\n vec3 nodeExtent = vec3(wide0.w, wide1.x, wide1.y);\n uint childInfos[4] = uint[4](\n floatBitsToUint(wide1.z), floatBitsToUint(wide1.w),\n floatBitsToUint(wide2.x), floatBitsToUint(wide2.y)\n );\n vec4 childMinX = unpackBvh4Bounds(floatBitsToUint(wide2.z));\n vec4 childMinY = unpackBvh4Bounds(floatBitsToUint(wide2.w));\n vec4 childMinZ = unpackBvh4Bounds(floatBitsToUint(wide3.x));\n vec4 childMaxX = unpackBvh4Bounds(floatBitsToUint(wide3.y));\n vec4 childMaxY = unpackBvh4Bounds(floatBitsToUint(wide3.z));\n vec4 childMaxZ = unpackBvh4Bounds(floatBitsToUint(wide3.w));\n vec3 invD = traversalInverse(d);\n vec3 padding = depthSlabPadding(invD, o);\n vec4 tx0 = (nodeMin.x + childMinX * nodeExtent.x - o.x) * invD.x;\n vec4 tx1 = (nodeMin.x + childMaxX * nodeExtent.x - o.x) * invD.x;\n vec4 ty0 = (nodeMin.y + childMinY * nodeExtent.y - o.y) * invD.y;\n vec4 ty1 = (nodeMin.y + childMaxY * nodeExtent.y - o.y) * invD.y;\n vec4 tz0 = (nodeMin.z + childMinZ * nodeExtent.z - o.z) * invD.z;\n vec4 tz1 = (nodeMin.z + childMaxZ * nodeExtent.z - o.z) * invD.z;\n vec4 tNear = max(max((min(tx0, tx1) - vec4(padding.x)), (min(ty0, ty1) - vec4(padding.y))), (min(tz0, tz1) - vec4(padding.z)));\n vec4 tFar = min(min((max(tx0, tx1) + vec4(padding.x)), (max(ty0, ty1) + vec4(padding.y))), (max(tz0, tz1) + vec4(padding.z)));\n vec4 distance = mix(tFar, tNear, greaterThan(tNear, vec4(0.0)));\n distance = mix(distance, vec4(-1.0), greaterThan(tNear, min(tFar, vec4(tMax))));\n float childDistances[4] = float[4](distance.x, distance.y, distance.z, distance.w);\n\n // Push far-to-near so that the closest child is popped first.\n for (int order = 0; order < 4; order++) {\n float farthestDistance = -1.0;\n int farthestChild = -1;\n for (int child = 0; child < 4; child++) {\n if (childInfos[child] != 0u && childDistances[child] > farthestDistance) {\n farthestDistance = childDistances[child];\n farthestChild = child;\n }\n }\n if (farthestChild >= 0 && farthestDistance > 0.0) {\n uint childInfo = childInfos[farthestChild];\n int childAddress = int(childInfo);\n if ((childInfo & 0x80000000u) == 0u) {\n childAddress += blasInsOffset;\n }\n stack[++top] = ivec2(childAddress, insIdx);\n childDistances[farthestChild] = -1.0;\n }\n }\n }\n }\n else {\n int i = node.x + bufferOff;\n vec4 r1 = fetchBVH(i);\n int pointerToRightOrNumPrims = floatBitsToInt(r1.w);\n\n if (pointerToRightOrNumPrims >= 0) {\n int left = node.x + 2;\n int right = pointerToRightOrNumPrims + blasInsOffset;\n\n float d0 = intersectBox(o, d, fetchBVH(left + bufferOff).xyz, fetchBVH(left + 1 + bufferOff).xyz, tMax);\n float d1 = intersectBox(o, d, fetchBVH(right + bufferOff).xyz, fetchBVH(right + 1 + bufferOff).xyz, tMax);\n if (d0 > d1) {\n if (d0 > 0.0) {\n stack[++top] = ivec2(left, insIdx);\n }\n if (d1 > 0.0) {\n stack[++top] = ivec2(right, insIdx);\n }\n }\n else {\n if (d1 > 0.0) {\n stack[++top] = ivec2(right, insIdx);\n }\n if (d0 > 0.0) {\n stack[++top] = ivec2(left, insIdx);\n }\n }\n }\n else {\n if (insIdx < 0) {\n for (int k = 0; k < -pointerToRightOrNumPrims; k++) {\n vec4 r3 = fetchBVH(i + 2 + k);\n stack[++top] = ivec2(floatBitsToInt(r3.y), floatBitsToInt(r3.x));\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\nexport default createShaderMixin({\n uniforms: {\n positionBuffer: Shader.uniform('sampler2D'),\n normalBuffer: Shader.uniform('sampler2D'),\n tangentBuffer: Shader.uniform('sampler2D'),\n uvBuffer: Shader.uniform('sampler2D'),\n uv1Buffer: Shader.uniform('sampler2D'),\n colorBuffer: Shader.uniform('sampler2D'),\n materialIndexBuffer: Shader.uniform('sampler2D'),\n\n bvhBuffer: Shader.uniform('sampler2D'),\n instancesBuffer: Shader.uniform('sampler2D'),\n\n bvhColumns: Shader.uniform('int', 1),\n vertexColumns: Shader.uniform('int', 1),\n\n instancesColumns: Shader.uniform('int', 1),\n\n blasOffset: Shader.uniform('int'),\n\n isEmptyScene: Shader.uniform('bool')\n },\n main: glsl`\n\nstruct Triangle {\n vec3 p0;\n vec3 p1;\n vec3 p2;\n};\n#ifdef NUM_LIGHTS\nfloat intersectSphere(float rad, vec3 pos, Ray r) {\n vec3 op = pos - r.o;\n float eps = 0.001;\n float b = dot(op, r.d);\n float det = b * b - dot(op, op) + rad * rad;\n if (det < 0.0)\n return INF;\n\n det = sqrt(det);\n float t1 = b - det;\n if (t1 > eps)\n return t1;\n\n float t2 = b + det;\n if (t2 > eps)\n return t2;\n\n return INF;\n}\n\nfloat intersectRect(vec3 pos, vec3 u, vec3 v, vec3 n, Ray r) {\n float dt = dot(r.d, n);\n float t = (dot(n, pos) - dot(n, r.o)) / dt;\n\n if (t > EPS) {\n vec3 p = r.o + r.d * t;\n vec3 vi = p - pos;\n float a1 = dot(u / dot(u, u), vi);\n if (a1 >= 0.0 && a1 <= 1.0) {\n float a2 = 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\nfloat intersectLight(Ray r, Light light, out LightSampleRec lightSample) {\n // Directional lights are delta sources without finite geometry. Start at\n // INF so they cannot accidentally terminate a BSDF continuation as a\n // geometric light hit; rect and sphere branches overwrite this value.\n float d = INF;\n vec3 u = light.u;\n vec3 v = light.v;\n int type = int(light.type);\n\n#ifdef HAS_RECT_LIGHTS\n if (type == RECT_LIGHT) {\n // Adjust the position from center to start corner\n vec3 pos = light.position-u*0.5-v*0.5;\n vec3 normal = normalize(cross(u, v));\n if (dot(normal, r.d) > 0.) // Hide backfacing quad light\n return INF;\n\n d = intersectRect(pos, u, v, normal, r);\n if (d < 0.) {\n d = INF;\n }\n else {\n float cosTheta = dot(-r.d, normal);\n float spread = light.spread;\n if (cosTheta >= cos(spread * PI / 2.0)) {\n lightSample.pdf = (d * d) / (light.area * cosTheta);\n lightSample.emission = light.emission;\n }\n }\n }\n#endif\n#ifdef HAS_SPHERE_LIGHTS\n if (type == SPHERE_LIGHT) {\n if (light.radius <= 0.0) {\n vec3 toPoint = light.position - r.o;\n float projection = dot(toPoint, r.d);\n vec3 perpendicular = toPoint - projection * r.d;\n if (projection <= EPS || dot(perpendicular, perpendicular) > 1e-8) return INF;\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 } else {\n d = intersectSphere(light.radius, light.position, r);\n lightSample.isDelta = false;\n }\n if (d < 0.) {\n d = INF;\n }\n else {\n float centerDistance = distance(r.o, light.position);\n float cosThetaMax = centerDistance <= light.radius\n ? -1.0\n : sqrt(max(0.0, 1.0 - light.radius * light.radius / (centerDistance * centerDistance)));\n if (light.radius > 0.0) {\n lightSample.pdf = centerDistance <= light.radius\n ? INV4PI\n : 1.0 / max(TWOPI * (1.0 - cosThetaMax), 1e-8);\n }\n lightSample.emission = light.radius <= 0.0\n ? light.emission / max(d * d, 1e-8)\n : light.emission;\n }\n }\n#endif\n#ifdef HAS_SPOT_LIGHTS\n if (type == SPOT_LIGHT) {\n if (light.radius <= 0.0) {\n vec3 toPoint = light.position - r.o;\n float projection = dot(toPoint, r.d);\n vec3 perpendicular = toPoint - projection * r.d;\n if (projection <= EPS || dot(perpendicular, perpendicular) > 1e-8) return INF;\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 } else {\n d = intersectSphere(light.radius, light.position, r);\n lightSample.isDelta = false;\n }\n if (d != INF) {\n vec3 hitPt = r.o + d * r.d;\n float multiplier = evaluateSpotEmissionMultiplier(light, normalize(r.o - hitPt));\n if (multiplier <= 0.0) return INF;\n float centerDistance = distance(r.o, light.position);\n float cosThetaMax = centerDistance <= light.radius\n ? -1.0\n : sqrt(max(0.0, 1.0 - light.radius * light.radius / (centerDistance * centerDistance)));\n if (light.radius > 0.0) {\n lightSample.pdf = centerDistance <= light.radius\n ? INV4PI\n : 1.0 / max(TWOPI * (1.0 - cosThetaMax), 1e-8);\n }\n lightSample.emission = light.emission * multiplier * (light.radius <= 0.0\n ? 1.0 / max(d * d, 1e-8)\n : 1.0);\n }\n }\n#endif\n\n return d;\n}\n#endif\n\nint randomPickMaterial(ivec4 m0, ivec4 m1, ivec4 m2, vec3 bary) {\n vec2 m00 = unpackMaterialIndex(m0.x);\n vec2 m01 = unpackMaterialIndex(m0.y);\n vec2 m02 = unpackMaterialIndex(m0.z);\n vec2 m10 = unpackMaterialIndex(m1.x);\n vec2 m11 = unpackMaterialIndex(m1.y);\n vec2 m12 = unpackMaterialIndex(m1.z);\n vec2 m20 = unpackMaterialIndex(m2.x);\n vec2 m21 = unpackMaterialIndex(m2.y);\n vec2 m22 = unpackMaterialIndex(m2.z);\n float w00 = m00.y * bary.x;\n float w01 = m01.y * bary.x;\n float w02 = m02.y * bary.x;\n float w10 = m10.y * bary.y;\n float w11 = m11.y * bary.y;\n float w12 = m12.y * bary.y;\n float w20 = m20.y * bary.z;\n float w21 = m21.y * bary.z;\n float w22 = m22.y * bary.z;\n\n float 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 float rand = hashedSample(0u);\n float mIdx;\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 int(mIdx);\n}\n\n\nvec4 fetchIns(int i);\n\n\nmat2x3 anisotropy_axes(vec3 normal, vec3 dp1, vec3 dp2, vec2 duv1, vec2 duv2, vec4 inputTangent) {\n float determinant = duv1.x * duv2.y - duv1.y * duv2.x;\n vec3 t = inputTangent.xyz;\n vec3 b = vec3(0.0);\n // Internal W=2 marks an authored XYZ tangent whose sign is derived from UVs.\n float handedness = abs(inputTangent.w) > 1.5 ? 0.0 : inputTangent.w;\n if (abs(determinant) > 1e-12) {\n vec3 dt = (dp1 * duv2.y - dp2 * duv1.y) / determinant;\n vec3 db = (dp2 * duv1.x - dp1 * duv2.x) / determinant;\n if (dot(t, t) < 1e-8) { t = dt; }\n if (handedness == 0.0) { handedness = (dot(cross(normal, dt), db) < 0.0 ? -1.0 : 1.0); }\n }\n t -= normal * dot(normal, t);\n if (dot(t, t) < 1e-8) {\n float signZ = (normal.z >= 0.0 ? 1.0 : -1.0);\n float factor = -1.0 / (signZ + normal.z);\n float xy = normal.x * normal.y * factor;\n t = vec3(1.0 + signZ * normal.x * normal.x * factor, signZ * xy, -signZ * normal.x);\n b = vec3(xy, signZ + normal.y * normal.y * factor, -normal.y);\n }\n else { t = normalize(t); b = normalize(cross(normal, t)); }\n return mat2x3(t, b * (handedness < 0.0 ? -1.0 : 1.0));\n}\n\nvoid fetchAttributes(inout SurfaceInteraction si, Triangle tri, vec3 barycentric, ivec3 index) {\n si.hit = true;\n si.position = barycentric.x * tri.p0 + barycentric.y * tri.p1 + barycentric.z * tri.p2;\n ivec2 i0 = unpackTexel(index.x, vertexColumns);\n ivec2 i1 = unpackTexel(index.y, vertexColumns);\n ivec2 i2 = unpackTexel(index.z, vertexColumns);\n\n vec3 n0 = texelFetch(normalBuffer, i0, 0).xyz;\n vec3 n1 = texelFetch(normalBuffer, i1, 0).xyz;\n vec3 n2 = texelFetch(normalBuffer, i2, 0).xyz;\n vec3 normal = normalize(barycentric.x * n0 + barycentric.y * n1 + barycentric.z * n2);\n si.vertexNormal = normal;\n\n // Approximate the smooth limit surface at this triangle hit. Direct-light\n // rays use this geometric offset only near the shadow terminator, avoiding\n // false self-shadowing by adjacent facets without moving ordinary hits.\n // Architecture follows Cycles' shadow_ray_smooth_surface_offset.\n vec3 geometricNormal = normalize(cross(tri.p1 - tri.p0, tri.p2 - tri.p0));\n vec3 smoothNormal = barycentric.x * n0 + barycentric.y * n1 + barycentric.z * n2;\n float a = dot(n2 - n0, tri.p0 - tri.p2);\n float b = dot(n2 - n1, tri.p1 - tri.p2);\n float c = dot(n1 - n0, tri.p1 - tri.p0);\n float h = a * barycentric.x * (barycentric.x - 1.0)\n + (a + b + c) * barycentric.x * barycentric.y\n + b * barycentric.y * (barycentric.y - 1.0);\n if (dot(smoothNormal, geometricNormal) > 0.0) {\n float h0 = max(max(dot(tri.p1 - tri.p0, n0), dot(tri.p2 - tri.p0, n0)), 0.0);\n float h1 = max(max(dot(tri.p0 - tri.p1, n1), dot(tri.p2 - tri.p1, n1)), 0.0);\n float 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 float h0 = max(max(dot(tri.p0 - tri.p1, n0), dot(tri.p0 - tri.p2, n0)), 0.0);\n float h1 = max(max(dot(tri.p1 - tri.p0, n1), dot(tri.p1 - tri.p2, n1)), 0.0);\n float 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 // TODO currently isampler doesn't work\n ivec4 m0 = ivec4(texelFetch(materialIndexBuffer, i0, 0));\n ivec4 m1 = ivec4(texelFetch(materialIndexBuffer, i1, 0));\n ivec4 m2 = ivec4(texelFetch(materialIndexBuffer, i2, 0));\n \n if (m0.x > 0 || m1.x > 0 || m2.x > 0) {\n si.materialIndex = randomPickMaterial(m0, m1, m2, barycentric);\n }\n \n vec2 uv0 = texelFetch(uvBuffer, i0, 0).xy;\n vec2 uv1 = texelFetch(uvBuffer, i1, 0).xy;\n vec2 uv2 = texelFetch(uvBuffer, i2, 0).xy;\n #if defined(NUM_DIFFUSE_MAPS) || defined(NUM_NORMAL_MAPS) || defined(NUM_METALLIC_ROUGHNESS_MAPS) || defined(NUM_ROUGHNESS_MAPS) || defined(NUM_METALNESS_MAPS) || defined(NUM_EMISSIVE_MAPS) || defined(NUM_THIN_FILM_MAPS) || defined(NUM_ANISOTROPY_MAPS) || defined(NUM_COAT_NORMAL_MAPS)\n vec2 uv = barycentric.x * uv0 + barycentric.y * uv1 + barycentric.z * uv2;\n vec2 secondaryUv0 = texelFetch(uv1Buffer, i0, 0).xy;\n vec2 secondaryUv1 = texelFetch(uv1Buffer, i1, 0).xy;\n vec2 secondaryUv2 = texelFetch(uv1Buffer, i2, 0).xy;\n vec2 secondaryUv = barycentric.x * secondaryUv0\n + barycentric.y * secondaryUv1\n + barycentric.z * secondaryUv2;\n\n vec4 uvInfo = fetchMatData(si.materialIndex * MAT_DATA_SIZE + MAT_ROW_UV);\n int uvProjection = int(uvInfo.r);\n if (uvProjection > 0) {\n uv = projectUV(uvProjection, si.position, normal, uvInfo.zw);\n secondaryUv = uv;\n } else {\n uv *= uvInfo.zw;\n secondaryUv *= uvInfo.zw;\n }\n #else\n vec2 uv = vec2(0.0);\n vec2 secondaryUv = vec2(0.0);\n #endif\n\n#ifdef NUM_COAT_NORMAL_MAPS\n si.coatNormal = normal;\n float coatNormalIndex = fetchMatData(si.materialIndex * MAT_DATA_SIZE + MAT_ROW_LAYER_MAPS).z;\n#endif\n vec4 directionMaterial = fetchMatData(si.materialIndex * MAT_DATA_SIZE + MAT_ROW_DIRECTION_NORMAL);\n float coatRotation = fetchMatData(si.materialIndex * MAT_DATA_SIZE + MAT_ROW_FILM_TEXTURE).x;\n if (directionMaterial.x >= 0.0 || directionMaterial.y != 0.0 || coatRotation != 0.0\n#ifdef NUM_COAT_NORMAL_MAPS\n || coatNormalIndex >= 0.0\n#endif\n ) {\n vec3 dp1 = tri.p0 - tri.p2;\n vec3 dp2 = tri.p1 - tri.p2;\n vec4 inputTangent = vec4(0.0);\n#ifdef USE_TANGENT\n inputTangent = barycentric.x * texelFetch(tangentBuffer, i0, 0)\n + barycentric.y * texelFetch(tangentBuffer, i1, 0)\n + barycentric.z * texelFetch(tangentBuffer, i2, 0);\n#endif\n mat2x3 geometryAxes = anisotropy_axes(normal, dp1, dp2, uv0-uv2, uv1-uv2, inputTangent);\n si.geometryTangent = geometryAxes[0];\n si.geometryBitangent = geometryAxes[1];\n mat2x3 directionAxes = geometryAxes;\n#ifdef NUM_ANISOTROPY_MAPS\n if (directionMaterial.x >= 0.0 && mapUvChannel[int(directionMaterial.x)] > 0.5) {\n vec2 a = texelFetch(uv1Buffer, i0, 0).xy;\n vec2 b = texelFetch(uv1Buffer, i1, 0).xy;\n vec2 c = texelFetch(uv1Buffer, i2, 0).xy;\n directionAxes = anisotropy_axes(normal, dp1, dp2, a-c, b-c, (inputTangent.w == 0.0 ? vec4(0.0) : inputTangent));\n }\n#endif\n si.directionTangent = directionAxes[0];\n si.directionBitangent = directionAxes[1];\n#ifdef NUM_COAT_NORMAL_MAPS\n if (coatNormalIndex >= 0.0) {\n int coatIndex = int(coatNormalIndex);\n mat2x3 coatAxes = geometryAxes;\n if (mapUvChannel[coatIndex] > 0.5) {\n vec2 a = texelFetch(uv1Buffer, i0, 0).xy;\n vec2 b = texelFetch(uv1Buffer, i1, 0).xy;\n vec2 c = texelFetch(uv1Buffer, i2, 0).xy;\n coatAxes = anisotropy_axes(normal, dp1, dp2, a-c, b-c,\n inputTangent.w == 0.0 ? vec4(0.0) : inputTangent);\n }\n vec3 decoded = sampleMap(coatIndex, getMapUvSet(coatIndex, uv, secondaryUv)).rgb * 2.0 - 1.0;\n vec3 mapped = coatAxes[0] * decoded.x * directionMaterial.z\n + coatAxes[1] * decoded.y * directionMaterial.w + normal * decoded.z;\n if (dot(mapped, mapped) > 1e-12) { si.coatNormal = normalize(mapped); }\n }\n#endif\n }\n\n si.uv = uv;\n si.uv1 = secondaryUv;\n\n #ifdef USE_TANGENT\n vec3 t0 = texelFetch(tangentBuffer, i0, 0).xyz;\n vec3 t1 = texelFetch(tangentBuffer, i1, 0).xyz;\n vec3 t2 = texelFetch(tangentBuffer, i2, 0).xyz;\n si.tangent = normalize(barycentric.x * t0 + barycentric.y * t1 + barycentric.z * t2);\n #endif\n #if defined(NUM_NORMAL_MAPS)\n vec3 dp1 = tri.p0 - tri.p2;\n vec3 dp2 = tri.p1 - tri.p2;\n vec2 duv1 = uv0 - uv2;\n vec2 duv2 = uv1 - uv2;\n si.normal = getMatNormal(\n si.materialIndex, uv, normal, dp1, dp2, duv1, duv2\n );\n #else\n si.normal = normal;\n #endif\n}\n\nstruct TriangleIntersect {\n float t;\n vec3 barycentric;\n};\n\nvec4 fetchBVH(int i) {\n return fetchData(bvhBuffer, i, bvhColumns);\n}\n\nvec4 fetchIns(int i) {\n return fetchData(instancesBuffer, i, instancesColumns);\n}\n\n// GLSL ES 3.00 has no bitCount intrinsic. Count the CWBVH mask bits using\n// integer operations supported by WebGL 2.\nuint countBvhBits(uint value) {\n value -= (value >> 1u) & 0x55555555u;\n value = (value & 0x33333333u) + ((value >> 2u) & 0x33333333u);\n value = (value + (value >> 4u)) & 0x0f0f0f0fu;\n return (value * 0x01010101u) >> 24u;\n}\n\nvec4 unpackBvh4Bounds(uint packedBounds) {\n return vec4(\n float(packedBounds & 0xffu),\n float((packedBounds >> 8u) & 0xffu),\n float((packedBounds >> 16u) & 0xffu),\n float(packedBounds >> 24u)\n ) / 255.0;\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\nTriangleIntersect intersectTriangle(vec3 o, vec3 d, Triangle tri, int maxDim, vec3 shear, float tMax) {\n TriangleIntersect ti;\n\n // translate vertices based on ray origin\n vec3 p0t = tri.p0 - o;\n vec3 p1t = tri.p1 - o;\n vec3 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 p0t.xy += shear.xy * p0t.z;\n p1t.xy += shear.xy * p1t.z;\n p2t.xy += shear.xy * p2t.z;\n\n // compute edge function coefficients\n vec3 e = vec3(\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#ifdef DEPTH_QUERY_MODE\n vec3 edgeScale = abs(vec3(p1t.x * p2t.y, p2t.x * p0t.y, p0t.x * p1t.y))\n + abs(vec3(p1t.y * p2t.x, p2t.y * p0t.x, p0t.y * p1t.x));\n e = mix(e, vec3(0.0), lessThanEqual(abs(e), edgeScale * 1e-6));\n#endif\n\n // check if intersection is inside triangle\n if (any(lessThan(e, vec3(0))) && any(greaterThan(e, vec3(0)))) {\n return ti;\n }\n\n float det = e.x + e.y + e.z;\n#ifdef DEPTH_QUERY_MODE\n if (det == 0.0) return ti;\n#endif\n\n p0t.z *= shear.z;\n p1t.z *= shear.z;\n p2t.z *= shear.z;\n float 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 float invDet = 1. / det;\n ti.t = tScaled * invDet;\n ti.barycentric = e * invDet;\n\n return ti;\n}\n\nstruct Box {\n vec3 min;\n vec3 max;\n};\n\n// Conservative slabs only for the unjittered depth query shader.\nvec3 traversalInverse(vec3 d) {\n#ifdef DEPTH_QUERY_MODE\n vec3 denominator = mix(d, mix(vec3(1e-20), vec3(-1e-20), lessThan(d, vec3(0.0))), lessThan(abs(d), vec3(1e-20)));\n return 1.0 / denominator;\n#else\n return 1.0 / d;\n#endif\n}\n\nvec3 depthSlabPadding(vec3 invD, vec3 o) {\n#ifdef DEPTH_QUERY_MODE\n return abs(invD) * max(vec3(1.0), abs(o)) * 1e-6;\n#else\n return vec3(0.0);\n#endif\n}\n\n// Branchless ray/box intersection\n// https://tavianator.com/fast-branchless-raybounding-box-intersections/\nfloat intersectBox(vec3 o, vec3 d, vec3 bmin, vec3 bmax, float tMax) {\n#ifdef DEPTH_QUERY_MODE\n vec3 invD = traversalInverse(d);\n vec3 tBot = (bmin - o) * invD;\n vec3 tTop = (bmax - o) * invD;\n vec3 padding = depthSlabPadding(invD, o);\n#else\n vec3 tBot = (bmin - o) / d;\n vec3 tTop = (bmax - o) / d;\n vec3 padding = vec3(0.0);\n#endif\n vec3 tNear = min(tBot, tTop) - padding;\n vec3 tFar = max(tBot, tTop) + padding;\n float t0 = max(tNear.x, max(tNear.y, tNear.z));\n float t1 = min(tFar.x, min(tFar.y, tFar.z));\n\n return (t0 > t1 || t0 > tMax) ? -1.0 : (t0 > 0.0 ? t0 : t1);\n}\n\nint maxDimension(vec3 v) {\n return v.x > v.y ? (v.x > v.z ? 0 : 2) : (v.y > v.z ? 1 : 2);\n}\n\nvec3 getShear(vec3 d, int maxDim) {\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 vec3(-d.y, -d.z, 1.0) / d.x;\n } else if (maxDim == 1) {\n return vec3(-d.z, -d.x, 1.0) / d.y;\n } else {\n return vec3(-d.x, -d.y, 1.0) / d.z;\n }\n}\n\n// Traverse BVH, find closest triangle intersection, and return surface information\nvoid intersectScene(in Ray ray, inout SurfaceInteraction si, bool doubleSided) {\n si.hit = false;\n\n if (isEmptyScene) {\n return;\n }\n\n int maxDim = maxDimension(abs(ray.d));\n vec3 shear = getShear(ray.d, maxDim);\n int hitInsIdx;\n\n${getBVHTraverseCode(glsl`\n si.triangleIndex = triangleIndex;\n hitTri = tri;\n hitFaceNormal = faceNormal;\n hitBarycentric = hit.barycentric;\n hitIndex = index;\n isHit = true;\n hitInsIdx = insIdx;\n`)}\n\n if (isHit) {\n si.instanceIndex = hitInsIdx;\n si.materialIndex = int(unpackMaterialIndex(floatBitsToInt(fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE}).z)).x);\n // fetchAttributes before transform to world space.\n fetchAttributes(si, hitTri, hitBarycentric, hitIndex);\n mat4 l2w = mat4(\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 5),\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 6),\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 7),\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 8)\n );\n mat4 w2l = mat4(\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 1),\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 2),\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 3),\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 4)\n );\n mat3 normalTransform = transpose(mat3(w2l));\n si.faceNormal = normalize(normalTransform * hitFaceNormal);\n si.vertexNormal = normalize(normalTransform * si.vertexNormal);\n si.normal = normalize(normalTransform * si.normal);\n#ifdef NUM_COAT_NORMAL_MAPS\n si.coatNormal = normalize(normalTransform * si.coatNormal);\n#endif\n#ifdef USE_TANGENT\n if (dot(si.tangent, si.tangent) > 0.0) {\n si.tangent = normalize(mat3(l2w) * si.tangent);\n }\n#endif\n si.shadowOffset = (l2w * vec4(si.shadowOffset, 0.0)).xyz;\n si.directionTangent = mat3(l2w) * si.directionTangent;\n si.directionBitangent = mat3(l2w) * si.directionBitangent;\n si.geometryTangent = mat3(l2w) * si.geometryTangent;\n si.geometryBitangent = mat3(l2w) * si.geometryBitangent;\n si.position = (l2w * vec4(si.position, 1.0)).xyz;\n }\n}\n\n// Local intersection helper for shader bevel.\nvoid intersectSceneBevel(in Ray ray, inout SurfaceInteraction si, int instanceIndex, float maxDistance) {\n si.hit = false;\n\n if (isEmptyScene) {\n return;\n }\n\n bool doubleSided = true;\n int maxDim = maxDimension(abs(ray.d));\n vec3 shear = getShear(ray.d, maxDim);\n int hitInsIdx;\n\n${getBVHTraverseCode(\n glsl`\n si.triangleIndex = triangleIndex;\n hitTri = tri;\n hitFaceNormal = faceNormal;\n hitBarycentric = hit.barycentric;\n hitIndex = index;\n isHit = true;\n hitInsIdx = insIdx;\n`,\n glsl`\n if (insIdx != instanceIndex) {\n continue;\n }\n`,\n 'maxDistance'\n)}\n\n if (isHit) {\n si.instanceIndex = hitInsIdx;\n si.materialIndex = int(unpackMaterialIndex(floatBitsToInt(fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE}).z)).x);\n fetchAttributes(si, hitTri, hitBarycentric, hitIndex);\n mat4 l2w = mat4(\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 5),\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 6),\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 7),\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 8)\n );\n mat4 w2l = mat4(\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 1),\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 2),\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 3),\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 4)\n );\n mat3 normalTransform = transpose(mat3(w2l));\n si.faceNormal = normalize(normalTransform * hitFaceNormal);\n si.vertexNormal = normalize(normalTransform * si.vertexNormal);\n si.normal = normalize(normalTransform * si.normal);\n#ifdef NUM_COAT_NORMAL_MAPS\n si.coatNormal = normalize(normalTransform * si.coatNormal);\n#endif\n#ifdef USE_TANGENT\n if (dot(si.tangent, si.tangent) > 0.0) {\n si.tangent = normalize(mat3(l2w) * si.tangent);\n }\n#endif\n si.shadowOffset = (l2w * vec4(si.shadowOffset, 0.0)).xyz;\n si.directionTangent = mat3(l2w) * si.directionTangent;\n si.directionBitangent = mat3(l2w) * si.directionBitangent;\n si.geometryTangent = mat3(l2w) * si.geometryTangent;\n si.geometryBitangent = mat3(l2w) * si.geometryBitangent;\n si.position = (l2w * vec4(si.position, 1.0)).xyz;\n }\n}\n\nbool intersectSceneShadow(in Ray ray, float lightDistance, int currTriangleIndex) {\n\n if (isEmptyScene) {\n return false;\n }\n\n bool doubleSided = true;\n int maxDim = maxDimension(abs(ray.d));\n vec3 shear = getShear(ray.d, maxDim);\n\n${getBVHTraverseCode(\n glsl`\n // Traversal is clipped to lightDistance. Affine instance transforms keep\n // the ray parameter unchanged because local directions are not normalized.\n bool isOccluded = true;\n vec3 barycentric = hit.barycentric;\n vec3 position = barycentric.x * tri.p0 + barycentric.y * tri.p1 + barycentric.z * tri.p2;\n\n if (isOccluded) {\n#ifdef HAS_ALPHA\n int materialIndex = int(unpackMaterialIndex(floatBitsToInt(fetchIns(insIdx * ${INSTANCE_VEC4_STRIDE}).z)).x);\n ivec2 i0 = unpackTexel(index.x, vertexColumns);\n ivec2 i1 = unpackTexel(index.y, vertexColumns);\n ivec2 i2 = unpackTexel(index.z, vertexColumns);\n ivec4 m0 = ivec4(texelFetch(materialIndexBuffer, i0, 0));\n ivec4 m1 = ivec4(texelFetch(materialIndexBuffer, i1, 0));\n ivec4 m2 = ivec4(texelFetch(materialIndexBuffer, i2, 0));\n \n if (m0.x > 0 || m1.x > 0 || m2.x > 0) {\n materialIndex = randomPickMaterial(m0, m1, m2, barycentric);\n }\n \n int ii = materialIndex * MAT_DATA_SIZE;\n int mapAlphaUsage = int(fetchMatData(ii + MAT_ROW_MISC).a);\n float alpha = fetchMatData(ii + MAT_ROW_ALPHA_FILM).r;\n #ifdef NUM_DIFFUSE_MAPS\n if (mapAlphaUsage == ${mapAlphaUsageConstants.alpha + ''}) {\n int diffuseMapIndex = int(fetchMatData(ii + MAT_ROW_METALLIC_MAPS).z);\n if (diffuseMapIndex >= 0) {\n vec2 uv0 = texelFetch(uvBuffer, i0, 0).xy;\n vec2 uv1 = texelFetch(uvBuffer, i1, 0).xy;\n vec2 uv2 = texelFetch(uvBuffer, i2, 0).xy;\n vec2 uv = barycentric.x * uv0 + barycentric.y * uv1 + barycentric.z * uv2;\n vec4 uvInfo = fetchMatData(ii + MAT_ROW_UV);\n int uvProjection = int(uvInfo.r);\n if (uvProjection > 0) {\n uv = projectUV(uvProjection, position, hitFaceNormal, uvInfo.zw);\n } else {\n uv *= uvInfo.zw;\n }\n \n alpha *= sampleMap(diffuseMapIndex, uv).a;\n }\n }\n #endif\n #ifdef NUM_ALPHA_MAPS\n int alphaMapIndex = int(fetchMatData(ii + MAT_ROW_STANDALONE_MAPS).z);\n if (alphaMapIndex >= 0) {\n vec2 uv0 = texelFetch(uvBuffer, i0, 0).xy;\n vec2 uv1 = texelFetch(uvBuffer, i1, 0).xy;\n vec2 uv2 = texelFetch(uvBuffer, i2, 0).xy;\n vec2 uv = barycentric.x * uv0 + barycentric.y * uv1 + barycentric.z * uv2;\n vec4 uvInfo = fetchMatData(ii + MAT_ROW_UV);\n int uvProjection = int(uvInfo.r);\n if (uvProjection > 0) {\n uv = projectUV(uvProjection, position, hitFaceNormal, uvInfo.zw);\n } else {\n uv *= uvInfo.zw;\n }\n alpha *= sampleMap(alphaMapIndex, uv).g;\n }\n #endif\n float alphaCutoff = fetchMatData(ii + MAT_ROW_STANDALONE_MAPS).a;\n alpha = resolveMaterialAlpha(\n alpha, int(fetchMatData(ii + MAT_ROW_ALPHA).w), alphaCutoff\n );\n if (hashedSample(0u) > alpha) {\n // Continue after a transparent hit without traversing beyond the light.\n tMax = min(max(lightDistance, 0.0), RAY_MAX_DISTANCE);\n isOccluded = false;\n }\n#endif\n }\n if (isOccluded) {\n return true;\n }\n`,\n glsl`\n if (triangleIndex == currTriangleIndex) {\n continue;\n }\n`,\n 'min(max(lightDistance, 0.0), RAY_MAX_DISTANCE)'\n)}\n return false;\n}\n`\n});\n"]}
1
+ {"version":3,"file":"intersect.glsl.js","sourceRoot":"","sources":["../../../../src/renderer/glsl/chunks/intersect.glsl.ts"],"names":[],"mappings":"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","sourcesContent":["import { mapAlphaUsageConstants } from '../../materialConstants.js';\n// 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 { INSTANCE_VEC4_STRIDE } from '../../bvhAccel.js';\nimport { createShaderMixin, glsl, Shader } from '../../shader.js';\n\nfunction getBVHTraverseCode(\n handleHitCode: string,\n skipTriangleCode = '',\n initialTMax = 'RAY_MAX_DISTANCE'\n) {\n const intersectTriangles = glsl`\n ivec3 vertexOffset = ivec3(floatBitsToInt(fetchIns(insIdx * ${INSTANCE_VEC4_STRIDE}).y));\n for (int k = 0; k < primitiveCount; k++) {\n int triangleIndex = primitiveOffset + k;\n ${skipTriangleCode}\n vec4 r3 = fetchBVH(triangleIndex);\n ivec3 index = floatBitsToInt(r3.xyz) + vertexOffset;\n Triangle tri = Triangle(\n fetchData(positionBuffer, index.x, vertexColumns).xyz,\n fetchData(positionBuffer, index.y, vertexColumns).xyz,\n fetchData(positionBuffer, index.z, vertexColumns).xyz\n );\n TriangleIntersect hit = intersectTriangle(o, d, tri, maxDim, shear, tMax);\n\n if (hit.t > 0.0) {\n tMax = hit.t;\n vec3 e1 = tri.p1 - tri.p0;\n vec3 e2 = tri.p2 - tri.p0;\n vec3 faceNormal = normalize(cross(e1, e2));\n int sideMaterialIndex = int(unpackMaterialIndex(\n floatBitsToInt(fetchIns(insIdx * ${INSTANCE_VEC4_STRIDE}).z)\n ).x);\n bool materialDoubleSided = fetchMatData(\n sideMaterialIndex * MAT_DATA_SIZE + MAT_ROW_ALPHA\n ).z > 0.5;\n if (!doubleSided && !materialDoubleSided && dot(faceNormal, d) > 0.0) {\n continue;\n }\n\n ${handleHitCode}\n }\n }\n`;\n return glsl`\n ivec2 stack[STACK_SIZE];\n int top = -1;\n int prevInsIdx = -1;\n float tMax = ${initialTMax};\n\n vec3 o = ray.o;\n vec3 d = ray.d;\n\n bool isHit = false;\n vec3 hitFaceNormal;\n vec3 hitBarycentric;\n ivec3 hitIndex;\n\n Triangle hitTri;\n\n // TODO not sure why. but do root node check here will cause rendering wrong in windows.\n top = 0;\n stack[0] = ivec2(0, -1);\n\n int iter = 0;\n while(top >= 0 && iter < 5000) {\n iter++;\n ivec2 node = stack[top--];\n\n int insIdx = node.y;\n\n if (insIdx != prevInsIdx) {\n if (insIdx < 0) {\n o = ray.o;\n d = ray.d;\n }\n else {\n mat4 w2l = mat4(\n fetchIns(insIdx * ${INSTANCE_VEC4_STRIDE} + 1),\n fetchIns(insIdx * ${INSTANCE_VEC4_STRIDE} + 2),\n fetchIns(insIdx * ${INSTANCE_VEC4_STRIDE} + 3),\n fetchIns(insIdx * ${INSTANCE_VEC4_STRIDE} + 4)\n );\n o = (w2l * vec4(ray.o, 1.0)).xyz;\n d = (w2l * vec4(ray.d, 0.0)).xyz;\n }\n maxDim = maxDimension(abs(d));\n shear = getShear(d, maxDim);\n prevInsIdx = insIdx;\n }\n\n int bufferOff = insIdx < 0 ? 0 : blasOffset;\n uint encodedBlasOffset = insIdx < 0 ? 0u : floatBitsToUint(fetchIns(insIdx * ${INSTANCE_VEC4_STRIDE}).x);\n int blasLayout = int(encodedBlasOffset >> 30u);\n int blasInsOffset = int(encodedBlasOffset & 0x3fffffffu);\n int primitiveCount = 0;\n int primitiveOffset = 0;\n\n if (blasLayout == 3) {\n uint nodeInfo = uint(node.x);\n if ((nodeInfo & 0x80000000u) != 0u) {\n primitiveCount = int((nodeInfo >> 23u) & 0xffu);\n primitiveOffset = bufferOff + blasInsOffset + int(nodeInfo & 0x007fffffu);\n }\n else {\n int i = node.x + bufferOff;\n vec4 compressed0 = fetchBVH(i);\n vec4 compressed1 = fetchBVH(i + 1);\n vec4 compressed2 = fetchBVH(i + 2);\n vec4 compressed3 = fetchBVH(i + 3);\n vec4 compressed4 = fetchBVH(i + 4);\n uint header = floatBitsToUint(compressed0.w);\n int exponentX = int(header << 24u) >> 24;\n int exponentY = int((header >> 8u) << 24u) >> 24;\n int exponentZ = int((header >> 16u) << 24u) >> 24;\n vec3 quantizationScale = vec3(\n uintBitsToFloat(uint(exponentX + 127) << 23u),\n uintBitsToFloat(uint(exponentY + 127) << 23u),\n uintBitsToFloat(uint(exponentZ + 127) << 23u)\n );\n uint childBase = floatBitsToUint(compressed1.x);\n uint triangleBase = floatBitsToUint(compressed1.y);\n uint internalMask = header >> 24u;\n uint octant = (d.x < 0.0 ? 4u : 0u) |\n (d.y < 0.0 ? 2u : 0u) |\n (d.z < 0.0 ? 1u : 0u);\n uint quantizedWords[12] = uint[12](\n floatBitsToUint(compressed2.x), floatBitsToUint(compressed2.y),\n floatBitsToUint(compressed2.z), floatBitsToUint(compressed2.w),\n floatBitsToUint(compressed3.x), floatBitsToUint(compressed3.y),\n floatBitsToUint(compressed3.z), floatBitsToUint(compressed3.w),\n floatBitsToUint(compressed4.x), floatBitsToUint(compressed4.y),\n floatBitsToUint(compressed4.z), floatBitsToUint(compressed4.w)\n );\n\n for (int order = 7; order >= 0; order--) {\n uint slot = uint(order) ^ octant;\n uint metaWord = slot >= 4u\n ? floatBitsToUint(compressed1.w)\n : floatBitsToUint(compressed1.z);\n uint meta = (metaWord >> ((slot & 3u) * 8u)) & 0xffu;\n if (meta == 0u) continue;\n uint byteShift = (slot & 3u) * 8u;\n uint wordOffset = slot >> 2u;\n vec3 quantizedMin = vec3(\n float((quantizedWords[wordOffset] >> byteShift) & 0xffu),\n float((quantizedWords[2u + wordOffset] >> byteShift) & 0xffu),\n float((quantizedWords[4u + wordOffset] >> byteShift) & 0xffu)\n );\n vec3 quantizedMax = vec3(\n float((quantizedWords[6u + wordOffset] >> byteShift) & 0xffu),\n float((quantizedWords[8u + wordOffset] >> byteShift) & 0xffu),\n float((quantizedWords[10u + wordOffset] >> byteShift) & 0xffu)\n );\n vec3 childMin = compressed0.xyz + quantizedMin * quantizationScale;\n vec3 childMax = compressed0.xyz + quantizedMax * quantizationScale;\n if (intersectBox(o, d, childMin, childMax, tMax) <= 0.0) continue;\n uint localOffset = meta & 0x1fu;\n if (localOffset >= 24u) {\n uint precedingMask = internalMask & ((1u << slot) - 1u);\n uint internalIndex = childBase + countBvhBits(precedingMask);\n stack[++top] = ivec2(\n blasInsOffset + int(internalIndex * 5u), insIdx\n );\n }\n else {\n uint triangleCount = countBvhBits(meta >> 5u);\n uint primitiveAddress = triangleBase + localOffset;\n uint leafInfo = 0x80000000u |\n (triangleCount << 23u) |\n (primitiveAddress & 0x007fffffu);\n stack[++top] = ivec2(int(leafInfo), insIdx);\n }\n }\n }\n }\n else if (blasLayout == 2) {\n int i = node.x + bufferOff;\n vec4 gpu0 = fetchBVH(i);\n vec4 gpu1 = fetchBVH(i + 1);\n vec4 gpu2 = fetchBVH(i + 2);\n vec4 gpu3 = fetchBVH(i + 3);\n primitiveCount = int(floatBitsToUint(gpu2.w));\n if (primitiveCount > 0) {\n primitiveOffset = bufferOff + blasInsOffset + int(floatBitsToUint(gpu3.w));\n }\n else {\n int left = blasInsOffset + int(floatBitsToUint(gpu0.w) * 4u);\n int right = blasInsOffset + int(floatBitsToUint(gpu1.w) * 4u);\n float d0 = intersectBox(o, d, gpu0.xyz, gpu1.xyz, tMax);\n float d1 = intersectBox(o, d, gpu2.xyz, gpu3.xyz, tMax);\n if (d0 > d1) {\n if (d0 > 0.0) stack[++top] = ivec2(left, insIdx);\n if (d1 > 0.0) stack[++top] = ivec2(right, insIdx);\n }\n else {\n if (d1 > 0.0) stack[++top] = ivec2(right, insIdx);\n if (d0 > 0.0) stack[++top] = ivec2(left, insIdx);\n }\n }\n }\n else if (blasLayout == 1) {\n uint nodeInfo = uint(node.x);\n if ((nodeInfo & 0x80000000u) != 0u) {\n primitiveCount = int((nodeInfo >> 23u) & 0xffu);\n primitiveOffset = bufferOff + blasInsOffset + int(nodeInfo & 0x007fffffu);\n }\n else {\n int i = node.x + bufferOff;\n vec4 wide0 = fetchBVH(i);\n vec4 wide1 = fetchBVH(i + 1);\n vec4 wide2 = fetchBVH(i + 2);\n vec4 wide3 = fetchBVH(i + 3);\n vec3 nodeMin = wide0.xyz;\n vec3 nodeExtent = vec3(wide0.w, wide1.x, wide1.y);\n uint childInfos[4] = uint[4](\n floatBitsToUint(wide1.z), floatBitsToUint(wide1.w),\n floatBitsToUint(wide2.x), floatBitsToUint(wide2.y)\n );\n vec4 childMinX = unpackBvh4Bounds(floatBitsToUint(wide2.z));\n vec4 childMinY = unpackBvh4Bounds(floatBitsToUint(wide2.w));\n vec4 childMinZ = unpackBvh4Bounds(floatBitsToUint(wide3.x));\n vec4 childMaxX = unpackBvh4Bounds(floatBitsToUint(wide3.y));\n vec4 childMaxY = unpackBvh4Bounds(floatBitsToUint(wide3.z));\n vec4 childMaxZ = unpackBvh4Bounds(floatBitsToUint(wide3.w));\n vec3 invD = traversalInverse(d);\n vec3 padding = depthSlabPadding(invD, o);\n vec4 tx0 = (nodeMin.x + childMinX * nodeExtent.x - o.x) * invD.x;\n vec4 tx1 = (nodeMin.x + childMaxX * nodeExtent.x - o.x) * invD.x;\n vec4 ty0 = (nodeMin.y + childMinY * nodeExtent.y - o.y) * invD.y;\n vec4 ty1 = (nodeMin.y + childMaxY * nodeExtent.y - o.y) * invD.y;\n vec4 tz0 = (nodeMin.z + childMinZ * nodeExtent.z - o.z) * invD.z;\n vec4 tz1 = (nodeMin.z + childMaxZ * nodeExtent.z - o.z) * invD.z;\n vec4 tNear = max(max((min(tx0, tx1) - vec4(padding.x)), (min(ty0, ty1) - vec4(padding.y))), (min(tz0, tz1) - vec4(padding.z)));\n vec4 tFar = min(min((max(tx0, tx1) + vec4(padding.x)), (max(ty0, ty1) + vec4(padding.y))), (max(tz0, tz1) + vec4(padding.z)));\n vec4 distance = mix(tFar, tNear, greaterThan(tNear, vec4(0.0)));\n distance = mix(distance, vec4(-1.0), greaterThan(tNear, min(tFar, vec4(tMax))));\n float childDistances[4] = float[4](distance.x, distance.y, distance.z, distance.w);\n\n // Push far-to-near so that the closest child is popped first.\n for (int order = 0; order < 4; order++) {\n float farthestDistance = -1.0;\n int farthestChild = -1;\n for (int child = 0; child < 4; child++) {\n if (childInfos[child] != 0u && childDistances[child] > farthestDistance) {\n farthestDistance = childDistances[child];\n farthestChild = child;\n }\n }\n if (farthestChild >= 0 && farthestDistance > 0.0) {\n uint childInfo = childInfos[farthestChild];\n int childAddress = int(childInfo);\n if ((childInfo & 0x80000000u) == 0u) {\n childAddress += blasInsOffset;\n }\n stack[++top] = ivec2(childAddress, insIdx);\n childDistances[farthestChild] = -1.0;\n }\n }\n }\n }\n else {\n int i = node.x + bufferOff;\n vec4 r1 = fetchBVH(i);\n int pointerToRightOrNumPrims = floatBitsToInt(r1.w);\n\n if (pointerToRightOrNumPrims >= 0) {\n int left = node.x + 2;\n int right = pointerToRightOrNumPrims + blasInsOffset;\n\n float d0 = intersectBox(o, d, fetchBVH(left + bufferOff).xyz, fetchBVH(left + 1 + bufferOff).xyz, tMax);\n float d1 = intersectBox(o, d, fetchBVH(right + bufferOff).xyz, fetchBVH(right + 1 + bufferOff).xyz, tMax);\n if (d0 > d1) {\n if (d0 > 0.0) {\n stack[++top] = ivec2(left, insIdx);\n }\n if (d1 > 0.0) {\n stack[++top] = ivec2(right, insIdx);\n }\n }\n else {\n if (d1 > 0.0) {\n stack[++top] = ivec2(right, insIdx);\n }\n if (d0 > 0.0) {\n stack[++top] = ivec2(left, insIdx);\n }\n }\n }\n else {\n if (insIdx < 0) {\n for (int k = 0; k < -pointerToRightOrNumPrims; k++) {\n vec4 r3 = fetchBVH(i + 2 + k);\n stack[++top] = ivec2(floatBitsToInt(r3.y), floatBitsToInt(r3.x));\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\nexport default createShaderMixin({\n uniforms: {\n positionBuffer: Shader.uniform('sampler2D'),\n normalBuffer: Shader.uniform('sampler2D'),\n tangentBuffer: Shader.uniform('sampler2D'),\n uvBuffer: Shader.uniform('sampler2D'),\n uv1Buffer: Shader.uniform('sampler2D'),\n colorBuffer: Shader.uniform('sampler2D'),\n materialIndexBuffer: Shader.uniform('sampler2D'),\n\n bvhBuffer: Shader.uniform('sampler2D'),\n instancesBuffer: Shader.uniform('sampler2D'),\n\n bvhColumns: Shader.uniform('int', 1),\n vertexColumns: Shader.uniform('int', 1),\n\n instancesColumns: Shader.uniform('int', 1),\n\n blasOffset: Shader.uniform('int'),\n\n isEmptyScene: Shader.uniform('bool')\n },\n main: glsl`\n\nstruct Triangle {\n vec3 p0;\n vec3 p1;\n vec3 p2;\n};\n#ifdef NUM_LIGHTS\nfloat intersectSphere(float rad, vec3 pos, Ray r) {\n vec3 op = pos - r.o;\n float eps = 0.001;\n float b = dot(op, r.d);\n float det = b * b - dot(op, op) + rad * rad;\n if (det < 0.0)\n return INF;\n\n det = sqrt(det);\n float t1 = b - det;\n if (t1 > eps)\n return t1;\n\n float t2 = b + det;\n if (t2 > eps)\n return t2;\n\n return INF;\n}\n\nfloat intersectRect(vec3 pos, vec3 u, vec3 v, vec3 n, Ray r) {\n float dt = dot(r.d, n);\n float t = (dot(n, pos) - dot(n, r.o)) / dt;\n\n if (t > EPS) {\n vec3 p = r.o + r.d * t;\n vec3 vi = p - pos;\n float a1 = dot(u / dot(u, u), vi);\n if (a1 >= 0.0 && a1 <= 1.0) {\n float a2 = 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\nfloat intersectLight(Ray r, Light light, out LightSampleRec lightSample) {\n // Directional lights are delta sources without finite geometry. Start at\n // INF so they cannot accidentally terminate a BSDF continuation as a\n // geometric light hit; rect and sphere branches overwrite this value.\n float d = INF;\n vec3 u = light.u;\n vec3 v = light.v;\n int type = int(light.type);\n\n#ifdef HAS_RECT_LIGHTS\n if (type == RECT_LIGHT) {\n // Adjust the position from center to start corner\n vec3 pos = light.position-u*0.5-v*0.5;\n vec3 normal = normalize(cross(u, v));\n if (dot(normal, r.d) > 0.) // Hide backfacing quad light\n return INF;\n\n d = intersectRect(pos, u, v, normal, r);\n if (d < 0.) {\n d = INF;\n }\n else {\n float cosTheta = dot(-r.d, normal);\n float spread = light.spread;\n if (cosTheta >= cos(spread * PI / 2.0)) {\n lightSample.pdf = (d * d) / (light.area * cosTheta);\n lightSample.emission = light.emission;\n }\n }\n }\n#endif\n#ifdef HAS_SPHERE_LIGHTS\n if (type == SPHERE_LIGHT) {\n if (light.radius <= 0.0) {\n vec3 toPoint = light.position - r.o;\n float projection = dot(toPoint, r.d);\n vec3 perpendicular = toPoint - projection * r.d;\n if (projection <= EPS || dot(perpendicular, perpendicular) > 1e-8) return INF;\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 } else {\n d = intersectSphere(light.radius, light.position, r);\n lightSample.isDelta = false;\n }\n if (d < 0.) {\n d = INF;\n }\n else {\n float centerDistance = distance(r.o, light.position);\n float cosThetaMax = centerDistance <= light.radius\n ? -1.0\n : sqrt(max(0.0, 1.0 - light.radius * light.radius / (centerDistance * centerDistance)));\n if (light.radius > 0.0) {\n lightSample.pdf = centerDistance <= light.radius\n ? INV4PI\n : 1.0 / max(TWOPI * (1.0 - cosThetaMax), 1e-8);\n }\n lightSample.emission = light.radius <= 0.0\n ? light.emission / max(d * d, 1e-8)\n : light.emission;\n }\n }\n#endif\n#ifdef HAS_SPOT_LIGHTS\n if (type == SPOT_LIGHT) {\n if (light.radius <= 0.0) {\n vec3 toPoint = light.position - r.o;\n float projection = dot(toPoint, r.d);\n vec3 perpendicular = toPoint - projection * r.d;\n if (projection <= EPS || dot(perpendicular, perpendicular) > 1e-8) return INF;\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 } else {\n d = intersectSphere(light.radius, light.position, r);\n lightSample.isDelta = false;\n }\n if (d != INF) {\n vec3 hitPt = r.o + d * r.d;\n float multiplier = evaluateSpotEmissionMultiplier(light, normalize(r.o - hitPt));\n if (multiplier <= 0.0) return INF;\n float centerDistance = distance(r.o, light.position);\n float cosThetaMax = centerDistance <= light.radius\n ? -1.0\n : sqrt(max(0.0, 1.0 - light.radius * light.radius / (centerDistance * centerDistance)));\n if (light.radius > 0.0) {\n lightSample.pdf = centerDistance <= light.radius\n ? INV4PI\n : 1.0 / max(TWOPI * (1.0 - cosThetaMax), 1e-8);\n }\n lightSample.emission = light.emission * multiplier * (light.radius <= 0.0\n ? 1.0 / max(d * d, 1e-8)\n : 1.0);\n }\n }\n#endif\n\n return d;\n}\n#endif\n\nint randomPickMaterial(ivec4 m0, ivec4 m1, ivec4 m2, vec3 bary) {\n vec2 m00 = unpackMaterialIndex(m0.x);\n vec2 m01 = unpackMaterialIndex(m0.y);\n vec2 m02 = unpackMaterialIndex(m0.z);\n vec2 m10 = unpackMaterialIndex(m1.x);\n vec2 m11 = unpackMaterialIndex(m1.y);\n vec2 m12 = unpackMaterialIndex(m1.z);\n vec2 m20 = unpackMaterialIndex(m2.x);\n vec2 m21 = unpackMaterialIndex(m2.y);\n vec2 m22 = unpackMaterialIndex(m2.z);\n float w00 = m00.y * bary.x;\n float w01 = m01.y * bary.x;\n float w02 = m02.y * bary.x;\n float w10 = m10.y * bary.y;\n float w11 = m11.y * bary.y;\n float w12 = m12.y * bary.y;\n float w20 = m20.y * bary.z;\n float w21 = m21.y * bary.z;\n float w22 = m22.y * bary.z;\n\n float 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 float rand = hashedSample(0u);\n float mIdx;\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 int(mIdx);\n}\n\n\nvec4 fetchIns(int i);\n\n\nmat2x3 anisotropy_axes(vec3 normal, vec3 dp1, vec3 dp2, vec2 duv1, vec2 duv2, vec4 inputTangent) {\n float determinant = duv1.x * duv2.y - duv1.y * duv2.x;\n vec3 t = inputTangent.xyz;\n vec3 b = vec3(0.0);\n // Internal W=2 marks an authored XYZ tangent whose sign is derived from UVs.\n float handedness = abs(inputTangent.w) > 1.5 ? 0.0 : inputTangent.w;\n if (abs(determinant) > 1e-12) {\n vec3 dt = (dp1 * duv2.y - dp2 * duv1.y) / determinant;\n vec3 db = (dp2 * duv1.x - dp1 * duv2.x) / determinant;\n if (dot(t, t) < 1e-8) { t = dt; }\n if (handedness == 0.0) { handedness = (dot(cross(normal, dt), db) < 0.0 ? -1.0 : 1.0); }\n }\n t -= normal * dot(normal, t);\n if (dot(t, t) < 1e-8) {\n float signZ = (normal.z >= 0.0 ? 1.0 : -1.0);\n float factor = -1.0 / (signZ + normal.z);\n float xy = normal.x * normal.y * factor;\n t = vec3(1.0 + signZ * normal.x * normal.x * factor, signZ * xy, -signZ * normal.x);\n b = vec3(xy, signZ + normal.y * normal.y * factor, -normal.y);\n }\n else { t = normalize(t); b = normalize(cross(normal, t)); }\n return mat2x3(t, b * (handedness < 0.0 ? -1.0 : 1.0));\n}\n\nvoid fetchAttributes(inout SurfaceInteraction si, Triangle tri, vec3 barycentric, ivec3 index) {\n si.hit = true;\n si.position = barycentric.x * tri.p0 + barycentric.y * tri.p1 + barycentric.z * tri.p2;\n ivec2 i0 = unpackTexel(index.x, vertexColumns);\n ivec2 i1 = unpackTexel(index.y, vertexColumns);\n ivec2 i2 = unpackTexel(index.z, vertexColumns);\n si.vertexColor = vec3(1.0);\n#ifdef USE_VERTEX_COLOR\n si.vertexColor = barycentric.x * texelFetch(colorBuffer, i0, 0).rgb\n + barycentric.y * texelFetch(colorBuffer, i1, 0).rgb\n + barycentric.z * texelFetch(colorBuffer, i2, 0).rgb;\n#endif\n\n vec3 n0 = texelFetch(normalBuffer, i0, 0).xyz;\n vec3 n1 = texelFetch(normalBuffer, i1, 0).xyz;\n vec3 n2 = texelFetch(normalBuffer, i2, 0).xyz;\n vec3 normal = normalize(barycentric.x * n0 + barycentric.y * n1 + barycentric.z * n2);\n si.vertexNormal = normal;\n\n // Approximate the smooth limit surface at this triangle hit. Direct-light\n // rays use this geometric offset only near the shadow terminator, avoiding\n // false self-shadowing by adjacent facets without moving ordinary hits.\n // Architecture follows Cycles' shadow_ray_smooth_surface_offset.\n vec3 geometricNormal = normalize(cross(tri.p1 - tri.p0, tri.p2 - tri.p0));\n vec3 smoothNormal = barycentric.x * n0 + barycentric.y * n1 + barycentric.z * n2;\n float a = dot(n2 - n0, tri.p0 - tri.p2);\n float b = dot(n2 - n1, tri.p1 - tri.p2);\n float c = dot(n1 - n0, tri.p1 - tri.p0);\n float h = a * barycentric.x * (barycentric.x - 1.0)\n + (a + b + c) * barycentric.x * barycentric.y\n + b * barycentric.y * (barycentric.y - 1.0);\n if (dot(smoothNormal, geometricNormal) > 0.0) {\n float h0 = max(max(dot(tri.p1 - tri.p0, n0), dot(tri.p2 - tri.p0, n0)), 0.0);\n float h1 = max(max(dot(tri.p0 - tri.p1, n1), dot(tri.p2 - tri.p1, n1)), 0.0);\n float 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 float h0 = max(max(dot(tri.p0 - tri.p1, n0), dot(tri.p0 - tri.p2, n0)), 0.0);\n float h1 = max(max(dot(tri.p1 - tri.p0, n1), dot(tri.p1 - tri.p2, n1)), 0.0);\n float 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 // TODO currently isampler doesn't work\n ivec4 m0 = ivec4(texelFetch(materialIndexBuffer, i0, 0));\n ivec4 m1 = ivec4(texelFetch(materialIndexBuffer, i1, 0));\n ivec4 m2 = ivec4(texelFetch(materialIndexBuffer, i2, 0));\n \n if (m0.x > 0 || m1.x > 0 || m2.x > 0) {\n si.materialIndex = randomPickMaterial(m0, m1, m2, barycentric);\n }\n \n vec2 uv0 = texelFetch(uvBuffer, i0, 0).xy;\n vec2 uv1 = texelFetch(uvBuffer, i1, 0).xy;\n vec2 uv2 = texelFetch(uvBuffer, i2, 0).xy;\n #if defined(NUM_DIFFUSE_MAPS) || defined(NUM_NORMAL_MAPS) || defined(NUM_METALLIC_ROUGHNESS_MAPS) || defined(NUM_ROUGHNESS_MAPS) || defined(NUM_METALNESS_MAPS) || defined(NUM_EMISSIVE_MAPS) || defined(NUM_THIN_FILM_MAPS) || defined(NUM_ANISOTROPY_MAPS) || defined(NUM_COAT_NORMAL_MAPS)\n vec2 uv = barycentric.x * uv0 + barycentric.y * uv1 + barycentric.z * uv2;\n vec2 secondaryUv0 = texelFetch(uv1Buffer, i0, 0).xy;\n vec2 secondaryUv1 = texelFetch(uv1Buffer, i1, 0).xy;\n vec2 secondaryUv2 = texelFetch(uv1Buffer, i2, 0).xy;\n vec2 secondaryUv = barycentric.x * secondaryUv0\n + barycentric.y * secondaryUv1\n + barycentric.z * secondaryUv2;\n\n vec4 uvInfo = fetchMatData(si.materialIndex * MAT_DATA_SIZE + MAT_ROW_UV);\n int uvProjection = int(uvInfo.r);\n if (uvProjection > 0) {\n uv = projectUV(uvProjection, si.position, normal, uvInfo.zw);\n secondaryUv = uv;\n } else {\n uv *= uvInfo.zw;\n secondaryUv *= uvInfo.zw;\n }\n #else\n vec2 uv = vec2(0.0);\n vec2 secondaryUv = vec2(0.0);\n #endif\n\n#ifdef USE_COAT_NORMAL_FRAMES\n si.coatNormal = normal;\n#ifdef NUM_COAT_NORMAL_MAPS\n float coatNormalIndex = fetchMatData(si.materialIndex * MAT_DATA_SIZE + MAT_ROW_LAYER_MAPS).z;\n#endif\n#endif\n vec4 directionMaterial = fetchMatData(si.materialIndex * MAT_DATA_SIZE + MAT_ROW_DIRECTION_NORMAL);\n float coatRotation = fetchMatData(si.materialIndex * MAT_DATA_SIZE + MAT_ROW_FILM_TEXTURE).x;\n if (directionMaterial.x >= 0.0 || directionMaterial.y != 0.0 || coatRotation != 0.0\n#ifdef USE_COAT_NORMAL_FRAMES\n || fetchMatData(si.materialIndex * MAT_DATA_SIZE + MAT_ROW_COAT).w > 0.0\n#endif\n ) {\n vec3 dp1 = tri.p0 - tri.p2;\n vec3 dp2 = tri.p1 - tri.p2;\n vec4 inputTangent = vec4(0.0);\n#ifdef USE_TANGENT\n inputTangent = barycentric.x * texelFetch(tangentBuffer, i0, 0)\n + barycentric.y * texelFetch(tangentBuffer, i1, 0)\n + barycentric.z * texelFetch(tangentBuffer, i2, 0);\n#endif\n mat2x3 geometryAxes = anisotropy_axes(normal, dp1, dp2, uv0-uv2, uv1-uv2, inputTangent);\n si.geometryTangent = geometryAxes[0];\n si.geometryBitangent = geometryAxes[1];\n mat2x3 directionAxes = geometryAxes;\n#ifdef NUM_ANISOTROPY_MAPS\n if (directionMaterial.x >= 0.0 && mapUvChannel[int(directionMaterial.x)] > 0.5) {\n vec2 a = texelFetch(uv1Buffer, i0, 0).xy;\n vec2 b = texelFetch(uv1Buffer, i1, 0).xy;\n vec2 c = texelFetch(uv1Buffer, i2, 0).xy;\n directionAxes = anisotropy_axes(normal, dp1, dp2, a-c, b-c, (inputTangent.w == 0.0 ? vec4(0.0) : inputTangent));\n }\n#endif\n si.directionTangent = directionAxes[0];\n si.directionBitangent = directionAxes[1];\n#if defined(USE_COAT_NORMAL_FRAMES) && defined(NUM_COAT_NORMAL_MAPS)\n if (coatNormalIndex >= 0.0) {\n int coatIndex = int(coatNormalIndex);\n mat2x3 coatAxes = geometryAxes;\n if (mapUvChannel[coatIndex] > 0.5) {\n vec2 a = texelFetch(uv1Buffer, i0, 0).xy;\n vec2 b = texelFetch(uv1Buffer, i1, 0).xy;\n vec2 c = texelFetch(uv1Buffer, i2, 0).xy;\n coatAxes = anisotropy_axes(normal, dp1, dp2, a-c, b-c,\n inputTangent.w == 0.0 ? vec4(0.0) : inputTangent);\n }\n vec3 decoded = sampleMap(coatIndex, getMapUvSet(coatIndex, uv, secondaryUv)).rgb * 2.0 - 1.0;\n vec3 mapped = coatAxes[0] * decoded.x * directionMaterial.z\n + coatAxes[1] * decoded.y * directionMaterial.w + normal * decoded.z;\n if (dot(mapped, mapped) > 1e-12) { si.coatNormal = normalize(mapped); }\n }\n#endif\n }\n\n si.uv = uv;\n si.uv1 = secondaryUv;\n\n #ifdef USE_TANGENT\n vec3 t0 = texelFetch(tangentBuffer, i0, 0).xyz;\n vec3 t1 = texelFetch(tangentBuffer, i1, 0).xyz;\n vec3 t2 = texelFetch(tangentBuffer, i2, 0).xyz;\n si.tangent = normalize(barycentric.x * t0 + barycentric.y * t1 + barycentric.z * t2);\n #endif\n #if defined(NUM_NORMAL_MAPS)\n vec3 dp1 = tri.p0 - tri.p2;\n vec3 dp2 = tri.p1 - tri.p2;\n vec2 duv1 = uv0 - uv2;\n vec2 duv2 = uv1 - uv2;\n si.normal = getMatNormal(\n si.materialIndex, uv, normal, dp1, dp2, duv1, duv2\n );\n #else\n si.normal = normal;\n #endif\n}\n\nstruct TriangleIntersect {\n float t;\n vec3 barycentric;\n};\n\nvec4 fetchBVH(int i) {\n return fetchData(bvhBuffer, i, bvhColumns);\n}\n\nvec4 fetchIns(int i) {\n return fetchData(instancesBuffer, i, instancesColumns);\n}\n\n// GLSL ES 3.00 has no bitCount intrinsic. Count the CWBVH mask bits using\n// integer operations supported by WebGL 2.\nuint countBvhBits(uint value) {\n value -= (value >> 1u) & 0x55555555u;\n value = (value & 0x33333333u) + ((value >> 2u) & 0x33333333u);\n value = (value + (value >> 4u)) & 0x0f0f0f0fu;\n return (value * 0x01010101u) >> 24u;\n}\n\nvec4 unpackBvh4Bounds(uint packedBounds) {\n return vec4(\n float(packedBounds & 0xffu),\n float((packedBounds >> 8u) & 0xffu),\n float((packedBounds >> 16u) & 0xffu),\n float(packedBounds >> 24u)\n ) / 255.0;\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\nTriangleIntersect intersectTriangle(vec3 o, vec3 d, Triangle tri, int maxDim, vec3 shear, float tMax) {\n TriangleIntersect ti;\n\n // translate vertices based on ray origin\n vec3 p0t = tri.p0 - o;\n vec3 p1t = tri.p1 - o;\n vec3 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 p0t.xy += shear.xy * p0t.z;\n p1t.xy += shear.xy * p1t.z;\n p2t.xy += shear.xy * p2t.z;\n\n // compute edge function coefficients\n vec3 e = vec3(\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#ifdef DEPTH_QUERY_MODE\n vec3 edgeScale = abs(vec3(p1t.x * p2t.y, p2t.x * p0t.y, p0t.x * p1t.y))\n + abs(vec3(p1t.y * p2t.x, p2t.y * p0t.x, p0t.y * p1t.x));\n e = mix(e, vec3(0.0), lessThanEqual(abs(e), edgeScale * 1e-6));\n#endif\n\n // check if intersection is inside triangle\n if (any(lessThan(e, vec3(0))) && any(greaterThan(e, vec3(0)))) {\n return ti;\n }\n\n float det = e.x + e.y + e.z;\n#ifdef DEPTH_QUERY_MODE\n if (det == 0.0) return ti;\n#endif\n\n p0t.z *= shear.z;\n p1t.z *= shear.z;\n p2t.z *= shear.z;\n float 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 float invDet = 1. / det;\n ti.t = tScaled * invDet;\n ti.barycentric = e * invDet;\n\n return ti;\n}\n\nstruct Box {\n vec3 min;\n vec3 max;\n};\n\n// Conservative slabs only for the unjittered depth query shader.\nvec3 traversalInverse(vec3 d) {\n#ifdef DEPTH_QUERY_MODE\n vec3 denominator = mix(d, mix(vec3(1e-20), vec3(-1e-20), lessThan(d, vec3(0.0))), lessThan(abs(d), vec3(1e-20)));\n return 1.0 / denominator;\n#else\n return 1.0 / d;\n#endif\n}\n\nvec3 depthSlabPadding(vec3 invD, vec3 o) {\n#ifdef DEPTH_QUERY_MODE\n return abs(invD) * max(vec3(1.0), abs(o)) * 1e-6;\n#else\n return vec3(0.0);\n#endif\n}\n\n// Branchless ray/box intersection\n// https://tavianator.com/fast-branchless-raybounding-box-intersections/\nfloat intersectBox(vec3 o, vec3 d, vec3 bmin, vec3 bmax, float tMax) {\n#ifdef DEPTH_QUERY_MODE\n vec3 invD = traversalInverse(d);\n vec3 tBot = (bmin - o) * invD;\n vec3 tTop = (bmax - o) * invD;\n vec3 padding = depthSlabPadding(invD, o);\n#else\n vec3 tBot = (bmin - o) / d;\n vec3 tTop = (bmax - o) / d;\n vec3 padding = vec3(0.0);\n#endif\n vec3 tNear = min(tBot, tTop) - padding;\n vec3 tFar = max(tBot, tTop) + padding;\n float t0 = max(tNear.x, max(tNear.y, tNear.z));\n float t1 = min(tFar.x, min(tFar.y, tFar.z));\n\n return (t0 > t1 || t0 > tMax) ? -1.0 : (t0 > 0.0 ? t0 : t1);\n}\n\nint maxDimension(vec3 v) {\n return v.x > v.y ? (v.x > v.z ? 0 : 2) : (v.y > v.z ? 1 : 2);\n}\n\nvec3 getShear(vec3 d, int maxDim) {\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 vec3(-d.y, -d.z, 1.0) / d.x;\n } else if (maxDim == 1) {\n return vec3(-d.z, -d.x, 1.0) / d.y;\n } else {\n return vec3(-d.x, -d.y, 1.0) / d.z;\n }\n}\n\n// Traverse BVH, find closest triangle intersection, and return surface information\nvoid intersectScene(in Ray ray, inout SurfaceInteraction si, bool doubleSided) {\n si.hit = false;\n\n if (isEmptyScene) {\n return;\n }\n\n int maxDim = maxDimension(abs(ray.d));\n vec3 shear = getShear(ray.d, maxDim);\n int hitInsIdx;\n\n${getBVHTraverseCode(glsl`\n si.triangleIndex = triangleIndex;\n hitTri = tri;\n hitFaceNormal = faceNormal;\n hitBarycentric = hit.barycentric;\n hitIndex = index;\n isHit = true;\n hitInsIdx = insIdx;\n`)}\n\n if (isHit) {\n si.instanceIndex = hitInsIdx;\n si.materialIndex = int(unpackMaterialIndex(floatBitsToInt(fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE}).z)).x);\n // fetchAttributes before transform to world space.\n fetchAttributes(si, hitTri, hitBarycentric, hitIndex);\n mat4 l2w = mat4(\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 5),\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 6),\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 7),\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 8)\n );\n mat4 w2l = mat4(\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 1),\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 2),\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 3),\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 4)\n );\n mat3 normalTransform = transpose(mat3(w2l));\n si.faceNormal = normalize(normalTransform * hitFaceNormal);\n si.vertexNormal = normalize(normalTransform * si.vertexNormal);\n si.normal = normalize(normalTransform * si.normal);\n#ifdef USE_COAT_NORMAL_FRAMES\n si.coatNormal = normalize(normalTransform * si.coatNormal);\n#endif\n#ifdef USE_TANGENT\n if (dot(si.tangent, si.tangent) > 0.0) {\n si.tangent = normalize(mat3(l2w) * si.tangent);\n }\n#endif\n si.shadowOffset = (l2w * vec4(si.shadowOffset, 0.0)).xyz;\n si.directionTangent = mat3(l2w) * si.directionTangent;\n si.directionBitangent = mat3(l2w) * si.directionBitangent;\n si.geometryTangent = mat3(l2w) * si.geometryTangent;\n si.geometryBitangent = mat3(l2w) * si.geometryBitangent;\n si.position = (l2w * vec4(si.position, 1.0)).xyz;\n }\n}\n\n// Local intersection helper for shader bevel.\nvoid intersectSceneBevel(in Ray ray, inout SurfaceInteraction si, int instanceIndex, float maxDistance) {\n si.hit = false;\n\n if (isEmptyScene) {\n return;\n }\n\n bool doubleSided = true;\n int maxDim = maxDimension(abs(ray.d));\n vec3 shear = getShear(ray.d, maxDim);\n int hitInsIdx;\n\n${getBVHTraverseCode(\n glsl`\n si.triangleIndex = triangleIndex;\n hitTri = tri;\n hitFaceNormal = faceNormal;\n hitBarycentric = hit.barycentric;\n hitIndex = index;\n isHit = true;\n hitInsIdx = insIdx;\n`,\n glsl`\n if (insIdx != instanceIndex) {\n continue;\n }\n`,\n 'maxDistance'\n)}\n\n if (isHit) {\n si.instanceIndex = hitInsIdx;\n si.materialIndex = int(unpackMaterialIndex(floatBitsToInt(fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE}).z)).x);\n fetchAttributes(si, hitTri, hitBarycentric, hitIndex);\n mat4 l2w = mat4(\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 5),\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 6),\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 7),\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 8)\n );\n mat4 w2l = mat4(\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 1),\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 2),\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 3),\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 4)\n );\n mat3 normalTransform = transpose(mat3(w2l));\n si.faceNormal = normalize(normalTransform * hitFaceNormal);\n si.vertexNormal = normalize(normalTransform * si.vertexNormal);\n si.normal = normalize(normalTransform * si.normal);\n#ifdef USE_COAT_NORMAL_FRAMES\n si.coatNormal = normalize(normalTransform * si.coatNormal);\n#endif\n#ifdef USE_TANGENT\n if (dot(si.tangent, si.tangent) > 0.0) {\n si.tangent = normalize(mat3(l2w) * si.tangent);\n }\n#endif\n si.shadowOffset = (l2w * vec4(si.shadowOffset, 0.0)).xyz;\n si.directionTangent = mat3(l2w) * si.directionTangent;\n si.directionBitangent = mat3(l2w) * si.directionBitangent;\n si.geometryTangent = mat3(l2w) * si.geometryTangent;\n si.geometryBitangent = mat3(l2w) * si.geometryBitangent;\n si.position = (l2w * vec4(si.position, 1.0)).xyz;\n }\n}\n\nbool intersectSceneShadow(in Ray ray, float lightDistance, int currTriangleIndex) {\n\n if (isEmptyScene) {\n return false;\n }\n\n bool doubleSided = true;\n int maxDim = maxDimension(abs(ray.d));\n vec3 shear = getShear(ray.d, maxDim);\n\n${getBVHTraverseCode(\n glsl`\n // Traversal is clipped to lightDistance. Affine instance transforms keep\n // the ray parameter unchanged because local directions are not normalized.\n bool isOccluded = true;\n vec3 barycentric = hit.barycentric;\n vec3 position = barycentric.x * tri.p0 + barycentric.y * tri.p1 + barycentric.z * tri.p2;\n\n if (isOccluded) {\n#ifdef HAS_ALPHA\n int materialIndex = int(unpackMaterialIndex(floatBitsToInt(fetchIns(insIdx * ${INSTANCE_VEC4_STRIDE}).z)).x);\n ivec2 i0 = unpackTexel(index.x, vertexColumns);\n ivec2 i1 = unpackTexel(index.y, vertexColumns);\n ivec2 i2 = unpackTexel(index.z, vertexColumns);\n ivec4 m0 = ivec4(texelFetch(materialIndexBuffer, i0, 0));\n ivec4 m1 = ivec4(texelFetch(materialIndexBuffer, i1, 0));\n ivec4 m2 = ivec4(texelFetch(materialIndexBuffer, i2, 0));\n \n if (m0.x > 0 || m1.x > 0 || m2.x > 0) {\n materialIndex = randomPickMaterial(m0, m1, m2, barycentric);\n }\n \n int ii = materialIndex * MAT_DATA_SIZE;\n int mapAlphaUsage = int(fetchMatData(ii + MAT_ROW_MISC).a);\n float alpha = fetchMatData(ii + MAT_ROW_ALPHA_FILM).r;\n #ifdef NUM_DIFFUSE_MAPS\n if (mapAlphaUsage == ${mapAlphaUsageConstants.alpha + ''}) {\n int diffuseMapIndex = int(fetchMatData(ii + MAT_ROW_METALLIC_MAPS).z);\n if (diffuseMapIndex >= 0) {\n vec2 uv0 = texelFetch(uvBuffer, i0, 0).xy;\n vec2 uv1 = texelFetch(uvBuffer, i1, 0).xy;\n vec2 uv2 = texelFetch(uvBuffer, i2, 0).xy;\n vec2 uv = barycentric.x * uv0 + barycentric.y * uv1 + barycentric.z * uv2;\n vec4 uvInfo = fetchMatData(ii + MAT_ROW_UV);\n int uvProjection = int(uvInfo.r);\n if (uvProjection > 0) {\n uv = projectUV(uvProjection, position, hitFaceNormal, uvInfo.zw);\n } else {\n uv *= uvInfo.zw;\n }\n \n alpha *= sampleMap(diffuseMapIndex, uv).a;\n }\n }\n #endif\n #ifdef NUM_ALPHA_MAPS\n int alphaMapIndex = int(fetchMatData(ii + MAT_ROW_STANDALONE_MAPS).z);\n if (alphaMapIndex >= 0) {\n vec2 uv0 = texelFetch(uvBuffer, i0, 0).xy;\n vec2 uv1 = texelFetch(uvBuffer, i1, 0).xy;\n vec2 uv2 = texelFetch(uvBuffer, i2, 0).xy;\n vec2 uv = barycentric.x * uv0 + barycentric.y * uv1 + barycentric.z * uv2;\n vec4 uvInfo = fetchMatData(ii + MAT_ROW_UV);\n int uvProjection = int(uvInfo.r);\n if (uvProjection > 0) {\n uv = projectUV(uvProjection, position, hitFaceNormal, uvInfo.zw);\n } else {\n uv *= uvInfo.zw;\n }\n alpha *= sampleMap(alphaMapIndex, uv).g;\n }\n #endif\n float alphaCutoff = fetchMatData(ii + MAT_ROW_STANDALONE_MAPS).a;\n alpha = resolveMaterialAlpha(\n alpha, int(fetchMatData(ii + MAT_ROW_ALPHA).w), alphaCutoff\n );\n if (hashedSample(0u) > alpha) {\n // Continue after a transparent hit without traversing beyond the light.\n tMax = min(max(lightDistance, 0.0), RAY_MAX_DISTANCE);\n isOccluded = false;\n }\n#endif\n }\n if (isOccluded) {\n return true;\n }\n`,\n glsl`\n if (triangleIndex == currTriangleIndex) {\n continue;\n }\n`,\n 'min(max(lightDistance, 0.0), RAY_MAX_DISTANCE)'\n)}\n return false;\n}\n`\n});\n"]}