@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/lib/Render.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { AovKind } from './renderer/aov.js';
1
+ import { type AovKind } from './renderer/aov.js';
2
2
  import type Engine from './Engine.js';
3
3
  import type Camera from './scene/Camera.js';
4
4
  import type { AlphaBlendStrategy } from './renderer/RenderingPipeline.js';
@@ -17,8 +17,8 @@ export type RenderStatus = 'preparing' | 'compiling' | 'converging' | 'denoising
17
17
  /** Execution overrides for one refine call; accumulated samples are retained. */
18
18
  export interface RefineOptions {
19
19
  /** Batch several samples in this call. Default this render's batchSamples. */ batchSamples?: boolean;
20
- /** Positive finite WebGPU queue budget in milliseconds; capped by timeBudgetMs, ignored on WebGL. Default timeBudgetMs. */ targetBatchTimeMs?: number;
21
- /** Leave the displayed image unchanged, for calls between display frames; the next presentation or pixel read updates it once with every new sample. Ignored on WebGL. Default false. */ deferResolve?: boolean;
20
+ /** @deprecated Use refineUntil; this option will be removed in a later release. Positive finite WebGPU queue budget in milliseconds; capped by timeBudgetMs, ignored on WebGL. Default timeBudgetMs. */ targetBatchTimeMs?: number;
21
+ /** @deprecated Use refineUntil; this option will be removed in a later release. Leave the displayed image unchanged, for calls between display frames; the next presentation or pixel read updates it once with every new sample. Ignored on WebGL. Default false. */ deferResolve?: boolean;
22
22
  }
23
23
  /** When a render filters its complete samples. Default converged. */
24
24
  export type DenoiseSchedule = 'converged' | 'progressive' | 'every-sample';
@@ -30,7 +30,7 @@ export interface Denoiser {
30
30
  width: number;
31
31
  height: number;
32
32
  }): Promise<void>;
33
- /** Write all output RGB, then resolve after submitting GPU work. Abort on input changes, restart, replacement or disposal; aborted results are discarded even if this resolves. Buffers are borrowed until settlement; do not retain or destroy them. */
33
+ /** Write all output RGB, then resolve after submitting GPU work. Abort on input changes, restart, replacement or disposal; aborted results are discarded even if this resolves. Settle within 10 seconds of an abort, or later denoising continues without waiting. Buffers are borrowed until settlement; do not retain or destroy them. */
34
34
  denoise(input: DenoiseInput, context: {
35
35
  signal: AbortSignal;
36
36
  schedule: DenoiseSchedule;
@@ -88,29 +88,49 @@ export type OIDNDenoiserOptions = OIDNSettings & ({
88
88
  });
89
89
  /** Assign a complete configuration, or null to disable denoising. The type selector is required. */
90
90
  export type DenoiserOptions = JNLMDenoiserOptions | OIDNDenoiserOptions | CustomDenoiserOptions;
91
+ /** Optional caustic estimators. All are disabled by default. Both fall back to path tracing when a smooth caster has stochastic blend coverage. */
92
+ export interface CausticSettings {
93
+ /** WebGPU progressive photon mapping; ignored on WebGL. Used alone, it can reduce denoising blotches on directly visible receivers, with noisier raw output. Default `false`. */
94
+ photonMapping?: boolean | {
95
+ /** Emitted photons per batch; 28 bytes per photon 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. Default pixel count / 4, at most 262144. */ photonsPerSample?: number;
96
+ /** Positive first-batch gather radius in scene units, independent of photonsPerSample; shrinks as batches accumulate. Default 3% of the caster bounding sphere radius. */ initialRadius?: number;
97
+ };
98
+ /**
99
+ * WebGPU light tracing for directly visible caustic paths; ignored on WebGL.
100
+ * Default `false`.
101
+ */ lightTracing?: boolean | {
102
+ /** Emitted photons per sample, rounded to a positive integer and capped by the device dispatch limit. Default pixel count / 4. */
103
+ photonsPerSample?: number;
104
+ };
105
+ }
91
106
  /** Optional per-render execution and image-quality settings. */
92
107
  export interface RenderSettings {
93
108
  /** Optional surface outputs. Default []; disabled outputs allocate no AOV resources. */ aovs?: readonly AovKind[];
94
- /** Independent AOV sample cap, positive integer. Default 16. */ aovSamples?: number;
109
+ /** AOV dimensions. Default render; display traces AOVs at the canvas size with at most 4 samples (aovSamples above 4 have no effect) on WebGPU, falls back with one warning on WebGL, and rejects fixed-size renders. Output definitions do not depend on this setting. */ aovResolution?: 'render' | 'display';
110
+ /** Independent AOV sample cap, positive integer. Default 16; display-resolution AOVs stop at 4. */ aovSamples?: number;
95
111
  /** Maximum complete samples. Default 2048. */ maxSamples?: number;
96
112
  /**
97
- * Maximum scattering depth, rounded and clamped to [1, 20] (NaN becomes 1).
98
- * Total path events, including transmission and alpha skips, are capped at 20
99
- * and at three times this depth. Intel Mac WebGL uses min(depth, 3), doubled
113
+ * Maximum scattering depth, rounded and clamped to [1, 50] on WebGPU or
114
+ * [1, 20] on WebGL (NaN becomes 1).
115
+ * Complete caustic light-to-camera paths can be longer than this depth.
116
+ * Total path events, including transmission and alpha skips, are capped at 50
117
+ * on WebGPU or 20 on WebGL, and at three times this depth.
118
+ * Intel Mac WebGL uses min(depth, 3), doubled
100
119
  * for scenes with transmission or alpha, with its existing early termination.
101
120
  * Default 3.
102
121
  */ bounces?: number;
103
122
  /** Radiance clamp; 0 disables it. Default 0. */ clamping?: number;
104
123
  /** Sampling sequence. Default sobol. */ samplingPattern?: SamplingPattern;
124
+ /** Caustic estimators. All are disabled by default. */ caustics?: CausticSettings;
105
125
  /** Light candidates per vertex; WebGPU only, ignored on WebGL. Default 4. */ directLightCandidates?: number;
106
126
  /** Rounded material edges. Default true. */ bevel?: boolean;
107
127
  /** Bevel neighborhood probes, clamped to 1–32. Default 8. */ bevelSamples?: number;
108
- /** Wavelengths per dispersion sample. Default 3; 1 selects one random wavelength. */ dispersionWavelengths?: 1 | 3;
128
+ /** Wavelength paths per dispersion sample, each carrying one RGB channel. Default 3; 1 selects one random channel. */ dispersionWavelengths?: 1 | 3;
109
129
  /** Primary-ray alpha blend strategy; WebGPU only. Default stochastic. */ alphaBlendStrategy?: AlphaBlendStrategy;
110
130
  /** Minimum transmissive path alpha, using path depth. Default 1. Changes restart this accumulation. */ transmissiveOutputAlphaMin?: number;
111
131
  /** Tile execution budget in milliseconds. Default 21. */ timeBudgetMs?: number;
112
- /** Maximum tiles per frame. Default Infinity. */ maxTiles?: number;
113
- /** Let one refine submit several samples, up to timeBudgetMs of GPU work; WebGPU requires GPU timestamps. RenderLoop also keeps batching between display frames. Default false. */ batchSamples?: boolean;
132
+ /** Maximum tile grid size per sample, rounded up to a rectangular grid. Fewer tiles make each tile larger; this does not cap work per frame or refine call. Default Infinity. */ maxTiles?: number;
133
+ /** Let one refine submit several samples within an estimated timeBudgetMs; one sample or tile can exceed it. WebGPU requires GPU timestamps. RenderLoop also batches between display frames when individual tiles fit the budget. Default false. */ batchSamples?: boolean;
114
134
  /** @deprecated Use batchSamples; this alias will be removed in a later release. Default false. */ allowMultipleSamplesPerFrame?: boolean;
115
135
  /** Adapt scale to frame timing; minScale must not exceed resolutionScale. Default false. */ adaptiveResolution?: {
116
136
  targetFrameMs: number;
@@ -157,17 +177,23 @@ export interface ConvergeResult {
157
177
  /** One independently configured image and its accumulation, owned by an engine. */
158
178
  export default class Render {
159
179
  /** Enabled surface outputs. Default []; readPixels rejects outputs not enabled here. */ aovs: readonly AovKind[];
160
- /** Independent AOV sample cap. Default 16. */ aovSamples: number;
180
+ /** AOV dimensions. Default render; display traces AOVs at the canvas size with at most 4 samples on WebGPU, falls back on WebGL, and rejects fixed-size renders. Output definitions do not depend on this setting. */ aovResolution: 'render' | 'display';
181
+ private _warnedAovResolution;
182
+ /** Independent AOV sample cap. Default 16; display-resolution AOVs stop at 4. */ aovSamples: number;
161
183
  /** Maximum complete samples. Default 2048. */ maxSamples: number;
162
184
  /**
163
- * Maximum scattering depth, rounded and clamped to [1, 20] (NaN becomes 1).
164
- * Total path events, including transmission and alpha skips, are capped at 20
165
- * and at three times this depth. Intel Mac WebGL uses min(depth, 3), doubled
185
+ * Maximum scattering depth, rounded and clamped to [1, 50] on WebGPU or
186
+ * [1, 20] on WebGL (NaN becomes 1).
187
+ * Complete caustic light-to-camera paths can be longer than this depth.
188
+ * Total path events, including transmission and alpha skips, are capped at 50
189
+ * on WebGPU or 20 on WebGL, and at three times this depth.
190
+ * Intel Mac WebGL uses min(depth, 3), doubled
166
191
  * for scenes with transmission or alpha, with its existing early termination.
167
192
  * Default 3.
168
193
  */ bounces: number;
169
194
  /** Per-sample radiance clamp; 0 disables it. Default 0. */ clamping: number;
170
195
  /** Sampling sequence. Default sobol. */ samplingPattern: SamplingPattern;
196
+ /** Caustic estimators. Default undefined (all disabled). */ caustics?: CausticSettings;
171
197
  /** WebGPU light candidates per vertex; ignored on WebGL. Default 4. */ directLightCandidates: number;
172
198
  /** Rounded material edges. Default true. */ bevel: boolean;
173
199
  /** Bevel probes, clamped to 1–32. Default 8. */ bevelSamples: number;
@@ -175,8 +201,8 @@ export default class Render {
175
201
  /** WebGPU primary-ray alpha policy; ignored on WebGL. Default stochastic. */ alphaBlendStrategy: AlphaBlendStrategy;
176
202
  /** Minimum transmissive path alpha. Default 1. Changing it restarts this render. */ transmissiveOutputAlphaMin: number;
177
203
  /** Execution budget in milliseconds. Default 21. */ timeBudgetMs: number;
178
- /** Maximum tiles per frame. Default Infinity. */ maxTiles: number;
179
- /** Let one refine submit several samples within timeBudgetMs; WebGPU requires GPU timestamps. Default false. */ batchSamples: boolean;
204
+ /** Maximum tile grid size per sample, rounded up to a rectangular grid. Fewer tiles make each tile larger; this does not cap work per frame or refine call. Default Infinity. */ maxTiles: number;
205
+ /** Let one refine submit several samples within an estimated timeBudgetMs; one sample or tile can exceed it. WebGPU requires GPU timestamps. Default false. */ batchSamples: boolean;
180
206
  /** @deprecated Use batchSamples; this alias will be removed in a later release. Default false. */
181
207
  get allowMultipleSamplesPerFrame(): boolean;
182
208
  set allowMultipleSamplesPerFrame(value: boolean);
@@ -210,6 +236,7 @@ export default class Render {
210
236
  private _disposed;
211
237
  private _busy;
212
238
  private _refineOptions?;
239
+ private _refillController?;
213
240
  private _offlineCamera;
214
241
  private _input;
215
242
  private _adaptiveScale;
@@ -238,22 +265,33 @@ export default class Render {
238
265
  private _lastDenoiseTime;
239
266
  private _warnedDenoisedReadback;
240
267
  constructor(engine: Engine, options?: RenderOptions);
241
- /** Actual target width; before allocation uses the requested dimensions. */ get width(): number;
242
- /** Actual target height; before allocation uses the requested dimensions. */ get height(): number;
268
+ /** Output width: exact for fixed renders; otherwise the target width, or the requested width before allocation. */ get width(): number;
269
+ /** Output height: exact for fixed renders; otherwise the target height, or the requested height before allocation. */ get height(): number;
243
270
  /** Complete beauty samples since the last restart; stale engine inputs report 0. Default 0. */
244
271
  get samples(): number;
245
272
  /** Whether complete beauty samples reached maxSamples. Default false. */ get converged(): boolean;
246
273
  /** Current execution state. Default preparing. */ get status(): RenderStatus;
247
274
  private get _current();
248
- /** Latest WebGPU full beauty sample estimate in milliseconds. Default undefined; also undefined for stale inputs, during converge or without GPU timestamps. */
275
+ /** @deprecated Use refineUntil for custom frame loops; this will be removed in a later release. Latest WebGPU full beauty sample estimate in milliseconds, extrapolated from tile GPU time when tiled. Default undefined; also undefined for stale inputs, during converge or without GPU timestamps. */
249
276
  get gpuSampleTimeMs(): number | undefined;
250
- /** Waits for the next release of WebGPU sampling queue capacity. Resolves immediately if no sampling is queued or on WebGL; does not submit work. Throws after disposal. */
251
- whenReadyToRefine(): Promise<void>;
277
+ /**
278
+ * @deprecated Use refineUntil; this will be removed in a later release. Waits for the
279
+ * next release of WebGPU sampling queue capacity and returns true when sampling was
280
+ * queued. Returns false immediately with no queued sampling, on WebGL, or for only
281
+ * denoising, display or offline convergence work. Does not submit work. Throws after
282
+ * disposal.
283
+ */
284
+ whenReadyToRefine(): Promise<boolean>;
252
285
  /** Complete surface-output samples since restart; stale inputs report 0. Independent of beauty samples. Default 0. */
253
286
  get aovSamplesRendered(): number;
254
287
  private _refineAov;
255
288
  private _validate;
256
289
  private _size;
290
+ /**
291
+ * Backends may round targets up. A fixed render widens the view over the extra
292
+ * pixels so the requested centered rectangle keeps its framing and square pixels.
293
+ */
294
+ private _targetCamera;
257
295
  private _settings;
258
296
  private _synchronize;
259
297
  private _trace;
@@ -261,6 +299,23 @@ export default class Render {
261
299
  private _cancelAdaptiveFrames;
262
300
  /** Accumulates within this frame's budget. Default undefined traces beauty and requested AOVs. Pass an enabled AOV kind to refine surface outputs only, without beauty or denoising; progress is aovSamplesRendered. Options default to this render's execution settings. Rejects calls during converge or after disposal. */
263
301
  refine(channel?: AovKind, options?: RefineOptions): RenderStatus;
302
+ /**
303
+ * Calls refine() once synchronously, then keeps sampling on WebGPU until deadline.
304
+ * Call present() right after it; later samples appear at the next presentation.
305
+ * Sampling continues only with batchSamples on, the final channel displayed, a
306
+ * visible page and tiles that fit timeBudgetMs. It stops on abort or convergence.
307
+ * Preparing, compiling, denoising and the first incomplete sample advance only
308
+ * through the synchronous call, which is all WebGL runs. A new call ends the
309
+ * previous one. Asynchronous errors set status to error and resolve; synchronous
310
+ * errors follow refine().
311
+ * @param deadline End time in performance.now() milliseconds.
312
+ * @param options.signal Optional application cancellation; default none. An
313
+ * already aborted signal still permits the synchronous refine().
314
+ */
315
+ refineUntil(deadline: number, options?: {
316
+ signal?: AbortSignal;
317
+ }): Promise<void>;
318
+ private _refineToDeadline;
264
319
  private _refine;
265
320
  private _refineOne;
266
321
  private _schedule;
@@ -272,7 +327,7 @@ export default class Render {
272
327
  private _warmDenoiser;
273
328
  private _cancelDenoise;
274
329
  private _maybeDenoise;
275
- /** Reads native RGBA, top row first; id uses uint32, other kinds float32. Default hdr. Beauty/guide reads are empty until a beauty sample completes. AOV reads require explicit aovs enablement. Custom denoised output is resolved linear HDR ray color before display background/catcher composition; built-ins retain their native display-space output. Unavailable denoised output falls back to hdr. */
330
+ /** Reads native RGBA, top row first; id uses uint32, other kinds float32. Default hdr. Beauty/guide reads are empty until a beauty sample completes. AOV reads require explicit aovs enablement and use the AOV dimensions. Custom denoised output is resolved linear HDR ray color before display background/catcher composition; built-ins retain their native display-space output. Unavailable denoised output falls back to hdr. */
276
331
  readPixels(kind: 'id'): Promise<{
277
332
  width: number;
278
333
  height: number;
@@ -288,7 +343,9 @@ export default class Render {
288
343
  height: number;
289
344
  data: Float32Array | Uint32Array;
290
345
  }>;
291
- /** Borrows a read-only WebGPU texture; undefined on WebGL or before a complete sample. Kind is required. HDR is native resolved linear ray color before denoising/display composition, with DenoiseInput.color normalization and coverage. Display is the latest compose, including filters and display passes. Valid only until this render's next refine/converge, display present/setSize, restart/dispose or device loss; never retain, write or destroy it. */
346
+ /** Borrows a raw linear primary-surface texture at the AOV resolution, top row first; default undefined until an enabled AOV has samples. WebGPU only. Colors use rgba8unorm-srgb (loads as linear [0,1]) with validity coverage in alpha; depth uses rg32float (depth, hit coverage). The texture is resolved from the accumulation when its samples change; the first call per kind also creates its pipeline. Borrowing follows getOutputTexture's lifetime rules; restart invalidates it. */
347
+ getAovTexture(kind: 'diffuseColor' | 'specularColor' | 'depth'): GPUTexture | undefined;
348
+ /** Borrows a read-only WebGPU texture; undefined on WebGL or before a complete sample. Kind is required. HDR is native resolved linear ray color before denoising/display composition, with DenoiseInput.color normalization and coverage. Display is the latest compose, including filters and display passes; its dimensions may differ from this render. Valid only until this render's next refine/converge, display present/setSize, restart/dispose or device loss; never retain, write or destroy it. */
292
349
  getOutputTexture(kind: 'hdr' | 'display'): GPUTexture | undefined;
293
350
  /** Discards only this render's accumulation and pending denoising. */
294
351
  restart(): void;
package/lib/Render.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { AOV_KINDS, DISPLAY_AOV_SAMPLE_CAP } from './renderer/aov.js';
1
2
  import { aovMask } from './renderer/aov.js';
2
3
  import { displayAovKind } from './renderer/aovDisplay.js';
3
4
  import { Events } from './util/events.js';
@@ -100,17 +101,23 @@ function denoiserKey(options) {
100
101
  /** One independently configured image and its accumulation, owned by an engine. */
101
102
  export default class Render {
102
103
  /** Enabled surface outputs. Default []; readPixels rejects outputs not enabled here. */ aovs = [];
103
- /** Independent AOV sample cap. Default 16. */ aovSamples = 16;
104
+ /** AOV dimensions. Default render; display traces AOVs at the canvas size with at most 4 samples on WebGPU, falls back on WebGL, and rejects fixed-size renders. Output definitions do not depend on this setting. */ aovResolution = 'render';
105
+ _warnedAovResolution = false;
106
+ /** Independent AOV sample cap. Default 16; display-resolution AOVs stop at 4. */ aovSamples = 16;
104
107
  /** Maximum complete samples. Default 2048. */ maxSamples = 2048;
105
108
  /**
106
- * Maximum scattering depth, rounded and clamped to [1, 20] (NaN becomes 1).
107
- * Total path events, including transmission and alpha skips, are capped at 20
108
- * and at three times this depth. Intel Mac WebGL uses min(depth, 3), doubled
109
+ * Maximum scattering depth, rounded and clamped to [1, 50] on WebGPU or
110
+ * [1, 20] on WebGL (NaN becomes 1).
111
+ * Complete caustic light-to-camera paths can be longer than this depth.
112
+ * Total path events, including transmission and alpha skips, are capped at 50
113
+ * on WebGPU or 20 on WebGL, and at three times this depth.
114
+ * Intel Mac WebGL uses min(depth, 3), doubled
109
115
  * for scenes with transmission or alpha, with its existing early termination.
110
116
  * Default 3.
111
117
  */ bounces = 3;
112
118
  /** Per-sample radiance clamp; 0 disables it. Default 0. */ clamping = 0;
113
119
  /** Sampling sequence. Default sobol. */ samplingPattern = 'sobol';
120
+ /** Caustic estimators. Default undefined (all disabled). */ caustics;
114
121
  /** WebGPU light candidates per vertex; ignored on WebGL. Default 4. */ directLightCandidates = 4;
115
122
  /** Rounded material edges. Default true. */ bevel = true;
116
123
  /** Bevel probes, clamped to 1–32. Default 8. */ bevelSamples = 8;
@@ -118,8 +125,8 @@ export default class Render {
118
125
  /** WebGPU primary-ray alpha policy; ignored on WebGL. Default stochastic. */ alphaBlendStrategy = 'stochastic';
119
126
  /** Minimum transmissive path alpha. Default 1. Changing it restarts this render. */ transmissiveOutputAlphaMin = 1;
120
127
  /** Execution budget in milliseconds. Default 21. */ timeBudgetMs = 21;
121
- /** Maximum tiles per frame. Default Infinity. */ maxTiles = Infinity;
122
- /** Let one refine submit several samples within timeBudgetMs; WebGPU requires GPU timestamps. Default false. */ batchSamples = false;
128
+ /** Maximum tile grid size per sample, rounded up to a rectangular grid. Fewer tiles make each tile larger; this does not cap work per frame or refine call. Default Infinity. */ maxTiles = Infinity;
129
+ /** Let one refine submit several samples within an estimated timeBudgetMs; one sample or tile can exceed it. WebGPU requires GPU timestamps. Default false. */ batchSamples = false;
123
130
  /** @deprecated Use batchSamples; this alias will be removed in a later release. Default false. */
124
131
  get allowMultipleSamplesPerFrame() { return this.batchSamples; }
125
132
  set allowMultipleSamplesPerFrame(value) { this.batchSamples = value; }
@@ -180,6 +187,7 @@ export default class Render {
180
187
  _disposed = false;
181
188
  _busy = false;
182
189
  _refineOptions;
190
+ _refillController;
183
191
  _offlineCamera = false;
184
192
  _input = { scene: -1, camera: -1, settings: '' };
185
193
  _adaptiveScale = 1;
@@ -231,8 +239,8 @@ export default class Render {
231
239
  if (denoiser)
232
240
  void this.setDenoiser(denoiser).catch(error => this._engine._notifyError(error));
233
241
  }
234
- /** Actual target width; before allocation uses the requested dimensions. */ get width() { return this._target?.getMemory().width || this._size().width; }
235
- /** Actual target height; before allocation uses the requested dimensions. */ get height() { return this._target?.getMemory().height || this._size().height; }
242
+ /** Output width: exact for fixed renders; otherwise the target width, or the requested width before allocation. */ get width() { return this._fixed?.width ?? (this._target?.getMemory().width || this._size().width); }
243
+ /** Output height: exact for fixed renders; otherwise the target height, or the requested height before allocation. */ get height() { return this._fixed?.height ?? (this._target?.getMemory().height || this._size().height); }
236
244
  /** Complete beauty samples since the last restart; stale engine inputs report 0. Default 0. */
237
245
  get samples() { return this._current ? this._target?.getTotalSamplesRendered() ?? 0 : 0; }
238
246
  /** Whether complete beauty samples reached maxSamples. Default false. */ get converged() { return this.samples >= this.maxSamples; }
@@ -244,15 +252,32 @@ export default class Render {
244
252
  get _current() { const inputs = this._engine._inputs; return inputs.scene === this._input.scene && inputs.camera === this._input.camera && !this._disposed; }
245
253
  /** @internal */ get _complete() { return this._current && !!this._target?.presentationComplete && !this._target.isFirstFrame() && this.samples > 0; }
246
254
  /** @internal */ get _presentable() { return this._complete && (!this._denoisePending || this._denoisedSamples > 0 || (this._schedule() === 'converged' && this.maxSamples > 1)); }
255
+ /** @internal Fixed export crop, applied before display passes. */ get _exportCrop() { return this._fixed; }
247
256
  /** @internal */ get _output() { if (this._disposed)
248
257
  throw new Error('Render is disposed.'); return this._target; }
249
- /** Latest WebGPU full beauty sample estimate in milliseconds. Default undefined; also undefined for stale inputs, during converge or without GPU timestamps. */
250
- get gpuSampleTimeMs() { return !this._busy && this._current ? this._target?.getRealtimeSampleTime?.() : undefined; }
251
- /** Waits for the next release of WebGPU sampling queue capacity. Resolves immediately if no sampling is queued or on WebGL; does not submit work. Throws after disposal. */
252
- whenReadyToRefine() {
258
+ /** @deprecated Use refineUntil for custom frame loops; this will be removed in a later release. Latest WebGPU full beauty sample estimate in milliseconds, extrapolated from tile GPU time when tiled. Default undefined; also undefined for stale inputs, during converge or without GPU timestamps. */
259
+ get gpuSampleTimeMs() { return this._gpuSampleTimeMs; }
260
+ /** @internal Latest WebGPU full beauty sample estimate in milliseconds; with tiling, extrapolated from tile GPU time. Default undefined; also undefined for stale inputs, during converge or without GPU timestamps. */
261
+ get _gpuSampleTimeMs() { return !this._busy && this._current ? this._target?.getRealtimeSampleTime?.() : undefined; }
262
+ /** @internal Latest WebGPU beauty tile estimate in milliseconds, including amortized caustic work; equal to the sample estimate without tiling. Default undefined; also undefined for stale inputs, during converge or without GPU timestamps. */
263
+ get gpuTileTimeMs() { return !this._busy && this._current ? this._target?.getRealtimeTileTime?.() : undefined; }
264
+ /**
265
+ * @deprecated Use refineUntil; this will be removed in a later release. Waits for the
266
+ * next release of WebGPU sampling queue capacity and returns true when sampling was
267
+ * queued. Returns false immediately with no queued sampling, on WebGL, or for only
268
+ * denoising, display or offline convergence work. Does not submit work. Throws after
269
+ * disposal.
270
+ */
271
+ whenReadyToRefine() { return this._whenReadyToRefine(); }
272
+ /** @internal Waits for the next release of WebGPU sampling queue capacity and returns true
273
+ * when sampling was queued. Returns false immediately with no queued sampling,
274
+ * on WebGL, or for only denoising, display or offline convergence work.
275
+ * Does not submit work. Throws after disposal.
276
+ */
277
+ _whenReadyToRefine() {
253
278
  if (this._disposed)
254
279
  throw new Error('Render is disposed.');
255
- return this._target?.whenTraceAvailable?.() ?? Promise.resolve();
280
+ return !this._busy ? this._target?.whenTraceAvailable?.() ?? Promise.resolve(false) : Promise.resolve(false);
256
281
  }
257
282
  /** Complete surface-output samples since restart; stale inputs report 0. Independent of beauty samples. Default 0. */
258
283
  get aovSamplesRendered() { return this._current ? this._target?.getAovSamples?.() ?? 0 : 0; }
@@ -288,8 +313,8 @@ export default class Render {
288
313
  if (!target)
289
314
  return this._status = 'preparing';
290
315
  const before = target.getAovSamples?.() ?? 0;
291
- const limit = kind === 'id' || kind === 'motion' ? 1 : Math.min(this.maxSamples, this.aovSamples);
292
- if (!target.refineAovs?.(this._engine._inputs.value, Math.min(limit, (target.getAovSamples?.() ?? 0) + 1)))
316
+ const limit = kind === 'id' || kind === 'motion' ? 1 : Math.min(this.maxSamples, this.aovSamples, this.aovResolution === 'display' && this._engine.backend === 'webgpu' ? DISPLAY_AOV_SAMPLE_CAP : Infinity);
317
+ if (!target.refineAovs?.(this._targetCamera(this._engine._inputs.value), Math.min(limit, (target.getAovSamples?.() ?? 0) + 1)))
293
318
  return this._status = 'compiling';
294
319
  if ((target.getAovSamples?.() ?? 0) > before) {
295
320
  this._frameTime = Math.max(.001, performance.now() - started);
@@ -301,6 +326,12 @@ export default class Render {
301
326
  }
302
327
  _validate() {
303
328
  aovMask(this.aovs);
329
+ if (this.aovResolution !== 'render' && this.aovResolution !== 'display')
330
+ throw new Error('Unknown aovResolution.');
331
+ if (this.aovResolution === 'display' && this._fixed)
332
+ throw new Error('aovResolution display requires a scale-based render.');
333
+ if (this.aovResolution === 'display' && this._denoiserOptions?.type === 'custom' && this._denoiserOptions.aovs?.length)
334
+ throw new Error('Custom denoiser AOV inputs require aovResolution render.');
304
335
  if (!Number.isInteger(this.aovSamples) || this.aovSamples < 1)
305
336
  throw new Error('aovSamples must be a positive integer.');
306
337
  if (!Number.isFinite(this.maxSamples) || this.maxSamples < 1)
@@ -317,8 +348,54 @@ export default class Render {
317
348
  const scale = this.adaptiveResolution ? Math.min(this._adaptiveScale, this._resolutionScale) : this._resolutionScale;
318
349
  return { width: Math.max(1, Math.round(this._engine.display.canvas.width * scale)), height: Math.max(1, Math.round(this._engine.display.canvas.height * scale)) };
319
350
  }
351
+ /**
352
+ * Backends may round targets up. A fixed render widens the view over the extra
353
+ * pixels so the requested centered rectangle keeps its framing and square pixels.
354
+ */
355
+ _targetCamera(camera) {
356
+ const memory = this._target?.getMemory(), fixed = this._fixed;
357
+ if (!memory || !fixed || (memory.width === fixed.width && memory.height === fixed.height))
358
+ return camera;
359
+ const scaleX = memory.width / fixed.width, scaleY = memory.height / fixed.height;
360
+ const widened = camera.clone();
361
+ widened.transform = camera.transform;
362
+ if (camera.projection === 'perspective') {
363
+ widened.fov = Math.atan(Math.tan(camera.fov * Math.PI / 360) * scaleY) * 360 / Math.PI;
364
+ widened.aspect = (camera.aspect ?? fixed.width / fixed.height) * scaleX / scaleY;
365
+ // Keeps the lens focal length, and with it depth of field.
366
+ widened.sensorHeight = camera.sensorHeight * scaleY;
367
+ }
368
+ else {
369
+ const x = (camera.left + camera.right) / 2, y = (camera.bottom + camera.top) / 2;
370
+ widened.left = x + (camera.left - x) * scaleX;
371
+ widened.right = x + (camera.right - x) * scaleX;
372
+ widened.bottom = y + (camera.bottom - y) * scaleY;
373
+ widened.top = y + (camera.top - y) * scaleY;
374
+ }
375
+ return widened;
376
+ }
377
+ /** @internal Crops a target-sized RGBA image to a fixed render's requested rectangle. */
378
+ _cropOutput(image) {
379
+ const fixed = this._fixed;
380
+ if (!fixed || !image.width || (image.width === fixed.width && image.height === fixed.height))
381
+ return image;
382
+ const { width, height } = fixed, data = new image.data.constructor(width * height * 4);
383
+ const left = (image.width - width) >> 1, top = (image.height - height) >> 1;
384
+ for (let y = 0; y < height; y++) {
385
+ const start = ((y + top) * image.width + left) * 4;
386
+ data.set(image.data.subarray(start, start + width * 4), y * width * 4);
387
+ }
388
+ return { width, height, data };
389
+ }
320
390
  _settings(sampleCap = this.maxSamples) {
321
391
  const settings = collectTraceSettings(this, sampleCap);
392
+ if (this.aovResolution === 'display' && this._engine.backend === 'webgl') {
393
+ settings.aovResolution = 'render';
394
+ if (!this._warnedAovResolution) {
395
+ this._warnedAovResolution = true;
396
+ console.warn('Display-resolution AOVs require WebGPU; using render resolution.');
397
+ }
398
+ }
322
399
  settings.batchSamples = this._refineOptions?.batchSamples ?? settings.batchSamples;
323
400
  if (this._denoiserOptions?.type === 'custom')
324
401
  settings.aovs = [...new Set([...this.aovs, ...(this._denoiserOptions.aovs ?? [])])];
@@ -365,7 +442,7 @@ export default class Render {
365
442
  this._sampleStart = performance.now();
366
443
  this._sampleScale = this._adaptiveScale;
367
444
  }
368
- const compiling = target.trace(this._engine._inputs.value, limit);
445
+ const compiling = target.trace(this._targetCamera(this._engine._inputs.value), limit);
369
446
  if (compiling)
370
447
  this._sampleStart = undefined;
371
448
  return compiling;
@@ -427,6 +504,95 @@ export default class Render {
427
504
  this._engine._reportShaderProgress();
428
505
  }
429
506
  }
507
+ /**
508
+ * Calls refine() once synchronously, then keeps sampling on WebGPU until deadline.
509
+ * Call present() right after it; later samples appear at the next presentation.
510
+ * Sampling continues only with batchSamples on, the final channel displayed, a
511
+ * visible page and tiles that fit timeBudgetMs. It stops on abort or convergence.
512
+ * Preparing, compiling, denoising and the first incomplete sample advance only
513
+ * through the synchronous call, which is all WebGL runs. A new call ends the
514
+ * previous one. Asynchronous errors set status to error and resolve; synchronous
515
+ * errors follow refine().
516
+ * @param deadline End time in performance.now() milliseconds.
517
+ * @param options.signal Optional application cancellation; default none. An
518
+ * already aborted signal still permits the synchronous refine().
519
+ */
520
+ refineUntil(deadline, options) {
521
+ return this._refineToDeadline(deadline, options?.signal);
522
+ }
523
+ _refineToDeadline(deadline, signal, options, beforeRefill) {
524
+ this._refillController?.abort();
525
+ const controller = this._refillController = new AbortController();
526
+ // Keep lifecycle exceptions synchronous, just like refine(). Install the
527
+ // controller first so a synchronous sample listener can revoke this call.
528
+ try {
529
+ this.refine(undefined, options);
530
+ }
531
+ catch (error) {
532
+ controller.abort();
533
+ if (this._refillController === controller)
534
+ this._refillController = undefined;
535
+ throw error;
536
+ }
537
+ const device = this._engine.device;
538
+ const canContinue = (afterWait = false) => !controller.signal.aborted && !signal?.aborted &&
539
+ !this._disposed && !this._busy && device !== undefined && device === this._engine.device &&
540
+ (typeof document === 'undefined' || !document.hidden) && performance.now() < deadline &&
541
+ this.batchSamples && this.status === 'converging' && !this.denoising && this.samples > 0 &&
542
+ this.gpuTileTimeMs !== undefined && this.gpuTileTimeMs <= this.timeBudgetMs &&
543
+ this._engine.display.channel === 'final' && (!afterWait || this._engine._isPresented(this._target));
544
+ if (!canContinue()) {
545
+ if (this._refillController === controller)
546
+ this._refillController = undefined;
547
+ return Promise.resolve();
548
+ }
549
+ return (async () => {
550
+ let timer;
551
+ const abort = () => controller.abort();
552
+ const visibility = () => { if (document.hidden)
553
+ abort(); };
554
+ let stopped;
555
+ const cancelled = new Promise(resolve => { stopped = () => resolve(false); });
556
+ controller.signal.addEventListener('abort', stopped, { once: true });
557
+ signal?.addEventListener('abort', abort, { once: true });
558
+ if (typeof document !== 'undefined')
559
+ document.addEventListener('visibilitychange', visibility);
560
+ if (Number.isFinite(deadline))
561
+ timer = setTimeout(abort, Math.min(2147483647, Math.max(0, deadline - performance.now())));
562
+ try {
563
+ while (canContinue()) {
564
+ if (!await Promise.race([this._whenReadyToRefine(), cancelled]))
565
+ break;
566
+ beforeRefill?.();
567
+ if (!canContinue(true))
568
+ break;
569
+ this.refine(undefined, { targetBatchTimeMs: 4, deferResolve: true });
570
+ }
571
+ }
572
+ catch (error) {
573
+ if (!controller.signal.aborted && !this._disposed) {
574
+ this._engine._setRenderError(this._engine._notifyError(error));
575
+ this._status = 'error';
576
+ }
577
+ }
578
+ finally {
579
+ if (timer !== undefined)
580
+ clearTimeout(timer);
581
+ signal?.removeEventListener('abort', abort);
582
+ controller.signal.removeEventListener('abort', stopped);
583
+ if (typeof document !== 'undefined')
584
+ document.removeEventListener('visibilitychange', visibility);
585
+ if (this._refillController === controller)
586
+ this._refillController = undefined;
587
+ }
588
+ })();
589
+ }
590
+ /** @internal Whether this render currently has a between-frame lease. */
591
+ get _refilling() { return !!this._refillController && !this._refillController.signal.aborted; }
592
+ /** @internal Preserve loop-specific synchronous overrides while using refineUntil. */
593
+ _refineUntil(deadline, signal, options, beforeRefill) {
594
+ return this._refineToDeadline(deadline, signal, options, beforeRefill);
595
+ }
430
596
  _refine() {
431
597
  if (this._engine._initializationError)
432
598
  throw this._engine._initializationError;
@@ -455,7 +621,7 @@ export default class Render {
455
621
  if (this._trace(target, limit))
456
622
  return this._status = 'compiling';
457
623
  }
458
- else if (target.refineAovs && !target.refineAovs(this._engine._inputs.value)) {
624
+ else if (target.refineAovs && !target.refineAovs(this._targetCamera(this._engine._inputs.value))) {
459
625
  return this._status = 'compiling';
460
626
  }
461
627
  this._maybeDenoise(false, limit);
@@ -572,9 +738,10 @@ export default class Render {
572
738
  });
573
739
  }
574
740
  _cancelDenoise() { this._denoiseController?.abort(); this._denoiseController = undefined; this._denoiseRevision++; this._target?.cancelDenoise?.(); this._denoisePending = undefined; this._denoisedSamples = 0; }
575
- /** @internal */ _inputsChanged(resetTiming = false) { this._cancelDenoise(); if (resetTiming)
741
+ /** @internal */ _inputsChanged(resetTiming = false) { this._refillController?.abort(); this._cancelDenoise(); if (resetTiming)
576
742
  this._target?.invalidateTiming?.(); }
577
743
  /** @internal */ _deviceLost(recovering) {
744
+ this._refillController?.abort();
578
745
  this._cancelAdaptiveFrames();
579
746
  this._cancelDenoise();
580
747
  this._filterRevision++;
@@ -585,7 +752,7 @@ export default class Render {
585
752
  this._input = { scene: -1, camera: -1, settings: '' };
586
753
  this._status = recovering ? 'preparing' : 'error';
587
754
  }
588
- /** @internal */ _displayChanged() { this._cancelDenoise(); this._target?.invalidateDenoisedOutput(); }
755
+ /** @internal */ _displayChanged() { this._refillController?.abort(); this._cancelDenoise(); this._target?.invalidateDenoisedOutput(); }
589
756
  _maybeDenoise(force = false, sampleTarget = this._nextSampleTarget()) {
590
757
  const target = this._target, filter = this._filter;
591
758
  if (!target || !filter || this._denoisePending || this.samples < 1 || this._denoisedSamples === this.samples)
@@ -624,7 +791,7 @@ export default class Render {
624
791
  if (frameStart !== undefined)
625
792
  this._frameTime = Math.max(this._frameTime, this._lastDenoiseTime - frameStart);
626
793
  }
627
- });
794
+ }, controller.signal);
628
795
  this._denoisePending = task;
629
796
  void task.catch(error => { if (revision === this._denoiseRevision && !this._disposed)
630
797
  this._engine._notifyError(error); }).finally(() => {
@@ -640,7 +807,7 @@ export default class Render {
640
807
  if (this._disposed)
641
808
  throw new Error('Render is disposed.');
642
809
  const target = this._target;
643
- if (['depth', 'position', 'roughness', 'diffuseAlbedo', 'specularAlbedo', 'id', 'motion'].includes(kind)) {
810
+ if (AOV_KINDS.includes(kind)) {
644
811
  const aov = kind;
645
812
  aovMask(this.aovs);
646
813
  if (!this.aovs.includes(aov))
@@ -648,7 +815,7 @@ export default class Render {
648
815
  if (!target?.readAov)
649
816
  throw new Error('AOV output is not ready');
650
817
  target.activate();
651
- return target.readAov(aov);
818
+ return this._cropOutput(await target.readAov(aov));
652
819
  }
653
820
  if (!target || this.samples === 0)
654
821
  return { width: 0, height: 0, data: new Float32Array(0) };
@@ -660,9 +827,15 @@ export default class Render {
660
827
  kind = 'hdr';
661
828
  }
662
829
  target.activate();
663
- return target.readOutput(kind);
830
+ return this._cropOutput(await target.readOutput(kind));
831
+ }
832
+ /** Borrows a raw linear primary-surface texture at the AOV resolution, top row first; default undefined until an enabled AOV has samples. WebGPU only. Colors use rgba8unorm-srgb (loads as linear [0,1]) with validity coverage in alpha; depth uses rg32float (depth, hit coverage). The texture is resolved from the accumulation when its samples change; the first call per kind also creates its pipeline. Borrowing follows getOutputTexture's lifetime rules; restart invalidates it. */
833
+ getAovTexture(kind) {
834
+ if (this._engine.backend !== 'webgpu' || !this._current || !this.aovs.includes(kind) || !this.aovSamplesRendered)
835
+ return;
836
+ return this._target?.getAovTexture?.(kind);
664
837
  }
665
- /** Borrows a read-only WebGPU texture; undefined on WebGL or before a complete sample. Kind is required. HDR is native resolved linear ray color before denoising/display composition, with DenoiseInput.color normalization and coverage. Display is the latest compose, including filters and display passes. Valid only until this render's next refine/converge, display present/setSize, restart/dispose or device loss; never retain, write or destroy it. */
838
+ /** Borrows a read-only WebGPU texture; undefined on WebGL or before a complete sample. Kind is required. HDR is native resolved linear ray color before denoising/display composition, with DenoiseInput.color normalization and coverage. Display is the latest compose, including filters and display passes; its dimensions may differ from this render. Valid only until this render's next refine/converge, display present/setSize, restart/dispose or device loss; never retain, write or destroy it. */
666
839
  getOutputTexture(kind) {
667
840
  if (this._engine.backend !== 'webgpu' || (kind === 'hdr' ? !this._complete : !this._current || !this._target?.presentationComplete || (!this._complete && !this._target.getAovSamples?.())))
668
841
  return;
@@ -670,11 +843,12 @@ export default class Render {
670
843
  }
671
844
  /** Discards only this render's accumulation and pending denoising. */
672
845
  restart() { if (this._disposed)
673
- throw new Error('Render is disposed.'); this._cancelDenoise(); this._cancelAdaptiveFrames(); this._sampleStart = this._frameStart = undefined; this._frameTime = 0; this._target?.restartSamples(); this._status = 'converging'; }
846
+ throw new Error('Render is disposed.'); this._refillController?.abort(); this._cancelDenoise(); this._cancelAdaptiveFrames(); this._sampleStart = this._frameStart = undefined; this._frameTime = 0; this._target?.restartSamples(); this._status = 'converging'; }
674
847
  /** Releases only this render's image resources and denoiser. */
675
848
  dispose() {
676
849
  if (this._disposed)
677
850
  return;
851
+ this._refillController?.abort();
678
852
  this._disposed = true;
679
853
  this._events.clear();
680
854
  this._filterRevision++;
@@ -720,6 +894,7 @@ export default class Render {
720
894
  if (current.scene !== inputs.scene || current.camera !== inputs.camera)
721
895
  throw new Error('Engine inputs changed during converge.');
722
896
  };
897
+ this._refillController?.abort();
723
898
  this._busy = true;
724
899
  const abortConverge = () => this._cancelDenoise();
725
900
  options.signal?.addEventListener('abort', abortConverge, { once: true });
@@ -759,7 +934,7 @@ export default class Render {
759
934
  target.activate();
760
935
  target.setOfflineRenderOptions({ samplesPerBatch: batch, deferPostProcess: true, sampleSequenceOffset: offset });
761
936
  const before = target.getTotalSamplesRendered(), batchStarted = performance.now();
762
- this._status = target.trace(camera, cap) ? 'compiling' : 'converging';
937
+ this._status = target.trace(this._targetCamera(camera), cap) ? 'compiling' : 'converging';
763
938
  const count = target.getTotalSamplesRendered();
764
939
  if (count !== previous) {
765
940
  previous = count;