@driftengine/core 4.4.2 → 4.6.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 (339) hide show
  1. package/CHANGELOG.json +97 -0
  2. package/README.md +1 -1
  3. package/dist/geometry/boxCsg.d.ts +28 -0
  4. package/dist/geometry/boxCsg.js +139 -0
  5. package/dist/geometry/csg.d.ts +36 -0
  6. package/dist/geometry/csg.js +329 -0
  7. package/dist/geometry/meshBuilder.js +13 -0
  8. package/dist/geometry/meshConcat.js +1 -0
  9. package/dist/geometry/pixelFont.js +2 -0
  10. package/dist/geometry/solid.d.ts +61 -0
  11. package/dist/geometry/solid.js +152 -0
  12. package/dist/geometry/solidFlat.d.ts +35 -0
  13. package/dist/geometry/solidFlat.js +428 -0
  14. package/dist/geometry/solidHarness.d.ts +19 -0
  15. package/dist/geometry/solidHarness.js +66 -0
  16. package/dist/geometry/solidNormals.d.ts +17 -0
  17. package/dist/geometry/solidNormals.js +86 -0
  18. package/dist/geometry/solidRound.d.ts +41 -0
  19. package/dist/geometry/solidRound.js +323 -0
  20. package/dist/geometry/solidWriter.d.ts +16 -0
  21. package/dist/geometry/solidWriter.js +42 -0
  22. package/dist/index.d.ts +28 -3
  23. package/dist/index.js +20 -1
  24. package/dist/render/backend/api.d.ts +34 -14
  25. package/dist/render/backend/drawFog.d.ts +26 -0
  26. package/dist/render/backend/drawFog.js +30 -0
  27. package/dist/render/backend/materialChanges.d.ts +2 -1
  28. package/dist/render/backend/materialChanges.js +6 -1
  29. package/dist/render/backend/webgl2/glassTintPrograms.d.ts +13 -0
  30. package/dist/render/backend/webgl2/glassTintPrograms.js +38 -0
  31. package/dist/render/backend/webgl2/instanced.d.ts +24 -5
  32. package/dist/render/backend/webgl2/instanced.js +72 -15
  33. package/dist/render/backend/webgl2/renderer.d.ts +201 -16
  34. package/dist/render/backend/webgl2/renderer.js +596 -224
  35. package/dist/render/backend/webgpu/buffers.d.ts +29 -1
  36. package/dist/render/backend/webgpu/buffers.js +61 -4
  37. package/dist/render/backend/webgpu/depthPass.d.ts +1 -1
  38. package/dist/render/backend/webgpu/depthPass.js +7 -2
  39. package/dist/render/backend/webgpu/drawCommand.d.ts +5 -0
  40. package/dist/render/backend/webgpu/drawCommand.js +1 -0
  41. package/dist/render/backend/webgpu/flatPass.d.ts +6 -1
  42. package/dist/render/backend/webgpu/flatPass.js +57 -9
  43. package/dist/render/backend/webgpu/glassTintPass.d.ts +28 -0
  44. package/dist/render/backend/webgpu/glassTintPass.js +206 -0
  45. package/dist/render/backend/webgpu/gpuDrivenPass.d.ts +60 -1
  46. package/dist/render/backend/webgpu/gpuDrivenPass.js +305 -5
  47. package/dist/render/backend/webgpu/instanceCullPass.d.ts +34 -0
  48. package/dist/render/backend/webgpu/instanceCullPass.js +144 -0
  49. package/dist/render/backend/webgpu/instanced.d.ts +23 -2
  50. package/dist/render/backend/webgpu/instanced.js +41 -4
  51. package/dist/render/backend/webgpu/latePass.d.ts +30 -0
  52. package/dist/render/backend/webgpu/latePass.js +140 -0
  53. package/dist/render/backend/webgpu/lightVolumePass.js +5 -1
  54. package/dist/render/backend/webgpu/motionPass.d.ts +95 -0
  55. package/dist/render/backend/webgpu/motionPass.js +491 -0
  56. package/dist/render/backend/webgpu/pipelineCache.d.ts +10 -1
  57. package/dist/render/backend/webgpu/pipelineCache.js +11 -1
  58. package/dist/render/backend/webgpu/pointGlassTintPass.d.ts +32 -0
  59. package/dist/render/backend/webgpu/pointGlassTintPass.js +193 -0
  60. package/dist/render/backend/webgpu/pointShadowPass.d.ts +35 -6
  61. package/dist/render/backend/webgpu/pointShadowPass.js +105 -20
  62. package/dist/render/backend/webgpu/postPass.d.ts +3 -13
  63. package/dist/render/backend/webgpu/renderer.d.ts +271 -64
  64. package/dist/render/backend/webgpu/renderer.js +1395 -580
  65. package/dist/render/backend/webgpu/scatterPass.js +2 -1
  66. package/dist/render/backend/webgpu/sdfTextPass.js +6 -1
  67. package/dist/render/backend/webgpu/shaders/instanceCull.wgsl.d.ts +18 -0
  68. package/dist/render/backend/webgpu/shaders/instanceCull.wgsl.js +59 -0
  69. package/dist/render/backend/webgpu/skinPaletteRing.d.ts +4 -0
  70. package/dist/render/backend/webgpu/skinPaletteRing.js +4 -0
  71. package/dist/render/backend/webgpu/snapshotMips.d.ts +29 -0
  72. package/dist/render/backend/webgpu/snapshotMips.js +52 -0
  73. package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +22 -3
  74. package/dist/render/backend/webgpu/surfaceTexturePass.js +78 -39
  75. package/dist/render/driftLight/bake.d.ts +7 -0
  76. package/dist/render/driftLight/bake.js +61 -56
  77. package/dist/render/driftLight/denseField.d.ts +46 -0
  78. package/dist/render/driftLight/denseField.js +109 -0
  79. package/dist/render/driftLight/lightField.d.ts +10 -5
  80. package/dist/render/driftLight/lightField.js +25 -19
  81. package/dist/render/driftLight/presence.d.ts +50 -0
  82. package/dist/render/driftLight/presence.js +43 -0
  83. package/dist/render/driftLight/uniforms.d.ts +3 -3
  84. package/dist/render/driftLight/uniforms.js +5 -6
  85. package/dist/render/driftLight/worldLightField.d.ts +41 -0
  86. package/dist/render/driftLight/worldLightField.js +66 -0
  87. package/dist/render/fixtureAtlas.d.ts +20 -0
  88. package/dist/render/fixtureAtlas.js +49 -0
  89. package/dist/render/glass.d.ts +45 -0
  90. package/dist/render/glass.js +39 -0
  91. package/dist/render/glassCasters.d.ts +40 -0
  92. package/dist/render/glassCasters.js +84 -0
  93. package/dist/render/glassShadow.d.ts +78 -0
  94. package/dist/render/glassShadow.js +125 -0
  95. package/dist/render/globalMediumPass.d.ts +2 -3
  96. package/dist/render/globalMediumPass.js +2 -8
  97. package/dist/render/gpudriven/lit.d.ts +12 -0
  98. package/dist/render/gpudriven/lit.js +23 -0
  99. package/dist/render/gpudriven/materialTable.d.ts +15 -5
  100. package/dist/render/gpudriven/materialTable.js +26 -6
  101. package/dist/render/gpudriven/pipeline.d.ts +1 -1
  102. package/dist/render/gpudriven/pipeline.js +4 -3
  103. package/dist/render/gpudriven/sunGlass.d.ts +35 -0
  104. package/dist/render/gpudriven/sunGlass.js +89 -0
  105. package/dist/render/instanceCull.d.ts +63 -0
  106. package/dist/render/instanceCull.js +160 -0
  107. package/dist/render/instances.d.ts +15 -0
  108. package/dist/render/lightBudget.d.ts +36 -35
  109. package/dist/render/lightBudget.js +38 -37
  110. package/dist/render/lightGrid.d.ts +42 -0
  111. package/dist/render/lightGrid.js +87 -0
  112. package/dist/render/mesh.d.ts +18 -24
  113. package/dist/render/mesh.js +51 -68
  114. package/dist/render/occlusion.d.ts +10 -0
  115. package/dist/render/occlusion.js +36 -7
  116. package/dist/render/pointGlassTint.d.ts +59 -0
  117. package/dist/render/pointGlassTint.js +198 -0
  118. package/dist/render/pointLightSelection.d.ts +7 -0
  119. package/dist/render/pointLightSelection.js +29 -4
  120. package/dist/render/pointShadowArray.d.ts +56 -3
  121. package/dist/render/pointShadowArray.js +127 -16
  122. package/dist/render/pointShadowMap.d.ts +23 -1
  123. package/dist/render/pointShadowMap.js +27 -1
  124. package/dist/render/rainField.d.ts +13 -0
  125. package/dist/render/rainField.js +27 -5
  126. package/dist/render/recon/changeFrames.d.ts +15 -0
  127. package/dist/render/recon/changeFrames.js +21 -0
  128. package/dist/render/recon/lateDepth.d.ts +11 -0
  129. package/dist/render/recon/lateDepth.js +70 -0
  130. package/dist/render/recon/lateRouting.d.ts +14 -0
  131. package/dist/render/recon/lateRouting.js +18 -0
  132. package/dist/render/recon/mover.d.ts +57 -0
  133. package/dist/render/recon/mover.js +86 -0
  134. package/dist/render/recon/resolve.d.ts +34 -4
  135. package/dist/render/recon/resolve.js +161 -20
  136. package/dist/render/renderQuality.d.ts +35 -0
  137. package/dist/render/renderQuality.js +7 -0
  138. package/dist/render/rendererHarness.js +16 -0
  139. package/dist/render/sceneTarget.d.ts +9 -1
  140. package/dist/render/sceneTarget.js +60 -12
  141. package/dist/render/shaders/depth.d.ts +16 -1
  142. package/dist/render/shaders/depth.js +94 -15
  143. package/dist/render/shaders/flat/directionalShadow.js +91 -6
  144. package/dist/render/shaders/flat/driftLight.d.ts +1 -1
  145. package/dist/render/shaders/flat/driftLight.js +32 -7
  146. package/dist/render/shaders/flat/glassTint.d.ts +6 -0
  147. package/dist/render/shaders/flat/glassTint.js +25 -0
  148. package/dist/render/shaders/flat/index.d.ts +13 -0
  149. package/dist/render/shaders/flat/index.js +9 -3
  150. package/dist/render/shaders/flat/lampGlass.d.ts +1 -0
  151. package/dist/render/shaders/flat/lampGlass.js +183 -0
  152. package/dist/render/shaders/flat/main.d.ts +1 -1
  153. package/dist/render/shaders/flat/main.js +190 -38
  154. package/dist/render/shaders/flat/pointShadow.d.ts +1 -1
  155. package/dist/render/shaders/flat/pointShadow.js +2 -0
  156. package/dist/render/shaders/flat/preamble.js +96 -26
  157. package/dist/render/shaders/flat/surfaceEffects.d.ts +29 -0
  158. package/dist/render/shaders/flat/surfaceEffects.js +152 -0
  159. package/dist/render/shaders/generated/depth.wgsl.d.ts +230 -7
  160. package/dist/render/shaders/generated/depth.wgsl.js +230 -7
  161. package/dist/render/shaders/generated/flat.wgsl.d.ts +1644 -1596
  162. package/dist/render/shaders/generated/flat.wgsl.js +1683 -1621
  163. package/dist/render/shaders/generated/globalMedium.wgsl.d.ts +3 -13
  164. package/dist/render/shaders/generated/globalMedium.wgsl.js +3 -13
  165. package/dist/render/shaders/generated/lightVolume.wgsl.d.ts +3 -13
  166. package/dist/render/shaders/generated/lightVolume.wgsl.js +21 -17
  167. package/dist/render/shaders/generated/octahedralResolve.wgsl.d.ts +29 -0
  168. package/dist/render/shaders/generated/octahedralResolve.wgsl.js +29 -0
  169. package/dist/render/shaders/generated/rush.wgsl.d.ts +21 -16
  170. package/dist/render/shaders/generated/rush.wgsl.js +21 -16
  171. package/dist/render/shaders/generated/sdfText.wgsl.d.ts +2 -2
  172. package/dist/render/shaders/generated/sdfText.wgsl.js +2 -2
  173. package/dist/render/shaders/generated/text.wgsl.d.ts +1 -1
  174. package/dist/render/shaders/generated/text.wgsl.js +1 -1
  175. package/dist/render/shaders/gi/driftLight.wgsl.d.ts +1 -1
  176. package/dist/render/shaders/gi/driftLight.wgsl.js +27 -4
  177. package/dist/render/shaders/globalMedium.js +6 -6
  178. package/dist/render/shaders/gpudriven/blendRaster.wgsl.d.ts +7 -0
  179. package/dist/render/shaders/gpudriven/blendRaster.wgsl.js +86 -1
  180. package/dist/render/shaders/gpudriven/cull.wgsl.d.ts +1 -1
  181. package/dist/render/shaders/gpudriven/cull.wgsl.js +11 -2
  182. package/dist/render/shaders/gpudriven/materialTable.wgsl.d.ts +1 -1
  183. package/dist/render/shaders/gpudriven/materialTable.wgsl.js +3 -1
  184. package/dist/render/shaders/gpudriven/shade.wgsl.js +50 -1
  185. package/dist/render/shaders/gpudriven/sunGlass.wgsl.d.ts +9 -0
  186. package/dist/render/shaders/gpudriven/sunGlass.wgsl.js +133 -0
  187. package/dist/render/shaders/lightVolume.js +6 -6
  188. package/dist/render/shaders/octahedralResolve.d.ts +8 -0
  189. package/dist/render/shaders/octahedralResolve.js +48 -17
  190. package/dist/render/shaders/recon/lateDepth.wgsl.d.ts +1 -0
  191. package/dist/render/shaders/recon/lateDepth.wgsl.js +69 -0
  192. package/dist/render/shaders/recon/motion.wgsl.d.ts +1 -1
  193. package/dist/render/shaders/recon/motion.wgsl.js +84 -0
  194. package/dist/render/shaders/recon/resolve.wgsl.d.ts +8 -7
  195. package/dist/render/shaders/recon/resolve.wgsl.js +161 -27
  196. package/dist/render/shaders/rush.js +11 -1
  197. package/dist/render/shaders/sdfText.d.ts +1 -1
  198. package/dist/render/shaders/sdfText.js +3 -2
  199. package/dist/render/shaders/text.d.ts +1 -1
  200. package/dist/render/shaders/text.js +18 -9
  201. package/dist/render/shadowCasters.d.ts +20 -1
  202. package/dist/render/shadowCasters.js +15 -1
  203. package/dist/render/shadowMap.d.ts +54 -5
  204. package/dist/render/shadowMap.js +190 -36
  205. package/dist/render/sunGlassTint.d.ts +25 -0
  206. package/dist/render/sunGlassTint.js +79 -0
  207. package/dist/render/surfaceEffects.d.ts +83 -0
  208. package/dist/render/surfaceEffects.js +75 -0
  209. package/dist/render/surfaceScene.d.ts +8 -0
  210. package/dist/render/surfaceScene.js +8 -0
  211. package/dist/render/surfaceTexture.d.ts +18 -2
  212. package/dist/render/surfaceTexture.js +68 -15
  213. package/dist/render/temporalAa.d.ts +25 -6
  214. package/dist/render/temporalAa.js +45 -15
  215. package/dist/render/textureSource.d.ts +30 -0
  216. package/dist/render/textureSource.js +53 -0
  217. package/dist/render/translucentQueue.d.ts +6 -0
  218. package/dist/render/translucentQueue.js +15 -1
  219. package/dist/render/vertexDefaults.js +2 -0
  220. package/dist/world/hlod.d.ts +104 -0
  221. package/dist/world/hlod.js +214 -0
  222. package/package.json +6 -6
  223. package/src/geometry/boxCsg.ts +186 -0
  224. package/src/geometry/csg.ts +384 -0
  225. package/src/geometry/meshBuilder.ts +15 -0
  226. package/src/geometry/meshConcat.ts +1 -0
  227. package/src/geometry/pixelFont.ts +2 -0
  228. package/src/geometry/solid.ts +189 -0
  229. package/src/geometry/solidFlat.ts +522 -0
  230. package/src/geometry/solidHarness.ts +83 -0
  231. package/src/geometry/solidNormals.ts +103 -0
  232. package/src/geometry/solidRound.ts +443 -0
  233. package/src/geometry/solidWriter.ts +58 -0
  234. package/src/index.ts +61 -1
  235. package/src/render/backend/api.ts +36 -11
  236. package/src/render/backend/drawFog.ts +31 -0
  237. package/src/render/backend/materialChanges.ts +9 -2
  238. package/src/render/backend/webgl2/glassTintPrograms.ts +58 -0
  239. package/src/render/backend/webgl2/instanced.ts +80 -14
  240. package/src/render/backend/webgl2/renderer.ts +700 -250
  241. package/src/render/backend/webgpu/buffers.ts +92 -5
  242. package/src/render/backend/webgpu/depthPass.ts +7 -2
  243. package/src/render/backend/webgpu/drawCommand.ts +6 -0
  244. package/src/render/backend/webgpu/flatPass.ts +62 -11
  245. package/src/render/backend/webgpu/glassTintPass.ts +245 -0
  246. package/src/render/backend/webgpu/gpuDrivenPass.ts +349 -5
  247. package/src/render/backend/webgpu/instanceCullPass.ts +165 -0
  248. package/src/render/backend/webgpu/instanced.ts +42 -4
  249. package/src/render/backend/webgpu/latePass.ts +161 -0
  250. package/src/render/backend/webgpu/lightVolumePass.ts +5 -1
  251. package/src/render/backend/webgpu/motionPass.ts +535 -0
  252. package/src/render/backend/webgpu/pipelineCache.ts +12 -1
  253. package/src/render/backend/webgpu/pointGlassTintPass.ts +233 -0
  254. package/src/render/backend/webgpu/pointShadowPass.ts +158 -26
  255. package/src/render/backend/webgpu/renderer.ts +1671 -670
  256. package/src/render/backend/webgpu/scatterPass.ts +2 -1
  257. package/src/render/backend/webgpu/sdfTextPass.ts +6 -1
  258. package/src/render/backend/webgpu/shaders/instanceCull.wgsl.ts +61 -0
  259. package/src/render/backend/webgpu/skinPaletteRing.ts +4 -0
  260. package/src/render/backend/webgpu/snapshotMips.ts +80 -0
  261. package/src/render/backend/webgpu/surfaceTexturePass.ts +91 -51
  262. package/src/render/driftLight/bake.ts +85 -62
  263. package/src/render/driftLight/denseField.ts +149 -0
  264. package/src/render/driftLight/lightField.ts +33 -18
  265. package/src/render/driftLight/presence.ts +69 -0
  266. package/src/render/driftLight/uniforms.ts +8 -9
  267. package/src/render/driftLight/worldLightField.ts +83 -0
  268. package/src/render/fixtureAtlas.ts +63 -0
  269. package/src/render/glass.ts +74 -0
  270. package/src/render/glassCasters.ts +164 -0
  271. package/src/render/glassShadow.ts +153 -0
  272. package/src/render/globalMediumPass.ts +4 -11
  273. package/src/render/gpudriven/lit.ts +31 -0
  274. package/src/render/gpudriven/materialTable.ts +28 -6
  275. package/src/render/gpudriven/pipeline.ts +4 -3
  276. package/src/render/gpudriven/sunGlass.ts +117 -0
  277. package/src/render/instanceCull.ts +205 -0
  278. package/src/render/instances.ts +16 -0
  279. package/src/render/lightBudget.ts +41 -38
  280. package/src/render/lightGrid.ts +130 -0
  281. package/src/render/mesh.ts +59 -72
  282. package/src/render/occlusion.ts +51 -7
  283. package/src/render/pointGlassTint.ts +250 -0
  284. package/src/render/pointLightSelection.ts +72 -4
  285. package/src/render/pointShadowArray.ts +188 -22
  286. package/src/render/pointShadowMap.ts +41 -0
  287. package/src/render/rainField.ts +39 -5
  288. package/src/render/recon/changeFrames.ts +24 -0
  289. package/src/render/recon/lateDepth.ts +83 -0
  290. package/src/render/recon/lateRouting.ts +29 -0
  291. package/src/render/recon/mover.ts +118 -0
  292. package/src/render/recon/resolve.ts +206 -20
  293. package/src/render/renderQuality.ts +47 -0
  294. package/src/render/rendererHarness.ts +16 -0
  295. package/src/render/sceneTarget.ts +62 -11
  296. package/src/render/shaders/depth.ts +118 -15
  297. package/src/render/shaders/flat/directionalShadow.ts +97 -6
  298. package/src/render/shaders/flat/driftLight.ts +32 -7
  299. package/src/render/shaders/flat/glassTint.ts +25 -0
  300. package/src/render/shaders/flat/index.ts +22 -3
  301. package/src/render/shaders/flat/lampGlass.ts +185 -0
  302. package/src/render/shaders/flat/main.ts +190 -38
  303. package/src/render/shaders/flat/pointShadow.ts +2 -0
  304. package/src/render/shaders/flat/preamble.ts +96 -26
  305. package/src/render/shaders/flat/surfaceEffects.ts +152 -0
  306. package/src/render/shaders/generated/depth.wgsl.ts +237 -7
  307. package/src/render/shaders/generated/flat.wgsl.ts +1685 -1621
  308. package/src/render/shaders/generated/globalMedium.wgsl.ts +3 -13
  309. package/src/render/shaders/generated/lightVolume.wgsl.ts +23 -17
  310. package/src/render/shaders/generated/octahedralResolve.wgsl.ts +30 -0
  311. package/src/render/shaders/generated/rush.wgsl.ts +21 -16
  312. package/src/render/shaders/generated/sdfText.wgsl.ts +2 -2
  313. package/src/render/shaders/generated/text.wgsl.ts +1 -1
  314. package/src/render/shaders/gi/driftLight.wgsl.ts +27 -4
  315. package/src/render/shaders/globalMedium.ts +6 -6
  316. package/src/render/shaders/gpudriven/blendRaster.wgsl.ts +88 -1
  317. package/src/render/shaders/gpudriven/cull.wgsl.ts +11 -2
  318. package/src/render/shaders/gpudriven/materialTable.wgsl.ts +3 -1
  319. package/src/render/shaders/gpudriven/shade.wgsl.ts +50 -1
  320. package/src/render/shaders/gpudriven/sunGlass.wgsl.ts +139 -0
  321. package/src/render/shaders/lightVolume.ts +6 -6
  322. package/src/render/shaders/octahedralResolve.ts +50 -17
  323. package/src/render/shaders/recon/lateDepth.wgsl.ts +72 -0
  324. package/src/render/shaders/recon/motion.wgsl.ts +84 -0
  325. package/src/render/shaders/recon/resolve.wgsl.ts +161 -27
  326. package/src/render/shaders/rush.ts +11 -1
  327. package/src/render/shaders/sdfText.ts +3 -2
  328. package/src/render/shaders/text.ts +18 -9
  329. package/src/render/shadowCasters.ts +28 -1
  330. package/src/render/shadowMap.ts +231 -37
  331. package/src/render/sunGlassTint.ts +86 -0
  332. package/src/render/surfaceEffects.ts +132 -0
  333. package/src/render/surfaceScene.ts +16 -0
  334. package/src/render/surfaceTexture.ts +110 -19
  335. package/src/render/temporalAa.ts +78 -14
  336. package/src/render/textureSource.ts +72 -0
  337. package/src/render/translucentQueue.ts +20 -1
  338. package/src/render/vertexDefaults.ts +2 -0
  339. package/src/world/hlod.ts +290 -0
@@ -334,7 +334,8 @@ export function createScatterDepthBindGroupLayout(
334
334
  {
335
335
  binding: previous.texture,
336
336
  visibility: VISIBILITY_FRAGMENT,
337
- texture: { sampleType: 'unfilterable-float' },
337
+ /* The depth program's peel read, an array since the sun's maps became one. */
338
+ texture: { sampleType: 'unfilterable-float', viewDimension: '2d-array' },
338
339
  },
339
340
  {
340
341
  binding: previous.sampler,
@@ -142,7 +142,12 @@ export function createSdfTextBindGroupLayout(device: GPUDevice): GPUBindGroupLay
142
142
  visibility: VISIBILITY_FRAGMENT,
143
143
  buffer: { type: 'uniform', hasDynamicOffset: true, minBindingSize: SDF_TEXT_FRAG_SIZE },
144
144
  },
145
- { binding: ATLAS.texture, visibility: VISIBILITY_FRAGMENT, texture: {} },
145
+ /* The atlas is a surface texture, and every surface texture is a `2d-array` view. */
146
+ {
147
+ binding: ATLAS.texture,
148
+ visibility: VISIBILITY_FRAGMENT,
149
+ texture: { viewDimension: '2d-array' },
150
+ },
146
151
  { binding: ATLAS.sampler, visibility: VISIBILITY_FRAGMENT, sampler: {} },
147
152
  ],
148
153
  });
@@ -0,0 +1,61 @@
1
+ import { INSTANCE_FLOATS } from '../../../instances.ts';
2
+
3
+ /**
4
+ * The instance cull, in WGSL, hand-authored under the compute exception `AGENTS.md` records
5
+ * (2026-08-24): a compute shader has no WebGL2 twin to generate from, and `npm run wgsl` neither
6
+ * writes nor polices this file.
7
+ *
8
+ * **It keeps exactly what `cullInstances` keeps**, and `gpu-parity.mjs` runs both on a device: an
9
+ * instance's world sphere is the mesh's sphere with its centre through the model matrix and its
10
+ * radius scaled by the largest column, and it is kept unless wholly beyond one of the six planes.
11
+ * The one difference is order — survivors are compacted by `atomicAdd`, so they arrive in whatever
12
+ * order the invocations reached it. That is invisible to an opaque draw and is why a blended draw
13
+ * is never culled here.
14
+ *
15
+ * `args` is a `drawIndexedIndirect` record, `(indexCount, instanceCount, firstIndex, baseVertex,
16
+ * firstInstance)`; the renderer writes it with a zero instance count before each dispatch and this
17
+ * counts the survivors into it.
18
+ */
19
+ export const INSTANCE_CULL_WORKGROUP = 64;
20
+
21
+ export const INSTANCE_CULL_WGSL = /* wgsl */ `
22
+ struct Params {
23
+ planes: array<vec4<f32>, 6>,
24
+ sphere: vec4<f32>,
25
+ count: u32,
26
+ pad0: u32,
27
+ pad1: u32,
28
+ pad2: u32,
29
+ }
30
+
31
+ @group(0) @binding(0) var<uniform> params: Params;
32
+ @group(0) @binding(1) var<storage, read> source: array<f32>;
33
+ @group(0) @binding(2) var<storage, read_write> kept: array<f32>;
34
+ @group(0) @binding(3) var<storage, read_write> args: array<atomic<u32>, 5>;
35
+
36
+ const STRIDE: u32 = ${INSTANCE_FLOATS}u;
37
+
38
+ @compute @workgroup_size(${INSTANCE_CULL_WORKGROUP})
39
+ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
40
+ let i = id.x;
41
+ if (i >= params.count) { return; }
42
+ let at = i * STRIDE;
43
+ let c0 = vec3<f32>(source[at], source[at + 1u], source[at + 2u]);
44
+ let c1 = vec3<f32>(source[at + 4u], source[at + 5u], source[at + 6u]);
45
+ let c2 = vec3<f32>(source[at + 8u], source[at + 9u], source[at + 10u]);
46
+ let t = vec3<f32>(source[at + 12u], source[at + 13u], source[at + 14u]);
47
+ let local = params.sphere;
48
+ let centre = c0 * local.x + c1 * local.y + c2 * local.z + t;
49
+ let scale = sqrt(max(dot(c0, c0), max(dot(c1, c1), dot(c2, c2))));
50
+ let radius = local.w * scale;
51
+ for (var p = 0u; p < 6u; p = p + 1u) {
52
+ let plane = params.planes[p];
53
+ if (dot(plane.xyz, centre) + plane.w < -radius) { return; }
54
+ }
55
+ let slot = atomicAdd(&args[1], 1u);
56
+ let to = slot * STRIDE;
57
+ for (var k = 0u; k < STRIDE; k = k + 1u) {
58
+ kept[to + k] = source[at + k];
59
+ }
60
+ }
61
+ `;
@@ -15,6 +15,10 @@ import { SkinPaletteTexture } from './skinPaletteTexture.ts';
15
15
  * **What would make it wrong** is a genuine crowd — a stadium, a swarm of rigged birds — and the
16
16
  * fix is then the one `skinPalette.ts` names: one texture holding every palette with a per-draw
17
17
  * row offset, which spends a uniform and a shader change to stop spending textures.
18
+ *
19
+ * **A reconstructing frame spends one more per skinned mover**: last frame's pose is uploaded into
20
+ * a slot of its own for the motion pass. So the same budget is about twelve such characters with a
21
+ * reflection and three shadow layers each.
18
22
  */
19
23
  export const MAX_SKIN_PALETTES = 128;
20
24
 
@@ -0,0 +1,80 @@
1
+ /**
2
+ * The mip chain of the frame's refraction copy, recorded into the frame's own encoder.
3
+ *
4
+ * **Frosted glass reads a blurrier level of what is behind it** (`glass.ts`), so on a frame that draws
5
+ * glass the copy needs every level filled from the one above. `generateMipChain` does that for an
6
+ * upload, on an encoder of its own that it submits at once — which here would run before the frame's
7
+ * encoder had even made the copy, and would build views and bind groups on every call. So this
8
+ * makes them once per copy texture and records one pass per level wherever the copy was taken.
9
+ *
10
+ * What it costs is a render pass per level on every frame that takes the copy — about a dozen for a
11
+ * 1080p copy, each a quarter of the one before, together about a third of the copy itself — and
12
+ * nothing on a frame that shows nothing through anything. **A frame that refracts and draws no glass
13
+ * pays it too**, and that is the trade: the copy is taken at the first draw that asks and shared by
14
+ * every later one, so a glass pane drawn after a refracting one would otherwise read levels nothing
15
+ * filled. WebGL2 fills its chain on the first glass draw instead, because a `generateMipmap` there
16
+ * ends no pass. What would change it is knowing at the copy whether glass follows in the frame.
17
+ */
18
+ import type { MipPipelines } from './surfaceTexturePass.ts';
19
+ import { mipBlitPipeline } from './surfaceTexturePass.ts';
20
+
21
+ export class SnapshotMips {
22
+ private readonly pipeline: GPURenderPipeline;
23
+ private readonly targets: GPUTextureView[] = [];
24
+ private readonly groups: GPUBindGroup[] = [];
25
+
26
+ constructor(
27
+ device: GPUDevice,
28
+ pipelines: MipPipelines,
29
+ texture: GPUTexture,
30
+ format: GPUTextureFormat,
31
+ /** One layer of an array texture, where the chain is a layer's rather than the texture's. */
32
+ layer: number | null = null,
33
+ private readonly label = 'refract.mips',
34
+ ) {
35
+ this.pipeline = mipBlitPipeline(device, pipelines, format);
36
+ const sampler = device.createSampler({
37
+ label: 'refract.mips.sampler',
38
+ magFilter: 'linear',
39
+ minFilter: 'linear',
40
+ });
41
+ const layout = this.pipeline.getBindGroupLayout(0);
42
+ const at =
43
+ layer === null ? {} : { dimension: '2d' as const, baseArrayLayer: layer, arrayLayerCount: 1 };
44
+ for (let level = 1; level < texture.mipLevelCount; level++) {
45
+ const source = texture.createView({ ...at, baseMipLevel: level - 1, mipLevelCount: 1 });
46
+ this.targets.push(texture.createView({ ...at, baseMipLevel: level, mipLevelCount: 1 }));
47
+ this.groups.push(
48
+ device.createBindGroup({
49
+ label: `refract.mips.${String(level)}`,
50
+ layout,
51
+ entries: [
52
+ { binding: 0, resource: source },
53
+ { binding: 1, resource: sampler },
54
+ ],
55
+ }),
56
+ );
57
+ }
58
+ }
59
+
60
+ /** Fill every level from level 0, in `encoder`, after whatever wrote level 0 there. */
61
+ record(encoder: GPUCommandEncoder): void {
62
+ for (let i = 0; i < this.targets.length; i++) {
63
+ const pass = encoder.beginRenderPass({
64
+ label: this.label,
65
+ colorAttachments: [
66
+ {
67
+ view: this.targets[i] as GPUTextureView,
68
+ loadOp: 'clear',
69
+ storeOp: 'store',
70
+ clearValue: [0, 0, 0, 0],
71
+ },
72
+ ],
73
+ });
74
+ pass.setPipeline(this.pipeline);
75
+ pass.setBindGroup(0, this.groups[i] as GPUBindGroup);
76
+ pass.draw(3);
77
+ pass.end();
78
+ }
79
+ }
80
+ }
@@ -1,4 +1,7 @@
1
1
  import type { SurfaceTextureOptions } from '../../surfaceTexture.ts';
2
+ import { SURFACE_EFFECT_TEXELS, packSurfaceEffects } from '../../surfaceEffects.ts';
3
+ import { toHalfFloats } from '../../halfFloat.ts';
4
+ import { isSourceList, layerSize, refuseArrayUpdate, sourceSize } from '../../textureSource.ts';
2
5
  import type { PipelineCache } from './pipelineCache.ts';
3
6
  import { shaderModule } from './shaderModules.ts';
4
7
 
@@ -89,6 +92,8 @@ export function generateMipChain(
89
92
  texture: GPUTexture,
90
93
  format: GPUTextureFormat,
91
94
  levels: number,
95
+ /** How many array layers to fill, each its own chain. One for a plain texture. */
96
+ layers = 1,
92
97
  ): void {
93
98
  if (levels < 2) return;
94
99
  const pipeline = mipBlitPipeline(device, pipelines, format);
@@ -98,29 +103,33 @@ export function generateMipChain(
98
103
  minFilter: 'linear',
99
104
  });
100
105
  const encoder = device.createCommandEncoder({ label: 'mip.chain' });
101
- for (let level = 1; level < levels; level++) {
102
- const source = texture.createView({ baseMipLevel: level - 1, mipLevelCount: 1 });
103
- const target = texture.createView({ baseMipLevel: level, mipLevelCount: 1 });
104
- const pass = encoder.beginRenderPass({
105
- label: `mip.level${level}`,
106
- colorAttachments: [
107
- { view: target, loadOp: 'clear', storeOp: 'store', clearValue: [0, 0, 0, 0] },
108
- ],
109
- });
110
- pass.setPipeline(pipeline);
111
- pass.setBindGroup(
112
- 0,
113
- device.createBindGroup({
114
- layout: pipeline.getBindGroupLayout(0),
115
- entries: [
116
- { binding: 0, resource: source },
117
- { binding: 1, resource: sampler },
106
+ /* One level of one layer at a time: a render target is a single 2D subresource, and the source
107
+ view is one level of the same layer, so no pass reads what it writes. */
108
+ for (let layer = 0; layer < layers; layer++)
109
+ for (let level = 1; level < levels; level++) {
110
+ const one = { dimension: '2d' as const, baseArrayLayer: layer, arrayLayerCount: 1 };
111
+ const source = texture.createView({ ...one, baseMipLevel: level - 1, mipLevelCount: 1 });
112
+ const target = texture.createView({ ...one, baseMipLevel: level, mipLevelCount: 1 });
113
+ const pass = encoder.beginRenderPass({
114
+ label: `mip.level${level}`,
115
+ colorAttachments: [
116
+ { view: target, loadOp: 'clear', storeOp: 'store', clearValue: [0, 0, 0, 0] },
118
117
  ],
119
- }),
120
- );
121
- pass.draw(3);
122
- pass.end();
123
- }
118
+ });
119
+ pass.setPipeline(pipeline);
120
+ pass.setBindGroup(
121
+ 0,
122
+ device.createBindGroup({
123
+ layout: pipeline.getBindGroupLayout(0),
124
+ entries: [
125
+ { binding: 0, resource: source },
126
+ { binding: 1, resource: sampler },
127
+ ],
128
+ }),
129
+ );
130
+ pass.draw(3);
131
+ pass.end();
132
+ }
124
133
  device.queue.submit([encoder.finish()]);
125
134
  }
126
135
 
@@ -128,7 +137,7 @@ export function generateMipChain(
128
137
  * Cached per format, because the two differ: an `-srgb` target encodes on write, and blitting a
129
138
  * linear chain through a pipeline declared for the other one is a gamma error per level.
130
139
  */
131
- function mipBlitPipeline(
140
+ export function mipBlitPipeline(
132
141
  device: GPUDevice,
133
142
  pipelines: MipPipelines,
134
143
  format: GPUTextureFormat,
@@ -149,19 +158,11 @@ function mipBlitPipeline(
149
158
  }));
150
159
  }
151
160
 
152
- function sourceSize(source: TexImageSource): { width: number; height: number } {
153
- const candidate = source as {
154
- width?: number;
155
- height?: number;
156
- videoWidth?: number;
157
- videoHeight?: number;
158
- };
159
- const width = candidate.videoWidth ?? candidate.width ?? 0;
160
- const height = candidate.videoHeight ?? candidate.height ?? 0;
161
- return { width: Math.max(1, width), height: Math.max(1, height) };
162
- }
163
-
164
- /** An image uploaded once and bound per material. */
161
+ /**
162
+ * An image uploaded once and bound per material — or several of one size, as an array's layers.
163
+ * Every surface texture is a `2d-array` view, a plain image an array of one; `surfaceTexture.ts`
164
+ * says why.
165
+ */
165
166
  export class GpuSurfaceTexture {
166
167
  readonly sampler: GPUSampler;
167
168
 
@@ -172,14 +173,22 @@ export class GpuSurfaceTexture {
172
173
  private levels: number;
173
174
  private width: number;
174
175
  private height: number;
176
+ /** How many images the array holds. */
177
+ readonly layers: number;
178
+ /** The per-layer effects table, or null for a texture given none. See `surfaceEffects.ts`. */
179
+ private effectsTable: GPUTexture | null = null;
180
+ /** Its view, which the flat group binds beside this image when it is a material's albedo. */
181
+ effectsView: GPUTextureView | null = null;
175
182
 
176
183
  constructor(
177
184
  private readonly device: GPUDevice,
178
185
  private readonly pipelines: PipelineCache,
179
- source: TexImageSource,
186
+ source: TexImageSource | readonly TexImageSource[],
180
187
  options: SurfaceTextureOptions = {},
181
188
  ) {
182
- const { width, height } = sourceSize(source);
189
+ const sources: readonly TexImageSource[] = isSourceList(source) ? source : [source];
190
+ const { width, height } = layerSize(sources);
191
+ this.layers = sources.length;
183
192
  this.mipmapped = options.mipmap ?? true;
184
193
  /* `SRGB8_ALPHA8`'s equivalent. Decoded in the sampler, before filtering, which is the only
185
194
  place it is correct — `surfaceTexture.ts` makes the argument in full. */
@@ -188,7 +197,7 @@ export class GpuSurfaceTexture {
188
197
  this.height = height;
189
198
  this.levels = this.mipmapped ? mipLevelCount(width, height) : 1;
190
199
  this.texture = this.allocate();
191
- this.current = this.texture.createView();
200
+ this.current = this.arrayView(this.texture);
192
201
 
193
202
  const wrap: GPUAddressMode =
194
203
  (options.wrap ?? 'repeat') === 'repeat' ? 'repeat' : 'clamp-to-edge';
@@ -223,7 +232,32 @@ export class GpuSurfaceTexture {
223
232
  : 1,
224
233
  });
225
234
 
226
- this.upload(source);
235
+ this.upload(sources);
236
+
237
+ /* Half floats, as `renderer.ts` uploads it: filterable, so the ordinary float layout takes it,
238
+ and read by `textureLoad` alone, so the filter never runs. */
239
+ const effects = options.effects ?? [];
240
+ if (effects.length > 0) {
241
+ const table = device.createTexture({
242
+ label: 'surface.effects',
243
+ size: [SURFACE_EFFECT_TEXELS, effects.length],
244
+ format: 'rgba16float',
245
+ usage: 0x4 | 0x2, // TEXTURE_BINDING | COPY_DST
246
+ });
247
+ device.queue.writeTexture(
248
+ { texture: table },
249
+ toHalfFloats(packSurfaceEffects(effects)),
250
+ { bytesPerRow: SURFACE_EFFECT_TEXELS * 8, rowsPerImage: effects.length },
251
+ { width: SURFACE_EFFECT_TEXELS, height: effects.length, depthOrArrayLayers: 1 },
252
+ );
253
+ this.effectsTable = table;
254
+ this.effectsView = table.createView();
255
+ }
256
+ }
257
+
258
+ /** A view of every layer, as the shaders' `texture_2d_array` bindings read it. */
259
+ private arrayView(texture: GPUTexture): GPUTextureView {
260
+ return texture.createView({ dimension: '2d-array', arrayLayerCount: this.layers });
227
261
  }
228
262
 
229
263
  /** The view every binding of this image holds. A new one when `update` changes the size. */
@@ -248,24 +282,25 @@ export class GpuSurfaceTexture {
248
282
  update(source: TexImageSource): GPUTexture | null {
249
283
  const replaced = this.texture;
250
284
  if (replaced === null) return null;
285
+ refuseArrayUpdate(this.layers);
251
286
  const { width, height } = sourceSize(source);
252
287
  if (width === this.width && height === this.height) {
253
- this.upload(source);
288
+ this.upload([source]);
254
289
  return null;
255
290
  }
256
291
  this.width = width;
257
292
  this.height = height;
258
293
  this.levels = this.mipmapped ? mipLevelCount(width, height) : 1;
259
294
  this.texture = this.allocate();
260
- this.current = this.texture.createView();
261
- this.upload(source);
295
+ this.current = this.arrayView(this.texture);
296
+ this.upload([source]);
262
297
  return replaced;
263
298
  }
264
299
 
265
300
  private allocate(): GPUTexture {
266
301
  return this.device.createTexture({
267
302
  label: 'surface.texture',
268
- size: [this.width, this.height],
303
+ size: [this.width, this.height, this.layers],
269
304
  format: this.format,
270
305
  mipLevelCount: this.levels,
271
306
  /*
@@ -290,9 +325,12 @@ export class GpuSurfaceTexture {
290
325
  if (this.texture === null) return;
291
326
  this.texture.destroy();
292
327
  this.texture = null;
328
+ this.effectsTable?.destroy();
329
+ this.effectsTable = null;
330
+ this.effectsView = null;
293
331
  }
294
332
 
295
- private upload(source: TexImageSource): void {
333
+ private upload(sources: readonly TexImageSource[]): void {
296
334
  const texture = this.texture;
297
335
  if (texture === null) return;
298
336
  /*
@@ -306,11 +344,13 @@ export class GpuSurfaceTexture {
306
344
  * The orientation check that passed this backend was run on a canvas alone, where the two
307
345
  * agreed. Measure both source types, or a texture pipeline is half tested.
308
346
  */
309
- this.device.queue.copyExternalImageToTexture(
310
- { source: source as GPUCopyExternalImageSource, flipY: false },
311
- { texture },
312
- [this.width, this.height],
313
- );
347
+ for (let layer = 0; layer < sources.length; layer++) {
348
+ this.device.queue.copyExternalImageToTexture(
349
+ { source: sources[layer] as GPUCopyExternalImageSource, flipY: false },
350
+ { texture, origin: [0, 0, layer] },
351
+ [this.width, this.height],
352
+ );
353
+ }
314
354
  if (this.levels > 1) this.generateMips(texture);
315
355
  }
316
356
 
@@ -322,6 +362,6 @@ export class GpuSurfaceTexture {
322
362
  * an attachment and a resource at once — rejected, and rejected silently enough to matter.
323
363
  */
324
364
  private generateMips(texture: GPUTexture): void {
325
- generateMipChain(this.device, this.pipelines, texture, this.format, this.levels);
365
+ generateMipChain(this.device, this.pipelines, texture, this.format, this.levels, this.layers);
326
366
  }
327
367
  }
@@ -57,69 +57,92 @@ export function bakeBrick(
57
57
  for (let k = 0; k < BRICK_SAMPLES; k++) {
58
58
  for (let j = 0; j < BRICK_SAMPLES; j++) {
59
59
  for (let i = 0; i < BRICK_SAMPLES; i++) {
60
- const texel = (i + BRICK_SAMPLES * (j + BRICK_SAMPLES * k)) * 4;
61
- const px = bx + i * spacing;
62
- const py = by + j * spacing;
63
- const pz = bz + k * spacing;
64
- light.fill(0, texel, texel + 4);
65
- direction.fill(0, texel, texel + 4);
66
- if (distance !== null && distance(px, py, pz) < 0) continue;
67
-
68
- let r = 0;
69
- let g = 0;
70
- let b = 0;
71
- let towardX = 0;
72
- let towardY = 0;
73
- let towardZ = 0;
74
- let weight = 0;
75
- for (let listed = first; listed < last; listed++) {
76
- const source = lights[layout.lights[listed] as number];
77
- if (source === undefined) continue;
78
- const dx = source.x - px;
79
- const dy = source.y - py;
80
- const dz = source.z - pz;
81
- const dist = Math.hypot(dx, dy, dz);
82
- if (dist >= source.radius) continue;
83
- const shape = pointLightShape(dist, source.radius, falloff);
84
- if (shape <= 0) continue;
85
- const seen =
86
- distance === null
87
- ? 1
88
- : softVisibility(
89
- distance,
90
- px,
91
- py,
92
- pz,
93
- source.x,
94
- source.y,
95
- source.z,
96
- source.sourceRadius,
97
- );
98
- if (seen <= 0) continue;
99
- const lr = source.r * shape * seen;
100
- const lg = source.g * shape * seen;
101
- const lb = source.b * shape * seen;
102
- r += lr;
103
- g += lg;
104
- b += lb;
105
- const bright = 0.2126 * lr + 0.7152 * lg + 0.0722 * lb;
106
- if (dist > 1e-6 && bright > 0) {
107
- towardX += (bright * dx) / dist;
108
- towardY += (bright * dy) / dist;
109
- towardZ += (bright * dz) / dist;
110
- weight += bright;
111
- }
112
- }
113
- light[texel] = r;
114
- light[texel + 1] = g;
115
- light[texel + 2] = b;
116
- light[texel + 3] = 1;
117
- if (weight > 0) {
118
- direction[texel] = towardX / weight;
119
- direction[texel + 1] = towardY / weight;
120
- direction[texel + 2] = towardZ / weight;
121
- }
60
+ sampleFieldLight(
61
+ bx + i * spacing,
62
+ by + j * spacing,
63
+ bz + k * spacing,
64
+ lights,
65
+ layout.lights,
66
+ first,
67
+ last,
68
+ falloff,
69
+ distance,
70
+ light,
71
+ direction,
72
+ (i + BRICK_SAMPLES * (j + BRICK_SAMPLES * k)) * 4,
73
+ );
122
74
  }
123
75
  }
124
76
  }
125
77
  }
78
+
79
+ /**
80
+ * One sample: the light arriving at (px, py, pz) from `lights[candidates[first..last)]`, written at
81
+ * `texel` in `light` (colour, then 1 for a sample and 0 for one inside solid geometry) and in
82
+ * `direction` (the brightness-weighted way it arrives from). Shared by every bake, so a brick and a
83
+ * dense volume cannot come to disagree about what a sample is.
84
+ */
85
+ export function sampleFieldLight(
86
+ px: number,
87
+ py: number,
88
+ pz: number,
89
+ lights: readonly FieldLight[],
90
+ candidates: ArrayLike<number>,
91
+ first: number,
92
+ last: number,
93
+ falloff: PointLightFalloff,
94
+ distance: DistanceAt | null,
95
+ light: Float32Array,
96
+ direction: Float32Array,
97
+ texel: number,
98
+ ): void {
99
+ light.fill(0, texel, texel + 4);
100
+ direction.fill(0, texel, texel + 4);
101
+ if (distance !== null && distance(px, py, pz) < 0) return;
102
+
103
+ let r = 0;
104
+ let g = 0;
105
+ let b = 0;
106
+ let towardX = 0;
107
+ let towardY = 0;
108
+ let towardZ = 0;
109
+ let weight = 0;
110
+ for (let listed = first; listed < last; listed++) {
111
+ const source = lights[candidates[listed] as number];
112
+ if (source === undefined) continue;
113
+ const dx = source.x - px;
114
+ const dy = source.y - py;
115
+ const dz = source.z - pz;
116
+ const dist = Math.hypot(dx, dy, dz);
117
+ if (dist >= source.radius) continue;
118
+ const shape = pointLightShape(dist, source.radius, falloff);
119
+ if (shape <= 0) continue;
120
+ const seen =
121
+ distance === null
122
+ ? 1
123
+ : softVisibility(distance, px, py, pz, source.x, source.y, source.z, source.sourceRadius);
124
+ if (seen <= 0) continue;
125
+ const lr = source.r * shape * seen;
126
+ const lg = source.g * shape * seen;
127
+ const lb = source.b * shape * seen;
128
+ r += lr;
129
+ g += lg;
130
+ b += lb;
131
+ const bright = 0.2126 * lr + 0.7152 * lg + 0.0722 * lb;
132
+ if (dist > 1e-6 && bright > 0) {
133
+ towardX += (bright * dx) / dist;
134
+ towardY += (bright * dy) / dist;
135
+ towardZ += (bright * dz) / dist;
136
+ weight += bright;
137
+ }
138
+ }
139
+ light[texel] = r;
140
+ light[texel + 1] = g;
141
+ light[texel + 2] = b;
142
+ light[texel + 3] = 1;
143
+ if (weight > 0) {
144
+ direction[texel] = towardX / weight;
145
+ direction[texel + 1] = towardY / weight;
146
+ direction[texel + 2] = towardZ / weight;
147
+ }
148
+ }