@meimu/mutracer 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (372) hide show
  1. package/CHANGELOG.md +222 -0
  2. package/LICENSES/FidelityFX-FSR.txt +19 -0
  3. package/LICENSES/pbrt-v4-Apache-2.0.txt +202 -0
  4. package/README.md +3 -0
  5. package/THIRD_PARTY_NOTICES.md +18 -4
  6. package/dist/assets/bvh.worker.js +1 -1
  7. package/dist/mutracer.umd.js +9328 -7614
  8. package/docs/api.md +83 -35
  9. package/docs/caustics.md +80 -0
  10. package/docs/materials.md +9 -6
  11. package/docs/performance.md +7 -0
  12. package/docs/post-processing.md +62 -9
  13. package/docs/progressive-rendering.md +53 -9
  14. package/docs/sampling.md +4 -0
  15. package/docs/technical/acceleration.md +52 -0
  16. package/docs/technical/aovs.md +79 -0
  17. package/docs/technical/caustics.md +109 -0
  18. package/docs/technical/continuous-spectrum.md +122 -0
  19. package/docs/technical/display-upscale.md +82 -0
  20. package/docs/technical/openpbr.md +99 -0
  21. package/docs/technical/pbrt-import.md +168 -0
  22. package/docs/technical/tiling.md +74 -0
  23. package/docs/technical/webgl.md +53 -0
  24. package/docs/updates.md +3 -1
  25. package/docs/viewer-checklist.md +65 -0
  26. package/docs/viewer.md +18 -6
  27. package/lib/Display.d.ts +19 -5
  28. package/lib/Display.js +10 -7
  29. package/lib/Display.js.map +1 -1
  30. package/lib/Engine.d.ts +4 -1
  31. package/lib/Engine.js +77 -14
  32. package/lib/Engine.js.map +1 -1
  33. package/lib/Render.d.ts +81 -24
  34. package/lib/Render.js +201 -26
  35. package/lib/Render.js.map +1 -1
  36. package/lib/RenderLoop.d.ts +7 -3
  37. package/lib/RenderLoop.js +68 -44
  38. package/lib/RenderLoop.js.map +1 -1
  39. package/lib/main.d.ts +4 -2
  40. package/lib/main.js +1 -0
  41. package/lib/main.js.map +1 -1
  42. package/lib/oidn.d.ts +1 -1
  43. package/lib/renderer/AovPrimitiveCache.js +39 -8
  44. package/lib/renderer/AovPrimitiveCache.js.map +1 -1
  45. package/lib/renderer/DisplayPostProcess.d.ts +8 -3
  46. package/lib/renderer/DisplayPostProcess.js +51 -18
  47. package/lib/renderer/DisplayPostProcess.js.map +1 -1
  48. package/lib/renderer/GPUTraceQueue.d.ts +2 -1
  49. package/lib/renderer/GPUTraceQueue.js +3 -1
  50. package/lib/renderer/GPUTraceQueue.js.map +1 -1
  51. package/lib/renderer/MaterialData.js +7 -2
  52. package/lib/renderer/MaterialData.js.map +1 -1
  53. package/lib/renderer/RenderingPipeline.d.ts +11 -1
  54. package/lib/renderer/RenderingPipeline.js.map +1 -1
  55. package/lib/renderer/TileRender.d.ts +2 -2
  56. package/lib/renderer/TileRender.js +128 -7
  57. package/lib/renderer/TileRender.js.map +1 -1
  58. package/lib/renderer/WGPUAov.d.ts +6 -1
  59. package/lib/renderer/WGPUAov.js +108 -14
  60. package/lib/renderer/WGPUAov.js.map +1 -1
  61. package/lib/renderer/WGPUCanvasSurface.js +12 -1
  62. package/lib/renderer/WGPUCanvasSurface.js.map +1 -1
  63. package/lib/renderer/WGPUCausticLightPass.d.ts +48 -0
  64. package/lib/renderer/WGPUCausticLightPass.js +217 -0
  65. package/lib/renderer/WGPUCausticLightPass.js.map +1 -0
  66. package/lib/renderer/WGPUEnvMapDistribution.js +19 -19
  67. package/lib/renderer/WGPUFullQuadPass.js +3 -3
  68. package/lib/renderer/WGPUPhotonMap.d.ts +37 -0
  69. package/lib/renderer/WGPUPhotonMap.js +191 -0
  70. package/lib/renderer/WGPUPhotonMap.js.map +1 -0
  71. package/lib/renderer/WGPURayTraceOutput.d.ts +13 -1
  72. package/lib/renderer/WGPURayTraceOutput.js +122 -28
  73. package/lib/renderer/WGPURayTraceOutput.js.map +1 -1
  74. package/lib/renderer/WGPURayTracePass.d.ts +65 -21
  75. package/lib/renderer/WGPURayTracePass.js +730 -409
  76. package/lib/renderer/WGPURayTracePass.js.map +1 -1
  77. package/lib/renderer/WGPURenderingPipeline.js +110 -68
  78. package/lib/renderer/WGPURenderingPipeline.js.map +1 -1
  79. package/lib/renderer/WebGLAov.js +3 -3
  80. package/lib/renderer/WebGLAov.js.map +1 -1
  81. package/lib/renderer/WebGLFullQuadPass.d.ts +2 -1
  82. package/lib/renderer/WebGLFullQuadPass.js +10 -0
  83. package/lib/renderer/WebGLFullQuadPass.js.map +1 -1
  84. package/lib/renderer/WebGLJNLMDenoiser.js +66 -66
  85. package/lib/renderer/WebGLJNLMDenoiser.js.map +1 -1
  86. package/lib/renderer/WebGLRayTraceOutputPass.js +4 -4
  87. package/lib/renderer/WebGLRayTracePass.d.ts +20 -11
  88. package/lib/renderer/WebGLRayTracePass.js +157 -116
  89. package/lib/renderer/WebGLRayTracePass.js.map +1 -1
  90. package/lib/renderer/WebGLRenderingPipeline.js +17 -14
  91. package/lib/renderer/WebGLRenderingPipeline.js.map +1 -1
  92. package/lib/renderer/WebGLSkyBaker.js +60 -60
  93. package/lib/renderer/accumulationTarget.d.ts +2 -1
  94. package/lib/renderer/accumulationTarget.js.map +1 -1
  95. package/lib/renderer/aov.d.ts +13 -1
  96. package/lib/renderer/aov.js +21 -2
  97. package/lib/renderer/aov.js.map +1 -1
  98. package/lib/renderer/aovDisplay.js +2 -2
  99. package/lib/renderer/aovDisplay.js.map +1 -1
  100. package/lib/renderer/binnedSahBvh.d.ts +2 -1
  101. package/lib/renderer/binnedSahBvh.js +24 -17
  102. package/lib/renderer/binnedSahBvh.js.map +1 -1
  103. package/lib/renderer/bvhAccel.d.ts +3 -1
  104. package/lib/renderer/bvhAccel.js +36 -6
  105. package/lib/renderer/bvhAccel.js.map +1 -1
  106. package/lib/renderer/cameraUniform.d.ts +1 -1
  107. package/lib/renderer/cameraUniform.js +6 -2
  108. package/lib/renderer/cameraUniform.js.map +1 -1
  109. package/lib/renderer/causticDiagnostics.d.ts +31 -0
  110. package/lib/renderer/causticDiagnostics.js +5 -0
  111. package/lib/renderer/causticDiagnostics.js.map +1 -0
  112. package/lib/renderer/causticEmission.d.ts +10 -0
  113. package/lib/renderer/causticEmission.js +40 -0
  114. package/lib/renderer/causticEmission.js.map +1 -0
  115. package/lib/renderer/causticUniform.d.ts +12 -0
  116. package/lib/renderer/causticUniform.js +22 -0
  117. package/lib/renderer/causticUniform.js.map +1 -0
  118. package/lib/renderer/caustics.d.ts +16 -0
  119. package/lib/renderer/caustics.js +93 -0
  120. package/lib/renderer/caustics.js.map +1 -0
  121. package/lib/renderer/compactVertices.d.ts +14 -0
  122. package/lib/renderer/compactVertices.js +124 -0
  123. package/lib/renderer/compactVertices.js.map +1 -0
  124. package/lib/renderer/easu.d.ts +11 -0
  125. package/lib/renderer/easu.js +156 -0
  126. package/lib/renderer/easu.js.map +1 -0
  127. package/lib/renderer/geometryUpdateState.d.ts +1 -0
  128. package/lib/renderer/geometryUpdateState.js.map +1 -1
  129. package/lib/renderer/geometryUpdates.js +3 -2
  130. package/lib/renderer/geometryUpdates.js.map +1 -1
  131. package/lib/renderer/glsl/aovDisplay.frag.js +5 -5
  132. package/lib/renderer/glsl/aovReflectance.glsl.js +53 -53
  133. package/lib/renderer/glsl/aovReflectanceSamples.glsl.js +1 -1
  134. package/lib/renderer/glsl/aovTrace.frag.js +45 -37
  135. package/lib/renderer/glsl/aovTrace.frag.js.map +1 -1
  136. package/lib/renderer/glsl/chunks/cameraProjection.glsl.d.ts +2 -0
  137. package/lib/renderer/glsl/chunks/cameraProjection.glsl.js +16 -0
  138. package/lib/renderer/glsl/chunks/cameraProjection.glsl.js.map +1 -0
  139. package/lib/renderer/glsl/chunks/cameraRay.glsl.d.ts +1 -1
  140. package/lib/renderer/glsl/chunks/cameraRay.glsl.js +9 -16
  141. package/lib/renderer/glsl/chunks/cameraRay.glsl.js.map +1 -1
  142. package/lib/renderer/glsl/chunks/emissiveLight.glsl.d.ts +2 -0
  143. package/lib/renderer/glsl/chunks/emissiveLight.glsl.js +35 -0
  144. package/lib/renderer/glsl/chunks/emissiveLight.glsl.js.map +1 -0
  145. package/lib/renderer/glsl/chunks/envMap.glsl.js +73 -73
  146. package/lib/renderer/glsl/chunks/intersect.glsl.js +478 -465
  147. package/lib/renderer/glsl/chunks/intersect.glsl.js.map +1 -1
  148. package/lib/renderer/glsl/chunks/materialBuffer.glsl.d.ts +1 -0
  149. package/lib/renderer/glsl/chunks/materialBuffer.glsl.js +156 -153
  150. package/lib/renderer/glsl/chunks/materialBuffer.glsl.js.map +1 -1
  151. package/lib/renderer/glsl/chunks/openPbrBsdf.glsl.js +771 -771
  152. package/lib/renderer/glsl/chunks/rand.glsl.js +3 -3
  153. package/lib/renderer/glsl/chunks/random.glsl.js +6 -6
  154. package/lib/renderer/glsl/chunks/rayTraceCore.glsl.d.ts +1 -0
  155. package/lib/renderer/glsl/chunks/rayTraceCore.glsl.js +70 -63
  156. package/lib/renderer/glsl/chunks/rayTraceCore.glsl.js.map +1 -1
  157. package/lib/renderer/glsl/chunks/sampleLight.glsl.js +89 -75
  158. package/lib/renderer/glsl/chunks/sampleLight.glsl.js.map +1 -1
  159. package/lib/renderer/glsl/chunks/sampleMaterial.glsl.d.ts +1 -0
  160. package/lib/renderer/glsl/chunks/sampleMaterial.glsl.js +160 -118
  161. package/lib/renderer/glsl/chunks/sampleMaterial.glsl.js.map +1 -1
  162. package/lib/renderer/glsl/chunks/sampleShadowCatcher.glsl.js +14 -14
  163. package/lib/renderer/glsl/chunks/sampling.glsl.js +88 -91
  164. package/lib/renderer/glsl/chunks/sampling.glsl.js.map +1 -1
  165. package/lib/renderer/glsl/chunks/sobol.glsl.js +79 -79
  166. package/lib/renderer/glsl/chunks/spectralPrefix.glsl.d.ts +6 -0
  167. package/lib/renderer/glsl/chunks/spectralPrefix.glsl.js +47 -0
  168. package/lib/renderer/glsl/chunks/spectralPrefix.glsl.js.map +1 -0
  169. package/lib/renderer/glsl/chunks/textureLinear.glsl.js +5 -5
  170. package/lib/renderer/glsl/chunks/uvProjection.glsl.js +11 -11
  171. package/lib/renderer/glsl/depthQuery.frag.js +13 -7
  172. package/lib/renderer/glsl/depthQuery.frag.js.map +1 -1
  173. package/lib/renderer/glsl/normalizeRayTraceOutput.frag.js +10 -10
  174. package/lib/renderer/glsl/rayTrace.frag.d.ts +1 -1
  175. package/lib/renderer/glsl/rayTrace.frag.js +338 -252
  176. package/lib/renderer/glsl/rayTrace.frag.js.map +1 -1
  177. package/lib/renderer/glsl/shadowCatcher.frag.js +27 -26
  178. package/lib/renderer/glsl/shadowCatcher.frag.js.map +1 -1
  179. package/lib/renderer/glsl/webglPipeline.frag.js +59 -59
  180. package/lib/renderer/gpuMemory.js +1 -1
  181. package/lib/renderer/gpuMemory.js.map +1 -1
  182. package/lib/renderer/lightSampling.d.ts +3 -2
  183. package/lib/renderer/lightSampling.js +14 -8
  184. package/lib/renderer/lightSampling.js.map +1 -1
  185. package/lib/renderer/openPbrEnergyLut.d.ts +2 -1
  186. package/lib/renderer/openPbrEnergyLut.js +18 -1
  187. package/lib/renderer/openPbrEnergyLut.js.map +1 -1
  188. package/lib/renderer/pathLimits.d.ts +5 -4
  189. package/lib/renderer/pathLimits.js +6 -5
  190. package/lib/renderer/pathLimits.js.map +1 -1
  191. package/lib/renderer/prepareMeshBVH.d.ts +2 -0
  192. package/lib/renderer/prepareMeshBVH.js +5 -3
  193. package/lib/renderer/prepareMeshBVH.js.map +1 -1
  194. package/lib/renderer/progressiveScheduler.js +5 -4
  195. package/lib/renderer/progressiveScheduler.js.map +1 -1
  196. package/lib/renderer/renderPipeline.d.ts +5 -0
  197. package/lib/renderer/renderPipeline.js +1 -0
  198. package/lib/renderer/renderPipeline.js.map +1 -1
  199. package/lib/renderer/researchTransport.d.ts +31 -0
  200. package/lib/renderer/researchTransport.js +5 -0
  201. package/lib/renderer/researchTransport.js.map +1 -0
  202. package/lib/renderer/sceneAssembly.d.ts +1 -1
  203. package/lib/renderer/sceneAssembly.js +4 -3
  204. package/lib/renderer/sceneAssembly.js.map +1 -1
  205. package/lib/renderer/sceneChangeDetector.d.ts +1 -1
  206. package/lib/renderer/sceneChangeDetector.js +3 -1
  207. package/lib/renderer/sceneChangeDetector.js.map +1 -1
  208. package/lib/renderer/sceneLayoutSchema.d.ts +2 -0
  209. package/lib/renderer/sceneLayoutSchema.js +3 -3
  210. package/lib/renderer/sceneLayoutSchema.js.map +1 -1
  211. package/lib/renderer/scenePreparation.d.ts +3 -2
  212. package/lib/renderer/scenePreparation.js +18 -2
  213. package/lib/renderer/scenePreparation.js.map +1 -1
  214. package/lib/renderer/scratchSampling.js +6 -6
  215. package/lib/renderer/spectrum.d.ts +21 -0
  216. package/lib/renderer/spectrum.js +154 -0
  217. package/lib/renderer/spectrum.js.map +1 -0
  218. package/lib/renderer/staticBvhPreparation.d.ts +9 -0
  219. package/lib/renderer/staticBvhPreparation.js +8 -2
  220. package/lib/renderer/staticBvhPreparation.js.map +1 -1
  221. package/lib/renderer/surfaceHit.d.ts +10 -0
  222. package/lib/renderer/surfaceHit.js +22 -0
  223. package/lib/renderer/surfaceHit.js.map +1 -0
  224. package/lib/renderer/tangentRequirements.d.ts +2 -0
  225. package/lib/renderer/tangentRequirements.js +7 -0
  226. package/lib/renderer/tangentRequirements.js.map +1 -0
  227. package/lib/renderer/textureAtlas.d.ts +4 -0
  228. package/lib/renderer/textureAtlas.js +48 -2
  229. package/lib/renderer/textureAtlas.js.map +1 -1
  230. package/lib/renderer/webgl.js +4 -0
  231. package/lib/renderer/webgl.js.map +1 -1
  232. package/lib/renderer/webglLightSampling.d.ts +39 -0
  233. package/lib/renderer/webglLightSampling.js +15 -0
  234. package/lib/renderer/webglLightSampling.js.map +1 -0
  235. package/lib/renderer/webglVertexAttributes.d.ts +17 -0
  236. package/lib/renderer/webglVertexAttributes.js +44 -0
  237. package/lib/renderer/webglVertexAttributes.js.map +1 -0
  238. package/lib/renderer/wgpuBindlessSource.js +3 -3
  239. package/lib/renderer/wgpuMaterialConstants.js +4 -3
  240. package/lib/renderer/wgpuMaterialConstants.js.map +1 -1
  241. package/lib/renderer/wgpuSceneUniform.js +2 -2
  242. package/lib/renderer/wgpuSceneUniform.js.map +1 -1
  243. package/lib/renderer/wgpuShaderSource.d.ts +22 -0
  244. package/lib/renderer/wgpuShaderSource.js +3 -1
  245. package/lib/renderer/wgpuShaderSource.js.map +1 -1
  246. package/lib/renderer/wgsl/aovCompact.wgsl.d.ts +4 -0
  247. package/lib/renderer/wgsl/aovCompact.wgsl.js +26 -0
  248. package/lib/renderer/wgsl/aovCompact.wgsl.js.map +1 -0
  249. package/lib/renderer/wgsl/aovDisplay.wgsl.js +9 -9
  250. package/lib/renderer/wgsl/aovReflectance.wgsl.js +55 -55
  251. package/lib/renderer/wgsl/aovReflectanceSamples.wgsl.js +1 -1
  252. package/lib/renderer/wgsl/aovResolve.wgsl.d.ts +4 -0
  253. package/lib/renderer/wgsl/aovResolve.wgsl.js +21 -2
  254. package/lib/renderer/wgsl/aovResolve.wgsl.js.map +1 -1
  255. package/lib/renderer/wgsl/aovSnapshot.wgsl.js +2 -2
  256. package/lib/renderer/wgsl/aovSnapshot.wgsl.js.map +1 -1
  257. package/lib/renderer/wgsl/aovTrace.wgsl.js +33 -33
  258. package/lib/renderer/wgsl/aovTraceDisplay.wgsl.d.ts +2 -0
  259. package/lib/renderer/wgsl/aovTraceDisplay.wgsl.js +56 -0
  260. package/lib/renderer/wgsl/aovTraceDisplay.wgsl.js.map +1 -0
  261. package/lib/renderer/wgsl/bloom.wgsl.js +18 -18
  262. package/lib/renderer/wgsl/causticLight.compute.d.ts +6 -0
  263. package/lib/renderer/wgsl/causticLight.compute.js +539 -0
  264. package/lib/renderer/wgsl/causticLight.compute.js.map +1 -0
  265. package/lib/renderer/wgsl/chunks/background.wgsl.d.ts +2 -0
  266. package/lib/renderer/wgsl/chunks/background.wgsl.js +102 -0
  267. package/lib/renderer/wgsl/chunks/background.wgsl.js.map +1 -0
  268. package/lib/renderer/wgsl/chunks/causticAdjoint.wgsl.d.ts +2 -0
  269. package/lib/renderer/wgsl/chunks/causticAdjoint.wgsl.js +12 -0
  270. package/lib/renderer/wgsl/chunks/causticAdjoint.wgsl.js.map +1 -0
  271. package/lib/renderer/wgsl/chunks/causticAtomic.wgsl.d.ts +2 -0
  272. package/lib/renderer/wgsl/chunks/causticAtomic.wgsl.js +16 -0
  273. package/lib/renderer/wgsl/chunks/causticAtomic.wgsl.js.map +1 -0
  274. package/lib/renderer/wgsl/chunks/caustics.wgsl.d.ts +1 -0
  275. package/lib/renderer/wgsl/chunks/caustics.wgsl.js +62 -0
  276. package/lib/renderer/wgsl/chunks/caustics.wgsl.js.map +1 -0
  277. package/lib/renderer/wgsl/chunks/common.wgsl.js +4 -4
  278. package/lib/renderer/wgsl/chunks/envMap.wgsl.js +37 -37
  279. package/lib/renderer/wgsl/chunks/intersect.wgsl.d.ts +6 -2
  280. package/lib/renderer/wgsl/chunks/intersect.wgsl.js +634 -620
  281. package/lib/renderer/wgsl/chunks/intersect.wgsl.js.map +1 -1
  282. package/lib/renderer/wgsl/chunks/intersectionAlpha.wgsl.d.ts +1 -1
  283. package/lib/renderer/wgsl/chunks/intersectionAlpha.wgsl.js +23 -21
  284. package/lib/renderer/wgsl/chunks/intersectionAlpha.wgsl.js.map +1 -1
  285. package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.d.ts +3 -1
  286. package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.js +77 -74
  287. package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.js.map +1 -1
  288. package/lib/renderer/wgsl/chunks/openPbrBsdf.wgsl.js +795 -795
  289. package/lib/renderer/wgsl/chunks/pdm_displacement.wgsl.js +139 -139
  290. package/lib/renderer/wgsl/chunks/rand.wgsl.js +3 -3
  291. package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.d.ts +5 -1
  292. package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.js +45 -29
  293. package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.js.map +1 -1
  294. package/lib/renderer/wgsl/chunks/sampleLight.wgsl.d.ts +1 -1
  295. package/lib/renderer/wgsl/chunks/sampleLight.wgsl.js +142 -138
  296. package/lib/renderer/wgsl/chunks/sampleLight.wgsl.js.map +1 -1
  297. package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.d.ts +1 -1
  298. package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.js +228 -189
  299. package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.js.map +1 -1
  300. package/lib/renderer/wgsl/chunks/sampleShadowCatcher.wgsl.d.ts +1 -1
  301. package/lib/renderer/wgsl/chunks/sampleShadowCatcher.wgsl.js +11 -11
  302. package/lib/renderer/wgsl/chunks/sampleShadowCatcher.wgsl.js.map +1 -1
  303. package/lib/renderer/wgsl/chunks/sampling.wgsl.d.ts +1 -1
  304. package/lib/renderer/wgsl/chunks/sampling.wgsl.js +88 -92
  305. package/lib/renderer/wgsl/chunks/sampling.wgsl.js.map +1 -1
  306. package/lib/renderer/wgsl/chunks/sobol.wgsl.js +83 -83
  307. package/lib/renderer/wgsl/chunks/spectralPrefix.wgsl.d.ts +6 -0
  308. package/lib/renderer/wgsl/chunks/spectralPrefix.wgsl.js +66 -0
  309. package/lib/renderer/wgsl/chunks/spectralPrefix.wgsl.js.map +1 -0
  310. package/lib/renderer/wgsl/chunks/uvProjection.wgsl.js +11 -11
  311. package/lib/renderer/wgsl/chunks/vertexAttributes.wgsl.d.ts +2 -0
  312. package/lib/renderer/wgsl/chunks/vertexAttributes.wgsl.js +61 -0
  313. package/lib/renderer/wgsl/chunks/vertexAttributes.wgsl.js.map +1 -0
  314. package/lib/renderer/wgsl/compose.frag.wgsl.d.ts +2 -0
  315. package/lib/renderer/wgsl/compose.frag.wgsl.js +100 -98
  316. package/lib/renderer/wgsl/compose.frag.wgsl.js.map +1 -1
  317. package/lib/renderer/wgsl/customDenoise.wgsl.js +10 -10
  318. package/lib/renderer/wgsl/depthQuery.wgsl.d.ts +1 -1
  319. package/lib/renderer/wgsl/depthQuery.wgsl.js +12 -3
  320. package/lib/renderer/wgsl/depthQuery.wgsl.js.map +1 -1
  321. package/lib/renderer/wgsl/easu.wgsl.d.ts +2 -0
  322. package/lib/renderer/wgsl/easu.wgsl.js +80 -0
  323. package/lib/renderer/wgsl/easu.wgsl.js.map +1 -0
  324. package/lib/renderer/wgsl/easuDetail.wgsl.d.ts +21 -0
  325. package/lib/renderer/wgsl/easuDetail.wgsl.js +230 -0
  326. package/lib/renderer/wgsl/easuDetail.wgsl.js.map +1 -0
  327. package/lib/renderer/wgsl/jnlm.compute.js +74 -74
  328. package/lib/renderer/wgsl/jnlm.compute.js.map +1 -1
  329. package/lib/renderer/wgsl/offlineAdaptive.compute.js +26 -26
  330. package/lib/renderer/wgsl/oidnAuxiliary.wgsl.js +3 -3
  331. package/lib/renderer/wgsl/photonMap.compute.d.ts +29 -0
  332. package/lib/renderer/wgsl/photonMap.compute.js +352 -0
  333. package/lib/renderer/wgsl/photonMap.compute.js.map +1 -0
  334. package/lib/renderer/wgsl/proceduralSky.compute.js +64 -64
  335. package/lib/renderer/wgsl/rayTrace.compute.d.ts +23 -1
  336. package/lib/renderer/wgsl/rayTrace.compute.js +531 -543
  337. package/lib/renderer/wgsl/rayTrace.compute.js.map +1 -1
  338. package/lib/scene/MaterialState.d.ts +2 -2
  339. package/lib/scene/MaterialState.js +2 -2
  340. package/lib/scene/MaterialState.js.map +1 -1
  341. package/lib/scene/Scene.js.map +1 -1
  342. package/lib/settings/render.d.ts +22 -0
  343. package/lib/settings/render.js +2 -0
  344. package/lib/settings/render.js.map +1 -1
  345. package/lib/util/fromTHREE.js +23 -16
  346. package/lib/util/fromTHREE.js.map +1 -1
  347. package/lib/version.d.ts +1 -1
  348. package/lib/version.js +1 -1
  349. package/lib/version.js.map +1 -1
  350. package/llms.txt +2 -1
  351. package/package.json +7 -4
  352. package/lib/oidnBundledWeights.cdn.d.ts +0 -2
  353. package/lib/oidnBundledWeights.cdn.js +0 -5
  354. package/lib/oidnBundledWeights.cdn.js.map +0 -1
  355. package/lib/renderer/bvh.worker.d.ts +0 -1
  356. package/lib/renderer/bvh.worker.js +0 -16
  357. package/lib/renderer/bvh.worker.js.map +0 -1
  358. package/lib/renderer/bvhWorkerBuild.d.ts +0 -13
  359. package/lib/renderer/bvhWorkerBuild.js +0 -17
  360. package/lib/renderer/bvhWorkerBuild.js.map +0 -1
  361. package/lib/renderer/lightCore.d.ts +0 -12
  362. package/lib/renderer/lightCore.js +0 -13
  363. package/lib/renderer/lightCore.js.map +0 -1
  364. package/lib/renderer/materialCore.d.ts +0 -8
  365. package/lib/renderer/materialCore.js +0 -9
  366. package/lib/renderer/materialCore.js.map +0 -1
  367. package/lib/scene/PixelTexture.d.ts +0 -6
  368. package/lib/scene/PixelTexture.js +0 -10
  369. package/lib/scene/PixelTexture.js.map +0 -1
  370. package/lib/util/studioLighting.d.ts +0 -18
  371. package/lib/util/studioLighting.js +0 -36
  372. package/lib/util/studioLighting.js.map +0 -1
package/docs/api.md CHANGED
@@ -127,41 +127,45 @@ other sequences retain their completed samples.
127
127
  | Option | Default | Meaning |
128
128
  | --- | --- | --- |
129
129
  | `maxSamples` | `2048` | Complete sample budget |
130
+ | `aovResolution` | `'render'` | `'display'` traces AOVs at canvas size on WebGPU, capped at 4 samples regardless of `aovSamples`; requires a scale-based render. WebGL falls back with one warning |
130
131
  | `aovs` / `aovSamples` | `[]` / `16` | Optional [surface outputs](#surface-outputs) / independent positive integer sample cap |
131
132
  | `bounces` / `clamping` | `3` / `0` | Scattering depth / per-sample radiance clamp; 0 disables clamping |
132
133
  | `samplingPattern` | `'sobol'` | Sobol, ZSobol or random; see [sampling](./sampling.md) |
134
+ | `caustics` | `undefined` | Optional caustics; all estimators default to off. See [caustics](#caustics) |
133
135
  | `directLightCandidates` | `4` | WebGPU light candidates, clamped 1–16; ignored on WebGL |
134
136
  | `bevel` / `bevelSamples` | `true` / `8` | Bevel shading / probes, clamped 1–32 |
135
- | `dispersionWavelengths` | `3` | 3 wavelengths, or one randomly chosen wavelength |
137
+ | `dispersionWavelengths` | `3` | One continuous wavelength per RGB channel, or one randomly chosen channel |
136
138
  | `alphaBlendStrategy` | `'stochastic'` | WebGPU primary BLEND policy; ignored on WebGL |
137
139
  | `transmissiveOutputAlphaMin` | `1` | Path-depth alpha rule over transparent backgrounds; changes restart this render |
138
140
  | `resolutionScale` | `1` | Relative to display canvas; exclusive with fixed width/height |
139
- | `width` / `height` | undefined | Paired fixed constructor dimensions; getters report actual output dimensions |
141
+ | `width` / `height` | undefined | Paired fixed constructor dimensions; `toBlob`, `readPixels` and the getters return exactly this size |
140
142
  | `adaptiveResolution` | `false` | `{targetFrameMs, minScale}` for scale-based renders; both required, positive, minScale ≤ resolutionScale |
141
- | `timeBudgetMs` / `maxTiles` | `21` / `Infinity` | Tile time budget / tiles per call |
142
- | `batchSamples` | `false` | Let one refine submit several samples within timeBudgetMs; see [sample batching](./progressive-rendering.md#sample-batching). `allowMultipleSamplesPerFrame` is a deprecated alias |
143
+ | `timeBudgetMs` / `maxTiles` | `21` / `Infinity` | Target tile time / maximum tile grid size per sample (rounded up to a rectangular grid); fewer tiles make larger tiles, without limiting work per frame or call |
144
+ | `batchSamples` | `false` | Let one refine submit several samples within an estimated timeBudgetMs; one sample or tile can exceed it; see [sample batching](./progressive-rendering.md#sample-batching). `allowMultipleSamplesPerFrame` is a deprecated alias |
143
145
  | `fallback` | `null` | Single-level Render on the same engine |
144
146
  | `denoiser` | `null` | Per-render configuration; read back through the frozen `denoiser` getter and change with `setDenoiser()`; see [denoising](./denoising.md) |
145
147
 
146
- `bounces` is rounded and clamped to [1, 20] in both backends; NaN becomes 1.
147
- The path loop allows at most `min(bounces * 3, 20)` events, including transmission,
148
- alpha skips and escape checks. Opaque paths also have their own scattering-depth
149
- termination. This event budget can truncate deep transparent paths even when
150
- the requested scattering depth is larger. Intel Mac WebGL retains its driver
151
- workaround: `min(bounces, 3)` loop events, doubled for scenes with transmission
152
- or alpha, plus its existing early depth termination.
148
+ `bounces` is rounded and clamped to [1, 50] on WebGPU or [1, 20] on WebGL;
149
+ NaN becomes 1. Paths allow at most `min(bounces * 3, 50)` events on WebGPU
150
+ or `min(bounces * 3, 20)` on WebGL, including transmission,
151
+ alpha skips and escape checks. Opaque paths also terminate at their scattering
152
+ limit. The event budget can truncate deep transparent paths before the requested
153
+ scattering depth. On Intel Mac WebGL, the event limit is `min(bounces, 3)`, doubled
154
+ for scenes with transmission or alpha, with earlier scattering termination.
155
+ See [caustic path-length limits](./caustics.md#limits) when comparing renderers.
153
156
 
154
157
  | Method / property | Meaning |
155
158
  | --- | --- |
156
159
  | `refine(channel?, options?)` | Trace beauty or an enabled AOV within this render's budget; returns status |
160
+ | `refineUntil(deadline, {signal}?)` | For custom frame loops: refine once synchronously, then keep sampling until the `performance.now()` deadline or cancellation; returns `Promise<void>`; see [sample batching](progressive-rendering.md#sample-batching) |
157
161
  | `converge(options)` | Deterministic offline image; see below |
158
162
  | `readPixels(kind = 'hdr')` | Read this render; see [pixel outputs](#pixel-outputs) |
159
163
  | `engine` | Owning engine |
160
164
  | `denoising` | Queued/running inference, including intermediate updates; default false |
161
165
  | `on('sample', listener)` | Complete samples: `{samples, isFirstFrame, frameTimeMs}`; returns unsubscribe |
162
166
  | `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 |
167
+ | `gpuSampleTimeMs` | Deprecated; use `refineUntil` for custom frame loops. Latest WebGPU full beauty sample estimate in ms, extrapolated from tile GPU time when tiled; undefined without timestamps, for stale inputs or during converge |
168
+ | `whenReadyToRefine()` | Deprecated; use `refineUntil`. Wait for WebGPU sampling capacity and return `true` if sampling was queued; return `false` immediately with no sampling, on WebGL, or for only denoising, display or offline convergence work; submits no work; throws after disposal |
165
169
  | `converged` | `samples >= maxSamples`, independently of filtering |
166
170
  | `status` | Preparing, compiling, converging, denoising, finished or error |
167
171
  | `setDenoiser(options)` | Replace the denoiser configuration; resolves when the filter is ready |
@@ -175,13 +179,10 @@ completed filtering. Synchronous execution failures return `'error'` and set
175
179
  engine.lastError. Invalid lifecycle operations (disposed render or refine during
176
180
  converge) throw.
177
181
 
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.
182
+ `refine(undefined, {batchSamples})` overrides batching for one call without
183
+ changing render settings or discarding samples; it defaults to the render's
184
+ `batchSamples`. The `targetBatchTimeMs` and `deferResolve` refine options are
185
+ deprecated; use `refineUntil`.
185
186
 
186
187
  A fallback gets one sample when the requested render cannot finish its first
187
188
  sample within budget and the fallback has no current complete image. A ready
@@ -206,6 +207,25 @@ Resolves `{samples, averageSamplesPerPixel, pilotSamples, adaptiveSampling,
206
207
  varianceHistory?}` after filtering. It rejects if engine input revisions change.
207
208
  The same render cannot refine concurrently; other renders remain usable.
208
209
 
210
+ ### Caustics
211
+
212
+ ```ts
213
+ render.caustics = {lightTracing: {photonsPerSample: 32768}};
214
+ render.caustics = undefined; // Disable every estimator.
215
+ ```
216
+
217
+ Changing any caustic option restarts accumulation. All estimators are experimental;
218
+ see the [caustics guide](./caustics.md) for choosing estimators, supported paths,
219
+ rendering cost and image-quality limitations.
220
+
221
+ | Option | Default | Meaning |
222
+ | --- | --- | --- |
223
+ | `lightTracing` | `false` | `true` or an object enables directly visible caustic light paths on WebGPU; ignored on WebGL 2 |
224
+ | `lightTracing.photonsPerSample` | pixel count / 4 | Emitted photons per sample, rounded to a positive integer and capped by the device dispatch limit |
225
+ | `photonMapping` | `false` | `true` or an object enables progressive caustic photon mapping on WebGPU; ignored on WebGL 2. See [PM-only denoising limits](./caustics.md#limits) |
226
+ | `photonMapping.photonsPerSample` | pixel count / 4, at most 262144 | Emitted photons per batch; 28 bytes each plus up to 13 MiB of overhead. Positive finite values are rounded to at least 1 and reduced to fit device limits and a 128 MiB batch budget; other values use the default |
227
+ | `photonMapping.initialRadius` | 3% of the caster bounding sphere radius | Positive first-batch gather radius in scene units, independent of `photonsPerSample`; shrinks as batches accumulate. Smaller radii preserve detail but require more photons |
228
+
209
229
  ## RenderLoop and Sync
210
230
 
211
231
  `start()` uses requestAnimationFrame when available and a 16 ms setTimeout
@@ -226,6 +246,7 @@ otherwise. Worker visibility and control forwarding are described in
226
246
 
227
247
  `engine`, `display`, `preview` (possibly undefined) and `final` are read-only.
228
248
  `changing` defaults false; `samples` counts final beauty samples and reports 0 during preview; `status` starts preparing.
249
+ An exception during a tick, such as from a sync, sets `status` to error and `lastError`; the next successful tick clears them.
229
250
  `select({changing})` defaults to changing ? preview : final. `addSync`/`removeSync`
230
251
  manage integrations without disposing them. `tick` executes a manual frame;
231
252
  `start`/`stop` manage animation frames. `converge` accepts the same options as
@@ -251,7 +272,7 @@ Use `engine.display`; creation of additional displays is outside this API.
251
272
  their next refine; fixed renders retain their dimensions.
252
273
  `present(render)` only presents: it returns the render or its ready fallback,
253
274
  or undefined without changing the canvas when neither is ready.
254
- `toBlob(render, {type?, quality?})` composes that render at its actual dimensions,
275
+ `toBlob(render, {type?, quality?})` composes that render at its output dimensions,
255
276
  without resizing or changing the current canvas. MIME defaults to image/png;
256
277
  quality defaults to the browser encoder value.
257
278
 
@@ -331,9 +352,20 @@ const depth = await render.readPixels('depth'); // Float32Array, RGBA
331
352
  const ids = await render.readPixels('id'); // Uint32Array, RGBA
332
353
  ```
333
354
 
334
- You can change `render.aovs` or `render.aovSamples` after sampling. Continue
355
+ You can change `render.aovs`, `render.aovSamples` or `render.aovResolution` after sampling. Continue
335
356
  calling `refine()` to regenerate AOVs for the current view; beauty samples and
336
- completed denoising are preserved. AOV accumulation restarts when either changes.
357
+ completed denoising are preserved. AOV accumulation restarts when any of them changes.
358
+
359
+ To keep beauty at reduced resolution while reading or displaying full-resolution
360
+ surface outputs, set `aovResolution: 'display'` on a scale-based render. The
361
+ default is `'render'`. AOV readbacks and visualization use their own dimensions;
362
+ beauty and denoiser guides keep the render dimensions. Both modes stop at
363
+ `min(samples, aovSamples)`; display mode also caps at 4, follows canvas resizes,
364
+ and rejects fixed-size renders. During tracing, display mode starts after the first
365
+ beauty sample and adds one AOV sample per frame; the frame that reaches
366
+ `maxSamples` completes the rest. WebGL falls back to render dimensions and warns
367
+ once per render. Each kind has the same meaning in both modes.
368
+ Custom denoiser AOV inputs require `'render'` resolution.
337
369
 
338
370
  | Kind | RGBA contents |
339
371
  | --- | --- |
@@ -342,13 +374,14 @@ completed denoising are preserved. AOV accumulation restarts when either changes
342
374
  | `roughness` | Specular roughness, metalness, unsupported-reflectance fraction, coverage |
343
375
  | `diffuseAlbedo` | Directional-hemispherical diffuse reflectance RGB, valid coverage |
344
376
  | `specularAlbedo` | Directional-hemispherical specular reflectance RGB, valid coverage |
377
+ | `diffuseColor` | Diffuse texture color: base weight × base color × (1 − metalness), tinted by coat color; valid coverage |
378
+ | `specularColor` | Specular texture color: metal base color plus dielectric specular color × F0, tinted by coat color; valid coverage |
345
379
  | `id` | Object, local instance (0 for ordinary meshes), material, source triangle index; background is `0xFFFFFFFF` in all channels |
346
380
  | `motion` | Previous position minus current position in pixels, x right and y down; z = 0, alpha = 3 for valid history or 0 otherwise |
347
381
 
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
382
+ Depth, position, roughness, reflectance and color average independent jittered primary
383
+ rays, including depth of field. RGB values use hit-count normalization; alpha is hit coverage over all AOV samples. Reflectance
384
+ and color use only supported hits for both normalization and valid coverage. A blank pixel
352
385
  is zero. These passes preserve beauty samples but do not sample the same paths.
353
386
  Alpha holes use the first surface with alpha ≥ 0.5, skipping shadow catchers,
354
387
  with at most 32 layers.
@@ -373,14 +406,23 @@ Reflectance outputs are separate from the existing OIDN `albedo` guide.
373
406
  Coat, fuzz, anisotropy, subsurface/volume scattering, bevel, thin film,
374
407
  dispersion, partial alpha and nonstandard materials are unsupported by this
375
408
  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.
409
+ reflectance validity. Reflectance tracing can be expensive; request it only when needed.
410
+
411
+ `diffuseColor` and `specularColor` read resolved material inputs without
412
+ integrating the BSDF, so they are cheap. They support coat, fuzz and anisotropic
413
+ surfaces; transmission, partial alpha, media and nonstandard materials have zero
414
+ color validity. A render can request `diffuseAlbedo` or `diffuseColor`, not both;
415
+ the same applies to `specularAlbedo` and `specularColor`.
378
416
 
379
417
  Reading a kind absent from `render.aovs` rejects. A custom denoiser can request
380
418
  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
419
+ are no AOV allocations or passes. Enabling any kind normally costs about 128 GPU bytes
420
+ per AOV pixel on WebGPU or 192 on WebGL, plus scene identity/history data.
421
+ WebGPU display mode requesting only `depth`, `diffuseColor` and/or `specularColor` costs
422
+ 24 bytes per pixel plus small scene metadata; borrowing all three GPU textures
423
+ adds another 24 bytes per pixel (about 288 MB total at 6 MP). Its color
424
+ accumulation has half precision. Other AOV selections use the full allocation;
425
+ each custom denoiser AOV snapshot adds 16 bytes per pixel. A render may retain one
384
426
  reusable snapshot per kind between denoising calls. Readback adds a CPU copy.
385
427
  WebGPU dimensions remain subject to device buffer limits. A full-frame AOV
386
428
  sample runs independently of tile/adaptive sampling and may exceed the render's
@@ -402,7 +444,7 @@ const png = await engine.display.toBlob(render);
402
444
  | `depth` | Black at the near depth, white at the far depth, clamped outside the range |
403
445
  | `position` | World xyz mapped to RGB; origin is 0.5 and ±position scale maps to 0/1 |
404
446
  | `roughness` | RGB = roughness, metalness, unsupported-reflectance fraction |
405
- | `diffuseAlbedo`, `specularAlbedo` | Linear reflectance RGB clamped to [0, 1], then sRGB encoded |
447
+ | `diffuseAlbedo`, `specularAlbedo`, `diffuseColor`, `specularColor` | Linear RGB clamped to [0, 1], then sRGB encoded |
406
448
  | `id` | Stable pseudocolor for the selected uint32 ID component; colors can collide |
407
449
  | `motion` | Direction as hue (+x is red, +y is yellow-green), displacement/scale as brightness |
408
450
 
@@ -579,8 +621,14 @@ see [denoising](./denoising.md#custom-oidn-networks). The main entry exports
579
621
  ## GPU output and display passes
580
622
 
581
623
  `render.getOutputTexture(kind)` borrows native HDR or the latest display image;
582
- `display.addPostProcess(pass)` returns unregister and runs on every present/export.
583
- Both are WebGPU-only. See [formats, lifecycle and overlay example](./post-processing.md).
624
+ `render.getAovTexture('diffuseColor' | 'specularColor' | 'depth')` borrows raw
625
+ linear AOV textures at their own resolution after sampling; WebGL returns undefined.
626
+ `display.addPostProcess(pass, options?)` returns unregister and runs on every present/export.
627
+ Both are WebGPU-only. `options.outputSize` defaults to preserving input dimensions;
628
+ the final display texture and export can differ from the render size. Fixed renders
629
+ are cropped to their requested rectangle before export passes. HDR textures can
630
+ include extra pixels around a fixed render's requested image. See
631
+ [formats, lifecycle, EASU upscale and overlay examples](./post-processing.md).
584
632
 
585
633
  Anisotropy direction options: `anisotropyMap` (default `undefined`) reads linear RG as a tangent direction and B as a multiplier for base specular strength. `specularAnisotropyRotation` (default `0`) rotates that direction counter-clockwise in radians; `coatAnisotropyRotation` (default `0`) rotates the coat relative to the geometry tangent. See [materials](./materials.md#anisotropy-directions) for frame and fallback conventions.
586
634
 
@@ -0,0 +1,80 @@
1
+ # Caustics
2
+
3
+ Enable caustics on the render that needs them:
4
+
5
+ ```ts
6
+ render.caustics = {lightTracing: true, photonMapping: true};
7
+ ```
8
+
9
+ Choose the estimators for the visible receiver:
10
+
11
+ | Scene | Settings |
12
+ | --- | --- |
13
+ | Glass or metal casting onto a directly visible surface | `{lightTracing: true}` |
14
+ | Water or glass between the camera and the caustic receiver | `{photonMapping: true}` |
15
+ | A mixture of direct, indirect and glass-covered receivers | `{lightTracing: true, photonMapping: true}` |
16
+
17
+ Light tracing preserves narrow, directly visible caustics without a gather
18
+ radius. Photon mapping covers indirect caustics and surfaces seen through glass,
19
+ with additional memory and gather cost. Combined estimators assign path coverage
20
+ automatically. The viewer's Cornell caustic preset enables both estimators.
21
+
22
+ Set `render.caustics = undefined` to disable every estimator. See
23
+ [API options and defaults](./api.md#caustics).
24
+
25
+ ## Options and cost
26
+
27
+ ```ts
28
+ render.caustics = {
29
+ lightTracing: {photonsPerSample: 32768},
30
+ photonMapping: {photonsPerSample: 32768, initialRadius: 0.1}
31
+ };
32
+ ```
33
+
34
+ Both estimators require WebGPU; WebGL ignores those options. HDR readback,
35
+ display and denoising include the combined result. Changes to any option restart
36
+ accumulation. The native SDK provides the same choices through Rust
37
+ `CausticSettings` and the C `MT_CAUSTIC_*` flags.
38
+
39
+ More emitted photons can reduce noise but cost more time and memory. Photon-map
40
+ storage adds 28 bytes per emitted photon plus up to 13 MiB of overhead. Requests
41
+ above the device limits or the 128 MiB batch budget use fewer photons. Photon
42
+ batches cover the full image even when path tracing uses tiles or adaptive
43
+ sampling. Their time counts toward the frame budget, but adaptive variance measures
44
+ only the path-traced component.
45
+
46
+ ## Limits
47
+
48
+ Light tracing follows surfaces with no diffuse, subsurface, fuzz or coat
49
+ contribution and specular roughness at most 0.1. It supports emissive triangles,
50
+ rectangles, spheres, spotlights, directional lights and environment lighting,
51
+ perspective and orthographic cameras, depth of field and dispersion. Photon
52
+ mapping adds caustics throughout the allowed camera path. Volume scattering
53
+ events and shadow catchers remain with ordinary path tracing.
54
+
55
+ Complete caustic light-to-camera paths can be longer than `bounces`. Align total
56
+ path-length limits when comparing radiance with another renderer.
57
+
58
+ Photon mapping starts blurred and shrinks its radius as samples accumulate. A
59
+ small initial radius can be noisy; a large one can blur nearby surfaces and details.
60
+ Distant smooth casters enlarge the automatic radius, so set `initialRadius` if deep
61
+ caustics look too blurred. At the first diffuse surface, the gather radius is capped
62
+ to about two projected pixels to reduce patches after denoising; reflections and
63
+ refractions use an approximate footprint. More photons can help, but not every
64
+ batch improves the image, and more samples do not remove the precision loss in
65
+ stored photon data.
66
+
67
+ With photon mapping alone, directly visible receivers can show fewer cloud-like
68
+ OIDN blotches but grainier raw images. Receivers seen through reflection or
69
+ refraction can still show blotches. Enable light tracing as well for the cleanest
70
+ directly visible receivers.
71
+
72
+ A denoiser can soften narrow caustic lines, so compare raw HDR with denoised output
73
+ when judging detail. Use raw HDR for energy comparisons: displayed or denoised
74
+ brightness can differ even when regional HDR energy agrees.
75
+
76
+ Both estimators fall back to path tracing for the whole scene when a smooth caster
77
+ uses stochastic blend coverage. Cutout coverage remains supported. Environment
78
+ caustics follow path tracing's visible-background behavior through the first smooth
79
+ transmission; an explicit black background suppresses that contribution.
80
+ Quantitative energy agreement with other renderers is not guaranteed.
package/docs/materials.md CHANGED
@@ -51,12 +51,14 @@ the textures they reference still share atlas space.
51
51
 
52
52
  Units: `transmissionDepth` and `subsurfaceRadius` are in scene units;
53
53
  `thinFilmThickness` is in micrometres (`0.45` = 450 nm).
54
+ Subsurface materials with short scattering radii may appear darker than in earlier releases.
54
55
 
55
56
  `baseWeight` scales diffuse reflection and the metallic normal-incidence color
56
57
  (`baseWeight * baseColor`). Dielectric specular, transmission and subsurface
57
58
  retain their own strengths. For metals, `specularColor` tints the F82 Fresnel
58
59
  curve near grazing angles and `specularWeight` scales the entire reflection,
59
60
  including thin-film reflection. At their default values, F82 reduces to Schlick.
61
+ Low-roughness metals may appear slightly darker under broad lighting than in earlier releases.
60
62
 
61
63
  Native `specularRoughnessAnisotropy` and `coatRoughnessAnisotropy` use the OpenPBR GGX mapping:
62
64
  `alphaT = roughness² * sqrt(2 / (1 + (1 - anisotropy)²))` and
@@ -97,10 +99,11 @@ the substrate through the attenuation term.
97
99
  **Interior media.** Transmission describes a homogeneous volume:
98
100
  `transmissionColor` is the color that survives one `transmissionDepth`, and
99
101
  `transmissionScatter` is the scattering albedo. With `transmissionDepth = 0`
100
- the tint is applied at the surface. Subsurface is a separate layer driven by its
101
- color and per-channel radius. When both are active, their coefficients combine
102
- into one volume. Dispersion (`transmissionDispersionScale > 0`) samples
103
- wavelengths spectrally.
102
+ the tint is applied at the surface. Subsurface has its own color and per-channel
103
+ radius. A positive `transmissionDispersionScale` enables
104
+ wavelength-dependent refraction; values above `1` increase color separation,
105
+ and `0` disables it. Lower `transmissionDispersionAbbeNumber` values also
106
+ increase color separation.
104
107
 
105
108
  ## Compatibility names
106
109
 
@@ -128,7 +131,7 @@ an alternate serialization format.
128
131
  | --- | --- | --- |
129
132
  | `map` | RGB (sRGB), A | Multiplies `baseColor`; alpha use set by `mapAlphaUsage` |
130
133
  | `alphaMap` | G | Opacity, as in Three.js |
131
- | `normalMap`, `normalScale` | RGB | Tangent space; tangents are generated when missing |
134
+ | `normalMap`, `normalScale` | RGB | Tangent space; uses triangle UV derivatives |
132
135
  | `metallicRoughnessMap` | G = roughness, B = metalness | glTF convention |
133
136
  | `roughnessMap` | G | |
134
137
  | `metalnessMap` | B | |
@@ -287,6 +290,6 @@ Set `specularAnisotropyRotation` or provide `anisotropyMap` to orient the base s
287
290
 
288
291
  `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.
289
292
 
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.
293
+ Provided tangent XYZ and optional W handedness are preserved. Missing tangents are generated from primary UVs only when a material using the geometry needs anisotropy or an active coat normal map; adding such a material prepares them automatically. For XYZ tangents the sign comes from triangle UV derivatives. Missing UVs or degenerate tangents use UV derivatives where available and then a stable orthonormal fallback. 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.
291
294
 
292
295
  The direction and strength inputs use native OpenPBR semantics by default. For imported glTF/Three.js strength, select `specularAnisotropyModel: 'gltf'`; the same roughness and strength properties then retain source values. Source scalar inputs are clamped to [0, 1] before texture factors; non-finite source roughness/strength fall back to 0.3/0. Native scalar handling is unchanged. Zero source strength preserves the original roughness path. The conversion matches the GGX alpha axes before existing minimum-alpha clamping; Fresnel, masking, multiple scattering and OpenPBR coating remain the library's model. See [the conversion rules](./threejs.md#anisotropy-source-conversion).
@@ -38,6 +38,13 @@ A tuning checklist. Each item links to the page with the details.
38
38
  - Textures share an atlas bounded by `textureAtlasBudgetBytes`; larger textures
39
39
  are downscaled. Check `engine.getDiagnostics()?.textureAtlas`.
40
40
 
41
+ WebGPU quantizes vertex normal and tangent directions; scenes with displacement
42
+ retain full normal precision. Vertex RGB values in
43
+ `[0, 1]` have at most `1/510` absolute error per channel; scenes containing
44
+ negative or HDR vertex colors retain float32 RGB instead. Positions and UV
45
+ coordinates retain float32 precision. Large scenes still depend on the device's
46
+ buffer limits and the browser's available CPU memory.
47
+
41
48
  ## Animation and editing
42
49
 
43
50
  See [scene updates](./updates.md) for the cost of each kind of edit.
@@ -18,8 +18,9 @@ remove(); // Safe to call again.
18
18
 
19
19
  Passes run in registration order on every `present()` and `toBlob()`, after
20
20
  composition and denoising. They receive the selected complete render, including
21
- fallbacks, at its actual dimensions. Export uses the same chain without resizing
22
- or changing the display canvas. `readPixels()` never runs these passes.
21
+ fallbacks, at its actual dimensions. Export uses the same chain without changing
22
+ the display canvas; fixed-size renders are cropped to their requested size before
23
+ entering the chain. `readPixels()` never runs these passes.
23
24
 
24
25
  For a complete overlay implementation, see
25
26
  [`createCrosshairPass`](../examples/shared/crosshairPass.ts). It loads the input
@@ -35,12 +36,55 @@ A changing overlay can read application state in its callback. `RenderLoop`
35
36
  stops presenting after convergence, so explicitly call `display.present(final)`
36
37
  when that state changes. This refreshes the overlay without tracing more samples.
37
38
 
39
+ For edge-adaptive enlargement without sharpening:
40
+
41
+ ```ts
42
+ import {createEasuPass} from '@meimu/mutracer';
43
+ const pass = createEasuPass();
44
+ const removeUpscale = engine.display.addPostProcess(pass, {
45
+ outputSize: (_, display) => display
46
+ });
47
+ ```
48
+
49
+ Register the upscale before overlays that should stay at canvas resolution. An
50
+ input that already has the output size is copied unchanged and a larger one is
51
+ area-averaged, so one registration can serve a full-resolution or supersampled
52
+ final render and a reduced-resolution preview.
53
+
54
+ To recover texture detail, enable `diffuseColor`, `specularColor` and `depth`
55
+ on a scale-based render with `aovResolution: 'display'`, and create the pass with
56
+ `createEasuPass({albedoDetail: true})`. Keep beauty at the desired reduced
57
+ `resolutionScale`. Unregister the pass, then call `pass.dispose()` when finished
58
+ to release its scratch images.
59
+
60
+ EASU works on display-encoded RGB after tone mapping, including extended-range
61
+ output, and preserves alpha. EASU alone cannot recover missing texture detail.
62
+ Albedo compensation recovers supported surface texture detail but cannot recover
63
+ reflection, refraction or geometric detail. It maps the displayed image back
64
+ through the display transform, estimates locally how much radiance follows albedo,
65
+ and changes only that part, so specular highlights mostly keep their brightness.
66
+ Clipped highlights cannot be recovered. Transmission, cutouts, background, low
67
+ coverage and depth boundaries receive EASU alone. With detail, a present costs about
68
+ 2.6 ms at 6 MP on an Apple M4 Max, compared with about 0.5 ms for EASU alone.
69
+
38
70
  ## Options and image semantics
39
71
 
40
72
  `getOutputTexture(kind)` requires `'hdr'` or `'display'`; both return undefined
41
73
  on WebGL, before a complete sample or after disposal. There are no passes by
42
74
  default. `addPostProcess(pass)` returns an idempotent unregister function.
43
- `PostProcess` and `PostProcessContext` are exported types.
75
+ `PostProcess`, `PostProcessContext` and `PostProcessOptions` are exported types.
76
+
77
+ Use `addPostProcess(pass, {outputSize: (input, display) => display})` to output
78
+ at canvas resolution. The default preserves the input dimensions. Each callback
79
+ receives the previous pass's dimensions and the current canvas dimensions, also
80
+ for `toBlob()`. A chain can change size more than once. Return positive integer
81
+ width and height within `device.limits.maxTextureDimension2D`; invalid dimensions
82
+ discard the whole chain and emit an engine error, as callback exceptions do.
83
+
84
+ `ctx.width` and `ctx.height` remain the actual render dimensions. Read
85
+ `ctx.input.width/height` and `ctx.output.width/height` for the pass dimensions.
86
+ `toBlob()` exports the final chain dimensions; `getOutputTexture('display')`
87
+ borrows that actual-sized image, which may differ from the render size.
44
88
 
45
89
  | Image | Meaning |
46
90
  | --- | --- |
@@ -59,6 +103,21 @@ by primary-hit coverage for partially hit pixels, restoring the per-sample
59
103
  average. Thus that CPU readback can differ from the borrowed native HDR texture.
60
104
  See [custom denoiser image semantics](./denoising.md#custom-denoisers).
61
105
 
106
+ `createEasuPass()` defaults to EASU alone. `albedoDetail` defaults to `false`.
107
+ When the displayed render lacks those AOVs or display resolution, the pass warns
108
+ once and upscales without detail; frames whose AOVs are not ready yet or differ
109
+ from the output size also use EASU alone. The optional AOVs add tracing and
110
+ memory cost; see [surface outputs](./api.md#surface-outputs).
111
+
112
+ `render.getAovTexture(kind)` borrows enabled `diffuseColor`, `specularColor` or
113
+ `depth` outputs after an AOV sample completes, only on WebGPU. Colors use
114
+ `rgba8unorm-srgb`, which loads as linear values in [0, 1], with valid coverage in
115
+ alpha; depth uses `rg32float`, with
116
+ camera-forward depth in red and hit coverage in green. These raw textures follow
117
+ the same borrowing lifetime as `getOutputTexture()` and have the AOV dimensions.
118
+ Each is resolved from the accumulation when its samples change, and the first
119
+ call for a kind also creates its pipeline.
120
+
62
121
  ## Caveats
63
122
 
64
123
  Textures are borrowed and read-only. Do not retain, write or destroy returned
@@ -79,9 +138,3 @@ discards the chain's recorded commands, displays the original composed image
79
138
  and emits the engine `error` event. GPU validation errors remain WebGPU errors;
80
139
  the callback must record valid commands.
81
140
 
82
- ## Implementation notes
83
-
84
- The composed image is kept separately, so a thrown callback cannot leave a
85
- partly processed result.
86
- Display passes do not enter denoiser inputs, change samples or invalidate cached
87
- filter results. HDR-stage callbacks are outside this API.
@@ -87,24 +87,68 @@ are refined that frame. See [scene updates](updates.md) for notifications.
87
87
 
88
88
  ## Sample batching
89
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:
90
+ Without batching, each call advances one sample or tile, so a fast GPU can idle
91
+ between display frames. With `batchSamples`, sampling works on two levels:
92
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.
93
+ - **Per call.** One `refine()` may submit several samples within an estimated
94
+ `timeBudgetMs` of GPU work, or one longer sample or tile, and returns without waiting for them.
95
95
  Without `batchSamples`, a call traces one sample or tile.
96
96
  - **Between frames.** RenderLoop also refines its final render each time the GPU
97
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.
98
+ queued, or one sample or tile if that is longer. Expensive tiled samples can
99
+ continue between display frames when each measured tile fits `timeBudgetMs`;
100
+ longer tiles wait for display frames to bound input delay. Presentation still
101
+ follows the display frame rate.
100
102
 
101
103
  Batching is off by default. Enable it on the loop's final render with
102
104
  `final: {batchSamples: true}`, or on a Render you supply. RenderLoop stops
103
105
  batching while the view is changing.
104
106
 
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).
107
+ Prefer RenderLoop for between-frame refilling. For a custom loop, use
108
+ `refineUntil(deadline, {signal})`. It synchronously calls `refine()` once, so
109
+ `present()` belongs immediately after the call. On WebGPU it then refills short
110
+ batches until the deadline, cancellation, a hidden page or sampling/display
111
+ conditions prevent more work. WebGL performs only the synchronous call.
112
+
113
+ This example assumes `engine` and `render` are created with
114
+ `render.batchSamples = true` and the final beauty channel selected. Adjust the
115
+ 60 Hz deadline to your application's cadence and update controls before
116
+ `engine.update()`.
117
+
118
+ <!-- own-loop-refill -->
119
+ ```js
120
+ let refill;
121
+ const stop = () => refill?.abort();
122
+ for (const type of ['pointerdown', 'pointermove', 'pointerup', 'wheel', 'keydown', 'input', 'change'])
123
+ addEventListener(type, stop, {capture: true, passive: true});
124
+
125
+ function frame(timestamp) {
126
+ requestAnimationFrame(frame);
127
+ stop();
128
+ engine.update();
129
+ refill = new AbortController();
130
+ void render.refineUntil(timestamp + 1000 / 60, {signal: refill.signal});
131
+ engine.display.present(render);
132
+ }
133
+ requestAnimationFrame(frame);
134
+ ```
135
+
136
+ Call `stop()` before pausing, exporting or switching to another render, and skip
137
+ `refineUntil()` while paused. A new call automatically ends the previous refill.
138
+ The signal cancels future submissions; work already queued on the GPU completes.
139
+ The returned Promise resolves when refilling stops, including after an
140
+ asynchronous error (reported through `render.status` and `engine.lastError`).
141
+
142
+ The library automatically continues the remaining tiles of the current sample
143
+ between presentations. `samples` increases only after the whole sample;
144
+ presentation keeps the previous complete sample until then. Committed scene or
145
+ camera edits discard an unfinished sample and restart against the new inputs.
146
+ `maxTiles` caps the sample's tile grid (rounded up to a rectangular grid), so a
147
+ smaller value makes larger tiles. It does not cap work per frame or per call;
148
+ use `timeBudgetMs` and the between-frame deadline to control queued work.
149
+ With GPU timing available, WebGPU can keep tiles above `timeBudgetMs` when the
150
+ budget cannot be reached or further subdivision brings little speedup. This limits
151
+ extra offline rendering cost while allowing longer pauses between updates.
108
152
 
109
153
  ## Reading and exporting
110
154
 
package/docs/sampling.md CHANGED
@@ -23,6 +23,10 @@ render.samplingPattern = 'zsobol';
23
23
  `sampleSequenceOffset`. Use disjoint offset ranges for animation frames.
24
24
  - **Backends**: WebGPU and WebGL 2 can differ in floating-point rounding even
25
25
  with identical sequence points.
26
+ - **Dispersion**: on WebGPU, `dispersionWavelengths: 3` with
27
+ `directLightCandidates > 1` can need more samples in individual RGB channels
28
+ under strongly colored lighting and materials. Lower overall noise does not
29
+ guarantee lower noise in every channel.
26
30
  - **Fireflies**: very narrow specular lobes under directional lights can
27
31
  produce rare, large contributions with any pattern. Sampling does not clamp
28
32
  them; use `render.clamping` if needed.
@@ -0,0 +1,52 @@
1
+ # Acceleration structures
2
+
3
+ The renderer traces a two-level hierarchy: one bottom-level BVH (BLAS) per geometry
4
+ and a top-level BVH (TLAS) over mesh instances. Builders, layouts and preparation
5
+ are documented in the [BVH guide](../bvh.md); this note records the choices behind
6
+ the defaults and the alternatives that were measured. The code is
7
+ `src/renderer/bvhAccel.ts` and `binnedSahBvh.ts`.
8
+
9
+ ## TLAS
10
+
11
+ - **Builder.** The TLAS always uses the built-in binned SAH builder, a JavaScript
12
+ port of TinyBVH's `BVH::Build`, built and refitted on the calling thread.
13
+ - **Bins.** The web TLAS uses 64 SAH bins (`WEB_TLAS_BINS`); native keeps 8. More
14
+ bins separate overlapping static instances in scenes with many instances.
15
+ Higher instance costs, exact SAH and forced fallbacks were compared and not
16
+ adopted. The rebuilt tree can pick a different object at coincident faces, which
17
+ changes `id` AOVs there; other AOVs matched, and HDR differed only by rounding.
18
+ - **Instance bounds.** Each instance's world bounds, padded by displacement, are
19
+ stored after the TLAS. Leaf traversal tests them before transforming the ray
20
+ into the BLAS's space, so a ray that misses an instance skips the transform and
21
+ the BLAS root test. Both web backends and the caustic kernel use them.
22
+
23
+ ## BLAS
24
+
25
+ - **Defaults.** The BVH2 layout with spatial splits off stays the default after a
26
+ matrix of builder, layout and split settings. CWBVH was slower on the measured
27
+ scenes despite its smaller nodes. Spatial splits help some scenes at three to
28
+ five times the build time, so they stay opt-in.
29
+ - **Traversal tweaks.** Changes to box tests and stack handling showed no reliable
30
+ gain and were not adopted.
31
+
32
+ ## Static merge prototype
33
+
34
+ A prototype merged static instances into one world-space BLAS, with world-space
35
+ triangle bounds and vertex tests in local space. It was faster on a heavy
36
+ many-instance scene, but:
37
+
38
+ - coincident geometry needs the old tree as a fallback to keep object identity,
39
+ which costs memory and cold build time;
40
+ - HDR and legacy guides differ by rare rounding, and subsurface scattering, alpha
41
+ random streams, instance colors and picking were not verified;
42
+ - incremental updates and the choice of which instances count as static are
43
+ undesigned.
44
+
45
+ It is not in production. Pursuing it needs both web layouts, an update strategy and
46
+ those verifications, then a native port and comparison.
47
+
48
+ ## Changing this code
49
+
50
+ - TLAS and BLAS changes affect `id` AOVs and picking at ties; compare those as well
51
+ as HDR.
52
+ - Keep native's TLAS parameters unless native is aligned in a separate change.