@thunderclap-engine/engine 0.0.0-stage → 1.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 (566) hide show
  1. package/CHANGELOG.md +390 -0
  2. package/LICENSE +21 -0
  3. package/README.md +333 -2
  4. package/dist/analytics/analytics.d.ts +80 -0
  5. package/dist/analytics/analytics.js +129 -0
  6. package/dist/analytics/index.d.ts +1 -0
  7. package/dist/analytics/index.js +1 -0
  8. package/dist/anim/aim.d.ts +43 -0
  9. package/dist/anim/aim.js +157 -0
  10. package/dist/anim/clip.d.ts +98 -0
  11. package/dist/anim/clip.js +282 -0
  12. package/dist/anim/compress.d.ts +65 -0
  13. package/dist/anim/compress.js +339 -0
  14. package/dist/anim/foot-ik.d.ts +74 -0
  15. package/dist/anim/foot-ik.js +241 -0
  16. package/dist/anim/graph.d.ts +153 -0
  17. package/dist/anim/graph.js +532 -0
  18. package/dist/anim/ik.d.ts +105 -0
  19. package/dist/anim/ik.js +435 -0
  20. package/dist/anim/index.d.ts +36 -0
  21. package/dist/anim/index.js +21 -0
  22. package/dist/anim/match.d.ts +66 -0
  23. package/dist/anim/match.js +153 -0
  24. package/dist/anim/mixer.d.ts +113 -0
  25. package/dist/anim/mixer.js +314 -0
  26. package/dist/anim/morph.d.ts +41 -0
  27. package/dist/anim/morph.js +94 -0
  28. package/dist/anim/pose.d.ts +74 -0
  29. package/dist/anim/pose.js +254 -0
  30. package/dist/anim/quat.d.ts +44 -0
  31. package/dist/anim/quat.js +187 -0
  32. package/dist/anim/retarget.d.ts +97 -0
  33. package/dist/anim/retarget.js +366 -0
  34. package/dist/anim/root-motion.d.ts +44 -0
  35. package/dist/anim/root-motion.js +224 -0
  36. package/dist/anim/skin-pose.d.ts +39 -0
  37. package/dist/anim/skin-pose.js +53 -0
  38. package/dist/anim/socket.d.ts +47 -0
  39. package/dist/anim/socket.js +84 -0
  40. package/dist/anim/spring-chain.d.ts +73 -0
  41. package/dist/anim/spring-chain.js +294 -0
  42. package/dist/assets/atlas.d.ts +90 -0
  43. package/dist/assets/atlas.js +212 -0
  44. package/dist/assets/baked.d.ts +33 -0
  45. package/dist/assets/baked.js +38 -0
  46. package/dist/assets/bundle.d.ts +88 -0
  47. package/dist/assets/bundle.js +464 -0
  48. package/dist/assets/cache.d.ts +56 -0
  49. package/dist/assets/cache.js +123 -0
  50. package/dist/assets/gltf-load.d.ts +252 -0
  51. package/dist/assets/gltf-load.js +661 -0
  52. package/dist/assets/gltf-skin.d.ts +36 -0
  53. package/dist/assets/gltf-skin.js +109 -0
  54. package/dist/assets/gltf.d.ts +706 -0
  55. package/dist/assets/gltf.js +1376 -0
  56. package/dist/assets/index.d.ts +20 -0
  57. package/dist/assets/index.js +10 -0
  58. package/dist/assets/ktx2.d.ts +71 -0
  59. package/dist/assets/ktx2.js +213 -0
  60. package/dist/assets/manifest.d.ts +79 -0
  61. package/dist/assets/manifest.js +7 -0
  62. package/dist/assets/meshopt.d.ts +23 -0
  63. package/dist/assets/meshopt.js +403 -0
  64. package/dist/audio/audio.d.ts +733 -0
  65. package/dist/audio/audio.js +2013 -0
  66. package/dist/audio/dsp.d.ts +61 -0
  67. package/dist/audio/dsp.js +159 -0
  68. package/dist/audio/duck.d.ts +44 -0
  69. package/dist/audio/duck.js +101 -0
  70. package/dist/audio/envelope.d.ts +53 -0
  71. package/dist/audio/envelope.js +106 -0
  72. package/dist/audio/index.d.ts +13 -0
  73. package/dist/audio/index.js +14 -0
  74. package/dist/audio/music.d.ts +113 -0
  75. package/dist/audio/music.js +548 -0
  76. package/dist/audio/occlusion.d.ts +55 -0
  77. package/dist/audio/occlusion.js +61 -0
  78. package/dist/audio/persist.d.ts +23 -0
  79. package/dist/audio/persist.js +27 -0
  80. package/dist/audio/recipes.d.ts +55 -0
  81. package/dist/audio/recipes.js +227 -0
  82. package/dist/audio/send.d.ts +41 -0
  83. package/dist/audio/send.js +118 -0
  84. package/dist/audio/sprites.d.ts +59 -0
  85. package/dist/audio/sprites.js +43 -0
  86. package/dist/audio/synth.d.ts +103 -0
  87. package/dist/audio/synth.js +197 -0
  88. package/dist/audio/variations.d.ts +25 -0
  89. package/dist/audio/variations.js +55 -0
  90. package/dist/audio/zones.d.ts +100 -0
  91. package/dist/audio/zones.js +167 -0
  92. package/dist/core/detached-surface.d.ts +44 -0
  93. package/dist/core/detached-surface.js +104 -0
  94. package/dist/core/events.d.ts +24 -0
  95. package/dist/core/events.js +43 -0
  96. package/dist/core/game.d.ts +409 -0
  97. package/dist/core/game.js +530 -0
  98. package/dist/core/hash.d.ts +24 -0
  99. package/dist/core/hash.js +49 -0
  100. package/dist/core/index.d.ts +17 -0
  101. package/dist/core/index.js +9 -0
  102. package/dist/core/loop.d.ts +113 -0
  103. package/dist/core/loop.js +185 -0
  104. package/dist/core/math.d.ts +58 -0
  105. package/dist/core/math.js +210 -0
  106. package/dist/core/noise.d.ts +40 -0
  107. package/dist/core/noise.js +247 -0
  108. package/dist/core/pool.d.ts +42 -0
  109. package/dist/core/pool.js +85 -0
  110. package/dist/core/random.d.ts +61 -0
  111. package/dist/core/random.js +199 -0
  112. package/dist/core/scene.d.ts +79 -0
  113. package/dist/core/scene.js +195 -0
  114. package/dist/core/timer.d.ts +66 -0
  115. package/dist/core/timer.js +286 -0
  116. package/dist/fx/billboard-wgsl.d.ts +29 -0
  117. package/dist/fx/billboard-wgsl.js +158 -0
  118. package/dist/fx/billboard.d.ts +249 -0
  119. package/dist/fx/billboard.js +702 -0
  120. package/dist/fx/debug-draw.d.ts +127 -0
  121. package/dist/fx/debug-draw.js +523 -0
  122. package/dist/fx/debug-wgsl.d.ts +7 -0
  123. package/dist/fx/debug-wgsl.js +32 -0
  124. package/dist/fx/decals-wgsl.d.ts +15 -0
  125. package/dist/fx/decals-wgsl.js +173 -0
  126. package/dist/fx/decals.d.ts +216 -0
  127. package/dist/fx/decals.js +607 -0
  128. package/dist/fx/effect.d.ts +129 -0
  129. package/dist/fx/effect.js +349 -0
  130. package/dist/fx/effects/hit-spark.json +48 -0
  131. package/dist/fx/effects/index.d.ts +8 -0
  132. package/dist/fx/effects/index.js +9 -0
  133. package/dist/fx/effects/magic-aura.json +47 -0
  134. package/dist/fx/effects/smoke-puff.json +34 -0
  135. package/dist/fx/emit-surface.d.ts +40 -0
  136. package/dist/fx/emit-surface.js +124 -0
  137. package/dist/fx/flicker.d.ts +30 -0
  138. package/dist/fx/flicker.js +42 -0
  139. package/dist/fx/index.d.ts +31 -0
  140. package/dist/fx/index.js +20 -0
  141. package/dist/fx/particles-wgsl.d.ts +57 -0
  142. package/dist/fx/particles-wgsl.js +828 -0
  143. package/dist/fx/particles.d.ts +566 -0
  144. package/dist/fx/particles.js +1533 -0
  145. package/dist/fx/random.d.ts +15 -0
  146. package/dist/fx/random.js +20 -0
  147. package/dist/fx/sky-wgsl.d.ts +23 -0
  148. package/dist/fx/sky-wgsl.js +127 -0
  149. package/dist/fx/sky.d.ts +96 -0
  150. package/dist/fx/sky.js +175 -0
  151. package/dist/fx/text-atlas.d.ts +143 -0
  152. package/dist/fx/text-atlas.js +327 -0
  153. package/dist/fx/trail-wgsl.d.ts +11 -0
  154. package/dist/fx/trail-wgsl.js +42 -0
  155. package/dist/fx/trail.d.ts +194 -0
  156. package/dist/fx/trail.js +555 -0
  157. package/dist/fx/water-wgsl.d.ts +34 -0
  158. package/dist/fx/water-wgsl.js +197 -0
  159. package/dist/fx/water.d.ts +176 -0
  160. package/dist/fx/water.js +371 -0
  161. package/dist/geom/collide/bvh.d.ts +151 -0
  162. package/dist/geom/collide/bvh.js +493 -0
  163. package/dist/geom/collide/debug.d.ts +14 -0
  164. package/dist/geom/collide/debug.js +74 -0
  165. package/dist/geom/collide/grid.d.ts +26 -0
  166. package/dist/geom/collide/grid.js +91 -0
  167. package/dist/geom/collide/index.d.ts +8 -0
  168. package/dist/geom/collide/index.js +8 -0
  169. package/dist/geom/collide/queries.d.ts +70 -0
  170. package/dist/geom/collide/queries.js +583 -0
  171. package/dist/geom/collide/serialize.d.ts +14 -0
  172. package/dist/geom/collide/serialize.js +145 -0
  173. package/dist/geom/collide/shapes.d.ts +161 -0
  174. package/dist/geom/collide/shapes.js +686 -0
  175. package/dist/geom/collide/slide.d.ts +66 -0
  176. package/dist/geom/collide/slide.js +375 -0
  177. package/dist/geom/collide/tile-slide.d.ts +88 -0
  178. package/dist/geom/collide/tile-slide.js +255 -0
  179. package/dist/geom/geometry.d.ts +95 -0
  180. package/dist/geom/geometry.js +322 -0
  181. package/dist/geom/heightfield.d.ts +72 -0
  182. package/dist/geom/heightfield.js +295 -0
  183. package/dist/geom/hull.d.ts +27 -0
  184. package/dist/geom/hull.js +279 -0
  185. package/dist/geom/index.d.ts +35 -0
  186. package/dist/geom/index.js +23 -0
  187. package/dist/geom/iso.d.ts +74 -0
  188. package/dist/geom/iso.js +89 -0
  189. package/dist/geom/lod.d.ts +29 -0
  190. package/dist/geom/lod.js +27 -0
  191. package/dist/geom/ops.d.ts +47 -0
  192. package/dist/geom/ops.js +285 -0
  193. package/dist/geom/primitives.d.ts +97 -0
  194. package/dist/geom/primitives.js +359 -0
  195. package/dist/geom/shapes.d.ts +43 -0
  196. package/dist/geom/shapes.js +88 -0
  197. package/dist/geom/simplify.d.ts +38 -0
  198. package/dist/geom/simplify.js +519 -0
  199. package/dist/geom/split.d.ts +37 -0
  200. package/dist/geom/split.js +413 -0
  201. package/dist/geom/sweep.d.ts +83 -0
  202. package/dist/geom/sweep.js +534 -0
  203. package/dist/geom/tangents.d.ts +16 -0
  204. package/dist/geom/tangents.js +124 -0
  205. package/dist/geom/triangulate.d.ts +19 -0
  206. package/dist/geom/triangulate.js +147 -0
  207. package/dist/host/default.d.ts +10 -0
  208. package/dist/host/default.js +10 -0
  209. package/dist/host/index.d.ts +4 -0
  210. package/dist/host/index.js +3 -0
  211. package/dist/host/types.d.ts +144 -0
  212. package/dist/host/types.js +1 -0
  213. package/dist/host/web-links.d.ts +38 -0
  214. package/dist/host/web-links.js +110 -0
  215. package/dist/host/web.d.ts +30 -0
  216. package/dist/host/web.js +216 -0
  217. package/dist/host/xr.d.ts +24 -0
  218. package/dist/host/xr.js +59 -0
  219. package/dist/i18n/i18n.d.ts +59 -0
  220. package/dist/i18n/i18n.js +241 -0
  221. package/dist/i18n/index.d.ts +3 -0
  222. package/dist/i18n/index.js +3 -0
  223. package/dist/i18n/message.d.ts +72 -0
  224. package/dist/i18n/message.js +369 -0
  225. package/dist/i18n/negotiate.d.ts +17 -0
  226. package/dist/i18n/negotiate.js +58 -0
  227. package/dist/index.d.ts +7 -0
  228. package/dist/index.js +1 -0
  229. package/dist/input/bindings.d.ts +31 -0
  230. package/dist/input/bindings.js +141 -0
  231. package/dist/input/clock.d.ts +44 -0
  232. package/dist/input/clock.js +49 -0
  233. package/dist/input/follow.d.ts +133 -0
  234. package/dist/input/follow.js +218 -0
  235. package/dist/input/gestures.d.ts +281 -0
  236. package/dist/input/gestures.js +662 -0
  237. package/dist/input/glyphs.d.ts +35 -0
  238. package/dist/input/glyphs.js +216 -0
  239. package/dist/input/gyro.d.ts +85 -0
  240. package/dist/input/gyro.js +140 -0
  241. package/dist/input/index.d.ts +19 -0
  242. package/dist/input/index.js +16 -0
  243. package/dist/input/input.d.ts +543 -0
  244. package/dist/input/input.js +1492 -0
  245. package/dist/input/look.d.ts +327 -0
  246. package/dist/input/look.js +681 -0
  247. package/dist/input/magnify.d.ts +177 -0
  248. package/dist/input/magnify.js +131 -0
  249. package/dist/input/pointer-dispatch.d.ts +138 -0
  250. package/dist/input/pointer-dispatch.js +180 -0
  251. package/dist/input/pointer-router.d.ts +204 -0
  252. package/dist/input/pointer-router.js +533 -0
  253. package/dist/input/pointer-sources.d.ts +164 -0
  254. package/dist/input/pointer-sources.js +306 -0
  255. package/dist/input/rays.d.ts +138 -0
  256. package/dist/input/rays.js +615 -0
  257. package/dist/input/record.d.ts +209 -0
  258. package/dist/input/record.js +601 -0
  259. package/dist/input/touch-zones.d.ts +147 -0
  260. package/dist/input/touch-zones.js +300 -0
  261. package/dist/input/types.d.ts +158 -0
  262. package/dist/input/types.js +1 -0
  263. package/dist/input/xr-source.d.ts +53 -0
  264. package/dist/input/xr-source.js +164 -0
  265. package/dist/nav/astar.d.ts +45 -0
  266. package/dist/nav/astar.js +182 -0
  267. package/dist/nav/corridor.d.ts +40 -0
  268. package/dist/nav/corridor.js +95 -0
  269. package/dist/nav/crowd.d.ts +94 -0
  270. package/dist/nav/crowd.js +624 -0
  271. package/dist/nav/flow.d.ts +61 -0
  272. package/dist/nav/flow.js +193 -0
  273. package/dist/nav/grid.d.ts +108 -0
  274. package/dist/nav/grid.js +210 -0
  275. package/dist/nav/heap.d.ts +22 -0
  276. package/dist/nav/heap.js +92 -0
  277. package/dist/nav/index.d.ts +37 -0
  278. package/dist/nav/index.js +19 -0
  279. package/dist/nav/mesh/areas.d.ts +44 -0
  280. package/dist/nav/mesh/areas.js +98 -0
  281. package/dist/nav/mesh/bake-options.d.ts +33 -0
  282. package/dist/nav/mesh/bake-options.js +76 -0
  283. package/dist/nav/mesh/contour.d.ts +22 -0
  284. package/dist/nav/mesh/contour.js +323 -0
  285. package/dist/nav/mesh/flow3.d.ts +35 -0
  286. package/dist/nav/mesh/flow3.js +229 -0
  287. package/dist/nav/mesh/links.d.ts +38 -0
  288. package/dist/nav/mesh/links.js +72 -0
  289. package/dist/nav/mesh/mesh-path.d.ts +152 -0
  290. package/dist/nav/mesh/mesh-path.js +643 -0
  291. package/dist/nav/mesh/mesh-reach.d.ts +78 -0
  292. package/dist/nav/mesh/mesh-reach.js +450 -0
  293. package/dist/nav/mesh/navmesh.d.ts +117 -0
  294. package/dist/nav/mesh/navmesh.js +647 -0
  295. package/dist/nav/mesh/polymesh.d.ts +24 -0
  296. package/dist/nav/mesh/polymesh.js +384 -0
  297. package/dist/nav/mesh/regions.d.ts +12 -0
  298. package/dist/nav/mesh/regions.js +367 -0
  299. package/dist/nav/mesh/serialize.d.ts +13 -0
  300. package/dist/nav/mesh/serialize.js +197 -0
  301. package/dist/nav/mesh/voxelize.d.ts +111 -0
  302. package/dist/nav/mesh/voxelize.js +552 -0
  303. package/dist/nav/obstacles.d.ts +75 -0
  304. package/dist/nav/obstacles.js +294 -0
  305. package/dist/nav/path.d.ts +59 -0
  306. package/dist/nav/path.js +177 -0
  307. package/dist/nav/query-queue.d.ts +46 -0
  308. package/dist/nav/query-queue.js +308 -0
  309. package/dist/nav/reach.d.ts +53 -0
  310. package/dist/nav/reach.js +148 -0
  311. package/dist/nav/steer.d.ts +36 -0
  312. package/dist/nav/steer.js +78 -0
  313. package/dist/platform/boot.d.ts +28 -0
  314. package/dist/platform/boot.js +43 -0
  315. package/dist/platform/device-mode.d.ts +40 -0
  316. package/dist/platform/device-mode.js +104 -0
  317. package/dist/platform/device.d.ts +31 -0
  318. package/dist/platform/device.js +91 -0
  319. package/dist/platform/environment.d.ts +42 -0
  320. package/dist/platform/environment.js +82 -0
  321. package/dist/platform/gpu.d.ts +40 -0
  322. package/dist/platform/gpu.js +57 -0
  323. package/dist/platform/index.d.ts +18 -0
  324. package/dist/platform/index.js +10 -0
  325. package/dist/platform/letterbox.d.ts +43 -0
  326. package/dist/platform/letterbox.js +57 -0
  327. package/dist/platform/links.d.ts +20 -0
  328. package/dist/platform/links.js +43 -0
  329. package/dist/platform/screens.d.ts +36 -0
  330. package/dist/platform/screens.js +138 -0
  331. package/dist/platform/surface.d.ts +72 -0
  332. package/dist/platform/surface.js +157 -0
  333. package/dist/platform/xr-fake.d.ts +168 -0
  334. package/dist/platform/xr-fake.js +434 -0
  335. package/dist/platform/xr-surface.d.ts +120 -0
  336. package/dist/platform/xr-surface.js +214 -0
  337. package/dist/platform/xr-types.d.ts +262 -0
  338. package/dist/post/ao.d.ts +12 -0
  339. package/dist/post/ao.js +136 -0
  340. package/dist/post/blend.d.ts +27 -0
  341. package/dist/post/blend.js +129 -0
  342. package/dist/post/depth-pass.d.ts +43 -0
  343. package/dist/post/depth-pass.js +118 -0
  344. package/dist/post/dof.d.ts +1 -0
  345. package/dist/post/dof.js +66 -0
  346. package/dist/post/exposure.d.ts +80 -0
  347. package/dist/post/exposure.js +322 -0
  348. package/dist/post/index.d.ts +12 -0
  349. package/dist/post/index.js +11 -0
  350. package/dist/post/lens-dirt.d.ts +13 -0
  351. package/dist/post/lens-dirt.js +91 -0
  352. package/dist/post/lut.d.ts +19 -0
  353. package/dist/post/lut.js +50 -0
  354. package/dist/post/motion-blur.d.ts +9 -0
  355. package/dist/post/motion-blur.js +78 -0
  356. package/dist/post/post-stack.d.ts +302 -0
  357. package/dist/post/post-stack.js +1137 -0
  358. package/dist/post/settings.d.ts +485 -0
  359. package/dist/post/settings.js +416 -0
  360. package/dist/post/shaders.d.ts +25 -0
  361. package/dist/post/shaders.js +410 -0
  362. package/dist/post/ssr.d.ts +16 -0
  363. package/dist/post/ssr.js +155 -0
  364. package/dist/post/taa.d.ts +74 -0
  365. package/dist/post/taa.js +235 -0
  366. package/dist/post/white-balance.d.ts +25 -0
  367. package/dist/post/white-balance.js +98 -0
  368. package/dist/render/bindings.d.ts +98 -0
  369. package/dist/render/bindings.js +281 -0
  370. package/dist/render/blend.d.ts +13 -0
  371. package/dist/render/blend.js +26 -0
  372. package/dist/render/camera-2d.d.ts +72 -0
  373. package/dist/render/camera-2d.js +92 -0
  374. package/dist/render/camera.d.ts +302 -0
  375. package/dist/render/camera.js +671 -0
  376. package/dist/render/color.d.ts +16 -0
  377. package/dist/render/color.js +41 -0
  378. package/dist/render/compressed.d.ts +104 -0
  379. package/dist/render/compressed.js +248 -0
  380. package/dist/render/diagnostics.d.ts +15 -0
  381. package/dist/render/diagnostics.js +43 -0
  382. package/dist/render/dynamic-resolution.d.ts +70 -0
  383. package/dist/render/dynamic-resolution.js +126 -0
  384. package/dist/render/fake-gpu.d.ts +204 -0
  385. package/dist/render/fake-gpu.js +426 -0
  386. package/dist/render/flipbook.d.ts +56 -0
  387. package/dist/render/flipbook.js +87 -0
  388. package/dist/render/frame.d.ts +278 -0
  389. package/dist/render/frame.js +174 -0
  390. package/dist/render/fullscreen.d.ts +54 -0
  391. package/dist/render/fullscreen.js +91 -0
  392. package/dist/render/gpu-timer.d.ts +73 -0
  393. package/dist/render/gpu-timer.js +186 -0
  394. package/dist/render/index.d.ts +53 -0
  395. package/dist/render/index.js +29 -0
  396. package/dist/render/instance-list.d.ts +185 -0
  397. package/dist/render/instance-list.js +547 -0
  398. package/dist/render/lights.d.ts +181 -0
  399. package/dist/render/lights.js +233 -0
  400. package/dist/render/lod.d.ts +54 -0
  401. package/dist/render/lod.js +52 -0
  402. package/dist/render/material.d.ts +165 -0
  403. package/dist/render/material.js +292 -0
  404. package/dist/render/mesh.d.ts +284 -0
  405. package/dist/render/mesh.js +472 -0
  406. package/dist/render/mipmap.d.ts +16 -0
  407. package/dist/render/mipmap.js +121 -0
  408. package/dist/render/parallax.d.ts +50 -0
  409. package/dist/render/parallax.js +47 -0
  410. package/dist/render/pbr-wgsl.d.ts +31 -0
  411. package/dist/render/pbr-wgsl.js +119 -0
  412. package/dist/render/ping-pong.d.ts +42 -0
  413. package/dist/render/ping-pong.js +74 -0
  414. package/dist/render/renderer.d.ts +741 -0
  415. package/dist/render/renderer.js +2486 -0
  416. package/dist/render/scene-pipelines.d.ts +44 -0
  417. package/dist/render/scene-pipelines.js +92 -0
  418. package/dist/render/scene-target.d.ts +170 -0
  419. package/dist/render/scene-target.js +450 -0
  420. package/dist/render/shadow.d.ts +74 -0
  421. package/dist/render/shadow.js +208 -0
  422. package/dist/render/skin.d.ts +112 -0
  423. package/dist/render/skin.js +240 -0
  424. package/dist/render/snapshot.d.ts +36 -0
  425. package/dist/render/snapshot.js +34 -0
  426. package/dist/render/standard.d.ts +162 -0
  427. package/dist/render/standard.js +263 -0
  428. package/dist/render/texture.d.ts +142 -0
  429. package/dist/render/texture.js +228 -0
  430. package/dist/render/tile-layer.d.ts +110 -0
  431. package/dist/render/tile-layer.js +304 -0
  432. package/dist/render/transform.d.ts +115 -0
  433. package/dist/render/transform.js +318 -0
  434. package/dist/render/wgsl.d.ts +131 -0
  435. package/dist/render/wgsl.js +528 -0
  436. package/dist/save/codec.d.ts +49 -0
  437. package/dist/save/codec.js +169 -0
  438. package/dist/save/index.d.ts +8 -0
  439. package/dist/save/index.js +8 -0
  440. package/dist/save/journal.d.ts +24 -0
  441. package/dist/save/journal.js +102 -0
  442. package/dist/save/migrate.d.ts +15 -0
  443. package/dist/save/migrate.js +37 -0
  444. package/dist/save/schema.d.ts +72 -0
  445. package/dist/save/schema.js +154 -0
  446. package/dist/save/settings.d.ts +28 -0
  447. package/dist/save/settings.js +59 -0
  448. package/dist/save/slots.d.ts +56 -0
  449. package/dist/save/slots.js +200 -0
  450. package/dist/save/storage.d.ts +35 -0
  451. package/dist/save/storage.js +62 -0
  452. package/dist/save/store.d.ts +114 -0
  453. package/dist/save/store.js +269 -0
  454. package/dist/testing/fake-host.d.ts +103 -0
  455. package/dist/testing/fake-host.js +163 -0
  456. package/dist/testing/fixed-clock.d.ts +25 -0
  457. package/dist/testing/fixed-clock.js +30 -0
  458. package/dist/testing/index.d.ts +4 -0
  459. package/dist/testing/index.js +5 -0
  460. package/dist/testing/test-game.d.ts +76 -0
  461. package/dist/testing/test-game.js +92 -0
  462. package/dist/tween/ease.d.ts +77 -0
  463. package/dist/tween/ease.js +192 -0
  464. package/dist/tween/index.d.ts +8 -0
  465. package/dist/tween/index.js +7 -0
  466. package/dist/tween/timeline.d.ts +78 -0
  467. package/dist/tween/timeline.js +269 -0
  468. package/dist/tween/tween.d.ts +168 -0
  469. package/dist/tween/tween.js +320 -0
  470. package/dist/ui/anchor.d.ts +206 -0
  471. package/dist/ui/anchor.js +307 -0
  472. package/dist/ui/caption.d.ts +122 -0
  473. package/dist/ui/caption.js +286 -0
  474. package/dist/ui/cursor.d.ts +105 -0
  475. package/dist/ui/cursor.js +261 -0
  476. package/dist/ui/dom.d.ts +30 -0
  477. package/dist/ui/dom.js +92 -0
  478. package/dist/ui/floats.d.ts +86 -0
  479. package/dist/ui/floats.js +260 -0
  480. package/dist/ui/hud.d.ts +198 -0
  481. package/dist/ui/hud.js +431 -0
  482. package/dist/ui/index.d.ts +37 -0
  483. package/dist/ui/index.js +34 -0
  484. package/dist/ui/inventory-card.d.ts +52 -0
  485. package/dist/ui/inventory-card.js +173 -0
  486. package/dist/ui/inventory-context.d.ts +29 -0
  487. package/dist/ui/inventory-context.js +125 -0
  488. package/dist/ui/inventory-drag.d.ts +63 -0
  489. package/dist/ui/inventory-drag.js +394 -0
  490. package/dist/ui/inventory-grid.d.ts +66 -0
  491. package/dist/ui/inventory-grid.js +625 -0
  492. package/dist/ui/inventory-radial.d.ts +39 -0
  493. package/dist/ui/inventory-radial.js +168 -0
  494. package/dist/ui/inventory.d.ts +128 -0
  495. package/dist/ui/inventory.js +154 -0
  496. package/dist/ui/layout.d.ts +356 -0
  497. package/dist/ui/layout.js +856 -0
  498. package/dist/ui/link.d.ts +64 -0
  499. package/dist/ui/link.js +155 -0
  500. package/dist/ui/loading.d.ts +40 -0
  501. package/dist/ui/loading.js +157 -0
  502. package/dist/ui/loupe.d.ts +74 -0
  503. package/dist/ui/loupe.js +164 -0
  504. package/dist/ui/menu-nav.d.ts +33 -0
  505. package/dist/ui/menu-nav.js +28 -0
  506. package/dist/ui/minimap-canvas.d.ts +145 -0
  507. package/dist/ui/minimap-canvas.js +460 -0
  508. package/dist/ui/notify.d.ts +40 -0
  509. package/dist/ui/notify.js +141 -0
  510. package/dist/ui/orb.d.ts +53 -0
  511. package/dist/ui/orb.js +304 -0
  512. package/dist/ui/overlay.d.ts +213 -0
  513. package/dist/ui/overlay.js +394 -0
  514. package/dist/ui/padnav.d.ts +53 -0
  515. package/dist/ui/padnav.js +146 -0
  516. package/dist/ui/perf.d.ts +130 -0
  517. package/dist/ui/perf.js +329 -0
  518. package/dist/ui/rotate-card.d.ts +54 -0
  519. package/dist/ui/rotate-card.js +97 -0
  520. package/dist/ui/stack.d.ts +128 -0
  521. package/dist/ui/stack.js +312 -0
  522. package/dist/ui/styles.d.ts +13 -0
  523. package/dist/ui/styles.js +200 -0
  524. package/dist/ui/text-scale.d.ts +67 -0
  525. package/dist/ui/text-scale.js +102 -0
  526. package/dist/ui/theme.d.ts +70 -0
  527. package/dist/ui/theme.js +98 -0
  528. package/dist/ui/toast.d.ts +23 -0
  529. package/dist/ui/toast.js +46 -0
  530. package/dist/ui/tooltip.d.ts +89 -0
  531. package/dist/ui/tooltip.js +333 -0
  532. package/dist/ui/widgets-2.d.ts +134 -0
  533. package/dist/ui/widgets-2.js +399 -0
  534. package/dist/ui/widgets-3.d.ts +91 -0
  535. package/dist/ui/widgets-3.js +212 -0
  536. package/dist/ui/widgets.d.ts +34 -0
  537. package/dist/ui/widgets.js +50 -0
  538. package/dist/ui/world-panel/index.d.ts +5 -0
  539. package/dist/ui/world-panel/index.js +5 -0
  540. package/dist/ui/world-panel/layout.d.ts +38 -0
  541. package/dist/ui/world-panel/layout.js +68 -0
  542. package/dist/ui/world-panel/paint.d.ts +96 -0
  543. package/dist/ui/world-panel/paint.js +136 -0
  544. package/dist/ui/world-panel/panel.d.ts +235 -0
  545. package/dist/ui/world-panel/panel.js +762 -0
  546. package/dist/ui/world-panel/wgsl.d.ts +10 -0
  547. package/dist/ui/world-panel/wgsl.js +52 -0
  548. package/dist/ui/world-panel/widgets.d.ts +207 -0
  549. package/dist/ui/world-panel/widgets.js +569 -0
  550. package/dist/voxel/chunks.d.ts +77 -0
  551. package/dist/voxel/chunks.js +149 -0
  552. package/dist/voxel/grid.d.ts +84 -0
  553. package/dist/voxel/grid.js +223 -0
  554. package/dist/voxel/index.d.ts +22 -0
  555. package/dist/voxel/index.js +7 -0
  556. package/dist/voxel/mesher-worker.d.ts +14 -0
  557. package/dist/voxel/mesher-worker.js +47 -0
  558. package/dist/voxel/mesher.d.ts +73 -0
  559. package/dist/voxel/mesher.js +306 -0
  560. package/dist/voxel/ray.d.ts +32 -0
  561. package/dist/voxel/ray.js +73 -0
  562. package/dist/voxel/wgsl.d.ts +16 -0
  563. package/dist/voxel/wgsl.js +28 -0
  564. package/dist/voxel/worker-client.d.ts +79 -0
  565. package/dist/voxel/worker-client.js +122 -0
  566. package/package.json +177 -3
@@ -0,0 +1,410 @@
1
+ import { WGSL_DEPTH_VIEW } from './ao.js';
2
+ import { WGSL_POST_UNIFORMS } from './settings.js';
3
+ /** Bright-pass + 2× downsample into bloom mip 0. Karis-weighted to tame fireflies. */
4
+ export const PREFILTER_WGSL = /* wgsl */ `
5
+ ${WGSL_POST_UNIFORMS}
6
+ @group(0) @binding(1) var samp: sampler;
7
+ @group(0) @binding(2) var colorTex: texture_2d<f32>;
8
+ @group(0) @binding(3) var emissiveTex: texture_2d<f32>;
9
+
10
+ fn soft_threshold(c: vec3f) -> vec3f {
11
+ let br = max(c.r, max(c.g, c.b));
12
+ let knee = max(P.bloom.z, 1e-4);
13
+ var rq = clamp(br - P.bloom.y + knee, 0.0, 2.0 * knee);
14
+ rq = rq * rq / (4.0 * knee);
15
+ let w = max(rq, br - P.bloom.y) / max(br, 1e-4);
16
+ return c * w;
17
+ }
18
+
19
+ fn tap(uv: vec2f) -> vec3f {
20
+ let c = textureSampleLevel(colorTex, samp, uv, 0.0).rgb;
21
+ let e = textureSampleLevel(emissiveTex, samp, uv, 0.0).rgb;
22
+ return min(e * P.bloom.w + soft_threshold(c), vec3f(64.0));
23
+ }
24
+
25
+ @fragment fn fs_main(in: FullscreenIn) -> @location(0) vec4f {
26
+ let t = 1.0 / vec2f(textureDimensions(colorTex));
27
+ let a = tap(in.uv + t * vec2f(-1.0, -1.0));
28
+ let b = tap(in.uv + t * vec2f(1.0, -1.0));
29
+ let c = tap(in.uv + t * vec2f(-1.0, 1.0));
30
+ let d = tap(in.uv + t * vec2f(1.0, 1.0));
31
+ let wa = 1.0 / (1.0 + engine_luma(a));
32
+ let wb = 1.0 / (1.0 + engine_luma(b));
33
+ let wc = 1.0 / (1.0 + engine_luma(c));
34
+ let wd = 1.0 / (1.0 + engine_luma(d));
35
+ let sum = (a * wa + b * wb + c * wc + d * wd) / max(wa + wb + wc + wd, 1e-4);
36
+ return vec4f(sum, 1.0);
37
+ }
38
+ `;
39
+ /** 13-tap downsample (Jimenez 2014). */
40
+ export const DOWN_WGSL = /* wgsl */ `
41
+ @group(0) @binding(1) var samp: sampler;
42
+ @group(0) @binding(2) var src: texture_2d<f32>;
43
+
44
+ fn s(uv: vec2f) -> vec3f { return textureSampleLevel(src, samp, uv, 0.0).rgb; }
45
+
46
+ @fragment fn fs_main(in: FullscreenIn) -> @location(0) vec4f {
47
+ let t = 1.0 / vec2f(textureDimensions(src));
48
+ let uv = in.uv;
49
+ let a = s(uv + t * vec2f(-2.0, -2.0));
50
+ let b = s(uv + t * vec2f(0.0, -2.0));
51
+ let c = s(uv + t * vec2f(2.0, -2.0));
52
+ let d = s(uv + t * vec2f(-1.0, -1.0));
53
+ let e = s(uv + t * vec2f(1.0, -1.0));
54
+ let f = s(uv + t * vec2f(-2.0, 0.0));
55
+ let g = s(uv);
56
+ let h = s(uv + t * vec2f(2.0, 0.0));
57
+ let i = s(uv + t * vec2f(-1.0, 1.0));
58
+ let j = s(uv + t * vec2f(1.0, 1.0));
59
+ let k = s(uv + t * vec2f(-2.0, 2.0));
60
+ let l = s(uv + t * vec2f(0.0, 2.0));
61
+ let m = s(uv + t * vec2f(2.0, 2.0));
62
+ var o = (d + e + i + j) * 0.125;
63
+ o += (a + b + g + f) * 0.03125;
64
+ o += (b + c + h + g) * 0.03125;
65
+ o += (f + g + l + k) * 0.03125;
66
+ o += (g + h + m + l) * 0.03125;
67
+ return vec4f(o, 1.0);
68
+ }
69
+ `;
70
+ /** 9-tap tent upsample; additively blended (× blend constant) onto the finer level. */
71
+ export const UP_WGSL = /* wgsl */ `
72
+ @group(0) @binding(1) var samp: sampler;
73
+ @group(0) @binding(2) var src: texture_2d<f32>;
74
+
75
+ fn s(uv: vec2f) -> vec3f { return textureSampleLevel(src, samp, uv, 0.0).rgb; }
76
+
77
+ @fragment fn fs_main(in: FullscreenIn) -> @location(0) vec4f {
78
+ let t = 1.0 / vec2f(textureDimensions(src));
79
+ let uv = in.uv;
80
+ var o = s(uv) * 4.0;
81
+ o += (s(uv + vec2f(-t.x, 0.0)) + s(uv + vec2f(t.x, 0.0)) + s(uv + vec2f(0.0, -t.y)) + s(uv + vec2f(0.0, t.y))) * 2.0;
82
+ o += s(uv + vec2f(-t.x, -t.y)) + s(uv + vec2f(t.x, -t.y)) + s(uv + vec2f(-t.x, t.y)) + s(uv + vec2f(t.x, t.y));
83
+ return vec4f(o / 16.0, 1.0);
84
+ }
85
+ `;
86
+ /** Screen-space god rays: radial march towards the light over bloom + a light disc. */
87
+ export const RAYS_WGSL = /* wgsl */ `
88
+ ${WGSL_POST_UNIFORMS}
89
+ @group(0) @binding(1) var samp: sampler;
90
+ @group(0) @binding(2) var bloomTex: texture_2d<f32>;
91
+
92
+ // Reference sample count: decay and weight are tuned per step at 64 samples. With fewer
93
+ // samples (P.quality.x) each step covers more distance, so decay and weight are rescaled.
94
+ const REF_SAMPLES: f32 = 64.0;
95
+
96
+ fn source(uv: vec2f) -> vec3f {
97
+ let aspect = P.frame.y;
98
+ var s = textureSampleLevel(bloomTex, samp, uv, 0.0).rgb * P.raysColor.w;
99
+ let d = length((uv - P.rays.xy) * vec2f(aspect, 1.0));
100
+ let r = max(P.rays2.z, 1e-4);
101
+ let disc = 1.0 - smoothstep(0.0, r, d);
102
+ s += vec3f(disc * disc * P.rays2.w);
103
+ // Outside the screen there is no bloom; keep the disc (light can sit off-screen).
104
+ return s * P.raysColor.rgb;
105
+ }
106
+
107
+ @fragment fn fs_main(in: FullscreenIn) -> @location(0) vec4f {
108
+ var coord = in.uv;
109
+ // Small per-pixel jitter hides the step pattern.
110
+ let jitter = engine_hash21(in.pos.xy + fract(P.frame.x) * 61.0);
111
+ let n = i32(clamp(P.quality.x, 8.0, 64.0));
112
+ let k = REF_SAMPLES / f32(n);
113
+ let delta = (coord - P.rays.xy) * (P.rays2.x / f32(n));
114
+ coord -= delta * jitter;
115
+ let decay = pow(P.rays.w, k);
116
+ let weight = P.rays2.y * k;
117
+ var illum = 1.0;
118
+ var acc = vec3f(0.0);
119
+ for (var i = 0; i < n; i++) {
120
+ coord -= delta;
121
+ acc += source(coord) * illum * weight;
122
+ illum *= decay;
123
+ }
124
+ return vec4f(acc * P.rays.z, 1.0);
125
+ }
126
+ `;
127
+ /** Final pass: combine, SSR, mist, bloom (× dirt), rays, white balance, exposure, tonemap, grade, vignette, dither, sRGB. */
128
+ export const COMPOSITE_WGSL = /* wgsl */ `
129
+ ${WGSL_POST_UNIFORMS}
130
+ @group(0) @binding(1) var samp: sampler;
131
+ @group(0) @binding(2) var colorTex: texture_2d<f32>;
132
+ @group(0) @binding(3) var emissiveTex: texture_2d<f32>;
133
+ @group(0) @binding(4) var bloomTex: texture_2d<f32>;
134
+ @group(0) @binding(5) var raysTex: texture_2d<f32>;
135
+ // Depth passes (ws/post-depth); bound to a placeholder and ignored while P.ao.x / P.dof.x are 0.
136
+ @group(0) @binding(6) var aoTex: texture_2d<f32>;
137
+ @group(0) @binding(7) var dofTex: texture_2d<f32>;
138
+ // Finishing (ws/post-2): the outline mask (placeholder while P.outline.x is 0) and the 3D LUT
139
+ // (a 1×1×1 placeholder while P.lut.x is 0).
140
+ @group(0) @binding(8) var outlineTex: texture_2d<f32>;
141
+ @group(0) @binding(9) var lutTex: texture_3d<f32>;
142
+ // Post 3 (ws/post-3): the half-res SSR term (placeholder while P.ssr.x is 0) and the lens-dirt
143
+ // map (placeholder while P.dirt.x is 0). TAA and motion blur replace colorTex with their result.
144
+ @group(0) @binding(10) var ssrTex: texture_2d<f32>;
145
+ @group(0) @binding(11) var dirtTex: texture_2d<f32>;
146
+
147
+ /** The scene term: colour × AO, blended toward the DoF result by CoC, plus emissive. */
148
+ fn scene_at(uv: vec2f) -> vec3f {
149
+ var scene = textureSampleLevel(colorTex, samp, uv, 0.0).rgb;
150
+ if (P.ao.x > 0.5) {
151
+ scene *= textureSampleLevel(aoTex, samp, uv, 0.0).r;
152
+ }
153
+ if (P.dof.x > 0.5) {
154
+ // Alpha holds the signed CoC in uv of the width; blend in the blur from half a scene px up.
155
+ let blur = textureSampleLevel(dofTex, samp, uv, 0.0);
156
+ let px = abs(blur.a) / P.frame.z;
157
+ scene = mix(scene, blur.rgb, smoothstep(0.5, 2.0, px));
158
+ }
159
+ if (P.ssr.x > 0.5) {
160
+ // Premultiplied by its fade and intensity in the SSR pass.
161
+ scene += textureSampleLevel(ssrTex, samp, uv, 0.0).rgb;
162
+ }
163
+ return scene + textureSampleLevel(emissiveTex, samp, uv, 0.0).rgb;
164
+ }
165
+
166
+ fn aces(x: vec3f) -> vec3f {
167
+ let a = 2.51;
168
+ let b = 0.03;
169
+ let c = 2.43;
170
+ let d = 0.59;
171
+ let e = 0.14;
172
+ return clamp((x * (a * x + b)) / (x * (c * x + d) + e), vec3f(0.0), vec3f(1.0));
173
+ }
174
+
175
+ fn to_srgb(c: vec3f) -> vec3f {
176
+ let lo = c * 12.92;
177
+ let hi = 1.055 * pow(c, vec3f(1.0 / 2.4)) - 0.055;
178
+ return select(hi, lo, c <= vec3f(0.0031308));
179
+ }
180
+
181
+ @fragment fn fs_main(in: FullscreenIn) -> @location(0) vec4f {
182
+ let uv = in.uv;
183
+ let aspect = P.frame.y;
184
+ let time = P.frame.x;
185
+ let texel = vec2f(P.frame.z, P.frame.w);
186
+ var c: vec3f;
187
+ if (P.aberration.x > 0.0) {
188
+ // Red and blue sampled either side of green along the radius, in scene px.
189
+ let d = (uv - 0.5) * vec2f(aspect, 1.0);
190
+ let r = length(d) / length(vec2f(aspect, 1.0) * 0.5);
191
+ let dir = d / max(length(d), 1e-4);
192
+ let amount = P.aberration.x * select(1.0, r * r, P.aberration.y > 0.5);
193
+ let o = dir * amount * texel;
194
+ c = vec3f(scene_at(uv + o).r, scene_at(uv).g, scene_at(uv - o).b);
195
+ } else {
196
+ c = scene_at(uv);
197
+ }
198
+ if (P.sharpen.x > 0.0) {
199
+ // Unsharp mask: the pixel minus its 4-neighbour mean, clamped so dark rims never go negative.
200
+ let blur = (scene_at(uv + vec2f(texel.x, 0.0)) + scene_at(uv - vec2f(texel.x, 0.0))
201
+ + scene_at(uv + vec2f(0.0, texel.y)) + scene_at(uv - vec2f(0.0, texel.y))) * 0.25;
202
+ c = max(c + (c - blur) * P.sharpen.x, vec3f(0.0));
203
+ }
204
+ if (P.outline.x > 0.5) {
205
+ let edge = textureSampleLevel(outlineTex, samp, uv, 0.0).r;
206
+ c = mix(c, P.outlineColor.rgb, clamp(edge * P.outlineColor.w, 0.0, 1.0));
207
+ }
208
+
209
+ var bloom = vec3f(0.0);
210
+ if (P.enables.x > 0.5) {
211
+ bloom = textureSampleLevel(bloomTex, samp, uv, 0.0).rgb;
212
+ if (P.dirt.x > 0.5) {
213
+ bloom *= 1.0 + textureSampleLevel(dirtTex, samp, uv, 0.0).rgb * P.dirt.y;
214
+ }
215
+ }
216
+
217
+ // Floor mist: height mask × two drifting fbm layers, lit by nearby glow.
218
+ if (P.enables.y > 0.5) {
219
+ let span = max(P.fog.y - P.fog.x, 1e-3);
220
+ let h = pow(clamp((uv.y - P.fog.x) / span, 0.0, 1.0), P.fog2.y);
221
+ if (h > 0.0) {
222
+ let p = vec2f(uv.x * aspect, uv.y * 2.2) * P.fog.z;
223
+ let t = time * P.fog.w;
224
+ // Fewer octaves (P.quality.y < 5) lose fine wisps; add back the dropped octaves' mean
225
+ // (0.5 each) so the mist keeps its density.
226
+ let o1 = i32(clamp(P.quality.y, 2.0, 5.0));
227
+ let o2 = max(o1 - 1, 1);
228
+ let m1 = 0.5 * (pow(0.5, f32(o1)) - pow(0.5, 5.0));
229
+ let m2 = 0.5 * (pow(0.5, f32(o2)) - pow(0.5, 4.0));
230
+ let n1 = engine_fbm(p + vec2f(t * 6.0, t * 0.7), o1) + m1;
231
+ let n2 = engine_fbm(p * 1.9 + vec2f(-t * 9.0, t * 1.3) + vec2f(13.7, 4.1), o2) + m2;
232
+ let wisps = smoothstep(0.2, 0.85, n1 * 0.6 + n2 * 0.55);
233
+ let amount = clamp(h * P.fogColor.w * (0.3 + 1.1 * wisps), 0.0, 1.0);
234
+ let lit = P.fogColor.rgb * (0.6 + 0.8 * wisps) + bloom * P.fog2.x;
235
+ c = mix(c, lit, amount);
236
+ }
237
+ }
238
+
239
+ c += bloom * P.bloom.x;
240
+ if (P.enables.z > 0.5) {
241
+ c += textureSampleLevel(raysTex, samp, uv, 0.0).rgb;
242
+ }
243
+
244
+ if (P.wb0.w > 0.5) {
245
+ // White balance: a 3×3 matrix on the linear colour (ws/post-3).
246
+ c = max(vec3f(dot(P.wb0.rgb, c), dot(P.wb1.rgb, c), dot(P.wb2.rgb, c)), vec3f(0.0));
247
+ }
248
+ c *= P.enables2.w;
249
+ let tm = P.enables2.y;
250
+ if (tm > 0.5 && tm < 1.5) {
251
+ c = aces(c);
252
+ } else if (tm > 1.5) {
253
+ c = c / (1.0 + c);
254
+ }
255
+ c = clamp(c, vec3f(0.0), vec3f(1.0));
256
+
257
+ if (P.enables2.x > 0.5) {
258
+ c = c * P.gain.rgb + P.lift.rgb * (1.0 - c);
259
+ c = pow(max(c, vec3f(0.0)), 1.0 / max(P.gamma.rgb, vec3f(1e-3)));
260
+ let l = engine_luma(c);
261
+ c = mix(vec3f(l), c, P.grade.x);
262
+ c = (c - 0.5) * P.grade.y + 0.5;
263
+ c = clamp(c, vec3f(0.0), vec3f(1.0));
264
+ }
265
+
266
+ if (P.lut.x > 0.5) {
267
+ // Texel centres: the LUT's first and last texels sit half a texel in from 0 and 1.
268
+ let looked = textureSampleLevel(lutTex, samp, c * P.lut.y + P.lut.w, 0.0).rgb;
269
+ c = clamp(mix(c, looked, P.lut.z), vec3f(0.0), vec3f(1.0));
270
+ }
271
+ if (P.grain.x > 0.0) {
272
+ let cell = floor(in.pos.xy / P.grain.y);
273
+ let n = engine_hash21(cell + vec2f(P.grain.w * 61.0, P.grain.w * 29.0)) - 0.5;
274
+ let response = 1.0 - P.grain.z * engine_luma(c);
275
+ c = clamp(c + n * P.grain.x * response, vec3f(0.0), vec3f(1.0));
276
+ }
277
+
278
+ if (P.enables.w > 0.5) {
279
+ let d = length((uv - 0.5) * vec2f(aspect, 1.0)) / length(vec2f(aspect, 1.0) * 0.5);
280
+ let v = smoothstep(P.vignette.y, P.vignette.y + P.vignette.z, d) * P.vignette.x;
281
+ c = mix(c, P.vignetteColor.rgb, v);
282
+ }
283
+
284
+ var outc = to_srgb(c);
285
+ if (P.enables2.z > 0.5) {
286
+ outc += (engine_hash21(in.pos.xy) - 0.5) / 255.0;
287
+ }
288
+ return vec4f(outc, 1.0);
289
+ }
290
+ `;
291
+ /**
292
+ * FXAA 3.11, console preset (ws/post-2): one full-res pass over the composite's display-encoded
293
+ * output. Luma from the four texel-corner taps and the centre; the blend direction from their
294
+ * gradient, two taps half a px out and two more up to two px out along it, the longer pair
295
+ * dropped when its luma leaves the local range. `P.fxaa.y` (subpixel) scales the blend;
296
+ * `P.fxaa.z` (edgeThreshold) is the relative contrast below which the pixel is left alone.
297
+ */
298
+ export const FXAA_WGSL = /* wgsl */ `
299
+ ${WGSL_POST_UNIFORMS}
300
+ @group(0) @binding(1) var samp: sampler;
301
+ @group(0) @binding(2) var src: texture_2d<f32>;
302
+
303
+ const EDGE_SHARPNESS: f32 = 8.0;
304
+ const EDGE_THRESHOLD_MIN: f32 = 0.05;
305
+
306
+ fn tap(uv: vec2f) -> vec3f { return textureSampleLevel(src, samp, uv, 0.0).rgb; }
307
+
308
+ @fragment fn fs_main(in: FullscreenIn) -> @location(0) vec4f {
309
+ let t = 1.0 / vec2f(textureDimensions(src));
310
+ let uv = in.uv;
311
+ let rgbM = tap(uv);
312
+ let lumaM = engine_luma(rgbM);
313
+ let lumaNw = engine_luma(tap(uv + vec2f(-0.5, -0.5) * t)) + 1.0 / 384.0;
314
+ let lumaNe = engine_luma(tap(uv + vec2f(0.5, -0.5) * t));
315
+ let lumaSw = engine_luma(tap(uv + vec2f(-0.5, 0.5) * t));
316
+ let lumaSe = engine_luma(tap(uv + vec2f(0.5, 0.5) * t));
317
+ let lumaMax = max(max(lumaNw, lumaNe), max(lumaSw, lumaSe));
318
+ let lumaMin = min(min(lumaNw, lumaNe), min(lumaSw, lumaSe));
319
+ let range = max(lumaMax, lumaM) - min(lumaMin, lumaM);
320
+ if (range < max(EDGE_THRESHOLD_MIN, lumaMax * P.fxaa.z)) {
321
+ return vec4f(rgbM, 1.0);
322
+ }
323
+ var dir = vec2f(-((lumaNw + lumaNe) - (lumaSw + lumaSe)), (lumaNw + lumaSw) - (lumaNe + lumaSe));
324
+ let dir1 = dir / max(length(dir), 1e-5);
325
+ let rgbN1 = tap(uv - dir1 * 0.5 * t);
326
+ let rgbP1 = tap(uv + dir1 * 0.5 * t);
327
+ let dirAbsMinTimesC = min(abs(dir1.x), abs(dir1.y)) * EDGE_SHARPNESS;
328
+ let dir2 = clamp(dir1 / max(dirAbsMinTimesC, 1e-5), vec2f(-2.0), vec2f(2.0));
329
+ let rgbN2 = tap(uv - dir2 * 2.0 * t);
330
+ let rgbP2 = tap(uv + dir2 * 2.0 * t);
331
+ let rgbA = (rgbN1 + rgbP1) * 0.5;
332
+ let rgbB = (rgbN2 + rgbP2) * 0.25 + rgbA * 0.5;
333
+ let lumaB = engine_luma(rgbB);
334
+ let outside = lumaB < lumaMin || lumaB > lumaMax;
335
+ let aa = select(rgbB, rgbA, outside);
336
+ return vec4f(mix(rgbM, aa, clamp(P.fxaa.y, 0.0, 1.0)), 1.0);
337
+ }
338
+ `;
339
+ /**
340
+ * Outline mask (ws/post-2): a Roberts cross over the linear depth and the normals from depth,
341
+ * at the scene size, written as an edge strength 0..1 (r8unorm) the composite mixes toward
342
+ * `outline.color` before fog. The cross is sampled `P.outline.y` scene px from the pixel.
343
+ * Sky (depth 1) counts as very far, so silhouettes against it are edges.
344
+ */
345
+ export const OUTLINE_WGSL = /* wgsl */ `
346
+ ${WGSL_POST_UNIFORMS}
347
+ @group(0) @binding(1) var nearest: sampler;
348
+ @group(0) @binding(2) var linSamp: sampler;
349
+ @group(0) @binding(3) var depthTex: texture_depth_2d;
350
+ ${WGSL_DEPTH_VIEW}
351
+
352
+ const SKY_Z: f32 = 1.0e6;
353
+
354
+ /** View position of the texel under \`uv\` (w = raw depth). */
355
+ fn fetch(uv: vec2f, dim: vec2f) -> vec4f {
356
+ let tuv = texel_uv(uv, dim);
357
+ let d = textureSampleLevel(depthTex, nearest, tuv, 0u);
358
+ return vec4f(view_pos(tuv, d), d);
359
+ }
360
+
361
+ fn zof(s: vec4f) -> f32 {
362
+ return select(-s.z, SKY_Z, s.w >= 1.0);
363
+ }
364
+
365
+ /** Normal at the texel under \`uv\` from its +x and +y neighbours (a zero vector in the sky). */
366
+ fn normal_at(uv: vec2f, dim: vec2f, t: vec2f) -> vec3f {
367
+ let p = fetch(uv, dim);
368
+ let px = fetch(uv + vec2f(t.x, 0.0), dim);
369
+ let py = fetch(uv + vec2f(0.0, t.y), dim);
370
+ if (p.w >= 1.0 || px.w >= 1.0 || py.w >= 1.0) {
371
+ return vec3f(0.0);
372
+ }
373
+ return normalize(cross(px.xyz - p.xyz, py.xyz - p.xyz));
374
+ }
375
+
376
+ @fragment fn fs_main(in: FullscreenIn) -> @location(0) vec4f {
377
+ let dim = vec2f(textureDimensions(depthTex));
378
+ let uv = texel_uv(in.uv, dim);
379
+ let t = vec2f(max(P.outline.y, 0.5)) / dim;
380
+ let a = fetch(uv - t, dim);
381
+ let b = fetch(uv + t, dim);
382
+ let c = fetch(uv + vec2f(t.x, -t.y), dim);
383
+ let d = fetch(uv + vec2f(-t.x, t.y), dim);
384
+ let za = zof(a);
385
+ let zb = zof(b);
386
+ let zc = zof(c);
387
+ let zd = zof(d);
388
+ let zmin = min(min(za, zb), min(zc, zd));
389
+ if (zmin >= SKY_Z) {
390
+ return vec4f(0.0, 0.0, 0.0, 1.0);
391
+ }
392
+ // Depth: the cross's two differences relative to the nearest tap (so far edges count less).
393
+ let dz = length(vec2f(za - zb, zc - zd)) / max(zmin, 1e-3);
394
+ let th = max(P.outline.z, 1e-4);
395
+ let edgeDepth = smoothstep(th, th * 2.0, dz);
396
+ // Normals: the cross over the four taps' normals, in 1 - cos.
397
+ let one = 1.0 / dim;
398
+ let na = normal_at(uv - t, dim, one);
399
+ let nb = normal_at(uv + t, dim, one);
400
+ let nc = normal_at(uv + vec2f(t.x, -t.y), dim, one);
401
+ let nd = normal_at(uv + vec2f(-t.x, t.y), dim, one);
402
+ var dn = 0.0;
403
+ if (dot(na, na) > 0.5 && dot(nb, nb) > 0.5) { dn = max(dn, 1.0 - dot(na, nb)); }
404
+ if (dot(nc, nc) > 0.5 && dot(nd, nd) > 0.5) { dn = max(dn, 1.0 - dot(nc, nd)); }
405
+ let tn = max(P.outline.w, 1e-4);
406
+ let edgeNormal = smoothstep(tn, tn * 1.5, dn);
407
+ let edge = max(edgeDepth, edgeNormal);
408
+ return vec4f(edge, edge, edge, 1.0);
409
+ }
410
+ `;
@@ -0,0 +1,16 @@
1
+ /** Length of one march step (view units): the longest ray split into the step budget. */
2
+ export declare function ssrStep(maxDistance: number, maxSteps: number): number;
3
+ /**
4
+ * Fade of a hit at `(u, v)` toward the screen border: 1 inside, falling to 0 at the edge over
5
+ * `fadeEdges` uv on each side (0 = a hard cut). Same shape as the shader's `edge_fade`.
6
+ */
7
+ export declare function ssrEdgeFade(u: number, v: number, fadeEdges: number): number;
8
+ /** Reflection fade for a surface roughness under `cutoff`: 1 when mirror-smooth, 0 at the cutoff. */
9
+ export declare function ssrRoughnessFade(roughness: number, cutoff: number): number;
10
+ /**
11
+ * The weight of a reflection off a surface of `metalness` seen at `cosView` (the cosine
12
+ * between the normal and the direction to the eye): Schlick's Fresnel with F0 0.04 for a
13
+ * dielectric, mixed toward 1 for a metal. The shader's `surface_weight` does the same.
14
+ */
15
+ export declare function ssrSurfaceWeight(metalness: number, cosView: number): number;
16
+ export declare const SSR_WGSL = "\n\nstruct PostUniforms {\n frame: vec4f, // time, aspect, 1/width, 1/height\n enables: vec4f, // bloom, fog, rays, vignette\n enables2: vec4f, // grade, tonemap (0 none, 1 aces, 2 reinhard), dither, exposure\n bloom: vec4f, // intensity, threshold, knee, emissiveWeight\n bloom2: vec4f, // scatter, -, -, -\n fogColor: vec4f, // rgb, density\n fog: vec4f, // top, bottom, noiseScale, speed\n fog2: vec4f, // lightScatter, falloff, -, -\n rays: vec4f, // light uv x, y, intensity, decay\n rays2: vec4f, // density, weight, source radius (uv-y units), source intensity\n raysColor: vec4f, // rgb, bloomSource\n vignette: vec4f, // intensity, radius, softness, -\n vignetteColor: vec4f,\n lift: vec4f,\n gamma: vec4f,\n gain: vec4f,\n grade: vec4f, // saturation, contrast, -, -\n quality: vec4f, // ray samples, fog octaves, ortho projection (1, ws/post-4), -\n ao: vec4f, // enabled (0 without depth), radius, intensity, bias\n ao2: vec4f, // power, samples, proj x scale, proj y scale\n dof: vec4f, // enabled (0 without depth), focus distance, focus range, max blur (uv of width)\n dof2: vec4f, // near blur, taps, near plane, far plane\n invProj: mat4x4f, // clip -> view\n fxaa: vec4f, // enabled, subpixel, edgeThreshold, -\n lut: vec4f, // enabled, (size-1)/size, intensity, 0.5/size\n grain: vec4f, // intensity, cell size (px), luminanceResponse, seed\n aberration: vec4f, // intensity (px), radialOnly, -, -\n sharpen: vec4f, // amount, -, -, -\n outline: vec4f, // enabled (0 without depth), thickness (scene px), depthThreshold, normalThreshold\n outlineColor: vec4f, // rgb, intensity\n // Post 3 (ws/post-3).\n taa: vec4f, // enabled (0 without depth), feedback, sharpen, clampMode (0 aabb, 1 variance)\n taa2: vec4f, // view jitter x (uv), view jitter y (uv), reset (1 = copy the current frame), velocity reprojection (1, ws/post-5)\n ssr: vec4f, // enabled (0 without depth), maxSteps, thickness, maxDistance\n ssr2: vec4f, // roughnessCutoff, intensity, fadeEdges (uv), rough-metal target (1)\n exposure: vec4f, // auto (1), histogramLow, histogramHigh, keyValue\n wb0: vec4f, // white-balance matrix row 0 (rgb weights), enabled\n wb1: vec4f, // row 1\n wb2: vec4f, // row 2\n motionBlur: vec4f, // enabled (0 without depth), intensity, samples, velocity target (1 = object)\n dirt: vec4f, // enabled, intensity, -, -\n reproj: mat4x4f, // current clip -> previous frame's clip (identity on a reset)\n // Post 5 (ws/post-5).\n taa3: vec4f, // depthReject (fraction), velocityReject (px), history filter (1 = Catmull-Rom, ws/post-6), -\n};\n@group(0) @binding(0) var<uniform> P: PostUniforms;\n\n@group(0) @binding(1) var nearest: sampler;\n@group(0) @binding(2) var linSamp: sampler;\n@group(0) @binding(3) var depthTex: texture_depth_2d;\n@group(0) @binding(4) var colorTex: texture_2d<f32>;\n@group(0) @binding(5) var roughMetalTex: texture_2d<f32>;\n\nfn view_pos(uv: vec2f, depth: f32) -> vec3f {\n let ndc = vec3f(uv.x * 2.0 - 1.0, 1.0 - uv.y * 2.0, depth);\n let p = P.invProj * vec4f(ndc, 1.0);\n return p.xyz / p.w;\n}\nfn linear_depth(uv: vec2f, depth: f32) -> f32 {\n return -view_pos(uv, depth).z;\n}\n/** The centre of the depth texel under `uv`: positions must be rebuilt where the depth was read. */\nfn texel_uv(uv: vec2f, dim: vec2f) -> vec2f {\n return (floor(clamp(uv, vec2f(0.0), vec2f(0.9999)) * dim) + 0.5) / dim;\n}\n\n\nfn surface_weight(metal: f32, cosView: f32) -> f32 {\n let f = 0.04 + 0.96 * pow(1.0 - clamp(cosView, 0.0, 1.0), 5.0);\n return mix(f, 1.0, clamp(metal, 0.0, 1.0));\n}\n\nfn fetch(uv: vec2f, dim: vec2f) -> vec4f {\n let tuv = texel_uv(uv, dim);\n let d = textureSampleLevel(depthTex, nearest, tuv, 0u);\n return vec4f(view_pos(tuv, d), d);\n}\n\nfn edge_fade(uv: vec2f) -> f32 {\n let w = max(P.ssr2.z, 1e-6);\n let dist = min(min(uv.x, 1.0 - uv.x), min(uv.y, 1.0 - uv.y));\n return smoothstep(0.0, 1.0, clamp(dist / w, 0.0, 1.0));\n}\n\nfn project(p: vec3f) -> vec2f {\n let z = select(max(-p.z, 1e-4), 1.0, P.quality.z > 0.5);\n let ndc = vec2f(P.ao2.z * p.x, P.ao2.w * p.y) / z;\n return vec2f(ndc.x * 0.5 + 0.5, 0.5 - ndc.y * 0.5);\n}\n\n@fragment fn fs_main(in: FullscreenIn) -> @location(0) vec4f {\n let dim = vec2f(textureDimensions(depthTex));\n let c = fetch(in.uv, dim);\n if (c.w >= 1.0) {\n return vec4f(0.0);\n }\n let uv = texel_uv(in.uv, dim);\n // Per-surface roughness and metalness when the target is bound; a mirror at full weight else.\n var roughMetal = vec2f(0.0, 1.0);\n if (P.ssr2.w > 0.5) {\n roughMetal = textureSampleLevel(roughMetalTex, nearest, uv, 0.0).rg;\n }\n let rough = clamp(roughMetal.x / max(P.ssr2.x, 1e-4), 0.0, 1.0);\n let roughFade = 1.0 - rough * rough;\n if (roughFade <= 0.0) {\n return vec4f(0.0);\n }\n let p = c.xyz;\n let t = 1.0 / dim;\n let pl = fetch(uv - vec2f(t.x, 0.0), dim).xyz;\n let pr = fetch(uv + vec2f(t.x, 0.0), dim).xyz;\n let pu = fetch(uv - vec2f(0.0, t.y), dim).xyz;\n let pd = fetch(uv + vec2f(0.0, t.y), dim).xyz;\n let dx = select(p - pl, pr - p, length(pr - p) < length(p - pl));\n let dy = select(p - pu, pd - p, length(pd - p) < length(p - pu));\n var n = normalize(cross(dx, dy));\n // The eye ray: toward the point under a perspective camera, the view axis under an ortho one.\n let v = select(normalize(p), vec3f(0.0, 0.0, -1.0), P.quality.z > 0.5);\n if (dot(n, -v) < 0.0) {\n n = -n;\n }\n var weight = 1.0;\n if (P.ssr2.w > 0.5) {\n weight = surface_weight(roughMetal.y, dot(n, -v));\n }\n let r = reflect(v, n);\n let steps = i32(P.ssr.y);\n let stepLen = P.ssr.w / f32(steps);\n var hitUv = vec2f(-1.0);\n var hitFade = 0.0;\n for (var i = 1; i <= steps; i++) {\n let q = p + r * (stepLen * f32(i));\n if (q.z > -1e-3) {\n break;\n }\n let suv = project(q);\n if (any(suv < vec2f(0.0)) || any(suv > vec2f(1.0))) {\n break;\n }\n let sd = textureSampleLevel(depthTex, nearest, suv, 0u);\n if (sd >= 1.0) {\n continue;\n }\n let sz = view_pos(suv, sd).z;\n // The ray point is behind the surface (further from the eye) by less than the thickness.\n let behind = sz - q.z;\n if (behind > 0.0 && behind < P.ssr.z) {\n hitUv = suv;\n hitFade = 1.0 - f32(i) / f32(steps);\n break;\n }\n }\n if (hitFade <= 0.0) {\n return vec4f(0.0);\n }\n let fade = hitFade * edge_fade(hitUv) * roughFade * weight;\n let col = textureSampleLevel(colorTex, linSamp, hitUv, 0.0).rgb;\n return vec4f(col * fade * P.ssr2.y, fade);\n}\n";
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Screen-space reflections (ws/post-3): a half-res `DepthPass` that marches a view-space ray
3
+ * from each pixel's position along the reflection of the eye direction about the normal
4
+ * (derived from the depth as the AO pass does), `P.ssr.y` steps of `maxDistance / maxSteps`,
5
+ * and takes the scene colour where the ray drops behind the depth buffer by less than
6
+ * `P.ssr.z` (thickness). The term is faded toward the screen edges over `P.ssr2.z` uv, by the
7
+ * march distance, and by the roughness term, and written as premultiplied colour with the
8
+ * fade in alpha; the composite adds it to the scene before bloom.
9
+ *
10
+ * A ray point projects through the projection's x/y scale (`P.ao2.zw`), divided by the depth
11
+ * under a perspective camera and not under an orthographic one (`P.quality.z`, ws/post-4),
12
+ * where the eye ray is the view axis too.
13
+ *
14
+ * Per surface (ws/post-4): with the rough-metal target bound (`P.ssr2.w`, `@binding(5)`) the
15
+ * pixel's roughness fades the term (`ssrRoughnessFade` against `roughnessCutoff`) and its
16
+ * metalness weights it (`ssrSurfaceWeight`: Schlick Fresnel from F0 0.04 for a dielectric,
17
+ * 1 for a metal). Without it every surface is a mirror at full weight, as in ws/post-3.
18
+ */
19
+ import { WGSL_DEPTH_VIEW } from './ao.js';
20
+ import { WGSL_POST_UNIFORMS } from './settings.js';
21
+ /** Length of one march step (view units): the longest ray split into the step budget. */
22
+ export function ssrStep(maxDistance, maxSteps) {
23
+ return Math.max(1e-3, maxDistance) / Math.max(1, Math.round(maxSteps));
24
+ }
25
+ /**
26
+ * Fade of a hit at `(u, v)` toward the screen border: 1 inside, falling to 0 at the edge over
27
+ * `fadeEdges` uv on each side (0 = a hard cut). Same shape as the shader's `edge_fade`.
28
+ */
29
+ export function ssrEdgeFade(u, v, fadeEdges) {
30
+ if (u < 0 || u > 1 || v < 0 || v > 1)
31
+ return 0;
32
+ const w = Math.max(fadeEdges, 1e-6);
33
+ const dist = Math.min(u, 1 - u, v, 1 - v);
34
+ const x = Math.min(1, Math.max(0, dist / w));
35
+ return x * x * (3 - 2 * x);
36
+ }
37
+ /** Reflection fade for a surface roughness under `cutoff`: 1 when mirror-smooth, 0 at the cutoff. */
38
+ export function ssrRoughnessFade(roughness, cutoff) {
39
+ const c = Math.max(cutoff, 1e-4);
40
+ const x = Math.min(1, Math.max(0, roughness / c));
41
+ return 1 - x * x;
42
+ }
43
+ /**
44
+ * The weight of a reflection off a surface of `metalness` seen at `cosView` (the cosine
45
+ * between the normal and the direction to the eye): Schlick's Fresnel with F0 0.04 for a
46
+ * dielectric, mixed toward 1 for a metal. The shader's `surface_weight` does the same.
47
+ */
48
+ export function ssrSurfaceWeight(metalness, cosView) {
49
+ const m = Math.min(1, Math.max(0, metalness));
50
+ const c = Math.min(1, Math.max(0, cosView));
51
+ const f = 0.04 + 0.96 * (1 - c) ** 5;
52
+ return m + (1 - m) * f;
53
+ }
54
+ export const SSR_WGSL = /* wgsl */ `
55
+ ${WGSL_POST_UNIFORMS}
56
+ @group(0) @binding(1) var nearest: sampler;
57
+ @group(0) @binding(2) var linSamp: sampler;
58
+ @group(0) @binding(3) var depthTex: texture_depth_2d;
59
+ @group(0) @binding(4) var colorTex: texture_2d<f32>;
60
+ @group(0) @binding(5) var roughMetalTex: texture_2d<f32>;
61
+ ${WGSL_DEPTH_VIEW}
62
+
63
+ fn surface_weight(metal: f32, cosView: f32) -> f32 {
64
+ let f = 0.04 + 0.96 * pow(1.0 - clamp(cosView, 0.0, 1.0), 5.0);
65
+ return mix(f, 1.0, clamp(metal, 0.0, 1.0));
66
+ }
67
+
68
+ fn fetch(uv: vec2f, dim: vec2f) -> vec4f {
69
+ let tuv = texel_uv(uv, dim);
70
+ let d = textureSampleLevel(depthTex, nearest, tuv, 0u);
71
+ return vec4f(view_pos(tuv, d), d);
72
+ }
73
+
74
+ fn edge_fade(uv: vec2f) -> f32 {
75
+ let w = max(P.ssr2.z, 1e-6);
76
+ let dist = min(min(uv.x, 1.0 - uv.x), min(uv.y, 1.0 - uv.y));
77
+ return smoothstep(0.0, 1.0, clamp(dist / w, 0.0, 1.0));
78
+ }
79
+
80
+ fn project(p: vec3f) -> vec2f {
81
+ let z = select(max(-p.z, 1e-4), 1.0, P.quality.z > 0.5);
82
+ let ndc = vec2f(P.ao2.z * p.x, P.ao2.w * p.y) / z;
83
+ return vec2f(ndc.x * 0.5 + 0.5, 0.5 - ndc.y * 0.5);
84
+ }
85
+
86
+ @fragment fn fs_main(in: FullscreenIn) -> @location(0) vec4f {
87
+ let dim = vec2f(textureDimensions(depthTex));
88
+ let c = fetch(in.uv, dim);
89
+ if (c.w >= 1.0) {
90
+ return vec4f(0.0);
91
+ }
92
+ let uv = texel_uv(in.uv, dim);
93
+ // Per-surface roughness and metalness when the target is bound; a mirror at full weight else.
94
+ var roughMetal = vec2f(0.0, 1.0);
95
+ if (P.ssr2.w > 0.5) {
96
+ roughMetal = textureSampleLevel(roughMetalTex, nearest, uv, 0.0).rg;
97
+ }
98
+ let rough = clamp(roughMetal.x / max(P.ssr2.x, 1e-4), 0.0, 1.0);
99
+ let roughFade = 1.0 - rough * rough;
100
+ if (roughFade <= 0.0) {
101
+ return vec4f(0.0);
102
+ }
103
+ let p = c.xyz;
104
+ let t = 1.0 / dim;
105
+ let pl = fetch(uv - vec2f(t.x, 0.0), dim).xyz;
106
+ let pr = fetch(uv + vec2f(t.x, 0.0), dim).xyz;
107
+ let pu = fetch(uv - vec2f(0.0, t.y), dim).xyz;
108
+ let pd = fetch(uv + vec2f(0.0, t.y), dim).xyz;
109
+ let dx = select(p - pl, pr - p, length(pr - p) < length(p - pl));
110
+ let dy = select(p - pu, pd - p, length(pd - p) < length(p - pu));
111
+ var n = normalize(cross(dx, dy));
112
+ // The eye ray: toward the point under a perspective camera, the view axis under an ortho one.
113
+ let v = select(normalize(p), vec3f(0.0, 0.0, -1.0), P.quality.z > 0.5);
114
+ if (dot(n, -v) < 0.0) {
115
+ n = -n;
116
+ }
117
+ var weight = 1.0;
118
+ if (P.ssr2.w > 0.5) {
119
+ weight = surface_weight(roughMetal.y, dot(n, -v));
120
+ }
121
+ let r = reflect(v, n);
122
+ let steps = i32(P.ssr.y);
123
+ let stepLen = P.ssr.w / f32(steps);
124
+ var hitUv = vec2f(-1.0);
125
+ var hitFade = 0.0;
126
+ for (var i = 1; i <= steps; i++) {
127
+ let q = p + r * (stepLen * f32(i));
128
+ if (q.z > -1e-3) {
129
+ break;
130
+ }
131
+ let suv = project(q);
132
+ if (any(suv < vec2f(0.0)) || any(suv > vec2f(1.0))) {
133
+ break;
134
+ }
135
+ let sd = textureSampleLevel(depthTex, nearest, suv, 0u);
136
+ if (sd >= 1.0) {
137
+ continue;
138
+ }
139
+ let sz = view_pos(suv, sd).z;
140
+ // The ray point is behind the surface (further from the eye) by less than the thickness.
141
+ let behind = sz - q.z;
142
+ if (behind > 0.0 && behind < P.ssr.z) {
143
+ hitUv = suv;
144
+ hitFade = 1.0 - f32(i) / f32(steps);
145
+ break;
146
+ }
147
+ }
148
+ if (hitFade <= 0.0) {
149
+ return vec4f(0.0);
150
+ }
151
+ let fade = hitFade * edge_fade(hitUv) * roughFade * weight;
152
+ let col = textureSampleLevel(colorTex, linSamp, hitUv, 0.0).rgb;
153
+ return vec4f(col * fade * P.ssr2.y, fade);
154
+ }
155
+ `;