@meimu/mutracer 0.2.0 → 0.3.1

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 (372) hide show
  1. package/CHANGELOG.md +222 -0
  2. package/LICENSES/FidelityFX-FSR.txt +19 -0
  3. package/LICENSES/pbrt-v4-Apache-2.0.txt +202 -0
  4. package/README.md +3 -0
  5. package/THIRD_PARTY_NOTICES.md +18 -4
  6. package/dist/assets/bvh.worker.js +1 -1
  7. package/dist/mutracer.umd.js +9328 -7614
  8. package/docs/api.md +83 -35
  9. package/docs/caustics.md +80 -0
  10. package/docs/materials.md +9 -6
  11. package/docs/performance.md +7 -0
  12. package/docs/post-processing.md +62 -9
  13. package/docs/progressive-rendering.md +53 -9
  14. package/docs/sampling.md +4 -0
  15. package/docs/technical/acceleration.md +52 -0
  16. package/docs/technical/aovs.md +79 -0
  17. package/docs/technical/caustics.md +109 -0
  18. package/docs/technical/continuous-spectrum.md +122 -0
  19. package/docs/technical/display-upscale.md +82 -0
  20. package/docs/technical/openpbr.md +99 -0
  21. package/docs/technical/pbrt-import.md +168 -0
  22. package/docs/technical/tiling.md +74 -0
  23. package/docs/technical/webgl.md +53 -0
  24. package/docs/updates.md +3 -1
  25. package/docs/viewer-checklist.md +65 -0
  26. package/docs/viewer.md +18 -6
  27. package/lib/Display.d.ts +19 -5
  28. package/lib/Display.js +10 -7
  29. package/lib/Display.js.map +1 -1
  30. package/lib/Engine.d.ts +4 -1
  31. package/lib/Engine.js +77 -14
  32. package/lib/Engine.js.map +1 -1
  33. package/lib/Render.d.ts +81 -24
  34. package/lib/Render.js +201 -26
  35. package/lib/Render.js.map +1 -1
  36. package/lib/RenderLoop.d.ts +7 -3
  37. package/lib/RenderLoop.js +68 -44
  38. package/lib/RenderLoop.js.map +1 -1
  39. package/lib/main.d.ts +4 -2
  40. package/lib/main.js +1 -0
  41. package/lib/main.js.map +1 -1
  42. package/lib/oidn.d.ts +1 -1
  43. package/lib/renderer/AovPrimitiveCache.js +39 -8
  44. package/lib/renderer/AovPrimitiveCache.js.map +1 -1
  45. package/lib/renderer/DisplayPostProcess.d.ts +8 -3
  46. package/lib/renderer/DisplayPostProcess.js +51 -18
  47. package/lib/renderer/DisplayPostProcess.js.map +1 -1
  48. package/lib/renderer/GPUTraceQueue.d.ts +2 -1
  49. package/lib/renderer/GPUTraceQueue.js +3 -1
  50. package/lib/renderer/GPUTraceQueue.js.map +1 -1
  51. package/lib/renderer/MaterialData.js +7 -2
  52. package/lib/renderer/MaterialData.js.map +1 -1
  53. package/lib/renderer/RenderingPipeline.d.ts +11 -1
  54. package/lib/renderer/RenderingPipeline.js.map +1 -1
  55. package/lib/renderer/TileRender.d.ts +2 -2
  56. package/lib/renderer/TileRender.js +128 -7
  57. package/lib/renderer/TileRender.js.map +1 -1
  58. package/lib/renderer/WGPUAov.d.ts +6 -1
  59. package/lib/renderer/WGPUAov.js +108 -14
  60. package/lib/renderer/WGPUAov.js.map +1 -1
  61. package/lib/renderer/WGPUCanvasSurface.js +12 -1
  62. package/lib/renderer/WGPUCanvasSurface.js.map +1 -1
  63. package/lib/renderer/WGPUCausticLightPass.d.ts +48 -0
  64. package/lib/renderer/WGPUCausticLightPass.js +217 -0
  65. package/lib/renderer/WGPUCausticLightPass.js.map +1 -0
  66. package/lib/renderer/WGPUEnvMapDistribution.js +19 -19
  67. package/lib/renderer/WGPUFullQuadPass.js +3 -3
  68. package/lib/renderer/WGPUPhotonMap.d.ts +37 -0
  69. package/lib/renderer/WGPUPhotonMap.js +191 -0
  70. package/lib/renderer/WGPUPhotonMap.js.map +1 -0
  71. package/lib/renderer/WGPURayTraceOutput.d.ts +13 -1
  72. package/lib/renderer/WGPURayTraceOutput.js +122 -28
  73. package/lib/renderer/WGPURayTraceOutput.js.map +1 -1
  74. package/lib/renderer/WGPURayTracePass.d.ts +65 -21
  75. package/lib/renderer/WGPURayTracePass.js +730 -409
  76. package/lib/renderer/WGPURayTracePass.js.map +1 -1
  77. package/lib/renderer/WGPURenderingPipeline.js +110 -68
  78. package/lib/renderer/WGPURenderingPipeline.js.map +1 -1
  79. package/lib/renderer/WebGLAov.js +3 -3
  80. package/lib/renderer/WebGLAov.js.map +1 -1
  81. package/lib/renderer/WebGLFullQuadPass.d.ts +2 -1
  82. package/lib/renderer/WebGLFullQuadPass.js +10 -0
  83. package/lib/renderer/WebGLFullQuadPass.js.map +1 -1
  84. package/lib/renderer/WebGLJNLMDenoiser.js +66 -66
  85. package/lib/renderer/WebGLJNLMDenoiser.js.map +1 -1
  86. package/lib/renderer/WebGLRayTraceOutputPass.js +4 -4
  87. package/lib/renderer/WebGLRayTracePass.d.ts +20 -11
  88. package/lib/renderer/WebGLRayTracePass.js +157 -116
  89. package/lib/renderer/WebGLRayTracePass.js.map +1 -1
  90. package/lib/renderer/WebGLRenderingPipeline.js +17 -14
  91. package/lib/renderer/WebGLRenderingPipeline.js.map +1 -1
  92. package/lib/renderer/WebGLSkyBaker.js +60 -60
  93. package/lib/renderer/accumulationTarget.d.ts +2 -1
  94. package/lib/renderer/accumulationTarget.js.map +1 -1
  95. package/lib/renderer/aov.d.ts +13 -1
  96. package/lib/renderer/aov.js +21 -2
  97. package/lib/renderer/aov.js.map +1 -1
  98. package/lib/renderer/aovDisplay.js +2 -2
  99. package/lib/renderer/aovDisplay.js.map +1 -1
  100. package/lib/renderer/binnedSahBvh.d.ts +2 -1
  101. package/lib/renderer/binnedSahBvh.js +24 -17
  102. package/lib/renderer/binnedSahBvh.js.map +1 -1
  103. package/lib/renderer/bvhAccel.d.ts +3 -1
  104. package/lib/renderer/bvhAccel.js +36 -6
  105. package/lib/renderer/bvhAccel.js.map +1 -1
  106. package/lib/renderer/cameraUniform.d.ts +1 -1
  107. package/lib/renderer/cameraUniform.js +6 -2
  108. package/lib/renderer/cameraUniform.js.map +1 -1
  109. package/lib/renderer/causticDiagnostics.d.ts +31 -0
  110. package/lib/renderer/causticDiagnostics.js +5 -0
  111. package/lib/renderer/causticDiagnostics.js.map +1 -0
  112. package/lib/renderer/causticEmission.d.ts +10 -0
  113. package/lib/renderer/causticEmission.js +40 -0
  114. package/lib/renderer/causticEmission.js.map +1 -0
  115. package/lib/renderer/causticUniform.d.ts +12 -0
  116. package/lib/renderer/causticUniform.js +22 -0
  117. package/lib/renderer/causticUniform.js.map +1 -0
  118. package/lib/renderer/caustics.d.ts +16 -0
  119. package/lib/renderer/caustics.js +93 -0
  120. package/lib/renderer/caustics.js.map +1 -0
  121. package/lib/renderer/compactVertices.d.ts +14 -0
  122. package/lib/renderer/compactVertices.js +124 -0
  123. package/lib/renderer/compactVertices.js.map +1 -0
  124. package/lib/renderer/easu.d.ts +11 -0
  125. package/lib/renderer/easu.js +156 -0
  126. package/lib/renderer/easu.js.map +1 -0
  127. package/lib/renderer/geometryUpdateState.d.ts +1 -0
  128. package/lib/renderer/geometryUpdateState.js.map +1 -1
  129. package/lib/renderer/geometryUpdates.js +3 -2
  130. package/lib/renderer/geometryUpdates.js.map +1 -1
  131. package/lib/renderer/glsl/aovDisplay.frag.js +5 -5
  132. package/lib/renderer/glsl/aovReflectance.glsl.js +53 -53
  133. package/lib/renderer/glsl/aovReflectanceSamples.glsl.js +1 -1
  134. package/lib/renderer/glsl/aovTrace.frag.js +45 -37
  135. package/lib/renderer/glsl/aovTrace.frag.js.map +1 -1
  136. package/lib/renderer/glsl/chunks/cameraProjection.glsl.d.ts +2 -0
  137. package/lib/renderer/glsl/chunks/cameraProjection.glsl.js +16 -0
  138. package/lib/renderer/glsl/chunks/cameraProjection.glsl.js.map +1 -0
  139. package/lib/renderer/glsl/chunks/cameraRay.glsl.d.ts +1 -1
  140. package/lib/renderer/glsl/chunks/cameraRay.glsl.js +9 -16
  141. package/lib/renderer/glsl/chunks/cameraRay.glsl.js.map +1 -1
  142. package/lib/renderer/glsl/chunks/emissiveLight.glsl.d.ts +2 -0
  143. package/lib/renderer/glsl/chunks/emissiveLight.glsl.js +35 -0
  144. package/lib/renderer/glsl/chunks/emissiveLight.glsl.js.map +1 -0
  145. package/lib/renderer/glsl/chunks/envMap.glsl.js +73 -73
  146. package/lib/renderer/glsl/chunks/intersect.glsl.js +478 -465
  147. package/lib/renderer/glsl/chunks/intersect.glsl.js.map +1 -1
  148. package/lib/renderer/glsl/chunks/materialBuffer.glsl.d.ts +1 -0
  149. package/lib/renderer/glsl/chunks/materialBuffer.glsl.js +156 -153
  150. package/lib/renderer/glsl/chunks/materialBuffer.glsl.js.map +1 -1
  151. package/lib/renderer/glsl/chunks/openPbrBsdf.glsl.js +771 -771
  152. package/lib/renderer/glsl/chunks/rand.glsl.js +3 -3
  153. package/lib/renderer/glsl/chunks/random.glsl.js +6 -6
  154. package/lib/renderer/glsl/chunks/rayTraceCore.glsl.d.ts +1 -0
  155. package/lib/renderer/glsl/chunks/rayTraceCore.glsl.js +70 -63
  156. package/lib/renderer/glsl/chunks/rayTraceCore.glsl.js.map +1 -1
  157. package/lib/renderer/glsl/chunks/sampleLight.glsl.js +89 -75
  158. package/lib/renderer/glsl/chunks/sampleLight.glsl.js.map +1 -1
  159. package/lib/renderer/glsl/chunks/sampleMaterial.glsl.d.ts +1 -0
  160. package/lib/renderer/glsl/chunks/sampleMaterial.glsl.js +160 -118
  161. package/lib/renderer/glsl/chunks/sampleMaterial.glsl.js.map +1 -1
  162. package/lib/renderer/glsl/chunks/sampleShadowCatcher.glsl.js +14 -14
  163. package/lib/renderer/glsl/chunks/sampling.glsl.js +88 -91
  164. package/lib/renderer/glsl/chunks/sampling.glsl.js.map +1 -1
  165. package/lib/renderer/glsl/chunks/sobol.glsl.js +79 -79
  166. package/lib/renderer/glsl/chunks/spectralPrefix.glsl.d.ts +6 -0
  167. package/lib/renderer/glsl/chunks/spectralPrefix.glsl.js +47 -0
  168. package/lib/renderer/glsl/chunks/spectralPrefix.glsl.js.map +1 -0
  169. package/lib/renderer/glsl/chunks/textureLinear.glsl.js +5 -5
  170. package/lib/renderer/glsl/chunks/uvProjection.glsl.js +11 -11
  171. package/lib/renderer/glsl/depthQuery.frag.js +13 -7
  172. package/lib/renderer/glsl/depthQuery.frag.js.map +1 -1
  173. package/lib/renderer/glsl/normalizeRayTraceOutput.frag.js +10 -10
  174. package/lib/renderer/glsl/rayTrace.frag.d.ts +1 -1
  175. package/lib/renderer/glsl/rayTrace.frag.js +338 -252
  176. package/lib/renderer/glsl/rayTrace.frag.js.map +1 -1
  177. package/lib/renderer/glsl/shadowCatcher.frag.js +27 -26
  178. package/lib/renderer/glsl/shadowCatcher.frag.js.map +1 -1
  179. package/lib/renderer/glsl/webglPipeline.frag.js +59 -59
  180. package/lib/renderer/gpuMemory.js +1 -1
  181. package/lib/renderer/gpuMemory.js.map +1 -1
  182. package/lib/renderer/lightSampling.d.ts +3 -2
  183. package/lib/renderer/lightSampling.js +14 -8
  184. package/lib/renderer/lightSampling.js.map +1 -1
  185. package/lib/renderer/openPbrEnergyLut.d.ts +2 -1
  186. package/lib/renderer/openPbrEnergyLut.js +18 -1
  187. package/lib/renderer/openPbrEnergyLut.js.map +1 -1
  188. package/lib/renderer/pathLimits.d.ts +5 -4
  189. package/lib/renderer/pathLimits.js +6 -5
  190. package/lib/renderer/pathLimits.js.map +1 -1
  191. package/lib/renderer/prepareMeshBVH.d.ts +2 -0
  192. package/lib/renderer/prepareMeshBVH.js +5 -3
  193. package/lib/renderer/prepareMeshBVH.js.map +1 -1
  194. package/lib/renderer/progressiveScheduler.js +5 -4
  195. package/lib/renderer/progressiveScheduler.js.map +1 -1
  196. package/lib/renderer/renderPipeline.d.ts +5 -0
  197. package/lib/renderer/renderPipeline.js +1 -0
  198. package/lib/renderer/renderPipeline.js.map +1 -1
  199. package/lib/renderer/researchTransport.d.ts +31 -0
  200. package/lib/renderer/researchTransport.js +5 -0
  201. package/lib/renderer/researchTransport.js.map +1 -0
  202. package/lib/renderer/sceneAssembly.d.ts +1 -1
  203. package/lib/renderer/sceneAssembly.js +4 -3
  204. package/lib/renderer/sceneAssembly.js.map +1 -1
  205. package/lib/renderer/sceneChangeDetector.d.ts +1 -1
  206. package/lib/renderer/sceneChangeDetector.js +3 -1
  207. package/lib/renderer/sceneChangeDetector.js.map +1 -1
  208. package/lib/renderer/sceneLayoutSchema.d.ts +2 -0
  209. package/lib/renderer/sceneLayoutSchema.js +3 -3
  210. package/lib/renderer/sceneLayoutSchema.js.map +1 -1
  211. package/lib/renderer/scenePreparation.d.ts +3 -2
  212. package/lib/renderer/scenePreparation.js +18 -2
  213. package/lib/renderer/scenePreparation.js.map +1 -1
  214. package/lib/renderer/scratchSampling.js +6 -6
  215. package/lib/renderer/spectrum.d.ts +21 -0
  216. package/lib/renderer/spectrum.js +154 -0
  217. package/lib/renderer/spectrum.js.map +1 -0
  218. package/lib/renderer/staticBvhPreparation.d.ts +9 -0
  219. package/lib/renderer/staticBvhPreparation.js +8 -2
  220. package/lib/renderer/staticBvhPreparation.js.map +1 -1
  221. package/lib/renderer/surfaceHit.d.ts +10 -0
  222. package/lib/renderer/surfaceHit.js +22 -0
  223. package/lib/renderer/surfaceHit.js.map +1 -0
  224. package/lib/renderer/tangentRequirements.d.ts +2 -0
  225. package/lib/renderer/tangentRequirements.js +7 -0
  226. package/lib/renderer/tangentRequirements.js.map +1 -0
  227. package/lib/renderer/textureAtlas.d.ts +4 -0
  228. package/lib/renderer/textureAtlas.js +48 -2
  229. package/lib/renderer/textureAtlas.js.map +1 -1
  230. package/lib/renderer/webgl.js +4 -0
  231. package/lib/renderer/webgl.js.map +1 -1
  232. package/lib/renderer/webglLightSampling.d.ts +39 -0
  233. package/lib/renderer/webglLightSampling.js +15 -0
  234. package/lib/renderer/webglLightSampling.js.map +1 -0
  235. package/lib/renderer/webglVertexAttributes.d.ts +17 -0
  236. package/lib/renderer/webglVertexAttributes.js +44 -0
  237. package/lib/renderer/webglVertexAttributes.js.map +1 -0
  238. package/lib/renderer/wgpuBindlessSource.js +3 -3
  239. package/lib/renderer/wgpuMaterialConstants.js +4 -3
  240. package/lib/renderer/wgpuMaterialConstants.js.map +1 -1
  241. package/lib/renderer/wgpuSceneUniform.js +2 -2
  242. package/lib/renderer/wgpuSceneUniform.js.map +1 -1
  243. package/lib/renderer/wgpuShaderSource.d.ts +22 -0
  244. package/lib/renderer/wgpuShaderSource.js +3 -1
  245. package/lib/renderer/wgpuShaderSource.js.map +1 -1
  246. package/lib/renderer/wgsl/aovCompact.wgsl.d.ts +4 -0
  247. package/lib/renderer/wgsl/aovCompact.wgsl.js +26 -0
  248. package/lib/renderer/wgsl/aovCompact.wgsl.js.map +1 -0
  249. package/lib/renderer/wgsl/aovDisplay.wgsl.js +9 -9
  250. package/lib/renderer/wgsl/aovReflectance.wgsl.js +55 -55
  251. package/lib/renderer/wgsl/aovReflectanceSamples.wgsl.js +1 -1
  252. package/lib/renderer/wgsl/aovResolve.wgsl.d.ts +4 -0
  253. package/lib/renderer/wgsl/aovResolve.wgsl.js +21 -2
  254. package/lib/renderer/wgsl/aovResolve.wgsl.js.map +1 -1
  255. package/lib/renderer/wgsl/aovSnapshot.wgsl.js +2 -2
  256. package/lib/renderer/wgsl/aovSnapshot.wgsl.js.map +1 -1
  257. package/lib/renderer/wgsl/aovTrace.wgsl.js +33 -33
  258. package/lib/renderer/wgsl/aovTraceDisplay.wgsl.d.ts +2 -0
  259. package/lib/renderer/wgsl/aovTraceDisplay.wgsl.js +56 -0
  260. package/lib/renderer/wgsl/aovTraceDisplay.wgsl.js.map +1 -0
  261. package/lib/renderer/wgsl/bloom.wgsl.js +18 -18
  262. package/lib/renderer/wgsl/causticLight.compute.d.ts +6 -0
  263. package/lib/renderer/wgsl/causticLight.compute.js +539 -0
  264. package/lib/renderer/wgsl/causticLight.compute.js.map +1 -0
  265. package/lib/renderer/wgsl/chunks/background.wgsl.d.ts +2 -0
  266. package/lib/renderer/wgsl/chunks/background.wgsl.js +102 -0
  267. package/lib/renderer/wgsl/chunks/background.wgsl.js.map +1 -0
  268. package/lib/renderer/wgsl/chunks/causticAdjoint.wgsl.d.ts +2 -0
  269. package/lib/renderer/wgsl/chunks/causticAdjoint.wgsl.js +12 -0
  270. package/lib/renderer/wgsl/chunks/causticAdjoint.wgsl.js.map +1 -0
  271. package/lib/renderer/wgsl/chunks/causticAtomic.wgsl.d.ts +2 -0
  272. package/lib/renderer/wgsl/chunks/causticAtomic.wgsl.js +16 -0
  273. package/lib/renderer/wgsl/chunks/causticAtomic.wgsl.js.map +1 -0
  274. package/lib/renderer/wgsl/chunks/caustics.wgsl.d.ts +1 -0
  275. package/lib/renderer/wgsl/chunks/caustics.wgsl.js +62 -0
  276. package/lib/renderer/wgsl/chunks/caustics.wgsl.js.map +1 -0
  277. package/lib/renderer/wgsl/chunks/common.wgsl.js +4 -4
  278. package/lib/renderer/wgsl/chunks/envMap.wgsl.js +37 -37
  279. package/lib/renderer/wgsl/chunks/intersect.wgsl.d.ts +6 -2
  280. package/lib/renderer/wgsl/chunks/intersect.wgsl.js +634 -620
  281. package/lib/renderer/wgsl/chunks/intersect.wgsl.js.map +1 -1
  282. package/lib/renderer/wgsl/chunks/intersectionAlpha.wgsl.d.ts +1 -1
  283. package/lib/renderer/wgsl/chunks/intersectionAlpha.wgsl.js +23 -21
  284. package/lib/renderer/wgsl/chunks/intersectionAlpha.wgsl.js.map +1 -1
  285. package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.d.ts +3 -1
  286. package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.js +77 -74
  287. package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.js.map +1 -1
  288. package/lib/renderer/wgsl/chunks/openPbrBsdf.wgsl.js +795 -795
  289. package/lib/renderer/wgsl/chunks/pdm_displacement.wgsl.js +139 -139
  290. package/lib/renderer/wgsl/chunks/rand.wgsl.js +3 -3
  291. package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.d.ts +5 -1
  292. package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.js +45 -29
  293. package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.js.map +1 -1
  294. package/lib/renderer/wgsl/chunks/sampleLight.wgsl.d.ts +1 -1
  295. package/lib/renderer/wgsl/chunks/sampleLight.wgsl.js +142 -138
  296. package/lib/renderer/wgsl/chunks/sampleLight.wgsl.js.map +1 -1
  297. package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.d.ts +1 -1
  298. package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.js +228 -189
  299. package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.js.map +1 -1
  300. package/lib/renderer/wgsl/chunks/sampleShadowCatcher.wgsl.d.ts +1 -1
  301. package/lib/renderer/wgsl/chunks/sampleShadowCatcher.wgsl.js +11 -11
  302. package/lib/renderer/wgsl/chunks/sampleShadowCatcher.wgsl.js.map +1 -1
  303. package/lib/renderer/wgsl/chunks/sampling.wgsl.d.ts +1 -1
  304. package/lib/renderer/wgsl/chunks/sampling.wgsl.js +88 -92
  305. package/lib/renderer/wgsl/chunks/sampling.wgsl.js.map +1 -1
  306. package/lib/renderer/wgsl/chunks/sobol.wgsl.js +83 -83
  307. package/lib/renderer/wgsl/chunks/spectralPrefix.wgsl.d.ts +6 -0
  308. package/lib/renderer/wgsl/chunks/spectralPrefix.wgsl.js +66 -0
  309. package/lib/renderer/wgsl/chunks/spectralPrefix.wgsl.js.map +1 -0
  310. package/lib/renderer/wgsl/chunks/uvProjection.wgsl.js +11 -11
  311. package/lib/renderer/wgsl/chunks/vertexAttributes.wgsl.d.ts +2 -0
  312. package/lib/renderer/wgsl/chunks/vertexAttributes.wgsl.js +61 -0
  313. package/lib/renderer/wgsl/chunks/vertexAttributes.wgsl.js.map +1 -0
  314. package/lib/renderer/wgsl/compose.frag.wgsl.d.ts +2 -0
  315. package/lib/renderer/wgsl/compose.frag.wgsl.js +100 -98
  316. package/lib/renderer/wgsl/compose.frag.wgsl.js.map +1 -1
  317. package/lib/renderer/wgsl/customDenoise.wgsl.js +10 -10
  318. package/lib/renderer/wgsl/depthQuery.wgsl.d.ts +1 -1
  319. package/lib/renderer/wgsl/depthQuery.wgsl.js +12 -3
  320. package/lib/renderer/wgsl/depthQuery.wgsl.js.map +1 -1
  321. package/lib/renderer/wgsl/easu.wgsl.d.ts +2 -0
  322. package/lib/renderer/wgsl/easu.wgsl.js +80 -0
  323. package/lib/renderer/wgsl/easu.wgsl.js.map +1 -0
  324. package/lib/renderer/wgsl/easuDetail.wgsl.d.ts +21 -0
  325. package/lib/renderer/wgsl/easuDetail.wgsl.js +230 -0
  326. package/lib/renderer/wgsl/easuDetail.wgsl.js.map +1 -0
  327. package/lib/renderer/wgsl/jnlm.compute.js +74 -74
  328. package/lib/renderer/wgsl/jnlm.compute.js.map +1 -1
  329. package/lib/renderer/wgsl/offlineAdaptive.compute.js +26 -26
  330. package/lib/renderer/wgsl/oidnAuxiliary.wgsl.js +3 -3
  331. package/lib/renderer/wgsl/photonMap.compute.d.ts +29 -0
  332. package/lib/renderer/wgsl/photonMap.compute.js +352 -0
  333. package/lib/renderer/wgsl/photonMap.compute.js.map +1 -0
  334. package/lib/renderer/wgsl/proceduralSky.compute.js +64 -64
  335. package/lib/renderer/wgsl/rayTrace.compute.d.ts +23 -1
  336. package/lib/renderer/wgsl/rayTrace.compute.js +531 -543
  337. package/lib/renderer/wgsl/rayTrace.compute.js.map +1 -1
  338. package/lib/scene/MaterialState.d.ts +2 -2
  339. package/lib/scene/MaterialState.js +2 -2
  340. package/lib/scene/MaterialState.js.map +1 -1
  341. package/lib/scene/Scene.js.map +1 -1
  342. package/lib/settings/render.d.ts +22 -0
  343. package/lib/settings/render.js +2 -0
  344. package/lib/settings/render.js.map +1 -1
  345. package/lib/util/fromTHREE.js +23 -16
  346. package/lib/util/fromTHREE.js.map +1 -1
  347. package/lib/version.d.ts +1 -1
  348. package/lib/version.js +1 -1
  349. package/lib/version.js.map +1 -1
  350. package/llms.txt +2 -1
  351. package/package.json +7 -4
  352. package/lib/oidnBundledWeights.cdn.d.ts +0 -2
  353. package/lib/oidnBundledWeights.cdn.js +0 -5
  354. package/lib/oidnBundledWeights.cdn.js.map +0 -1
  355. package/lib/renderer/bvh.worker.d.ts +0 -1
  356. package/lib/renderer/bvh.worker.js +0 -16
  357. package/lib/renderer/bvh.worker.js.map +0 -1
  358. package/lib/renderer/bvhWorkerBuild.d.ts +0 -13
  359. package/lib/renderer/bvhWorkerBuild.js +0 -17
  360. package/lib/renderer/bvhWorkerBuild.js.map +0 -1
  361. package/lib/renderer/lightCore.d.ts +0 -12
  362. package/lib/renderer/lightCore.js +0 -13
  363. package/lib/renderer/lightCore.js.map +0 -1
  364. package/lib/renderer/materialCore.d.ts +0 -8
  365. package/lib/renderer/materialCore.js +0 -9
  366. package/lib/renderer/materialCore.js.map +0 -1
  367. package/lib/scene/PixelTexture.d.ts +0 -6
  368. package/lib/scene/PixelTexture.js +0 -10
  369. package/lib/scene/PixelTexture.js.map +0 -1
  370. package/lib/util/studioLighting.d.ts +0 -18
  371. package/lib/util/studioLighting.js +0 -36
  372. package/lib/util/studioLighting.js.map +0 -1
@@ -27,8 +27,8 @@ _padding: f32,
27
27
  const LUMINANCE = vec3f(0.2126, 0.7152, 0.0722);
28
28
 
29
29
  @compute @workgroup_size(64)
30
- fn main(@builtin(global_invocation_id) vQ: vec3u) {
31
- let tile = vQ.x;
30
+ fn main(@builtin(global_invocation_id) eJ: vec3u) {
31
+ let tile = eJ.x;
32
32
  let tileCount = params.columns * params.rows;
33
33
  if (tile >= tileCount) {
34
34
  return;
@@ -36,16 +36,16 @@ return;
36
36
 
37
37
  let tileX = tile % params.columns;
38
38
  let tileY = tile / params.columns;
39
- let vY = tileX * params.tileSize;
40
- let vZ = tileY * params.tileSize;
41
- let ye = min(vY + params.tileSize, params.width);
42
- let yf = min(vZ + params.tileSize, params.height);
43
- var nW = 0.0;
44
- var jB = 0.0;
39
+ let yp = tileX * params.tileSize;
40
+ let yq = tileY * params.tileSize;
41
+ let AD = min(yp + params.tileSize, params.width);
42
+ let AE = min(yq + params.tileSize, params.height);
43
+ var oL = 0.0;
44
+ var jL = 0.0;
45
45
  var count = 0u;
46
46
 
47
- for (var y = vZ; y < yf; y++) {
48
- for (var x = vY; x < ye; x++) {
47
+ for (var y = yq; y < AE; y++) {
48
+ for (var x = yp; x < AD; x++) {
49
49
  let pixel = y * params.width + x;
50
50
  let offset = pixel * 4u;
51
51
  let samples = pilotMoments[offset + 3u];
@@ -60,28 +60,28 @@ pilotColor[offset + 2u]
60
60
  ),
61
61
  LUMINANCE
62
62
  ) / samples;
63
- let va = pilotMoments[offset] / samples;
64
- let qg = max(va - mean * mean, 0.0);
65
- var variance = qg;
63
+ let wX = pilotMoments[offset] / samples;
64
+ let rj = max(wX - mean * mean, 0.0);
65
+ var variance = rj;
66
66
  if (samples > 1.0) {
67
- variance = qg * samples / (samples - 1.0);
67
+ variance = rj * samples / (samples - 1.0);
68
68
  }
69
- nW += mean;
70
- jB = max(jB, variance);
69
+ oL += mean;
70
+ jL = max(jL, variance);
71
71
  count += 1u;
72
72
  }
73
73
  }
74
74
 
75
- let mean = nW / f32(max(count, 1u));
76
- var pT = 0.0;
75
+ let mean = oL / f32(max(count, 1u));
76
+ var qO = 0.0;
77
77
  if (params.hasPrevious != 0u) {
78
- pT = previousVariance[tile];
78
+ qO = previousVariance[tile];
79
79
  }
80
- let tu = max(jB, pT);
81
- let uD = params.absoluteError + params.relativeError * abs(mean);
80
+ let uX = max(jL, qO);
81
+ let sf = params.absoluteError + params.relativeError * abs(mean);
82
82
  let required = ceil(
83
- params.confidence * params.confidence * tu /
84
- max(uD * uD, 1e-16)
83
+ params.confidence * params.confidence * uX /
84
+ max(sf * sf, 1e-16)
85
85
  );
86
86
  let targetSamples = u32(min(
87
87
  f32(params.maxSamples),
@@ -91,11 +91,11 @@ max(f32(params.minSamples), required)
91
91
  tileMeans[tile] = mean;
92
92
 
93
93
 
94
- tileVariances[tile] = jB;
94
+ tileVariances[tile] = jL;
95
95
  tileTargets[tile] = targetSamples;
96
96
 
97
- for (var y = vZ; y < yf; y++) {
98
- for (var x = vY; x < ye; x++) {
97
+ for (var y = yq; y < AE; y++) {
98
+ for (var x = yp; x < AD; x++) {
99
99
  textureStore(
100
100
  targetTexture,
101
101
  vec2i(i32(x), i32(y)),
@@ -1,16 +1,16 @@
1
1
  // OIDN's LDR auxiliary networks use sRGB albedo and normals in [0,1].
2
2
  export const oidnAuxiliaryDefine = /* wgsl */ `
3
- fn sT(value: vec3f) -> vec3f {
3
+ fn uq(value: vec3f) -> vec3f {
4
4
  let linear = clamp(value, vec3f(0.0), vec3f(1.0));
5
5
  return select(1.055 * pow(linear, vec3f(1.0 / 2.4)) - 0.055,
6
6
  12.92 * linear, linear <= vec3f(0.0031308));
7
7
  }
8
- fn sS(value: vec3f) -> vec3f {
8
+ fn uo(value: vec3f) -> vec3f {
9
9
  let encoded = clamp(value, vec3f(0.0), vec3f(1.0));
10
10
  return select(pow((encoded + 0.055) / 1.055, vec3f(2.4)),
11
11
  encoded / 12.92, encoded <= vec3f(0.04045));
12
12
  }
13
- fn rL(value: vec3f) -> vec3f {
13
+ fn sW(value: vec3f) -> vec3f {
14
14
  return clamp(value, vec3f(0.0), vec3f(1.0));
15
15
  }
16
16
  `;
@@ -0,0 +1,29 @@
1
+ export declare const PHOTON_BYTES = 20;
2
+ export declare const PHOTON_GRID_SIZE = 262144;
3
+ export declare const PHOTON_SCAN_BLOCK = 256;
4
+ export declare const PHOTON_REFERENCE_COUNT: number;
5
+ export type PhotonRadiusScaling = 'none' | 'ppm' | 'constant-count';
6
+ /** Internal density A/B, with the same progressive schedule in all modes. */
7
+ export declare function photonRadius(initial: number, batch: number, alpha?: number, photons?: number, scaling?: PhotonRadiusScaling): number;
8
+ /** Default photon-map batch: pixel count / 4, capped at 2^18. */
9
+ export declare function defaultPhotonMapPhotons(width: number, height: number, divisor?: number): number;
10
+ /** Power-of-two hash capacity, bounded independently of large photon batches. */
11
+ export declare function photonBucketCount(photons: number): number;
12
+ /** Shared texture address contract; payload is stored once, indices twice. */
13
+ export declare function photonLayout(count: number, perCasterRadius?: boolean): {
14
+ buckets: number;
15
+ coarseHeaders: number;
16
+ fineHeaders: number;
17
+ coarseIndices: number;
18
+ fineIndices: number;
19
+ directions: number;
20
+ texels: number;
21
+ };
22
+ /** Batch-local hash buckets: count, two-level parallel prefix sum, then parallel scatter. */
23
+ export declare const photonGridCompute = "\n struct Bucket {\n count: atomic<u32>,\n offset: u32,\n cursor: atomic<u32>,\n padding: u32,\n }\n struct Parameters {\n radius: f32,\n count: u32,\n screenPixels: f32,\n skipQuery: f32,\n buckets: u32,\n fineRadius: f32,\n experiments: u32,\n padding2: u32,\n }\n @group(0)@binding(0)var photons:texture_2d<u32>;\n @group(0)@binding(1)var<storage,read_write>grid:array<Bucket>;\n @group(0)@binding(2)var<uniform>parameters:Parameters;\n @group(0)@binding(4)var headers:texture_storage_2d<r32uint,write>;\n fn hashCell(cell:vec3i)->u32 {\n let p=bitcast<vec3u>(cell);\n return ((p.x*73856093u)^(p.y*19349663u)^(p.z*83492791u))&(parameters.buckets-1u);\n }\n fn hashPhoton(p:vec3f,layer:u32)->u32 {\n let radius=select(parameters.radius,parameters.fineRadius,layer==1u);\n let cellSize=select(radius,2.0*radius,layer==1u);\n return hashCell(vec3i(floor(p/cellSize)));\n }\n fn gridStart(layer:u32)->u32 {\n return layer*(parameters.buckets+parameters.buckets/256u);\n }\n fn storeIndex(i:u32,value:u32) {\n let width=textureDimensions(headers).x;\n textureStore(headers,vec2i(vec2u(i%width,i/width)),vec4u(value));\n }\n fn photonRecord(i:u32)->vec4u {\n let width=textureDimensions(photons).x;\n return textureLoad(photons,vec2i(vec2u(i%width,i/width)),0);\n }\n fn photonPosition(i:u32)->vec3f {\n return bitcast<vec3f>(photonRecord(i).xyz);\n }\n @compute @workgroup_size(64)\n fn count(@builtin(global_invocation_id)id:vec3u) {\n if(id.x+id.y*4194240u>=parameters.count) {\n return;\n }\n let index=id.x+id.y*4194240u;\n if(photonRecord(index).w==0u) {\n return;\n }\n for(var layer=0u;layer<2u;layer++) {\n atomicAdd(&grid[gridStart(layer)+hashPhoton(photonPosition(index),layer)].count,1u);\n }\n }\n // Each block scans 256 buckets; the next workgroup scans chunks of block totals.\n var<workgroup> scan:array<u32,256>;\n fn inclusiveScan(local:u32,value:u32)->u32 {\n scan[local]=value;\n workgroupBarrier();\n for(var stride=1u;stride<256u;stride*=2u) {\n var previous=0u;\n if(local>=stride) {\n previous=scan[local-stride];\n }\n workgroupBarrier();\n scan[local]+=previous;\n workgroupBarrier();\n }\n return scan[local];\n }\n @compute @workgroup_size(256)\n fn scanBlocks(@builtin(local_invocation_index)local:u32,@builtin(workgroup_id)group:vec3u) {\n let index=gridStart(group.y)+group.x*256u+local;\n let count=atomicLoad(&grid[index].count);\n let prefix=inclusiveScan(local,count);\n grid[index].offset=prefix-count;\n if(local==255u) {\n atomicStore(&grid[gridStart(group.y)+parameters.buckets+group.x].count,prefix);\n }\n }\n @compute @workgroup_size(256)\n fn scanSums(@builtin(local_invocation_index)local:u32,@builtin(workgroup_id)group:vec3u) {\n let blocks=parameters.buckets/256u;\n let chunk=(blocks+255u)/256u;\n let first=local*chunk;\n let start=gridStart(group.y)+parameters.buckets;\n var count=0u;\n for(var i=first;i<min(first+chunk,blocks);i++) {\n count+=atomicLoad(&grid[start+i].count);\n }\n let prefix=inclusiveScan(local,count);\n var offset=prefix-count;\n for(var i=first;i<min(first+chunk,blocks);i++) {\n grid[start+i].offset=offset;\n offset+=atomicLoad(&grid[start+i].count);\n }\n if(local==255u&&group.y==0u) {\n storeIndex(0u,bitcast<u32>(parameters.radius));\n storeIndex(1u,prefix);\n storeIndex(2u,bitcast<u32>(parameters.screenPixels));\n storeIndex(3u,bitcast<u32>(parameters.skipQuery));\n storeIndex(4u,parameters.count);\n storeIndex(5u,parameters.buckets);\n storeIndex(6u,bitcast<u32>(parameters.fineRadius));\n storeIndex(7u,parameters.experiments);\n }\n }\n @compute @workgroup_size(64)\n fn scatter(@builtin(global_invocation_id)id:vec3u) {\n let invocation=id.x+id.y*4194240u;\n for(var layer=0u;layer<2u;layer++) {\n let start=gridStart(layer);\n if(invocation<parameters.buckets) {\n let offset=grid[start+invocation].offset+grid[start+parameters.buckets+invocation/256u].offset;\n storeIndex(8u+layer*parameters.buckets*2u+invocation*2u,offset);\n storeIndex(9u+layer*parameters.buckets*2u+invocation*2u,atomicLoad(&grid[start+invocation].count));\n }\n if(invocation<parameters.count&&photonRecord(invocation).w!=0u) {\n let bucket=hashPhoton(photonPosition(invocation),layer);\n let index=grid[start+bucket].offset+grid[start+parameters.buckets+bucket/256u].offset+atomicAdd(&grid[start+bucket].cursor,1u);\n storeIndex(8u+parameters.buckets*4u+layer*parameters.count+index,invocation);\n }\n }\n }\n";
24
+ /** Packed photon payload: position f32 x3, RGBE8 flux, two octahedral directions. */
25
+ export declare const photonPackingWgsl = "\n fn photonOctSign(v:vec2f)->vec2f {\n return select(vec2f(-1.0),vec2f(1.0),v>=vec2f(0.0));\n }\n fn photonEncodeOct(n:vec3f)->vec2f {\n let p=n/(abs(n.x)+abs(n.y)+abs(n.z));\n let uv=select(p.xy,(vec2f(1.0)-abs(p.yx))*photonOctSign(p.xy),p.z<0.0);\n return uv*.5+.5;\n }\n fn photonDecodeOct(uv:vec2f)->vec3f {\n let p=uv*2.0-1.0;\n var n=vec3f(p,1.0-abs(p.x)-abs(p.y));\n if(n.z<0.0) {\n n=vec3f((vec2f(1.0)-abs(n.yx))*photonOctSign(n.xy),n.z);\n }\n return normalize(n);\n }\n // An eight-bit exponent avoids RGB9E5's limited range for very dim/bright lights.\n // Flux is stored before dividing by emitted count, preserving tiny large batches.\n fn photonEncodeFlux(value:vec3f)->u32 {\n let peak=max(max(value.x,value.y),value.z);\n if(!(peak>0.0)) {\n return 0u;\n }\n let exponent=clamp(i32(floor(log2(peak)))+1,-126,127);\n let rgb=vec3u(clamp(round(value*exp2(-f32(exponent))*255.0),vec3f(0.0),vec3f(255.0)));\n return rgb.x|(rgb.y<<8u)|(rgb.z<<16u)|(u32(exponent+127)<<24u);\n }\n fn photonDecodeFlux(value:u32)->vec3f {\n if(value==0u) {\n return vec3f(0.0);\n }\n return vec3f(f32(value&255u),f32((value>>8u)&255u),f32((value>>16u)&255u))*exp2(f32(i32(value>>24u)-127))/255.0;\n }\n";
26
+ export declare const photonQueryWgsl = "\n fn photonOctSign(v:vec2f)->vec2f {\n return select(vec2f(-1.0),vec2f(1.0),v>=vec2f(0.0));\n }\n fn photonEncodeOct(n:vec3f)->vec2f {\n let p=n/(abs(n.x)+abs(n.y)+abs(n.z));\n let uv=select(p.xy,(vec2f(1.0)-abs(p.yx))*photonOctSign(p.xy),p.z<0.0);\n return uv*.5+.5;\n }\n fn photonDecodeOct(uv:vec2f)->vec3f {\n let p=uv*2.0-1.0;\n var n=vec3f(p,1.0-abs(p.x)-abs(p.y));\n if(n.z<0.0) {\n n=vec3f((vec2f(1.0)-abs(n.yx))*photonOctSign(n.xy),n.z);\n }\n return normalize(n);\n }\n // An eight-bit exponent avoids RGB9E5's limited range for very dim/bright lights.\n // Flux is stored before dividing by emitted count, preserving tiny large batches.\n fn photonEncodeFlux(value:vec3f)->u32 {\n let peak=max(max(value.x,value.y),value.z);\n if(!(peak>0.0)) {\n return 0u;\n }\n let exponent=clamp(i32(floor(log2(peak)))+1,-126,127);\n let rgb=vec3u(clamp(round(value*exp2(-f32(exponent))*255.0),vec3f(0.0),vec3f(255.0)));\n return rgb.x|(rgb.y<<8u)|(rgb.z<<16u)|(u32(exponent+127)<<24u);\n }\n fn photonDecodeFlux(value:u32)->vec3f {\n if(value==0u) {\n return vec3f(0.0);\n }\n return vec3f(f32(value&255u),f32((value>>8u)&255u),f32((value>>16u)&255u))*exp2(f32(i32(value>>24u)-127))/255.0;\n }\n\n @group(1)@binding(11)var photonMapData:texture_2d<u32>;\n @group(1)@binding(12)var photonMapGrid:texture_2d<u32>;\n fn photonLoad(i:u32)->vec4u {\n let width=textureDimensions(photonMapData).x;\n return textureLoad(photonMapData,vec2i(vec2u(i%width,i/width)),0);\n }\n fn photonIndexWord(i:u32)->u32 {\n let width=textureDimensions(photonMapGrid).x;\n return textureLoad(photonMapGrid,vec2i(vec2u(i%width,i/width)),0).x;\n }\n fn photonHash(cell:vec3i)->u32 {\n let p=bitcast<vec3u>(cell);\n return ((p.x*73856093u)^(p.y*19349663u)^(p.z*83492791u))&(photonIndexWord(5u)-1u);\n }\n // Approximate a specular prefix by the camera-space depth of its first diffuse\n // receiver. Orthographic footprints do not grow with distance. Grazing surfaces\n // expand the disk conservatively; the progressive world radius remains the cap.\n fn photonPixelFootprint(si:SurfaceInteraction)->f32 {\n let inverse=cameraUniform.viewProjectionInv;\n let a=inverse*vec4f(0.0,0.0,-1.0,1.0);\n let b=inverse*vec4f(2.0/rtUniform.outputSize.x,0.0,-1.0,1.0);\n let origin=a.xyz/a.w;\n let offset=b.xyz/b.w;\n let eye=cameraUniform.viewInv[3].xyz;\n let forward=normalize(cameraUniform.viewInv[2].xyz);\n let depth=abs(dot(si.position-eye,forward));\n let nearDepth=max(abs(dot(origin-eye,forward)),1e-8);\n let perspective=abs(inverse[2].w)>1e-8;\n let footprint=length(offset-origin)*select(1.0,depth/nearDepth,perspective);\n let cosine=abs(dot(si.faceNormal,normalize(si.position-eye)));\n return max(1e-8,footprint/sqrt(max(.0625,cosine)));\n }\n fn photonContribution(si:SurfaceInteraction,prepared:OpenPbrPreparedBsdf,primary:bool,visible:bool)->vec3f {\n if(samplingBounce>CAUSTIC_PHOTON_MAX_DEPTH||isCausticSpecular(prepared.material)||prepared.material.materialType==SHADOW_CATCHER) {\n return vec3f(0.0);\n }\n if(CAUSTIC_LIGHT_TRACING&&primary) {\n return vec3f(0.0);\n }\n let globalRadius=bitcast<f32>(photonIndexWord(0u));\n let screenPixels=bitcast<f32>(photonIndexWord(2u));\n if(!(globalRadius>0.0)||bitcast<f32>(photonIndexWord(3u))==1.0||(bitcast<f32>(photonIndexWord(3u))==2.0&&visible)) {\n return vec3f(0.0);\n }\n var radius=globalRadius;\n let experiments=photonIndexWord(7u);\n let deepPixels=f32(experiments>>8u);\n let queryPixels=select(deepPixels,screenPixels,visible);\n if(queryPixels>0.0) {\n radius=min(radius,photonPixelFootprint(si)*queryPixels);\n }\n let layer=select(0u,1u,visible&&screenPixels>0.0);\n let cellSize=select(globalRadius,2.0*bitcast<f32>(photonIndexWord(6u)),layer==1u);\n let firstCell=vec3i(floor((si.position-vec3f(radius))/cellSize));\n let lastCell=vec3i(floor((si.position+vec3f(radius))/cellSize));\n let buckets=photonIndexWord(5u);\n let headerStart=8u+layer*buckets*2u;\n let indexStart=8u+buckets*4u+layer*photonIndexWord(4u);\n let emitted=f32(photonIndexWord(4u));\n var sum=vec3f(0.0);\n for(var z=firstCell.z;z<=lastCell.z;z++) {\n for(var y=firstCell.y;y<=lastCell.y;y++) {\n for(var x=firstCell.x;x<=lastCell.x;x++) {\n let queryCell=vec3i(x,y,z);\n // Reject cube corners before loading their photon lists. Padding keeps\n // the bound conservative when cell multiplication rounds in f32.\n let cellMin=vec3f(queryCell)*cellSize;\n let padding=max(vec3f(cellSize*1e-5),abs(cellMin)*1e-6);\n let closest=clamp(si.position,cellMin-padding,cellMin+vec3f(cellSize)+padding);\n if(distance(closest,si.position)>=radius) { continue; }\n let bucket=photonHash(queryCell);\n let offset=photonIndexWord(headerStart+bucket*2u);\n let count=photonIndexWord(headerStart+1u+bucket*2u);\n for(var i=offset;i<offset+count;i++) {\n let index=photonIndexWord(indexStart+i);\n let record=photonLoad(index);\n let position=bitcast<vec3f>(record.xyz);\n // Hash collisions and neighboring buckets never count a photon twice.\n if(any(vec3i(floor(position/cellSize))!=queryCell)||distance(position,si.position)>=radius) {\n continue;\n }\n var kernelRadius=radius;\n if((experiments&1u)!=0u) {\n let scale=bitcast<f32>(photonIndexWord(8u+buckets*4u+u32(emitted)*3u+index));\n kernelRadius=min(radius,globalRadius*scale);\n if(!(kernelRadius>0.0)||distance(position,si.position)>=kernelRadius) { continue; }\n }\n let directions=photonIndexWord(8u+buckets*4u+u32(emitted)*2u+index);\n let normal=photonDecodeOct(vec2f(f32(directions&255u)/255.0,f32((directions>>8u)&127u)/127.0));\n if(dot(normal,si.faceNormal)<0.9) {\n continue;\n }\n if(CAUSTIC_MNEE&&samplingBounce<=CAUSTIC_MNEE_MAX_DEPTH&&(directions&32768u)!=0u&&prepared.material.baseWeight*(1.0-prepared.material.baseMetalness)*(1.0-prepared.material.transmissionWeight)>0.0&&prepared.material.subsurfaceWeight==0.0) {\n continue;\n }\n let incoming=photonDecodeOct(vec2f(f32((directions>>16u)&255u),f32(directions>>24u))/255.0);\n let cosine=abs(dot(si.faceNormal,incoming));\n if(cosine<=0.0) {\n continue;\n }\n var flux=photonDecodeFlux(record.w)/emitted;\n if(SPECTRAL) {\n flux=sampling_spectral_rgb_filter(flux,si.spectralChannel);\n }\n var pdf:f32;\n let response=openpbr_eval_prepared(prepared,incoming,&pdf);\n sum+=response*flux/cosine*(radius*radius)/(kernelRadius*kernelRadius);\n }\n }\n }\n }\n return sum/(PI*radius*radius);\n }\n";
27
+ export declare function photonDirectQuerySource(enabled?: boolean): string;
28
+ /** Evidence-only per-query counters; no extra bindings or public render options. */
29
+ export declare function photonQuerySource(debugGroup?: number, directSubset?: boolean): string;
@@ -0,0 +1,352 @@
1
+ export const PHOTON_BYTES = 20;
2
+ export const PHOTON_GRID_SIZE = 262144;
3
+ export const PHOTON_SCAN_BLOCK = 256;
4
+ export const PHOTON_REFERENCE_COUNT = 2 ** 18;
5
+ /** Internal density A/B, with the same progressive schedule in all modes. */
6
+ export function photonRadius(initial, batch, alpha = 2 / 3, photons = PHOTON_REFERENCE_COUNT, scaling = 'none') {
7
+ const radius = initial * Math.pow(Math.max(1, batch), (alpha - 1) / 2);
8
+ if (scaling === 'none' || photons === PHOTON_REFERENCE_COUNT)
9
+ return radius;
10
+ const exponent = scaling === 'ppm' ? (alpha - 1) / 2 : -1 / 2;
11
+ return radius * Math.pow(Math.max(1, photons) / PHOTON_REFERENCE_COUNT, exponent);
12
+ }
13
+ /** Default photon-map batch: pixel count / 4, capped at 2^18. */
14
+ export function defaultPhotonMapPhotons(width, height, divisor = 4) {
15
+ return Math.min(2 ** 18, Math.max(1, Math.round((width * height) / divisor)));
16
+ }
17
+ /** Power-of-two hash capacity, bounded independently of large photon batches. */
18
+ export function photonBucketCount(photons) {
19
+ return Math.min(PHOTON_GRID_SIZE, 2 ** Math.ceil(Math.log2(Math.max(256, photons * 2))));
20
+ }
21
+ /** Shared texture address contract; payload is stored once, indices twice. */
22
+ export function photonLayout(count, perCasterRadius = false) {
23
+ const buckets = photonBucketCount(count);
24
+ const coarseHeaders = 8;
25
+ const fineHeaders = coarseHeaders + buckets * 2;
26
+ const coarseIndices = fineHeaders + buckets * 2;
27
+ const fineIndices = coarseIndices + count;
28
+ const directions = fineIndices + count;
29
+ return { buckets, coarseHeaders, fineHeaders, coarseIndices, fineIndices, directions, texels: directions + count * (perCasterRadius ? 2 : 1) };
30
+ }
31
+ /** Batch-local hash buckets: count, two-level parallel prefix sum, then parallel scatter. */
32
+ export const photonGridCompute = /* wgsl */ `
33
+ struct Bucket {
34
+ count: atomic<u32>,
35
+ offset: u32,
36
+ cursor: atomic<u32>,
37
+ padding: u32,
38
+ }
39
+ struct Parameters {
40
+ radius: f32,
41
+ count: u32,
42
+ screenPixels: f32,
43
+ skipQuery: f32,
44
+ buckets: u32,
45
+ fineRadius: f32,
46
+ experiments: u32,
47
+ padding2: u32,
48
+ }
49
+ @group(0)@binding(0)var photons:texture_2d<u32>;
50
+ @group(0)@binding(1)var<storage,read_write>grid:array<Bucket>;
51
+ @group(0)@binding(2)var<uniform>parameters:Parameters;
52
+ @group(0)@binding(4)var headers:texture_storage_2d<r32uint,write>;
53
+ fn zv(sF:vec3i)->u32 {
54
+ let p=bitcast<vec3u>(sF);
55
+ return ((p.x*73856093u)^(p.y*19349663u)^(p.z*83492791u))&(parameters.buckets-1u);
56
+ }
57
+ fn vq(p:vec3f,layer:u32)->u32 {
58
+ let radius=select(parameters.radius,parameters.fineRadius,layer==1u);
59
+ let mo=select(radius,2.0*radius,layer==1u);
60
+ return zv(vec3i(floor(p/mo)));
61
+ }
62
+ fn pl(layer:u32)->u32 {
63
+ return layer*(parameters.buckets+parameters.buckets/256u);
64
+ }
65
+ fn fE(i:u32,value:u32) {
66
+ let width=textureDimensions(headers).x;
67
+ textureStore(headers,vec2i(vec2u(i%width,i/width)),vec4u(value));
68
+ }
69
+ fn qs(i:u32)->vec4u {
70
+ let width=textureDimensions(photons).x;
71
+ return textureLoad(photons,vec2i(vec2u(i%width,i/width)),0);
72
+ }
73
+ fn rI(i:u32)->vec3f {
74
+ return bitcast<vec3f>(qs(i).xyz);
75
+ }
76
+ @compute @workgroup_size(64)
77
+ fn count(@builtin(global_invocation_id)id:vec3u) {
78
+ if(id.x+id.y*4194240u>=parameters.count) {
79
+ return;
80
+ }
81
+ let index=id.x+id.y*4194240u;
82
+ if(qs(index).w==0u) {
83
+ return;
84
+ }
85
+ for(var layer=0u;layer<2u;layer++) {
86
+ atomicAdd(&grid[pl(layer)+vq(rI(index),layer)].count,1u);
87
+ }
88
+ }
89
+
90
+ var<workgroup> scan:array<u32,256>;
91
+ fn jA(local:u32,value:u32)->u32 {
92
+ scan[local]=value;
93
+ workgroupBarrier();
94
+ for(var stride=1u;stride<256u;stride*=2u) {
95
+ var previous=0u;
96
+ if(local>=stride) {
97
+ previous=scan[local-stride];
98
+ }
99
+ workgroupBarrier();
100
+ scan[local]+=previous;
101
+ workgroupBarrier();
102
+ }
103
+ return scan[local];
104
+ }
105
+ @compute @workgroup_size(256)
106
+ fn scanBlocks(@builtin(local_invocation_index)local:u32,@builtin(workgroup_id)group:vec3u) {
107
+ let index=pl(group.y)+group.x*256u+local;
108
+ let count=atomicLoad(&grid[index].count);
109
+ let prefix=jA(local,count);
110
+ grid[index].offset=prefix-count;
111
+ if(local==255u) {
112
+ atomicStore(&grid[pl(group.y)+parameters.buckets+group.x].count,prefix);
113
+ }
114
+ }
115
+ @compute @workgroup_size(256)
116
+ fn scanSums(@builtin(local_invocation_index)local:u32,@builtin(workgroup_id)group:vec3u) {
117
+ let blocks=parameters.buckets/256u;
118
+ let chunk=(blocks+255u)/256u;
119
+ let first=local*chunk;
120
+ let start=pl(group.y)+parameters.buckets;
121
+ var count=0u;
122
+ for(var i=first;i<min(first+chunk,blocks);i++) {
123
+ count+=atomicLoad(&grid[start+i].count);
124
+ }
125
+ let prefix=jA(local,count);
126
+ var offset=prefix-count;
127
+ for(var i=first;i<min(first+chunk,blocks);i++) {
128
+ grid[start+i].offset=offset;
129
+ offset+=atomicLoad(&grid[start+i].count);
130
+ }
131
+ if(local==255u&&group.y==0u) {
132
+ fE(0u,bitcast<u32>(parameters.radius));
133
+ fE(1u,prefix);
134
+ fE(2u,bitcast<u32>(parameters.screenPixels));
135
+ fE(3u,bitcast<u32>(parameters.skipQuery));
136
+ fE(4u,parameters.count);
137
+ fE(5u,parameters.buckets);
138
+ fE(6u,bitcast<u32>(parameters.fineRadius));
139
+ fE(7u,parameters.experiments);
140
+ }
141
+ }
142
+ @compute @workgroup_size(64)
143
+ fn scatter(@builtin(global_invocation_id)id:vec3u) {
144
+ let eJ=id.x+id.y*4194240u;
145
+ for(var layer=0u;layer<2u;layer++) {
146
+ let start=pl(layer);
147
+ if(eJ<parameters.buckets) {
148
+ let offset=grid[start+eJ].offset+grid[start+parameters.buckets+eJ/256u].offset;
149
+ fE(8u+layer*parameters.buckets*2u+eJ*2u,offset);
150
+ fE(9u+layer*parameters.buckets*2u+eJ*2u,atomicLoad(&grid[start+eJ].count));
151
+ }
152
+ if(eJ<parameters.count&&qs(eJ).w!=0u) {
153
+ let tU=vq(rI(eJ),layer);
154
+ let index=grid[start+tU].offset+grid[start+parameters.buckets+tU/256u].offset+atomicAdd(&grid[start+tU].cursor,1u);
155
+ fE(8u+parameters.buckets*4u+layer*parameters.count+index,eJ);
156
+ }
157
+ }
158
+ }
159
+ `;
160
+ /** Packed photon payload: position f32 x3, RGBE8 flux, two octahedral directions. */
161
+ export const photonPackingWgsl = /* wgsl */ `
162
+ fn sv(v:vec2f)->vec2f {
163
+ return select(vec2f(-1.0),vec2f(1.0),v>=vec2f(0.0));
164
+ }
165
+ fn qR(n:vec3f)->vec2f {
166
+ let p=n/(abs(n.x)+abs(n.y)+abs(n.z));
167
+ let uv=select(p.xy,(vec2f(1.0)-abs(p.yx))*sv(p.xy),p.z<0.0);
168
+ return uv*.5+.5;
169
+ }
170
+ fn qQ(uv:vec2f)->vec3f {
171
+ let p=uv*2.0-1.0;
172
+ var n=vec3f(p,1.0-abs(p.x)-abs(p.y));
173
+ if(n.z<0.0) {
174
+ n=vec3f((vec2f(1.0)-abs(n.yx))*sv(n.xy),n.z);
175
+ }
176
+ return normalize(n);
177
+ }
178
+
179
+
180
+ fn us(value:vec3f)->u32 {
181
+ let peak=max(max(value.x,value.y),value.z);
182
+ if(!(peak>0.0)) {
183
+ return 0u;
184
+ }
185
+ let exponent=clamp(i32(floor(log2(peak)))+1,-126,127);
186
+ let rgb=vec3u(clamp(round(value*exp2(-f32(exponent))*255.0),vec3f(0.0),vec3f(255.0)));
187
+ return rgb.x|(rgb.y<<8u)|(rgb.z<<16u)|(u32(exponent+127)<<24u);
188
+ }
189
+ fn photonDecodeFlux(value:u32)->vec3f {
190
+ if(value==0u) {
191
+ return vec3f(0.0);
192
+ }
193
+ return vec3f(f32(value&255u),f32((value>>8u)&255u),f32((value>>16u)&255u))*exp2(f32(i32(value>>24u)-127))/255.0;
194
+ }
195
+ `;
196
+ export const photonQueryWgsl = /* wgsl */ `${photonPackingWgsl}
197
+ @group(1)@binding(11)var photonMapData:texture_2d<u32>;
198
+ @group(1)@binding(12)var photonMapGrid:texture_2d<u32>;
199
+ fn photonLoad(i:u32)->vec4u {
200
+ let width=textureDimensions(photonMapData).x;
201
+ return textureLoad(photonMapData,vec2i(vec2u(i%width,i/width)),0);
202
+ }
203
+ fn photonIndexWord(i:u32)->u32 {
204
+ let width=textureDimensions(photonMapGrid).x;
205
+ return textureLoad(photonMapGrid,vec2i(vec2u(i%width,i/width)),0).x;
206
+ }
207
+ fn ym(sF:vec3i)->u32 {
208
+ let p=bitcast<vec3u>(sF);
209
+ return ((p.x*73856093u)^(p.y*19349663u)^(p.z*83492791u))&(photonIndexWord(5u)-1u);
210
+ }
211
+
212
+
213
+
214
+ fn photonPixelFootprint(si:SurfaceInteraction)->f32 {
215
+ let inverse=cameraUniform.viewProjectionInv;
216
+ let a=inverse*vec4f(0.0,0.0,-1.0,1.0);
217
+ let b=inverse*vec4f(2.0/rtUniform.outputSize.x,0.0,-1.0,1.0);
218
+ let origin=a.xyz/a.w;
219
+ let offset=b.xyz/b.w;
220
+ let eye=cameraUniform.viewInv[3].xyz;
221
+ let forward=normalize(cameraUniform.viewInv[2].xyz);
222
+ let depth=abs(dot(si.position-eye,forward));
223
+ let zg=max(abs(dot(origin-eye,forward)),1e-8);
224
+ let perspective=abs(inverse[2].w)>1e-8;
225
+ let footprint=length(offset-origin)*select(1.0,depth/zg,perspective);
226
+ let cosine=abs(dot(si.faceNormal,normalize(si.position-eye)));
227
+ return max(1e-8,footprint/sqrt(max(.0625,cosine)));
228
+ }
229
+ fn sY(si:SurfaceInteraction,prepared:OpenPbrPreparedBsdf,wP:bool,visible:bool)->vec3f {
230
+ if(samplingBounce>CAUSTIC_PHOTON_MAX_DEPTH||isCausticSpecular(prepared.material)||prepared.material.materialType==SHADOW_CATCHER) {
231
+ return vec3f(0.0);
232
+ }
233
+ if(CAUSTIC_LIGHT_TRACING&&wP) {
234
+ return vec3f(0.0);
235
+ }
236
+ let ni=bitcast<f32>(photonIndexWord(0u));
237
+ let screenPixels=bitcast<f32>(photonIndexWord(2u));
238
+ if(!(ni>0.0)||bitcast<f32>(photonIndexWord(3u))==1.0||(bitcast<f32>(photonIndexWord(3u))==2.0&&visible)) {
239
+ return vec3f(0.0);
240
+ }
241
+ var radius=ni;
242
+ let experiments=photonIndexWord(7u);
243
+ let xW=f32(experiments>>8u);
244
+ let uI=select(xW,screenPixels,visible);
245
+ if(uI>0.0) {
246
+ radius=min(radius,photonPixelFootprint(si)*uI);
247
+ }
248
+ let layer=select(0u,1u,visible&&screenPixels>0.0);
249
+ let mo=select(ni,2.0*bitcast<f32>(photonIndexWord(6u)),layer==1u);
250
+ let uf=vec3i(floor((si.position-vec3f(radius))/mo));
251
+ let vB=vec3i(floor((si.position+vec3f(radius))/mo));
252
+ let buckets=photonIndexWord(5u);
253
+ let uE=8u+layer*buckets*2u;
254
+ let indexStart=8u+buckets*4u+layer*photonIndexWord(4u);
255
+ let emitted=f32(photonIndexWord(4u));
256
+ var sum=vec3f(0.0);
257
+ for(var z=uf.z;z<=vB.z;z++) {
258
+ for(var y=uf.y;y<=vB.y;y++) {
259
+ for(var x=uf.x;x<=vB.x;x++) {
260
+ let uw=vec3i(x,y,z);
261
+
262
+
263
+ let wy=vec3f(uw)*mo;
264
+ let padding=max(vec3f(mo*1e-5),abs(wy)*1e-6);
265
+ let pQ=clamp(si.position,wy-padding,wy+vec3f(mo)+padding);
266
+ if(distance(pQ,si.position)>=radius) { continue; }
267
+ let tU=ym(uw);
268
+ let offset=photonIndexWord(uE+tU*2u);
269
+ let count=photonIndexWord(uE+1u+tU*2u);
270
+ for(var i=offset;i<offset+count;i++) {
271
+ let index=photonIndexWord(indexStart+i);
272
+ let record=photonLoad(index);
273
+ let position=bitcast<vec3f>(record.xyz);
274
+
275
+ if(any(vec3i(floor(position/mo))!=uw)||distance(position,si.position)>=radius) {
276
+ continue;
277
+ }
278
+ var kE=radius;
279
+ if((experiments&1u)!=0u) {
280
+ let scale=bitcast<f32>(photonIndexWord(8u+buckets*4u+u32(emitted)*3u+index));
281
+ kE=min(radius,ni*scale);
282
+ if(!(kE>0.0)||distance(position,si.position)>=kE) { continue; }
283
+ }
284
+ let directions=photonIndexWord(8u+buckets*4u+u32(emitted)*2u+index);
285
+ let normal=qQ(vec2f(f32(directions&255u)/255.0,f32((directions>>8u)&127u)/127.0));
286
+ if(dot(normal,si.faceNormal)<0.9) {
287
+ continue;
288
+ }
289
+ if(CAUSTIC_MNEE&&samplingBounce<=CAUSTIC_MNEE_MAX_DEPTH&&(directions&32768u)!=0u&&prepared.material.baseWeight*(1.0-prepared.material.baseMetalness)*(1.0-prepared.material.transmissionWeight)>0.0&&prepared.material.subsurfaceWeight==0.0) {
290
+ continue;
291
+ }
292
+ let iA=qQ(vec2f(f32((directions>>16u)&255u),f32(directions>>24u))/255.0);
293
+ let cosine=abs(dot(si.faceNormal,iA));
294
+ if(cosine<=0.0) {
295
+ continue;
296
+ }
297
+ var flux=photonDecodeFlux(record.w)/emitted;
298
+ if(SPECTRAL) {
299
+ flux=sampling_spectral_rgb_filter(flux,si.spectralChannel);
300
+ }
301
+ var pdf:f32;
302
+ let response=openpbr_eval_prepared(prepared,iA,&pdf);
303
+ sum+=response*flux/cosine*(radius*radius)/(kE*kE);
304
+ }
305
+ }
306
+ }
307
+ }
308
+ return sum/(PI*radius*radius);
309
+ }
310
+ `;
311
+ // Each translated 2x2 tile assigns all four labels once. Conditional on any
312
+ // stored photon, a uniform pixel label accepts it with probability 1/4; scaling
313
+ // accepted flux by four preserves the original expected density estimate.
314
+ // Only the first camera hit participates; specular prefixes and deeper queries
315
+ // keep the original estimator. Disjoint salts consume no sampling dimensions.
316
+ export function photonDirectQuerySource(enabled = false) {
317
+ if (!enabled)
318
+ return photonQueryWgsl;
319
+ return photonQueryWgsl
320
+ .replace('var sum=vec3f(0.0);', `let tb=ak(aC,rtUniform.sequenceSeed^0x91e10da5u);
321
+ let xO=vec2u(cD(tb^0x68bc21ebu),cD(tb^0x967a889bu));
322
+ let uL=aJ+xO;
323
+ let xN=(uL.x&1u)|((uL.y&1u)<<1u);
324
+ var sum=vec3f(0.0);`)
325
+ .replace('let record=photonLoad(index);', `if(wP&&(ak(index,tb^0x02e5be93u)&3u)!=xN) { continue; }
326
+ let record=photonLoad(index);`)
327
+ .replace('var flux=photonDecodeFlux(record.w)/emitted;', `var flux=photonDecodeFlux(record.w)/emitted;
328
+ if(wP) { flux*=4.0; }`);
329
+ }
330
+ /** Evidence-only per-query counters; no extra bindings or public render options. */
331
+ export function photonQuerySource(debugGroup = -1, directSubset = false) {
332
+ const source = photonDirectQuerySource(directSubset);
333
+ if (debugGroup < 50)
334
+ return source;
335
+ // Offline union-of-photons audit. Each depth is replayed with the same seed;
336
+ // payloads are positions/normals rather than radiance, before path throughput.
337
+ if (debugGroup >= 80) {
338
+ const normal = debugGroup >= 130 || debugGroup === 81;
339
+ const visible = debugGroup < 100;
340
+ const depth = debugGroup - (normal ? 130 : 100);
341
+ const selected = visible ? 'visible' : `!visible&&samplingBounce==${depth}u`;
342
+ const payload = debugGroup === 82 ? 'vec3f(photonPixelFootprint(si),radius,1.0)' : normal ? 'si.faceNormal' : 'si.position';
343
+ return 'var<private> photonQueryStats:vec3f;\n' + source.replace('return sum/(PI*radius*radius);', `if(${selected}){photonQueryStats=${payload};}return sum/(PI*radius*radius);`);
344
+ }
345
+ const selected = debugGroup === 50 || debugGroup === 73 ? 'visible' : debugGroup === 72 ? '!visible' : `!visible&&samplingBounce==${debugGroup - 51}u`;
346
+ return 'var<private> photonQueryStats:vec3f;\n' + source
347
+ .replace('var sum=vec3f(0.0);', 'var sum=vec3f(0.0);var reads=0u;var passed=0u;')
348
+ .replace('let index=photonIndexWord(indexStart+i);', 'reads++;let index=photonIndexWord(indexStart+i);')
349
+ .replace(debugGroup === 73 ? 'var flux=photonDecodeFlux' : 'let directions=photonIndexWord', debugGroup === 73 ? 'passed++;var flux=photonDecodeFlux' : 'passed++;let directions=photonIndexWord')
350
+ .replace('return sum/(PI*radius*radius);', `if(${selected}){photonQueryStats${debugGroup === 72 ? '+' : ''}=vec3f(f32(reads),f32(passed),1.0);}return sum/(PI*radius*radius);`);
351
+ }
352
+ //# sourceMappingURL=photonMap.compute.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"photonMap.compute.js","sourceRoot":"","sources":["../../../src/renderer/wgsl/photonMap.compute.ts"],"names":[],"mappings":"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","sourcesContent":["export const PHOTON_BYTES = 20;\nexport const PHOTON_GRID_SIZE = 262144;\nexport const PHOTON_SCAN_BLOCK = 256;\nexport const PHOTON_REFERENCE_COUNT = 2 ** 18;\nexport type PhotonRadiusScaling = 'none' | 'ppm' | 'constant-count';\n/** Internal density A/B, with the same progressive schedule in all modes. */\nexport function photonRadius(\n initial: number,\n batch: number,\n alpha = 2 / 3,\n photons = PHOTON_REFERENCE_COUNT,\n scaling: PhotonRadiusScaling = 'none'\n) {\n const radius = initial * Math.pow(Math.max(1, batch), (alpha - 1) / 2);\n if (scaling === 'none' || photons === PHOTON_REFERENCE_COUNT) return radius;\n const exponent = scaling === 'ppm' ? (alpha - 1) / 2 : -1 / 2;\n return radius * Math.pow(Math.max(1, photons) / PHOTON_REFERENCE_COUNT, exponent);\n}\n/** Default photon-map batch: pixel count / 4, capped at 2^18. */\nexport function defaultPhotonMapPhotons(width: number, height: number, divisor = 4) {\n return Math.min(2 ** 18, Math.max(1, Math.round((width * height) / divisor)));\n}\n/** Power-of-two hash capacity, bounded independently of large photon batches. */\nexport function photonBucketCount(photons: number) {\n return Math.min(PHOTON_GRID_SIZE, 2 ** Math.ceil(Math.log2(Math.max(256, photons * 2))));\n}\n/** Shared texture address contract; payload is stored once, indices twice. */\nexport function photonLayout(count: number, perCasterRadius = false) {\n const buckets = photonBucketCount(count);\n const coarseHeaders = 8;\n const fineHeaders = coarseHeaders + buckets * 2;\n const coarseIndices = fineHeaders + buckets * 2;\n const fineIndices = coarseIndices + count;\n const directions = fineIndices + count;\n return {buckets, coarseHeaders, fineHeaders, coarseIndices, fineIndices, directions, texels: directions + count * (perCasterRadius ? 2 : 1)};\n}\n/** Batch-local hash buckets: count, two-level parallel prefix sum, then parallel scatter. */\nexport const photonGridCompute = /* wgsl */ `\n struct Bucket {\n count: atomic<u32>,\n offset: u32,\n cursor: atomic<u32>,\n padding: u32,\n }\n struct Parameters {\n radius: f32,\n count: u32,\n screenPixels: f32,\n skipQuery: f32,\n buckets: u32,\n fineRadius: f32,\n experiments: u32,\n padding2: u32,\n }\n @group(0)@binding(0)var photons:texture_2d<u32>;\n @group(0)@binding(1)var<storage,read_write>grid:array<Bucket>;\n @group(0)@binding(2)var<uniform>parameters:Parameters;\n @group(0)@binding(4)var headers:texture_storage_2d<r32uint,write>;\n fn hashCell(cell:vec3i)->u32 {\n let p=bitcast<vec3u>(cell);\n return ((p.x*73856093u)^(p.y*19349663u)^(p.z*83492791u))&(parameters.buckets-1u);\n }\n fn hashPhoton(p:vec3f,layer:u32)->u32 {\n let radius=select(parameters.radius,parameters.fineRadius,layer==1u);\n let cellSize=select(radius,2.0*radius,layer==1u);\n return hashCell(vec3i(floor(p/cellSize)));\n }\n fn gridStart(layer:u32)->u32 {\n return layer*(parameters.buckets+parameters.buckets/256u);\n }\n fn storeIndex(i:u32,value:u32) {\n let width=textureDimensions(headers).x;\n textureStore(headers,vec2i(vec2u(i%width,i/width)),vec4u(value));\n }\n fn photonRecord(i:u32)->vec4u {\n let width=textureDimensions(photons).x;\n return textureLoad(photons,vec2i(vec2u(i%width,i/width)),0);\n }\n fn photonPosition(i:u32)->vec3f {\n return bitcast<vec3f>(photonRecord(i).xyz);\n }\n @compute @workgroup_size(64)\n fn count(@builtin(global_invocation_id)id:vec3u) {\n if(id.x+id.y*4194240u>=parameters.count) {\n return;\n }\n let index=id.x+id.y*4194240u;\n if(photonRecord(index).w==0u) {\n return;\n }\n for(var layer=0u;layer<2u;layer++) {\n atomicAdd(&grid[gridStart(layer)+hashPhoton(photonPosition(index),layer)].count,1u);\n }\n }\n // Each block scans 256 buckets; the next workgroup scans chunks of block totals.\n var<workgroup> scan:array<u32,256>;\n fn inclusiveScan(local:u32,value:u32)->u32 {\n scan[local]=value;\n workgroupBarrier();\n for(var stride=1u;stride<256u;stride*=2u) {\n var previous=0u;\n if(local>=stride) {\n previous=scan[local-stride];\n }\n workgroupBarrier();\n scan[local]+=previous;\n workgroupBarrier();\n }\n return scan[local];\n }\n @compute @workgroup_size(256)\n fn scanBlocks(@builtin(local_invocation_index)local:u32,@builtin(workgroup_id)group:vec3u) {\n let index=gridStart(group.y)+group.x*256u+local;\n let count=atomicLoad(&grid[index].count);\n let prefix=inclusiveScan(local,count);\n grid[index].offset=prefix-count;\n if(local==255u) {\n atomicStore(&grid[gridStart(group.y)+parameters.buckets+group.x].count,prefix);\n }\n }\n @compute @workgroup_size(256)\n fn scanSums(@builtin(local_invocation_index)local:u32,@builtin(workgroup_id)group:vec3u) {\n let blocks=parameters.buckets/256u;\n let chunk=(blocks+255u)/256u;\n let first=local*chunk;\n let start=gridStart(group.y)+parameters.buckets;\n var count=0u;\n for(var i=first;i<min(first+chunk,blocks);i++) {\n count+=atomicLoad(&grid[start+i].count);\n }\n let prefix=inclusiveScan(local,count);\n var offset=prefix-count;\n for(var i=first;i<min(first+chunk,blocks);i++) {\n grid[start+i].offset=offset;\n offset+=atomicLoad(&grid[start+i].count);\n }\n if(local==255u&&group.y==0u) {\n storeIndex(0u,bitcast<u32>(parameters.radius));\n storeIndex(1u,prefix);\n storeIndex(2u,bitcast<u32>(parameters.screenPixels));\n storeIndex(3u,bitcast<u32>(parameters.skipQuery));\n storeIndex(4u,parameters.count);\n storeIndex(5u,parameters.buckets);\n storeIndex(6u,bitcast<u32>(parameters.fineRadius));\n storeIndex(7u,parameters.experiments);\n }\n }\n @compute @workgroup_size(64)\n fn scatter(@builtin(global_invocation_id)id:vec3u) {\n let invocation=id.x+id.y*4194240u;\n for(var layer=0u;layer<2u;layer++) {\n let start=gridStart(layer);\n if(invocation<parameters.buckets) {\n let offset=grid[start+invocation].offset+grid[start+parameters.buckets+invocation/256u].offset;\n storeIndex(8u+layer*parameters.buckets*2u+invocation*2u,offset);\n storeIndex(9u+layer*parameters.buckets*2u+invocation*2u,atomicLoad(&grid[start+invocation].count));\n }\n if(invocation<parameters.count&&photonRecord(invocation).w!=0u) {\n let bucket=hashPhoton(photonPosition(invocation),layer);\n let index=grid[start+bucket].offset+grid[start+parameters.buckets+bucket/256u].offset+atomicAdd(&grid[start+bucket].cursor,1u);\n storeIndex(8u+parameters.buckets*4u+layer*parameters.count+index,invocation);\n }\n }\n }\n`;\n/** Packed photon payload: position f32 x3, RGBE8 flux, two octahedral directions. */\nexport const photonPackingWgsl = /* wgsl */ `\n fn photonOctSign(v:vec2f)->vec2f {\n return select(vec2f(-1.0),vec2f(1.0),v>=vec2f(0.0));\n }\n fn photonEncodeOct(n:vec3f)->vec2f {\n let p=n/(abs(n.x)+abs(n.y)+abs(n.z));\n let uv=select(p.xy,(vec2f(1.0)-abs(p.yx))*photonOctSign(p.xy),p.z<0.0);\n return uv*.5+.5;\n }\n fn photonDecodeOct(uv:vec2f)->vec3f {\n let p=uv*2.0-1.0;\n var n=vec3f(p,1.0-abs(p.x)-abs(p.y));\n if(n.z<0.0) {\n n=vec3f((vec2f(1.0)-abs(n.yx))*photonOctSign(n.xy),n.z);\n }\n return normalize(n);\n }\n // An eight-bit exponent avoids RGB9E5's limited range for very dim/bright lights.\n // Flux is stored before dividing by emitted count, preserving tiny large batches.\n fn photonEncodeFlux(value:vec3f)->u32 {\n let peak=max(max(value.x,value.y),value.z);\n if(!(peak>0.0)) {\n return 0u;\n }\n let exponent=clamp(i32(floor(log2(peak)))+1,-126,127);\n let rgb=vec3u(clamp(round(value*exp2(-f32(exponent))*255.0),vec3f(0.0),vec3f(255.0)));\n return rgb.x|(rgb.y<<8u)|(rgb.z<<16u)|(u32(exponent+127)<<24u);\n }\n fn photonDecodeFlux(value:u32)->vec3f {\n if(value==0u) {\n return vec3f(0.0);\n }\n return vec3f(f32(value&255u),f32((value>>8u)&255u),f32((value>>16u)&255u))*exp2(f32(i32(value>>24u)-127))/255.0;\n }\n`;\nexport const photonQueryWgsl = /* wgsl */ `${photonPackingWgsl}\n @group(1)@binding(11)var photonMapData:texture_2d<u32>;\n @group(1)@binding(12)var photonMapGrid:texture_2d<u32>;\n fn photonLoad(i:u32)->vec4u {\n let width=textureDimensions(photonMapData).x;\n return textureLoad(photonMapData,vec2i(vec2u(i%width,i/width)),0);\n }\n fn photonIndexWord(i:u32)->u32 {\n let width=textureDimensions(photonMapGrid).x;\n return textureLoad(photonMapGrid,vec2i(vec2u(i%width,i/width)),0).x;\n }\n fn photonHash(cell:vec3i)->u32 {\n let p=bitcast<vec3u>(cell);\n return ((p.x*73856093u)^(p.y*19349663u)^(p.z*83492791u))&(photonIndexWord(5u)-1u);\n }\n // Approximate a specular prefix by the camera-space depth of its first diffuse\n // receiver. Orthographic footprints do not grow with distance. Grazing surfaces\n // expand the disk conservatively; the progressive world radius remains the cap.\n fn photonPixelFootprint(si:SurfaceInteraction)->f32 {\n let inverse=cameraUniform.viewProjectionInv;\n let a=inverse*vec4f(0.0,0.0,-1.0,1.0);\n let b=inverse*vec4f(2.0/rtUniform.outputSize.x,0.0,-1.0,1.0);\n let origin=a.xyz/a.w;\n let offset=b.xyz/b.w;\n let eye=cameraUniform.viewInv[3].xyz;\n let forward=normalize(cameraUniform.viewInv[2].xyz);\n let depth=abs(dot(si.position-eye,forward));\n let nearDepth=max(abs(dot(origin-eye,forward)),1e-8);\n let perspective=abs(inverse[2].w)>1e-8;\n let footprint=length(offset-origin)*select(1.0,depth/nearDepth,perspective);\n let cosine=abs(dot(si.faceNormal,normalize(si.position-eye)));\n return max(1e-8,footprint/sqrt(max(.0625,cosine)));\n }\n fn photonContribution(si:SurfaceInteraction,prepared:OpenPbrPreparedBsdf,primary:bool,visible:bool)->vec3f {\n if(samplingBounce>CAUSTIC_PHOTON_MAX_DEPTH||isCausticSpecular(prepared.material)||prepared.material.materialType==SHADOW_CATCHER) {\n return vec3f(0.0);\n }\n if(CAUSTIC_LIGHT_TRACING&&primary) {\n return vec3f(0.0);\n }\n let globalRadius=bitcast<f32>(photonIndexWord(0u));\n let screenPixels=bitcast<f32>(photonIndexWord(2u));\n if(!(globalRadius>0.0)||bitcast<f32>(photonIndexWord(3u))==1.0||(bitcast<f32>(photonIndexWord(3u))==2.0&&visible)) {\n return vec3f(0.0);\n }\n var radius=globalRadius;\n let experiments=photonIndexWord(7u);\n let deepPixels=f32(experiments>>8u);\n let queryPixels=select(deepPixels,screenPixels,visible);\n if(queryPixels>0.0) {\n radius=min(radius,photonPixelFootprint(si)*queryPixels);\n }\n let layer=select(0u,1u,visible&&screenPixels>0.0);\n let cellSize=select(globalRadius,2.0*bitcast<f32>(photonIndexWord(6u)),layer==1u);\n let firstCell=vec3i(floor((si.position-vec3f(radius))/cellSize));\n let lastCell=vec3i(floor((si.position+vec3f(radius))/cellSize));\n let buckets=photonIndexWord(5u);\n let headerStart=8u+layer*buckets*2u;\n let indexStart=8u+buckets*4u+layer*photonIndexWord(4u);\n let emitted=f32(photonIndexWord(4u));\n var sum=vec3f(0.0);\n for(var z=firstCell.z;z<=lastCell.z;z++) {\n for(var y=firstCell.y;y<=lastCell.y;y++) {\n for(var x=firstCell.x;x<=lastCell.x;x++) {\n let queryCell=vec3i(x,y,z);\n // Reject cube corners before loading their photon lists. Padding keeps\n // the bound conservative when cell multiplication rounds in f32.\n let cellMin=vec3f(queryCell)*cellSize;\n let padding=max(vec3f(cellSize*1e-5),abs(cellMin)*1e-6);\n let closest=clamp(si.position,cellMin-padding,cellMin+vec3f(cellSize)+padding);\n if(distance(closest,si.position)>=radius) { continue; }\n let bucket=photonHash(queryCell);\n let offset=photonIndexWord(headerStart+bucket*2u);\n let count=photonIndexWord(headerStart+1u+bucket*2u);\n for(var i=offset;i<offset+count;i++) {\n let index=photonIndexWord(indexStart+i);\n let record=photonLoad(index);\n let position=bitcast<vec3f>(record.xyz);\n // Hash collisions and neighboring buckets never count a photon twice.\n if(any(vec3i(floor(position/cellSize))!=queryCell)||distance(position,si.position)>=radius) {\n continue;\n }\n var kernelRadius=radius;\n if((experiments&1u)!=0u) {\n let scale=bitcast<f32>(photonIndexWord(8u+buckets*4u+u32(emitted)*3u+index));\n kernelRadius=min(radius,globalRadius*scale);\n if(!(kernelRadius>0.0)||distance(position,si.position)>=kernelRadius) { continue; }\n }\n let directions=photonIndexWord(8u+buckets*4u+u32(emitted)*2u+index);\n let normal=photonDecodeOct(vec2f(f32(directions&255u)/255.0,f32((directions>>8u)&127u)/127.0));\n if(dot(normal,si.faceNormal)<0.9) {\n continue;\n }\n if(CAUSTIC_MNEE&&samplingBounce<=CAUSTIC_MNEE_MAX_DEPTH&&(directions&32768u)!=0u&&prepared.material.baseWeight*(1.0-prepared.material.baseMetalness)*(1.0-prepared.material.transmissionWeight)>0.0&&prepared.material.subsurfaceWeight==0.0) {\n continue;\n }\n let incoming=photonDecodeOct(vec2f(f32((directions>>16u)&255u),f32(directions>>24u))/255.0);\n let cosine=abs(dot(si.faceNormal,incoming));\n if(cosine<=0.0) {\n continue;\n }\n var flux=photonDecodeFlux(record.w)/emitted;\n if(SPECTRAL) {\n flux=sampling_spectral_rgb_filter(flux,si.spectralChannel);\n }\n var pdf:f32;\n let response=openpbr_eval_prepared(prepared,incoming,&pdf);\n sum+=response*flux/cosine*(radius*radius)/(kernelRadius*kernelRadius);\n }\n }\n }\n }\n return sum/(PI*radius*radius);\n }\n`;\n\n// Each translated 2x2 tile assigns all four labels once. Conditional on any\n// stored photon, a uniform pixel label accepts it with probability 1/4; scaling\n// accepted flux by four preserves the original expected density estimate.\n// Only the first camera hit participates; specular prefixes and deeper queries\n// keep the original estimator. Disjoint salts consume no sampling dimensions.\nexport function photonDirectQuerySource(enabled = false) {\n if (!enabled) return photonQueryWgsl;\n return photonQueryWgsl\n .replace('var sum=vec3f(0.0);', `let subsetSeed=sampleSeed(pixelSampleIndex,rtUniform.sequenceSeed^0x91e10da5u);\n let subsetShift=vec2u(sampleHash(subsetSeed^0x68bc21ebu),sampleHash(subsetSeed^0x967a889bu));\n let subsetPixel=samplingPixel+subsetShift;\n let subsetGroup=(subsetPixel.x&1u)|((subsetPixel.y&1u)<<1u);\n var sum=vec3f(0.0);`)\n .replace('let record=photonLoad(index);', `if(primary&&(sampleSeed(index,subsetSeed^0x02e5be93u)&3u)!=subsetGroup) { continue; }\n let record=photonLoad(index);`)\n .replace('var flux=photonDecodeFlux(record.w)/emitted;', `var flux=photonDecodeFlux(record.w)/emitted;\n if(primary) { flux*=4.0; }`);\n}\n/** Evidence-only per-query counters; no extra bindings or public render options. */\nexport function photonQuerySource(debugGroup = -1, directSubset = false) {\n const source = photonDirectQuerySource(directSubset);\n if (debugGroup < 50) return source;\n // Offline union-of-photons audit. Each depth is replayed with the same seed;\n // payloads are positions/normals rather than radiance, before path throughput.\n if (debugGroup >= 80) {\n const normal = debugGroup >= 130 || debugGroup === 81;\n const visible = debugGroup < 100;\n const depth = debugGroup - (normal ? 130 : 100);\n const selected = visible ? 'visible' : `!visible&&samplingBounce==${depth}u`;\n const payload = debugGroup === 82 ? 'vec3f(photonPixelFootprint(si),radius,1.0)' : normal ? 'si.faceNormal' : 'si.position';\n return 'var<private> photonQueryStats:vec3f;\\n' + source.replace('return sum/(PI*radius*radius);', `if(${selected}){photonQueryStats=${payload};}return sum/(PI*radius*radius);`);\n }\n const selected = debugGroup === 50 || debugGroup === 73 ? 'visible' : debugGroup === 72 ? '!visible' : `!visible&&samplingBounce==${debugGroup - 51}u`;\n return 'var<private> photonQueryStats:vec3f;\\n' + source\n .replace('var sum=vec3f(0.0);', 'var sum=vec3f(0.0);var reads=0u;var passed=0u;')\n .replace('let index=photonIndexWord(indexStart+i);', 'reads++;let index=photonIndexWord(indexStart+i);')\n .replace(debugGroup === 73 ? 'var flux=photonDecodeFlux' : 'let directions=photonIndexWord',\n debugGroup === 73 ? 'passed++;var flux=photonDecodeFlux' : 'passed++;let directions=photonIndexWord')\n .replace('return sum/(PI*radius*radius);', `if(${selected}){photonQueryStats${debugGroup === 72 ? '+' : ''}=vec3f(f32(reads),f32(passed),1.0);}return sum/(PI*radius*radius);`);\n}\n"]}