@gnsx/three 0.185.15 → 0.186.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 (417) hide show
  1. package/build/three.cjs +10 -82600
  2. package/build/three.core.js +1303 -728
  3. package/build/three.module.js +439 -338
  4. package/build/three.tsl.js +74 -44
  5. package/build/three.webgpu.js +6036 -2184
  6. package/build/three.webgpu.nodes.js +6020 -2184
  7. package/examples/jsm/Addons.js +6 -6
  8. package/examples/jsm/controls/ArcballControls.js +1 -1
  9. package/examples/jsm/controls/FirstPersonControls.js +44 -20
  10. package/examples/jsm/controls/OrbitControls.js +9 -0
  11. package/examples/jsm/controls/TrackballControls.js +109 -4
  12. package/examples/jsm/csm/CSMShadowNode.js +13 -0
  13. package/examples/jsm/exporters/GLTFExporter.js +23 -7
  14. package/examples/jsm/exporters/USDZExporter.js +2 -2
  15. package/examples/jsm/generators/CityGenerator.js +377 -45
  16. package/examples/jsm/generators/ForestGenerator.js +12 -12
  17. package/examples/jsm/generators/TerrainGenerator.js +49 -37
  18. package/examples/jsm/generators/TreeGenerator.js +70 -76
  19. package/examples/jsm/generators/city/BenchGenerator.js +127 -0
  20. package/examples/jsm/generators/city/CarGenerator.js +479 -0
  21. package/examples/jsm/generators/city/CityGeneratorUtils.js +31 -0
  22. package/examples/jsm/generators/city/HydrantGenerator.js +106 -0
  23. package/examples/jsm/generators/city/InstancedMeshGenerator.js +86 -0
  24. package/examples/jsm/generators/city/PersonGenerator.js +447 -0
  25. package/examples/jsm/generators/city/SidewalkGenerator.js +33 -38
  26. package/examples/jsm/generators/city/SkyscraperGenerator.js +448 -48
  27. package/examples/jsm/generators/city/StreetTreeGenerator.js +231 -0
  28. package/examples/jsm/generators/city/StreetlightGenerator.js +100 -0
  29. package/examples/jsm/generators/city/TrafficlightGenerator.js +106 -0
  30. package/examples/jsm/generators/city/TrashcanGenerator.js +100 -0
  31. package/examples/jsm/geometries/BoxLineGeometry.js +1 -1
  32. package/examples/jsm/geometries/ParametricGeometry.js +1 -1
  33. package/examples/jsm/geometries/RoundedBoxGeometry.js +1 -1
  34. package/examples/jsm/gpgpu/CountingSort.js +271 -0
  35. package/examples/jsm/helpers/LightProbeGridHelper.js +60 -239
  36. package/examples/jsm/helpers/LightProbeGridHelperWebGL.js +221 -0
  37. package/examples/jsm/inspector/Extension.js +94 -0
  38. package/examples/jsm/inspector/Inspector.js +145 -41
  39. package/examples/jsm/inspector/RendererInspector.js +100 -52
  40. package/examples/jsm/inspector/extensions/color-grading/ColorGrading.js +2609 -0
  41. package/examples/jsm/inspector/extensions/color-grading/ColorWheel.js +352 -0
  42. package/examples/jsm/inspector/extensions/color-grading/CurveEditor.js +599 -0
  43. package/examples/jsm/inspector/extensions/color-grading/LUT3DStyle.js +1201 -0
  44. package/examples/jsm/inspector/extensions/color-grading/LUTMath.js +788 -0
  45. package/examples/jsm/inspector/extensions/color-grading/modules/BrightnessModule.js +72 -0
  46. package/examples/jsm/inspector/extensions/color-grading/modules/ColorWheelModule.js +112 -0
  47. package/examples/jsm/inspector/extensions/color-grading/modules/ContrastModule.js +82 -0
  48. package/examples/jsm/inspector/extensions/color-grading/modules/CurvesModule.js +109 -0
  49. package/examples/jsm/inspector/extensions/color-grading/modules/ExposureHueModule.js +110 -0
  50. package/examples/jsm/inspector/extensions/color-grading/modules/ExposureModule.js +73 -0
  51. package/examples/jsm/inspector/extensions/color-grading/modules/HueModule.js +78 -0
  52. package/examples/jsm/inspector/extensions/color-grading/modules/ImportedCubeModule.js +89 -0
  53. package/examples/jsm/inspector/extensions/color-grading/modules/Module.js +285 -0
  54. package/examples/jsm/inspector/extensions/color-grading/modules/OutputModule.js +101 -0
  55. package/examples/jsm/inspector/extensions/color-grading/modules/RendererModule.js +225 -0
  56. package/examples/jsm/inspector/extensions/color-grading/modules/SatVibranceModule.js +102 -0
  57. package/examples/jsm/inspector/extensions/color-grading/modules/SaturationModule.js +76 -0
  58. package/examples/jsm/inspector/extensions/color-grading/modules/VibranceModule.js +76 -0
  59. package/examples/jsm/inspector/extensions/color-grading/modules/WhiteBalanceModule.js +86 -0
  60. package/examples/jsm/inspector/extensions/tsl-graph/TSLGraphEditor.js +26 -31
  61. package/examples/jsm/inspector/extensions/tsl-graph/TSLGraphLoader.js +1 -0
  62. package/examples/jsm/inspector/tabs/Memory.js +0 -2
  63. package/examples/jsm/inspector/tabs/Settings.js +4 -0
  64. package/examples/jsm/inspector/tabs/Timeline.js +223 -171
  65. package/examples/jsm/inspector/tabs/Viewer.js +515 -21
  66. package/examples/jsm/inspector/ui/List.js +15 -9
  67. package/examples/jsm/inspector/ui/Profiler.js +170 -14
  68. package/examples/jsm/inspector/ui/Style.js +269 -23
  69. package/examples/jsm/inspector/ui/Tab.js +2 -0
  70. package/examples/jsm/inspector/ui/Values.js +14 -2
  71. package/examples/jsm/interactive/HTMLMesh.js +19 -3
  72. package/examples/jsm/lighting/LightProbeGrid.js +484 -889
  73. package/examples/jsm/lighting/LightProbeGridUtils.js +107 -0
  74. package/examples/jsm/lighting/LightProbeGridWebGL.js +841 -0
  75. package/examples/jsm/lighting/vxgi/VXGIConeTracer.js +194 -0
  76. package/examples/jsm/lighting/vxgi/VXGINode.js +660 -0
  77. package/examples/jsm/lighting/vxgi/VXGISceneCollector.js +478 -0
  78. package/examples/jsm/lighting/vxgi/VXGIVolume.js +1430 -0
  79. package/examples/jsm/lights/SunLight.js +95 -0
  80. package/examples/jsm/lights/SunLightNode.js +56 -0
  81. package/examples/jsm/lights/SunLightShadow.js +319 -0
  82. package/examples/jsm/lights/SunShadowNode.js +312 -0
  83. package/examples/jsm/loaders/3DMLoader.js +150 -49
  84. package/examples/jsm/loaders/3MFLoader.js +202 -1
  85. package/examples/jsm/loaders/FBXLoader.js +81 -5
  86. package/examples/jsm/loaders/GLTFGaussianSplatLoaderExtension.js +330 -0
  87. package/examples/jsm/loaders/GLTFLoader.js +79 -14
  88. package/examples/jsm/loaders/GaussianSplatPLYLoader.js +386 -0
  89. package/examples/jsm/loaders/KSPLATLoader.js +463 -0
  90. package/examples/jsm/loaders/MaterialXLoader.js +111 -1010
  91. package/examples/jsm/loaders/PCDLoader.js +1 -1
  92. package/examples/jsm/loaders/SPLATLoader.js +139 -0
  93. package/examples/jsm/loaders/SPZLoader.js +561 -0
  94. package/examples/jsm/loaders/materialx/MaterialXArchive.js +108 -0
  95. package/examples/jsm/loaders/materialx/MaterialXDocument.js +878 -0
  96. package/examples/jsm/loaders/materialx/MaterialXHextile.js +183 -0
  97. package/examples/jsm/loaders/materialx/MaterialXInterfaceValidation.js +376 -0
  98. package/examples/jsm/loaders/materialx/MaterialXLog.js +75 -0
  99. package/examples/jsm/loaders/materialx/MaterialXNodeInterfaceRegistry.js +4 -0
  100. package/examples/jsm/loaders/materialx/MaterialXNodeLibrary.js +1021 -0
  101. package/examples/jsm/loaders/materialx/MaterialXSurfaceMappings.js +661 -0
  102. package/examples/jsm/loaders/materialx/MaterialXUtils.js +57 -0
  103. package/examples/jsm/loaders/materialx/compile/MaterialXCompileRegistry.js +997 -0
  104. package/examples/jsm/loaders/materialx/parse/MaterialXParser.js +28 -0
  105. package/examples/jsm/loaders/usd/USDAParser.js +15 -2
  106. package/examples/jsm/loaders/usd/USDCParser.js +1 -4
  107. package/examples/jsm/loaders/usd/USDComposer.js +80 -42
  108. package/examples/jsm/materials/WoodNodeMaterial.js +6 -108
  109. package/examples/jsm/misc/RollerCoaster.js +2 -2
  110. package/examples/jsm/misc/SculptGL.LICENSE.txt +21 -0
  111. package/examples/jsm/misc/Sculptor.js +1679 -0
  112. package/examples/jsm/misc/SculptorMesh.js +1504 -0
  113. package/examples/jsm/misc/SculptorTools.js +1180 -0
  114. package/examples/jsm/misc/SculptorUtils.js +345 -0
  115. package/examples/jsm/misc/TubePainter.js +28 -16
  116. package/examples/jsm/misc/VolumeSlice.js +24 -24
  117. package/examples/jsm/modifiers/SimplifyModifier.js +63 -556
  118. package/examples/jsm/objects/GaussianSplat.js +1039 -0
  119. package/examples/jsm/objects/Sky.js +66 -45
  120. package/examples/jsm/objects/SkyMesh.js +70 -51
  121. package/examples/jsm/physics/JoltPhysics.js +1 -1
  122. package/examples/jsm/postprocessing/AfterimagePass.js +4 -2
  123. package/examples/jsm/postprocessing/BloomPass.js +2 -2
  124. package/examples/jsm/postprocessing/GTAOPass.js +1 -1
  125. package/examples/jsm/postprocessing/OutlinePass.js +5 -5
  126. package/examples/jsm/postprocessing/SAOPass.js +1 -1
  127. package/examples/jsm/postprocessing/SSAOPass.js +1 -1
  128. package/examples/jsm/postprocessing/SSRPass.js +4 -2
  129. package/examples/jsm/postprocessing/SavePass.js +1 -1
  130. package/examples/jsm/postprocessing/TAARenderPass.js +1 -1
  131. package/examples/jsm/postprocessing/UnrealBloomPass.js +30 -13
  132. package/examples/jsm/renderers/CSS2DRenderer.js +29 -5
  133. package/examples/jsm/shaders/DepthLimitedBlurShader.js +4 -1
  134. package/examples/jsm/shaders/ExposureShader.js +1 -1
  135. package/examples/jsm/shaders/FilmShader.js +2 -1
  136. package/examples/jsm/transpiler/AST.js +21 -2
  137. package/examples/jsm/transpiler/GLSLDecoder.js +498 -8
  138. package/examples/jsm/transpiler/TSLEncoder.js +82 -10
  139. package/examples/jsm/transpiler/WGSLEncoder.js +33 -4
  140. package/examples/jsm/tsl/WebGLNodesHandler.js +29 -39
  141. package/examples/jsm/tsl/display/AfterImageNode.js +2 -0
  142. package/examples/jsm/tsl/display/AnaglyphPassNode.js +3 -2
  143. package/examples/jsm/tsl/display/BilateralBlurNode.js +5 -2
  144. package/examples/jsm/tsl/display/BloomNode.js +55 -30
  145. package/examples/jsm/tsl/display/DenoiseNode.js +21 -1
  146. package/examples/jsm/tsl/display/DepthOfFieldNode.js +24 -6
  147. package/examples/jsm/tsl/display/DotScreenNode.js +11 -11
  148. package/examples/jsm/tsl/display/FSR1Node.js +8 -4
  149. package/examples/jsm/tsl/display/GTAONode.js +199 -86
  150. package/examples/jsm/tsl/display/GaussianBlurNode.js +65 -52
  151. package/examples/jsm/tsl/display/GodraysNode.js +5 -2
  152. package/examples/jsm/tsl/display/ImportanceSampledEnvironment.js +2 -2
  153. package/examples/jsm/tsl/display/LensflareNode.js +5 -2
  154. package/examples/jsm/tsl/display/OITPassNode.js +352 -0
  155. package/examples/jsm/tsl/display/OutlineNode.js +2 -0
  156. package/examples/jsm/tsl/display/ParallaxBarrierPassNode.js +3 -2
  157. package/examples/jsm/tsl/display/RGBShiftNode.js +9 -9
  158. package/examples/jsm/tsl/display/RecurrentDenoiseNode.js +7 -4
  159. package/examples/jsm/tsl/display/RetroPassNode.js +1 -1
  160. package/examples/jsm/tsl/display/SMAANode.js +22 -61
  161. package/examples/jsm/tsl/display/SSAAPassNode.js +2 -4
  162. package/examples/jsm/tsl/display/SSAONode.js +436 -0
  163. package/examples/jsm/tsl/display/SSGINode.js +5 -2
  164. package/examples/jsm/tsl/display/SSRNode.js +7 -2
  165. package/examples/jsm/tsl/display/SSSNode.js +5 -2
  166. package/examples/jsm/tsl/display/SharpenNode.js +5 -4
  167. package/examples/jsm/tsl/display/StereoCompositePassNode.js +1 -3
  168. package/examples/jsm/tsl/display/StereoPassNode.js +1 -3
  169. package/examples/jsm/tsl/display/TAAUNode.js +22 -125
  170. package/examples/jsm/tsl/display/TRAANode.js +16 -142
  171. package/examples/jsm/tsl/display/TemporalReprojectNode.js +21 -15
  172. package/examples/jsm/tsl/display/depthAwareBlur.js +63 -0
  173. package/examples/jsm/tsl/display/hashBlur.js +3 -2
  174. package/examples/jsm/tsl/lighting/ClusteredLightsNode.js +38 -27
  175. package/examples/jsm/tsl/lighting/LightProbeGridNode.js +136 -0
  176. package/examples/jsm/tsl/math/voronoiNoise.js +112 -0
  177. package/examples/jsm/tsl/shadows/TileShadowNode.js +4 -6
  178. package/examples/jsm/tsl/utils/Raymarching.js +5 -5
  179. package/examples/jsm/tsl/utils/SoftParticles.js +60 -0
  180. package/examples/jsm/tsl/utils/TAAUtils.js +195 -0
  181. package/examples/jsm/utils/BufferGeometryUtils.js +14 -14
  182. package/examples/jsm/utils/GaussianSplatUtils.js +229 -0
  183. package/examples/jsm/utils/WebGLTextureUtils.js +1 -1
  184. package/examples/jsm/utils/WebGPUTextureUtils.js +1 -1
  185. package/examples/jsm/webxr/WebGLXRFallback.js +2 -16
  186. package/examples/jsm/webxr/XRControllerModelFactory.js +16 -4
  187. package/package.json +9 -16
  188. package/src/Three.Core.js +2 -1
  189. package/src/Three.TSL.js +73 -43
  190. package/src/Three.WebGPU.Nodes.js +1 -0
  191. package/src/Three.WebGPU.js +1 -0
  192. package/src/animation/AnimationClip.js +15 -2
  193. package/src/animation/AnimationObjectGroup.js +18 -6
  194. package/src/animation/AnimationUtils.js +26 -0
  195. package/src/animation/KeyframeTrack.js +37 -0
  196. package/src/constants.js +23 -1
  197. package/src/core/BufferAttribute.js +3 -2
  198. package/src/core/BufferGeometry.js +1 -1
  199. package/src/core/InterleavedBuffer.js +5 -1
  200. package/src/core/Object3D.js +40 -12
  201. package/src/core/Raycaster.js +4 -0
  202. package/src/core/RenderTarget.js +99 -9
  203. package/src/core/RenderTarget3D.js +12 -8
  204. package/src/extras/Controls.js +0 -8
  205. package/src/extras/PMREMGenerator.js +99 -205
  206. package/src/geometries/TorusGeometry.js +1 -1
  207. package/src/helpers/ArrowHelper.js +2 -0
  208. package/src/helpers/AxesHelper.js +2 -0
  209. package/src/helpers/Box3Helper.js +2 -0
  210. package/src/helpers/BoxHelper.js +2 -0
  211. package/src/helpers/CameraHelper.js +2 -0
  212. package/src/helpers/DirectionalLightHelper.js +2 -0
  213. package/src/helpers/GridHelper.js +2 -0
  214. package/src/helpers/HemisphereLightHelper.js +2 -0
  215. package/src/helpers/PlaneHelper.js +2 -0
  216. package/src/helpers/PointLightHelper.js +2 -0
  217. package/src/helpers/PolarGridHelper.js +2 -0
  218. package/src/helpers/SkeletonHelper.js +2 -0
  219. package/src/helpers/SpotLightHelper.js +2 -0
  220. package/src/lights/Light.js +0 -10
  221. package/src/lights/LightShadow.js +43 -12
  222. package/src/lights/SpotLightShadow.js +18 -0
  223. package/src/loaders/BufferGeometryLoader.js +5 -0
  224. package/src/loaders/ImageBitmapLoader.js +3 -1
  225. package/src/loaders/ObjectLoader.js +8 -5
  226. package/src/materials/Material.js +80 -57
  227. package/src/materials/MeshPhysicalMaterial.js +32 -0
  228. package/src/materials/nodes/Line2NodeMaterial.js +17 -8
  229. package/src/materials/nodes/MeshPhysicalNodeMaterial.js +42 -5
  230. package/src/materials/nodes/NodeMaterial.js +19 -13
  231. package/src/materials/nodes/PointsNodeMaterial.js +2 -1
  232. package/src/materials/nodes/manager/NodeMaterialObserver.js +282 -55
  233. package/src/math/Frustum.js +9 -0
  234. package/src/math/FrustumArray.js +1 -1
  235. package/src/math/MathUtils.js +5 -3
  236. package/src/math/Matrix4.js +2 -9
  237. package/src/math/Plane.js +30 -1
  238. package/src/math/Quaternion.js +0 -10
  239. package/src/math/Ray.js +95 -45
  240. package/src/math/Triangle.js +0 -2
  241. package/src/math/Vector3.js +2 -2
  242. package/src/math/Vector4.js +0 -4
  243. package/src/math/interpolants/BezierInterpolant.js +36 -23
  244. package/src/nodes/Nodes.js +2 -0
  245. package/src/nodes/TSL.js +11 -3
  246. package/src/nodes/accessors/Batch.js +85 -13
  247. package/src/nodes/accessors/BufferAttributeNode.js +1 -1
  248. package/src/nodes/accessors/BuiltinNode.js +1 -1
  249. package/src/nodes/accessors/Instance.js +19 -18
  250. package/src/nodes/accessors/MaterialNode.js +9 -0
  251. package/src/nodes/accessors/ModelNode.js +85 -9
  252. package/src/nodes/accessors/ReferenceBaseNode.js +1 -70
  253. package/src/nodes/accessors/ReferenceElementNode.js +72 -0
  254. package/src/nodes/accessors/ReferenceNode.js +1 -70
  255. package/src/nodes/accessors/Skinning.js +124 -12
  256. package/src/nodes/accessors/StorageTextureNode.js +7 -3
  257. package/src/nodes/accessors/Texture3DNode.js +1 -1
  258. package/src/nodes/accessors/TextureNode.js +51 -21
  259. package/src/nodes/accessors/TextureSizeNode.js +4 -2
  260. package/src/nodes/accessors/UniformArrayNode.js +30 -5
  261. package/src/nodes/code/CodeNode.js +1 -1
  262. package/src/nodes/code/FunctionNode.js +12 -2
  263. package/src/nodes/core/ArrayNode.js +2 -2
  264. package/src/nodes/core/ContextNode.js +57 -4
  265. package/src/nodes/core/IndexNode.js +69 -4
  266. package/src/nodes/core/InspectorNode.js +2 -2
  267. package/src/nodes/core/MRTNode.js +44 -2
  268. package/src/nodes/core/Node.js +6 -3
  269. package/src/nodes/core/NodeBuilder.js +65 -50
  270. package/src/nodes/core/NodeFrame.js +11 -3
  271. package/src/nodes/core/NodeUtils.js +88 -0
  272. package/src/nodes/core/PropertyNode.js +8 -0
  273. package/src/nodes/core/StackNode.js +0 -1
  274. package/src/nodes/core/StructNode.js +4 -3
  275. package/src/nodes/core/UniformGroupNode.js +3 -2
  276. package/src/nodes/core/UniformNode.js +26 -12
  277. package/src/nodes/core/VarNode.js +0 -1
  278. package/src/nodes/display/BumpMapNode.js +2 -2
  279. package/src/nodes/display/ColorAdjustment.js +1 -1
  280. package/src/nodes/display/PassNode.js +76 -15
  281. package/src/nodes/display/ScreenNode.js +22 -65
  282. package/src/nodes/fog/Fog.js +4 -4
  283. package/src/nodes/functions/BSDF/BRDF_GGX.js +6 -6
  284. package/src/nodes/functions/BSDF/BRDF_Sheen.js +1 -1
  285. package/src/nodes/functions/BSDF/DFGLUT.js +2 -1
  286. package/src/nodes/functions/BSDF/LTC.js +1 -1
  287. package/src/nodes/functions/PhongLightingModel.js +1 -1
  288. package/src/nodes/functions/PhysicalLightingModel.js +83 -25
  289. package/src/nodes/functions/ToonLightingModel.js +2 -2
  290. package/src/nodes/functions/material/getParallaxCorrectNormal.js +3 -3
  291. package/src/nodes/gpgpu/AtomicFunctionNode.js +64 -13
  292. package/src/nodes/gpgpu/BarrierNode.js +13 -4
  293. package/src/nodes/gpgpu/ComputeBuiltinNode.js +1 -1
  294. package/src/nodes/gpgpu/ComputeNode.js +20 -17
  295. package/src/nodes/gpgpu/SubgroupFunctionNode.js +165 -37
  296. package/src/nodes/gpgpu/WorkgroupInfoNode.js +44 -6
  297. package/src/nodes/lighting/EnvironmentNode.js +40 -4
  298. package/src/nodes/lighting/LightsNode.js +20 -4
  299. package/src/nodes/lighting/ShadowBaseNode.js +158 -1
  300. package/src/nodes/lighting/ShadowFilterNode.js +2 -106
  301. package/src/nodes/lighting/ShadowNode.js +22 -91
  302. package/src/nodes/materialx/{lib/mx_hsv.js → MaterialXColor.js} +3 -6
  303. package/src/nodes/materialx/{lib/mx_transform_color.js → MaterialXColorTransform.js} +3 -6
  304. package/src/nodes/materialx/MaterialXCore.js +37 -0
  305. package/src/nodes/materialx/MaterialXNodes.js +64 -72
  306. package/src/nodes/materialx/{lib/mx_noise.js → MaterialXNoise.js} +426 -197
  307. package/src/nodes/math/BitcountNode.js +24 -4
  308. package/src/nodes/math/MathNode.js +4 -4
  309. package/src/nodes/math/MathUtils.js +15 -4
  310. package/src/nodes/math/OperatorNode.js +2 -2
  311. package/src/nodes/math/PackFloatNode.js +29 -1
  312. package/src/nodes/math/Packed4x8IntegerNode.js +433 -0
  313. package/src/nodes/math/UnpackFloatNode.js +30 -2
  314. package/src/nodes/parsers/GLSLNodeFunction.js +1 -1
  315. package/src/nodes/pmrem/PMREMNode.js +1 -1
  316. package/src/nodes/pmrem/PMREMUtils.js +31 -74
  317. package/src/nodes/shapes/Shapes.js +1 -1
  318. package/src/nodes/tsl/TSLCore.js +1 -31
  319. package/src/nodes/utils/EventNode.js +75 -0
  320. package/src/nodes/utils/LoopNode.js +54 -13
  321. package/src/nodes/utils/PostProcessingUtils.js +23 -2
  322. package/src/nodes/utils/RTTNode.js +97 -32
  323. package/src/nodes/utils/ReflectorNode.js +8 -4
  324. package/src/nodes/utils/RotateNode.js +85 -8
  325. package/src/nodes/utils/SpriteUtils.js +58 -23
  326. package/src/objects/BatchedMesh.js +6 -0
  327. package/src/objects/InstancedMesh.js +1 -1
  328. package/src/objects/LOD.js +1 -1
  329. package/src/objects/Line.js +12 -0
  330. package/src/objects/Mesh.js +12 -0
  331. package/src/objects/Points.js +12 -0
  332. package/src/objects/Sprite.js +12 -0
  333. package/src/renderers/WebGLRenderer.js +93 -27
  334. package/src/renderers/common/Animation.js +12 -2
  335. package/src/renderers/common/Backend.js +38 -10
  336. package/src/renderers/common/BindGroup.js +0 -1
  337. package/src/renderers/common/Bindings.js +50 -4
  338. package/src/renderers/common/BundleGroup.js +4 -0
  339. package/src/renderers/common/ClippingContext.js +11 -1
  340. package/src/renderers/common/Color4.js +9 -0
  341. package/src/renderers/common/Constants.js +0 -5
  342. package/src/renderers/common/DirectRenderPipeline.js +233 -0
  343. package/src/renderers/common/Geometries.js +24 -3
  344. package/src/renderers/common/Info.js +3 -3
  345. package/src/renderers/common/InspectorBase.js +37 -8
  346. package/src/renderers/common/Lighting.js +11 -4
  347. package/src/renderers/common/Pipelines.js +6 -4
  348. package/src/renderers/common/QuadMesh.js +16 -2
  349. package/src/renderers/common/RenderContext.js +8 -0
  350. package/src/renderers/common/RenderList.js +53 -19
  351. package/src/renderers/common/RenderLists.js +58 -11
  352. package/src/renderers/common/RenderObject.js +14 -0
  353. package/src/renderers/common/RenderObjects.js +14 -9
  354. package/src/renderers/common/RenderPipeline.js +52 -44
  355. package/src/renderers/common/Renderer.js +226 -55
  356. package/src/renderers/common/Storage3DTexture.js +16 -0
  357. package/src/renderers/common/Textures.js +3 -3
  358. package/src/renderers/common/XRManager.js +79 -72
  359. package/src/renderers/common/extras/PMREMGenerator.js +76 -144
  360. package/src/renderers/common/nodes/NodeManager.js +144 -6
  361. package/src/renderers/common/nodes/NodeStorageBuffer.js +3 -1
  362. package/src/renderers/common/nodes/NodeUniformBuffer.js +3 -1
  363. package/src/renderers/shaders/DFGLUTData.js +2 -1
  364. package/src/renderers/shaders/ShaderChunk/defaultnormal_vertex.glsl.js +4 -2
  365. package/src/renderers/shaders/ShaderChunk/envmap_physical_pars_fragment.glsl.js +48 -0
  366. package/src/renderers/shaders/ShaderChunk/lights_fragment_begin.glsl.js +53 -4
  367. package/src/renderers/shaders/ShaderChunk/lights_fragment_maps.glsl.js +20 -2
  368. package/src/renderers/shaders/ShaderChunk/lights_pars_begin.glsl.js +20 -0
  369. package/src/renderers/shaders/ShaderChunk/lights_physical_fragment.glsl.js +6 -0
  370. package/src/renderers/shaders/ShaderChunk/lights_physical_pars_fragment.glsl.js +61 -57
  371. package/src/renderers/shaders/ShaderChunk/shadowmap_pars_fragment.glsl.js +82 -0
  372. package/src/renderers/shaders/ShaderChunk/shadowmap_pars_vertex.glsl.js +9 -0
  373. package/src/renderers/shaders/ShaderChunk/shadowmap_vertex.glsl.js +8 -1
  374. package/src/renderers/shaders/ShaderChunk/shadowmask_pars_fragment.glsl.js +15 -0
  375. package/src/renderers/shaders/ShaderLib/meshphysical.glsl.js +4 -0
  376. package/src/renderers/shaders/ShaderLib.js +1 -0
  377. package/src/renderers/shaders/UniformsLib.js +16 -0
  378. package/src/renderers/webgl/WebGLAnimation.js +2 -2
  379. package/src/renderers/webgl/WebGLCapabilities.js +2 -2
  380. package/src/renderers/webgl/WebGLLights.js +74 -4
  381. package/src/renderers/webgl/WebGLMaterials.js +6 -0
  382. package/src/renderers/webgl/WebGLOutput.js +36 -24
  383. package/src/renderers/webgl/WebGLProgram.js +3 -0
  384. package/src/renderers/webgl/WebGLPrograms.js +8 -0
  385. package/src/renderers/webgl/WebGLRenderLists.js +6 -10
  386. package/src/renderers/webgl/WebGLShadowMap.js +47 -44
  387. package/src/renderers/webgl/WebGLTextures.js +7 -7
  388. package/src/renderers/webgl-fallback/WebGLBackend.js +153 -45
  389. package/src/renderers/webgl-fallback/nodes/GLSLNodeBuilder.js +83 -29
  390. package/src/renderers/webgl-fallback/utils/WebGLState.js +163 -17
  391. package/src/renderers/webgl-fallback/utils/WebGLTextureUtils.js +32 -5
  392. package/src/renderers/webgl-fallback/utils/WebGLTimestampQueryPool.js +11 -4
  393. package/src/renderers/webgpu/WebGPUBackend.js +513 -145
  394. package/src/renderers/webgpu/nodes/WGSLNodeBuilder.js +99 -20
  395. package/src/renderers/webgpu/nodes/WGSLNodeFunction.js +2 -1
  396. package/src/renderers/webgpu/utils/WebGPUAttributeUtils.js +3 -12
  397. package/src/renderers/webgpu/utils/WebGPUBindingUtils.js +24 -14
  398. package/src/renderers/webgpu/utils/WebGPUPipelineUtils.js +80 -14
  399. package/src/renderers/webgpu/utils/WebGPUTextureUtils.js +62 -7
  400. package/src/renderers/webgpu/utils/WebGPUTimestampQueryPool.js +9 -3
  401. package/src/renderers/webgpu/utils/WebGPUUtils.js +16 -4
  402. package/src/renderers/webxr/WebXRManager.js +25 -2
  403. package/src/scenes/Scene.js +3 -3
  404. package/src/textures/CompressedArrayTexture.js +16 -0
  405. package/src/textures/Data3DTexture.js +16 -0
  406. package/src/textures/DataArrayTexture.js +16 -0
  407. package/src/textures/DepthTexture.js +3 -3
  408. package/src/textures/Texture.js +3 -3
  409. package/src/textures/{Source.js → TextureSource.js} +39 -8
  410. package/build/three.core.min.js +0 -6
  411. package/build/three.module.min.js +0 -6
  412. package/build/three.tsl.min.js +0 -6
  413. package/build/three.webgpu.min.js +0 -6
  414. package/build/three.webgpu.nodes.min.js +0 -6
  415. package/examples/jsm/libs/ecsy.module.js +0 -1792
  416. package/examples/jsm/loaders/LottieLoader.js +0 -130
  417. package/src/nodes/functions/BSDF/BRDF_GGX_Multiscatter.js +0 -52
@@ -1,122 +1,288 @@
1
1
  import {
2
2
  Box3,
3
3
  CubeCamera,
4
+ CubeRenderTarget,
4
5
  FloatType,
5
6
  HalfFloatType,
7
+ Light,
6
8
  LinearFilter,
7
- Mesh,
9
+ MathUtils,
8
10
  NearestFilter,
9
- Object3D,
10
- OrthographicCamera,
11
- PlaneGeometry,
11
+ NodeMaterial,
12
+ QuadMesh,
13
+ RenderTarget,
14
+ RenderTarget3D,
12
15
  RGBAFormat,
13
- Scene,
14
- ShaderMaterial,
15
- Vector3,
16
- Vector4,
17
- WebGL3DRenderTarget,
18
- WebGLCubeRenderTarget,
19
- WebGLRenderTarget
20
- } from '@gnsx/three';
21
- // WITH_GENESYS
22
- import { CubeRenderTarget, Storage3DTexture } from '@gnsx/three/webgpu';
16
+ Vector3
17
+ } from '@gnsx/three/webgpu';
18
+
23
19
  import {
24
- Fn,
25
- If,
26
- Loop,
27
- PI,
28
- convert,
20
+ array,
29
21
  cubeTexture,
30
22
  float,
23
+ Fn,
31
24
  int,
32
- select,
33
- storageTexture,
34
- textureStore,
25
+ ivec2,
26
+ Loop,
27
+ screenCoordinate,
28
+ texture,
35
29
  uniform,
36
- uint,
37
- uvec3,
38
30
  vec3,
39
31
  vec4
40
32
  } from '@gnsx/three/tsl';
41
- // !WITH_GENESYS
42
33
 
43
- // Shared fullscreen-quad scene / camera
44
- let _scene = null;
45
- let _camera = null;
46
- let _mesh = null;
34
+ import { LightProbeGridNode, ATLAS_PADDING } from '../tsl/lighting/LightProbeGridNode.js';
35
+ import { replaceSunLights, restoreSunLights } from './LightProbeGridUtils.js';
47
36
 
48
- // SH projection material (depends on cubemapSize)
49
- let _shMaterial = null;
50
- let _lastCubemapSize = 0;
37
+ // Shared fullscreen-quad for the bake passes.
38
+ const _quad = /*@__PURE__*/ new QuadMesh();
51
39
 
52
- // Repack materials (one per output sub-volume / texture index)
40
+ // Reusable temp objects.
41
+ const _position = /*@__PURE__*/ new Vector3();
42
+ const _size = /*@__PURE__*/ new Vector3();
43
+ const _copyRegion = /*@__PURE__*/ new Box3();
44
+
45
+ // Bake materials, shared across grids so the shaders compile once, not per bake.
46
+ let _shMaterial = null;
47
+ let _shSampleCount = - 1;
48
+ let _cubeNode = null;
49
+ let _batchNode = null;
50
+ let _resolutionUniform = null;
51
+ let _sliceZUniform = null;
53
52
  let _repackMaterials = null;
54
53
 
55
- // Cached bake resources
54
+ // Bake render targets, pooled by size so rebakes don't churn allocations.
56
55
  let _cubeRenderTarget = null;
57
56
  let _cubeCamera = null;
58
- let _cachedCubemapSize = 0;
59
- let _cachedNear = 0;
60
- let _cachedFar = 0;
61
-
62
- // WITH_GENESYS
63
- // Cached WebGPU bake resources. The WebGPU path projects cubemaps on the GPU
64
- // via TSL compute and writes directly into a Storage3DTexture atlas.
65
- let _webgpuCubeRenderTarget = null;
66
- let _webgpuCubeCamera = null;
67
- let _webgpuCachedCubemapSize = 0;
68
- let _webgpuCachedNear = 0;
69
- let _webgpuCachedFar = 0;
70
- let _webgpuComputeNode = null;
71
- let _webgpuComputeCubemapSize = 0;
72
- let _webgpuComputeAtlas = null;
73
- let _webgpuComputeEnvMap = null;
74
- let _webgpuProbeUniforms = null;
75
- // !WITH_GENESYS
76
-
77
- // Cached batch render target
57
+ let _cubeKey = '';
78
58
  let _batchTarget = null;
79
- let _batchTargetProbes = 0;
59
+ let _batchProbes = - 1;
80
60
 
81
- // Reusable temp objects
82
- const _position = /*@__PURE__*/ new Vector3();
83
- const _size = /*@__PURE__*/ new Vector3();
84
- const _currentViewport = /*@__PURE__*/ new Vector4();
85
- const _currentScissor = /*@__PURE__*/ new Vector4();
61
+ // Golden-angle increment for the equal-area Fibonacci sphere.
62
+ const GOLDEN_ANGLE = Math.PI * ( 3.0 - Math.sqrt( 5.0 ) );
86
63
 
87
- // Number of padding texels added at each boundary of every sub-volume in the atlas.
88
- const ATLAS_PADDING = 1;
64
+ /**
65
+ * Returns the output node for the spherical-harmonic projection pass. Each
66
+ * fragment of the 9-wide batch row computes a single SH coefficient by
67
+ * integrating the captured cubemap over an equal-area Fibonacci sphere,
68
+ * selecting the basis function for its column. Sampling the cubemap by world
69
+ * direction keeps the projection independent of the cube face layout.
70
+ *
71
+ * @private
72
+ * @param {Node} cube - The captured environment cubemap texture node.
73
+ * @param {number} sampleCount - Number of directions to integrate.
74
+ * @return {Node<vec4>} The projected coefficient.
75
+ */
76
+ function projectSHNode( cube, sampleCount ) {
77
+
78
+ return Fn( () => {
79
+
80
+ const coefIndex = int( screenCoordinate.x ).toVar();
81
+ const accum = vec3( 0.0 ).toVar();
82
+
83
+ Loop( sampleCount, ( { i } ) => {
84
+
85
+ const fi = float( i );
86
+
87
+ // Equal-area Fibonacci sphere direction.
88
+ const z = float( 1.0 ).sub( fi.mul( 2.0 ).add( 1.0 ).div( sampleCount ) );
89
+ const r = z.mul( z ).oneMinus().max( 0.0 ).sqrt();
90
+ const phi = fi.mul( GOLDEN_ANGLE );
91
+ const dir = vec3( r.mul( phi.cos() ), z, r.mul( phi.sin() ) ).toVar();
92
+
93
+ const radiance = cube.sample( dir ).level( 0 ).rgb;
94
+
95
+ // The L2 SH basis function for this fragment's coefficient.
96
+ const x = dir.x, y = dir.y, zc = dir.z;
97
+ const basis = array( [
98
+ float( 0.282095 ),
99
+ y.mul( 0.488603 ),
100
+ zc.mul( 0.488603 ),
101
+ x.mul( 0.488603 ),
102
+ x.mul( y ).mul( 1.092548 ),
103
+ y.mul( zc ).mul( 1.092548 ),
104
+ zc.mul( zc ).mul( 3.0 ).sub( 1.0 ).mul( 0.315392 ),
105
+ x.mul( zc ).mul( 1.092548 ),
106
+ x.mul( x ).sub( y.mul( y ) ).mul( 0.546274 )
107
+ ] ).element( coefIndex );
108
+
109
+ accum.addAssign( radiance.mul( basis ) );
110
+
111
+ } );
112
+
113
+ // Equal-area quadrature: each direction covers 4*PI / sampleCount.
114
+ const norm = float( 4.0 * Math.PI / sampleCount );
115
+
116
+ return vec4( accum.mul( norm ), 1.0 );
117
+
118
+ } )();
119
+
120
+ }
89
121
 
90
122
  /**
91
- * A 3D grid of L2 Spherical Harmonic irradiance probes that provides
92
- * position-dependent diffuse global illumination.
123
+ * Returns the repack output node for one of the seven SH textures. It reads the
124
+ * 9 projected coefficients from the batch texture for the probe at the current
125
+ * texel and packs the four floats stored by this texture index.
93
126
  *
94
- * Note that this class can only be used with {@link WebGLRenderer}.
95
- * A version for {@link WebGPURenderer} will be added at a later point.
127
+ * @private
128
+ * @param {Node} batch - The batch texture node holding projected coefficients.
129
+ * @param {number} textureIndex - The output texture index (0–6).
130
+ * @param {Node<vec3>} resolution - The probe grid resolution uniform.
131
+ * @param {Node<int>} sliceZ - The current Z slice being written.
132
+ * @return {Node<vec4>} The packed texel.
133
+ */
134
+ function repackNode( batch, textureIndex, resolution, sliceZ ) {
135
+
136
+ return Fn( () => {
137
+
138
+ const ix = int( screenCoordinate.x );
139
+ const iy = int( screenCoordinate.y );
140
+
141
+ const nx = int( resolution.x );
142
+ const ny = int( resolution.y );
143
+ const probeIndex = ix.add( iy.mul( nx ) ).add( sliceZ.mul( nx ).mul( ny ) );
144
+
145
+ const c0 = batch.load( ivec2( 0, probeIndex ) );
146
+ const c1 = batch.load( ivec2( 1, probeIndex ) );
147
+ const c2 = batch.load( ivec2( 2, probeIndex ) );
148
+ const c3 = batch.load( ivec2( 3, probeIndex ) );
149
+ const c4 = batch.load( ivec2( 4, probeIndex ) );
150
+ const c5 = batch.load( ivec2( 5, probeIndex ) );
151
+ const c6 = batch.load( ivec2( 6, probeIndex ) );
152
+ const c7 = batch.load( ivec2( 7, probeIndex ) );
153
+ const c8 = batch.load( ivec2( 8, probeIndex ) );
154
+
155
+ let packed;
156
+
157
+ switch ( textureIndex ) {
158
+
159
+ case 0: packed = vec4( c0.xyz, c1.x ); break;
160
+ case 1: packed = vec4( c1.yz, c2.xy ); break;
161
+ case 2: packed = vec4( c2.z, c3.xyz ); break;
162
+ case 3: packed = vec4( c4.xyz, c5.x ); break;
163
+ case 4: packed = vec4( c5.yz, c6.xy ); break;
164
+ case 5: packed = vec4( c6.z, c7.xyz ); break;
165
+ default: packed = vec4( c8.xyz, 0.0 ); break;
166
+
167
+ }
168
+
169
+ return packed;
170
+
171
+ } )();
172
+
173
+ }
174
+
175
+ /**
176
+ * Lazily pools the shared cube and batch render targets, recreating them only
177
+ * when their dimensions change.
96
178
  *
97
- * All seven packed SH sub-volumes are stored in a **single** RGBA
98
- * `WebGL3DRenderTarget` using a texture-atlas layout along the Z axis.
99
- * Each sub-volume occupies `( nz + 2 )` atlas slices: one padding slice at
100
- * each end (a copy of the nearest edge data slice) to prevent color bleeding
101
- * when the hardware trilinear filter reads across a sub-volume boundary.
179
+ * @private
180
+ * @param {number} cubemapSize - Resolution of each cubemap face.
181
+ * @param {number} near - Cube camera near plane.
182
+ * @param {number} far - Cube camera far plane.
183
+ * @param {number} totalProbes - Number of probes (batch target height).
184
+ */
185
+ function ensureBakeTargets( cubemapSize, near, far, totalProbes ) {
186
+
187
+ const cubeKey = `${ cubemapSize },${ near },${ far }`;
188
+
189
+ if ( _cubeRenderTarget === null || _cubeKey !== cubeKey ) {
190
+
191
+ if ( _cubeRenderTarget !== null ) _cubeRenderTarget.dispose();
192
+
193
+ _cubeRenderTarget = new CubeRenderTarget( cubemapSize, { type: HalfFloatType, generateMipmaps: false } );
194
+ _cubeCamera = new CubeCamera( near, far, _cubeRenderTarget );
195
+ _cubeKey = cubeKey;
196
+
197
+ }
198
+
199
+ if ( _batchTarget === null || _batchProbes !== totalProbes ) {
200
+
201
+ if ( _batchTarget !== null ) _batchTarget.dispose();
202
+
203
+ _batchTarget = new RenderTarget( 9, totalProbes, {
204
+ type: FloatType,
205
+ format: RGBAFormat,
206
+ minFilter: NearestFilter,
207
+ magFilter: NearestFilter,
208
+ depthBuffer: false
209
+ } );
210
+
211
+ _batchProbes = totalProbes;
212
+
213
+ }
214
+
215
+ }
216
+
217
+ /**
218
+ * Lazily builds the shared bake materials and rebinds them to the current
219
+ * cube/batch textures. The SH projection material is rebuilt only when the
220
+ * sample count changes; the repack materials are static.
102
221
  *
103
- * Atlas layout (nz = resolution.z, PADDING = 1):
104
- * ```
105
- * slice 0 : padding (copy of sub-volume 0, data slice 0)
106
- * slices 1 to nz : sub-volume 0 data
107
- * slice nz + 1 : padding (copy of sub-volume 0, data slice nz-1)
108
- * slice nz + 2 : padding (copy of sub-volume 1, data slice 0)
109
- * slices nz+3 to 2*nz+2 : sub-volume 1 data
110
- * ...
111
- * ```
112
- * Total atlas depth = `7 * ( nz + 2 )`.
222
+ * @private
223
+ * @param {number} sampleCount - Number of directions integrated by the projection.
224
+ * @param {CubeTexture} cubeMap - The current cube render target texture.
225
+ * @param {Texture} batchMap - The current batch render target texture.
226
+ */
227
+ function ensureBakeMaterials( sampleCount, cubeMap, batchMap ) {
228
+
229
+ if ( _repackMaterials === null ) {
230
+
231
+ _cubeNode = cubeTexture( cubeMap );
232
+ _batchNode = texture( batchMap );
233
+ _resolutionUniform = uniform( new Vector3() );
234
+ _sliceZUniform = uniform( 0, 'int' );
235
+ _repackMaterials = [];
236
+
237
+ for ( let t = 0; t < 7; t ++ ) {
238
+
239
+ const material = new NodeMaterial();
240
+ material.outputNode = repackNode( _batchNode, t, _resolutionUniform, _sliceZUniform );
241
+ material.depthTest = false;
242
+ material.depthWrite = false;
243
+ _repackMaterials.push( material );
244
+
245
+ }
246
+
247
+ } else {
248
+
249
+ _cubeNode.value = cubeMap;
250
+ _batchNode.value = batchMap;
251
+
252
+ }
253
+
254
+ if ( _shMaterial === null || _shSampleCount !== sampleCount ) {
255
+
256
+ if ( _shMaterial !== null ) _shMaterial.dispose();
257
+
258
+ _shMaterial = new NodeMaterial();
259
+ _shMaterial.outputNode = projectSHNode( _cubeNode, sampleCount );
260
+ _shMaterial.depthTest = false;
261
+ _shMaterial.depthWrite = false;
262
+ _shSampleCount = sampleCount;
263
+
264
+ }
265
+
266
+ }
267
+
268
+ /**
269
+ * A 3D grid of L2 Spherical Harmonic irradiance probes that provides
270
+ * position-dependent diffuse global illumination.
271
+ *
272
+ * This is the {@link WebGPURenderer} version of `LightProbeGrid`. The grid is a
273
+ * {@link Light}, so adding it to the scene applies its baked irradiance to every
274
+ * lit node material automatically. When using {@link WebGLRenderer}, import the
275
+ * grid from `LightProbeGridWebGL.js` instead.
113
276
  *
114
- * Baking is fully GPU-resident: cubemap rendering, SH projection, and
115
- * texture packing all happen on the GPU with zero CPU readback.
277
+ * The baked data is stored in a single RGBA `RenderTarget3D` atlas that packs
278
+ * the nine L2 SH coefficients into seven sub-volumes stacked along Z. Baking is
279
+ * fully GPU-resident: cubemap rendering, SH projection, and texture packing all
280
+ * happen on the GPU with zero CPU readback.
116
281
  *
282
+ * @augments Light
117
283
  * @three_import import { LightProbeGrid } from 'three/addons/lighting/LightProbeGrid.js';
118
284
  */
119
- class LightProbeGrid extends Object3D {
285
+ class LightProbeGrid extends Light {
120
286
 
121
287
  /**
122
288
  * Constructs a new irradiance probe grid.
@@ -132,7 +298,7 @@ class LightProbeGrid extends Object3D {
132
298
  */
133
299
  constructor( width = 1, height = 1, depth = 1, widthProbes, heightProbes, depthProbes ) {
134
300
 
135
- super();
301
+ super( 0xffffff, 1 );
136
302
 
137
303
  /**
138
304
  * This flag can be used for type testing.
@@ -143,6 +309,8 @@ class LightProbeGrid extends Object3D {
143
309
  */
144
310
  this.isLightProbeGrid = true;
145
311
 
312
+ this.type = 'LightProbeGrid';
313
+
146
314
  /**
147
315
  * The full width of the volume along X.
148
316
  *
@@ -184,48 +352,37 @@ class LightProbeGrid extends Object3D {
184
352
  this.boundingBox = new Box3();
185
353
 
186
354
  /**
187
- * The single RGBA atlas 3D texture storing all seven packed SH sub-volumes.
188
- *
189
- * @type {?Data3DTexture}
190
- * @default null
191
- */
192
- this.texture = null;
193
-
194
- /**
195
- * Internal render target for GPU-resident baking.
355
+ * Distance in world units over which the grid contribution fades out
356
+ * past the volume boundary. `0` applies the contribution everywhere
357
+ * (clamped), which matches a single-volume setup. Use a small positive
358
+ * value to blend multiple overlapping grids.
196
359
  *
197
- * @private
198
- * @type {?WebGL3DRenderTarget}
199
- * @default null
360
+ * @type {number}
361
+ * @default 0
200
362
  */
201
- this._renderTarget = null;
363
+ this.falloff = 0;
202
364
 
203
365
  /**
204
- * Array of cube textures captured during baking, only populated
205
- * when `retainCubemaps` option is true in {@link LightProbeGrid#bake}.
366
+ * The single RGBA atlas 3D texture storing all seven packed SH
367
+ * sub-volumes stacked along Z.
206
368
  *
207
- * @type {?CubeTexture[]}
369
+ * @type {?Data3DTexture}
208
370
  * @default null
209
371
  */
210
- this.cubeTextures = null;
372
+ this.texture = null;
211
373
 
212
374
  /**
213
- * Render targets backing retained cube textures.
375
+ * Internal render target for GPU-resident baking.
214
376
  *
215
377
  * @private
216
- * @type {?Array<WebGLCubeRenderTarget|CubeRenderTarget>}
378
+ * @type {?RenderTarget3D}
217
379
  * @default null
218
380
  */
219
- this._cubeRenderTargets = null;
381
+ this._renderTarget = null;
220
382
 
221
- /**
222
- * Whether retained cube render targets are WebGPU render targets.
223
- *
224
- * @private
225
- * @type {?boolean}
226
- * @default null
227
- */
228
- this._cubeRenderTargetsWebGPU = null;
383
+ // Indirect captures read a snapshot while the live atlas is updated in place.
384
+ this._bounceGrid = null;
385
+ this._bouncePass = - 1;
229
386
 
230
387
  this.updateBoundingBox();
231
388
 
@@ -242,11 +399,11 @@ class LightProbeGrid extends Object3D {
242
399
  */
243
400
  getProbePosition( ix, iy, iz, target ) {
244
401
 
245
- // const pos = this.position;
246
402
  // WITH_GENESYS
247
403
  const pos = _position;
248
404
  this.getWorldPosition( pos );
249
405
  // !WITH_GENESYS
406
+ // const pos = this.position;
250
407
  const res = this.resolution;
251
408
  const w = this.width, h = this.height, d = this.depth;
252
409
 
@@ -278,411 +435,346 @@ class LightProbeGrid extends Object3D {
278
435
  }
279
436
 
280
437
  /**
281
- * Bakes all probes by rendering cubemaps at each probe position
282
- * and projecting to L2 SH. Optionally iterates additional passes to
283
- * capture indirect bounces - each extra pass samples the previous pass's
284
- * atlas as indirect light, so a grid added to the scene before baking
285
- * accumulates one bounce per extra pass.
438
+ * Bakes probes by rendering cubemaps at each probe position and
439
+ * projecting to L2 SH. Optionally iterates additional passes to capture
440
+ * indirect bounces: each extra pass samples the previous pass's data as
441
+ * indirect light, accumulating one bounce per extra pass.
442
+ *
443
+ * Use `start` and `count` to bake a range and publish its cells immediately.
444
+ * Indices advance along X, then Z, then Y, filling horizontal layers from bottom
445
+ * to top. For incremental indirect bounces, finish the whole grid for `pass: 0`,
446
+ * then repeat with `pass: 1`, etc. Start each pass at index 0 to snapshot the
447
+ * previous pass before updating its cells.
286
448
  *
287
- * @param {WebGLRenderer} renderer - The renderer.
449
+ * Shadow-casting instances of `SunLight` are temporarily replaced with
450
+ * equivalent directional lights, since their view-fitted shadow cascades
451
+ * cannot be frozen across probe renders.
452
+ *
453
+ * @param {WebGPURenderer} renderer - The renderer.
288
454
  * @param {Scene} scene - The scene to render.
289
455
  * @param {Object} [options] - Bake options.
290
456
  * @param {number} [options.cubemapSize=8] - Resolution of each cubemap face.
291
457
  * @param {number} [options.near=0.1] - Near plane for the cube camera.
292
458
  * @param {number} [options.far=100] - Far plane for the cube camera.
293
- * @param {number} [options.bounces=0] - Additional bounce passes after the initial direct pass.
294
- * @param {boolean} [options.retainCubemaps=false] - Whether to retain the captured cube maps for visualization.
295
- * @return {Promise<void>} Resolves when baking has completed.
459
+ * @param {number} [options.bounces=0] - Additional bounce passes. Only available when baking the whole grid.
460
+ * @param {number} [options.sampleCount=512] - Directions integrated when projecting each cubemap to SH.
461
+ * @param {number} [options.start=0] - Index of the first probe to bake.
462
+ * @param {number} [options.count] - Number of probes to bake. Defaults to the remaining probes.
463
+ * @param {number} [options.pass=0] - Starting pass. Zero captures direct light; later passes sample the previous pass. Ranged calls require `bounces: 0`.
296
464
  */
297
- async bake( renderer, scene, options = {} ) {
465
+ bake( renderer, scene, options = {} ) {
466
+
467
+ // The bake is node based, so it needs a WebGPURenderer.
468
+ if ( renderer.isWebGPURenderer !== true ) {
298
469
 
299
- const wasVisible = this.visible; // WITH_GENESYS
300
- const retainCubemaps = options.retainCubemaps === true;
470
+ throw new Error( 'THREE.LightProbeGrid: .bake() requires a WebGPURenderer. For WebGLRenderer, use LightProbeGridWebGL.' );
301
471
 
302
- // Prevent feedback: temporarily hide the volume during baking
303
- this.visible = false;
472
+ }
304
473
 
305
- if ( ! retainCubemaps ) this._disposeRetainedCubemaps();
474
+ // The bake issues GPU work immediately, so the renderer must be ready.
475
+ if ( renderer.initialized === false ) {
306
476
 
307
- // WITH_GENESYS
308
- if ( renderer.isWebGPURenderer === true ) {
477
+ throw new Error( 'THREE.LightProbeGrid: .bake() called before the renderer is initialized. Use "await renderer.init();" first.' );
309
478
 
310
- await this._bakeWebGPU( renderer, scene, options );
479
+ }
311
480
 
312
- } else { // !WITH_GENESYS
481
+ // Register the light node with this renderer (idempotent).
482
+ if ( renderer.library.getLightNodeClass( LightProbeGrid ) === null ) {
313
483
 
314
- const { bounces = 0 } = options;
315
- const { cubeRenderTarget, cubeCamera } = _ensureBakeResources( options );
484
+ renderer.library.addLight( LightProbeGridNode, LightProbeGrid );
316
485
 
317
- this._ensureTextures();
318
- this.updateBoundingBox();
486
+ }
319
487
 
320
488
  const res = this.resolution;
321
489
  const totalProbes = res.x * res.y * res.z;
490
+ const {
491
+ cubemapSize = 8,
492
+ near = 0.1,
493
+ far = 100,
494
+ bounces = 0,
495
+ sampleCount = 512,
496
+ start = 0,
497
+ count = totalProbes - start,
498
+ pass: firstPass = 0
499
+ } = options;
500
+ const end = start + count;
501
+
502
+ if ( ! Number.isInteger( start ) || ! Number.isInteger( count ) || start < 0 || count < 0 || end > totalProbes ) {
503
+
504
+ throw new RangeError( 'THREE.LightProbeGrid: Invalid probe range.' );
322
505
 
323
- // Batch render target for SH coefficients: 9 pixels wide, one row per probe
324
- const batchTarget = _ensureBatchTarget( totalProbes );
325
-
326
- // Save renderer state
327
- const currentRenderTarget = renderer.getRenderTarget();
328
- renderer.getViewport( _currentViewport );
329
- renderer.getScissor( _currentScissor );
330
- const currentScissorTest = renderer.getScissorTest();
506
+ }
331
507
 
332
- // Scene is static across the bake - update once and disable per-render auto updates.
333
- const currentMatrixWorldAutoUpdate = scene.matrixWorldAutoUpdate;
334
- if ( currentMatrixWorldAutoUpdate === true ) {
508
+ if ( ! Number.isInteger( firstPass ) || firstPass < 0 || ! Number.isInteger( bounces ) || bounces < 0 ) {
335
509
 
336
- scene.updateMatrixWorld( true );
337
- scene.matrixWorldAutoUpdate = false;
510
+ throw new RangeError( 'THREE.LightProbeGrid: Pass and bounce counts must be non-negative integers.' );
338
511
 
339
512
  }
340
513
 
341
- // Disable shadow map auto-update across all passes - lights don't move.
342
- // Force a single shadow update on the first render so maps are initialized.
343
- const currentShadowAutoUpdate = renderer.shadowMap.autoUpdate;
344
- renderer.shadowMap.autoUpdate = false;
345
- renderer.shadowMap.needsUpdate = true;
514
+ if ( bounces > 0 && count !== totalProbes ) {
346
515
 
347
- _ensureRepackResources();
516
+ throw new RangeError( 'THREE.LightProbeGrid: For ranged baking, use pass instead of bounces.' );
348
517
 
349
- const paddedSlices = res.z + 2 * ATLAS_PADDING;
350
- const rt = this._renderTarget;
518
+ }
351
519
 
352
- // const t0 = performance.now();
520
+ if ( count === 0 ) return;
353
521
 
354
- // Pass 0 captures direct light only (grid hidden, so probesSH is not sampled
355
- // the atlas at this point may be uninitialized or hold a prior bake).
356
- // Each subsequent pass keeps the grid visible so the cube cameras read the
357
- // previous pass's atlas as indirect light, accumulating one bounce per pass.
358
- // Phase 1 writes to the batch target and Phase 2 only swaps it into the atlas
359
- // at the very end of each pass, which gives an implicit ping-pong for free.
522
+ if ( firstPass > 0 && start > 0 && this._bouncePass !== firstPass ) {
360
523
 
361
- for ( let pass = 0; pass <= bounces; pass ++ ) {
524
+ throw new Error( 'THREE.LightProbeGrid: Start each indirect pass at probe 0.' );
362
525
 
363
- this.visible = pass > 0;
526
+ }
364
527
 
365
- // Clear pooled batch target so skipped probes read as zero
366
- batchTarget.scissorTest = false;
367
- batchTarget.viewport.set( 0, 0, 9, totalProbes );
368
- renderer.setRenderTarget( batchTarget );
369
- renderer.clear();
528
+ this._ensureTextures();
529
+ this.updateBoundingBox();
370
530
 
371
- // Phase 1: Render cubemaps and project to SH into batch target
372
- // Note: set viewport/scissor on the render target directly to avoid pixel ratio scaling
373
- batchTarget.scissorTest = true;
531
+ // Bind the pooled bake resources to the current textures.
374
532
 
375
- // Initialize cube texture/render target arrays if retaining
376
- if ( retainCubemaps ) {
533
+ ensureBakeTargets( cubemapSize, near, far, totalProbes );
534
+ ensureBakeMaterials( sampleCount, _cubeRenderTarget.texture, _batchTarget.texture );
535
+ _resolutionUniform.value.copy( res );
377
536
 
378
- if ( this.cubeTextures === null || this.cubeTextures.length !== totalProbes ) {
537
+ // Save renderer / scene state to restore after the bake.
379
538
 
380
- this.cubeTextures = new Array( totalProbes );
539
+ const currentRenderTarget = renderer.getRenderTarget();
540
+ const currentActiveCubeFace = renderer.getActiveCubeFace();
541
+ const currentActiveMipmapLevel = renderer.getActiveMipmapLevel();
542
+ const currentAutoClear = renderer.autoClear;
543
+ const currentXrEnabled = renderer.xr.enabled;
544
+ const currentInspectorEnabled = renderer.inspector.enabled;
545
+ const currentMatrixWorldAutoUpdate = scene.matrixWorldAutoUpdate;
546
+ const currentVisible = this.visible;
547
+ const renderTarget = this._renderTarget;
548
+ const currentViewport = renderTarget.viewport.clone();
549
+ const shadowStates = [];
550
+ let replacedSunLights = null;
381
551
 
382
- }
552
+ try {
383
553
 
384
- if ( this._cubeRenderTargets === null || this._cubeRenderTargets.length !== totalProbes ) {
554
+ renderer.inspector.enabled = false;
555
+ this.visible = false;
385
556
 
386
- this._disposeRetainedCubemaps();
387
- this.cubeTextures = new Array( totalProbes );
557
+ // Scene is static during the bake: update once, disable auto-update.
388
558
 
389
- this._cubeRenderTargets = new Array( totalProbes );
559
+ if ( currentMatrixWorldAutoUpdate === true ) {
390
560
 
391
- }
561
+ scene.updateMatrixWorld( true );
562
+ scene.matrixWorldAutoUpdate = false;
392
563
 
393
564
  }
394
565
 
395
- for ( let iz = 0; iz < res.z; iz ++ ) {
566
+ replacedSunLights = replaceSunLights( scene );
396
567
 
397
- for ( let iy = 0; iy < res.y; iy ++ ) {
568
+ // Render each shadow map once, not once per cube face.
398
569
 
399
- for ( let ix = 0; ix < res.x; ix ++ ) {
570
+ scene.traverse( ( object ) => {
400
571
 
401
- const probeIndex = ix + iy * res.x + iz * res.x * res.y;
572
+ if ( object.isLight && object.castShadow && object.shadow ) {
402
573
 
403
- this.getProbePosition( ix, iy, iz, _position );
404
-
405
- let envMapTexture = cubeRenderTarget.texture;
406
-
407
- if ( retainCubemaps ) {
408
-
409
- const retainedTarget = this._ensureRetainedCubeRenderTarget( probeIndex, options );
410
- const retainedCamera = new CubeCamera(
411
- options.near !== undefined ? options.near : 0.1,
412
- options.far !== undefined ? options.far : 100,
413
- retainedTarget
414
- );
415
-
416
- retainedCamera.position.copy( _position );
417
- retainedCamera.update( renderer, scene );
418
-
419
- envMapTexture = retainedTarget.texture;
420
- this.cubeTextures[ probeIndex ] = retainedTarget.texture;
421
-
422
- } else {
423
-
424
- cubeCamera.position.copy( _position );
425
- cubeCamera.update( renderer, scene );
426
-
427
- }
428
-
429
- // SH projection
430
- _shMaterial.uniforms.envMap.value = envMapTexture;
431
- _mesh.material = _shMaterial;
432
- batchTarget.viewport.set( 0, probeIndex, 9, 1 );
433
- batchTarget.scissor.set( 0, probeIndex, 9, 1 );
434
- renderer.setRenderTarget( batchTarget );
435
- renderer.render( _scene, _camera );
574
+ const shadow = object.shadow;
575
+ shadowStates.push( { shadow, autoUpdate: shadow.autoUpdate } );
576
+ shadow.autoUpdate = false;
577
+ shadow.needsUpdate = true;
436
578
 
437
579
  }
438
580
 
439
- }
440
-
441
- }
442
-
443
- // Phase 2: Repack SH data from batch target into the atlas 3D texture on the GPU.
444
- //
445
- // For each of the 7 packed sub-volumes (texture index t) we write:
446
- // - A leading padding slice (copy of data slice iz = 0)
447
- // - All nz data slices (iz = 0 to nz-1)
448
- // - A trailing padding slice (copy of data slice iz = nz-1)
449
- //
450
- // In the atlas the slices for sub-volume t occupy the range:
451
- // [ t * paddedSlices, t * paddedSlices + paddedSlices - 1 ]
452
- // where paddedSlices = nz + 2 * ATLAS_PADDING.
453
-
454
- rt.scissorTest = false;
455
- rt.viewport.set( 0, 0, res.x, res.y );
456
-
457
- for ( let t = 0; t < 7; t ++ ) {
458
-
459
- _repackMaterials[ t ].uniforms.batchTexture.value = batchTarget.texture;
460
- _repackMaterials[ t ].uniforms.resolution.value.copy( res );
581
+ } );
461
582
 
462
- // Write data slices
463
- for ( let iz = 0; iz < res.z; iz ++ ) {
583
+ for ( let pass = firstPass; pass <= firstPass + bounces; pass ++ ) {
464
584
 
465
- _repackMaterials[ t ].uniforms.sliceZ.value = iz;
466
- _mesh.material = _repackMaterials[ t ];
467
- renderer.setRenderTarget( rt, t * paddedSlices + ATLAS_PADDING + iz );
468
- renderer.render( _scene, _camera );
585
+ this._updateBounceGrid( renderer, scene, pass, start );
586
+ this._captureProbes( renderer, scene, start, end );
587
+ this._repackProbes( renderer, start, end );
469
588
 
470
589
  }
471
590
 
472
- // Leading padding: copy of data slice iz = 0
473
- _repackMaterials[ t ].uniforms.sliceZ.value = 0;
474
- _mesh.material = _repackMaterials[ t ];
475
- renderer.setRenderTarget( rt, t * paddedSlices );
476
- renderer.render( _scene, _camera );
591
+ } finally {
477
592
 
478
- // Trailing padding: copy of data slice iz = nz - 1
479
- _repackMaterials[ t ].uniforms.sliceZ.value = res.z - 1;
480
- _mesh.material = _repackMaterials[ t ];
481
- renderer.setRenderTarget( rt, t * paddedSlices + ATLAS_PADDING + res.z );
482
- renderer.render( _scene, _camera );
593
+ // Restore renderer / scene state (pooled targets and materials kept).
483
594
 
484
- }
485
-
486
- }
595
+ renderTarget.viewport.copy( currentViewport );
596
+ renderer.setRenderTarget( currentRenderTarget, currentActiveCubeFace, currentActiveMipmapLevel );
597
+ renderer.autoClear = currentAutoClear;
598
+ renderer.xr.enabled = currentXrEnabled;
599
+ scene.matrixWorldAutoUpdate = currentMatrixWorldAutoUpdate;
487
600
 
488
- renderer.shadowMap.autoUpdate = currentShadowAutoUpdate;
601
+ for ( const { shadow, autoUpdate } of shadowStates ) shadow.autoUpdate = autoUpdate;
489
602
 
490
- // Restore renderer state
491
- renderer.setRenderTarget( currentRenderTarget );
492
- renderer.setViewport( _currentViewport );
493
- renderer.setScissor( _currentScissor );
494
- renderer.setScissorTest( currentScissorTest );
603
+ if ( replacedSunLights !== null ) restoreSunLights( scene, replacedSunLights );
495
604
 
496
- scene.matrixWorldAutoUpdate = currentMatrixWorldAutoUpdate;
605
+ this.visible = currentVisible;
606
+ if ( this._bounceGrid !== null ) this._bounceGrid.removeFromParent();
497
607
 
498
- // console.log( `LightProbeGrid: bake complete ${ ( performance.now() - t0 ).toFixed( 1 ) }ms` );
608
+ renderer.inspector.enabled = currentInspectorEnabled;
499
609
 
500
610
  }
501
611
 
502
- this.visible = wasVisible; // WITH_GENESYS
503
- // this.visible = true;
504
-
505
612
  }
506
613
 
507
- // WITH_GENESYS
508
614
  /**
509
- * Ensures a retained cube render target exists for a probe.
615
+ * Snapshots each indirect pass before its first range overwrites the live atlas.
616
+ * A separate light keeps the capture and main-view texture bindings stable.
510
617
  *
511
618
  * @private
512
- * @param {number} probeIndex - The probe index.
513
- * @param {Object} options - Bake options.
514
- * @param {boolean} [webgpu=false] - Whether to create a WebGPU cube render target.
515
- * @return {WebGLCubeRenderTarget|CubeRenderTarget} The retained cube render target.
619
+ * @param {WebGPURenderer} renderer - The renderer.
620
+ * @param {Scene} scene - The scene to capture.
621
+ * @param {number} pass - The bounce pass.
622
+ * @param {number} start - The first probe index.
516
623
  */
517
- _ensureRetainedCubeRenderTarget( probeIndex, options, webgpu = false ) {
518
-
519
- const cubemapSize = options.cubemapSize !== undefined ? options.cubemapSize : 8;
624
+ _updateBounceGrid( renderer, scene, pass, start ) {
520
625
 
521
- if ( this._cubeRenderTargetsWebGPU !== null && this._cubeRenderTargetsWebGPU !== webgpu ) {
626
+ if ( pass === 0 ) {
522
627
 
523
- this._disposeRetainedCubemaps();
628
+ if ( start === 0 ) this._bouncePass = - 1;
629
+ return;
524
630
 
525
631
  }
526
632
 
527
- if ( this._cubeRenderTargets === null ) {
633
+ if ( start === 0 ) {
528
634
 
529
- this._cubeRenderTargets = [];
635
+ const renderTarget = this._renderTarget;
530
636
 
531
- }
532
-
533
- this._cubeRenderTargetsWebGPU = webgpu;
534
-
535
- let target = this._cubeRenderTargets[ probeIndex ];
637
+ if ( this._bounceGrid === null ) {
536
638
 
537
- if ( target === undefined || target.width !== cubemapSize || target.height !== cubemapSize ) {
639
+ const res = this.resolution;
640
+ this._bounceGrid = new LightProbeGrid( this.width, this.height, this.depth, res.x, res.y, res.z );
641
+ this._bounceGrid._ensureTextures();
538
642
 
539
- if ( target !== undefined ) target.dispose();
643
+ }
540
644
 
541
- target = webgpu === true
542
- ? new CubeRenderTarget( cubemapSize, { type: HalfFloatType } )
543
- : new WebGLCubeRenderTarget( cubemapSize, { type: HalfFloatType } );
544
- this._cubeRenderTargets[ probeIndex ] = target;
645
+ renderer.initRenderTarget( renderTarget );
646
+ renderer.initRenderTarget( this._bounceGrid._renderTarget );
647
+ _copyRegion.min.set( 0, 0, 0 );
648
+ _copyRegion.max.set( renderTarget.width, renderTarget.height, renderTarget.depth );
649
+ renderer.copyTextureToTexture( renderTarget.texture, this._bounceGrid.texture, _copyRegion );
650
+ this._bouncePass = pass;
545
651
 
546
652
  }
547
653
 
548
- return target;
654
+ const bounceGrid = this._bounceGrid;
655
+ bounceGrid.boundingBox.copy( this.boundingBox );
656
+ bounceGrid.intensity = this.intensity;
657
+ bounceGrid.falloff = this.falloff;
658
+ scene.add( bounceGrid );
549
659
 
550
660
  }
551
661
 
552
662
  /**
553
- * Frees retained cube map render targets.
663
+ * Captures cubemaps and projects their SH coefficients into the batch target.
554
664
  *
555
665
  * @private
666
+ * @param {WebGPURenderer} renderer - The renderer.
667
+ * @param {Scene} scene - The scene to capture.
668
+ * @param {number} start - The first probe index.
669
+ * @param {number} end - The exclusive end probe index.
556
670
  */
557
- _disposeRetainedCubemaps() {
671
+ _captureProbes( renderer, scene, start, end ) {
558
672
 
559
- if ( this._cubeRenderTargets !== null ) {
673
+ const { x: nx, y: ny, z: nz } = this.resolution;
674
+ const probesPerLayer = nx * nz;
560
675
 
561
- for ( const target of this._cubeRenderTargets ) {
676
+ _quad.material = _shMaterial;
562
677
 
563
- if ( target !== undefined ) target.dispose();
678
+ for ( let probeIndex = start; probeIndex < end; probeIndex ++ ) {
564
679
 
565
- }
566
-
567
- this._cubeRenderTargets = null;
680
+ const ix = probeIndex % nx;
681
+ const iy = Math.floor( probeIndex / probesPerLayer );
682
+ const iz = Math.floor( probeIndex / nx ) % nz;
568
683
 
569
- }
684
+ this.getProbePosition( ix, iy, iz, _position );
685
+ _cubeCamera.position.copy( _position );
570
686
 
571
- this._cubeRenderTargetsWebGPU = null;
572
- this.cubeTextures = null;
687
+ // The cube faces must be cleared per face.
688
+ renderer.autoClear = true;
689
+ _cubeCamera.update( renderer, scene );
573
690
 
574
- }
691
+ // Keep batch rows in texture order (X, Y, Z).
692
+ const batchRow = ix + iy * nx + iz * nx * ny;
575
693
 
576
- /**
577
- * Frees retained cube maps used for cube map visualization.
578
- */
579
- disposeRetainedCubemaps() {
694
+ renderer.autoClear = false;
695
+ _batchTarget.viewport.set( 0, batchRow, 9, 1 );
696
+ renderer.setRenderTarget( _batchTarget );
697
+ _quad.render( renderer );
580
698
 
581
- this._disposeRetainedCubemaps();
699
+ }
582
700
 
583
701
  }
584
702
 
585
703
  /**
586
- * Bakes all probes with a WebGPU renderer.
704
+ * Packs a probe range into the live atlas, including its boundary padding.
587
705
  *
588
706
  * @private
589
- * @async
590
707
  * @param {WebGPURenderer} renderer - The renderer.
591
- * @param {Scene} scene - The scene to render.
592
- * @param {Object} [options] - Bake options.
593
- * @return {Promise<void>} Resolves when baking has completed.
708
+ * @param {number} start - The first probe index.
709
+ * @param {number} end - The exclusive end probe index.
594
710
  */
595
- async _bakeWebGPU( renderer, scene, options = {} ) {
596
-
597
- const cubemapSize = options.cubemapSize !== undefined ? options.cubemapSize : 8;
598
- const retainCubemaps = options.retainCubemaps === true;
599
- const { cubeRenderTarget, cubeCamera } = _ensureWebGPUBakeResources( options );
600
-
601
- this._ensureWebGPUTexture();
602
- this.updateBoundingBox();
711
+ _repackProbes( renderer, start, end ) {
603
712
 
604
- const computeNode = _ensureWebGPUComputeResources( cubemapSize, cubeRenderTarget.texture, this.texture );
605
- const uniforms = _webgpuProbeUniforms;
606
-
607
- const res = this.resolution;
608
- const nx = res.x, ny = res.y, nz = res.z;
609
- const totalProbes = nx * ny * nz;
713
+ const { x: nx, y: ny, z: nz } = this.resolution;
714
+ const probesPerLayer = nx * nz;
715
+ const startY = Math.floor( start / probesPerLayer );
716
+ const endY = Math.floor( end / probesPerLayer );
610
717
  const paddedSlices = nz + 2 * ATLAS_PADDING;
718
+ const renderTarget = this._renderTarget;
611
719
 
612
- uniforms.paddedSlices.value = paddedSlices;
613
-
614
- const savedShadowAutoUpdate = renderer.shadowMap.autoUpdate;
615
- renderer.shadowMap.autoUpdate = false;
616
- renderer.shadowMap.needsUpdate = true;
617
-
618
- // Initialize cube textures array if retaining
619
- if ( retainCubemaps ) {
720
+ // Map the horizontal bake range to contiguous rows in each Z slice.
721
+ for ( let iz = 0; iz < nz; iz ++ ) {
620
722
 
621
- if ( this.cubeTextures === null || this.cubeTextures.length !== totalProbes ) {
723
+ const sliceStart = startY * nx + MathUtils.clamp( start % probesPerLayer - iz * nx, 0, nx );
724
+ const sliceEnd = endY * nx + MathUtils.clamp( end % probesPerLayer - iz * nx, 0, nx );
622
725
 
623
- this.cubeTextures = new Array( totalProbes );
726
+ for ( let probeIndex = sliceStart; probeIndex < sliceEnd; ) {
624
727
 
625
- }
728
+ const ix = probeIndex % nx;
729
+ const iy = Math.floor( probeIndex / nx );
626
730
 
627
- if ( this._cubeRenderTargets === null || this._cubeRenderTargets.length !== totalProbes ) {
731
+ // Coalesce complete rows within a slice into one rectangle.
732
+ const width = Math.min( nx - ix, sliceEnd - probeIndex );
733
+ let height = 1;
628
734
 
629
- this._disposeRetainedCubemaps();
630
- this.cubeTextures = new Array( totalProbes );
631
- this._cubeRenderTargets = new Array( totalProbes );
735
+ if ( width === nx ) {
632
736
 
633
- }
737
+ height = Math.min( ny - iy, Math.floor( ( sliceEnd - probeIndex ) / nx ) );
634
738
 
635
- }
739
+ }
636
740
 
637
- for ( let iz = 0; iz < nz; iz ++ ) {
741
+ renderTarget.viewport.set( ix, iy, width, height );
742
+ _sliceZUniform.value = iz;
638
743
 
639
- for ( let iy = 0; iy < ny; iy ++ ) {
744
+ for ( let t = 0; t < 7; t ++ ) {
640
745
 
641
- for ( let ix = 0; ix < nx; ix ++ ) {
746
+ _quad.material = _repackMaterials[ t ];
747
+ const base = t * paddedSlices;
642
748
 
643
- const probeIndex = ix + iy * nx + iz * nx * ny;
749
+ renderer.setRenderTarget( renderTarget, base + ATLAS_PADDING + iz );
750
+ _quad.render( renderer );
644
751
 
645
- this.getProbePosition( ix, iy, iz, _position );
646
- cubeCamera.position.copy( _position );
647
- cubeCamera.update( renderer, scene );
752
+ if ( iz === 0 ) {
648
753
 
649
- if ( retainCubemaps ) {
754
+ renderer.setRenderTarget( renderTarget, base );
755
+ _quad.render( renderer );
650
756
 
651
- const retainedTarget = this._ensureRetainedCubeRenderTarget( probeIndex, options, true );
652
- const retainedCamera = new CubeCamera(
653
- options.near !== undefined ? options.near : 0.1,
654
- options.far !== undefined ? options.far : 100,
655
- retainedTarget
656
- );
757
+ }
657
758
 
658
- retainedCamera.position.copy( _position );
659
- retainedCamera.update( renderer, scene );
759
+ if ( iz === nz - 1 ) {
660
760
 
661
- this.cubeTextures[ probeIndex ] = retainedTarget.texture;
761
+ renderer.setRenderTarget( renderTarget, base + ATLAS_PADDING + nz );
762
+ _quad.render( renderer );
662
763
 
663
764
  }
664
765
 
665
- uniforms.probeIx.value = ix;
666
- uniforms.probeIy.value = iy;
667
- uniforms.probeIz.value = iz;
668
-
669
- renderer.compute( computeNode );
670
-
671
766
  }
672
767
 
768
+ probeIndex += width * height;
769
+
673
770
  }
674
771
 
675
772
  }
676
773
 
677
- renderer.shadowMap.autoUpdate = savedShadowAutoUpdate;
678
-
679
- await renderer.backend.device.queue.onSubmittedWorkDone();
680
-
681
774
  }
682
- // !WITH_GENESYS
683
775
 
684
776
  /**
685
- * Ensures the atlas 3D render target exists with the correct dimensions.
777
+ * Ensures the atlas 3D texture exists with the correct dimensions.
686
778
  *
687
779
  * @private
688
780
  */
@@ -690,547 +782,50 @@ class LightProbeGrid extends Object3D {
690
782
 
691
783
  if ( this._renderTarget !== null ) return;
692
784
 
693
- // WITH_GENESYS
694
- if ( this.texture !== null ) {
695
-
696
- this.texture.dispose();
697
- this.texture = null;
698
-
699
- }
700
- // !WITH_GENESYS
701
-
702
785
  const res = this.resolution;
703
786
  const nx = res.x, ny = res.y, nz = res.z;
704
787
 
705
- // Atlas depth: 7 sub-volumes, each with ATLAS_PADDING slices at both ends
788
+ // Atlas depth: 7 sub-volumes, each with ATLAS_PADDING slices at both ends.
706
789
  const atlasDepth = 7 * ( nz + 2 * ATLAS_PADDING );
707
790
 
708
- const rt = new WebGL3DRenderTarget( nx, ny, atlasDepth, {
791
+ this._renderTarget = new RenderTarget3D( nx, ny, atlasDepth, {
792
+ type: HalfFloatType,
709
793
  format: RGBAFormat,
710
- type: FloatType,
711
794
  minFilter: LinearFilter,
712
795
  magFilter: LinearFilter,
713
796
  generateMipmaps: false,
714
797
  depthBuffer: false
715
798
  } );
716
799
 
717
- this._renderTarget = rt;
718
- this.texture = rt.texture;
800
+ this.texture = this._renderTarget.texture;
719
801
 
720
802
  }
721
803
 
722
- // WITH_GENESYS
723
804
  /**
724
- * Ensures the atlas Storage3DTexture exists with the correct dimensions for WebGPU.
725
- *
726
- * @private
805
+ * Frees GPU resources.
727
806
  */
728
- _ensureWebGPUTexture() {
729
-
730
- if ( this._renderTarget !== null ) {
731
-
732
- this._renderTarget.dispose();
733
- this._renderTarget = null;
734
- this.texture = null;
735
-
736
- }
737
-
738
- const res = this.resolution;
739
- const nx = res.x, ny = res.y, nz = res.z;
740
- const atlasDepth = 7 * ( nz + 2 * ATLAS_PADDING );
741
-
742
- if ( this.texture !== null ) {
743
-
744
- const image = this.texture.image;
745
-
746
- if ( image.width === nx && image.height === ny && image.depth === atlasDepth ) {
747
-
748
- return;
807
+ dispose() {
749
808
 
750
- }
809
+ if ( this._bounceGrid !== null ) {
751
810
 
752
- this.texture.dispose();
753
- this.texture = null;
811
+ this._bounceGrid.dispose();
812
+ this._bounceGrid = null;
813
+ this._bouncePass = - 1;
754
814
 
755
815
  }
756
816
 
757
- const texture = new Storage3DTexture( nx, ny, atlasDepth );
758
-
759
- texture.format = RGBAFormat;
760
- texture.type = FloatType;
761
- texture.minFilter = LinearFilter;
762
- texture.magFilter = LinearFilter;
763
- texture.generateMipmaps = false;
764
-
765
- this.texture = texture;
766
-
767
- }
768
- // !WITH_GENESYS
769
-
770
- /**
771
- * Frees GPU resources.
772
- */
773
- dispose() {
774
-
775
817
  if ( this._renderTarget !== null ) {
776
818
 
777
819
  this._renderTarget.dispose();
778
820
  this._renderTarget = null;
779
821
  this.texture = null;
780
822
 
781
- } else if ( this.texture !== null ) { // WITH_GENESYS
782
-
783
- this.texture.dispose();
784
- this.texture = null;
785
-
786
- } // !WITH_GENESYS
787
-
788
- this._disposeRetainedCubemaps();
789
-
790
- }
791
-
792
- }
793
-
794
- // Internal: Ensure the shared fullscreen-quad scene exists
795
- function _ensureScene() {
796
-
797
- if ( _scene === null ) {
798
-
799
- _camera = new OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );
800
- _mesh = new Mesh( new PlaneGeometry( 2, 2 ) );
801
- _scene = new Scene();
802
- _scene.add( _mesh );
803
-
804
- }
805
-
806
- }
807
-
808
- // Internal: Ensure GPU resources for SH projection are created
809
- function _ensureGPUResources( cubemapSize ) {
810
-
811
- _ensureScene();
812
-
813
- // Recreate material when cubemap size changes
814
- if ( cubemapSize !== _lastCubemapSize ) {
815
-
816
- if ( _shMaterial !== null ) _shMaterial.dispose();
817
-
818
- _shMaterial = new ShaderMaterial( {
819
- precision: 'highp',
820
- defines: {
821
- CUBEMAP_SIZE: cubemapSize
822
- },
823
- uniforms: {
824
- envMap: { value: null }
825
- },
826
- vertexShader: /* glsl */`
827
- void main() {
828
- gl_Position = vec4( position.xy, 0.0, 1.0 );
829
- }
830
- `,
831
- fragmentShader: /* glsl */`
832
- #include <common>
833
-
834
- uniform samplerCube envMap;
835
-
836
- void main() {
837
-
838
- int coefIndex = int( gl_FragCoord.x );
839
-
840
- vec3 accum0 = vec3( 0.0 );
841
- vec3 accum1 = vec3( 0.0 );
842
- vec3 accum2 = vec3( 0.0 );
843
- vec3 accum3 = vec3( 0.0 );
844
- vec3 accum4 = vec3( 0.0 );
845
- vec3 accum5 = vec3( 0.0 );
846
- vec3 accum6 = vec3( 0.0 );
847
- vec3 accum7 = vec3( 0.0 );
848
- vec3 accum8 = vec3( 0.0 );
849
- float totalWeight = 0.0;
850
- float pixelSize = 2.0 / float( CUBEMAP_SIZE );
851
-
852
- for ( int face = 0; face < 6; face ++ ) {
853
-
854
- for ( int iy = 0; iy < CUBEMAP_SIZE; iy ++ ) {
855
-
856
- for ( int ix = 0; ix < CUBEMAP_SIZE; ix ++ ) {
857
-
858
- // WebGL cubemaps have a left-handed orientation (flip = -1)
859
- float col = ( float( ix ) + 0.5 ) * pixelSize - 1.0;
860
- float row = 1.0 - ( float( iy ) + 0.5 ) * pixelSize;
861
-
862
- vec3 coord;
863
-
864
- if ( face == 0 ) coord = vec3( 1.0, row, -col );
865
- else if ( face == 1 ) coord = vec3( -1.0, row, col );
866
- else if ( face == 2 ) coord = vec3( col, 1.0, -row );
867
- else if ( face == 3 ) coord = vec3( col, -1.0, row );
868
- else if ( face == 4 ) coord = vec3( col, row, 1.0 );
869
- else coord = vec3( -col, row, -1.0 );
870
-
871
- float lengthSq = dot( coord, coord );
872
- float weight = 4.0 / ( sqrt( lengthSq ) * lengthSq );
873
- totalWeight += weight;
874
-
875
- vec3 dir = normalize( coord );
876
- vec3 cw = textureCube( envMap, coord ).rgb * weight;
877
-
878
- // band 0
879
- accum0 += cw * 0.282095;
880
-
881
- // band 1
882
- accum1 += cw * ( 0.488603 * dir.y );
883
- accum2 += cw * ( 0.488603 * dir.z );
884
- accum3 += cw * ( 0.488603 * dir.x );
885
-
886
- // band 2
887
- accum4 += cw * ( 1.092548 * ( dir.x * dir.y ) );
888
- accum5 += cw * ( 1.092548 * ( dir.y * dir.z ) );
889
- accum6 += cw * ( 0.315392 * ( 3.0 * dir.z * dir.z - 1.0 ) );
890
- accum7 += cw * ( 1.092548 * ( dir.x * dir.z ) );
891
- accum8 += cw * ( 0.546274 * ( dir.x * dir.x - dir.y * dir.y ) );
892
-
893
- }
894
-
895
- }
896
-
897
- }
898
-
899
- float norm = 4.0 * PI / totalWeight;
900
-
901
- vec3 accum;
902
- if ( coefIndex == 0 ) accum = accum0;
903
- else if ( coefIndex == 1 ) accum = accum1;
904
- else if ( coefIndex == 2 ) accum = accum2;
905
- else if ( coefIndex == 3 ) accum = accum3;
906
- else if ( coefIndex == 4 ) accum = accum4;
907
- else if ( coefIndex == 5 ) accum = accum5;
908
- else if ( coefIndex == 6 ) accum = accum6;
909
- else if ( coefIndex == 7 ) accum = accum7;
910
- else accum = accum8;
911
-
912
- gl_FragColor = vec4( accum * norm, 1.0 );
913
-
914
- }
915
- `
916
- } );
917
-
918
- _lastCubemapSize = cubemapSize;
919
-
920
- }
921
-
922
- }
923
-
924
- // Internal: Ensure GPU resources for repacking SH into the atlas 3D texture
925
- function _ensureRepackResources() {
926
-
927
- if ( _repackMaterials !== null ) return;
928
-
929
- _ensureScene();
930
-
931
- // Create 7 materials, one per output texture packing
932
- // Texture 0: (c0.r, c0.g, c0.b, c1.r)
933
- // Texture 1: (c1.g, c1.b, c2.r, c2.g)
934
- // Texture 2: (c2.b, c3.r, c3.g, c3.b)
935
- // Texture 3: (c4.r, c4.g, c4.b, c5.r)
936
- // Texture 4: (c5.g, c5.b, c6.r, c6.g)
937
- // Texture 5: (c6.b, c7.r, c7.g, c7.b)
938
- // Texture 6: (c8.r, c8.g, c8.b, 0.0)
939
-
940
- const repackVertexShader = /* glsl */`
941
- void main() {
942
- gl_Position = vec4( position.xy, 0.0, 1.0 );
943
823
  }
944
- `;
945
-
946
- _repackMaterials = [];
947
-
948
- for ( let t = 0; t < 7; t ++ ) {
949
-
950
- _repackMaterials[ t ] = new ShaderMaterial( {
951
- precision: 'highp',
952
- defines: {
953
- TEXTURE_INDEX: t
954
- },
955
- uniforms: {
956
- batchTexture: { value: null },
957
- resolution: { value: new Vector3() },
958
- sliceZ: { value: 0 }
959
- },
960
- vertexShader: repackVertexShader,
961
- fragmentShader: /* glsl */`
962
- uniform sampler2D batchTexture;
963
- uniform vec3 resolution;
964
- uniform int sliceZ;
965
-
966
- void main() {
967
-
968
- int ix = int( gl_FragCoord.x );
969
- int iy = int( gl_FragCoord.y );
970
- int iz = sliceZ;
971
-
972
- int probeIndex = ix + iy * int( resolution.x ) + iz * int( resolution.x ) * int( resolution.y );
973
-
974
- // Read 9 SH coefficients from the batch texture row
975
- vec4 c0 = texelFetch( batchTexture, ivec2( 0, probeIndex ), 0 );
976
- vec4 c1 = texelFetch( batchTexture, ivec2( 1, probeIndex ), 0 );
977
- vec4 c2 = texelFetch( batchTexture, ivec2( 2, probeIndex ), 0 );
978
- vec4 c3 = texelFetch( batchTexture, ivec2( 3, probeIndex ), 0 );
979
- vec4 c4 = texelFetch( batchTexture, ivec2( 4, probeIndex ), 0 );
980
- vec4 c5 = texelFetch( batchTexture, ivec2( 5, probeIndex ), 0 );
981
- vec4 c6 = texelFetch( batchTexture, ivec2( 6, probeIndex ), 0 );
982
- vec4 c7 = texelFetch( batchTexture, ivec2( 7, probeIndex ), 0 );
983
- vec4 c8 = texelFetch( batchTexture, ivec2( 8, probeIndex ), 0 );
984
-
985
- // Pack into the output format for this texture index
986
- #if TEXTURE_INDEX == 0
987
- gl_FragColor = vec4( c0.rgb, c1.r );
988
- #elif TEXTURE_INDEX == 1
989
- gl_FragColor = vec4( c1.gb, c2.rg );
990
- #elif TEXTURE_INDEX == 2
991
- gl_FragColor = vec4( c2.b, c3.rgb );
992
- #elif TEXTURE_INDEX == 3
993
- gl_FragColor = vec4( c4.rgb, c5.r );
994
- #elif TEXTURE_INDEX == 4
995
- gl_FragColor = vec4( c5.gb, c6.rg );
996
- #elif TEXTURE_INDEX == 5
997
- gl_FragColor = vec4( c6.b, c7.rgb );
998
- #else
999
- gl_FragColor = vec4( c8.rgb, 0.0 );
1000
- #endif
1001
-
1002
- }
1003
- `
1004
- } );
1005
-
1006
- }
1007
-
1008
- }
1009
-
1010
- // Internal: Ensure cube render target and camera exist with the right parameters
1011
- function _ensureBakeResources( options ) {
1012
-
1013
- const {
1014
- cubemapSize = 8,
1015
- near = 0.1,
1016
- far = 100
1017
- } = options;
1018
-
1019
- if ( _cubeRenderTarget === null || cubemapSize !== _cachedCubemapSize || near !== _cachedNear || far !== _cachedFar ) {
1020
-
1021
- if ( _cubeRenderTarget !== null ) _cubeRenderTarget.dispose();
1022
-
1023
- _cubeRenderTarget = new WebGLCubeRenderTarget( cubemapSize, { type: HalfFloatType } );
1024
- _cubeCamera = new CubeCamera( near, far, _cubeRenderTarget );
1025
- _cachedCubemapSize = cubemapSize;
1026
- _cachedNear = near;
1027
- _cachedFar = far;
1028
-
1029
- }
1030
-
1031
- _ensureGPUResources( cubemapSize );
1032
-
1033
- return { cubeRenderTarget: _cubeRenderTarget, cubeCamera: _cubeCamera };
1034
-
1035
- }
1036
-
1037
- // WITH_GENESYS
1038
- function _ensureWebGPUBakeResources( options ) {
1039
-
1040
- const {
1041
- cubemapSize = 8,
1042
- near = 0.1,
1043
- far = 100
1044
- } = options;
1045
-
1046
- if ( _webgpuCubeRenderTarget === null || cubemapSize !== _webgpuCachedCubemapSize || near !== _webgpuCachedNear || far !== _webgpuCachedFar ) {
1047
-
1048
- if ( _webgpuCubeRenderTarget !== null ) _webgpuCubeRenderTarget.dispose();
1049
-
1050
- _webgpuCubeRenderTarget = new CubeRenderTarget( cubemapSize, { type: HalfFloatType } );
1051
- _webgpuCubeCamera = new CubeCamera( near, far, _webgpuCubeRenderTarget );
1052
- _webgpuCachedCubemapSize = cubemapSize;
1053
- _webgpuCachedNear = near;
1054
- _webgpuCachedFar = far;
1055
-
1056
- }
1057
-
1058
- return { cubeRenderTarget: _webgpuCubeRenderTarget, cubeCamera: _webgpuCubeCamera };
1059
-
1060
- }
1061
-
1062
- function _ensureWebGPUComputeResources( cubemapSize, envMapTexture, atlasTexture ) {
1063
-
1064
- if (
1065
- _webgpuComputeNode !== null &&
1066
- _webgpuComputeCubemapSize === cubemapSize &&
1067
- _webgpuComputeAtlas === atlasTexture &&
1068
- _webgpuComputeEnvMap === envMapTexture
1069
- ) {
1070
-
1071
- return _webgpuComputeNode;
1072
-
1073
- }
1074
-
1075
- _webgpuComputeCubemapSize = cubemapSize;
1076
- _webgpuComputeAtlas = atlasTexture;
1077
- _webgpuComputeEnvMap = envMapTexture;
1078
-
1079
- const probeIx = uniform( 'uint' );
1080
- const probeIy = uniform( 'uint' );
1081
- const probeIz = uniform( 'uint' );
1082
- const paddedSlices = uniform( 'uint' );
1083
-
1084
- _webgpuProbeUniforms = { probeIx, probeIy, probeIz, paddedSlices };
1085
-
1086
- const atlas = storageTexture( atlasTexture );
1087
-
1088
- const projectProbe = Fn( () => {
1089
-
1090
- const accum0 = vec3( 0 ).toVar();
1091
- const accum1 = vec3( 0 ).toVar();
1092
- const accum2 = vec3( 0 ).toVar();
1093
- const accum3 = vec3( 0 ).toVar();
1094
- const accum4 = vec3( 0 ).toVar();
1095
- const accum5 = vec3( 0 ).toVar();
1096
- const accum6 = vec3( 0 ).toVar();
1097
- const accum7 = vec3( 0 ).toVar();
1098
- const accum8 = vec3( 0 ).toVar();
1099
- const totalWeight = float( 0 ).toVar();
1100
- const pixelSize = float( 2 ).div( float( cubemapSize ) );
1101
-
1102
- Loop(
1103
- { end: 6, name: 'face', type: 'int' },
1104
- { end: cubemapSize, name: 'iy', type: 'int' },
1105
- { end: cubemapSize, name: 'ix', type: 'int' },
1106
- ( { face, iy, ix } ) => {
1107
-
1108
- const col = convert( ix, 'float' ).add( 0.5 ).mul( pixelSize ).sub( 1.0 );
1109
- const row = float( 1 ).sub( convert( iy, 'float' ).add( 0.5 ).mul( pixelSize ) );
1110
-
1111
- const dirX = select( face.equal( int( 0 ) ), float( 1 ), select( face.equal( int( 1 ) ), float( - 1 ), select( face.equal( int( 5 ) ), col.negate(), col ) ) );
1112
- const dirY = select( face.equal( int( 2 ) ), float( 1 ), select( face.equal( int( 3 ) ), float( - 1 ), row ) );
1113
- const dirZ = select( face.equal( int( 0 ) ), col.negate(), select( face.equal( int( 1 ) ), col, select( face.equal( int( 2 ) ), row.negate(), select( face.equal( int( 3 ) ), row, select( face.equal( int( 4 ) ), float( 1 ), float( - 1 ) ) ) ) ) );
1114
- const coord = vec3( dirX, dirY, dirZ );
1115
-
1116
- const lengthSq = coord.dot( coord );
1117
- const weight = float( 4 ).div( lengthSq.sqrt().mul( lengthSq ) );
1118
- totalWeight.addAssign( weight );
1119
-
1120
- const dir = coord.normalize();
1121
- // Pass explicit direction; omitting UV would default to reflectVector (needs camera).
1122
- const cw = cubeTexture( envMapTexture, coord ).rgb.mul( weight );
1123
-
1124
- accum0.addAssign( cw.mul( 0.282095 ) );
1125
- accum1.addAssign( cw.mul( 0.488603 ).mul( dir.y ) );
1126
- accum2.addAssign( cw.mul( 0.488603 ).mul( dir.z ) );
1127
- accum3.addAssign( cw.mul( 0.488603 ).mul( dir.x ) );
1128
- accum4.addAssign( cw.mul( 1.092548 ).mul( dir.x ).mul( dir.y ) );
1129
- accum5.addAssign( cw.mul( 1.092548 ).mul( dir.y ).mul( dir.z ) );
1130
- accum6.addAssign( cw.mul( 0.315392 ).mul( dir.z.mul( dir.z ).mul( 3 ).sub( 1 ) ) );
1131
- accum7.addAssign( cw.mul( 1.092548 ).mul( dir.x ).mul( dir.z ) );
1132
- accum8.addAssign( cw.mul( 0.546274 ).mul( dir.x.mul( dir.x ).sub( dir.y.mul( dir.y ) ) ) );
1133
-
1134
- }
1135
-
1136
- );
1137
-
1138
- const norm = float( 4 ).mul( PI ).div( totalWeight );
1139
- const c0 = accum0.mul( norm );
1140
- const c1 = accum1.mul( norm );
1141
- const c2 = accum2.mul( norm );
1142
- const c3 = accum3.mul( norm );
1143
- const c4 = accum4.mul( norm );
1144
- const c5 = accum5.mul( norm );
1145
- const c6 = accum6.mul( norm );
1146
- const c7 = accum7.mul( norm );
1147
- const c8 = accum8.mul( norm );
1148
-
1149
- Loop( { end: 7, name: 't', type: 'int' }, ( { t } ) => {
1150
-
1151
- const baseSlice = paddedSlices.mul( t );
1152
- const atlasZ = baseSlice.add( 1 ).add( probeIz );
1153
- const trailingSlice = baseSlice.add( paddedSlices ).sub( 1 );
1154
- const lastDataSlice = paddedSlices.sub( 3 );
1155
- const storeProbeValue = ( value ) => {
1156
-
1157
- textureStore( atlas, uvec3( probeIx, probeIy, atlasZ ), value ).toWriteOnly();
1158
-
1159
- // Write padding during the compute pass; WebGPU validation rejects atlas-to-itself copies.
1160
- If( probeIz.equal( uint( 0 ) ), () => {
1161
-
1162
- textureStore( atlas, uvec3( probeIx, probeIy, baseSlice ), value ).toWriteOnly();
1163
-
1164
- } );
1165
-
1166
- If( probeIz.equal( lastDataSlice ), () => {
1167
-
1168
- textureStore( atlas, uvec3( probeIx, probeIy, trailingSlice ), value ).toWriteOnly();
1169
-
1170
- } );
1171
-
1172
- };
1173
-
1174
- If( t.equal( int( 0 ) ), () => {
1175
-
1176
- storeProbeValue( vec4( c0.x, c0.y, c0.z, c1.x ) );
1177
-
1178
- } ).ElseIf( t.equal( int( 1 ) ), () => {
1179
-
1180
- storeProbeValue( vec4( c1.y, c1.z, c2.x, c2.y ) );
1181
-
1182
- } ).ElseIf( t.equal( int( 2 ) ), () => {
1183
-
1184
- storeProbeValue( vec4( c2.z, c3.x, c3.y, c3.z ) );
1185
-
1186
- } ).ElseIf( t.equal( int( 3 ) ), () => {
1187
-
1188
- storeProbeValue( vec4( c4.x, c4.y, c4.z, c5.x ) );
1189
-
1190
- } ).ElseIf( t.equal( int( 4 ) ), () => {
1191
-
1192
- storeProbeValue( vec4( c5.y, c5.z, c6.x, c6.y ) );
1193
824
 
1194
- } ).ElseIf( t.equal( int( 5 ) ), () => {
1195
-
1196
- storeProbeValue( vec4( c6.z, c7.x, c7.y, c7.z ) );
1197
-
1198
- } ).Else( () => {
1199
-
1200
- storeProbeValue( vec4( c8.x, c8.y, c8.z, float( 0 ) ) );
1201
-
1202
- } );
1203
-
1204
- } );
1205
-
1206
- } );
1207
-
1208
- _webgpuComputeNode = projectProbe().compute( 1 );
1209
-
1210
- return _webgpuComputeNode;
1211
-
1212
- }
1213
- // !WITH_GENESYS
1214
-
1215
- function _ensureBatchTarget( totalProbes ) {
1216
-
1217
- if ( _batchTarget === null || _batchTargetProbes !== totalProbes ) {
1218
-
1219
- if ( _batchTarget !== null ) _batchTarget.dispose();
1220
-
1221
- _batchTarget = new WebGLRenderTarget( 9, totalProbes, {
1222
- type: FloatType,
1223
- minFilter: NearestFilter,
1224
- magFilter: NearestFilter,
1225
- depthBuffer: false
1226
- } );
1227
-
1228
- _batchTargetProbes = totalProbes;
825
+ super.dispose();
1229
826
 
1230
827
  }
1231
828
 
1232
- return _batchTarget;
1233
-
1234
829
  }
1235
830
 
1236
831
  export { LightProbeGrid };