@meimu/mutracer 0.1.4 → 0.2.0

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 (529) hide show
  1. package/LICENSES/Classroom-CC0.txt +121 -0
  2. package/README.md +7 -7
  3. package/THIRD_PARTY_NOTICES.md +13 -8
  4. package/dist/assets/bvh.worker.js +1 -1
  5. package/dist/mutracer.umd.js +10421 -8363
  6. package/docs/api.md +202 -11
  7. package/docs/denoising.md +30 -15
  8. package/docs/materials.md +18 -27
  9. package/docs/performance.md +5 -2
  10. package/docs/progressive-rendering.md +32 -1
  11. package/docs/threejs.md +19 -10
  12. package/docs/updates.md +44 -2
  13. package/docs/viewer.md +41 -3
  14. package/lib/Display.d.ts +13 -2
  15. package/lib/Display.js +27 -6
  16. package/lib/Display.js.map +1 -1
  17. package/lib/Engine.d.ts +2 -0
  18. package/lib/Engine.js +52 -25
  19. package/lib/Engine.js.map +1 -1
  20. package/lib/Render.d.ts +44 -9
  21. package/lib/Render.js +115 -17
  22. package/lib/Render.js.map +1 -1
  23. package/lib/RenderLoop.d.ts +7 -1
  24. package/lib/RenderLoop.js +94 -13
  25. package/lib/RenderLoop.js.map +1 -1
  26. package/lib/main.d.ts +6 -3
  27. package/lib/main.js +1 -0
  28. package/lib/main.js.map +1 -1
  29. package/lib/math/access.d.ts +4 -0
  30. package/lib/math/access.js +10 -0
  31. package/lib/math/access.js.map +1 -0
  32. package/lib/math/mat3.d.ts +4 -0
  33. package/lib/math/mat3.js +54 -0
  34. package/lib/math/mat3.js.map +1 -0
  35. package/lib/math/mat4.d.ts +19 -0
  36. package/lib/math/mat4.js +413 -0
  37. package/lib/math/mat4.js.map +1 -0
  38. package/lib/math/storage.d.ts +6 -0
  39. package/lib/math/storage.js +22 -0
  40. package/lib/math/storage.js.map +1 -0
  41. package/lib/math/types.d.ts +3 -0
  42. package/lib/math/types.js +2 -0
  43. package/lib/math/types.js.map +1 -0
  44. package/lib/math/vec2.d.ts +13 -0
  45. package/lib/math/vec2.js +52 -0
  46. package/lib/math/vec2.js.map +1 -0
  47. package/lib/math/vec3.d.ts +23 -0
  48. package/lib/math/vec3.js +139 -0
  49. package/lib/math/vec3.js.map +1 -0
  50. package/lib/math.d.ts +5 -17
  51. package/lib/math.js +5 -55
  52. package/lib/math.js.map +1 -1
  53. package/lib/oidn.d.ts +1 -1
  54. package/lib/oidn.js +4 -2
  55. package/lib/oidn.js.map +1 -1
  56. package/lib/oidnBundledWeights.cdn.d.ts +2 -2
  57. package/lib/renderer/AovPrimitiveCache.d.ts +10 -0
  58. package/lib/renderer/AovPrimitiveCache.js +48 -0
  59. package/lib/renderer/AovPrimitiveCache.js.map +1 -0
  60. package/lib/renderer/Box3.d.ts +5 -0
  61. package/lib/renderer/Box3.js +9 -0
  62. package/lib/renderer/Box3.js.map +1 -0
  63. package/lib/renderer/GPUTraceQueue.d.ts +10 -0
  64. package/lib/renderer/GPUTraceQueue.js +31 -0
  65. package/lib/renderer/GPUTraceQueue.js.map +1 -0
  66. package/lib/renderer/MaterialData.d.ts +15 -0
  67. package/lib/renderer/MaterialData.js +61 -0
  68. package/lib/renderer/MaterialData.js.map +1 -0
  69. package/lib/renderer/MaterialTable.d.ts +11 -98
  70. package/lib/renderer/MaterialTable.js +34 -412
  71. package/lib/renderer/MaterialTable.js.map +1 -1
  72. package/lib/renderer/RenderingPipeline.d.ts +32 -13
  73. package/lib/renderer/RenderingPipeline.js +1 -17
  74. package/lib/renderer/RenderingPipeline.js.map +1 -1
  75. package/lib/renderer/TileRender.d.ts +0 -1
  76. package/lib/renderer/TileRender.js +14 -1
  77. package/lib/renderer/TileRender.js.map +1 -1
  78. package/lib/renderer/WGPUAov.d.ts +39 -0
  79. package/lib/renderer/WGPUAov.js +239 -0
  80. package/lib/renderer/WGPUAov.js.map +1 -0
  81. package/lib/renderer/WGPUBindlessMaterialData.d.ts +17 -0
  82. package/lib/renderer/WGPUBindlessMaterialData.js +48 -0
  83. package/lib/renderer/WGPUBindlessMaterialData.js.map +1 -0
  84. package/lib/renderer/WGPUBindlessTextures.d.ts +47 -0
  85. package/lib/renderer/WGPUBindlessTextures.js +180 -0
  86. package/lib/renderer/WGPUBindlessTextures.js.map +1 -0
  87. package/lib/renderer/WGPUBloomPass.js +17 -97
  88. package/lib/renderer/WGPUBloomPass.js.map +1 -1
  89. package/lib/renderer/WGPUComputePass.d.ts +1 -1
  90. package/lib/renderer/WGPUComputePass.js.map +1 -1
  91. package/lib/renderer/WGPUEnvMapDistribution.d.ts +12 -0
  92. package/lib/renderer/WGPUEnvMapDistribution.js +150 -0
  93. package/lib/renderer/WGPUEnvMapDistribution.js.map +1 -0
  94. package/lib/renderer/WGPUFullQuadPass.js +10 -9
  95. package/lib/renderer/WGPUFullQuadPass.js.map +1 -1
  96. package/lib/renderer/WGPUJNLMDenoiser.d.ts +1 -20
  97. package/lib/renderer/WGPUJNLMDenoiser.js +4 -267
  98. package/lib/renderer/WGPUJNLMDenoiser.js.map +1 -1
  99. package/lib/renderer/WGPURayTraceOutput.js +26 -17
  100. package/lib/renderer/WGPURayTraceOutput.js.map +1 -1
  101. package/lib/renderer/WGPURayTracePass.d.ts +36 -40
  102. package/lib/renderer/WGPURayTracePass.js +399 -548
  103. package/lib/renderer/WGPURayTracePass.js.map +1 -1
  104. package/lib/renderer/WGPURenderingPipeline.d.ts +5 -3
  105. package/lib/renderer/WGPURenderingPipeline.js +239 -60
  106. package/lib/renderer/WGPURenderingPipeline.js.map +1 -1
  107. package/lib/renderer/WGPUShaderCache.d.ts +2 -2
  108. package/lib/renderer/WGPUShaderCache.js +10 -5
  109. package/lib/renderer/WGPUShaderCache.js.map +1 -1
  110. package/lib/renderer/WGPUShaderCapabilities.d.ts +10 -0
  111. package/lib/renderer/WGPUShaderCapabilities.js +54 -0
  112. package/lib/renderer/WGPUShaderCapabilities.js.map +1 -0
  113. package/lib/renderer/WGPUSkyBaker.d.ts +13 -0
  114. package/lib/renderer/WGPUSkyBaker.js +85 -0
  115. package/lib/renderer/WGPUSkyBaker.js.map +1 -0
  116. package/lib/renderer/WebGLAov.d.ts +41 -0
  117. package/lib/renderer/WebGLAov.js +106 -0
  118. package/lib/renderer/WebGLAov.js.map +1 -0
  119. package/lib/renderer/WebGLFullQuadPass.d.ts +5 -3
  120. package/lib/renderer/WebGLFullQuadPass.js +18 -2
  121. package/lib/renderer/WebGLFullQuadPass.js.map +1 -1
  122. package/lib/renderer/WebGLJNLMDenoiser.d.ts +1 -1
  123. package/lib/renderer/WebGLJNLMDenoiser.js +83 -74
  124. package/lib/renderer/WebGLJNLMDenoiser.js.map +1 -1
  125. package/lib/renderer/WebGLRayTraceOutputPass.js +7 -4
  126. package/lib/renderer/WebGLRayTraceOutputPass.js.map +1 -1
  127. package/lib/renderer/WebGLRayTracePass.d.ts +12 -2
  128. package/lib/renderer/WebGLRayTracePass.js +83 -7
  129. package/lib/renderer/WebGLRayTracePass.js.map +1 -1
  130. package/lib/renderer/WebGLRenderingPipeline.js +116 -8
  131. package/lib/renderer/WebGLRenderingPipeline.js.map +1 -1
  132. package/lib/renderer/WebGLSkyBaker.d.ts +39 -0
  133. package/lib/renderer/WebGLSkyBaker.js +422 -0
  134. package/lib/renderer/WebGLSkyBaker.js.map +1 -0
  135. package/lib/renderer/WebGLSkySource.d.ts +9 -0
  136. package/lib/renderer/WebGLSkySource.js +28 -0
  137. package/lib/renderer/WebGLSkySource.js.map +1 -0
  138. package/lib/renderer/accumulationTarget.d.ts +5 -0
  139. package/lib/renderer/accumulationTarget.js.map +1 -1
  140. package/lib/renderer/aov.d.ts +42 -0
  141. package/lib/renderer/aov.js +50 -0
  142. package/lib/renderer/aov.js.map +1 -0
  143. package/lib/renderer/aovDisplay.d.ts +14 -0
  144. package/lib/renderer/aovDisplay.js +32 -0
  145. package/lib/renderer/aovDisplay.js.map +1 -0
  146. package/lib/renderer/aovScene.d.ts +21 -0
  147. package/lib/renderer/aovScene.js +139 -0
  148. package/lib/renderer/aovScene.js.map +1 -0
  149. package/lib/renderer/binnedSahBvh.js +2 -1
  150. package/lib/renderer/binnedSahBvh.js.map +1 -1
  151. package/lib/renderer/bvhAccel.d.ts +20 -10
  152. package/lib/renderer/bvhAccel.js +168 -58
  153. package/lib/renderer/bvhAccel.js.map +1 -1
  154. package/lib/renderer/bvhWorkerBuild.d.ts +1 -0
  155. package/lib/renderer/cameraSnapshot.js +2 -1
  156. package/lib/renderer/cameraSnapshot.js.map +1 -1
  157. package/lib/renderer/cameraUniform.d.ts +2 -0
  158. package/lib/renderer/cameraUniform.js +12 -0
  159. package/lib/renderer/cameraUniform.js.map +1 -0
  160. package/lib/renderer/committedScene.js +4 -0
  161. package/lib/renderer/committedScene.js.map +1 -1
  162. package/lib/renderer/denoiser.d.ts +3 -11
  163. package/lib/renderer/denoiser.js.map +1 -1
  164. package/lib/renderer/depthQuery.js +4 -1
  165. package/lib/renderer/depthQuery.js.map +1 -1
  166. package/lib/renderer/displacementPreparation.d.ts +11 -0
  167. package/lib/renderer/displacementPreparation.js +351 -0
  168. package/lib/renderer/displacementPreparation.js.map +1 -0
  169. package/lib/renderer/displacementRange.d.ts +1 -0
  170. package/lib/renderer/displacementRange.js +9 -0
  171. package/lib/renderer/displacementRange.js.map +1 -0
  172. package/lib/renderer/displayParityDiagnostics.d.ts +11 -0
  173. package/lib/renderer/displayParityDiagnostics.js +2 -0
  174. package/lib/renderer/displayParityDiagnostics.js.map +1 -0
  175. package/lib/renderer/envMapCreation.d.ts +9 -8
  176. package/lib/renderer/envMapCreation.js +36 -12
  177. package/lib/renderer/envMapCreation.js.map +1 -1
  178. package/lib/renderer/envMapDistribution.d.ts +12 -2
  179. package/lib/renderer/envMapDistribution.js +43 -34
  180. package/lib/renderer/envMapDistribution.js.map +1 -1
  181. package/lib/renderer/geometryBvhBounds.d.ts +4 -0
  182. package/lib/renderer/geometryBvhBounds.js +82 -0
  183. package/lib/renderer/geometryBvhBounds.js.map +1 -0
  184. package/lib/renderer/geometryUpdateState.d.ts +32 -0
  185. package/lib/renderer/geometryUpdateState.js +2 -0
  186. package/lib/renderer/geometryUpdateState.js.map +1 -0
  187. package/lib/renderer/geometryUpdates.d.ts +17 -9
  188. package/lib/renderer/geometryUpdates.js +92 -33
  189. package/lib/renderer/geometryUpdates.js.map +1 -1
  190. package/lib/renderer/glsl/aovDisplay.frag.d.ts +3 -0
  191. package/lib/renderer/glsl/aovDisplay.frag.js +54 -0
  192. package/lib/renderer/glsl/aovDisplay.frag.js.map +1 -0
  193. package/lib/renderer/glsl/aovReflectance.glsl.d.ts +2 -0
  194. package/lib/renderer/glsl/aovReflectance.glsl.js +87 -0
  195. package/lib/renderer/glsl/aovReflectance.glsl.js.map +1 -0
  196. package/lib/renderer/glsl/aovReflectanceSamples.glsl.d.ts +2 -0
  197. package/lib/renderer/glsl/aovReflectanceSamples.glsl.js +135 -0
  198. package/lib/renderer/glsl/aovReflectanceSamples.glsl.js.map +1 -0
  199. package/lib/renderer/glsl/aovTrace.frag.d.ts +2 -0
  200. package/lib/renderer/glsl/aovTrace.frag.js +121 -0
  201. package/lib/renderer/glsl/aovTrace.frag.js.map +1 -0
  202. package/lib/renderer/glsl/chunks/cameraRay.glsl.js +12 -12
  203. package/lib/renderer/glsl/chunks/envMap.glsl.js +80 -74
  204. package/lib/renderer/glsl/chunks/envMap.glsl.js.map +1 -1
  205. package/lib/renderer/glsl/chunks/intersect.glsl.js +437 -435
  206. package/lib/renderer/glsl/chunks/intersect.glsl.js.map +1 -1
  207. package/lib/renderer/glsl/chunks/materialBuffer.glsl.js +146 -145
  208. package/lib/renderer/glsl/chunks/materialBuffer.glsl.js.map +1 -1
  209. package/lib/renderer/glsl/chunks/openPbrBsdf.glsl.js +821 -821
  210. package/lib/renderer/glsl/chunks/openPbrBsdf.glsl.js.map +1 -1
  211. package/lib/renderer/glsl/chunks/rand.glsl.js +3 -3
  212. package/lib/renderer/glsl/chunks/random.glsl.js +6 -6
  213. package/lib/renderer/glsl/chunks/rayTraceCore.glsl.js +51 -51
  214. package/lib/renderer/glsl/chunks/rayTraceCore.glsl.js.map +1 -1
  215. package/lib/renderer/glsl/chunks/sampleLight.glsl.js +80 -80
  216. package/lib/renderer/glsl/chunks/sampleMaterial.glsl.js +108 -108
  217. package/lib/renderer/glsl/chunks/sampleMaterial.glsl.js.map +1 -1
  218. package/lib/renderer/glsl/chunks/sampleShadowCatcher.glsl.js +20 -17
  219. package/lib/renderer/glsl/chunks/sampleShadowCatcher.glsl.js.map +1 -1
  220. package/lib/renderer/glsl/chunks/sampling.glsl.js +93 -93
  221. package/lib/renderer/glsl/chunks/sobol.glsl.js +79 -79
  222. package/lib/renderer/glsl/chunks/textureLinear.glsl.js +5 -5
  223. package/lib/renderer/glsl/chunks/uvProjection.glsl.js +12 -12
  224. package/lib/renderer/glsl/chunks/uvProjection.glsl.js.map +1 -1
  225. package/lib/renderer/glsl/depthQuery.frag.js +7 -7
  226. package/lib/renderer/glsl/normalizeRayTraceOutput.frag.js +17 -9
  227. package/lib/renderer/glsl/normalizeRayTraceOutput.frag.js.map +1 -1
  228. package/lib/renderer/glsl/rayTrace.frag.js +191 -191
  229. package/lib/renderer/glsl/shadowCatcher.frag.js +25 -20
  230. package/lib/renderer/glsl/shadowCatcher.frag.js.map +1 -1
  231. package/lib/renderer/glsl/webglPipeline.frag.js +65 -60
  232. package/lib/renderer/glsl/webglPipeline.frag.js.map +1 -1
  233. package/lib/renderer/lightConstants.d.ts +10 -0
  234. package/lib/renderer/lightConstants.js +11 -0
  235. package/lib/renderer/lightConstants.js.map +1 -0
  236. package/lib/renderer/lightCore.d.ts +12 -0
  237. package/lib/renderer/lightCore.js +13 -0
  238. package/lib/renderer/lightCore.js.map +1 -0
  239. package/lib/renderer/lightSampling.d.ts +20 -17
  240. package/lib/renderer/lightSampling.js +123 -56
  241. package/lib/renderer/lightSampling.js.map +1 -1
  242. package/lib/renderer/lightsData.d.ts +21 -0
  243. package/lib/renderer/lightsData.js +192 -0
  244. package/lib/renderer/lightsData.js.map +1 -0
  245. package/lib/renderer/lightsUtil.d.ts +3 -26
  246. package/lib/renderer/lightsUtil.js +3 -153
  247. package/lib/renderer/lightsUtil.js.map +1 -1
  248. package/lib/renderer/materialBufferData.d.ts +70 -0
  249. package/lib/renderer/materialBufferData.js +2 -0
  250. package/lib/renderer/materialBufferData.js.map +1 -0
  251. package/lib/renderer/materialConstants.d.ts +11 -0
  252. package/lib/renderer/materialConstants.js +11 -0
  253. package/lib/renderer/materialConstants.js.map +1 -1
  254. package/lib/renderer/materialCore.d.ts +8 -0
  255. package/lib/renderer/materialCore.js +9 -0
  256. package/lib/renderer/materialCore.js.map +1 -0
  257. package/lib/renderer/materialEmission.d.ts +2 -2
  258. package/lib/renderer/materialEmission.js +3 -3
  259. package/lib/renderer/materialEmission.js.map +1 -1
  260. package/lib/renderer/materialLayout.d.ts +2 -101
  261. package/lib/renderer/materialLayout.js +110 -172
  262. package/lib/renderer/materialLayout.js.map +1 -1
  263. package/lib/renderer/materialLayoutSchema.d.ts +26 -0
  264. package/lib/renderer/materialLayoutSchema.js +172 -0
  265. package/lib/renderer/materialLayoutSchema.js.map +1 -0
  266. package/lib/renderer/materialParams.d.ts +69 -0
  267. package/lib/renderer/materialParams.js +272 -0
  268. package/lib/renderer/materialParams.js.map +1 -0
  269. package/lib/renderer/materialTextureSlots.d.ts +14 -324
  270. package/lib/renderer/materialTextureSlots.js +21 -8
  271. package/lib/renderer/materialTextureSlots.js.map +1 -1
  272. package/lib/renderer/materialTextures.d.ts +6 -0
  273. package/lib/renderer/{texturesFromMaterials.js → materialTextures.js} +1 -2
  274. package/lib/renderer/materialTextures.js.map +1 -0
  275. package/lib/renderer/mergeMeshesToGeometry.js +6 -147
  276. package/lib/renderer/mergeMeshesToGeometry.js.map +1 -1
  277. package/lib/renderer/meshInstances.d.ts +4 -21
  278. package/lib/renderer/meshInstances.js +10 -56
  279. package/lib/renderer/meshInstances.js.map +1 -1
  280. package/lib/renderer/oidnNetwork.d.ts +2 -2
  281. package/lib/renderer/oidnNetwork.js +1 -1
  282. package/lib/renderer/oidnNetwork.js.map +1 -1
  283. package/lib/renderer/openPbrEnergyLut.js +850 -846
  284. package/lib/renderer/openPbrEnergyLut.js.map +1 -1
  285. package/lib/renderer/ownedDenoiser.d.ts +9 -0
  286. package/lib/renderer/ownedDenoiser.js +2 -0
  287. package/lib/renderer/ownedDenoiser.js.map +1 -0
  288. package/lib/renderer/packedMeshInstances.d.ts +23 -0
  289. package/lib/renderer/packedMeshInstances.js +80 -0
  290. package/lib/renderer/packedMeshInstances.js.map +1 -0
  291. package/lib/renderer/prepareMeshBVH.d.ts +12 -47
  292. package/lib/renderer/prepareMeshBVH.js +21 -642
  293. package/lib/renderer/prepareMeshBVH.js.map +1 -1
  294. package/lib/renderer/proceduralEnvironmentPower.d.ts +3 -0
  295. package/lib/renderer/proceduralEnvironmentPower.js +8 -0
  296. package/lib/renderer/proceduralEnvironmentPower.js.map +1 -0
  297. package/lib/renderer/progressiveScheduler.d.ts +1 -0
  298. package/lib/renderer/progressiveScheduler.js +3 -0
  299. package/lib/renderer/progressiveScheduler.js.map +1 -1
  300. package/lib/renderer/renderPipeline.d.ts +3 -1
  301. package/lib/renderer/renderPipeline.js.map +1 -1
  302. package/lib/renderer/renderingLimits.d.ts +9 -0
  303. package/lib/renderer/renderingLimits.js +18 -0
  304. package/lib/renderer/renderingLimits.js.map +1 -0
  305. package/lib/renderer/sceneAssembly.d.ts +73 -0
  306. package/lib/renderer/sceneAssembly.js +109 -0
  307. package/lib/renderer/sceneAssembly.js.map +1 -0
  308. package/lib/renderer/sceneChangeCore.d.ts +39 -0
  309. package/lib/renderer/sceneChangeCore.js +307 -0
  310. package/lib/renderer/sceneChangeCore.js.map +1 -0
  311. package/lib/renderer/sceneChangeDetector.d.ts +3 -6
  312. package/lib/renderer/sceneChangeDetector.js +24 -150
  313. package/lib/renderer/sceneChangeDetector.js.map +1 -1
  314. package/lib/renderer/sceneLayoutSchema.d.ts +118 -0
  315. package/lib/renderer/sceneLayoutSchema.js +111 -0
  316. package/lib/renderer/sceneLayoutSchema.js.map +1 -0
  317. package/lib/renderer/sceneLightChanges.d.ts +3 -3
  318. package/lib/renderer/sceneLightChanges.js.map +1 -1
  319. package/lib/renderer/scenePreparation.d.ts +55 -0
  320. package/lib/renderer/scenePreparation.js +360 -0
  321. package/lib/renderer/scenePreparation.js.map +1 -0
  322. package/lib/renderer/scratchSampling.js +18 -18
  323. package/lib/renderer/scratchSampling.js.map +1 -1
  324. package/lib/renderer/shader.d.ts +2 -0
  325. package/lib/renderer/shader.js +2 -0
  326. package/lib/renderer/shader.js.map +1 -1
  327. package/lib/renderer/shaderCapabilities.d.ts +23 -0
  328. package/lib/renderer/shaderCapabilities.js +55 -0
  329. package/lib/renderer/shaderCapabilities.js.map +1 -0
  330. package/lib/renderer/shaderCapacity.d.ts +2 -0
  331. package/lib/renderer/shaderCapacity.js +5 -0
  332. package/lib/renderer/shaderCapacity.js.map +1 -0
  333. package/lib/renderer/skyModel.d.ts +9 -0
  334. package/lib/renderer/skyModel.js +10 -0
  335. package/lib/renderer/skyModel.js.map +1 -0
  336. package/lib/renderer/textureAtlas.d.ts +60 -0
  337. package/lib/renderer/textureAtlas.js +145 -0
  338. package/lib/renderer/textureAtlas.js.map +1 -0
  339. package/lib/renderer/textureAtlasPixels.d.ts +7 -0
  340. package/lib/renderer/textureAtlasPixels.js +68 -0
  341. package/lib/renderer/textureAtlasPixels.js.map +1 -0
  342. package/lib/renderer/util.d.ts +1 -1
  343. package/lib/renderer/util.js.map +1 -1
  344. package/lib/renderer/webgl.d.ts +9 -2
  345. package/lib/renderer/webgl.js +54 -9
  346. package/lib/renderer/webgl.js.map +1 -1
  347. package/lib/renderer/wgpuBindless.d.ts +26 -0
  348. package/lib/renderer/wgpuBindless.js +95 -0
  349. package/lib/renderer/wgpuBindless.js.map +1 -0
  350. package/lib/renderer/wgpuBindlessSource.d.ts +2 -0
  351. package/lib/renderer/wgpuBindlessSource.js +51 -0
  352. package/lib/renderer/wgpuBindlessSource.js.map +1 -0
  353. package/lib/renderer/wgpuMaterialConstants.d.ts +4 -0
  354. package/lib/renderer/wgpuMaterialConstants.js +52 -0
  355. package/lib/renderer/wgpuMaterialConstants.js.map +1 -0
  356. package/lib/renderer/wgpuSceneUniform.d.ts +42 -0
  357. package/lib/renderer/wgpuSceneUniform.js +125 -0
  358. package/lib/renderer/wgpuSceneUniform.js.map +1 -0
  359. package/lib/renderer/wgpuShaderSource.d.ts +15 -0
  360. package/lib/renderer/wgpuShaderSource.js +31 -0
  361. package/lib/renderer/wgpuShaderSource.js.map +1 -0
  362. package/lib/renderer/wgsl/aovDisplay.wgsl.d.ts +1 -0
  363. package/lib/renderer/wgsl/aovDisplay.wgsl.js +70 -0
  364. package/lib/renderer/wgsl/aovDisplay.wgsl.js.map +1 -0
  365. package/lib/renderer/wgsl/aovReflectance.wgsl.d.ts +2 -0
  366. package/lib/renderer/wgsl/aovReflectance.wgsl.js +98 -0
  367. package/lib/renderer/wgsl/aovReflectance.wgsl.js.map +1 -0
  368. package/lib/renderer/wgsl/aovReflectanceSamples.wgsl.d.ts +2 -0
  369. package/lib/renderer/wgsl/aovReflectanceSamples.wgsl.js +135 -0
  370. package/lib/renderer/wgsl/aovReflectanceSamples.wgsl.js.map +1 -0
  371. package/lib/renderer/wgsl/aovResolve.wgsl.d.ts +5 -0
  372. package/lib/renderer/wgsl/aovResolve.wgsl.js +38 -0
  373. package/lib/renderer/wgsl/aovResolve.wgsl.js.map +1 -0
  374. package/lib/renderer/wgsl/aovSnapshot.wgsl.d.ts +4 -0
  375. package/lib/renderer/wgsl/aovSnapshot.wgsl.js +21 -0
  376. package/lib/renderer/wgsl/aovSnapshot.wgsl.js.map +1 -0
  377. package/lib/renderer/wgsl/aovTrace.wgsl.d.ts +2 -0
  378. package/lib/renderer/wgsl/aovTrace.wgsl.js +115 -0
  379. package/lib/renderer/wgsl/aovTrace.wgsl.js.map +1 -0
  380. package/lib/renderer/wgsl/bloom.wgsl.d.ts +13 -0
  381. package/lib/renderer/wgsl/bloom.wgsl.js +92 -0
  382. package/lib/renderer/wgsl/bloom.wgsl.js.map +1 -0
  383. package/lib/renderer/wgsl/chunks/common.wgsl.js +4 -4
  384. package/lib/renderer/wgsl/chunks/envMap.wgsl.d.ts +1 -1
  385. package/lib/renderer/wgsl/chunks/envMap.wgsl.js +43 -37
  386. package/lib/renderer/wgsl/chunks/envMap.wgsl.js.map +1 -1
  387. package/lib/renderer/wgsl/chunks/intersect.wgsl.d.ts +13 -1
  388. package/lib/renderer/wgsl/chunks/intersect.wgsl.js +743 -706
  389. package/lib/renderer/wgsl/chunks/intersect.wgsl.js.map +1 -1
  390. package/lib/renderer/wgsl/chunks/intersectionAlpha.wgsl.js +16 -16
  391. package/lib/renderer/wgsl/chunks/intersectionAlpha.wgsl.js.map +1 -1
  392. package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.d.ts +3 -1
  393. package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.js +86 -83
  394. package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.js.map +1 -1
  395. package/lib/renderer/wgsl/chunks/openPbrBsdf.wgsl.js +855 -857
  396. package/lib/renderer/wgsl/chunks/openPbrBsdf.wgsl.js.map +1 -1
  397. package/lib/renderer/wgsl/chunks/pdm_displacement.wgsl.js +139 -139
  398. package/lib/renderer/wgsl/chunks/rand.wgsl.js +3 -3
  399. package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.d.ts +2 -1
  400. package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.js +42 -32
  401. package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.js.map +1 -1
  402. package/lib/renderer/wgsl/chunks/sampleLight.wgsl.d.ts +1 -1
  403. package/lib/renderer/wgsl/chunks/sampleLight.wgsl.js +152 -148
  404. package/lib/renderer/wgsl/chunks/sampleLight.wgsl.js.map +1 -1
  405. package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.js +239 -232
  406. package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.js.map +1 -1
  407. package/lib/renderer/wgsl/chunks/sampleShadowCatcher.wgsl.d.ts +1 -1
  408. package/lib/renderer/wgsl/chunks/sampleShadowCatcher.wgsl.js +26 -13
  409. package/lib/renderer/wgsl/chunks/sampleShadowCatcher.wgsl.js.map +1 -1
  410. package/lib/renderer/wgsl/chunks/sampling.wgsl.js +91 -91
  411. package/lib/renderer/wgsl/chunks/shadowCatcherAccumulation.wgsl.d.ts +1 -1
  412. package/lib/renderer/wgsl/chunks/shadowCatcherAccumulation.wgsl.js +1 -0
  413. package/lib/renderer/wgsl/chunks/shadowCatcherAccumulation.wgsl.js.map +1 -1
  414. package/lib/renderer/wgsl/chunks/sobol.wgsl.js +83 -83
  415. package/lib/renderer/wgsl/chunks/uvProjection.wgsl.js +12 -12
  416. package/lib/renderer/wgsl/chunks/uvProjection.wgsl.js.map +1 -1
  417. package/lib/renderer/wgsl/compose.frag.wgsl.d.ts +8 -3
  418. package/lib/renderer/wgsl/compose.frag.wgsl.js +143 -118
  419. package/lib/renderer/wgsl/compose.frag.wgsl.js.map +1 -1
  420. package/lib/renderer/wgsl/customDenoise.wgsl.js +10 -10
  421. package/lib/renderer/wgsl/depthQuery.wgsl.js +7 -7
  422. package/lib/renderer/wgsl/jnlm.compute.d.ts +25 -0
  423. package/lib/renderer/wgsl/jnlm.compute.js +315 -0
  424. package/lib/renderer/wgsl/jnlm.compute.js.map +1 -0
  425. package/lib/renderer/wgsl/offlineAdaptive.compute.js +28 -28
  426. package/lib/renderer/wgsl/oidnAuxiliary.wgsl.d.ts +1 -0
  427. package/lib/renderer/wgsl/oidnAuxiliary.wgsl.js +17 -0
  428. package/lib/renderer/wgsl/oidnAuxiliary.wgsl.js.map +1 -0
  429. package/lib/renderer/wgsl/proceduralSky.compute.d.ts +1 -0
  430. package/lib/renderer/wgsl/proceduralSky.compute.js +198 -0
  431. package/lib/renderer/wgsl/proceduralSky.compute.js.map +1 -0
  432. package/lib/renderer/wgsl/rayTrace.compute.d.ts +17 -1
  433. package/lib/renderer/wgsl/rayTrace.compute.js +612 -393
  434. package/lib/renderer/wgsl/rayTrace.compute.js.map +1 -1
  435. package/lib/scene/Camera.js +7 -3
  436. package/lib/scene/Camera.js.map +1 -1
  437. package/lib/scene/EnvironmentLight.d.ts +5 -9
  438. package/lib/scene/EnvironmentLight.js +4 -14
  439. package/lib/scene/EnvironmentLight.js.map +1 -1
  440. package/lib/scene/EnvironmentLightState.d.ts +18 -0
  441. package/lib/scene/EnvironmentLightState.js +19 -0
  442. package/lib/scene/EnvironmentLightState.js.map +1 -0
  443. package/lib/scene/Geometry.d.ts +12 -6
  444. package/lib/scene/Geometry.js +37 -13
  445. package/lib/scene/Geometry.js.map +1 -1
  446. package/lib/scene/InstanceState.d.ts +39 -0
  447. package/lib/scene/InstanceState.js +77 -0
  448. package/lib/scene/InstanceState.js.map +1 -0
  449. package/lib/scene/InstancedMesh.d.ts +1 -1
  450. package/lib/scene/InstancedMesh.js +3 -0
  451. package/lib/scene/InstancedMesh.js.map +1 -1
  452. package/lib/scene/MaterialState.d.ts +209 -0
  453. package/lib/scene/MaterialState.js +231 -0
  454. package/lib/scene/MaterialState.js.map +1 -0
  455. package/lib/scene/Mesh.d.ts +4 -6
  456. package/lib/scene/Mesh.js +3 -11
  457. package/lib/scene/Mesh.js.map +1 -1
  458. package/lib/scene/MeshState.d.ts +20 -0
  459. package/lib/scene/MeshState.js +35 -0
  460. package/lib/scene/MeshState.js.map +1 -0
  461. package/lib/scene/OpenPBRMaterial.d.ts +7 -169
  462. package/lib/scene/OpenPBRMaterial.js +5 -223
  463. package/lib/scene/OpenPBRMaterial.js.map +1 -1
  464. package/lib/scene/PixelTexture.d.ts +6 -0
  465. package/lib/scene/PixelTexture.js +10 -0
  466. package/lib/scene/PixelTexture.js.map +1 -0
  467. package/lib/scene/ProceduralSky.d.ts +24 -0
  468. package/lib/scene/ProceduralSky.js +40 -0
  469. package/lib/scene/ProceduralSky.js.map +1 -0
  470. package/lib/scene/RectLight.d.ts +2 -13
  471. package/lib/scene/RectLight.js +3 -21
  472. package/lib/scene/RectLight.js.map +1 -1
  473. package/lib/scene/RectLightState.d.ts +31 -0
  474. package/lib/scene/RectLightState.js +26 -0
  475. package/lib/scene/RectLightState.js.map +1 -0
  476. package/lib/scene/Scene.d.ts +15 -4
  477. package/lib/scene/Scene.js.map +1 -1
  478. package/lib/scene/SceneNode.js +7 -6
  479. package/lib/scene/SceneNode.js.map +1 -1
  480. package/lib/scene/Texture.d.ts +10 -41
  481. package/lib/scene/Texture.js +4 -33
  482. package/lib/scene/Texture.js.map +1 -1
  483. package/lib/scene/TextureState.d.ts +35 -0
  484. package/lib/scene/TextureState.js +35 -0
  485. package/lib/scene/TextureState.js.map +1 -0
  486. package/lib/scene/attributeData.d.ts +4 -0
  487. package/lib/scene/attributeData.js +10 -0
  488. package/lib/scene/attributeData.js.map +1 -0
  489. package/lib/scene/lightState.d.ts +8 -0
  490. package/lib/scene/lightState.js +2 -0
  491. package/lib/scene/lightState.js.map +1 -0
  492. package/lib/scene/openPbrSchema.d.ts +7 -12
  493. package/lib/scene/openPbrSchema.js +273 -28
  494. package/lib/scene/openPbrSchema.js.map +1 -1
  495. package/lib/scene/skyParameters.d.ts +20 -0
  496. package/lib/scene/skyParameters.js +28 -0
  497. package/lib/scene/skyParameters.js.map +1 -0
  498. package/lib/scene/textureData.d.ts +9 -0
  499. package/lib/scene/textureData.js +2 -0
  500. package/lib/scene/textureData.js.map +1 -0
  501. package/lib/scene/texturePixels.d.ts +6 -0
  502. package/lib/scene/texturePixels.js +10 -0
  503. package/lib/scene/texturePixels.js.map +1 -0
  504. package/lib/settings/display.d.ts +33 -0
  505. package/lib/settings/display.js +7 -0
  506. package/lib/settings/display.js.map +1 -1
  507. package/lib/settings/render.d.ts +25 -3
  508. package/lib/settings/render.js +3 -1
  509. package/lib/settings/render.js.map +1 -1
  510. package/lib/tinybvh/tinybvh.wasm +0 -0
  511. package/lib/util/commonGeometry.js +7 -7
  512. package/lib/util/commonGeometry.js.map +1 -1
  513. package/lib/util/fromTHREE.d.ts +4 -1
  514. package/lib/util/fromTHREE.js +18 -5
  515. package/lib/util/fromTHREE.js.map +1 -1
  516. package/lib/util/geometry.js +42 -29
  517. package/lib/util/geometry.js.map +1 -1
  518. package/lib/util/loadRGBE.js +3 -4
  519. package/lib/util/loadRGBE.js.map +1 -1
  520. package/lib/util/studioLighting.d.ts +18 -0
  521. package/lib/util/studioLighting.js +36 -0
  522. package/lib/util/studioLighting.js.map +1 -0
  523. package/lib/version.d.ts +1 -1
  524. package/lib/version.js +1 -1
  525. package/lib/version.js.map +1 -1
  526. package/llms.txt +4 -0
  527. package/package.json +31 -5
  528. package/lib/renderer/texturesFromMaterials.d.ts +0 -6
  529. package/lib/renderer/texturesFromMaterials.js.map +0 -1
package/docs/api.md CHANGED
@@ -5,7 +5,7 @@ See [progressive rendering](./progressive-rendering.md) for complete examples.
5
5
 
6
6
  ## Engine
7
7
 
8
- `new Engine({canvas?, backend?, device?})` manages the device and one GPU
8
+ `new Engine({canvas?, backend?, device?, textureBinding?})` manages the device and one GPU
9
9
  scene. `scene` and `camera` default to undefined. Assign them and call
10
10
  `update()` to commit edits before rendering; see [updates](./updates.md).
11
11
 
@@ -14,6 +14,7 @@ scene. `scene` and `camera` default to undefined. Assign them and call
14
14
  | `canvas` | new HTML canvas, or OffscreenCanvas without document | Accepts either canvas type; default `engine.display.canvas` |
15
15
  | `device` | undefined | Caller-owned WebGPU device; see [requirements](#device-requirements) |
16
16
  | `backend` | `'auto'` | Automatic WebGPU/WebGL selection; `'webgpu'` requires WebGPU; `'webgl'` forces WebGL 2 |
17
+ | `textureBinding` | `'atlas'` | Constructor-only; `'auto'` tries experimental WebGPU bindless textures, with atlas fallback; see [texture bindings](#texture-bindings) |
17
18
  | `bvhBuilder` | `'builtin'` | JavaScript or `'tinybvh'` WebAssembly |
18
19
  | `bvhWorkerCount` | `'auto'` | Hardware concurrency minus one, clamped 1–4; or positive integer |
19
20
  | `bvhLayout` | `'bvh2'` | Static TinyBVH layout; see [BVH](./bvh.md) |
@@ -41,6 +42,28 @@ Events are `progress` (`{stage: 'device' | 'bvh' | 'shaders', progress}`),
41
42
  is reported once; console errors are deduplicated by message. Invalid light
42
43
  values are clamped and reported while rendering continues.
43
44
 
45
+ ### Texture bindings
46
+
47
+ Use `new Engine({textureBinding: 'auto'})` to try bindless material textures
48
+ on WebGPU. The default `'atlas'` uses the existing texture atlas. This option
49
+ is fixed at construction. Check `engine.getDiagnostics()?.materialTextures`
50
+ for the active mode, fallback reason, scale and estimated texture bytes.
51
+
52
+ Bindless requires a resource-table feature enabled on the device and sufficient
53
+ device limits. Chrome currently exposes an experimental implementation behind
54
+ Unsafe WebGPU Support. Devices without it, including WebGL 2, use atlas textures.
55
+ Caller-owned devices must enable `sampling-resource-table` or
56
+ `chromium-experimental-sampling-resource-table` when available;
57
+ `getRecommendedDeviceDescriptor()` does not request these experimental features.
58
+
59
+ `textureAtlasBudgetBytes` applies to both modes. Bindless keeps independent
60
+ textures and scales them together to fit the estimated pixel budget, preserving
61
+ the current LOD 0 filtering and color rules. GPU allocation overhead, resource
62
+ tables and metadata add memory beyond that estimate. Replacing textures can
63
+ temporarily retain the previous GPU resources. Benefits depend on scene texture
64
+ sizes and editing patterns; bindless does not guarantee faster rendering.
65
+ See [texture updates](./updates.md#materials-textures-and-lights).
66
+
44
67
  ### Device requirements
45
68
 
46
69
  `EngineParams.device` defaults to undefined (the library's shared device). A
@@ -104,6 +127,7 @@ other sequences retain their completed samples.
104
127
  | Option | Default | Meaning |
105
128
  | --- | --- | --- |
106
129
  | `maxSamples` | `2048` | Complete sample budget |
130
+ | `aovs` / `aovSamples` | `[]` / `16` | Optional [surface outputs](#surface-outputs) / independent positive integer sample cap |
107
131
  | `bounces` / `clamping` | `3` / `0` | Scattering depth / per-sample radiance clamp; 0 disables clamping |
108
132
  | `samplingPattern` | `'sobol'` | Sobol, ZSobol or random; see [sampling](./sampling.md) |
109
133
  | `directLightCandidates` | `4` | WebGPU light candidates, clamped 1–16; ignored on WebGL |
@@ -115,7 +139,7 @@ other sequences retain their completed samples.
115
139
  | `width` / `height` | undefined | Paired fixed constructor dimensions; getters report actual output dimensions |
116
140
  | `adaptiveResolution` | `false` | `{targetFrameMs, minScale}` for scale-based renders; both required, positive, minScale ≤ resolutionScale |
117
141
  | `timeBudgetMs` / `maxTiles` | `21` / `Infinity` | Tile time budget / tiles per call |
118
- | `allowMultipleSamplesPerFrame` | `false` | Add multiple samples when time permits |
142
+ | `batchSamples` | `false` | Let one refine submit several samples within timeBudgetMs; see [sample batching](./progressive-rendering.md#sample-batching). `allowMultipleSamplesPerFrame` is a deprecated alias |
119
143
  | `fallback` | `null` | Single-level Render on the same engine |
120
144
  | `denoiser` | `null` | Per-render configuration; read back through the frozen `denoiser` getter and change with `setDenoiser()`; see [denoising](./denoising.md) |
121
145
 
@@ -129,13 +153,15 @@ or alpha, plus its existing early depth termination.
129
153
 
130
154
  | Method / property | Meaning |
131
155
  | --- | --- |
132
- | `refine()` | Trace within this render's budget; returns status |
156
+ | `refine(channel?, options?)` | Trace beauty or an enabled AOV within this render's budget; returns status |
133
157
  | `converge(options)` | Deterministic offline image; see below |
134
158
  | `readPixels(kind = 'hdr')` | Read this render; see [pixel outputs](#pixel-outputs) |
135
159
  | `engine` | Owning engine |
136
160
  | `denoising` | Queued/running inference, including intermediate updates; default false |
137
161
  | `on('sample', listener)` | Complete samples: `{samples, isFirstFrame, frameTimeMs}`; returns unsubscribe |
138
162
  | `samples` | Complete samples since restart; stale engine inputs report 0 |
163
+ | `gpuSampleTimeMs` | Latest WebGPU full beauty sample estimate in ms; undefined without timestamps, for stale inputs or during converge |
164
+ | `whenReadyToRefine()` | Wait for the next release of WebGPU sampling queue capacity; resolves immediately if none is queued or on WebGL; submits no work |
139
165
  | `converged` | `samples >= maxSamples`, independently of filtering |
140
166
  | `status` | Preparing, compiling, converging, denoising, finished or error |
141
167
  | `setDenoiser(options)` | Replace the denoiser configuration; resolves when the filter is ready |
@@ -149,6 +175,14 @@ completed filtering. Synchronous execution failures return `'error'` and set
149
175
  engine.lastError. Invalid lifecycle operations (disposed render or refine during
150
176
  converge) throw.
151
177
 
178
+ `refine(undefined, {batchSamples, targetBatchTimeMs, deferResolve})`
179
+ overrides batching for one call without changing render settings or discarding
180
+ samples. The first two options default to the render's execution settings.
181
+ `targetBatchTimeMs` must be positive and finite, defaults to `timeBudgetMs`, is
182
+ capped by that budget and is ignored on WebGL. `deferResolve` (default false,
183
+ WebGPU only) leaves the displayed image unchanged for calls between display
184
+ frames; the next presentation or pixel read updates it once with every new sample.
185
+
152
186
  A fallback gets one sample when the requested render cannot finish its first
153
187
  sample within budget and the fallback has no current complete image. A ready
154
188
  fallback is not traced again. Fallback chains and cycles are rejected.
@@ -191,7 +225,7 @@ otherwise. Worker visibility and control forwarding are described in
191
225
  | `coalesceSceneUpdates` | false | Defer commits during preview inference, then commit latest camera/scene |
192
226
 
193
227
  `engine`, `display`, `preview` (possibly undefined) and `final` are read-only.
194
- `changing` defaults false; `samples` counts final only and reports 0 during preview; `status` starts preparing.
228
+ `changing` defaults false; `samples` counts final beauty samples and reports 0 during preview; `status` starts preparing.
195
229
  `select({changing})` defaults to changing ? preview : final. `addSync`/`removeSync`
196
230
  manage integrations without disposing them. `tick` executes a manual frame;
197
231
  `start`/`stop` manage animation frames. `converge` accepts the same options as
@@ -228,10 +262,16 @@ quality defaults to the browser encoder value.
228
262
  | `temperature`, `tint`, `vignette` | `0` |
229
263
  | `bloomIntensity`, `bloomThreshold`, `bloomRadius` | `0`, `1`, `0.6` |
230
264
  | `hdrOutputEnabled` | `false`; WebGPU only |
231
- | `channel: DisplayChannel` | `'final'`; also albedo, normal, shadow, shadowCatcher |
265
+ | `channel: DisplayChannel` | `'final'`; also albedo, normal, shadow, shadowCatcher and the seven [AOV kinds](#aov-visualization) |
266
+ | `aovDepthRange` | `[0, 10]`; finite scene-unit black/white depths, `0 <= near < far` |
267
+ | `aovPositionScale` | `1`; finite positive scene-unit half-range around the origin |
268
+ | `aovMotionScale` | `16`; finite positive pixel displacement for full brightness |
269
+ | `aovIdComponent: AovIdComponent` | `'object'`; also instance, material, primitive |
232
270
  | `shadowCatcherColor`, `shadowCatcherAlpha` | `[0, 0, 0]`, `1` |
233
271
 
234
272
  All display settings apply at composition and retain accumulated samples.
273
+ AOV mapping numbers must remain finite in float32; depth endpoints must remain
274
+ distinct after conversion.
235
275
  Use the exported `DisplaySettings` type for a partial batch:
236
276
 
237
277
  ```ts
@@ -259,9 +299,11 @@ semantics are documented in [Custom denoisers](./denoising.md#custom-denoisers).
259
299
 
260
300
  ### Pixel outputs
261
301
 
262
- `readPixels(kind = 'hdr')` returns `{ width, height, data: Float32Array }`,
263
- RGBA with the top row first. Dimensions describe the internal render texture,
264
- which WebGPU rounds up to multiples of 16. Before a pipeline exists, it returns an empty image.
302
+ `readPixels(kind = 'hdr')` returns `{ width, height, data }`. `data` is a
303
+ `Uint32Array` for `id` and a `Float32Array` for every other kind.
304
+ Pixels are RGBA with the top row first. Dimensions describe the internal render
305
+ texture, which WebGPU rounds up to multiples of 16. Before a pipeline exists,
306
+ beauty and OIDN-guide reads return an empty image; AOV reads reject.
265
307
 
266
308
  | Kind | Values |
267
309
  | --- | --- |
@@ -276,6 +318,120 @@ existing pipeline semantics. If no denoiser is attached or denoising has not
276
318
  completed, `denoised` returns `hdr` and warns once per render. The method
277
319
  makes a CPU copy and can be costly; avoid calling it every animation frame.
278
320
 
321
+ ### Surface outputs
322
+
323
+ Request only the surface outputs you need on each render:
324
+
325
+ ```ts
326
+ const render = new Render(engine, {
327
+ aovs: ['depth', 'position', 'id', 'motion'], aovSamples: 16
328
+ });
329
+ await render.converge({samples: 64});
330
+ const depth = await render.readPixels('depth'); // Float32Array, RGBA
331
+ const ids = await render.readPixels('id'); // Uint32Array, RGBA
332
+ ```
333
+
334
+ You can change `render.aovs` or `render.aovSamples` after sampling. Continue
335
+ calling `refine()` to regenerate AOVs for the current view; beauty samples and
336
+ completed denoising are preserved. AOV accumulation restarts when either changes.
337
+
338
+ | Kind | RGBA contents |
339
+ | --- | --- |
340
+ | `depth` | Camera-forward distance in scene units, 0, 0, coverage |
341
+ | `position` | World position xyz, coverage |
342
+ | `roughness` | Specular roughness, metalness, unsupported-reflectance fraction, coverage |
343
+ | `diffuseAlbedo` | Directional-hemispherical diffuse reflectance RGB, valid coverage |
344
+ | `specularAlbedo` | Directional-hemispherical specular reflectance RGB, valid coverage |
345
+ | `id` | Object, local instance (0 for ordinary meshes), material, source triangle index; background is `0xFFFFFFFF` in all channels |
346
+ | `motion` | Previous position minus current position in pixels, x right and y down; z = 0, alpha = 3 for valid history or 0 otherwise |
347
+
348
+ Depth, position, roughness and reflectance average independent jittered primary
349
+ rays, including depth of field, up to `min(samples, aovSamples)`. RGB values use
350
+ hit-count normalization; alpha is hit coverage over all AOV samples. Reflectance
351
+ uses only supported hits for both normalization and valid coverage. A blank pixel
352
+ is zero. These passes preserve beauty samples but do not sample the same paths.
353
+ Alpha holes use the first surface with alpha ≥ 0.5, skipping shadow catchers,
354
+ with at most 32 layers.
355
+
356
+ ID and motion use the unjittered pixel center without depth of field. `Mesh.aovId`
357
+ and `OpenPBRMaterial.aovId` default to process-unique integers. Explicit values
358
+ must be integers from 0 through `0xFFFFFFFE`, unique among the scene's objects
359
+ or materials respectively. Commit changes with `engine.update()`. Keep identities
360
+ stable and avoid reusing an ID for a different object while history is live.
361
+ Instance IDs are local indices; primitive IDs refer to the geometry's original
362
+ triangle order. A ray exactly on a shared edge can select either adjacent
363
+ triangle; host/GPU rounding can choose different IDs at that boundary.
364
+
365
+ Motion history belongs to each render and advances when it produces an AOV
366
+ frame. Camera pose and rigid object transforms can reproject across updates.
367
+ The first frame, changed dimensions or projection, missing identities, changed
368
+ topology or vertex positions, and failed visibility correspondence invalidate
369
+ motion. Device recovery also clears history. Deformed vertices have no motion
370
+ estimate; validity 0 must not be treated as a stationary surface.
371
+
372
+ Reflectance outputs are separate from the existing OIDN `albedo` guide.
373
+ Coat, fuzz, anisotropy, subsurface/volume scattering, bevel, thin film,
374
+ dispersion, partial alpha and nonstandard materials are unsupported by this
375
+ reflectance estimate. Such hits contribute to `roughness.b` and have zero
376
+ reflectance validity. Reflectance uses expensive BSDF quadrature; request it
377
+ only when needed.
378
+
379
+ Reading a kind absent from `render.aovs` rejects. A custom denoiser can request
380
+ its own [AOV inputs](./denoising.md#custom-denoisers). With neither request there
381
+ are no AOV allocations or passes. Enabling any kind costs about 128 GPU bytes
382
+ per pixel on WebGPU or 192 on WebGL, plus scene identity/history data; each
383
+ custom denoiser AOV snapshot adds 16 bytes per pixel. A render may retain one
384
+ reusable snapshot per kind between denoising calls. Readback adds a CPU copy.
385
+ WebGPU dimensions remain subject to device buffer limits. A full-frame AOV
386
+ sample runs independently of tile/adaptive sampling and may exceed the render's
387
+ time budget at large resolutions. WebGL requires `EXT_color_buffer_float`.
388
+
389
+ ### AOV visualization
390
+
391
+ Select an enabled surface output on Display to present or export it:
392
+
393
+ ```ts
394
+ engine.display.channel = 'depth'; // render.aovs must include 'depth'
395
+ engine.display.aovDepthRange = [0, 20];
396
+ engine.display.present(render);
397
+ const png = await engine.display.toBlob(render);
398
+ ```
399
+
400
+ | Channel | Display mapping |
401
+ | --- | --- |
402
+ | `depth` | Black at the near depth, white at the far depth, clamped outside the range |
403
+ | `position` | World xyz mapped to RGB; origin is 0.5 and ±position scale maps to 0/1 |
404
+ | `roughness` | RGB = roughness, metalness, unsupported-reflectance fraction |
405
+ | `diffuseAlbedo`, `specularAlbedo` | Linear reflectance RGB clamped to [0, 1], then sRGB encoded |
406
+ | `id` | Stable pseudocolor for the selected uint32 ID component; colors can collide |
407
+ | `motion` | Direction as hue (+x is red, +y is yellow-green), displacement/scale as brightness |
408
+
409
+ AOV channels bypass tone mapping, grading, bloom, vignette and denoising;
410
+ registered display passes still run. Background and invalid reflectance or
411
+ motion are transparent; accumulated outputs use their coverage as alpha. Zero valid motion
412
+ is opaque black. The first frame's invalid motion is transparent.
413
+ Selecting a kind absent from `render.aovs` rejects, even if a custom denoiser
414
+ requests it. An enabled output awaiting sampling keeps the current canvas;
415
+ export rejects until that output is ready. Display selection alone does not enable tracing.
416
+ Changing the channel or mapping preserves beauty samples, raw AOV values and completed denoising.
417
+ With `RenderLoop`, selecting an enabled AOV traces surface outputs without
418
+ advancing beauty or running a denoiser, including after camera or scene edits.
419
+ When viewing motion, the loop refreshes once inputs settle so stationary surfaces
420
+ return to black. Switching between preview and final first displays zero motion;
421
+ subsequent camera or object changes show motion from that view.
422
+ ID and motion need one center sample; other AOVs stop at the smaller of
423
+ `maxSamples` and `aovSamples`. Loop status can be `finished` while beauty
424
+ `samples` remains zero. Switching back to Final resumes beauty for the current
425
+ view. Direct `render.refine()` and `converge()` still trace beauty and requested
426
+ AOVs together. Use `render.refine(kind)` for a surface-only step and
427
+ `render.aovSamplesRendered` for its progress. The existing `normal` and `albedo` channels keep their OIDN-guide meaning.
428
+
429
+ These mappings also apply to PNG export. Raw `render.readPixels(kind)` retains
430
+ the [surface formats](#surface-outputs) above. WebGPU HDR presentation decodes
431
+ the mapped colors to linear light; the visualization stays within SDR white.
432
+ Visualization retains an extra 8 GPU bytes per render pixel on WebGPU or 4 on
433
+ WebGL until the AOV resources are resized or disposed.
434
+
279
435
  ### Depth sampling
280
436
 
281
437
  After rendering the current scene and camera, call
@@ -311,11 +467,12 @@ Public spatial values use `Vec2` (`[number, number] | Float32Array`), `Vec3` (`[
311
467
  | `Geometry`, `GeometryAttribute` | Triangle data: required `position`; optional `normal` and `indices` are generated at construction; `vertexCount` / `triangleCount` getters; optional `uv`, `uv1`, `tangent`, `color`, `materialIndex` (raw per-vertex indices, itemSize 1; all three corners of each triangle must match; invalid indices fall back to material 0 with one warning); `dynamic` (default `false`) for [deforming geometry](./updates.md#dynamic-geometry) |
312
468
  | `OpenPBRMaterial` | `new OpenPBRMaterial(params?)`; see [materials](./materials.md) |
313
469
  | `Texture` | HTMLImageElement, HTMLCanvasElement, ImageBitmap, OffscreenCanvas or `{ data, width, height }`; `colorSpace`: `'srgb'` or `'linear'`; default undefined uses sRGB for images/bytes and linear for `Float32Array`/`Int32Array`; independent `wrapS` / `wrapT`: `'repeat'` (default), `'clamp-to-edge'`, `'mirrored-repeat'`, applied after UV scale/offset for material textures |
470
+ | `ProceduralSky`, `ProceduralSkyOptions` | Clear-sky environment texture; [usage and options](#proceduralsky) |
314
471
  | `Camera` | `new Camera({ projection: 'perspective', ... })` or orthographic; `position`, `lookAt()`, `copy(source)`, `clone()`; aspect defaults to automatic; depth of field with `fStop`, `focusDistance`, `sensorHeight`, `metersPerUnit` ([details](./getting-started.md#depth-of-field)) |
315
472
  | `RectLight`, `SphereLight`, `SpotLight`, `DirectionalLight`, `EnvironmentLight`, `AmbientLight` | Parameter-object constructors; analytic lights use `transform`, `position`, `lookAt()` and rigid axes. See [lights](./getting-started.md#lights) |
316
473
  | `EnvironmentLight` | `new EnvironmentLight({map, intensity?})`; `intensity` defaults to `1`, scales environment radiance, and accepts `0` to disable lighting. Ambient lights and explicit background textures retain their own brightness. `transform` rotates the environment from local to world space (identity by default); translation and positive axis scale are ignored. An explicit background texture keeps its own orientation |
317
474
  | `IESProfile`, `parseIESProfile(text)` | Photometric profiles for `SpotLight` |
318
- | `SceneBackground` | `[r, g, b, a]`, `Texture` or `GroundedSkyboxBackground`; grounded backgrounds accept `rotation` around +Y in radians (default `0`) and `intensity` (default `1`). These affect only the background; the ground stays level and alpha is preserved |
475
+ | `SceneBackground` | `[r, g, b, a]`, `Texture`, `EquirectBackground` (`{type: 'Equirect', map, transform?, intensity?}`) or `GroundedSkyboxBackground`. Equirect `transform`: column-major map-to-world rotation (default identity); translation and positive axis scale are ignored. Grounded `rotation`: radians around +Y (default `0`). `intensity` on either: nonnegative RGB multiplier, default `1`. These affect only the background; ground stays level and alpha is preserved |
319
476
 
320
477
  `OpenPBRMaterial` accepts canonical `specularRoughnessAnisotropy` (default `0`),
321
478
  `coatRoughnessAnisotropy` (`0`), `subsurfaceScatterAnisotropy` (`0`),
@@ -333,7 +490,8 @@ one. See [thin-film textures](./materials.md#textures) for range behavior.
333
490
  `undefined`. Channels and color decoding are listed in [material textures](./materials.md#textures).
334
491
  They follow the existing material/texture update mechanism.
335
492
 
336
- `coatNormalMap` defaults to `undefined`, retaining the shared shading normal. With a map, linear RGB supplies an independent coat normal; `coatNormalScale` defaults to `[1, 1]` and scales its decoded XY. See [coat normals](./materials.md#coat-normals) for frame, sampling and update behavior.
493
+ `coatNormalMap` defaults to `undefined`; `coatNormalScale` defaults to `[1, 1]`.
494
+ See [coat normals](./materials.md#coat-normals) for independent base and coat normal mapping.
337
495
 
338
496
  Raw texture sources contain exactly `width * height * 4` RGBA values, with
339
497
  rows ordered from top to bottom. `Uint8Array` and `Uint8ClampedArray` store
@@ -361,6 +519,39 @@ and `markUpdated(): this`. `Geometry` also has the read-only `topologyVersion`
361
519
  (initially `0`) and `markVerticesUpdated(): this`, which advances only
362
520
  `version`. See [scene updates](./updates.md) for when to call each.
363
521
 
522
+ ### ProceduralSky
523
+
524
+ Use the sky as an environment map, an explicit background, or the map of a
525
+ `GroundedSkyboxBackground`:
526
+
527
+ ```ts
528
+ import { ProceduralSky, EnvironmentLight } from '@meimu/mutracer';
529
+
530
+ const sky = new ProceduralSky({ resolution: [2048, 1024], time: 15 });
531
+ scene.lights.push(new EnvironmentLight({ map: sky }));
532
+ // With no explicit scene.background, the environment is also the background.
533
+ sky.time = 9;
534
+ engine.update();
535
+ ```
536
+
537
+ | Option / property | Default | Behavior |
538
+ | --- | --- | --- |
539
+ | `resolution` | `[2048, 1024]` | Positive integer equirectangular width and height; assign a new pair to resize |
540
+ | `time` | `15` | Local solar time in hours, from `0` to `24`, for an equinox at latitude 40° |
541
+
542
+ Time and resolution assignments follow the normal [scene update rules](./updates.md).
543
+ Use `EnvironmentLight.intensity` and `transform` for brightness and rotation.
544
+ With Three.js, pass the sky as the `environment` option to `THREESync` or
545
+ `fromTHREEScene`; the Three.js scene still supplies intensity and rotation.
546
+
547
+ Higher resolutions use more memory and can exceed device limits. Frequent
548
+ time edits can be slower with WebGL 2. Dispose the engine when finished;
549
+ `ProceduralSky` needs no separate disposal.
550
+
551
+ This is a clear atmosphere without clouds, stars, moonlight or artificial
552
+ light. Very low resolutions can blur or miss the small sun disk. Use the sky
553
+ for environments and backgrounds, not material texture slots.
554
+
364
555
  ## Helpers
365
556
 
366
557
  | Export | Description |
@@ -372,7 +563,7 @@ and `markUpdated(): this`. `Geometry` also has the read-only `topologyVersion`
372
563
  | `THREESync` | Keeps a `THREE.Scene` in sync: `new THREESync(engine, {scene?, camera?, controls?, updateControls?})`; RenderLoop calls update; dispose when done; conversion hooks `convertMaterial` / `convertLight`; see [Three.js](./threejs.md#custom-conversion) |
373
564
  | `fromTHREECamera(camera, options?)` | Converts a Three.js camera; one-shot conversion; THREESync retains and updates cameras. Reads `focus` and the film height; `FromTHREECameraOptions` sets `fStop` (default `0`), `focusDistance` and `metersPerUnit` (default `1`) |
374
565
  | `fromTHREEScene` | One-shot Three.js scene conversion with FromTHREESceneOptions conversion hooks; every call reruns hooks and creates fresh default objects |
375
- | `FromTHREESceneOptions` | Optional `onMeshConverted(mesh, source, group, groupIndex)` callback; default: none |
566
+ | `FromTHREESceneOptions` | Optional `environment` overrides the Three.js environment with a MuTracer `Texture` or `ProceduralSky` (default undefined); optional `onMeshConverted(mesh, source, group, groupIndex)` callback; default: none |
376
567
  | `VERSION` | Library version |
377
568
 
378
569
  `@meimu/mutracer/oidn` exports weight constants (`OIDN_MODEL_FILENAMES`,
package/docs/denoising.md CHANGED
@@ -6,7 +6,7 @@ albedo and normal guides.
6
6
  - **JNLM**: a built-in joint non-local means filter, on WebGPU and
7
7
  WebGL 2. It needs no weight download.
8
8
  - **OIDN**: [Intel Open Image Denoise](https://www.openimagedenoise.org/)
9
- networks, run through [`oidn-web`](https://www.npmjs.com/package/oidn-web),
9
+ networks, run through [`oidn-webgpu`](https://github.com/pissang/oidn-webgpu),
10
10
  WebGPU only. They take longer and need weights, but are cleaner at one or
11
11
  two samples per pixel, for example for denoising while the camera moves.
12
12
 
@@ -17,16 +17,17 @@ final.setDenoiser({type: 'oidn', model: 'standard'});
17
17
  final.setDenoiser(null); // disable
18
18
  ```
19
19
 
20
- `type` is required: use `jnlm` or `oidn`. `render.denoiser` returns a frozen
21
- copy of the configuration; call `setDenoiser` again to change it. Unknown types,
20
+ `type` is required: use `jnlm`, `oidn`, or a [custom denoiser](#custom-denoisers).
21
+ `render.denoiser` returns a frozen copy of the configuration; call `setDenoiser`
22
+ again to change it. Unknown types,
22
23
  foreign fields and invalid JNLM settings throw synchronously, preserving the
23
- active network and output. Equal implementation settings share the network;
24
+ active filter and output. Equal built-in settings reuse the loaded network;
24
25
  changing only `schedule` preserves active inference, and passing an identical
25
26
  configuration does nothing.
26
27
  Each render has its own configuration and scheduling. Converge waits for its
27
28
  configured filter; intermediate progressive inference reports converging and
28
- final inference reports denoising. Engine caches equal network configurations
29
- and weights, releasing the network after its last render lease is released.
29
+ final inference reports denoising. Built-in networks and weights are shared
30
+ within an engine and released when no render uses them.
30
31
 
31
32
  `setDenoiser` returns a promise that resolves when the filter is ready, which
32
33
  for OIDN means its weights have loaded, and rejects when loading fails. A
@@ -67,7 +68,7 @@ network prewarm results are discarded.
67
68
  | `model` | OIDN network: `'standard'` (default), `'small'` or `'cleanAuxLarge'` |
68
69
  | `weightsBaseUrl` | Directory containing the OIDN `.tza` files |
69
70
  | `schedule` | `'converged'` (default), `'progressive'`, or `'every-sample'` |
70
- | `maxTileSize` | Maximum OIDN tile size; chosen by oidn-web by default |
71
+ | `maxTileSize` | Maximum output tile edge in pixels; default `512` |
71
72
  | `signal` | `AbortSignal` for the OIDN weight download; default undefined |
72
73
  | `weightsUrl` | Explicit custom network; mutually exclusive with model/base directory |
73
74
  | `aux`, `hdr`, `hdrTransfer` | Custom network metadata only; defaults true, true, `'pu'` |
@@ -112,8 +113,11 @@ preview.setDenoiser({type: 'oidn', model: 'small', schedule: 'every-sample'});
112
113
  ```
113
114
 
114
115
  Converged filters the final sample budget. Progressive filters sample 1,
115
- then 4, 8, 16, … and the final budget, with a ten-second limit between
116
- updates. Every-sample filters every complete sample of its render. Coalescing
116
+ then the first count that is at least double the previous update (and at
117
+ least 4) and comes 500 ms or more after it, and finally the sample budget.
118
+ Updates come at least every ten seconds. With [sample batching](./progressive-rendering.md#sample-batching),
119
+ counts grow faster, so the 500 ms floor skips intermediate doublings.
120
+ Every-sample filters every complete sample of its render. Coalescing
117
121
  continuous scene/camera edits is application loop policy; the engine accepts
118
122
  edits only when update is called. See [progressive rendering](./progressive-rendering.md).
119
123
 
@@ -175,7 +179,7 @@ experimental WebNN path. `precision` defaults to `'auto'` (FP16 when
175
179
  shader-f16 is available), with `'fp32'` and `'fp16'` overrides. `dynamicTile`
176
180
  defaults to `true`; use `false` for fixed tiles or an object with
177
181
  `minTileSize` (256), `initialTileSize` (432), `targetTileTimeMs` (16) and
178
- `adjustmentStep` (16). Tile sizes are clamped/aligned by oidn-web.
182
+ `adjustmentStep` (16).
179
183
 
180
184
  All implementation settings participate in the network cache key; `schedule`
181
185
  and download `signal` do not. Renders with different signals share a pending
@@ -204,7 +208,10 @@ const copy: Denoiser = {
204
208
  device.queue.submit([encoder.finish()]);
205
209
  }
206
210
  };
207
- final.setDenoiser({type: 'custom', denoiser: copy, schedule: 'converged'});
211
+ final.setDenoiser({
212
+ type: 'custom', denoiser: copy, schedule: 'converged',
213
+ aovs: ['depth', 'motion']
214
+ });
208
215
  ```
209
216
 
210
217
  The required `type` and `denoiser` choose this branch; `schedule` defaults to
@@ -219,13 +226,21 @@ render sizes is prepared again. `DenoiseInput` contains these buffers:
219
226
  | `albedo` | Linear RGB albedo averaged over complete samples; alpha is primary-hit coverage |
220
227
  | `normal` | Resolved world-space normal averaged and encoded as `normal*.5+.5`, without renormalization; alpha 1 for valid surface data, otherwise zero RGBA |
221
228
  | `output` | Write all linear HDR RGB; output alpha is ignored and the input path coverage is preserved |
229
+ | `aovs` | Optional map of the requested surface-output buffers, such as `input.aovs?.depth`; absent by default |
222
230
 
223
- All four are tightly packed RGBA float32, top row first, exactly
224
- `width * height * 16` bytes, with STORAGE, COPY_SRC and COPY_DST usages.
231
+ Color, albedo, normal and output are tightly packed RGBA float32, top row first,
232
+ exactly `width * height * 16` bytes, with STORAGE, COPY_SRC and COPY_DST usages.
225
233
  Dimensions describe the actual internal image (WebGPU rounds up to 16).
234
+ `CustomDenoiserOptions.aovs` defaults to `[]`. Requests enable the corresponding
235
+ passes automatically; `render.aovs` is needed only for direct pixel readback.
236
+ Each AOV buffer is top row first, exactly `width * height * 16` bytes: `id` is
237
+ RGBA uint32 and the others RGBA float32. Values, validity and sampling limits
238
+ follow [surface outputs](./api.md#surface-outputs). These are immutable snapshots
239
+ of this invocation and cost 16 additional GPU bytes per pixel per requested kind.
240
+
226
241
  Input buffers are read-only. Buffers are borrowed until the promise settles;
227
- do not retain or destroy them. Resolve after submitting every output write;
228
- MuTracer copies the result, waits for GPU work, then releases its queue lease.
242
+ do not retain or destroy them. Resolve after submitting every output write.
243
+
229
244
  Custom `readPixels('denoised')` returns the filtered native HDR ray color,
230
245
  with input path coverage. Display settings then follow the ordinary compose
231
246
  path: flat backgrounds retain their display-space treatment and catcher
package/docs/materials.md CHANGED
@@ -186,7 +186,7 @@ selects `blend`. With none of these inputs, `auto` selects `opaque`.
186
186
 
187
187
  | Property | Description |
188
188
  | --- | --- |
189
- | `shadowCatcher` | Invisible ground that only receives shadows; composite over a transparent background |
189
+ | `shadowCatcher` | Invisible ground that only receives shadows of the environment and analytic lights, not of emissive meshes; composite over a transparent background |
190
190
  | `unlit` | Shows emission without scattering or lighting other surfaces |
191
191
  | `bevelSize` | Rounded-edge shading radius in scene units; `0` disables (see below) |
192
192
 
@@ -260,41 +260,32 @@ with coat, and the finite path-event budget can truncate deep glass paths.
260
260
  ## Coat normals
261
261
 
262
262
  Set `coatNormalMap` to give the coat a tangent-space normal independently of
263
- `normalMap`. The renderer decodes linear RGB as `2 * RGB - 1`, multiplies XY
264
- by `coatNormalScale`, then normalizes. The scale defaults to `[1, 1]` and can
265
- be negative or zero. A missing map (the default), zero coat weight or zero sampled coat coverage retains the existing shared
266
- shading normal, including the base normal map.
267
-
268
- The coat TBN starts from the interpolated geometry normal before base normal
269
- mapping. It uses the coat texture's UV0/UV1 selection and geometric
270
- tangent handedness; generated tangents use UV1 differentials when that set is
271
- selected. Degenerate frames and zero decoded vectors use stable fallbacks.
272
- The texture's scale, offset and `flipY` follow the ordinary atlas sampling
273
- rules. As with other slots, sharing an image never changes its color space.
274
-
275
- Base lobes keep their base frame. Coat reflection, its sampling PDF, directional
276
- albedo and passage absorption use the coat frame; the renderer combines their
277
- responses and densities for the same world direction. Each frame is corrected
278
- against the geometric/view hemisphere independently. Triangle normals still
279
- control reflection versus transmission, medium boundaries and ray offsets.
280
- A coat reflection can contribute below the base shading hemisphere when it
281
- remains above the coat and geometric hemispheres. As before, solid interior
282
- hits omit coat reflection; thin surfaces are treated as exterior hits.
263
+ `normalMap`. Without a coat map (the default), the coat uses the interpolated
264
+ surface normal before base normal mapping. A bumpy base can therefore have a
265
+ smooth coat. Assign the same texture to both maps to perturb both layers.
283
266
 
284
- Strongly tilted shading normals lose some lobe support when clipped to the
285
- geometric hemisphere. A unit white environment can therefore render below
286
- unit radiance; the renderer does not renormalize the clipped lobes.
267
+ The map decodes linear RGB as `2 * RGB - 1`, multiplies XY by
268
+ `coatNormalScale`, then normalizes. The scale defaults to `[1, 1]` and can be
269
+ negative or zero. UV0/UV1 selection, scale, offset and `flipY` follow the
270
+ ordinary texture rules. Zero coat weight or zero sampled coat coverage
271
+ disables the coat without changing the base normal.
287
272
 
288
273
  Replacing the map or editing its scale is detected by `engine.update()`. Call
289
274
  `texture.markUpdated()` after pixel or UV-array element edits; see [scene updates](./updates.md).
290
- These edits update material parameters/textures without rebuilding geometry. Normal maps do not alter
291
- emission's existing normal-incidence absorption or light-selection proposals.
275
+
276
+ Coat reflections can remain visible where the base normal points away from
277
+ the light. Solid interior hits omit coat reflection; thin surfaces are
278
+ treated as exterior hits.
279
+
280
+ Strongly tilted shading normals lose some lobe support when clipped to the
281
+ geometric hemisphere. A unit white environment can therefore render below
282
+ unit radiance; the renderer does not renormalize the clipped lobes.
292
283
 
293
284
  ## Anisotropy directions
294
285
 
295
286
  Set `specularAnisotropyRotation` or provide `anisotropyMap` to orient the base specular lobe. The map reads linear `2 * RG - 1`, normalizes the direction, multiplies strength by B, and then applies the rotation. A decoded squared length at or below `1e-4` falls back to `(1, 0)` before rotation (including the two central RGBA8 codes). Without a map the direction is `(1, 0)` and the strength multiplier is one. The rotation defaults to zero; the map defaults to undefined.
296
287
 
297
- `coatAnisotropyRotation` defaults to zero and uses the geometry tangent independently of the base map. Base and coat evaluation, sampling and PDF use their respective directions. The coat uses the shared shading normal unless `coatNormalMap` selects an [independent normal](#coat-normals). Zero strength ignores the new direction and retains the original isotropic path.
288
+ `coatAnisotropyRotation` defaults to zero and uses the geometry tangent independently of the base map. Base and coat use their respective directions and [normals](#coat-normals). Zero anisotropy strength ignores the direction.
298
289
 
299
290
  Provided tangent XYZ and optional W handedness are preserved. For XYZ tangents the sign comes from triangle UV derivatives. Missing or degenerate tangents use UV derivatives and then a stable orthonormal fallback; missing tangents produce one diagnostic per geometry. The direction texture's selected UV set is used for derivative fallback, including UV1. A provided XYZW tangent remains authoritative. Directions are reorthogonalized against the final shading normal, including normal maps and back faces, and instance transforms preserve mirrored handedness. UV scale, offset and flipY affect texel lookup; they do not rotate the encoded direction.
300
291
 
@@ -18,8 +18,11 @@ A tuning checklist. Each item links to the page with the details.
18
18
  - Use a preview Render with cheaper quality and optional adaptiveResolution:
19
19
  `{targetFrameMs: 20, minScale: .25}`, plus resolutionScale .5. Choose it during
20
20
  motion and configure final.fallback once; see [progressive rendering](./progressive-rendering.md).
21
- - timeBudgetMs (default 21) bounds each render's tile cost. Raise it for exports.
22
- - allowMultipleSamplesPerFrame adds several samples per refine when time permits.
21
+ - `timeBudgetMs` (default 21) targets each render's tile cost. Raise it for exports.
22
+ - `batchSamples` lets a still render use the whole GPU instead of one sample per
23
+ display frame; enable it with `RenderLoop({final: {batchSamples: true}})`. In your
24
+ own loop, a larger `timeBudgetMs` batches more per call but delays the first
25
+ preview after an edit. See [sample batching](./progressive-rendering.md#sample-batching).
23
26
  - For exports, `render.converge` with `targetBatchTimeMs` adapts batch sizes
24
27
  to the GPU; omit `previewIntervalMs` for maximum throughput.
25
28
 
@@ -17,6 +17,7 @@ edits start final immediately; consecutive changes without explicit sync activit
17
17
  hold preview for idleMs (default 150 ms). When interaction ends, final starts after
18
18
  the current preview completes; its fallback remains visible until final completes a sample.
19
19
  With intermediate denoising enabled, it stays visible until the first filtered final image is ready.
20
+ To keep the GPU busy while the view is still, enable [sample batching](#sample-batching) on final.
20
21
 
21
22
  ```ts
22
23
  loop.select = ({changing}) => changing || slider.dragging ? 'preview' : 'final';
@@ -84,6 +85,27 @@ it runs, object edits and scene/camera replacement have no effect on tracing.
84
85
  Call it once after updating your scene and controls, even when several renders
85
86
  are refined that frame. See [scene updates](updates.md) for notifications.
86
87
 
88
+ ## Sample batching
89
+
90
+ Without batching, a still image gets one sample per display frame, so a fast GPU
91
+ idles for most of each frame. With `batchSamples`, sampling works on two levels:
92
+
93
+ - **Per call.** One `refine()` may submit several samples while at most
94
+ `timeBudgetMs` of GPU work is queued, and returns without waiting for them.
95
+ Without `batchSamples`, a call traces one sample or tile.
96
+ - **Between frames.** RenderLoop also refines its final render each time the GPU
97
+ finishes queued work, until the next display frame. It keeps about 4 ms of work
98
+ queued, or one sample if that is longer, so a camera edit waits behind at most
99
+ that much work at any refresh rate.
100
+
101
+ Batching is off by default. Enable it on the loop's final render with
102
+ `final: {batchSamples: true}`, or on a Render you supply. RenderLoop stops
103
+ batching while the view is changing.
104
+
105
+ In your own loop, `batchSamples` gives the first level. For the second, call
106
+ `render.refine(undefined, {targetBatchTimeMs: 4, deferResolve: true})` after each
107
+ `await render.whenReadyToRefine()` until your next frame; see [Render](./api.md#render).
108
+
87
109
  ## Reading and exporting
88
110
 
89
111
  A fixed-size render does not need to be displayed. Its camera defaults to the
@@ -136,7 +158,8 @@ lower preview's scale; resizing the display canvas affects both.
136
158
 
137
159
  Call `render.setDenoiser({ type: 'jnlm', schedule: 'converged' })` with JNLM or an OIDN
138
160
  configuration. The schedules are `converged` (default), `progressive` (first
139
- sample, then 4, 8, 16, … with a ten-second limit), and `every-sample`. Same-engine
161
+ sample, then doubling counts at least 500 ms apart; see [scheduling](./denoising.md#scheduling)),
162
+ and `every-sample`. Same-engine
140
163
  renders share equal filter configurations and OIDN weight downloads. The engine
141
164
  releases a network when its last render lease is released. OIDN is ignored with
142
165
  a warning on WebGL; JNLM works on both backends.
@@ -150,6 +173,14 @@ final sample does not render the fallback. `present` selects the requested
150
173
  complete image, then its complete fallback, and otherwise leaves the canvas
151
174
  unchanged. It never traces.
152
175
 
176
+ Sample batching on WebGPU needs GPU timestamps; without them, a call traces one
177
+ sample, and between-frame refines are WebGPU-only. A larger `timeBudgetMs`
178
+ batches more per call, but an edit made just after waits for that work before its
179
+ preview starts. Batching keeps the GPU at full speed until `maxSamples`, which
180
+ draws more power than one sample per frame. `stop()` ends between-frame refines
181
+ at once, though submitted work still finishes; a manual `tick()` refines between
182
+ frames only until the next display frame.
183
+
153
184
  `prepare(nextScene)` performs optional CPU BVH preparation without disturbing
154
185
  the current GPU scene. Setting `engine.scene` and calling `update` commits a
155
186
  scene switch for every render. Only one GPU scene resides in an engine.