@driftengine/core 3.63.0 → 4.0.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 +111 -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,92 @@
1
+ /**
2
+ * Rebuilding a pixel's surface from the triangle it recorded.
3
+ *
4
+ * **This is the only part of the shading that differs between the two pipelines.** The forward
5
+ * path gets its surface attributes from the rasteriser's varyings; a visibility buffer has none,
6
+ * so every attribute has to be interpolated here from the three vertices of the triangle the pixel
7
+ * wrote down. The lighting itself is not this module's business and must not become a second copy
8
+ * of the forward path's — a pair of lighting expressions drifts, and the parity gate is then the
9
+ * only thing that notices.
10
+ *
11
+ * **Perspective correction is not optional and screen-space barycentrics are not enough.** A
12
+ * triangle seen at an angle covers pixels whose interpolated attributes are *not* linear in screen
13
+ * position: the correct weights are the screen-space ones divided by each vertex's clip `w` and
14
+ * renormalised. Skipping it is the classic affine-texturing look, and on a floor it is unmistakable.
15
+ *
16
+ * **The gradients are derived analytically, and this is the subtle one.** A fragment shader gets
17
+ * `dFdx` and `dFdy` for free because it runs in quads of four pixels; a compute invocation has no
18
+ * neighbours and no derivative at all. A texture read without one either picks a fixed level —
19
+ * which is a blurry picture everywhere, or an aliased one — or picks level zero, which sparkles.
20
+ * So the rate of change of every barycentric with respect to screen x and y is computed from the
21
+ * triangle's own edge functions, and an attribute's gradient is the same weighted sum its value
22
+ * is. `attributeGradients` is that, and `barycentricGradients` is what it rests on.
23
+ *
24
+ * Everything here takes plain numbers and fills arrays the caller owns, because the shader does the
25
+ * same arithmetic per pixel and the two have to be comparable term by term.
26
+ */
27
+ /** Screen position and reciprocal clip w of one vertex: x, y, 1/w. */
28
+ export declare const SCREEN_VERTEX_FLOATS = 3;
29
+ /**
30
+ * Project one clip-space vertex into pixel coordinates.
31
+ *
32
+ * Fills `x, y, 1/w`. Answers **false behind the eye**, where the division has no meaning: a
33
+ * visibility buffer cannot record such a pixel in the first place — the rasteriser clipped the
34
+ * triangle before it was drawn — so reaching this with `w <= 0` means the identifier and the
35
+ * geometry have gone out of step, and a silent NaN would shade a plausible-looking wrong surface.
36
+ */
37
+ export declare function screenVertex(clipX: number, clipY: number, clipW: number, width: number, height: number, out: Float32Array, at: number): boolean;
38
+ /** Twice the signed area of the screen triangle. Zero where it is degenerate. */
39
+ export declare function screenArea(screen: Float32Array): number;
40
+ /**
41
+ * The screen-space barycentric weights of a point, before perspective correction.
42
+ *
43
+ * Answers false for a degenerate triangle — one whose three screen positions are collinear, which
44
+ * covers no pixel and whose weights are a division by zero. A caller that reaches this has a
45
+ * visibility identifier for a triangle the rasteriser never drew.
46
+ *
47
+ * **A point outside the triangle gets a negative weight rather than a clamp.** Clamping is how a
48
+ * pixel on the far side of an edge ends up shaded as though it were on the triangle, which along a
49
+ * silhouette is a fringe of the wrong material.
50
+ */
51
+ export declare function screenBarycentrics(screen: Float32Array, x: number, y: number, out: Float32Array): boolean;
52
+ /**
53
+ * The perspective-correct weights, from the screen-space ones and the vertices' reciprocal `w`.
54
+ *
55
+ * Each screen weight is scaled by its vertex's `1/w` and the three renormalised. At an orthographic
56
+ * projection every `1/w` is equal and this is the identity, which is why the affine version looks
57
+ * right on a test quad facing the camera and wrong on the floor beneath it.
58
+ */
59
+ export declare function perspectiveBarycentrics(screen: Float32Array, lambda: Float32Array, out: Float32Array): boolean;
60
+ /**
61
+ * How each perspective-correct weight changes with screen x and y, at one pixel.
62
+ *
63
+ * Fills six numbers: `d0/dx, d1/dx, d2/dx, d0/dy, d1/dy, d2/dy`.
64
+ *
65
+ * **Derived rather than differenced.** The screen weights are linear in `x` and `y`, so their
66
+ * gradients are constants of the triangle; the perspective-correct ones are a ratio of two linear
67
+ * functions, and the quotient rule gives the rest. What this buys over a finite difference is that
68
+ * it is exact at the pixel rather than an average over one, and that it costs no neighbours — a
69
+ * compute invocation has none.
70
+ */
71
+ export declare function barycentricGradients(screen: Float32Array, lambda: Float32Array, out: Float32Array): boolean;
72
+ /** One attribute interpolated by the weights already computed. */
73
+ export declare function interpolate(a0: number, a1: number, a2: number, weights: Float32Array): number;
74
+ /**
75
+ * One attribute's rate of change with screen x and y, from the weight gradients.
76
+ *
77
+ * An attribute is a weighted sum of three constants, so its gradient is the same sum of the
78
+ * weights' gradients. This is what a texture fetch's level of detail is computed from.
79
+ */
80
+ export declare function attributeGradients(a0: number, a1: number, a2: number, gradients: Float32Array): {
81
+ dx: number;
82
+ dy: number;
83
+ };
84
+ /**
85
+ * Which pixel a shading invocation is responsible for.
86
+ *
87
+ * A group covers `BIN_GROUP_SIZE` entries of one material's slice; an invocation past the end of
88
+ * the slice has no pixel, which is the ordinary case because the slice is not a multiple of the
89
+ * group size. Answers -1 there rather than the last pixel of the bin, which would shade it once
90
+ * per spare lane.
91
+ */
92
+ export declare function binPixel(pixels: Uint32Array, offset: number, count: number, invocation: number): number;
@@ -0,0 +1,182 @@
1
+ /**
2
+ * Rebuilding a pixel's surface from the triangle it recorded.
3
+ *
4
+ * **This is the only part of the shading that differs between the two pipelines.** The forward
5
+ * path gets its surface attributes from the rasteriser's varyings; a visibility buffer has none,
6
+ * so every attribute has to be interpolated here from the three vertices of the triangle the pixel
7
+ * wrote down. The lighting itself is not this module's business and must not become a second copy
8
+ * of the forward path's — a pair of lighting expressions drifts, and the parity gate is then the
9
+ * only thing that notices.
10
+ *
11
+ * **Perspective correction is not optional and screen-space barycentrics are not enough.** A
12
+ * triangle seen at an angle covers pixels whose interpolated attributes are *not* linear in screen
13
+ * position: the correct weights are the screen-space ones divided by each vertex's clip `w` and
14
+ * renormalised. Skipping it is the classic affine-texturing look, and on a floor it is unmistakable.
15
+ *
16
+ * **The gradients are derived analytically, and this is the subtle one.** A fragment shader gets
17
+ * `dFdx` and `dFdy` for free because it runs in quads of four pixels; a compute invocation has no
18
+ * neighbours and no derivative at all. A texture read without one either picks a fixed level —
19
+ * which is a blurry picture everywhere, or an aliased one — or picks level zero, which sparkles.
20
+ * So the rate of change of every barycentric with respect to screen x and y is computed from the
21
+ * triangle's own edge functions, and an attribute's gradient is the same weighted sum its value
22
+ * is. `attributeGradients` is that, and `barycentricGradients` is what it rests on.
23
+ *
24
+ * Everything here takes plain numbers and fills arrays the caller owns, because the shader does the
25
+ * same arithmetic per pixel and the two have to be comparable term by term.
26
+ */
27
+ /** Screen position and reciprocal clip w of one vertex: x, y, 1/w. */
28
+ export const SCREEN_VERTEX_FLOATS = 3;
29
+ /**
30
+ * Project one clip-space vertex into pixel coordinates.
31
+ *
32
+ * Fills `x, y, 1/w`. Answers **false behind the eye**, where the division has no meaning: a
33
+ * visibility buffer cannot record such a pixel in the first place — the rasteriser clipped the
34
+ * triangle before it was drawn — so reaching this with `w <= 0` means the identifier and the
35
+ * geometry have gone out of step, and a silent NaN would shade a plausible-looking wrong surface.
36
+ */
37
+ export function screenVertex(clipX, clipY, clipW, width, height, out, at) {
38
+ if (!(clipW > 1e-9))
39
+ return false;
40
+ const inverse = 1 / clipW;
41
+ out[at] = (clipX * inverse * 0.5 + 0.5) * width;
42
+ out[at + 1] = (clipY * inverse * 0.5 + 0.5) * height;
43
+ out[at + 2] = inverse;
44
+ return true;
45
+ }
46
+ /** Twice the signed area of the screen triangle. Zero where it is degenerate. */
47
+ export function screenArea(screen) {
48
+ const x0 = screen[0];
49
+ const y0 = screen[1];
50
+ const x1 = screen[3];
51
+ const y1 = screen[4];
52
+ const x2 = screen[6];
53
+ const y2 = screen[7];
54
+ return (x1 - x0) * (y2 - y0) - (x2 - x0) * (y1 - y0);
55
+ }
56
+ /**
57
+ * The screen-space barycentric weights of a point, before perspective correction.
58
+ *
59
+ * Answers false for a degenerate triangle — one whose three screen positions are collinear, which
60
+ * covers no pixel and whose weights are a division by zero. A caller that reaches this has a
61
+ * visibility identifier for a triangle the rasteriser never drew.
62
+ *
63
+ * **A point outside the triangle gets a negative weight rather than a clamp.** Clamping is how a
64
+ * pixel on the far side of an edge ends up shaded as though it were on the triangle, which along a
65
+ * silhouette is a fringe of the wrong material.
66
+ */
67
+ export function screenBarycentrics(screen, x, y, out) {
68
+ const area = screenArea(screen);
69
+ if (Math.abs(area) < 1e-12)
70
+ return false;
71
+ const x0 = screen[0];
72
+ const y0 = screen[1];
73
+ const x1 = screen[3];
74
+ const y1 = screen[4];
75
+ const x2 = screen[6];
76
+ const y2 = screen[7];
77
+ const inverse = 1 / area;
78
+ out[1] = ((x - x0) * (y2 - y0) - (x2 - x0) * (y - y0)) * inverse;
79
+ out[2] = ((x1 - x0) * (y - y0) - (x - x0) * (y1 - y0)) * inverse;
80
+ out[0] = 1 - out[1] - out[2];
81
+ return true;
82
+ }
83
+ /**
84
+ * The perspective-correct weights, from the screen-space ones and the vertices' reciprocal `w`.
85
+ *
86
+ * Each screen weight is scaled by its vertex's `1/w` and the three renormalised. At an orthographic
87
+ * projection every `1/w` is equal and this is the identity, which is why the affine version looks
88
+ * right on a test quad facing the camera and wrong on the floor beneath it.
89
+ */
90
+ export function perspectiveBarycentrics(screen, lambda, out) {
91
+ const w0 = lambda[0] * screen[2];
92
+ const w1 = lambda[1] * screen[5];
93
+ const w2 = lambda[2] * screen[8];
94
+ const sum = w0 + w1 + w2;
95
+ if (Math.abs(sum) < 1e-20)
96
+ return false;
97
+ const inverse = 1 / sum;
98
+ out[0] = w0 * inverse;
99
+ out[1] = w1 * inverse;
100
+ out[2] = w2 * inverse;
101
+ return true;
102
+ }
103
+ /**
104
+ * How each perspective-correct weight changes with screen x and y, at one pixel.
105
+ *
106
+ * Fills six numbers: `d0/dx, d1/dx, d2/dx, d0/dy, d1/dy, d2/dy`.
107
+ *
108
+ * **Derived rather than differenced.** The screen weights are linear in `x` and `y`, so their
109
+ * gradients are constants of the triangle; the perspective-correct ones are a ratio of two linear
110
+ * functions, and the quotient rule gives the rest. What this buys over a finite difference is that
111
+ * it is exact at the pixel rather than an average over one, and that it costs no neighbours — a
112
+ * compute invocation has none.
113
+ */
114
+ export function barycentricGradients(screen, lambda, out) {
115
+ const area = screenArea(screen);
116
+ if (Math.abs(area) < 1e-12)
117
+ return false;
118
+ const inverse = 1 / area;
119
+ const x0 = screen[0];
120
+ const y0 = screen[1];
121
+ const x1 = screen[3];
122
+ const y1 = screen[4];
123
+ const x2 = screen[6];
124
+ const y2 = screen[7];
125
+ /* The screen weights' own gradients, which are constant over the triangle. */
126
+ const d1dx = (y2 - y0) * inverse;
127
+ const d1dy = (x0 - x2) * inverse;
128
+ const d2dx = (y0 - y1) * inverse;
129
+ const d2dy = (x1 - x0) * inverse;
130
+ const d0dx = -d1dx - d2dx;
131
+ const d0dy = -d1dy - d2dy;
132
+ const iw0 = screen[2];
133
+ const iw1 = screen[5];
134
+ const iw2 = screen[8];
135
+ const n0 = lambda[0] * iw0;
136
+ const n1 = lambda[1] * iw1;
137
+ const n2 = lambda[2] * iw2;
138
+ const sum = n0 + n1 + n2;
139
+ if (Math.abs(sum) < 1e-20)
140
+ return false;
141
+ const inverseSum = 1 / sum;
142
+ /* The denominator's own gradient: the same weighted sum of the reciprocal w values. */
143
+ const dSumDx = d0dx * iw0 + d1dx * iw1 + d2dx * iw2;
144
+ const dSumDy = d0dy * iw0 + d1dy * iw1 + d2dy * iw2;
145
+ /* Quotient rule, per weight, with the shared denominator factored out. */
146
+ out[0] = (d0dx * iw0 - n0 * inverseSum * dSumDx) * inverseSum;
147
+ out[1] = (d1dx * iw1 - n1 * inverseSum * dSumDx) * inverseSum;
148
+ out[2] = (d2dx * iw2 - n2 * inverseSum * dSumDx) * inverseSum;
149
+ out[3] = (d0dy * iw0 - n0 * inverseSum * dSumDy) * inverseSum;
150
+ out[4] = (d1dy * iw1 - n1 * inverseSum * dSumDy) * inverseSum;
151
+ out[5] = (d2dy * iw2 - n2 * inverseSum * dSumDy) * inverseSum;
152
+ return true;
153
+ }
154
+ /** One attribute interpolated by the weights already computed. */
155
+ export function interpolate(a0, a1, a2, weights) {
156
+ return a0 * weights[0] + a1 * weights[1] + a2 * weights[2];
157
+ }
158
+ /**
159
+ * One attribute's rate of change with screen x and y, from the weight gradients.
160
+ *
161
+ * An attribute is a weighted sum of three constants, so its gradient is the same sum of the
162
+ * weights' gradients. This is what a texture fetch's level of detail is computed from.
163
+ */
164
+ export function attributeGradients(a0, a1, a2, gradients) {
165
+ return {
166
+ dx: a0 * gradients[0] + a1 * gradients[1] + a2 * gradients[2],
167
+ dy: a0 * gradients[3] + a1 * gradients[4] + a2 * gradients[5],
168
+ };
169
+ }
170
+ /**
171
+ * Which pixel a shading invocation is responsible for.
172
+ *
173
+ * A group covers `BIN_GROUP_SIZE` entries of one material's slice; an invocation past the end of
174
+ * the slice has no pixel, which is the ordinary case because the slice is not a multiple of the
175
+ * group size. Answers -1 there rather than the last pixel of the bin, which would shade it once
176
+ * per spare lane.
177
+ */
178
+ export function binPixel(pixels, offset, count, invocation) {
179
+ if (invocation < 0 || invocation >= count)
180
+ return -1;
181
+ return pixels[offset + invocation] ?? -1;
182
+ }
@@ -0,0 +1,136 @@
1
+ /** The directional shadow lookup, in TypeScript, so the second pipeline's copy has a reference. */
2
+ /**
3
+ * **`flat/directionalShadow.ts` in TypeScript, for one depth layer.**
4
+ *
5
+ * The lesson of the lit expression was a week old when this was written: a term that exists only
6
+ * as a shader is a term nothing can disagree with. So the lookup was written here first, tested
7
+ * here, and `SHADE_SHADOW_WGSL` beside it is the same arithmetic for the device with
8
+ * `gpu-parity.mjs` running both — which is the order the map itself then followed.
9
+ *
10
+ * **One layer, where the forward path has three.** That path keeps a static map, a peeled one
11
+ * holding the second static occluder, and a dynamic one for movers, and multiplies their
12
+ * transmissions — an arrangement its own comments argue for at length and which exists because its
13
+ * shadow *pool* is shared between lights and rationed across frames. The second pipeline has no
14
+ * such pool: it owns its geometry, and a pass owns whatever target it renders in `prepare`. When
15
+ * there is one caster set there is one map, and stating that is better than pretending the shape
16
+ * is the same.
17
+ *
18
+ * **Everything else is term for term**, because the two pipelines drawing one scene must not
19
+ * disagree about where a shadow ends. Each fade below returns *fully lit*, which is the thing that
20
+ * makes them dangerous: a guard that fires when it should not removes a shadow, and a missing
21
+ * shadow reads as a scene that never had one.
22
+ */
23
+ /** How much of a texel the filter reaches, which sets the softness of an edge. */
24
+ export declare const SHADOW_TEXEL_SCALE = 1.35;
25
+ /**
26
+ * The most depth the receiver-plane compensation may carry a tap, in texels.
27
+ *
28
+ * **A product of an unbounded gradient and a bounded offset**, and `directionalShadow.ts` records
29
+ * paying for the version where only the offset was bounded: a gradient that has blown up turns a
30
+ * fraction of a texel into a depth swing of whole map units, the comparison flips on alternate
31
+ * shadow texels, and what draws is hard straight stripes along the map's texel grid — oblique on
32
+ * screen, because that grid is turned by the sun rather than by the camera.
33
+ */
34
+ export declare const SHADOW_SLOPE_TEXELS = 9.1;
35
+ /** Subtracted from every comparison, in depth-span units. The contact bias. */
36
+ export declare const SHADOW_DEPTH_BIAS = 0.14;
37
+ /** The band of `n · l` over which the shadow modulation is given up. See `shadowFactor`. */
38
+ export declare const SHADOW_TERMINATOR_BAND: readonly [number, number];
39
+ /**
40
+ * The stretch of stored depth, 0 to 1, over which a shadow is given up at the map's far end.
41
+ *
42
+ * **A fit has to keep its receivers short of it**, and that is why it is a name rather than two
43
+ * literals: in the scene fit the far end is the scene's own rim, where nothing lies beyond, and a
44
+ * fit that followed the eye first put the whole of the eye's sphere inside it — `fitShadow` derives
45
+ * its least depth reach from the first of these two numbers.
46
+ */
47
+ export declare const SHADOW_DEPTH_FADE: readonly [number, number];
48
+ /**
49
+ * The twelve taps, transcribed from `flat/pointShadow.ts`.
50
+ *
51
+ * A Poisson set rather than a grid: a grid filters along its own axes and bands across them, and
52
+ * the sun turns the map's grid relative to the screen, so the banding arrives at an angle nobody
53
+ * can attribute to anything.
54
+ */
55
+ export declare const SHADOW_PCF_OFFSETS: readonly (readonly [number, number])[];
56
+ export interface ShadowSettings {
57
+ /** 0 turns the whole lookup off, and off is exact rather than nearly. */
58
+ readonly strength: number;
59
+ readonly mapSize: number;
60
+ /** Metres the map's depth range spans, which turns a stored depth into a distance. */
61
+ readonly depthSpan: number;
62
+ /** Metres a shadow may travel along the ground before it has faded out. */
63
+ readonly maxDistance: number;
64
+ /** The steepest `|light.xz| / light.y` that still casts. */
65
+ readonly maxSlope: number;
66
+ readonly taps: number;
67
+ /** Toward the light, unit length, as `Environment.directionalDir` is. */
68
+ readonly lightDir: readonly [number, number, number];
69
+ }
70
+ /**
71
+ * The receiver plane: how the light-space depth changes per unit of the light's own `u` and `v`.
72
+ *
73
+ * **A fragment shader gets `dFdx`/`dFdy` and a compute invocation has none**, having no neighbours
74
+ * to difference against — the same fact `SHADE_SURFACE_WGSL` exists for. So this takes the two
75
+ * derivatives rather than computing them: the forward path passes what the hardware gave it, and
76
+ * the second pipeline passes what it derives from the barycentric gradients it already has. The
77
+ * arithmetic between them is one function, which is the whole point.
78
+ *
79
+ * **It is a two-by-two solve and it is refused where the system is ill-conditioned.** A triangle
80
+ * seen almost edge-on in the light's projection has a determinant near zero, and dividing by it
81
+ * turns a fraction of a texel into a depth swing of whole map units — which the clamp downstream
82
+ * then has to catch. Refusing here is cheaper and says what happened: a plane whose slope cannot be
83
+ * measured gets no compensation rather than a wrong one.
84
+ *
85
+ * **Exact only where the light's projection is orthographic**, which a directional light's is: `w`
86
+ * is one, so the light-space position is linear in the world position and a barycentric
87
+ * interpolation of the three corners is the real thing rather than an approximation of it.
88
+ */
89
+ export declare function receiverPlaneDepthGradient(dx: readonly [number, number, number], dy: readonly [number, number, number], out: Float32Array): Float32Array;
90
+ /**
91
+ * The receiver plane a shading invocation can actually get, from the two things it already has.
92
+ *
93
+ * **A compute invocation has no neighbours, so the derivative has to be built rather than taken.**
94
+ * `SHADE_SURFACE_WGSL` already computes how each barycentric weight changes per pixel of screen —
95
+ * that is what a visibility buffer's shading needs for texture gradients — and a light-space
96
+ * position is a linear combination of the three corners with exactly those weights. So the
97
+ * derivative of the light-space position is the same combination of the corners with the *weight
98
+ * gradients*, which is what this assembles before handing it to the solve above.
99
+ *
100
+ * **Exact rather than approximate, because a directional light's projection is orthographic.** `w`
101
+ * is one everywhere, so the light-space position is linear in the world position and no perspective
102
+ * divide stands between the weights and the answer. A spot light would need the quotient rule here
103
+ * and this function would be wrong for it, which is worth knowing before one arrives.
104
+ *
105
+ * **The halving is the remap.** The lookup addresses the map in 0-to-1 coordinates and clip `x` and
106
+ * `y` run -1 to 1, so a slope stated per clip unit is twice the slope per map unit. Depth is not
107
+ * halved: this pipeline's light matrix already emits `z` in 0..1, which is the same asymmetry
108
+ * `shadowFactor` carries and for the same reason.
109
+ *
110
+ * Each corner is a light-space clip position, `x`, `y` and `z`, with `w` taken as one.
111
+ */
112
+ export declare function receiverPlaneFromWeights(l0: readonly [number, number, number], l1: readonly [number, number, number], l2: readonly [number, number, number], gx: readonly [number, number, number], gy: readonly [number, number, number], out: Float32Array): Float32Array;
113
+ /** How much of a shadow survives having been cast this far along the ground. */
114
+ export declare function shadowReach(distance: number, maxDistance: number): number;
115
+ /**
116
+ * Whether one tap is lit, and by how much.
117
+ *
118
+ * **A tap behind an occluder is not simply dark**: how far the shadow has travelled along the
119
+ * ground decides how much of it is left, which is what keeps one map's worth of geometry from
120
+ * shadowing the whole world.
121
+ */
122
+ export declare function directionalVisibility(receiverDepth: number, compareDepth: number, storedDepth: number, settings: ShadowSettings): number;
123
+ /**
124
+ * How much light reaches a receiver, from 0 in full shadow to 1 in full light.
125
+ *
126
+ * `lightPos` is the receiver in the light's clip space, before the divide. `depthGradient` is how
127
+ * the light-space depth changes across a pixel of the map — the receiver plane — which a fragment
128
+ * shader gets from `dFdx`/`dFdy` and a **compute invocation cannot**, having no neighbours. The
129
+ * second pipeline derives it from the barycentric gradients `SHADE_SURFACE_WGSL` already computes,
130
+ * which is the same reason that file computes them: a derivative has to come from somewhere.
131
+ *
132
+ * `depthAt` reads the map with nearest filtering, which is what the comparison assumes: the fetched
133
+ * depth belongs to the centre of the containing texel, and following the continuous coordinate
134
+ * instead leaves half a texel of uncompensated slope — the repeating ribs a grazing face shows.
135
+ */
136
+ export declare function shadowFactor(lightPos: readonly [number, number, number, number], depthGradient: readonly [number, number], ndl: number, settings: ShadowSettings, depthAt: (u: number, v: number) => number): number;
@@ -0,0 +1,224 @@
1
+ /** The directional shadow lookup, in TypeScript, so the second pipeline's copy has a reference. */
2
+ import { DIRECTIONAL_SHADOW_FADE_START, POINT_SHADOW_FADE_START } from '../renderQuality.js';
3
+ /**
4
+ * **`flat/directionalShadow.ts` in TypeScript, for one depth layer.**
5
+ *
6
+ * The lesson of the lit expression was a week old when this was written: a term that exists only
7
+ * as a shader is a term nothing can disagree with. So the lookup was written here first, tested
8
+ * here, and `SHADE_SHADOW_WGSL` beside it is the same arithmetic for the device with
9
+ * `gpu-parity.mjs` running both — which is the order the map itself then followed.
10
+ *
11
+ * **One layer, where the forward path has three.** That path keeps a static map, a peeled one
12
+ * holding the second static occluder, and a dynamic one for movers, and multiplies their
13
+ * transmissions — an arrangement its own comments argue for at length and which exists because its
14
+ * shadow *pool* is shared between lights and rationed across frames. The second pipeline has no
15
+ * such pool: it owns its geometry, and a pass owns whatever target it renders in `prepare`. When
16
+ * there is one caster set there is one map, and stating that is better than pretending the shape
17
+ * is the same.
18
+ *
19
+ * **Everything else is term for term**, because the two pipelines drawing one scene must not
20
+ * disagree about where a shadow ends. Each fade below returns *fully lit*, which is the thing that
21
+ * makes them dangerous: a guard that fires when it should not removes a shadow, and a missing
22
+ * shadow reads as a scene that never had one.
23
+ */
24
+ /** How much of a texel the filter reaches, which sets the softness of an edge. */
25
+ export const SHADOW_TEXEL_SCALE = 1.35;
26
+ /**
27
+ * The most depth the receiver-plane compensation may carry a tap, in texels.
28
+ *
29
+ * **A product of an unbounded gradient and a bounded offset**, and `directionalShadow.ts` records
30
+ * paying for the version where only the offset was bounded: a gradient that has blown up turns a
31
+ * fraction of a texel into a depth swing of whole map units, the comparison flips on alternate
32
+ * shadow texels, and what draws is hard straight stripes along the map's texel grid — oblique on
33
+ * screen, because that grid is turned by the sun rather than by the camera.
34
+ */
35
+ export const SHADOW_SLOPE_TEXELS = 9.1;
36
+ /** Subtracted from every comparison, in depth-span units. The contact bias. */
37
+ export const SHADOW_DEPTH_BIAS = 0.14;
38
+ /** The band of `n · l` over which the shadow modulation is given up. See `shadowFactor`. */
39
+ export const SHADOW_TERMINATOR_BAND = [0.08, 0.2];
40
+ /**
41
+ * The stretch of stored depth, 0 to 1, over which a shadow is given up at the map's far end.
42
+ *
43
+ * **A fit has to keep its receivers short of it**, and that is why it is a name rather than two
44
+ * literals: in the scene fit the far end is the scene's own rim, where nothing lies beyond, and a
45
+ * fit that followed the eye first put the whole of the eye's sphere inside it — `fitShadow` derives
46
+ * its least depth reach from the first of these two numbers.
47
+ */
48
+ export const SHADOW_DEPTH_FADE = [0.9, 1];
49
+ /**
50
+ * The twelve taps, transcribed from `flat/pointShadow.ts`.
51
+ *
52
+ * A Poisson set rather than a grid: a grid filters along its own axes and bands across them, and
53
+ * the sun turns the map's grid relative to the screen, so the banding arrives at an angle nobody
54
+ * can attribute to anything.
55
+ */
56
+ export const SHADOW_PCF_OFFSETS = [
57
+ [-0.326, -0.406],
58
+ [0.519, 0.767],
59
+ [0.962, -0.195],
60
+ [-0.696, 0.457],
61
+ [-0.84, -0.074],
62
+ [0.185, -0.893],
63
+ [0.896, 0.412],
64
+ [-0.203, 0.621],
65
+ [0.473, -0.48],
66
+ [0.507, 0.064],
67
+ [-0.322, -0.933],
68
+ [-0.792, -0.598],
69
+ ];
70
+ /**
71
+ * The receiver plane: how the light-space depth changes per unit of the light's own `u` and `v`.
72
+ *
73
+ * **A fragment shader gets `dFdx`/`dFdy` and a compute invocation has none**, having no neighbours
74
+ * to difference against — the same fact `SHADE_SURFACE_WGSL` exists for. So this takes the two
75
+ * derivatives rather than computing them: the forward path passes what the hardware gave it, and
76
+ * the second pipeline passes what it derives from the barycentric gradients it already has. The
77
+ * arithmetic between them is one function, which is the whole point.
78
+ *
79
+ * **It is a two-by-two solve and it is refused where the system is ill-conditioned.** A triangle
80
+ * seen almost edge-on in the light's projection has a determinant near zero, and dividing by it
81
+ * turns a fraction of a texel into a depth swing of whole map units — which the clamp downstream
82
+ * then has to catch. Refusing here is cheaper and says what happened: a plane whose slope cannot be
83
+ * measured gets no compensation rather than a wrong one.
84
+ *
85
+ * **Exact only where the light's projection is orthographic**, which a directional light's is: `w`
86
+ * is one, so the light-space position is linear in the world position and a barycentric
87
+ * interpolation of the three corners is the real thing rather than an approximation of it.
88
+ */
89
+ export function receiverPlaneDepthGradient(dx, dy, out) {
90
+ const determinant = dx[0] * dy[1] - dx[1] * dy[0];
91
+ const conditioning = Math.hypot(dx[0], dx[1]) * Math.hypot(dy[0], dy[1]);
92
+ if (Math.abs(determinant) < 1e-4 * conditioning) {
93
+ out[0] = 0;
94
+ out[1] = 0;
95
+ return out;
96
+ }
97
+ out[0] = (dx[2] * dy[1] - dx[1] * dy[2]) / determinant;
98
+ out[1] = (dx[0] * dy[2] - dx[2] * dy[0]) / determinant;
99
+ return out;
100
+ }
101
+ /**
102
+ * The receiver plane a shading invocation can actually get, from the two things it already has.
103
+ *
104
+ * **A compute invocation has no neighbours, so the derivative has to be built rather than taken.**
105
+ * `SHADE_SURFACE_WGSL` already computes how each barycentric weight changes per pixel of screen —
106
+ * that is what a visibility buffer's shading needs for texture gradients — and a light-space
107
+ * position is a linear combination of the three corners with exactly those weights. So the
108
+ * derivative of the light-space position is the same combination of the corners with the *weight
109
+ * gradients*, which is what this assembles before handing it to the solve above.
110
+ *
111
+ * **Exact rather than approximate, because a directional light's projection is orthographic.** `w`
112
+ * is one everywhere, so the light-space position is linear in the world position and no perspective
113
+ * divide stands between the weights and the answer. A spot light would need the quotient rule here
114
+ * and this function would be wrong for it, which is worth knowing before one arrives.
115
+ *
116
+ * **The halving is the remap.** The lookup addresses the map in 0-to-1 coordinates and clip `x` and
117
+ * `y` run -1 to 1, so a slope stated per clip unit is twice the slope per map unit. Depth is not
118
+ * halved: this pipeline's light matrix already emits `z` in 0..1, which is the same asymmetry
119
+ * `shadowFactor` carries and for the same reason.
120
+ *
121
+ * Each corner is a light-space clip position, `x`, `y` and `z`, with `w` taken as one.
122
+ */
123
+ export function receiverPlaneFromWeights(l0, l1, l2, gx, gy, out) {
124
+ const along = (g, axis) => g[0] * l0[axis] +
125
+ g[1] * l1[axis] +
126
+ g[2] * l2[axis];
127
+ return receiverPlaneDepthGradient([along(gx, 0) * 0.5, along(gx, 1) * 0.5, along(gx, 2)], [along(gy, 0) * 0.5, along(gy, 1) * 0.5, along(gy, 2)], out);
128
+ }
129
+ /** GLSL's `smoothstep`, which is not `Math`'s anything. */
130
+ function smoothstep(edge0, edge1, x) {
131
+ const t = Math.min(Math.max((x - edge0) / (edge1 - edge0), 0), 1);
132
+ return t * t * (3 - 2 * t);
133
+ }
134
+ /** How much of a shadow survives having been cast this far along the ground. */
135
+ export function shadowReach(distance, maxDistance) {
136
+ return 1 - smoothstep(maxDistance * POINT_SHADOW_FADE_START, maxDistance, distance);
137
+ }
138
+ /**
139
+ * Whether one tap is lit, and by how much.
140
+ *
141
+ * **A tap behind an occluder is not simply dark**: how far the shadow has travelled along the
142
+ * ground decides how much of it is left, which is what keeps one map's worth of geometry from
143
+ * shadowing the whole world.
144
+ */
145
+ export function directionalVisibility(receiverDepth, compareDepth, storedDepth, settings) {
146
+ if (compareDepth <= storedDepth)
147
+ return 1;
148
+ const rayDistance = Math.max(receiverDepth - storedDepth, 0) * settings.depthSpan;
149
+ const ground = rayDistance * Math.hypot(settings.lightDir[0], settings.lightDir[2]);
150
+ return 1 - shadowReach(ground, settings.maxDistance);
151
+ }
152
+ /**
153
+ * How much light reaches a receiver, from 0 in full shadow to 1 in full light.
154
+ *
155
+ * `lightPos` is the receiver in the light's clip space, before the divide. `depthGradient` is how
156
+ * the light-space depth changes across a pixel of the map — the receiver plane — which a fragment
157
+ * shader gets from `dFdx`/`dFdy` and a **compute invocation cannot**, having no neighbours. The
158
+ * second pipeline derives it from the barycentric gradients `SHADE_SURFACE_WGSL` already computes,
159
+ * which is the same reason that file computes them: a derivative has to come from somewhere.
160
+ *
161
+ * `depthAt` reads the map with nearest filtering, which is what the comparison assumes: the fetched
162
+ * depth belongs to the centre of the containing texel, and following the continuous coordinate
163
+ * instead leaves half a texel of uncompensated slope — the repeating ribs a grazing face shows.
164
+ */
165
+ export function shadowFactor(lightPos, depthGradient, ndl, settings, depthAt) {
166
+ /*
167
+ * **Four early-outs below, and every one of them is a cost rather than a correctness guard.**
168
+ * The result is `1 + (lit - 1) * amount` and `amount` is the product of all four fades, so a fade
169
+ * at zero returns exactly one whether or not the return fires — perturbing any of them changes
170
+ * no answer, which is recorded here rather than papered over. They stay because each one skips a
171
+ * loop of twelve texture reads, which on a full screen is the difference between a lookup and a
172
+ * pass. The bounds check is the one with a second job: without it the taps read outside the map.
173
+ */
174
+ if (settings.strength <= 0)
175
+ return 1;
176
+ const w = lightPos[3];
177
+ if (w === 0)
178
+ return 1;
179
+ /*
180
+ * **`x` and `y` are remapped and `z` is not, and that is a convention rather than an asymmetry.**
181
+ * WebGPU clips `z` to 0..1 already; OpenGL clips it to -1..1, which is why the GLSL this is
182
+ * transcribed from remaps all three. This module is the second pipeline's and the second pipeline
183
+ * is WebGPU only, so it takes WebGPU's convention — and a caller handing it a GL-style depth gets
184
+ * a shadow that is half as deep as it should be and biased by half the map, which reads as
185
+ * everything being lit. `CLIP_CORRECTION` records the same family of mistake one level up.
186
+ */
187
+ const px = (lightPos[0] / w) * 0.5 + 0.5;
188
+ const py = (lightPos[1] / w) * 0.5 + 0.5;
189
+ const pz = lightPos[2] / w;
190
+ const receiverFade = smoothstep(SHADOW_TERMINATOR_BAND[0], SHADOW_TERMINATOR_BAND[1], ndl);
191
+ if (receiverFade <= 0)
192
+ return 1;
193
+ if (pz > 1 || px < 0 || px > 1 || py < 0 || py > 1)
194
+ return 1;
195
+ const fromCentre = Math.max(Math.abs(px - 0.5) * 2, Math.abs(py - 0.5) * 2);
196
+ const edgeFade = (1 - smoothstep(0.72, 0.98, fromCentre)) *
197
+ (1 - smoothstep(SHADOW_DEPTH_FADE[0], SHADOW_DEPTH_FADE[1], pz));
198
+ if (edgeFade <= 0)
199
+ return 1;
200
+ const slope = Math.hypot(settings.lightDir[0], settings.lightDir[2]) /
201
+ Math.max(settings.lightDir[1], 0.001);
202
+ const lowElevationFade = 1 - smoothstep(settings.maxSlope * DIRECTIONAL_SHADOW_FADE_START, settings.maxSlope, slope);
203
+ if (lowElevationFade <= 0)
204
+ return 1;
205
+ const texel = SHADOW_TEXEL_SCALE / settings.mapSize;
206
+ const slopeLimit = SHADOW_SLOPE_TEXELS / settings.mapSize;
207
+ const taps = Math.max(1, Math.min(Math.trunc(settings.taps), SHADOW_PCF_OFFSETS.length));
208
+ let lit = 0;
209
+ for (let tap = 0; tap < taps; tap += 1) {
210
+ const offset = SHADOW_PCF_OFFSETS[tap];
211
+ const u = px + offset[0] * texel;
212
+ const v = py + offset[1] * texel;
213
+ const centreU = (Math.floor(u * settings.mapSize) + 0.5) / settings.mapSize;
214
+ const centreV = (Math.floor(v * settings.mapSize) + 0.5) / settings.mapSize;
215
+ const raw = depthGradient[0] * (centreU - px) + depthGradient[1] * (centreV - py);
216
+ const slopeOffset = Math.min(Math.max(raw, -slopeLimit), slopeLimit);
217
+ const receiverDepth = pz + slopeOffset;
218
+ const compare = receiverDepth - SHADOW_DEPTH_BIAS / settings.depthSpan;
219
+ lit += directionalVisibility(receiverDepth, compare, depthAt(u, v), settings);
220
+ }
221
+ lit /= taps;
222
+ const amount = settings.strength * edgeFade * lowElevationFade * receiverFade;
223
+ return 1 + (lit - 1) * amount;
224
+ }