@meimu/mutracer 0.3.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 (304) 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/THIRD_PARTY_NOTICES.md +18 -4
  5. package/dist/assets/bvh.worker.js +1 -1
  6. package/dist/mutracer.umd.js +6260 -5580
  7. package/docs/api.md +45 -18
  8. package/docs/materials.md +4 -2
  9. package/docs/performance.md +7 -0
  10. package/docs/post-processing.md +62 -9
  11. package/docs/progressive-rendering.md +3 -0
  12. package/docs/sampling.md +4 -0
  13. package/docs/technical/acceleration.md +52 -0
  14. package/docs/technical/aovs.md +79 -0
  15. package/docs/technical/caustics.md +109 -0
  16. package/docs/technical/continuous-spectrum.md +29 -0
  17. package/docs/technical/display-upscale.md +82 -0
  18. package/docs/technical/openpbr.md +99 -0
  19. package/docs/technical/pbrt-import.md +168 -0
  20. package/docs/technical/tiling.md +74 -0
  21. package/docs/technical/webgl.md +53 -0
  22. package/docs/viewer.md +11 -1
  23. package/lib/Display.d.ts +19 -5
  24. package/lib/Display.js +10 -7
  25. package/lib/Display.js.map +1 -1
  26. package/lib/Engine.d.ts +2 -1
  27. package/lib/Engine.js +30 -2
  28. package/lib/Engine.js.map +1 -1
  29. package/lib/Render.d.ts +20 -11
  30. package/lib/Render.js +32 -7
  31. package/lib/Render.js.map +1 -1
  32. package/lib/main.d.ts +3 -1
  33. package/lib/main.js +1 -0
  34. package/lib/main.js.map +1 -1
  35. package/lib/oidn.d.ts +1 -1
  36. package/lib/renderer/AovPrimitiveCache.js +39 -8
  37. package/lib/renderer/AovPrimitiveCache.js.map +1 -1
  38. package/lib/renderer/DisplayPostProcess.d.ts +8 -3
  39. package/lib/renderer/DisplayPostProcess.js +51 -18
  40. package/lib/renderer/DisplayPostProcess.js.map +1 -1
  41. package/lib/renderer/MaterialData.js +7 -2
  42. package/lib/renderer/MaterialData.js.map +1 -1
  43. package/lib/renderer/RenderingPipeline.d.ts +7 -1
  44. package/lib/renderer/RenderingPipeline.js.map +1 -1
  45. package/lib/renderer/TileRender.d.ts +2 -2
  46. package/lib/renderer/TileRender.js +128 -7
  47. package/lib/renderer/TileRender.js.map +1 -1
  48. package/lib/renderer/WGPUAov.d.ts +6 -1
  49. package/lib/renderer/WGPUAov.js +108 -14
  50. package/lib/renderer/WGPUAov.js.map +1 -1
  51. package/lib/renderer/WGPUCanvasSurface.js +12 -1
  52. package/lib/renderer/WGPUCanvasSurface.js.map +1 -1
  53. package/lib/renderer/WGPUEnvMapDistribution.js +19 -19
  54. package/lib/renderer/WGPUFullQuadPass.js +3 -3
  55. package/lib/renderer/WGPURayTraceOutput.js +12 -12
  56. package/lib/renderer/WGPURayTracePass.d.ts +17 -4
  57. package/lib/renderer/WGPURayTracePass.js +194 -71
  58. package/lib/renderer/WGPURayTracePass.js.map +1 -1
  59. package/lib/renderer/WGPURenderingPipeline.js +46 -40
  60. package/lib/renderer/WGPURenderingPipeline.js.map +1 -1
  61. package/lib/renderer/WebGLAov.js +3 -3
  62. package/lib/renderer/WebGLAov.js.map +1 -1
  63. package/lib/renderer/WebGLFullQuadPass.d.ts +1 -1
  64. package/lib/renderer/WebGLFullQuadPass.js +8 -2
  65. package/lib/renderer/WebGLFullQuadPass.js.map +1 -1
  66. package/lib/renderer/WebGLJNLMDenoiser.js +66 -66
  67. package/lib/renderer/WebGLJNLMDenoiser.js.map +1 -1
  68. package/lib/renderer/WebGLRayTraceOutputPass.js +4 -4
  69. package/lib/renderer/WebGLRayTracePass.d.ts +14 -10
  70. package/lib/renderer/WebGLRayTracePass.js +115 -111
  71. package/lib/renderer/WebGLRayTracePass.js.map +1 -1
  72. package/lib/renderer/WebGLRenderingPipeline.js +10 -3
  73. package/lib/renderer/WebGLRenderingPipeline.js.map +1 -1
  74. package/lib/renderer/WebGLSkyBaker.js +57 -57
  75. package/lib/renderer/aov.d.ts +13 -1
  76. package/lib/renderer/aov.js +21 -2
  77. package/lib/renderer/aov.js.map +1 -1
  78. package/lib/renderer/aovDisplay.js +2 -2
  79. package/lib/renderer/aovDisplay.js.map +1 -1
  80. package/lib/renderer/binnedSahBvh.d.ts +2 -1
  81. package/lib/renderer/binnedSahBvh.js +24 -17
  82. package/lib/renderer/binnedSahBvh.js.map +1 -1
  83. package/lib/renderer/bvhAccel.d.ts +3 -1
  84. package/lib/renderer/bvhAccel.js +36 -6
  85. package/lib/renderer/bvhAccel.js.map +1 -1
  86. package/lib/renderer/cameraUniform.d.ts +1 -1
  87. package/lib/renderer/cameraUniform.js +6 -2
  88. package/lib/renderer/cameraUniform.js.map +1 -1
  89. package/lib/renderer/compactVertices.d.ts +14 -0
  90. package/lib/renderer/compactVertices.js +124 -0
  91. package/lib/renderer/compactVertices.js.map +1 -0
  92. package/lib/renderer/easu.d.ts +11 -0
  93. package/lib/renderer/easu.js +156 -0
  94. package/lib/renderer/easu.js.map +1 -0
  95. package/lib/renderer/geometryUpdateState.d.ts +1 -0
  96. package/lib/renderer/geometryUpdateState.js.map +1 -1
  97. package/lib/renderer/geometryUpdates.js +3 -2
  98. package/lib/renderer/geometryUpdates.js.map +1 -1
  99. package/lib/renderer/glsl/aovDisplay.frag.js +5 -5
  100. package/lib/renderer/glsl/aovReflectance.glsl.js +35 -35
  101. package/lib/renderer/glsl/aovReflectanceSamples.glsl.js +1 -1
  102. package/lib/renderer/glsl/aovTrace.frag.js +44 -36
  103. package/lib/renderer/glsl/aovTrace.frag.js.map +1 -1
  104. package/lib/renderer/glsl/chunks/cameraProjection.glsl.d.ts +2 -0
  105. package/lib/renderer/glsl/chunks/cameraProjection.glsl.js +16 -0
  106. package/lib/renderer/glsl/chunks/cameraProjection.glsl.js.map +1 -0
  107. package/lib/renderer/glsl/chunks/cameraRay.glsl.d.ts +1 -1
  108. package/lib/renderer/glsl/chunks/cameraRay.glsl.js +9 -16
  109. package/lib/renderer/glsl/chunks/cameraRay.glsl.js.map +1 -1
  110. package/lib/renderer/glsl/chunks/emissiveLight.glsl.js +8 -8
  111. package/lib/renderer/glsl/chunks/envMap.glsl.js +67 -67
  112. package/lib/renderer/glsl/chunks/intersect.glsl.js +390 -375
  113. package/lib/renderer/glsl/chunks/intersect.glsl.js.map +1 -1
  114. package/lib/renderer/glsl/chunks/materialBuffer.glsl.js +120 -120
  115. package/lib/renderer/glsl/chunks/openPbrBsdf.glsl.js +562 -562
  116. package/lib/renderer/glsl/chunks/rand.glsl.js +3 -3
  117. package/lib/renderer/glsl/chunks/random.glsl.js +4 -4
  118. package/lib/renderer/glsl/chunks/rayTraceCore.glsl.js +37 -35
  119. package/lib/renderer/glsl/chunks/rayTraceCore.glsl.js.map +1 -1
  120. package/lib/renderer/glsl/chunks/sampleLight.glsl.js +70 -74
  121. package/lib/renderer/glsl/chunks/sampleLight.glsl.js.map +1 -1
  122. package/lib/renderer/glsl/chunks/sampleMaterial.glsl.d.ts +1 -1
  123. package/lib/renderer/glsl/chunks/sampleMaterial.glsl.js +107 -88
  124. package/lib/renderer/glsl/chunks/sampleMaterial.glsl.js.map +1 -1
  125. package/lib/renderer/glsl/chunks/sampleShadowCatcher.glsl.js +11 -11
  126. package/lib/renderer/glsl/chunks/sampling.glsl.js +77 -77
  127. package/lib/renderer/glsl/chunks/sobol.glsl.js +70 -70
  128. package/lib/renderer/glsl/chunks/spectralPrefix.glsl.d.ts +6 -0
  129. package/lib/renderer/glsl/chunks/spectralPrefix.glsl.js +47 -0
  130. package/lib/renderer/glsl/chunks/spectralPrefix.glsl.js.map +1 -0
  131. package/lib/renderer/glsl/chunks/textureLinear.glsl.js +5 -5
  132. package/lib/renderer/glsl/chunks/uvProjection.glsl.js +11 -11
  133. package/lib/renderer/glsl/depthQuery.frag.js +12 -6
  134. package/lib/renderer/glsl/depthQuery.frag.js.map +1 -1
  135. package/lib/renderer/glsl/normalizeRayTraceOutput.frag.js +10 -10
  136. package/lib/renderer/glsl/rayTrace.frag.js +233 -170
  137. package/lib/renderer/glsl/rayTrace.frag.js.map +1 -1
  138. package/lib/renderer/glsl/shadowCatcher.frag.js +10 -9
  139. package/lib/renderer/glsl/shadowCatcher.frag.js.map +1 -1
  140. package/lib/renderer/glsl/webglPipeline.frag.js +58 -58
  141. package/lib/renderer/gpuMemory.js +1 -1
  142. package/lib/renderer/gpuMemory.js.map +1 -1
  143. package/lib/renderer/openPbrEnergyLut.d.ts +2 -1
  144. package/lib/renderer/openPbrEnergyLut.js +18 -1
  145. package/lib/renderer/openPbrEnergyLut.js.map +1 -1
  146. package/lib/renderer/pathLimits.d.ts +5 -4
  147. package/lib/renderer/pathLimits.js +6 -5
  148. package/lib/renderer/pathLimits.js.map +1 -1
  149. package/lib/renderer/prepareMeshBVH.d.ts +2 -0
  150. package/lib/renderer/prepareMeshBVH.js +5 -3
  151. package/lib/renderer/prepareMeshBVH.js.map +1 -1
  152. package/lib/renderer/progressiveScheduler.js +1 -1
  153. package/lib/renderer/progressiveScheduler.js.map +1 -1
  154. package/lib/renderer/renderPipeline.d.ts +4 -0
  155. package/lib/renderer/renderPipeline.js +1 -0
  156. package/lib/renderer/renderPipeline.js.map +1 -1
  157. package/lib/renderer/researchTransport.d.ts +1 -1
  158. package/lib/renderer/researchTransport.js.map +1 -1
  159. package/lib/renderer/sceneAssembly.d.ts +1 -1
  160. package/lib/renderer/sceneAssembly.js +4 -3
  161. package/lib/renderer/sceneAssembly.js.map +1 -1
  162. package/lib/renderer/sceneChangeDetector.d.ts +1 -1
  163. package/lib/renderer/sceneChangeDetector.js +3 -1
  164. package/lib/renderer/sceneChangeDetector.js.map +1 -1
  165. package/lib/renderer/sceneLayoutSchema.d.ts +1 -0
  166. package/lib/renderer/sceneLayoutSchema.js +2 -2
  167. package/lib/renderer/sceneLayoutSchema.js.map +1 -1
  168. package/lib/renderer/scenePreparation.d.ts +3 -2
  169. package/lib/renderer/scenePreparation.js +18 -2
  170. package/lib/renderer/scenePreparation.js.map +1 -1
  171. package/lib/renderer/scratchSampling.js +6 -6
  172. package/lib/renderer/spectrum.js +2 -2
  173. package/lib/renderer/staticBvhPreparation.d.ts +9 -0
  174. package/lib/renderer/staticBvhPreparation.js +8 -2
  175. package/lib/renderer/staticBvhPreparation.js.map +1 -1
  176. package/lib/renderer/surfaceHit.d.ts +10 -0
  177. package/lib/renderer/surfaceHit.js +22 -0
  178. package/lib/renderer/surfaceHit.js.map +1 -0
  179. package/lib/renderer/tangentRequirements.d.ts +2 -0
  180. package/lib/renderer/tangentRequirements.js +7 -0
  181. package/lib/renderer/tangentRequirements.js.map +1 -0
  182. package/lib/renderer/textureAtlas.d.ts +4 -0
  183. package/lib/renderer/textureAtlas.js +48 -2
  184. package/lib/renderer/textureAtlas.js.map +1 -1
  185. package/lib/renderer/webgl.js +4 -0
  186. package/lib/renderer/webgl.js.map +1 -1
  187. package/lib/renderer/webglVertexAttributes.d.ts +17 -0
  188. package/lib/renderer/webglVertexAttributes.js +44 -0
  189. package/lib/renderer/webglVertexAttributes.js.map +1 -0
  190. package/lib/renderer/wgpuBindlessSource.js +2 -2
  191. package/lib/renderer/wgpuMaterialConstants.js +4 -3
  192. package/lib/renderer/wgpuMaterialConstants.js.map +1 -1
  193. package/lib/renderer/wgpuSceneUniform.js +2 -2
  194. package/lib/renderer/wgpuSceneUniform.js.map +1 -1
  195. package/lib/renderer/wgpuShaderSource.d.ts +15 -0
  196. package/lib/renderer/wgpuShaderSource.js +1 -1
  197. package/lib/renderer/wgpuShaderSource.js.map +1 -1
  198. package/lib/renderer/wgsl/aovCompact.wgsl.d.ts +4 -0
  199. package/lib/renderer/wgsl/aovCompact.wgsl.js +26 -0
  200. package/lib/renderer/wgsl/aovCompact.wgsl.js.map +1 -0
  201. package/lib/renderer/wgsl/aovDisplay.wgsl.js +9 -9
  202. package/lib/renderer/wgsl/aovReflectance.wgsl.js +39 -39
  203. package/lib/renderer/wgsl/aovReflectanceSamples.wgsl.js +1 -1
  204. package/lib/renderer/wgsl/aovResolve.wgsl.d.ts +4 -0
  205. package/lib/renderer/wgsl/aovResolve.wgsl.js +21 -2
  206. package/lib/renderer/wgsl/aovResolve.wgsl.js.map +1 -1
  207. package/lib/renderer/wgsl/aovSnapshot.wgsl.js +2 -2
  208. package/lib/renderer/wgsl/aovSnapshot.wgsl.js.map +1 -1
  209. package/lib/renderer/wgsl/aovTrace.wgsl.js +31 -31
  210. package/lib/renderer/wgsl/aovTraceDisplay.wgsl.d.ts +2 -0
  211. package/lib/renderer/wgsl/aovTraceDisplay.wgsl.js +56 -0
  212. package/lib/renderer/wgsl/aovTraceDisplay.wgsl.js.map +1 -0
  213. package/lib/renderer/wgsl/bloom.wgsl.js +18 -18
  214. package/lib/renderer/wgsl/causticLight.compute.d.ts +1 -1
  215. package/lib/renderer/wgsl/causticLight.compute.js +121 -119
  216. package/lib/renderer/wgsl/causticLight.compute.js.map +1 -1
  217. package/lib/renderer/wgsl/chunks/background.wgsl.js +46 -46
  218. package/lib/renderer/wgsl/chunks/causticAdjoint.wgsl.js +4 -4
  219. package/lib/renderer/wgsl/chunks/causticAtomic.wgsl.js +3 -3
  220. package/lib/renderer/wgsl/chunks/caustics.wgsl.js +3 -3
  221. package/lib/renderer/wgsl/chunks/common.wgsl.js +4 -4
  222. package/lib/renderer/wgsl/chunks/envMap.wgsl.js +30 -30
  223. package/lib/renderer/wgsl/chunks/intersect.wgsl.d.ts +6 -2
  224. package/lib/renderer/wgsl/chunks/intersect.wgsl.js +550 -542
  225. package/lib/renderer/wgsl/chunks/intersect.wgsl.js.map +1 -1
  226. package/lib/renderer/wgsl/chunks/intersectionAlpha.wgsl.d.ts +1 -1
  227. package/lib/renderer/wgsl/chunks/intersectionAlpha.wgsl.js +18 -16
  228. package/lib/renderer/wgsl/chunks/intersectionAlpha.wgsl.js.map +1 -1
  229. package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.d.ts +2 -1
  230. package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.js +70 -69
  231. package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.js.map +1 -1
  232. package/lib/renderer/wgsl/chunks/openPbrBsdf.wgsl.js +593 -593
  233. package/lib/renderer/wgsl/chunks/pdm_displacement.wgsl.js +118 -118
  234. package/lib/renderer/wgsl/chunks/rand.wgsl.js +3 -3
  235. package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.d.ts +4 -1
  236. package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.js +32 -26
  237. package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.js.map +1 -1
  238. package/lib/renderer/wgsl/chunks/sampleLight.wgsl.js +114 -114
  239. package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.d.ts +1 -1
  240. package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.js +159 -137
  241. package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.js.map +1 -1
  242. package/lib/renderer/wgsl/chunks/sampleShadowCatcher.wgsl.js +8 -8
  243. package/lib/renderer/wgsl/chunks/sampling.wgsl.js +74 -74
  244. package/lib/renderer/wgsl/chunks/sobol.wgsl.js +74 -74
  245. package/lib/renderer/wgsl/chunks/spectralPrefix.wgsl.d.ts +6 -0
  246. package/lib/renderer/wgsl/chunks/spectralPrefix.wgsl.js +66 -0
  247. package/lib/renderer/wgsl/chunks/spectralPrefix.wgsl.js.map +1 -0
  248. package/lib/renderer/wgsl/chunks/uvProjection.wgsl.js +11 -11
  249. package/lib/renderer/wgsl/chunks/vertexAttributes.wgsl.d.ts +2 -0
  250. package/lib/renderer/wgsl/chunks/vertexAttributes.wgsl.js +61 -0
  251. package/lib/renderer/wgsl/chunks/vertexAttributes.wgsl.js.map +1 -0
  252. package/lib/renderer/wgsl/compose.frag.wgsl.d.ts +2 -0
  253. package/lib/renderer/wgsl/compose.frag.wgsl.js +99 -97
  254. package/lib/renderer/wgsl/compose.frag.wgsl.js.map +1 -1
  255. package/lib/renderer/wgsl/customDenoise.wgsl.js +10 -10
  256. package/lib/renderer/wgsl/depthQuery.wgsl.d.ts +1 -1
  257. package/lib/renderer/wgsl/depthQuery.wgsl.js +11 -2
  258. package/lib/renderer/wgsl/depthQuery.wgsl.js.map +1 -1
  259. package/lib/renderer/wgsl/easu.wgsl.d.ts +2 -0
  260. package/lib/renderer/wgsl/easu.wgsl.js +80 -0
  261. package/lib/renderer/wgsl/easu.wgsl.js.map +1 -0
  262. package/lib/renderer/wgsl/easuDetail.wgsl.d.ts +21 -0
  263. package/lib/renderer/wgsl/easuDetail.wgsl.js +230 -0
  264. package/lib/renderer/wgsl/easuDetail.wgsl.js.map +1 -0
  265. package/lib/renderer/wgsl/jnlm.compute.js +74 -74
  266. package/lib/renderer/wgsl/jnlm.compute.js.map +1 -1
  267. package/lib/renderer/wgsl/offlineAdaptive.compute.js +26 -26
  268. package/lib/renderer/wgsl/oidnAuxiliary.wgsl.js +3 -3
  269. package/lib/renderer/wgsl/photonMap.compute.js +89 -89
  270. package/lib/renderer/wgsl/proceduralSky.compute.js +61 -61
  271. package/lib/renderer/wgsl/rayTrace.compute.d.ts +14 -0
  272. package/lib/renderer/wgsl/rayTrace.compute.js +395 -333
  273. package/lib/renderer/wgsl/rayTrace.compute.js.map +1 -1
  274. package/lib/scene/Scene.js.map +1 -1
  275. package/lib/settings/render.d.ts +11 -0
  276. package/lib/settings/render.js +1 -0
  277. package/lib/settings/render.js.map +1 -1
  278. package/lib/util/fromTHREE.js +21 -14
  279. package/lib/util/fromTHREE.js.map +1 -1
  280. package/lib/version.d.ts +1 -1
  281. package/lib/version.js +1 -1
  282. package/lib/version.js.map +1 -1
  283. package/package.json +5 -2
  284. package/lib/oidnBundledWeights.cdn.d.ts +0 -2
  285. package/lib/oidnBundledWeights.cdn.js +0 -5
  286. package/lib/oidnBundledWeights.cdn.js.map +0 -1
  287. package/lib/renderer/bvh.worker.d.ts +0 -1
  288. package/lib/renderer/bvh.worker.js +0 -16
  289. package/lib/renderer/bvh.worker.js.map +0 -1
  290. package/lib/renderer/bvhWorkerBuild.d.ts +0 -13
  291. package/lib/renderer/bvhWorkerBuild.js +0 -17
  292. package/lib/renderer/bvhWorkerBuild.js.map +0 -1
  293. package/lib/renderer/lightCore.d.ts +0 -12
  294. package/lib/renderer/lightCore.js +0 -13
  295. package/lib/renderer/lightCore.js.map +0 -1
  296. package/lib/renderer/materialCore.d.ts +0 -8
  297. package/lib/renderer/materialCore.js +0 -9
  298. package/lib/renderer/materialCore.js.map +0 -1
  299. package/lib/scene/PixelTexture.d.ts +0 -6
  300. package/lib/scene/PixelTexture.js +0 -10
  301. package/lib/scene/PixelTexture.js.map +0 -1
  302. package/lib/util/studioLighting.d.ts +0 -18
  303. package/lib/util/studioLighting.js +0 -36
  304. package/lib/util/studioLighting.js.map +0 -1
@@ -1,3 +1,5 @@
1
+ import { resolveSurfaceHit } from './surfaceHit.js';
2
+ import { compactVertexLayout, packCompactVertices, packTriangleMaterials } from './compactVertices.js';
1
3
  import { causticDiagnostics } from './causticDiagnostics.js';
2
4
  import { WGPUPhotonMap, photonRadius } from './WGPUPhotonMap.js';
3
5
  import { defaultPhotonMapPhotons } from './wgsl/photonMap.compute.js';
@@ -8,10 +10,10 @@ import { causticLightCompute } from './wgsl/causticLight.compute.js';
8
10
  import { causticCasterSpheres, hasStochasticCausticCaster, causticCasterSphere, isCausticSpecular } from './caustics.js';
9
11
  import { AovPrimitiveCache } from './AovPrimitiveCache.js';
10
12
  import { packAovScene } from './aovScene.js';
11
- import { aovMask } from './aov.js';
13
+ import { aovMask, AOV_ACCUMULATED_MASK, AOV_REFLECTANCE_MASK, COMPACT_AOV_KINDS } from './aov.js';
12
14
  import { proceduralEnvironmentPower } from './proceduralEnvironmentPower.js';
13
15
  import { WGPUSkyBaker } from './WGPUSkyBaker.js';
14
- import { initialSceneConstants, sceneAttributeLayout, sceneTraversalConstants, prepareSceneLightSampling, writeTraceLighting, packSceneAnalyticLights, writeTraceQuality, traceSampling, writeTraceSampling } from './sceneAssembly.js';
16
+ import { initialSceneConstants, sceneTraversalConstants, prepareSceneLightSampling, writeTraceLighting, packSceneAnalyticLights, writeTraceQuality, traceSampling, writeTraceSampling } from './sceneAssembly.js';
15
17
  import { rayTraceBindGroupLayouts } from './sceneLayoutSchema.js';
16
18
  import { WGPUBindlessMaterialData } from './WGPUBindlessMaterialData.js';
17
19
  import { WGPUBindlessTextures } from './WGPUBindlessTextures.js';
@@ -22,7 +24,7 @@ import { wgpuShaderSource, wgpuPipelineConstantKeys, WGPU_SHADER_DEFINE_KEYS } f
22
24
  import { makeRayTraceUniform, WGPU_SCENE_BUFFER_MIN_BYTES } from './wgpuSceneUniform.js';
23
25
  import { constants } from './webgl.js';
24
26
  import { MATERIAL_FLOAT_COUNT } from './materialLayoutSchema.js';
25
- import { normalizeBounces } from './pathLimits.js';
27
+ import { normalizeBounces, MAX_PATH_EVENTS_WGPU } from './pathLimits.js';
26
28
  import { updateMeshInstanceColors } from './prepareMeshBVH.js';
27
29
  import { updateTLASInstanceColors } from './bvhAccel.js';
28
30
  import { getMaterialHashKey } from '../scene/openPbrSchema.js';
@@ -35,8 +37,7 @@ import { packCameraUniform } from './cameraUniform.js';
35
37
  import { SHADOW_CATCHER_ACCUMULATION_BYTES } from './wgsl/chunks/shadowCatcherAccumulation.wgsl.js';
36
38
  import { DEPTH_SAMPLE_COUNT, packDepthSampleArea, unpackDepthSamples } from './depthQuery.js';
37
39
  import { MeshMembershipCache } from './meshMembership.js';
38
- import { mergeRefitRanges } from './bvhAccel.js';
39
- import { updatePreparedGeometries, canUpdateGeometries, geometryTopologyChanged, packVertexAttributes } from './geometryUpdates.js';
40
+ import { updatePreparedGeometries, canUpdateGeometries, geometryTopologyChanged, } from './geometryUpdates.js';
40
41
  import { SceneEnvironmentCache, RectLightTextureTracker } from './sceneLightChanges.js';
41
42
  import { MaterialChangeTracker, materialIsEmissive } from './materialChanges.js';
42
43
  import { WGPUEnvMapDistribution } from './WGPUEnvMapDistribution.js';
@@ -51,7 +52,7 @@ import { generateBackgroundMapFromSceneBackground, environmentRotation, backgrou
51
52
  import { WORKGROUP_SIZE } from './wgsl/rayTrace.compute.js';
52
53
  import { envMapDistribution, environmentMapSourcesMatch } from './envMapDistribution.js';
53
54
  import { updateMeshInstances, updateMeshInstanceTransforms } from './prepareMeshBVH.js';
54
- import { buildTLAS, concatBLASBuffers, getTraversalStackSizes, refitBLAS, refitTLAS } from './bvhAccel.js';
55
+ import { buildWebTLAS, getTraversalStackSizes, refitBLAS, refitTLAS } from './bvhAccel.js';
55
56
  import { isTextureDataSource } from '../scene/Texture.js';
56
57
  import { DEFAULT_BEVEL_SAMPLES, DEFAULT_DIRECT_LIGHT_CANDIDATES, normalizeBevelSamples, normalizeDirectLightCandidates } from './RenderingPipeline.js';
57
58
  import { EmissiveTriangleCache, LightChoiceBuffer, estimateEnvironmentPower } from './lightSampling.js';
@@ -271,6 +272,7 @@ class WGPURayTracePass {
271
272
  _clamping;
272
273
  _transmissiveAlphaMin = 1;
273
274
  _viewProjInv = mat4.create();
275
+ _projectionInv = mat4.create();
274
276
  _cameraWorld = mat4.create();
275
277
  _apertureRadius = 0;
276
278
  _focusDistance = 10;
@@ -286,7 +288,8 @@ class WGPURayTracePass {
286
288
  _depthTracePipeline;
287
289
  _depthPipeline;
288
290
  _shaderCache;
289
- _debugBVHBuffer;
291
+ _bvhCPU;
292
+ _bvhDataBytes = 0;
290
293
  // Fold scene invariants before backend inlining; runtime edits use uniforms.
291
294
  _bakePipelineConstants = true;
292
295
  constructor(device, _textureSelection = { requested: 'atlas' }) {
@@ -296,7 +299,7 @@ class WGPURayTracePass {
296
299
  if (_textureSelection.support)
297
300
  this._bindless = new WGPUBindlessTextures(device);
298
301
  this._uniforms = {
299
- cameraUniform: this._uniformBuffer(36 * 4),
302
+ cameraUniform: this._uniformBuffer(52 * 4),
300
303
  rtUniform: this._uniformBuffer(this._rtUniform.buffer.byteLength),
301
304
  analyticLights: this._uniformBuffer(FIXED_LIGHT_COUNT * PIXELS_PER_LIGHT * 4 * 4)
302
305
  };
@@ -558,6 +561,9 @@ class WGPURayTracePass {
558
561
  _updateGPUBuffer(label, gpuBuffer, data, minSize = WGPU_SCENE_BUFFER_MIN_BYTES) {
559
562
  const device = this._device;
560
563
  const size = Math.max(data.byteLength, minSize);
564
+ const limit = Math.min(device.limits?.maxBufferSize ?? Infinity, device.limits?.maxStorageBufferBindingSize ?? Infinity);
565
+ if (size > limit)
566
+ throw new RangeError(`${label} buffer requires ${size} bytes; device storage buffer limit is ${limit} bytes.`);
561
567
  if (gpuBuffer == null || gpuBuffer.size < size) {
562
568
  this._needsUpdateBindGroups = true;
563
569
  if (gpuBuffer) {
@@ -568,10 +574,22 @@ class WGPURayTracePass {
568
574
  size,
569
575
  usage: GPUBufferUsage.STORAGE |
570
576
  GPUBufferUsage.COPY_DST |
571
- GPUBufferUsage.COPY_SRC
577
+ GPUBufferUsage.COPY_SRC,
578
+ // Chromium's mapped-creation IPC payload is limited to a signed 32-bit
579
+ // size even when the adapter supports larger storage buffers.
580
+ mappedAtCreation: size < 0x80000000
572
581
  });
582
+ if (size < 0x80000000) {
583
+ new Uint8Array(gpuBuffer.getMappedRange()).set(new Uint8Array(data.buffer, data.byteOffset, data.byteLength));
584
+ gpuBuffer.unmap();
585
+ return gpuBuffer;
586
+ }
587
+ }
588
+ // Bound each staging allocation/IPC payload for large or reused buffers.
589
+ const chunkBytes = 64 * 1024 * 1024;
590
+ for (let offset = 0; offset < data.byteLength; offset += chunkBytes) {
591
+ this._device.queue.writeBuffer(gpuBuffer, offset, data.buffer, data.byteOffset + offset, Math.min(chunkBytes, data.byteLength - offset));
573
592
  }
574
- this._device.queue.writeBuffer(gpuBuffer, 0, data.buffer);
575
593
  return gpuBuffer;
576
594
  }
577
595
  _uniformBuffer(size) {
@@ -646,6 +664,7 @@ class WGPURayTracePass {
646
664
  this._preparePhotonTarget();
647
665
  const pipelineInputs = JSON.stringify([
648
666
  this._constants,
667
+ Boolean(this._constants.SPECTRAL) && this._shareSpectralPrefix,
649
668
  this._hasDisplacement,
650
669
  this._alphaBlendStrategy,
651
670
  this._blasLayoutMask,
@@ -675,6 +694,7 @@ class WGPURayTracePass {
675
694
  const pipelineConstantsKeys = wgpuPipelineConstantKeys(constants);
676
695
  const defineCode = WGPU_SHADER_DEFINE_KEYS.map((key) => `const ${key} = ${constants[key]};`).join('\n');
677
696
  const structure = {
697
+ shareSpectralPrefix: Boolean(constants.SPECTRAL) && this._shareSpectralPrefix,
678
698
  hasDisplacement: this._hasDisplacement,
679
699
  caustics: this._causticKey,
680
700
  bindless: !!this._bindless,
@@ -756,13 +776,14 @@ class WGPURayTracePass {
756
776
  }
757
777
  /** Snapshots everything compilation needs. The task must not read mutable pass state afterwards. */
758
778
  _startTraceVariant(signature, constants, shader) {
779
+ const shareSpectralPrefix = Boolean(constants.SPECTRAL) && this._shareSpectralPrefix;
759
780
  const bake = this._bakePipelineConstants, bindless = this._bindless;
760
781
  const hasDisplacement = this._hasDisplacement, blasLayoutMask = this._blasLayoutMask;
761
782
  const lightTracing = this._lightTracing, photonMapping = this._photonMapping;
762
783
  const causticKey = this._causticKey, transport = this._transport;
763
784
  const { hasAlpha, hasSceneAlpha, defineCode, pipelineConstantsKeys } = shader;
764
785
  const emitterSource = (mode) => {
765
- const code = causticLightCompute(constants, blasLayoutMask, hasDisplacement, mode, '', causticDiagnostics.fullEmissionRandom !== false);
786
+ const code = causticLightCompute(constants, blasLayoutMask, hasDisplacement, mode, '', causticDiagnostics.fullEmissionRandom !== false, true, true);
766
787
  return bindless ? bindlessShaderSource(code, this._textureSelection.support.enable) : code;
767
788
  };
768
789
  const lightEmitter = lightTracing ? this._causticLight ??= new WGPUCausticLightPass(this._device) : undefined;
@@ -786,13 +807,16 @@ class WGPURayTracePass {
786
807
  startedAt = shaderCompilationNow();
787
808
  // Override values are not part of the WGSL unless they are baked, so a
788
809
  // quality edit can share the module that the driver has already parsed.
789
- const moduleKey = JSON.stringify([causticKey, !!bindless, hasAlpha, hasSceneAlpha, hasDisplacement,
810
+ const moduleKey = JSON.stringify([causticKey, !!bindless, shareSpectralPrefix, Boolean(constants.SPECTRAL), hasAlpha, hasSceneAlpha, hasDisplacement,
790
811
  constants.USE_ANISOTROPY_DIRECTIONS, constants.USE_COAT_NORMAL_FRAMES, blasLayoutMask, defineCode, pipelineConstantsKeys,
791
812
  bake && pipelineConstantsKeys.map((key) => constants[key])]);
792
813
  let source = this._traceModules.get(moduleKey);
793
814
  if (!source) {
794
815
  let code = wgpuShaderSource(constants, {
816
+ maxPathEvents: MAX_PATH_EVENTS_WGPU,
795
817
  hasDisplacement,
818
+ channelIndependentRis: Boolean(constants.SPECTRAL),
819
+ shareSpectralPrefix,
796
820
  hasAlpha,
797
821
  hasSceneAlpha,
798
822
  ...transport?.features,
@@ -805,7 +829,11 @@ class WGPURayTracePass {
805
829
  hasAnisotropyDirections: Boolean(constants.USE_ANISOTROPY_DIRECTIONS),
806
830
  hasCoatNormals: Boolean(constants.USE_COAT_NORMAL_FRAMES),
807
831
  blasLayoutMask,
808
- dynamicEnvironment: true
832
+ cameraProjectionInverse: true,
833
+ perInstanceBounds: true,
834
+ compactVertices: true,
835
+ dynamicEnvironment: true,
836
+ displayAovStorage: true
809
837
  }, bake);
810
838
  if (bindless)
811
839
  code = bindlessShaderSource(code, this._textureSelection.support.enable);
@@ -948,20 +976,21 @@ class WGPURayTracePass {
948
976
  if (index >= 0)
949
977
  this._queuedCompiles.push(...this._queuedCompiles.splice(index, 1));
950
978
  }
951
- _prepareAovPipelines(kinds) {
979
+ _prepareAovPipelines(kinds, compact = false) {
952
980
  const trace = this._rayTracePipeline;
953
981
  if (!trace || !this._depthCompute)
954
982
  return;
955
- const reflectance = (aovMask(kinds) & 24) !== 0;
983
+ const reflectance = (aovMask(kinds) & AOV_REFLECTANCE_MASK) !== 0;
956
984
  let variants = this._aovPipelines.get(trace);
957
985
  if (!variants) {
958
986
  variants = new Map();
959
987
  this._aovPipelines.set(trace, variants);
960
988
  }
961
- let pipelines = variants.get(reflectance);
989
+ const key = Number(reflectance) + Number(compact) * 2;
990
+ let pipelines = variants.get(key);
962
991
  if (!pipelines) {
963
992
  pipelines = {};
964
- variants.set(reflectance, pipelines);
993
+ variants.set(key, pipelines);
965
994
  const pending = pipelines;
966
995
  const textures = this._bindless, groupIndex = textures ? 2 : 3;
967
996
  const output = this._device.createBindGroupLayout({ entries: [
@@ -973,7 +1002,7 @@ class WGPURayTracePass {
973
1002
  ] });
974
1003
  const descriptor = { bindGroupLayouts: Array.from({ length: groupIndex }, (_, i) => trace.getBindGroupLayout(i)).concat(output) };
975
1004
  const layout = this._device.createPipelineLayout(textures ? resourceTableLayout(descriptor) : descriptor);
976
- const compute = { ...this._depthCompute, constants: { ...this._depthCompute.constants, DEPTH_QUERY_MODE: 1, AOV_REFLECTANCE: reflectance ? 1 : 0 } };
1005
+ const compute = { ...this._depthCompute, constants: { ...this._depthCompute.constants, DEPTH_QUERY_MODE: 1, AOV_REFLECTANCE: reflectance ? 1 : 0, AOV_COMPACT: compact ? 1 : 0 } };
977
1006
  // Keep the UI responsive during first use, and reuse both variants across
978
1007
  // preview/final target changes rather than recreating synchronous pipelines.
979
1008
  void Promise.all([
@@ -985,30 +1014,33 @@ class WGPURayTracePass {
985
1014
  throw pipelines.error;
986
1015
  return pipelines;
987
1016
  }
988
- prepareAovs(kinds) {
989
- const pipelines = this._prepareAovPipelines(kinds);
1017
+ prepareAovs(kinds, compact = false) {
1018
+ const pipelines = this._prepareAovPipelines(kinds, compact);
990
1019
  return !!pipelines?.accumulation && !!pipelines.center;
991
1020
  }
992
- renderAovs(state, camera, samples, cap, kinds) {
1021
+ renderAovs(state, camera, samples, cap, kinds, resolution) {
993
1022
  const groups = this._rayTraceBindGroups;
994
1023
  if (!groups || !this._tlas || !this._preparedMeshData || !kinds.length)
995
1024
  return false;
996
- const pipelines = this._prepareAovPipelines(kinds);
1025
+ const compact = !!resolution && kinds.every(kind => COMPACT_AOV_KINDS.includes(kind));
1026
+ const pipelines = this._prepareAovPipelines(kinds, compact);
997
1027
  if (!pipelines?.accumulation || !pipelines.center)
998
1028
  return false;
999
- state.resize(this._width, this._height);
1029
+ state.resize(resolution?.width ?? this._width, resolution?.height ?? this._height, compact);
1000
1030
  const textures = this._bindless, groupIndex = textures ? 2 : 3;
1001
1031
  const target = Math.min(samples, cap);
1002
1032
  if (state.centerReady && state.samples >= target)
1003
1033
  return true;
1004
1034
  if (!state.frame) {
1005
- const primitiveIds = this._aovPrimitiveCache.get(this._preparedMeshData, this._tlas);
1006
- const packed = packAovScene(this._preparedMeshData, this._tlas, camera, this._width, this._height, state.history, primitiveIds, false);
1035
+ // Only the center pass reads primitive IDs and compact display AOVs skip
1036
+ // it; building the map walks every BVH leaf on the main thread.
1037
+ const primitiveIds = compact ? undefined : this._aovPrimitiveCache.get(this._preparedMeshData, this._tlas);
1038
+ const packed = packAovScene(this._preparedMeshData, this._tlas, camera, state.width, state.height, state.history, primitiveIds, false);
1007
1039
  state.frame = packed.frame;
1008
1040
  state.setScene(packed.words, primitiveIds);
1009
1041
  }
1010
1042
  this._device.queue.writeBuffer(this._uniforms.rtUniform, 0, this._rtUniform.buffer);
1011
- this._device.queue.writeBuffer(this._uniforms.cameraUniform, 0, packCameraUniform(this._viewProjInv, this._cameraWorld, this._apertureRadius, this._focusDistance));
1043
+ this._device.queue.writeBuffer(this._uniforms.cameraUniform, 0, packCameraUniform(this._viewProjInv, this._cameraWorld, this._apertureRadius, this._focusDistance, this._projectionInv));
1012
1044
  const device = this._device;
1013
1045
  const run = (pipeline, sample) => {
1014
1046
  // A fresh uniform for each queued dispatch keeps batch samples independent.
@@ -1032,11 +1064,12 @@ class WGPURayTracePass {
1032
1064
  parameters.destroy();
1033
1065
  };
1034
1066
  if (!state.centerReady) {
1035
- run(pipelines.center, 0);
1067
+ if (!compact)
1068
+ run(pipelines.center, 0);
1036
1069
  state.centerReady = true;
1037
1070
  state.history.accept(state.frame);
1038
1071
  }
1039
- if ((aovMask(kinds) & 31) === 0)
1072
+ if ((aovMask(kinds) & AOV_ACCUMULATED_MASK) === 0)
1040
1073
  state.samples = target;
1041
1074
  while (state.samples < target) {
1042
1075
  run(pipelines.accumulation, state.samples);
@@ -1044,13 +1077,27 @@ class WGPURayTracePass {
1044
1077
  }
1045
1078
  return true;
1046
1079
  }
1047
- async readDepthSamples(area, aspect) {
1080
+ async pickSurface(x, y, aspect) {
1081
+ const source = this._preparedMeshData, tlas = this._tlas;
1082
+ if (!source || !tlas)
1083
+ return;
1084
+ const values = await this._readDepthSamples({ x, y, size: 0 }, aspect, true);
1085
+ if (!values || source !== this._preparedMeshData || tlas !== this._tlas)
1086
+ return;
1087
+ return resolveSurfaceHit(source, tlas, values);
1088
+ }
1089
+ readDepthSamples(area, aspect) {
1090
+ return this._readDepthSamples(area, aspect);
1091
+ }
1092
+ async _readDepthSamples(area, aspect, picking = false) {
1048
1093
  const tracePipeline = this._rayTracePipeline;
1049
1094
  const groups = this._rayTraceBindGroups;
1050
1095
  const textures = this._bindless, textureRevision = textures?.revision;
1051
1096
  if (!tracePipeline || !groups || !this._depthCompute || this.isCompilingShader())
1052
1097
  return;
1053
1098
  const packed = packDepthSampleArea(area, aspect ?? this._width / this._height);
1099
+ if (picking)
1100
+ packed.set([-1, -1], 2);
1054
1101
  if (this._depthTracePipeline !== tracePipeline || !this._depthPipeline) {
1055
1102
  const outputLayout = this._shaderCache.createBindGroupLayout({
1056
1103
  label: 'Depth query output',
@@ -1102,7 +1149,8 @@ class WGPURayTracePass {
1102
1149
  encoder.copyBufferToBuffer(output, 0, readback, 0, bytes);
1103
1150
  device.queue.submit([encoder.finish()]);
1104
1151
  await readback.mapAsync(GPUMapMode.READ);
1105
- return unpackDepthSamples(new Float32Array(readback.getMappedRange()));
1152
+ const rgba = new Float32Array(readback.getMappedRange());
1153
+ return picking ? rgba.slice(0, 4) : unpackDepthSamples(rgba);
1106
1154
  }
1107
1155
  finally {
1108
1156
  if (readback.mapState === 'mapped')
@@ -1333,12 +1381,12 @@ class WGPURayTracePass {
1333
1381
  return (causticBatch ? 1 : 0) | (photonBatch ? 2 : 0);
1334
1382
  }
1335
1383
  async getDebugBVHData() {
1336
- if (!this._debugBVHBuffer) {
1337
- throw new Error(`software-bvh exposes no CPU-readable BVH debug buffer`);
1338
- }
1339
- return {
1340
- buffer: this._debugBVHBuffer
1341
- };
1384
+ if (!this._bvhCPU)
1385
+ throw new Error('software-bvh exposes no CPU-readable BVH debug buffer');
1386
+ // Debug capture pays for a combined copy only when explicitly requested.
1387
+ const buffer = new ArrayBuffer(this._bvhDataBytes);
1388
+ this._writeBVH(buffer, this._bvhCPU.tlas, this._bvhCPU.blases);
1389
+ return { buffer };
1342
1390
  }
1343
1391
  getDiagnostics() {
1344
1392
  const atlas = this._materialData instanceof MaterialTable ? this._materialData.textureAtlasStats : undefined;
@@ -1379,7 +1427,7 @@ class WGPURayTracePass {
1379
1427
  return this._apertureRadius > 0;
1380
1428
  }
1381
1429
  setBounce(bounce) {
1382
- const next = normalizeBounces(bounce);
1430
+ const next = normalizeBounces(bounce, MAX_PATH_EVENTS_WGPU);
1383
1431
  const changed = this._bounces !== next;
1384
1432
  this._bounces = next;
1385
1433
  this._rtUniform.views.bounces[0] = next;
@@ -1435,9 +1483,11 @@ class WGPURayTracePass {
1435
1483
  this._ensureAdaptiveSamplingTexture(width, height);
1436
1484
  this._device.queue.writeTexture({ texture: this._adaptiveSamplingTexture }, data, { bytesPerRow: width * Float32Array.BYTES_PER_ELEMENT }, [width, height]);
1437
1485
  }
1486
+ _shareSpectralPrefix = true;
1438
1487
  setQuality(options) {
1488
+ this._shareSpectralPrefix = options.dispersionWavelengths !== 1;
1439
1489
  WGPURayTracePass.prototype._setCaustics.call(this, options.caustics);
1440
- writeTraceQuality(this._rtUniform.views, options);
1490
+ writeTraceQuality(this._rtUniform.views, options, MAX_PATH_EVENTS_WGPU);
1441
1491
  }
1442
1492
  _setCaustics(settings) {
1443
1493
  this._causticSettings = settings;
@@ -1870,22 +1920,54 @@ class WGPURayTracePass {
1870
1920
  const stacks = getTraversalStackSizes(tlas, blases);
1871
1921
  this._constants.TLAS_STACK_SIZE = retainedShaderCapacity(stacks.tlas, Number(this._constants.TLAS_STACK_SIZE ?? 0));
1872
1922
  this._constants.BLAS_STACK_SIZE = retainedShaderCapacity(stacks.blas, Number(this._constants.BLAS_STACK_SIZE ?? 0), 32);
1873
- const tlasBuffer = new Float32Array(tlas.buffer);
1874
- const blasBuffer = new Float32Array(concatBLASBuffers(blases));
1875
- const bvhData = new Float32Array(tlasBuffer.length + blasBuffer.length);
1876
- const blasOffset = tlasBuffer.length / 4;
1877
- bvhData.set(tlasBuffer);
1878
- bvhData.set(blasBuffer, blasOffset * 4);
1879
- const bvhBuffer = this._updateGPUBuffer('BVH', this._bvhBuffer, bvhData);
1923
+ const size = tlas.buffer.byteLength + blases.reduce((sum, b) => sum + b.bvh.buffer.byteLength, 0);
1924
+ const limit = Math.min(this._device.limits.maxBufferSize, this._device.limits.maxStorageBufferBindingSize);
1925
+ if (size > limit)
1926
+ throw new RangeError(`BVH buffer requires ${size} bytes; device storage buffer limit is ${limit} bytes.`);
1927
+ let bvhBuffer = this._bvhBuffer;
1928
+ if (!bvhBuffer || bvhBuffer.size < Math.max(size, WGPU_SCENE_BUFFER_MIN_BYTES)) {
1929
+ bvhBuffer?.destroy();
1930
+ bvhBuffer = this._device.createBuffer({ label: 'BVH', size: Math.max(size, WGPU_SCENE_BUFFER_MIN_BYTES),
1931
+ usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC, mappedAtCreation: size < 0x80000000 });
1932
+ if (size < 0x80000000) {
1933
+ this._writeBVH(bvhBuffer.getMappedRange(), tlas, blases);
1934
+ bvhBuffer.unmap();
1935
+ }
1936
+ else
1937
+ this._uploadBVHBlocks(bvhBuffer, tlas, blases);
1938
+ }
1939
+ else
1940
+ this._uploadBVHBlocks(bvhBuffer, tlas, blases);
1941
+ this._bvhCPU = { tlas, blases };
1942
+ this._bvhDataBytes = size;
1943
+ const blasOffset = tlas.buffer.byteLength / 16;
1880
1944
  const instancesBuffer = this._updateGPUBuffer('Instances', this._instancesBuffer, new Float32Array(tlas.instancesBuffer));
1881
- if (bvhBuffer !== this._bvhBuffer || instancesBuffer !== this._instancesBuffer) {
1945
+ if (bvhBuffer !== this._bvhBuffer || instancesBuffer !== this._instancesBuffer)
1882
1946
  this._needsUpdateBindGroups = true;
1883
- }
1884
1947
  this._bvhBuffer = bvhBuffer;
1885
1948
  this._instancesBuffer = instancesBuffer;
1886
- this._debugBVHBuffer = bvhData.buffer.slice(0);
1887
1949
  this._rtUniform.views.blasOffset.set([blasOffset]);
1888
1950
  }
1951
+ _uploadBVHBlocks(buffer, tlas, blases) {
1952
+ this._device.queue.writeBuffer(buffer, 0, tlas.buffer);
1953
+ let target = tlas.buffer.byteLength;
1954
+ for (const blas of blases) {
1955
+ const chunkBytes = 64 * 1024 * 1024;
1956
+ for (let offset = 0; offset < blas.bvh.buffer.byteLength; offset += chunkBytes) {
1957
+ this._device.queue.writeBuffer(buffer, target + offset, blas.bvh.buffer, offset, Math.min(chunkBytes, blas.bvh.buffer.byteLength - offset));
1958
+ }
1959
+ target += blas.bvh.buffer.byteLength;
1960
+ }
1961
+ }
1962
+ _writeBVH(buffer, tlas, blases) {
1963
+ const bytes = new Uint8Array(buffer);
1964
+ bytes.set(new Uint8Array(tlas.buffer));
1965
+ let offset = tlas.buffer.byteLength;
1966
+ for (const blas of blases) {
1967
+ bytes.set(new Uint8Array(blas.bvh.buffer), offset);
1968
+ offset += blas.bvh.buffer.byteLength;
1969
+ }
1970
+ }
1889
1971
  _updateMeshData() {
1890
1972
  this._pendingGeometryUpdates.clear();
1891
1973
  this._emissiveCache = new EmissiveTriangleCache();
@@ -1900,7 +1982,7 @@ class WGPURayTracePass {
1900
1982
  }
1901
1983
  this._geometriesNeedRefit.clear();
1902
1984
  // TODO check bvh needs to change
1903
- const res = updateMeshInstances(meshes);
1985
+ const res = updateMeshInstances(meshes, { mergeAttributes: false });
1904
1986
  this._preparedMeshData = res;
1905
1987
  this._membershipCache.capture(res);
1906
1988
  this._pendingMembershipData = undefined;
@@ -1908,7 +1990,7 @@ class WGPURayTracePass {
1908
1990
  const mergedGeometry = res.geometry;
1909
1991
  const materials = res.materials;
1910
1992
  const isEmpty = meshes.length === 0 || res.instances.length === 0;
1911
- const tlas = buildTLAS(res.instances, res.blases);
1993
+ const tlas = buildWebTLAS(res.instances, res.blases);
1912
1994
  this._tlas = tlas;
1913
1995
  this._uploadBVH(tlas, res.blases);
1914
1996
  const materialData = (this._materialData = this._createMaterialData(materials, this._analyticLights, {
@@ -1918,13 +2000,23 @@ class WGPURayTracePass {
1918
2000
  }));
1919
2001
  this._emissiveTriangles = this._emissiveCache.collect(res.emissiveBindings, materials);
1920
2002
  this._needsUpdateLightSamplingData = true;
1921
- const attributeLayout = sceneAttributeLayout(mergedGeometry);
2003
+ // Displacement geometry and its CPU BVH bounds must use the same normal.
2004
+ // Preserve f32 normals in these scenes, avoiding quantization outside bounds.
2005
+ const attributeLayout = compactVertexLayout(res.geometries, materials.some(m => !!m.displacementMap));
1922
2006
  const { stride, offsets } = attributeLayout;
1923
- const attrBufferData = new F32(mergedGeometry.vertexCount * stride);
1924
- packVertexAttributes(mergedGeometry, attrBufferData, stride, offsets, 0, mergedGeometry.vertexCount);
2007
+ const vertexWords = res.geometries.reduce((sum, g) => sum + g.vertexCount, 0) * stride;
2008
+ const materialWords = attributeLayout.constants.HAS_TRIANGLE_MATERIAL ? this._bvhDataBytes / 16 : 0;
2009
+ const attrBufferData = new Uint32Array(vertexWords + materialWords);
2010
+ attributeLayout.constants.MATERIALINDEX_OFF = vertexWords;
2011
+ for (const geometry of res.geometries) {
2012
+ const region = res.geometryRegions.get(geometry);
2013
+ packCompactVertices(geometry, attrBufferData, stride, offsets, 0, geometry.vertexCount, region.vertexOffset, false, !!attributeLayout.constants.COLOR_FLOAT, !!attributeLayout.constants.NORMAL_FLOAT);
2014
+ }
1925
2015
  this._attributeData = attrBufferData;
1926
2016
  this._attributeStride = stride;
1927
2017
  this._attributeOffsets = offsets;
2018
+ Object.assign(this._constants, attributeLayout.constants);
2019
+ this._packTriangleMaterials(res, tlas);
1928
2020
  this._attrBuffers = this._updateGPUBuffer('Attributes', this._attrBuffers, attrBufferData);
1929
2021
  this._materialsBuffer = this._updateGPUBuffer('Materials', this._materialsBuffer, materialData.getBufferArray(),
1930
2022
  // WGSL requires a runtime array binding to contain at least one
@@ -1971,11 +2063,47 @@ class WGPURayTracePass {
1971
2063
  if (this._bindless)
1972
2064
  this._constants.NUM_MAPS = 20;
1973
2065
  }
2066
+ _packTriangleMaterials(res, tlas) {
2067
+ if (!this._constants.HAS_TRIANGLE_MATERIAL)
2068
+ return;
2069
+ let offset = Number(this._constants.MATERIALINDEX_OFF) + tlas.buffer.byteLength / 16;
2070
+ res.geometries.forEach((g, i) => {
2071
+ packTriangleMaterials(g, res.geometryRegions.get(g).materialIndices, res.blases[i].bvh.buffer, this._attributeData, offset);
2072
+ offset += res.blases[i].bvh.buffer.byteLength / 16;
2073
+ });
2074
+ }
2075
+ _refreshTriangleMaterials(res, tlas) {
2076
+ if (!this._constants.HAS_TRIANGLE_MATERIAL)
2077
+ return;
2078
+ const vertexWords = Number(this._constants.MATERIALINDEX_OFF);
2079
+ const required = vertexWords + this._bvhDataBytes / 16;
2080
+ if (this._attributeData.length !== required) {
2081
+ const next = new Uint32Array(required);
2082
+ next.set(this._attributeData.subarray(0, vertexWords));
2083
+ this._attributeData = next;
2084
+ }
2085
+ this._packTriangleMaterials(res, tlas);
2086
+ if (this._attrBuffers.size < this._attributeData.byteLength) {
2087
+ this._attrBuffers = this._updateGPUBuffer('Attributes', this._attrBuffers, this._attributeData);
2088
+ }
2089
+ else {
2090
+ const first = vertexWords * 4, end = this._attributeData.byteLength, chunk = 64 * 1024 * 1024;
2091
+ for (let offset = first; offset < end; offset += chunk) {
2092
+ this._device.queue.writeBuffer(this._attrBuffers, offset, this._attributeData.buffer, offset, Math.min(chunk, end - offset));
2093
+ }
2094
+ }
2095
+ }
1974
2096
  _updateGeometryData() {
1975
2097
  const res = this._preparedMeshData, tlas = this._tlas;
2098
+ // Promote the scene color plane if an edit introduces HDR/negative RGB.
2099
+ if (!this._constants.COLOR_FLOAT && [...this._pendingGeometryUpdates].some(g => g.color?.array.some(value => !(value >= 0 && value <= 1)))) {
2100
+ this._updateMeshData();
2101
+ return;
2102
+ }
1976
2103
  const update = updatePreparedGeometries(res, this._pendingGeometryUpdates);
1977
- for (const { first, count, topologyChanged, deformationOnly } of update.vertices) {
1978
- packVertexAttributes(res.geometry, this._attributeData, this._attributeStride, this._attributeOffsets, first, count, topologyChanged, deformationOnly);
2104
+ for (const { first, count, deformationOnly } of update.vertices) {
2105
+ const geometry = res.geometries.find(g => res.geometryRegions.get(g).vertexOffset === first);
2106
+ packCompactVertices(geometry, this._attributeData, this._attributeStride, this._attributeOffsets, 0, count, first, deformationOnly, !!this._constants.COLOR_FLOAT, !!this._constants.NORMAL_FLOAT);
1979
2107
  const offset = first * this._attributeStride * 4, size = count * this._attributeStride * 4;
1980
2108
  this._device.queue.writeBuffer(this._attrBuffers, offset, this._attributeData.buffer, offset, size);
1981
2109
  }
@@ -1991,17 +2119,13 @@ class WGPURayTracePass {
1991
2119
  this._uploadBVH(tlas, res.blases);
1992
2120
  }
1993
2121
  else {
1994
- const bytes = new Uint8Array(this._debugBVHBuffer);
1995
- const ranges = [];
1996
2122
  for (const { blas, offset } of update.blases) {
1997
- const target = tlas.buffer.byteLength + offset;
1998
- bytes.set(new Uint8Array(blas.buffer), target);
1999
- ranges.push({ offset: target, size: blas.buffer.byteLength });
2000
- }
2001
- for (const { offset, size } of mergeRefitRanges(ranges)) {
2002
- this._device.queue.writeBuffer(this._bvhBuffer, offset, this._debugBVHBuffer, offset, size);
2123
+ this._device.queue.writeBuffer(this._bvhBuffer, tlas.buffer.byteLength + offset, blas.buffer);
2003
2124
  }
2004
2125
  }
2126
+ if (this._constants.HAS_TRIANGLE_MATERIAL && (update.relocated || update.vertices.some(v => v.topologyChanged))) {
2127
+ this._refreshTriangleMaterials(res, tlas);
2128
+ }
2005
2129
  const bits = { bvh2: 1, bvh4: 2, 'bvh-gpu': 4, cwbvh: 8 };
2006
2130
  this._blasLayoutMask = res.blases.reduce((mask, b) => mask | bits[b.bvh.layout ?? 'bvh2'], 0);
2007
2131
  const stacks = getTraversalStackSizes(tlas, res.blases);
@@ -2061,9 +2185,10 @@ class WGPURayTracePass {
2061
2185
  if (pending)
2062
2186
  this._residentPreparedMeshData = res;
2063
2187
  this._pendingMembershipData = undefined;
2064
- const tlas = buildTLAS(instances, res.blases);
2188
+ const tlas = buildWebTLAS(instances, res.blases);
2065
2189
  this._tlas = tlas;
2066
2190
  this._uploadBVH(tlas, res.blases);
2191
+ this._refreshTriangleMaterials(res, tlas);
2067
2192
  this._emissiveTriangles = this._emissiveCache.collect(emissiveBindings, res.materials);
2068
2193
  this._needsUpdateLightSamplingData = true;
2069
2194
  this._rtUniform.views.isEmpty[0] = +(instances.length === 0);
@@ -2097,8 +2222,9 @@ class WGPURayTracePass {
2097
2222
  const previousInstances = res.instances;
2098
2223
  res.instances = updateMeshInstanceTransforms(res.instanceBindings, incremental ? res.instances : undefined, incremental ? dirtyIndices : undefined);
2099
2224
  if (!sameInstanceMembership(previousInstances, res.instances)) {
2100
- this._tlas = buildTLAS(res.instances, res.blases);
2225
+ this._tlas = buildWebTLAS(res.instances, res.blases);
2101
2226
  this._uploadBVH(this._tlas, res.blases);
2227
+ this._refreshTriangleMaterials(res, this._tlas);
2102
2228
  this._emissiveTriangles = this._emissiveCache.collect(res.emissiveBindings, res.materials);
2103
2229
  this._needsUpdateLightSamplingData = true;
2104
2230
  this._rtUniform.views.isEmpty[0] = +(res.instances.length === 0);
@@ -2107,16 +2233,12 @@ class WGPURayTracePass {
2107
2233
  return;
2108
2234
  }
2109
2235
  const ranges = refitTLAS(tlas, res.instances, res.blases, incremental ? dirtyIndices : undefined);
2110
- const tlasData = new Float32Array(tlas.buffer);
2111
2236
  for (const { offset, size } of ranges.tlasRanges) {
2112
2237
  this._device.queue.writeBuffer(this._bvhBuffer, offset, tlas.buffer, offset, size);
2113
2238
  }
2114
2239
  for (const { offset, size } of ranges.instanceRanges) {
2115
2240
  this._device.queue.writeBuffer(this._instancesBuffer, offset, tlas.instancesBuffer, offset, size);
2116
2241
  }
2117
- if (this._debugBVHBuffer) {
2118
- new Float32Array(this._debugBVHBuffer).set(tlasData);
2119
- }
2120
2242
  if (!incremental ||
2121
2243
  res.emissiveBindings.some(({ mesh }) => this._dirtyTransformMeshes.has(mesh))) {
2122
2244
  this._emissiveTriangles = this._emissiveCache.collect(res.emissiveBindings, res.materials);
@@ -2254,7 +2376,8 @@ class WGPURayTracePass {
2254
2376
  const viewProjectionInvMat = this._viewProjInv;
2255
2377
  this._apertureRadius = cameraApertureRadius(camera);
2256
2378
  this._focusDistance = camera.focusDistance;
2257
- this._updateViewProjInv(camera, viewProjectionInvMat);
2379
+ mat4.invert(this._projectionInv, projection);
2380
+ mat4.mul(viewProjectionInvMat, camera.transform, this._projectionInv);
2258
2381
  mat4.copy(this._cameraWorld, camera.transform);
2259
2382
  }
2260
2383
  cameraChanged(camera) {
@@ -2310,7 +2433,7 @@ class WGPURayTracePass {
2310
2433
  const arr = this._rtUniform.views.jitter;
2311
2434
  arr[0] = x;
2312
2435
  arr[1] = y;
2313
- this._device.queue.writeBuffer(this._uniforms.cameraUniform, 0, packCameraUniform(this._viewProjInv, this._cameraWorld, this._apertureRadius, this._focusDistance));
2436
+ this._device.queue.writeBuffer(this._uniforms.cameraUniform, 0, packCameraUniform(this._viewProjInv, this._cameraWorld, this._apertureRadius, this._focusDistance, this._projectionInv));
2314
2437
  }
2315
2438
  setSampleTiles(columns, rows, tileX, tileY) {
2316
2439
  const arr = this._rtUniform.views.scaleOffset;