@driftengine/core 3.63.0 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (428) hide show
  1. package/CHANGELOG.json +122 -0
  2. package/NOTICE +20 -0
  3. package/README.md +1 -1
  4. package/dist/core/loop.js +18 -2
  5. package/dist/core/storage.js +5 -0
  6. package/dist/geometry/meshBuilder.d.ts +1 -1
  7. package/dist/geometry/meshBuilder.js +6 -2
  8. package/dist/index.d.ts +69 -4
  9. package/dist/index.js +112 -2
  10. package/dist/input/input.js +6 -1
  11. package/dist/math/exact.d.ts +13 -3
  12. package/dist/math/exact.js +68 -3
  13. package/dist/math/frustum.d.ts +17 -2
  14. package/dist/math/frustum.js +24 -0
  15. package/dist/render/backend/api.d.ts +34 -1
  16. package/dist/render/backend/api.js +22 -1
  17. package/dist/render/backend/budget.d.ts +7 -6
  18. package/dist/render/backend/budget.js +7 -6
  19. package/dist/render/backend/createRenderer.d.ts +57 -0
  20. package/dist/render/backend/createRenderer.js +51 -2
  21. package/dist/render/backend/materialChanges.d.ts +41 -0
  22. package/dist/render/backend/materialChanges.js +38 -0
  23. package/dist/render/backend/select.js +8 -0
  24. package/dist/render/backend/webgl2/renderer.d.ts +80 -1
  25. package/dist/render/backend/webgl2/renderer.js +208 -10
  26. package/dist/render/backend/webgpu/buffers.d.ts +20 -1
  27. package/dist/render/backend/webgpu/buffers.js +116 -58
  28. package/dist/render/backend/webgpu/fieldCompose.d.ts +156 -0
  29. package/dist/render/backend/webgpu/fieldCompose.js +396 -0
  30. package/dist/render/backend/webgpu/giFieldPass.d.ts +66 -0
  31. package/dist/render/backend/webgpu/giFieldPass.js +137 -0
  32. package/dist/render/backend/webgpu/gpuDrivenPass.d.ts +728 -0
  33. package/dist/render/backend/webgpu/gpuDrivenPass.js +2473 -0
  34. package/dist/render/backend/webgpu/linePass.d.ts +4 -14
  35. package/dist/render/backend/webgpu/linePass.js +4 -33
  36. package/dist/render/backend/webgpu/probeBake.d.ts +148 -0
  37. package/dist/render/backend/webgpu/probeBake.js +493 -0
  38. package/dist/render/backend/webgpu/renderer.d.ts +379 -24
  39. package/dist/render/backend/webgpu/renderer.js +1369 -76
  40. package/dist/render/backend/webgpu/scatterPass.js +15 -1
  41. package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +19 -9
  42. package/dist/render/backend/webgpu/surfaceTexturePass.js +58 -32
  43. package/dist/render/causticsRenderer.d.ts +3 -1
  44. package/dist/render/causticsRenderer.js +4 -1
  45. package/dist/render/depthConvention.d.ts +12 -0
  46. package/dist/render/depthConvention.js +14 -0
  47. package/dist/render/deviceFeatures.d.ts +32 -0
  48. package/dist/render/deviceFeatures.js +41 -0
  49. package/dist/render/fog.d.ts +79 -0
  50. package/dist/render/fog.js +138 -0
  51. package/dist/render/frame/alias.d.ts +30 -0
  52. package/dist/render/frame/alias.js +61 -0
  53. package/dist/render/frame/deps.d.ts +37 -0
  54. package/dist/render/frame/deps.js +122 -0
  55. package/dist/render/frame/flushGraph.d.ts +43 -0
  56. package/dist/render/frame/flushGraph.js +84 -0
  57. package/dist/render/frame/graphSchedule.d.ts +49 -0
  58. package/dist/render/frame/graphSchedule.js +120 -0
  59. package/dist/render/frame/index.d.ts +15 -0
  60. package/dist/render/frame/index.js +8 -0
  61. package/dist/render/frame/lifetime.d.ts +24 -0
  62. package/dist/render/frame/lifetime.js +36 -0
  63. package/dist/render/frame/passTimings.d.ts +42 -0
  64. package/dist/render/frame/passTimings.js +38 -0
  65. package/dist/render/frame/validate.d.ts +15 -0
  66. package/dist/render/frame/validate.js +22 -0
  67. package/dist/render/frame/virtual.d.ts +44 -0
  68. package/dist/render/frame/virtual.js +61 -0
  69. package/dist/render/gi/chain.d.ts +82 -0
  70. package/dist/render/gi/chain.js +135 -0
  71. package/dist/render/gi/denoise.d.ts +114 -0
  72. package/dist/render/gi/denoise.js +169 -0
  73. package/dist/render/gi/fieldScene.d.ts +125 -0
  74. package/dist/render/gi/fieldScene.js +219 -0
  75. package/dist/render/gi/globalField.d.ts +157 -0
  76. package/dist/render/gi/globalField.js +373 -0
  77. package/dist/render/gi/probeGridFit.d.ts +24 -0
  78. package/dist/render/gi/probeGridFit.js +71 -0
  79. package/dist/render/gi/probeTrace.d.ts +82 -0
  80. package/dist/render/gi/probeTrace.js +128 -0
  81. package/dist/render/gi/probeVolume.d.ts +136 -0
  82. package/dist/render/gi/probeVolume.js +191 -0
  83. package/dist/render/gi/reflection.d.ts +73 -0
  84. package/dist/render/gi/reflection.js +109 -0
  85. package/dist/render/gi/traceField.d.ts +91 -0
  86. package/dist/render/gi/traceField.js +129 -0
  87. package/dist/render/gi/traceScreen.d.ts +68 -0
  88. package/dist/render/gi/traceScreen.js +82 -0
  89. package/dist/render/gpuScene.d.ts +38 -0
  90. package/dist/render/gpuScene.js +41 -0
  91. package/dist/render/gpudriven/alphaTest.d.ts +28 -0
  92. package/dist/render/gpudriven/alphaTest.js +32 -0
  93. package/dist/render/gpudriven/clusterUpload.d.ts +59 -0
  94. package/dist/render/gpudriven/clusterUpload.js +75 -0
  95. package/dist/render/gpudriven/clusterWorld.d.ts +57 -0
  96. package/dist/render/gpudriven/clusterWorld.js +155 -0
  97. package/dist/render/gpudriven/compact.d.ts +51 -0
  98. package/dist/render/gpudriven/compact.js +68 -0
  99. package/dist/render/gpudriven/cullClusters.d.ts +129 -0
  100. package/dist/render/gpudriven/cullClusters.js +237 -0
  101. package/dist/render/gpudriven/decodeTables.d.ts +64 -0
  102. package/dist/render/gpudriven/decodeTables.js +219 -0
  103. package/dist/render/gpudriven/frustum.d.ts +25 -0
  104. package/dist/render/gpudriven/frustum.js +59 -0
  105. package/dist/render/gpudriven/geometryHarness.d.ts +15 -0
  106. package/dist/render/gpudriven/geometryHarness.js +31 -0
  107. package/dist/render/gpudriven/hzb.d.ts +60 -0
  108. package/dist/render/gpudriven/hzb.js +97 -0
  109. package/dist/render/gpudriven/ibl.d.ts +85 -0
  110. package/dist/render/gpudriven/ibl.js +129 -0
  111. package/dist/render/gpudriven/indirect.d.ts +67 -0
  112. package/dist/render/gpudriven/indirect.js +77 -0
  113. package/dist/render/gpudriven/instances.d.ts +43 -0
  114. package/dist/render/gpudriven/instances.js +172 -0
  115. package/dist/render/gpudriven/lit.d.ts +165 -0
  116. package/dist/render/gpudriven/lit.js +165 -0
  117. package/dist/render/gpudriven/lodCut.d.ts +28 -0
  118. package/dist/render/gpudriven/lodCut.js +38 -0
  119. package/dist/render/gpudriven/materialBin.d.ts +60 -0
  120. package/dist/render/gpudriven/materialBin.js +108 -0
  121. package/dist/render/gpudriven/materialTable.d.ts +88 -0
  122. package/dist/render/gpudriven/materialTable.js +115 -0
  123. package/dist/render/gpudriven/pipeline.d.ts +135 -0
  124. package/dist/render/gpudriven/pipeline.js +237 -0
  125. package/dist/render/gpudriven/rangeAlloc.d.ts +50 -0
  126. package/dist/render/gpudriven/rangeAlloc.js +112 -0
  127. package/dist/render/gpudriven/sceneUpload.d.ts +96 -0
  128. package/dist/render/gpudriven/sceneUpload.js +115 -0
  129. package/dist/render/gpudriven/shadeBins.d.ts +92 -0
  130. package/dist/render/gpudriven/shadeBins.js +182 -0
  131. package/dist/render/gpudriven/shadow.d.ts +136 -0
  132. package/dist/render/gpudriven/shadow.js +224 -0
  133. package/dist/render/gpudriven/shadowCamera.d.ts +87 -0
  134. package/dist/render/gpudriven/shadowCamera.js +345 -0
  135. package/dist/render/gpudriven/streamScene.d.ts +194 -0
  136. package/dist/render/gpudriven/streamScene.js +459 -0
  137. package/dist/render/gpudriven/surfaceFrame.d.ts +25 -0
  138. package/dist/render/gpudriven/surfaceFrame.js +51 -0
  139. package/dist/render/gpudriven/twoPhase.d.ts +122 -0
  140. package/dist/render/gpudriven/twoPhase.js +160 -0
  141. package/dist/render/gpudriven/visbuffer.d.ts +25 -0
  142. package/dist/render/gpudriven/visbuffer.js +37 -0
  143. package/dist/render/halfFloat.d.ts +10 -0
  144. package/dist/render/halfFloat.js +23 -4
  145. package/dist/render/inference/attentionTiled.d.ts +27 -0
  146. package/dist/render/inference/attentionTiled.js +117 -0
  147. package/dist/render/inference/denseKernels.d.ts +29 -0
  148. package/dist/render/inference/denseKernels.js +312 -0
  149. package/dist/render/inference/device.d.ts +21 -0
  150. package/dist/render/inference/device.js +32 -0
  151. package/dist/render/inference/deviceGraph.d.ts +34 -0
  152. package/dist/render/inference/deviceGraph.js +17 -0
  153. package/dist/render/inference/fuse.d.ts +15 -0
  154. package/dist/render/inference/fuse.js +37 -0
  155. package/dist/render/inference/kernelKit.d.ts +52 -0
  156. package/dist/render/inference/kernelKit.js +70 -0
  157. package/dist/render/inference/kernels.d.ts +3 -0
  158. package/dist/render/inference/kernels.js +11 -0
  159. package/dist/render/inference/plan.d.ts +13 -0
  160. package/dist/render/inference/plan.js +18 -0
  161. package/dist/render/inference/reuse.d.ts +39 -0
  162. package/dist/render/inference/reuse.js +65 -0
  163. package/dist/render/inference/runner.d.ts +31 -0
  164. package/dist/render/inference/runner.js +175 -0
  165. package/dist/render/inference/schedule.d.ts +39 -0
  166. package/dist/render/inference/schedule.js +59 -0
  167. package/dist/render/inference/shapeKernels.d.ts +10 -0
  168. package/dist/render/inference/shapeKernels.js +80 -0
  169. package/dist/render/inference/spatialKernels.d.ts +21 -0
  170. package/dist/render/inference/spatialKernels.js +179 -0
  171. package/dist/render/inference/splatRaster.wgsl.d.ts +75 -0
  172. package/dist/render/inference/splatRaster.wgsl.js +337 -0
  173. package/dist/render/inference/splatTiles.d.ts +79 -0
  174. package/dist/render/inference/splatTiles.js +161 -0
  175. package/dist/render/lineBatch.d.ts +2 -5
  176. package/dist/render/lineBatch.js +4 -24
  177. package/dist/render/mesh.d.ts +4 -0
  178. package/dist/render/mesh.js +45 -1
  179. package/dist/render/pass.d.ts +145 -8
  180. package/dist/render/prefilterEnvMap.d.ts +22 -0
  181. package/dist/render/prefilterEnvMap.js +31 -0
  182. package/dist/render/readback.d.ts +30 -0
  183. package/dist/render/readback.js +47 -0
  184. package/dist/render/recon/clamp.d.ts +36 -0
  185. package/dist/render/recon/clamp.js +92 -0
  186. package/dist/render/recon/disocclusion.d.ts +60 -0
  187. package/dist/render/recon/disocclusion.js +109 -0
  188. package/dist/render/recon/frameGen.d.ts +81 -0
  189. package/dist/render/recon/frameGen.js +150 -0
  190. package/dist/render/recon/frameSizes.d.ts +34 -0
  191. package/dist/render/recon/frameSizes.js +58 -0
  192. package/dist/render/recon/jitter.d.ts +60 -0
  193. package/dist/render/recon/jitter.js +118 -0
  194. package/dist/render/recon/motionVectors.d.ts +55 -0
  195. package/dist/render/recon/motionVectors.js +107 -0
  196. package/dist/render/recon/reproject.d.ts +41 -0
  197. package/dist/render/recon/reproject.js +131 -0
  198. package/dist/render/recon/resolve.d.ts +90 -0
  199. package/dist/render/recon/resolve.js +313 -0
  200. package/dist/render/recon/tier.d.ts +24 -0
  201. package/dist/render/recon/tier.js +8 -0
  202. package/dist/render/reflectionProbe.d.ts +26 -0
  203. package/dist/render/reflectionProbe.js +33 -0
  204. package/dist/render/renderQuality.d.ts +69 -0
  205. package/dist/render/renderQuality.js +22 -0
  206. package/dist/render/screenSpaceReflection.d.ts +23 -0
  207. package/dist/render/screenSpaceReflection.js +22 -3
  208. package/dist/render/sdfFont.js +2 -0
  209. package/dist/render/segmentQuads.d.ts +13 -0
  210. package/dist/render/segmentQuads.js +32 -0
  211. package/dist/render/shaders/flat/lobes.d.ts +27 -0
  212. package/dist/render/shaders/flat/lobes.js +33 -3
  213. package/dist/render/shaders/flat/preamble.d.ts +7 -0
  214. package/dist/render/shaders/flat/preamble.js +24 -1
  215. package/dist/render/shaders/flat/probeGrid.d.ts +1 -1
  216. package/dist/render/shaders/flat/probeGrid.js +71 -2
  217. package/dist/render/shaders/generated/flat.wgsl.d.ts +48 -8
  218. package/dist/render/shaders/generated/flat.wgsl.js +64 -24
  219. package/dist/render/shaders/gi/composeField.wgsl.d.ts +56 -0
  220. package/dist/render/shaders/gi/composeField.wgsl.js +231 -0
  221. package/dist/render/shaders/gi/marchField.wgsl.d.ts +30 -0
  222. package/dist/render/shaders/gi/marchField.wgsl.js +141 -0
  223. package/dist/render/shaders/gi/probeBake.wgsl.d.ts +113 -0
  224. package/dist/render/shaders/gi/probeBake.wgsl.js +635 -0
  225. package/dist/render/shaders/gi/probeTrace.wgsl.d.ts +72 -0
  226. package/dist/render/shaders/gi/probeTrace.wgsl.js +261 -0
  227. package/dist/render/shaders/gi/probeVolume.wgsl.d.ts +49 -0
  228. package/dist/render/shaders/gi/probeVolume.wgsl.js +242 -0
  229. package/dist/render/shaders/gi/sampleField.wgsl.d.ts +32 -0
  230. package/dist/render/shaders/gi/sampleField.wgsl.js +134 -0
  231. package/dist/render/shaders/gpudriven/blendRaster.wgsl.d.ts +26 -0
  232. package/dist/render/shaders/gpudriven/blendRaster.wgsl.js +339 -0
  233. package/dist/render/shaders/gpudriven/compact.wgsl.d.ts +29 -0
  234. package/dist/render/shaders/gpudriven/compact.wgsl.js +58 -0
  235. package/dist/render/shaders/gpudriven/cull.wgsl.d.ts +90 -0
  236. package/dist/render/shaders/gpudriven/cull.wgsl.js +423 -0
  237. package/dist/render/shaders/gpudriven/decode.wgsl.d.ts +72 -0
  238. package/dist/render/shaders/gpudriven/decode.wgsl.js +349 -0
  239. package/dist/render/shaders/gpudriven/environment.wgsl.d.ts +44 -0
  240. package/dist/render/shaders/gpudriven/environment.wgsl.js +137 -0
  241. package/dist/render/shaders/gpudriven/fog.wgsl.d.ts +28 -0
  242. package/dist/render/shaders/gpudriven/fog.wgsl.js +107 -0
  243. package/dist/render/shaders/gpudriven/frameBlock.wgsl.d.ts +13 -0
  244. package/dist/render/shaders/gpudriven/frameBlock.wgsl.js +62 -0
  245. package/dist/render/shaders/gpudriven/materialBin.wgsl.d.ts +51 -0
  246. package/dist/render/shaders/gpudriven/materialBin.wgsl.js +112 -0
  247. package/dist/render/shaders/gpudriven/materialTable.wgsl.d.ts +25 -0
  248. package/dist/render/shaders/gpudriven/materialTable.wgsl.js +43 -0
  249. package/dist/render/shaders/gpudriven/meshTransform.wgsl.d.ts +18 -0
  250. package/dist/render/shaders/gpudriven/meshTransform.wgsl.js +37 -0
  251. package/dist/render/shaders/gpudriven/present.wgsl.d.ts +43 -0
  252. package/dist/render/shaders/gpudriven/present.wgsl.js +127 -0
  253. package/dist/render/shaders/gpudriven/shade.wgsl.d.ts +127 -0
  254. package/dist/render/shaders/gpudriven/shade.wgsl.js +874 -0
  255. package/dist/render/shaders/gpudriven/shadow.wgsl.d.ts +44 -0
  256. package/dist/render/shaders/gpudriven/shadow.wgsl.js +308 -0
  257. package/dist/render/shaders/gpudriven/visbufferRaster.wgsl.d.ts +28 -0
  258. package/dist/render/shaders/gpudriven/visbufferRaster.wgsl.js +245 -0
  259. package/dist/render/shaders/network.wgsl.d.ts +101 -0
  260. package/dist/render/shaders/network.wgsl.js +206 -0
  261. package/dist/render/shaders/recon/motion.wgsl.d.ts +44 -0
  262. package/dist/render/shaders/recon/motion.wgsl.js +96 -0
  263. package/dist/render/shaders/recon/resolve.wgsl.d.ts +51 -0
  264. package/dist/render/shaders/recon/resolve.wgsl.js +733 -0
  265. package/dist/render/temporalAa.js +8 -33
  266. package/dist/render/textLayout.js +12 -6
  267. package/dist/render/textRenderer.d.ts +3 -1
  268. package/dist/render/textRenderer.js +4 -1
  269. package/dist/render/translucentQueue.d.ts +6 -0
  270. package/dist/render/translucentQueue.js +11 -0
  271. package/dist/render/windStreakRenderer.d.ts +2 -1
  272. package/dist/render/windStreakRenderer.js +3 -1
  273. package/dist/world/cell.d.ts +63 -0
  274. package/dist/world/cell.js +181 -0
  275. package/dist/world/cellStream.d.ts +103 -0
  276. package/dist/world/cellStream.js +198 -0
  277. package/dist/world/freeze.d.ts +81 -0
  278. package/dist/world/freeze.js +114 -0
  279. package/dist/world/rebase.d.ts +29 -0
  280. package/dist/world/rebase.js +41 -0
  281. package/package.json +8 -3
  282. package/scripts/frames.mjs +92 -0
  283. package/scripts/gpuCompute.mjs +323 -0
  284. package/scripts/png.d.mts +16 -0
  285. package/scripts/png.mjs +25 -0
  286. package/src/core/loop.ts +19 -2
  287. package/src/core/storage.ts +5 -0
  288. package/src/geometry/meshBuilder.ts +5 -1
  289. package/src/index.ts +246 -4
  290. package/src/input/input.ts +6 -1
  291. package/src/math/exact.ts +69 -3
  292. package/src/math/frustum.ts +52 -3
  293. package/src/render/backend/api.ts +42 -1
  294. package/src/render/backend/budget.ts +7 -6
  295. package/src/render/backend/createRenderer.ts +92 -3
  296. package/src/render/backend/materialChanges.ts +54 -0
  297. package/src/render/backend/select.ts +8 -0
  298. package/src/render/backend/webgl2/renderer.ts +211 -9
  299. package/src/render/backend/webgpu/buffers.ts +182 -69
  300. package/src/render/backend/webgpu/fieldCompose.ts +482 -0
  301. package/src/render/backend/webgpu/giFieldPass.ts +200 -0
  302. package/src/render/backend/webgpu/gpuDrivenPass.ts +2993 -0
  303. package/src/render/backend/webgpu/linePass.ts +4 -39
  304. package/src/render/backend/webgpu/probeBake.ts +588 -0
  305. package/src/render/backend/webgpu/renderer.ts +1481 -76
  306. package/src/render/backend/webgpu/scatterPass.ts +15 -1
  307. package/src/render/backend/webgpu/surfaceTexturePass.ts +63 -36
  308. package/src/render/causticsRenderer.ts +5 -2
  309. package/src/render/depthConvention.ts +15 -0
  310. package/src/render/deviceFeatures.ts +77 -0
  311. package/src/render/fog.ts +185 -0
  312. package/src/render/frame/alias.ts +97 -0
  313. package/src/render/frame/deps.ts +155 -0
  314. package/src/render/frame/flushGraph.ts +135 -0
  315. package/src/render/frame/graphSchedule.ts +170 -0
  316. package/src/render/frame/index.ts +52 -0
  317. package/src/render/frame/lifetime.ts +61 -0
  318. package/src/render/frame/passTimings.ts +69 -0
  319. package/src/render/frame/validate.ts +40 -0
  320. package/src/render/frame/virtual.ts +91 -0
  321. package/src/render/gi/chain.ts +225 -0
  322. package/src/render/gi/denoise.ts +276 -0
  323. package/src/render/gi/fieldScene.ts +264 -0
  324. package/src/render/gi/globalField.ts +543 -0
  325. package/src/render/gi/probeGridFit.ts +89 -0
  326. package/src/render/gi/probeTrace.ts +143 -0
  327. package/src/render/gi/probeVolume.ts +288 -0
  328. package/src/render/gi/reflection.ts +163 -0
  329. package/src/render/gi/traceField.ts +223 -0
  330. package/src/render/gi/traceScreen.ts +106 -0
  331. package/src/render/gpuScene.ts +80 -0
  332. package/src/render/gpudriven/alphaTest.ts +33 -0
  333. package/src/render/gpudriven/clusterUpload.ts +123 -0
  334. package/src/render/gpudriven/clusterWorld.ts +239 -0
  335. package/src/render/gpudriven/compact.ts +80 -0
  336. package/src/render/gpudriven/cullClusters.ts +333 -0
  337. package/src/render/gpudriven/decodeTables.ts +332 -0
  338. package/src/render/gpudriven/frustum.ts +70 -0
  339. package/src/render/gpudriven/geometryHarness.ts +51 -0
  340. package/src/render/gpudriven/hzb.ts +113 -0
  341. package/src/render/gpudriven/ibl.ts +147 -0
  342. package/src/render/gpudriven/indirect.ts +89 -0
  343. package/src/render/gpudriven/instances.ts +234 -0
  344. package/src/render/gpudriven/lit.ts +329 -0
  345. package/src/render/gpudriven/lodCut.ts +62 -0
  346. package/src/render/gpudriven/materialBin.ts +131 -0
  347. package/src/render/gpudriven/materialTable.ts +164 -0
  348. package/src/render/gpudriven/pipeline.ts +336 -0
  349. package/src/render/gpudriven/rangeAlloc.ts +122 -0
  350. package/src/render/gpudriven/sceneUpload.ts +173 -0
  351. package/src/render/gpudriven/shadeBins.ts +222 -0
  352. package/src/render/gpudriven/shadow.ts +285 -0
  353. package/src/render/gpudriven/shadowCamera.ts +406 -0
  354. package/src/render/gpudriven/streamScene.ts +575 -0
  355. package/src/render/gpudriven/surfaceFrame.ts +68 -0
  356. package/src/render/gpudriven/twoPhase.ts +214 -0
  357. package/src/render/gpudriven/visbuffer.ts +41 -0
  358. package/src/render/halfFloat.ts +22 -4
  359. package/src/render/inference/attentionTiled.ts +132 -0
  360. package/src/render/inference/denseKernels.ts +369 -0
  361. package/src/render/inference/device.ts +44 -0
  362. package/src/render/inference/deviceGraph.ts +39 -0
  363. package/src/render/inference/fuse.ts +47 -0
  364. package/src/render/inference/kernelKit.ts +120 -0
  365. package/src/render/inference/kernels.ts +18 -0
  366. package/src/render/inference/plan.ts +26 -0
  367. package/src/render/inference/reuse.ts +79 -0
  368. package/src/render/inference/runner.ts +204 -0
  369. package/src/render/inference/schedule.ts +80 -0
  370. package/src/render/inference/shapeKernels.ts +112 -0
  371. package/src/render/inference/spatialKernels.ts +220 -0
  372. package/src/render/inference/splatRaster.wgsl.ts +348 -0
  373. package/src/render/inference/splatTiles.ts +186 -0
  374. package/src/render/lineBatch.ts +4 -24
  375. package/src/render/mesh.ts +46 -1
  376. package/src/render/pass.ts +155 -10
  377. package/src/render/prefilterEnvMap.ts +33 -0
  378. package/src/render/readback.ts +73 -0
  379. package/src/render/recon/clamp.ts +113 -0
  380. package/src/render/recon/disocclusion.ts +143 -0
  381. package/src/render/recon/frameGen.ts +191 -0
  382. package/src/render/recon/frameSizes.ts +64 -0
  383. package/src/render/recon/jitter.ts +126 -0
  384. package/src/render/recon/motionVectors.ts +138 -0
  385. package/src/render/recon/reproject.ts +179 -0
  386. package/src/render/recon/resolve.ts +444 -0
  387. package/src/render/recon/tier.ts +31 -0
  388. package/src/render/reflectionProbe.ts +34 -0
  389. package/src/render/renderQuality.ts +87 -0
  390. package/src/render/screenSpaceReflection.ts +37 -3
  391. package/src/render/sdfFont.ts +2 -0
  392. package/src/render/segmentQuads.ts +39 -0
  393. package/src/render/shaders/flat/lobes.ts +35 -3
  394. package/src/render/shaders/flat/preamble.ts +25 -1
  395. package/src/render/shaders/flat/probeGrid.ts +77 -2
  396. package/src/render/shaders/generated/flat.wgsl.ts +64 -24
  397. package/src/render/shaders/gi/composeField.wgsl.ts +239 -0
  398. package/src/render/shaders/gi/marchField.wgsl.ts +146 -0
  399. package/src/render/shaders/gi/probeBake.wgsl.ts +649 -0
  400. package/src/render/shaders/gi/probeTrace.wgsl.ts +269 -0
  401. package/src/render/shaders/gi/probeVolume.wgsl.ts +246 -0
  402. package/src/render/shaders/gi/sampleField.wgsl.ts +140 -0
  403. package/src/render/shaders/gpudriven/blendRaster.wgsl.ts +343 -0
  404. package/src/render/shaders/gpudriven/compact.wgsl.ts +59 -0
  405. package/src/render/shaders/gpudriven/cull.wgsl.ts +433 -0
  406. package/src/render/shaders/gpudriven/decode.wgsl.ts +373 -0
  407. package/src/render/shaders/gpudriven/environment.wgsl.ts +141 -0
  408. package/src/render/shaders/gpudriven/fog.wgsl.ts +110 -0
  409. package/src/render/shaders/gpudriven/frameBlock.wgsl.ts +62 -0
  410. package/src/render/shaders/gpudriven/materialBin.wgsl.ts +116 -0
  411. package/src/render/shaders/gpudriven/materialTable.wgsl.ts +46 -0
  412. package/src/render/shaders/gpudriven/meshTransform.wgsl.ts +37 -0
  413. package/src/render/shaders/gpudriven/present.wgsl.ts +129 -0
  414. package/src/render/shaders/gpudriven/shade.wgsl.ts +884 -0
  415. package/src/render/shaders/gpudriven/shadow.wgsl.ts +322 -0
  416. package/src/render/shaders/gpudriven/visbufferRaster.wgsl.ts +251 -0
  417. package/src/render/shaders/network.wgsl.ts +238 -0
  418. package/src/render/shaders/recon/motion.wgsl.ts +101 -0
  419. package/src/render/shaders/recon/resolve.wgsl.ts +741 -0
  420. package/src/render/temporalAa.ts +9 -33
  421. package/src/render/textLayout.ts +12 -6
  422. package/src/render/textRenderer.ts +5 -2
  423. package/src/render/translucentQueue.ts +11 -0
  424. package/src/render/windStreakRenderer.ts +4 -2
  425. package/src/world/cell.ts +231 -0
  426. package/src/world/cellStream.ts +278 -0
  427. package/src/world/freeze.ts +157 -0
  428. package/src/world/rebase.ts +56 -0
@@ -0,0 +1,373 @@
1
+ /** The world near the camera as one distance field, assembled from the objects standing in it. */
2
+ import { mat4 } from 'gl-matrix';
3
+ /**
4
+ * How much of a cascade's extent, at each face, fades into the cascade outside it.
5
+ *
6
+ * **A hard switch between cascades is worse than either cascade's own error.** An error that is
7
+ * wrong everywhere reads as a coarse solution; one that changes abruptly along a surface reads as
8
+ * a crack, and the crack moves with the camera because the boundary does. A tenth of the extent
9
+ * is wide enough that the fade is below a voxel per step and narrow enough that the inner
10
+ * cascade's resolution is what most of the inner cascade actually delivers.
11
+ */
12
+ export const GLOBAL_FIELD_BLEND = 0.1;
13
+ /** Allocate a field. Called once; `composeGlobalField` fills it and allocates nothing. */
14
+ export function createGlobalField(resolution, cascades) {
15
+ const samples = Math.trunc(resolution);
16
+ if (!(samples >= 2)) {
17
+ throw new Error(`createGlobalField: resolution is ${String(resolution)} and the minimum is 2. One sample on ` +
18
+ 'an axis has nothing to interpolate between.');
19
+ }
20
+ const levels = Math.trunc(cascades);
21
+ if (!(levels >= 1)) {
22
+ throw new Error(`createGlobalField: cascade count is ${String(cascades)} and the minimum is 1.`);
23
+ }
24
+ const out = [];
25
+ for (let level = 0; level < levels; level++) {
26
+ out.push({
27
+ field: new Float32Array(samples ** 3),
28
+ albedo: new Float32Array(samples ** 3 * 3),
29
+ dims: [samples, samples, samples],
30
+ bounds: new Float32Array(6),
31
+ step: 0,
32
+ });
33
+ }
34
+ return { cascades: out };
35
+ }
36
+ /**
37
+ * Put one cascade where the camera is, and answer its half-extent.
38
+ *
39
+ * **Exported because the compute path needs the same placement and two spellings drift.** A pass
40
+ * filling these on a device has to agree with this about where every cascade stands, to the last
41
+ * bit: disagree by a fraction of a step and the fade between cascades lands somewhere different on
42
+ * each side, which is a seam that appears only when both paths are compared.
43
+ *
44
+ * The snapping is the decision. See `composeGlobalField` for why a grid centred on the exact camera
45
+ * position makes the whole field shimmer.
46
+ */
47
+ export function placeCascade(cascade, level, radius, cameraPos) {
48
+ const half = radius * 2 ** level;
49
+ const step = (2 * half) / (cascade.dims[0] - 1);
50
+ cascade.step = step;
51
+ for (let axis = 0; axis < 3; axis++) {
52
+ const centre = Math.round(cameraPos[axis] / step) * step;
53
+ cascade.bounds[axis] = centre - half;
54
+ cascade.bounds[axis + 3] = centre + half;
55
+ }
56
+ return half;
57
+ }
58
+ /** Scratch the composer reuses, because it runs once a frame and the rule about that is absolute. */
59
+ const INVERSE = new Float32Array(16);
60
+ const OBJECT = new Float32Array(3);
61
+ /** The nearest point of a source's box to the sample, for the bound outside it. */
62
+ const CLAMPED = new Float32Array(3);
63
+ /**
64
+ * Fill every cascade from the instances standing near the camera.
65
+ *
66
+ * `radius` is the innermost cascade's half-extent in metres; each cascade outside it reaches twice
67
+ * as far at the same sample count.
68
+ *
69
+ * **Every cascade's centre is snapped to a multiple of its own step, and that is the decision this
70
+ * function exists to make.** A grid centred on the exact camera position resamples the entire
71
+ * world at a new sub-pixel offset every frame, so every value changes slightly every frame and the
72
+ * indirect light shimmers — worst on a slow camera, which is exactly when a viewer is looking at
73
+ * it. Snapped, a camera move is either nothing at all or a whole voxel, and a whole voxel leaves
74
+ * every sample the two grids share bit-identical.
75
+ *
76
+ * **A point no instance is near takes the cascade's own reach rather than infinity.** That is an
77
+ * underestimate of the true distance, which is the safe direction: a march that steps by it takes
78
+ * a shorter step than it could and converges anyway, where an overestimate steps through the wall
79
+ * it was meant to stop at.
80
+ *
81
+ * **This is the reference, and it visits every sample of every cascade for every instance that is
82
+ * not culled outright.** `scripts/gpu-parity.mjs` is the pattern the WGSL will be checked against,
83
+ * and the WGSL is where the brick culling belongs — a CPU implementation that was already
84
+ * accelerated would be a second thing to be wrong rather than the thing the first is checked by.
85
+ */
86
+ export function composeGlobalField(instances, cameraPos, radius, out) {
87
+ if (!(radius > 0)) {
88
+ throw new Error(`composeGlobalField: radius is ${String(radius)} and must be positive.`);
89
+ }
90
+ for (const instance of instances) {
91
+ assertUniformScale(instance.transform);
92
+ assertCubicVoxels(instance.source);
93
+ }
94
+ for (let level = 0; level < out.cascades.length; level++) {
95
+ const cascade = out.cascades[level];
96
+ const half = placeCascade(cascade, level, radius, cameraPos);
97
+ const [nx, ny, nz] = cascade.dims;
98
+ const step = cascade.step;
99
+ /* The reach, which is both the initial value and the distance past which an instance cannot
100
+ change one — so it is also what culls an instance whose box is further away than that. */
101
+ cascade.field.fill(half);
102
+ /* White, so a sample no instance reached is neutral. See `GlobalFieldCascade.albedo`. */
103
+ cascade.albedo.fill(1);
104
+ const ox = cascade.bounds[0];
105
+ const oy = cascade.bounds[1];
106
+ const oz = cascade.bounds[2];
107
+ for (const instance of instances) {
108
+ const albedo = instance.albedo;
109
+ const ar = albedo === undefined ? 1 : (albedo[0] ?? 1);
110
+ const ag = albedo === undefined ? 1 : (albedo[1] ?? 1);
111
+ const ab = albedo === undefined ? 1 : (albedo[2] ?? 1);
112
+ const scale = uniformScaleOf(instance.transform);
113
+ if (mat4.invert(INVERSE, instance.transform) === null)
114
+ continue;
115
+ /*
116
+ * **An instance further from this cascade than the cascade's own reach cannot change a
117
+ * single sample in it**, because every sample already holds the reach and the minimum keeps
118
+ * the smaller. So this is a pure speed decision and no test can see it: 64 instances over a
119
+ * kilometre with four of them near the camera, four cascades of 33, compose in **19.4 ms
120
+ * with this line and 150.2 ms without it**, to bit-identical output. Recorded as a
121
+ * measurement for that reason — `sdf.ts`'s `NEIGHBOURS` is the same situation arriving from
122
+ * the other direction, where the measurement said the extra work bought nothing.
123
+ */
124
+ if (boxDistance(instance, INVERSE, scale, ox, oy, oz, ox + 2 * half, oy + 2 * half, oz + 2 * half) > half) {
125
+ continue;
126
+ }
127
+ for (let iz = 0; iz < nz; iz++) {
128
+ const wz = oz + iz * step;
129
+ for (let iy = 0; iy < ny; iy++) {
130
+ const wy = oy + iy * step;
131
+ for (let ix = 0; ix < nx; ix++) {
132
+ const index = ix + nx * (iy + ny * iz);
133
+ const current = cascade.field[index];
134
+ const distance = sampleInstance(instance, INVERSE, scale, ox + ix * step, wy, wz);
135
+ /* **The minimum, because the union of two solids is the nearer surface.** A maximum
136
+ is the intersection and carves a groove along the seam of every wall that meets
137
+ another wall, with the sign right on both sides of it. */
138
+ /* **Written only where the instance won**, which is what makes the colour the
139
+ nearest surface's rather than the last one's. A composer that wrote it beside the
140
+ comparison would paint the whole field with whichever instance came last. */
141
+ if (distance < current) {
142
+ cascade.field[index] = distance;
143
+ cascade.albedo[index * 3] = ar;
144
+ cascade.albedo[index * 3 + 1] = ag;
145
+ cascade.albedo[index * 3 + 2] = ab;
146
+ }
147
+ }
148
+ }
149
+ }
150
+ }
151
+ }
152
+ }
153
+ /**
154
+ * The field at a world point, from the finest cascade that holds it.
155
+ *
156
+ * A point past every cascade reads the outermost one clamped to its edge, rather than a sentinel:
157
+ * a caller marching outward wants a number it can step by, and the outermost cascade's own reach
158
+ * is the honest one.
159
+ */
160
+ export function sampleGlobalField(field, x, y, z) {
161
+ const last = field.cascades.length - 1;
162
+ for (let level = 0; level <= last; level++) {
163
+ const cascade = field.cascades[level];
164
+ const inside = insideBy(cascade, x, y, z);
165
+ if (inside <= 0)
166
+ continue;
167
+ const value = trilinear(cascade, x, y, z);
168
+ if (level === last)
169
+ return value;
170
+ const band = GLOBAL_FIELD_BLEND * (cascade.bounds[3] - cascade.bounds[0]);
171
+ if (inside >= band)
172
+ return value;
173
+ const outer = trilinear(field.cascades[level + 1], x, y, z);
174
+ const weight = inside / band;
175
+ return outer + (value - outer) * weight;
176
+ }
177
+ return trilinear(field.cascades[last], x, y, z);
178
+ }
179
+ /** How far inside a cascade a point is, measured to the nearest face. Negative means outside. */
180
+ function insideBy(cascade, x, y, z) {
181
+ const p = [x, y, z];
182
+ let least = Infinity;
183
+ for (let axis = 0; axis < 3; axis++) {
184
+ const min = cascade.bounds[axis];
185
+ const max = cascade.bounds[axis + 3];
186
+ least = Math.min(least, p[axis] - min, max - p[axis]);
187
+ }
188
+ return least;
189
+ }
190
+ /**
191
+ * The distance one instance reports at a world point.
192
+ *
193
+ * **The world point is carried into object space through the inverse**, which is the transform
194
+ * every implementation gets wrong first — and a sphere cannot see the mistake, because a sphere is
195
+ * invariant under every rotation. Using the placement matrix itself puts the object at the mirror
196
+ * of where it was asked to stand.
197
+ *
198
+ * **Outside the source's own box, the distance to the box is a bound and on its own it is a
199
+ * useless one.** Every surface the field describes is inside the box, so nothing can be nearer
200
+ * than the box is — true, and just outside the box it says *zero*, which is an underestimate of a
201
+ * distance that is really most of a metre. Safe for a march in the sense that it never steps
202
+ * through anything, and fatal in practice: a sphere trace stops where the field falls under its
203
+ * epsilon, so a shell of near-zero around every instance's bounding box is a **phantom surface**.
204
+ * It draws as dark fins radiating from every object, which is how it was found — by looking at
205
+ * `demo/giFieldRig.ts`, after `globalField.test.ts`'s "never overestimates" passed it happily,
206
+ * zero being an underestimate of everything.
207
+ *
208
+ * So the bound is taken against the field's own value at the nearest point of the box as well:
209
+ * `d(q) >= d(c) - |q - c|` by the triangle inequality, and `d(q) >= |q - c|` because the surface
210
+ * is inside. The larger of the two is still an underestimate and is the useful one — it reports
211
+ * the real clearance just outside the box and falls back to the box distance far away.
212
+ */
213
+ function sampleInstance(instance, inverse, scale, x, y, z) {
214
+ const source = instance.source;
215
+ OBJECT[0] =
216
+ inverse[0] * x +
217
+ inverse[4] * y +
218
+ inverse[8] * z +
219
+ inverse[12];
220
+ OBJECT[1] =
221
+ inverse[1] * x +
222
+ inverse[5] * y +
223
+ inverse[9] * z +
224
+ inverse[13];
225
+ OBJECT[2] =
226
+ inverse[2] * x +
227
+ inverse[6] * y +
228
+ inverse[10] * z +
229
+ inverse[14];
230
+ let outsideSq = 0;
231
+ for (let axis = 0; axis < 3; axis++) {
232
+ const min = source.bounds[axis];
233
+ const max = source.bounds[axis + 3];
234
+ const value = OBJECT[axis];
235
+ CLAMPED[axis] = value < min ? min : value > max ? max : value;
236
+ const over = value - CLAMPED[axis];
237
+ outsideSq += over * over;
238
+ }
239
+ if (outsideSq > 0) {
240
+ const outside = Math.sqrt(outsideSq);
241
+ const atBox = sourceAt(source, CLAMPED[0], CLAMPED[1], CLAMPED[2]);
242
+ return Math.max(outside, atBox - outside) * scale;
243
+ }
244
+ return sourceAt(source, OBJECT[0], OBJECT[1], OBJECT[2]) * scale;
245
+ }
246
+ /** How far an instance's box is from a cascade's, so an instance too far away is skipped whole. */
247
+ function boxDistance(instance, inverse, scale, minX, minY, minZ, maxX, maxY, maxZ) {
248
+ /*
249
+ * The nearest point of the cascade to the instance's own centre, then that point's distance to
250
+ * the instance's box. Conservative in the right direction: it never reports further than the
251
+ * true separation, so an instance that could contribute is never culled.
252
+ */
253
+ const centre = instance.source.bounds;
254
+ const world = instance.transform;
255
+ const cx = (centre[0] + centre[3]) / 2;
256
+ const cy = (centre[1] + centre[4]) / 2;
257
+ const cz = (centre[2] + centre[5]) / 2;
258
+ const wx = world[0] * cx +
259
+ world[4] * cy +
260
+ world[8] * cz +
261
+ world[12];
262
+ const wy = world[1] * cx +
263
+ world[5] * cy +
264
+ world[9] * cz +
265
+ world[13];
266
+ const wz = world[2] * cx +
267
+ world[6] * cy +
268
+ world[10] * cz +
269
+ world[14];
270
+ const nearX = Math.min(Math.max(wx, minX), maxX);
271
+ const nearY = Math.min(Math.max(wy, minY), maxY);
272
+ const nearZ = Math.min(Math.max(wz, minZ), maxZ);
273
+ return sampleInstance(instance, inverse, scale, nearX, nearY, nearZ);
274
+ }
275
+ /** Trilinear read of a cascade, clamped at its own edge. */
276
+ function trilinear(cascade, x, y, z) {
277
+ return read(cascade.field, cascade.dims, cascade.bounds, cascade.step, x, y, z);
278
+ }
279
+ /** Trilinear read of a source field, whose step this has to derive from its own bounds. */
280
+ function sourceAt(source, x, y, z) {
281
+ const step = (source.bounds[3] - source.bounds[0]) /
282
+ (source.dims[0] - 1);
283
+ return read(source.field, source.dims, source.bounds, step, x, y, z);
284
+ }
285
+ /** The one trilinear read both grids use, so the two cannot disagree about the layout. */
286
+ function read(field, dims, bounds, step, x, y, z) {
287
+ const [nx, ny, nz] = dims;
288
+ const counts = [nx, ny, nz];
289
+ const p = [x, y, z];
290
+ const low = [0, 0, 0];
291
+ const fraction = [0, 0, 0];
292
+ for (let axis = 0; axis < 3; axis++) {
293
+ const count = counts[axis];
294
+ const local = (p[axis] - bounds[axis]) / step;
295
+ const clamped = local <= 0 ? 0 : local >= count - 1 ? count - 1 : local;
296
+ const index = Math.min(Math.floor(clamped), count - 2);
297
+ low[axis] = index;
298
+ fraction[axis] = clamped - index;
299
+ }
300
+ const [lx, ly, lz] = low;
301
+ const [fx, fy, fz] = fraction;
302
+ let total = 0;
303
+ for (let cz = 0; cz < 2; cz++) {
304
+ const wz = cz === 0 ? 1 - fz : fz;
305
+ for (let cy = 0; cy < 2; cy++) {
306
+ const wy = cy === 0 ? 1 - fy : fy;
307
+ for (let cx = 0; cx < 2; cx++) {
308
+ const wx = cx === 0 ? 1 - fx : fx;
309
+ const index = lx + cx + nx * (ly + cy + ny * (lz + cz));
310
+ total += field[index] * wx * wy * wz;
311
+ }
312
+ }
313
+ }
314
+ return total;
315
+ }
316
+ /** The scale a transform applies, refusing one that differs between axes. See `transform`. */
317
+ function uniformScaleOf(transform) {
318
+ return Math.hypot(transform[0], transform[1], transform[2]);
319
+ }
320
+ /**
321
+ * Refuse a source whose voxels are not cubic, which `sourceAt` and the shader both assume.
322
+ *
323
+ * **One step for all three axes is the contract, and it was unwritten until it cost a feature.**
324
+ * `bakeObjectSdf` picks its step from the object's longest axis and gives each axis whatever count
325
+ * it needs at that step, so `dims` differ per axis and the spacing does not. Both readers rely on
326
+ * that: `sourceAt` here derives the step from x alone, and `composeField.wgsl.ts` says so in a
327
+ * comment. A source sampled with the same count on every axis of a box that is not a cube has
328
+ * oblong voxels, and every sample of it is taken at the wrong place along y and z — far enough
329
+ * wrong that a point at the centre of a slab reads the slab's corner.
330
+ *
331
+ * **Measured on `demo/dev/bounce.html`**, whose room is five such slabs: the composed field held
332
+ * almost no room, 98.9% of every probe's rays left a closed room, and the traced bounce delivered a
333
+ * thirty-fifth of the light. Nothing failed; the field was simply empty. Refusing is the only
334
+ * honest answer, because a distance field carries no record of the axis it was sampled along and
335
+ * there is nothing here to correct it with — the same reason a non-uniform scale is refused above.
336
+ *
337
+ * **An axis of one sample is refused here too, and by its own sentence**, because `read` addresses
338
+ * `index` and `index + 1` on every axis and cannot be given an axis with no pair. `bakeObjectSdf`
339
+ * never produces one — its counts are a ceiling plus one over a span that includes two voxels of
340
+ * padding — but `FieldSource` is a shape a consumer may build by hand, which is the whole reason
341
+ * this function exists.
342
+ */
343
+ export function assertCubicVoxels(source) {
344
+ const steps = [0, 1, 2].map((axis) => {
345
+ const count = source.dims[axis];
346
+ if (count < 2) {
347
+ throw new Error(`driftengine: a distance field source is ${source.dims.join('x')} samples and every axis ` +
348
+ 'needs at least two. One sample on an axis has no neighbour to interpolate towards.');
349
+ }
350
+ return (source.bounds[axis + 3] - source.bounds[axis]) / (count - 1);
351
+ });
352
+ const low = Math.min(...steps);
353
+ const high = Math.max(...steps);
354
+ if (high - low <= 1e-3 * high)
355
+ return;
356
+ throw new Error(`driftengine: a distance field source's voxels are ${steps
357
+ .map((step) => step.toFixed(4))
358
+ .join(' by ')} metres and have to be cubic. Its dims are ${source.dims.join('x')} over a ` +
359
+ 'box that is not that shape; give each axis the count it needs at one step, as ' +
360
+ 'bakeObjectSdf does.');
361
+ }
362
+ function assertUniformScale(transform) {
363
+ const x = Math.hypot(transform[0], transform[1], transform[2]);
364
+ const y = Math.hypot(transform[4], transform[5], transform[6]);
365
+ const z = Math.hypot(transform[8], transform[9], transform[10]);
366
+ const spread = Math.max(x, y, z) - Math.min(x, y, z);
367
+ if (spread > 1e-4 * Math.max(x, y, z)) {
368
+ throw new Error(`composeGlobalField: an instance is scaled ${x.toFixed(3)}, ${y.toFixed(3)}, ` +
369
+ `${z.toFixed(3)}, and a distance field takes a uniform scale only. There is no factor to ` +
370
+ 'correct a non-uniform one by: it depends on the direction to the nearest surface, which ' +
371
+ 'is what the field does not record.');
372
+ }
373
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * A probe grid for a scene that asked for indirect light and declared no grid.
3
+ *
4
+ * **A declared grid always wins.** This exists so that turning the feature on shows light rather
5
+ * than a warning, which is the difference between a feature a consumer can try and one they have
6
+ * to study first. A scene that cares about where its probes stand says so and this is never asked.
7
+ *
8
+ * **Every axis carries the same spacing.** Fitting each axis to its own extent would make a probe's
9
+ * cell a slab, and a slab interpolates over different distances along different axes — so a
10
+ * surface's indirect light would change as it turned, which is the one thing an irradiance probe
11
+ * must not do. When the budget bites, the spacing widens uniformly and the cells stay cubic.
12
+ */
13
+ import type { ProbeGridOptions } from '../probeGrid.ts';
14
+ /**
15
+ * A grid covering `bounds` at about `spacing` metres, within `maxProbes`.
16
+ *
17
+ * The probes surround the scene rather than stopping at it: a surface on the boundary has to be
18
+ * between probes on both sides of it, and a grid that stopped at the bounds would leave every
19
+ * outward-facing wall interpolating between probes that are all behind it.
20
+ */
21
+ export declare function fitProbeGrid(bounds: {
22
+ readonly min: ArrayLike<number>;
23
+ readonly max: ArrayLike<number>;
24
+ }, spacing: number, maxProbes: number): ProbeGridOptions;
@@ -0,0 +1,71 @@
1
+ /**
2
+ * A probe grid for a scene that asked for indirect light and declared no grid.
3
+ *
4
+ * **A declared grid always wins.** This exists so that turning the feature on shows light rather
5
+ * than a warning, which is the difference between a feature a consumer can try and one they have
6
+ * to study first. A scene that cares about where its probes stand says so and this is never asked.
7
+ *
8
+ * **Every axis carries the same spacing.** Fitting each axis to its own extent would make a probe's
9
+ * cell a slab, and a slab interpolates over different distances along different axes — so a
10
+ * surface's indirect light would change as it turned, which is the one thing an irradiance probe
11
+ * must not do. When the budget bites, the spacing widens uniformly and the cells stay cubic.
12
+ */
13
+ import {} from '../../math/color.js';
14
+ /** How far outside the scene the outermost probes stand, in cells. */
15
+ const MARGIN_CELLS = 1;
16
+ /**
17
+ * A grid covering `bounds` at about `spacing` metres, within `maxProbes`.
18
+ *
19
+ * The probes surround the scene rather than stopping at it: a surface on the boundary has to be
20
+ * between probes on both sides of it, and a grid that stopped at the bounds would leave every
21
+ * outward-facing wall interpolating between probes that are all behind it.
22
+ */
23
+ export function fitProbeGrid(bounds, spacing, maxProbes) {
24
+ const budget = Math.max(1, Math.floor(maxProbes));
25
+ const extent = [0, 1, 2].map((axis) => Math.max(0, bounds.max[axis] - bounds.min[axis]));
26
+ const centre = [0, 1, 2].map((axis) => (bounds.min[axis] + bounds.max[axis]) / 2);
27
+ /*
28
+ * Widened until the whole grid fits the budget, by whole steps rather than by solving for it:
29
+ * the count is a ceiling of a ratio, so the closed form would still have to be checked and a
30
+ * loop over a handful of doublings is clearer than a formula nobody can verify by reading.
31
+ */
32
+ let step = Math.max(1e-3, spacing);
33
+ let counts = countsFor(extent, step);
34
+ while (counts[0] * counts[1] * counts[2] > budget && step < 1e9) {
35
+ step *= 1.5;
36
+ counts = countsFor(extent, step);
37
+ }
38
+ /*
39
+ * **A budget too small for a margin collapses to one probe**, which is not a special case
40
+ * anywhere: `probeGrid.ts` says a world that bakes one environment is a grid of one, through the
41
+ * same array, convolution and shader. An axis with any extent needs three probes to surround it,
42
+ * so the smallest grid that surrounds anything is twenty-seven — and a budget below that cannot
43
+ * be met by widening, however far the step goes.
44
+ */
45
+ if (counts[0] * counts[1] * counts[2] > budget) {
46
+ return {
47
+ origin: [centre[0], centre[1], centre[2]],
48
+ spacing: [step, step, step],
49
+ counts: [1, 1, 1],
50
+ };
51
+ }
52
+ const origin = [0, 0, 0];
53
+ for (let axis = 0; axis < 3; axis += 1) {
54
+ /* Centred on the scene, so the margin outside each face is the same. */
55
+ origin[axis] = centre[axis] - (step * (counts[axis] - 1)) / 2;
56
+ }
57
+ return {
58
+ origin,
59
+ spacing: [step, step, step],
60
+ counts: [counts[0], counts[1], counts[2]],
61
+ };
62
+ }
63
+ /**
64
+ * Probes an axis needs to span `extent` at `step`, with a cell of margin at each end.
65
+ *
66
+ * At least one, always: an axis with no extent — a scene that is a floor, or a point — is a single
67
+ * probe rather than a count of zero, which would make the grid empty and the shading read nothing.
68
+ */
69
+ function countsFor(extent, step) {
70
+ return extent.map((length) => length <= 0 ? 1 : Math.ceil(length / step) + 1 + 2 * MARGIN_CELLS - 1);
71
+ }
@@ -0,0 +1,82 @@
1
+ /**
2
+ * What a probe asks the indirect chain, and what it does with the answers.
3
+ *
4
+ * **A probe's measurement is an octahedral irradiance map**, because that is what the shading
5
+ * already samples: `gridIrradiance` in `shaders/flat/probeGrid.ts` blends eight probes and reads
6
+ * each through `probeIrradiance(dir, layer)`. So nothing downstream of a probe changes — only where
7
+ * the numbers in that map came from. Today they come from six rasterised faces of the world;
8
+ * `chain.ts` is what they come from once this is wired up, and the difference is a second bounce
9
+ * and radiance from surfaces that were never in frame.
10
+ *
11
+ * **Why a spherical Fibonacci spiral and not random directions.** At a few hundred samples a random
12
+ * set leaves holes and pairs, and a probe with a hole in its sphere has looked at part of its world
13
+ * and reports all of it — as a dark patch in a direction nothing happened to be traced along. The
14
+ * spiral's points are as far from each other as the count allows, and its cost is one sine and one
15
+ * cosine a direction.
16
+ *
17
+ * **And the set turns every frame, which is what makes a probe converge.** A fixed set measures the
18
+ * same few hundred directions for ever, so whatever it misses it misses permanently. Rotating it by
19
+ * an irrational fraction of a turn each refresh means successive refreshes sample different
20
+ * directions, and the accumulation over a few refreshes is a denser set than any one of them.
21
+ */
22
+ /**
23
+ * Why a scene is not lit by what bounces on WebGL2, in words, so both backends read one sentence.
24
+ *
25
+ * **A probe is a few hundred rays and that is a compute dispatch.** This backend has no compute
26
+ * stage, and the alternatives are both worse than saying so: tracing on the processor is a
27
+ * main-thread cost proportional to the grid, and approximating with more frequent cube bakes means
28
+ * the two backends light one scene differently — which is the disagreement every published scene's
29
+ * pixel gate exists to prevent.
30
+ *
31
+ * Written here rather than in either renderer for the reason `OIT_MULTISAMPLE_REFUSAL` is written
32
+ * outside both: it is a decision the two share, so it belongs where neither owns it.
33
+ */
34
+ export declare const INDIRECT_LIGHT_WEBGL2_REFUSAL: string;
35
+ /**
36
+ * Directions a probe traces per refresh.
37
+ *
38
+ * **A few hundred, and the number is a cost rather than a quality.** A refresh is this many rays
39
+ * down a three-level chain, and `probeUpdateSchedule` decides how many probes pay it a frame. The
40
+ * convergence comes from the rotation between refreshes, not from this count, so it is set where a
41
+ * single refresh is already recognisable rather than where one refresh is final.
42
+ */
43
+ export declare const PROBE_DIRECTIONS = 256;
44
+ /**
45
+ * The golden angle, which is what spaces a spiral's points as far apart as a count allows.
46
+ *
47
+ * Exported because `shaders/gi/probeBake.wgsl.ts` interpolates it: a shader cannot import a
48
+ * constant, so the choice is one value written here and pasted in, or two literals kept in step by
49
+ * hand. `probe-bake-parity.mjs` checks the result either way, but only one of them can be right by
50
+ * construction.
51
+ */
52
+ export declare const GOLDEN_ANGLE: number;
53
+ /**
54
+ * How far the set turns between one refresh and the next, in turns.
55
+ *
56
+ * The golden ratio's fractional part: successive frames land their offsets as far from each other
57
+ * as the sequence allows, for the same reason the spiral's own points do, and it never repeats.
58
+ */
59
+ export declare const FRAME_TURN = 0.618033988749895;
60
+ /**
61
+ * Direction `index` of the set this `frame` traces, into `out`.
62
+ *
63
+ * The spiral runs `z` from near `+1` to near `-1` in equal steps and turns by the golden angle at
64
+ * each, which is the construction whose points are evenly spread on a sphere; the frame's own
65
+ * rotation is added to that turn.
66
+ */
67
+ export declare function probeDirection(index: number, frame: number, out: Float32Array): void;
68
+ /**
69
+ * The cosine convolution of `count` radiance samples into an octahedral map of `edge` texels.
70
+ *
71
+ * Each texel's direction is the normal of a surface facing that way, and its value is the
72
+ * irradiance such a surface receives: every sample weighted by how much of its hemisphere it
73
+ * occupies, which for a cosine lobe is `max(0, dot(normal, direction))`.
74
+ *
75
+ * **Normalised by the weights that were actually used, and that is the whole of the white-furnace
76
+ * property.** Dividing by an analytic constant — pi, or the sample count — is right only in the
77
+ * limit and wrong for any finite set, so a uniformly white sphere would convolve to something
78
+ * slightly other than white and a room would brighten or darken a little at every refresh. Dividing
79
+ * by the sum of the weights is exact for any set, and it is what makes a constant come back
80
+ * constant to the last place a float holds.
81
+ */
82
+ export declare function convolveProbe(samples: Float32Array, directions: Float32Array, count: number, edge: number, out: Float32Array): void;