@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
@@ -0,0 +1,1430 @@
1
+ import { Box3, Vector3, Vector4, Matrix4, Layers, Storage3DTexture, StorageBufferAttribute, StorageTexture3DNode, CubeTextureNode, RendererUtils, HalfFloatType, UnsignedByteType, RGBAFormat, LinearFilter, LinearMipmapLinearFilter, ClampToEdgeWrapping, MathUtils } from '@gnsx/three/webgpu';
2
+ import { Fn, If, Loop, nodeObject, uniform, uniformArray, storage, instanceIndex, textureStore, texture3D, texture, float, int, uint, vec2, vec3, vec4, ivec3, uvec3, max, min, abs, dot, cross, normalize, floor, sign, select, countOneBits, atomicOr, smoothstep, hash, fract, sqrt, cos, sin, length, PI, getDistanceAttenuation, viewZToPerspectiveDepth, viewZToReversedPerspectiveDepth } from '@gnsx/three/tsl';
3
+
4
+ import { collectSceneTriangles, computeSceneBounds, TRIANGLE_STRIDE } from './VXGISceneCollector.js';
5
+ import { createConeTracer } from './VXGIConeTracer.js';
6
+
7
+ const _box = /*@__PURE__*/ new Box3();
8
+ const _size = /*@__PURE__*/ new Vector3();
9
+ const _position = /*@__PURE__*/ new Vector3();
10
+ const _target = /*@__PURE__*/ new Vector3();
11
+
12
+ const MAX_EDGE_SUBVOXELS = 16;
13
+ const BOUNCE_CONE_COUNT = 8;
14
+
15
+ // the six directions a ray can travel, as (axis, sign) pairs
16
+
17
+ const DIRECTIONS = [[ 0, 1 ], [ 0, - 1 ], [ 1, 1 ], [ 1, - 1 ], [ 2, 1 ], [ 2, - 1 ]];
18
+
19
+ let _rendererState;
20
+
21
+ /**
22
+ * Storage texture node bound to a single mip level. Texture nodes normally share one binding
23
+ * per texture; this node keys the binding by access and mip level so a kernel can read one
24
+ * level while writing another.
25
+ *
26
+ * @private
27
+ */
28
+ class MipStorageTexture3DNode extends StorageTexture3DNode {
29
+
30
+ constructor( value, access, mipLevel ) {
31
+
32
+ super( value, null, null );
33
+
34
+ this.setAccess( access );
35
+ this.setMipLevel( mipLevel );
36
+
37
+ }
38
+
39
+ getUniformHash() {
40
+
41
+ return `${ this.value.uuid }:${ this.access }:${ this.mipLevel }`;
42
+
43
+ }
44
+
45
+ }
46
+
47
+ /**
48
+ * Cube texture node without the material's environment rotation, which requires a material and
49
+ * scene context that compute passes lack. Used for point light shadow maps.
50
+ *
51
+ * @private
52
+ */
53
+ class PlainCubeTextureNode extends CubeTextureNode {
54
+
55
+ setupUV( builder, uvNode ) {
56
+
57
+ if ( this.value.isDepthTexture === true ) {
58
+
59
+ return vec3( uvNode.x, uvNode.y.negate(), uvNode.z );
60
+
61
+ }
62
+
63
+ return vec3( uvNode.x.negate(), uvNode.yz );
64
+
65
+ }
66
+
67
+ }
68
+
69
+ /**
70
+ * Holds the voxel representation of a scene for {@link VXGINode}: an anisotropic opacity
71
+ * mip chain, a direct radiance volume and a radiance volume with cached bounces.
72
+ *
73
+ * The representation is a dense variant of the pre-filtered voxel hierarchy of Crassin et al. 2011
74
+ * (a dense mip chain instead of a sparse octree, which keeps cone samples to two texture fetches):
75
+ * opacity stores visibility per major axis and is filtered directionally (volumetric integration
76
+ * along the axis, averaging across it), radiance is stored opacity-premultiplied and indirect bounces
77
+ * are cached in the volume via cone tracing. Only direct lights are injected.
78
+ * Voxelization uses conservative rasterization along the dominant triangle axis in a compute shader.
79
+ *
80
+ * Direct light is injected per voxel instead of splatting photons from a light-view map as in the
81
+ * paper: the shadow maps rendered by the renderer are the light-view maps, and every occupied voxel
82
+ * pulls its visibility from them (2D maps for directional and spot lights, cube maps for point
83
+ * lights) and evaluates its irradiance analytically. This reuses the existing shadow passes, makes
84
+ * the injected shadows match the direct lighting exactly and needs neither atomics nor a
85
+ * normalization by photon density. Lights without a shadow map fall back to a visibility cone
86
+ * traced through the volume. The trade-off is a cost proportional to the number of occupied voxels
87
+ * times lights rather than to the light-view resolution, and that only outgoing diffuse radiance
88
+ * is stored (no incoming direction distribution for glossy cones).
89
+ *
90
+ * References:
91
+ * - {@link https://research.nvidia.com/publication/2011-09_interactive-indirect-illumination-using-voxel-cone-tracing}:
92
+ * Crassin et al., Interactive Indirect Illumination Using Voxel Cone Tracing, Pacific Graphics 2011.
93
+ * - {@link https://developer.nvidia.com/content/basics-gpu-voxelization}: Basics of GPU voxelization.
94
+ *
95
+ * Note: This class can only be used with `WebGPURenderer` and a WebGPU backend.
96
+ *
97
+ * @three_import import { VXGIVolume } from 'three/addons/lighting/vxgi/VXGIVolume.js';
98
+ */
99
+ class VXGIVolume {
100
+
101
+ /**
102
+ * Constructs a new volume.
103
+ *
104
+ * @param {number} [resolution=128] - Number of voxels along the longest axis of the bounds. Should not exceed `256`, higher values exceed the maximum storage buffer size of the voxelizer.
105
+ */
106
+ constructor( resolution = 128 ) {
107
+
108
+ /**
109
+ * Number of voxels along the longest axis of the bounds. Should not exceed `256`, higher values exceed the maximum storage buffer size of the voxelizer.
110
+ *
111
+ * @type {number}
112
+ * @default 128
113
+ */
114
+ this.resolution = resolution;
115
+
116
+ /**
117
+ * The requested world-space bounds of the volume. If empty (the default), the bounds are
118
+ * computed from the scene at voxelization. See {@link VXGIVolume#worldBounds} for the
119
+ * effective bounds.
120
+ *
121
+ * @type {Box3}
122
+ */
123
+ this.bounds = new Box3();
124
+
125
+ /**
126
+ * The effective world-space bounds of the voxel grid, updated at voxelization.
127
+ *
128
+ * @type {Box3}
129
+ * @readonly
130
+ */
131
+ this.worldBounds = new Box3();
132
+
133
+ /**
134
+ * Only meshes that pass this layer test are voxelized.
135
+ *
136
+ * @type {Layers}
137
+ */
138
+ this.layers = new Layers();
139
+
140
+ /**
141
+ * Number of cached indirect bounces stored in the volume. Should be in the range `[0, 2]`.
142
+ *
143
+ * @type {number}
144
+ * @default 1
145
+ */
146
+ this.bounces = 1;
147
+
148
+ /**
149
+ * Triangles with a lower opacity are not voxelized.
150
+ *
151
+ * @type {number}
152
+ * @default 0.1
153
+ */
154
+ this.minOpacity = 0.1;
155
+
156
+ /**
157
+ * Whether the coarser radiance levels are filtered directionally: along each axis the finer
158
+ * voxels are composited front to back using their surface normals, so a cone only gathers
159
+ * the surfaces facing it. This reduces light bleeding through thin walls and floors (e.g. a
160
+ * sunlit floor brightening the ceiling of the room below) at the cost of additional memory
161
+ * and a more expensive radiance lookup. Changing it triggers a re-voxelization.
162
+ *
163
+ * @type {boolean}
164
+ * @default false
165
+ */
166
+ this.directionalRadiance = false;
167
+
168
+ /**
169
+ * Maximum number of lights injected into the volume.
170
+ *
171
+ * @type {number}
172
+ * @default 8
173
+ */
174
+ this.maxLights = 8;
175
+
176
+ /**
177
+ * Set to `true` to re-voxelize the scene in the next update.
178
+ *
179
+ * @type {boolean}
180
+ * @default true
181
+ */
182
+ this.needsUpdate = true;
183
+
184
+ /**
185
+ * Set to `true` to re-inject lighting in the next update. Changes of the lights and of the
186
+ * injection parameters are detected automatically, so this is rarely needed.
187
+ *
188
+ * @type {boolean}
189
+ * @default true
190
+ */
191
+ this.lightingNeedsUpdate = true;
192
+
193
+ /**
194
+ * Maximum cone length in world units. `0` means unbounded.
195
+ *
196
+ * @type {UniformNode<float>}
197
+ * @default 0
198
+ */
199
+ this.maxDistance = uniform( 0 );
200
+
201
+ /**
202
+ * Step size relative to the texel size of the sampled mip level.
203
+ *
204
+ * @type {UniformNode<float>}
205
+ * @default 0.5
206
+ */
207
+ this.stepScale = uniform( 0.5 );
208
+
209
+ /**
210
+ * Aperture in degrees of the visibility cones traced towards lights that do not provide a
211
+ * shadow map. Wider cones are cheaper but soften the injected shadows.
212
+ *
213
+ * @type {UniformNode<float>}
214
+ * @default 10
215
+ */
216
+ this.shadowConeAngle = uniform( 10 );
217
+
218
+ /**
219
+ * Aperture of the cones used for the cached bounces in degrees.
220
+ *
221
+ * @type {UniformNode<float>}
222
+ * @default 60
223
+ */
224
+ this.bounceConeAngle = uniform( 60 );
225
+
226
+ /**
227
+ * The minimum corner of the volume.
228
+ *
229
+ * @type {UniformNode<vec3>}
230
+ */
231
+ this.boundsMinNode = uniform( new Vector3() );
232
+
233
+ /**
234
+ * The size of the volume.
235
+ *
236
+ * @type {UniformNode<vec3>}
237
+ */
238
+ this.volumeSizeNode = uniform( new Vector3( 1, 1, 1 ) );
239
+
240
+ /**
241
+ * The size of a voxel.
242
+ *
243
+ * @type {UniformNode<float>}
244
+ */
245
+ this.voxelSizeNode = uniform( 1 );
246
+
247
+ /**
248
+ * The highest valid mip level of the voxel textures.
249
+ *
250
+ * @type {UniformNode<float>}
251
+ */
252
+ this.maxLevelNode = uniform( 0 );
253
+
254
+ /**
255
+ * The per-axis opacity of the scene (`xyz`) and the occupancy (`w`) as a mip chain.
256
+ *
257
+ * @type {Storage3DTexture}
258
+ */
259
+ this.opacityTexture = this._createTexture( 'VXGI.Opacity', UnsignedByteType, true, 1, 1, 1 );
260
+
261
+ /**
262
+ * The radiance of the scene including cached bounces, premultiplied by occupancy, as a mip chain.
263
+ * With {@link VXGIVolume#directionalRadiance} only the finest level is used and the coarser
264
+ * levels live in {@link VXGIVolume#directionalTexture}.
265
+ *
266
+ * @type {Storage3DTexture}
267
+ */
268
+ this.radianceTexture = this._createTexture( 'VXGI.Radiance', HalfFloatType, true, 1, 1, 1 );
269
+
270
+ /**
271
+ * Texture node of {@link VXGIVolume#opacityTexture}. Stays valid across re-allocations of the volume.
272
+ *
273
+ * @type {Texture3DNode}
274
+ */
275
+ this.opacityNode = texture3D( this.opacityTexture );
276
+
277
+ /**
278
+ * Texture node of {@link VXGIVolume#radianceTexture}. Stays valid across re-allocations of the volume.
279
+ *
280
+ * @type {Texture3DNode}
281
+ */
282
+ this.radianceNode = texture3D( this.radianceTexture );
283
+
284
+ /**
285
+ * The coarser radiance levels filtered directionally for the six directions a ray can travel
286
+ * (+x, -x, +y, -y, +z, -z), only allocated with {@link VXGIVolume#directionalRadiance}. Each
287
+ * texel holds the radiance of the surfaces facing the direction premultiplied by their weight
288
+ * (`rgb`) and the weight (`a`); occlusion comes from the opacity mip chain. The six directions
289
+ * are stored side by side along x in one half-resolution mip chain; level `n` of the volume is
290
+ * its level `n - 1`.
291
+ *
292
+ * @type {Storage3DTexture}
293
+ */
294
+ this.directionalTexture = this._createTexture( 'VXGI.Directional', HalfFloatType, true, 6, 1, 1 );
295
+
296
+ /**
297
+ * Texture node of {@link VXGIVolume#directionalTexture}. Stays valid across re-allocations of the volume.
298
+ *
299
+ * @type {Texture3DNode}
300
+ */
301
+ this.directionalNode = texture3D( this.directionalTexture );
302
+
303
+ /**
304
+ * The width of one direction block of the directional texture in texels (at its level 0).
305
+ *
306
+ * @type {UniformNode<float>}
307
+ */
308
+ this.directionalWidthNode = uniform( 1 );
309
+
310
+ // private
311
+
312
+ this._traceDistance = this.maxDistance.greaterThan( 0 ).select( this.maxDistance, float( 1e10 ) );
313
+ this._allocated = false;
314
+
315
+ this._gridSize = new Vector3();
316
+ this._levels = 1;
317
+ this._voxelCount = 0;
318
+ this._gridSizeNode = [ uniform( 1, 'uint' ), uniform( 1, 'uint' ), uniform( 1, 'uint' ) ];
319
+ this._voxelCountNode = uniform( 0, 'uint' );
320
+
321
+ this._pingPongTexture = null;
322
+ this._directTexture = null;
323
+ this._pingPongDirectionalTexture = null;
324
+ this._normalTexture = null;
325
+ this._directional = false;
326
+
327
+ this._occupancyAttribute = null;
328
+ this._triangleIdAttribute = null;
329
+ this._triangleAttribute = null;
330
+ this._trianglesNode = null;
331
+ this._triangleCount = 0;
332
+ this._triangleCountNode = uniform( 0, 'uint' );
333
+
334
+ this._kernels = null;
335
+
336
+ this._lightsArray = [];
337
+ for ( let i = 0; i < this.maxLights * 4; i ++ ) this._lightsArray.push( new Vector4() );
338
+ this._lightsNode = uniformArray( this._lightsArray, 'vec4' );
339
+ this._lightCountNode = uniform( 0, 'uint' );
340
+ this._lightKey = '';
341
+
342
+ // per light slot: type, shadow map (2D depth or cube depth) and its matrix / bias / near / far
343
+
344
+ this._lightTypes = [];
345
+ this._lightShadows = [];
346
+ this._shadowMatrices = [];
347
+ this._shadowParams = [];
348
+
349
+ for ( let i = 0; i < this.maxLights; i ++ ) {
350
+
351
+ this._shadowMatrices.push( uniform( new Matrix4() ) );
352
+ this._shadowParams.push( uniform( new Vector4() ) );
353
+
354
+ }
355
+
356
+ this._lightsKey = '';
357
+
358
+
359
+ this._exclude = new Set();
360
+
361
+ }
362
+
363
+ /**
364
+ * The effective maximum cone length as a node.
365
+ *
366
+ * @type {Node<float>}
367
+ */
368
+ get traceDistanceNode() {
369
+
370
+ return this._traceDistance;
371
+
372
+ }
373
+
374
+ /**
375
+ * Updates the volume if required. Voxelizes the scene when `needsUpdate` is set and re-injects
376
+ * lighting when `lightingNeedsUpdate` is set or a light has changed.
377
+ *
378
+ * @param {Renderer} renderer - The renderer.
379
+ * @param {Scene} scene - The scene.
380
+ */
381
+ update( renderer, scene ) {
382
+
383
+ _rendererState = RendererUtils.resetRendererState( renderer, _rendererState );
384
+
385
+ // the volume might be updated before the scene was rendered for the first time
386
+
387
+ if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld();
388
+
389
+ if ( this.needsUpdate === true || this._allocated === false || this._directional !== this.directionalRadiance ) {
390
+
391
+ this._voxelize( renderer, scene );
392
+
393
+ this.needsUpdate = false;
394
+ this.lightingNeedsUpdate = true;
395
+
396
+ }
397
+
398
+ const lightKey = this._collectLights( renderer, scene );
399
+
400
+ if ( this.lightingNeedsUpdate === true || lightKey !== this._lightKey ) {
401
+
402
+ this._lightKey = lightKey;
403
+ this._updateLighting( renderer );
404
+
405
+ this.lightingNeedsUpdate = false;
406
+
407
+ }
408
+
409
+ RendererUtils.restoreRendererState( renderer, _rendererState );
410
+
411
+ }
412
+
413
+ /**
414
+ * Frees internal resources.
415
+ */
416
+ dispose() {
417
+
418
+ this._disposeGrid();
419
+
420
+ }
421
+
422
+ // geometry
423
+
424
+ _voxelize( renderer, scene ) {
425
+
426
+ this._exclude.clear();
427
+
428
+ const bounds = this.bounds.isEmpty() ? computeSceneBounds( scene, this.layers, this._exclude, _box ) : _box.copy( this.bounds );
429
+
430
+ if ( bounds.isEmpty() ) bounds.min.set( - 1, - 1, - 1 ), bounds.max.set( 1, 1, 1 );
431
+
432
+ // grid setup: the longest axis gets `resolution` voxels, all dimensions are padded by
433
+ // one voxel and rounded up so every mip level has integer dimensions
434
+
435
+ bounds.getSize( _size );
436
+
437
+ const resolution = Math.max( 8, this.resolution );
438
+ const voxelSize = Math.max( _size.x, _size.y, _size.z ) / resolution;
439
+ let levels = MathUtils.clamp( Math.floor( Math.log2( resolution ) ) - 2, 1, 8 );
440
+ const multiple = 2 ** ( levels - 1 );
441
+
442
+ const gridSize = _size.clone();
443
+ gridSize.x = Math.ceil( ( Math.ceil( _size.x / voxelSize ) + 2 ) / multiple ) * multiple;
444
+ gridSize.y = Math.ceil( ( Math.ceil( _size.y / voxelSize ) + 2 ) / multiple ) * multiple;
445
+ gridSize.z = Math.ceil( ( Math.ceil( _size.z / voxelSize ) + 2 ) / multiple ) * multiple;
446
+
447
+ levels = Math.min( levels, Math.floor( Math.log2( Math.max( gridSize.x, gridSize.y ) ) ) + 1 );
448
+
449
+ this.boundsMinNode.value.copy( bounds.min ).subScalar( voxelSize );
450
+ this.volumeSizeNode.value.copy( gridSize ).multiplyScalar( voxelSize );
451
+ this.worldBounds.min.copy( this.boundsMinNode.value );
452
+ this.worldBounds.max.copy( this.boundsMinNode.value ).add( this.volumeSizeNode.value );
453
+ this.voxelSizeNode.value = voxelSize;
454
+ this.maxLevelNode.value = levels - 1;
455
+
456
+ if ( gridSize.equals( this._gridSize ) === false || levels !== this._levels || this._directional !== this.directionalRadiance ) {
457
+
458
+ this._allocateGrid( gridSize, levels );
459
+
460
+ }
461
+
462
+ // collect triangles
463
+
464
+ _box.min.copy( this.boundsMinNode.value );
465
+ _box.max.copy( this.boundsMinNode.value ).add( this.volumeSizeNode.value );
466
+
467
+ const { data, count } = collectSceneTriangles( scene, {
468
+ bounds: _box,
469
+ layers: this.layers,
470
+ exclude: this._exclude,
471
+ subVoxelSize: voxelSize * 0.5,
472
+ maxEdge: MAX_EDGE_SUBVOXELS,
473
+ minOpacity: this.minOpacity
474
+ } );
475
+
476
+ this._triangleCount = count;
477
+ this._triangleCountNode.value = count;
478
+ this._triangleAttribute = new StorageBufferAttribute( count > 0 ? data : new Float32Array( TRIANGLE_STRIDE ), 4 );
479
+ this._trianglesNode = storage( this._triangleAttribute, 'vec4', this._triangleAttribute.count ).toReadOnly();
480
+
481
+ // kernels depend on the triangle buffer and grid
482
+
483
+ this._kernels = null;
484
+ const kernels = this._getKernels();
485
+
486
+ renderer.compute( kernels.clear );
487
+
488
+ if ( count > 0 ) renderer.compute( kernels.voxelize );
489
+
490
+ renderer.compute( kernels.resolve );
491
+
492
+ for ( const kernel of kernels.opacityMips ) renderer.compute( kernel );
493
+
494
+ }
495
+
496
+ _allocateGrid( gridSize, levels ) {
497
+
498
+ this._disposeGrid();
499
+
500
+ const { x, y, z } = gridSize;
501
+
502
+ this._gridSize.copy( gridSize );
503
+ this._levels = levels;
504
+ this._voxelCount = x * y * z;
505
+ this._gridSizeNode[ 0 ].value = x;
506
+ this._gridSizeNode[ 1 ].value = y;
507
+ this._gridSizeNode[ 2 ].value = z;
508
+ this._voxelCountNode.value = this._voxelCount;
509
+
510
+ const directional = this._directional = this.directionalRadiance;
511
+
512
+ this.opacityTexture = this._createTexture( 'VXGI.Opacity', UnsignedByteType, true, x, y, z );
513
+ this.radianceTexture = this._createTexture( 'VXGI.Radiance', HalfFloatType, directional === false, x, y, z );
514
+ this._pingPongTexture = this._createTexture( 'VXGI.RadiancePingPong', HalfFloatType, directional === false, x, y, z );
515
+ this._directTexture = this._createTexture( 'VXGI.Direct', HalfFloatType, false, x, y, z );
516
+
517
+ // the directional levels start at level 1, i.e. at half resolution, six direction blocks along x
518
+
519
+ if ( directional === true ) {
520
+
521
+ const hx = Math.ceil( x / 2 ), hy = Math.ceil( y / 2 ), hz = Math.ceil( z / 2 );
522
+
523
+ this.directionalTexture = this._createTexture( 'VXGI.Directional', HalfFloatType, true, 6 * hx, hy, hz );
524
+ this._pingPongDirectionalTexture = this._createTexture( 'VXGI.DirectionalPingPong', HalfFloatType, true, 6 * hx, hy, hz );
525
+ this._normalTexture = this._createTexture( 'VXGI.Normal', UnsignedByteType, false, x, y, z );
526
+ this.directionalWidthNode.value = hx;
527
+
528
+ } else {
529
+
530
+ this.directionalTexture = this._createTexture( 'VXGI.Directional', HalfFloatType, true, 6, 1, 1 );
531
+ this.directionalWidthNode.value = 1;
532
+
533
+ }
534
+
535
+ this.opacityNode.value = this.opacityTexture;
536
+ this.radianceNode.value = this.radianceTexture;
537
+ this.directionalNode.value = this.directionalTexture;
538
+
539
+ this._allocated = true;
540
+
541
+ this._occupancyAttribute = new StorageBufferAttribute( this._voxelCount, 1, Uint32Array );
542
+ this._triangleIdAttribute = new StorageBufferAttribute( this._voxelCount, 1, Uint32Array );
543
+
544
+ }
545
+
546
+ _createTexture( name, type, mipmaps, x, y, z ) {
547
+
548
+ const texture = new Storage3DTexture( x, y, z );
549
+ texture.name = name;
550
+ texture.type = type;
551
+ texture.format = RGBAFormat;
552
+ texture.generateMipmaps = mipmaps;
553
+ texture.mipmapsAutoUpdate = false;
554
+ texture.minFilter = mipmaps ? LinearMipmapLinearFilter : LinearFilter;
555
+ texture.magFilter = LinearFilter;
556
+ texture.wrapS = texture.wrapT = texture.wrapR = ClampToEdgeWrapping;
557
+
558
+ return texture;
559
+
560
+ }
561
+
562
+ _disposeGrid() {
563
+
564
+ this.opacityTexture.dispose();
565
+ this.radianceTexture.dispose();
566
+ this.directionalTexture.dispose();
567
+
568
+ if ( this._pingPongTexture !== null ) {
569
+
570
+ this._pingPongTexture.dispose();
571
+ this._directTexture.dispose();
572
+
573
+ this._pingPongTexture = null;
574
+ this._directTexture = null;
575
+
576
+ }
577
+
578
+ if ( this._normalTexture !== null ) {
579
+
580
+ this._normalTexture.dispose();
581
+ this._pingPongDirectionalTexture.dispose();
582
+
583
+ this._normalTexture = null;
584
+ this._pingPongDirectionalTexture = null;
585
+
586
+ }
587
+
588
+ this._occupancyAttribute = null;
589
+ this._triangleIdAttribute = null;
590
+ this._kernels = null;
591
+ this._allocated = false;
592
+
593
+ }
594
+
595
+ // lighting
596
+
597
+ _collectLights( renderer, scene ) {
598
+
599
+ const lights = this._lightsArray;
600
+ let count = 0;
601
+ let key = renderer.shadowMap.enabled ? 'S' : 'N';
602
+
603
+ this._lightTypes.length = 0;
604
+ this._lightShadows.length = 0;
605
+
606
+ scene.traverseVisible( ( object ) => {
607
+
608
+ if ( object.isLight !== true || count >= this.maxLights ) return;
609
+
610
+ let type;
611
+
612
+ if ( object.isDirectionalLight === true ) type = 0;
613
+ else if ( object.isSpotLight === true ) type = 2;
614
+ else if ( object.isPointLight === true ) type = 1;
615
+ else return;
616
+
617
+ const l0 = lights[ count * 4 ], l1 = lights[ count * 4 + 1 ], l2 = lights[ count * 4 + 2 ], l3 = lights[ count * 4 + 3 ];
618
+
619
+ _position.setFromMatrixPosition( object.matrixWorld );
620
+ l0.set( _position.x, _position.y, _position.z, type );
621
+
622
+ if ( type === 0 || type === 2 ) {
623
+
624
+ _target.setFromMatrixPosition( object.target.matrixWorld );
625
+
626
+ if ( type === 0 ) {
627
+
628
+ _target.subVectors( _position, _target ).normalize(); // direction towards the light
629
+
630
+ } else {
631
+
632
+ _target.subVectors( _target, _position ).normalize(); // spot axis
633
+
634
+ }
635
+
636
+ } else {
637
+
638
+ _target.set( 0, 0, 0 );
639
+
640
+ }
641
+
642
+ l1.set( _target.x, _target.y, _target.z, object.distance || 0 );
643
+
644
+ const color = object.color, intensity = object.intensity;
645
+ l2.set( color.r * intensity, color.g * intensity, color.b * intensity, object.decay !== undefined ? object.decay : 2 );
646
+
647
+ // the light's shadow map is used for the injected visibility when available
648
+
649
+ let shadowTexture = null;
650
+
651
+ if ( object.castShadow === true && renderer.shadowMap.enabled === true && object.shadow.map !== null && object.shadow.map.depthTexture !== undefined ) {
652
+
653
+ const shadow = object.shadow;
654
+
655
+ shadowTexture = shadow.map.depthTexture;
656
+ this._shadowMatrices[ count ].value.copy( shadow.matrix );
657
+ this._shadowParams[ count ].value.set( shadow.bias, shadow.camera.near, shadow.camera.far, 0 );
658
+
659
+ }
660
+
661
+ this._lightTypes.push( type );
662
+ this._lightShadows.push( shadowTexture );
663
+
664
+ if ( type === 2 ) {
665
+
666
+ l3.set( Math.cos( object.angle ), Math.cos( object.angle * ( 1 - object.penumbra ) ), 0, 0 );
667
+
668
+ } else {
669
+
670
+ l3.set( 0, 0, 0, 0 );
671
+
672
+ }
673
+
674
+ key += `|${ type },${ l0.x.toFixed( 3 ) },${ l0.y.toFixed( 3 ) },${ l0.z.toFixed( 3 ) },${ l1.x.toFixed( 4 ) },${ l1.y.toFixed( 4 ) },${ l1.z.toFixed( 4 ) },${ l1.w },${ l2.x.toFixed( 3 ) },${ l2.y.toFixed( 3 ) },${ l2.z.toFixed( 3 ) },${ l2.w },${ l3.x.toFixed( 4 ) },${ l3.y.toFixed( 4 ) },${ shadowTexture !== null ? shadowTexture.uuid : '-' }`;
675
+
676
+ count ++;
677
+
678
+ } );
679
+
680
+ this._lightCountNode.value = count;
681
+
682
+ // injection parameters that are baked into the cached radiance
683
+
684
+ key += `|${ this.bounces },${ this.bounceConeAngle.value },${ this.shadowConeAngle.value },${ this.stepScale.value },${ this.maxDistance.value }`;
685
+
686
+ // the inject kernels are specialized for the set of lights and their shadow maps
687
+
688
+ this._lightsKey = this._lightTypes.map( ( type, i ) => type + ':' + ( this._lightShadows[ i ] !== null ? this._lightShadows[ i ].uuid : '-' ) ).join( '|' );
689
+
690
+ return key;
691
+
692
+ }
693
+
694
+ _updateLighting( renderer ) {
695
+
696
+ const kernels = this._getKernels();
697
+ const bounces = Math.max( 0, Math.round( this.bounces ) );
698
+
699
+ // the ping-pong order is chosen so the final result always ends up in `radianceTexture`
700
+
701
+ let index = bounces % 2;
702
+
703
+ if ( kernels.inject[ index ] === null || kernels.injectKey[ index ] !== this._lightsKey ) {
704
+
705
+ kernels.inject[ index ] = this._createInjectKernel( renderer, index );
706
+ kernels.injectKey[ index ] = this._lightsKey;
707
+
708
+ }
709
+
710
+ renderer.compute( kernels.inject[ index ] );
711
+
712
+ for ( const kernel of kernels.radianceMips[ index ] ) renderer.compute( kernel );
713
+
714
+ for ( let i = 0; i < bounces; i ++ ) {
715
+
716
+ const target = 1 - index;
717
+
718
+ renderer.compute( kernels.bounce[ target ] );
719
+
720
+ for ( const kernel of kernels.radianceMips[ target ] ) renderer.compute( kernel );
721
+
722
+ index = target;
723
+
724
+ }
725
+
726
+ }
727
+
728
+ // kernels
729
+
730
+ _getRadianceTexture( index ) {
731
+
732
+ return index === 0 ? this.radianceTexture : this._pingPongTexture;
733
+
734
+ }
735
+
736
+ _getDirectionalTexture( index ) {
737
+
738
+ return index === 0 ? this.directionalTexture : this._pingPongDirectionalTexture;
739
+
740
+ }
741
+
742
+ _getKernels() {
743
+
744
+ if ( this._kernels !== null ) return this._kernels;
745
+
746
+ const kernels = {
747
+ clear: this._createClearKernel(),
748
+ voxelize: this._createVoxelizeKernel(),
749
+ resolve: this._createResolveKernel(),
750
+ opacityMips: [],
751
+ radianceMips: [[], []],
752
+ inject: [ null, null ],
753
+ injectKey: [ null, null ],
754
+ bounce: [ null, null ]
755
+ };
756
+
757
+ for ( let level = 1; level < this._levels; level ++ ) {
758
+
759
+ kernels.opacityMips.push( this._createOpacityMipKernel( level ) );
760
+
761
+ // the coarser radiance levels are either isotropic mips or directionally filtered
762
+
763
+ for ( let index = 0; index < 2; index ++ ) {
764
+
765
+ kernels.radianceMips[ index ].push( this._directional === true ? this._createDirectionalKernel( index, level ) : this._createRadianceMipKernel( this._getRadianceTexture( index ), level ) );
766
+
767
+ }
768
+
769
+ }
770
+
771
+ kernels.bounce[ 0 ] = this._createBounceKernel( 0 );
772
+ kernels.bounce[ 1 ] = this._createBounceKernel( 1 );
773
+
774
+ this._kernels = kernels;
775
+
776
+ return kernels;
777
+
778
+ }
779
+
780
+ _getLevelSize( level ) {
781
+
782
+ return [ this._gridSize.x >> level, this._gridSize.y >> level, this._gridSize.z >> level ];
783
+
784
+ }
785
+
786
+ /**
787
+ * Returns the 3D coordinates of the given linear voxel index.
788
+ *
789
+ * @private
790
+ */
791
+ _coords( index, size ) {
792
+
793
+ const [ x, y ] = size;
794
+
795
+ return uvec3( index.mod( uint( x ) ), index.div( uint( x ) ).mod( uint( y ) ), index.div( uint( x * y ) ) );
796
+
797
+ }
798
+
799
+ _createClearKernel() {
800
+
801
+ const occupancy = storage( this._occupancyAttribute, 'uint', this._voxelCount );
802
+ const triangleIds = storage( this._triangleIdAttribute, 'uint', this._voxelCount );
803
+ const voxelCount = this._voxelCountNode;
804
+
805
+ return Fn( () => {
806
+
807
+ If( instanceIndex.lessThan( voxelCount ), () => {
808
+
809
+ occupancy.element( instanceIndex ).assign( uint( 0 ) );
810
+ triangleIds.element( instanceIndex ).assign( uint( 0 ) );
811
+
812
+ } );
813
+
814
+ } )().compute( this._voxelCount ).setName( 'VXGI.Clear' );
815
+
816
+ }
817
+
818
+ _createVoxelizeKernel() {
819
+
820
+ const occupancy = storage( this._occupancyAttribute, 'uint', this._voxelCount ).toAtomic();
821
+ const triangleIds = storage( this._triangleIdAttribute, 'uint', this._voxelCount );
822
+ const triangles = this._trianglesNode;
823
+ const triangleCount = this._triangleCountNode;
824
+ const boundsMin = this.boundsMinNode;
825
+ const invSubVoxel = float( 2 ).div( this.voxelSizeNode );
826
+ const [ gx, gy ] = this._gridSizeNode;
827
+ const subSize = vec3( this._gridSize.x * 2, this._gridSize.y * 2, this._gridSize.z * 2 );
828
+
829
+ return Fn( () => {
830
+
831
+ const triangleIndex = instanceIndex;
832
+
833
+ If( triangleIndex.lessThan( triangleCount ), () => {
834
+
835
+ const base = triangleIndex.mul( 5 ).toConst();
836
+
837
+ // triangle in sub-voxel space
838
+
839
+ const p0 = triangles.element( base ).xyz.sub( boundsMin ).mul( invSubVoxel ).toConst();
840
+ const p1 = triangles.element( base.add( 1 ) ).xyz.sub( boundsMin ).mul( invSubVoxel ).toConst();
841
+ const p2 = triangles.element( base.add( 2 ) ).xyz.sub( boundsMin ).mul( invSubVoxel ).toConst();
842
+
843
+ const n = cross( p1.sub( p0 ), p2.sub( p0 ) ).toConst();
844
+ const an = abs( n ).toConst();
845
+
846
+ // project along the dominant axis: swizzle so the dominant axis becomes z
847
+
848
+ const isZ = an.z.greaterThanEqual( an.x ).and( an.z.greaterThanEqual( an.y ) ).toConst();
849
+ const isY = isZ.not().and( an.y.greaterThanEqual( an.x ) ).toConst();
850
+
851
+ const swizzle = ( v ) => select( isZ, v.xyz, select( isY, v.zxy, v.yzx ) );
852
+
853
+ const q0 = swizzle( p0 ).toConst();
854
+ const q1 = swizzle( p1 ).toConst();
855
+ const q2 = swizzle( p2 ).toConst();
856
+ const nq = swizzle( n ).toConst();
857
+ const sd = swizzle( subSize ).toConst();
858
+
859
+ const qmin = min( q0, min( q1, q2 ) ).toConst();
860
+ const qmax = max( q0, max( q1, q2 ) ).toConst();
861
+
862
+ const i0 = int( floor( qmin.x ) ).max( 0 ).toConst();
863
+ const i1 = int( floor( qmax.x ) ).min( int( sd.x ).sub( 1 ) ).toConst();
864
+ const j0 = int( floor( qmin.y ) ).max( 0 ).toConst();
865
+ const j1 = int( floor( qmax.y ) ).min( int( sd.y ).sub( 1 ) ).toConst();
866
+
867
+ // conservative edge functions
868
+
869
+ const areaSign = sign( nq.z ).toConst();
870
+
871
+ const edge = ( a, b ) => {
872
+
873
+ const normal = vec2( a.y.sub( b.y ), b.x.sub( a.x ) ).mul( areaSign ).toConst();
874
+ const bias = float( 0.5 ).mul( abs( normal.x ).add( abs( normal.y ) ) ).toConst();
875
+
876
+ return { a, normal, bias };
877
+
878
+ };
879
+
880
+ const e0 = edge( q0.xy, q1.xy );
881
+ const e1 = edge( q1.xy, q2.xy );
882
+ const e2 = edge( q2.xy, q0.xy );
883
+
884
+ const halfExtent = float( 0.5 ).mul( abs( nq.x ).add( abs( nq.y ) ) ).div( abs( nq.z ) ).toConst();
885
+
886
+ Loop( { start: i0, end: i1, type: 'int', condition: '<=', name: 'i' }, { start: j0, end: j1, type: 'int', condition: '<=', name: 'j' }, ( { i, j } ) => {
887
+
888
+ const c = vec2( float( i ).add( 0.5 ), float( j ).add( 0.5 ) ).toConst();
889
+
890
+ const inside = dot( e0.normal, c.sub( e0.a ) ).add( e0.bias ).greaterThanEqual( 0 )
891
+ .and( dot( e1.normal, c.sub( e1.a ) ).add( e1.bias ).greaterThanEqual( 0 ) )
892
+ .and( dot( e2.normal, c.sub( e2.a ) ).add( e2.bias ).greaterThanEqual( 0 ) );
893
+
894
+ If( inside, () => {
895
+
896
+ // depth range of the triangle plane within this column
897
+
898
+ const wc = q0.z.sub( nq.x.mul( c.x.sub( q0.x ) ).add( nq.y.mul( c.y.sub( q0.y ) ) ).div( nq.z ) ).toConst();
899
+
900
+ const k0 = int( floor( max( wc.sub( halfExtent ), qmin.z ) ) ).max( 0 ).toConst();
901
+ const k1 = int( floor( min( wc.add( halfExtent ), qmax.z ) ) ).min( int( sd.z ).sub( 1 ) ).toConst();
902
+
903
+ Loop( { start: k0, end: k1, type: 'int', condition: '<=', name: 'k' }, ( { k } ) => {
904
+
905
+ const s = select( isZ, ivec3( i, j, k ), select( isY, ivec3( j, k, i ), ivec3( k, i, j ) ) ).toConst();
906
+
907
+ const voxel = uvec3( s.div( 2 ) ).toConst();
908
+ const bit = uint( s.x.bitAnd( 1 ) ).bitOr( uint( s.y.bitAnd( 1 ) ).shiftLeft( uint( 1 ) ) ).bitOr( uint( s.z.bitAnd( 1 ) ).shiftLeft( uint( 2 ) ) ).toConst();
909
+ const voxelIndex = voxel.x.add( gx.mul( voxel.y.add( gy.mul( voxel.z ) ) ) ).toConst();
910
+
911
+ atomicOr( occupancy.element( voxelIndex ), uint( 1 ).shiftLeft( bit ) );
912
+ triangleIds.element( voxelIndex ).assign( triangleIndex.add( 1 ) );
913
+
914
+ } );
915
+
916
+ } );
917
+
918
+ } );
919
+
920
+ } );
921
+
922
+ } )().compute( this._triangleCount ).setName( 'VXGI.Voxelize' );
923
+
924
+ }
925
+
926
+ _createResolveKernel() {
927
+
928
+ const occupancy = storage( this._occupancyAttribute, 'uint', this._voxelCount ).toReadOnly();
929
+ const opacityTexture = this.opacityTexture;
930
+ const voxelCount = this._voxelCountNode;
931
+ const size = this._getLevelSize( 0 );
932
+
933
+ return Fn( () => {
934
+
935
+ If( instanceIndex.lessThan( voxelCount ), () => {
936
+
937
+ const coords = this._coords( instanceIndex, size ).toConst();
938
+ const bits = occupancy.element( instanceIndex ).toConst();
939
+
940
+ // per-axis coverage from the 2x2x2 sub-voxel occupancy
941
+
942
+ const coverage = ( m0, m1, m2, m3 ) => {
943
+
944
+ return select( bits.bitAnd( uint( m0 ) ).notEqual( uint( 0 ) ), 0.25, 0 )
945
+ .add( select( bits.bitAnd( uint( m1 ) ).notEqual( uint( 0 ) ), 0.25, 0 ) )
946
+ .add( select( bits.bitAnd( uint( m2 ) ).notEqual( uint( 0 ) ), 0.25, 0 ) )
947
+ .add( select( bits.bitAnd( uint( m3 ) ).notEqual( uint( 0 ) ), 0.25, 0 ) );
948
+
949
+ };
950
+
951
+ const opacityX = coverage( 0x03, 0x0C, 0x30, 0xC0 );
952
+ const opacityY = coverage( 0x05, 0x0A, 0x50, 0xA0 );
953
+ const opacityZ = coverage( 0x11, 0x22, 0x44, 0x88 );
954
+ const occupied = float( countOneBits( bits ) ).div( 8 );
955
+
956
+ textureStore( opacityTexture, coords, vec4( opacityX, opacityY, opacityZ, occupied ) );
957
+
958
+ } );
959
+
960
+ } )().compute( this._voxelCount ).setName( 'VXGI.Resolve' );
961
+
962
+ }
963
+
964
+ _createOpacityMipKernel( level ) {
965
+
966
+ const size = this._getLevelSize( level );
967
+ const count = size[ 0 ] * size[ 1 ] * size[ 2 ];
968
+
969
+ const source = new MipStorageTexture3DNode( this.opacityTexture, 'readOnly', level - 1 );
970
+ const target = new MipStorageTexture3DNode( this.opacityTexture, 'writeOnly', level );
971
+
972
+ return Fn( () => {
973
+
974
+ If( instanceIndex.lessThan( uint( count ) ), () => {
975
+
976
+ const coords = this._coords( instanceIndex, size ).toConst();
977
+ const base = ivec3( coords.mul( uint( 2 ) ) ).toConst();
978
+
979
+ const children = [];
980
+
981
+ for ( let z = 0; z < 2; z ++ ) {
982
+
983
+ for ( let y = 0; y < 2; y ++ ) {
984
+
985
+ for ( let x = 0; x < 2; x ++ ) {
986
+
987
+ children[ x + y * 2 + z * 4 ] = source.load( base.add( ivec3( x, y, z ) ) ).toConst();
988
+
989
+ }
990
+
991
+ }
992
+
993
+ }
994
+
995
+ // along each axis the two children are combined, across the axis they are averaged
996
+
997
+ const child = ( x, y, z ) => children[ x + y * 2 + z * 4 ];
998
+ const combine = ( a, b ) => a.oneMinus().mul( b.oneMinus() ).oneMinus();
999
+
1000
+ let opacityX = float( 0 ), opacityY = float( 0 ), opacityZ = float( 0 ), occupied = float( 0 );
1001
+
1002
+ for ( let a = 0; a < 2; a ++ ) {
1003
+
1004
+ for ( let b = 0; b < 2; b ++ ) {
1005
+
1006
+ opacityX = opacityX.add( combine( child( 0, a, b ).x, child( 1, a, b ).x ) );
1007
+ opacityY = opacityY.add( combine( child( a, 0, b ).y, child( a, 1, b ).y ) );
1008
+ opacityZ = opacityZ.add( combine( child( a, b, 0 ).z, child( a, b, 1 ).z ) );
1009
+
1010
+ }
1011
+
1012
+ }
1013
+
1014
+ for ( let i = 0; i < 8; i ++ ) occupied = occupied.add( children[ i ].w );
1015
+
1016
+ textureStore( target, coords, vec4( opacityX.mul( 0.25 ), opacityY.mul( 0.25 ), opacityZ.mul( 0.25 ), occupied.mul( 0.125 ) ) );
1017
+
1018
+ } );
1019
+
1020
+ } )().compute( count ).setName( 'VXGI.OpacityMip' + level );
1021
+
1022
+ }
1023
+
1024
+ _createRadianceMipKernel( radianceTexture, level ) {
1025
+
1026
+ const size = this._getLevelSize( level );
1027
+ const count = size[ 0 ] * size[ 1 ] * size[ 2 ];
1028
+
1029
+ const source = new MipStorageTexture3DNode( radianceTexture, 'readOnly', level - 1 );
1030
+ const target = new MipStorageTexture3DNode( radianceTexture, 'writeOnly', level );
1031
+
1032
+ return Fn( () => {
1033
+
1034
+ If( instanceIndex.lessThan( uint( count ) ), () => {
1035
+
1036
+ const coords = this._coords( instanceIndex, size ).toConst();
1037
+ const base = ivec3( coords.mul( uint( 2 ) ) ).toConst();
1038
+
1039
+ let sum = vec4( 0 );
1040
+
1041
+ for ( let z = 0; z < 2; z ++ ) {
1042
+
1043
+ for ( let y = 0; y < 2; y ++ ) {
1044
+
1045
+ for ( let x = 0; x < 2; x ++ ) {
1046
+
1047
+ sum = sum.add( source.load( base.add( ivec3( x, y, z ) ) ) );
1048
+
1049
+ }
1050
+
1051
+ }
1052
+
1053
+ }
1054
+
1055
+ textureStore( target, coords, sum.mul( 0.125 ) );
1056
+
1057
+ } );
1058
+
1059
+ } )().compute( count ).setName( 'VXGI.RadianceMip' + level );
1060
+
1061
+ }
1062
+
1063
+ /**
1064
+ * Creates the kernel that filters the six direction blocks of the directional texture at a
1065
+ * level from the next finer level: along the direction's axis the two finer voxels are
1066
+ * composited front to back with the front voxel's opacity along that axis, across the axis
1067
+ * they are averaged. Level 1 is filtered from the isotropic finest level, where a voxel only
1068
+ * contributes to the directions its surface faces against.
1069
+ *
1070
+ * @private
1071
+ */
1072
+ _createDirectionalKernel( index, level ) {
1073
+
1074
+ const size = this._getLevelSize( level );
1075
+ const count = size[ 0 ] * size[ 1 ] * size[ 2 ];
1076
+ const texture = this._getDirectionalTexture( index );
1077
+ const opacity = texture3D( this.opacityTexture );
1078
+ const radiance = texture3D( this._getRadianceTexture( index ) );
1079
+ const normals = texture3D( this._normalTexture );
1080
+
1081
+ // level n of the volume is level n - 1 of the half resolution directional texture
1082
+
1083
+ const source = level === 1 ? null : new MipStorageTexture3DNode( texture, 'readOnly', level - 2 );
1084
+ const target = new MipStorageTexture3DNode( texture, 'writeOnly', level - 1 );
1085
+ const blockWidth = size[ 0 ];
1086
+
1087
+ return Fn( () => {
1088
+
1089
+ If( instanceIndex.lessThan( uint( count ) ), () => {
1090
+
1091
+ const coords = this._coords( instanceIndex, size ).toConst();
1092
+ const base = ivec3( coords.mul( uint( 2 ) ) ).toConst();
1093
+
1094
+ const offset = ( i ) => ivec3( i & 1, ( i >> 1 ) & 1, ( i >> 2 ) & 1 );
1095
+
1096
+ const childOpacity = [];
1097
+ const childNormal = [];
1098
+ const childRadiance = [];
1099
+
1100
+ for ( let i = 0; i < 8; i ++ ) {
1101
+
1102
+ childOpacity.push( opacity.load( base.add( offset( i ) ) ).level( level - 1 ).toConst() );
1103
+
1104
+ if ( level === 1 ) {
1105
+
1106
+ childNormal.push( normals.load( base.add( offset( i ) ) ).xyz.mul( 2 ).sub( 1 ).toConst() );
1107
+ childRadiance.push( radiance.load( base.add( offset( i ) ) ).toConst() );
1108
+
1109
+ }
1110
+
1111
+ }
1112
+
1113
+ for ( let direction = 0; direction < 6; direction ++ ) {
1114
+
1115
+ const [ axis, sign ] = DIRECTIONS[ direction ];
1116
+ const travel = vec3( axis === 0 ? sign : 0, axis === 1 ? sign : 0, axis === 2 ? sign : 0 );
1117
+ const block = ivec3( direction * blockWidth * 2, 0, 0 ); // block offset in the finer level
1118
+
1119
+ // a child holds the radiance of its surfaces facing the direction premultiplied by
1120
+ // their weight (rgb) and the weight (a); at the finest level a voxel only counts for a
1121
+ // direction if its surface faces against it, so a surface parallel to the axis does
1122
+ // not mix in. The front child hides the back child by its opacity along the axis.
1123
+
1124
+ const child = ( i ) => level === 1
1125
+ ? childRadiance[ i ].mul( smoothstep( 0, 0.3, dot( childNormal[ i ], travel.negate() ) ) )
1126
+ : source.load( base.add( block ).add( offset( i ) ) );
1127
+ const axisOpacity = ( i ) => [ childOpacity[ i ].x, childOpacity[ i ].y, childOpacity[ i ].z ][ axis ];
1128
+
1129
+ let sum = vec4( 0 );
1130
+
1131
+ for ( let i = 0; i < 8; i ++ ) {
1132
+
1133
+ // each column along the axis is composited once, starting at its front voxel
1134
+
1135
+ if ( ( ( i >> axis ) & 1 ) !== ( sign > 0 ? 0 : 1 ) ) continue;
1136
+
1137
+ const front = i;
1138
+ const back = i ^ ( 1 << axis );
1139
+
1140
+ sum = sum.add( child( front ) ).add( child( back ).mul( axisOpacity( front ).oneMinus() ) );
1141
+
1142
+ }
1143
+
1144
+ textureStore( target, ivec3( coords ).add( ivec3( direction * blockWidth, 0, 0 ) ), sum.mul( 0.25 ) );
1145
+
1146
+ }
1147
+
1148
+ } );
1149
+
1150
+ } )().compute( count ).setName( 'VXGI.Directional' + level );
1151
+
1152
+ }
1153
+
1154
+ /**
1155
+ * Emits the code for reading the surface data of the given voxel.
1156
+ *
1157
+ * @private
1158
+ */
1159
+ _surface( coords, triangleId ) {
1160
+
1161
+ const triangles = this._trianglesNode;
1162
+
1163
+ const base = triangleId.sub( 1 ).mul( 5 ).toConst();
1164
+ const a = triangles.element( base ).xyz.toConst();
1165
+ const b = triangles.element( base.add( 1 ) ).xyz.toConst();
1166
+ const c = triangles.element( base.add( 2 ) ).xyz.toConst();
1167
+ const albedo = triangles.element( base.add( 3 ) ).toConst();
1168
+ const emissive = triangles.element( base.add( 4 ) ).xyz.toConst();
1169
+
1170
+ const normal = normalize( cross( b.sub( a ), c.sub( a ) ) ).toVar();
1171
+ normal.assign( select( albedo.w.equal( 1 ), normal.negate(), normal ) ); // back side
1172
+
1173
+ const position = this.boundsMinNode.add( vec3( coords ).add( 0.5 ).mul( this.voxelSizeNode ) ).toConst();
1174
+
1175
+ return { position, normal, albedo: albedo.xyz, side: albedo.w, emissive };
1176
+
1177
+ }
1178
+
1179
+ _createInjectKernel( renderer, index ) {
1180
+
1181
+ const occupancy = storage( this._occupancyAttribute, 'uint', this._voxelCount ).toReadOnly();
1182
+ const triangleIds = storage( this._triangleIdAttribute, 'uint', this._voxelCount ).toReadOnly();
1183
+ const voxelCount = this._voxelCountNode;
1184
+ const size = this._getLevelSize( 0 );
1185
+ const voxelSize = this.voxelSizeNode;
1186
+ const lights = this._lightsNode;
1187
+
1188
+ const directTexture = this._directTexture;
1189
+ const normalTexture = this._normalTexture;
1190
+ const radianceTexture = this._getRadianceTexture( index );
1191
+
1192
+ const lightTypes = this._lightTypes.slice();
1193
+ const lightShadows = this._lightShadows.slice();
1194
+ const shadowMatrices = this._shadowMatrices;
1195
+ const shadowParams = this._shadowParams;
1196
+ const reversedDepth = renderer.reversedDepthBuffer === true;
1197
+ const shadowTanHalfAngle = this.shadowConeAngle.mul( 0.5 ).radians().tan();
1198
+
1199
+ const trace = createConeTracer( this, { maxSteps: 256 } );
1200
+
1201
+ return Fn( () => {
1202
+
1203
+ If( instanceIndex.lessThan( voxelCount ), () => {
1204
+
1205
+ const coords = this._coords( instanceIndex, size ).toConst();
1206
+ const bits = occupancy.element( instanceIndex ).toConst();
1207
+ const result = vec4( 0 ).toVar();
1208
+ const encodedNormal = normalTexture !== null ? vec4( 0 ).toVar() : null;
1209
+
1210
+ If( bits.notEqual( uint( 0 ) ), () => {
1211
+
1212
+ const surface = this._surface( coords, triangleIds.element( instanceIndex ) );
1213
+ const { position, normal, albedo, side, emissive } = surface;
1214
+
1215
+ if ( encodedNormal !== null ) encodedNormal.assign( vec4( normal.mul( 0.5 ).add( 0.5 ), 1 ) );
1216
+
1217
+ const irradiance = vec3( 0 ).toVar();
1218
+
1219
+ // one specialized block per light; the kernel is rebuilt when the light set changes
1220
+
1221
+ for ( let i = 0; i < lightTypes.length; i ++ ) {
1222
+
1223
+ const type = lightTypes[ i ];
1224
+ const shadowTexture = lightShadows[ i ];
1225
+
1226
+ const l0 = lights.element( i * 4 ).toConst();
1227
+ const l1 = lights.element( i * 4 + 1 ).toConst();
1228
+ const l2 = lights.element( i * 4 + 2 ).toConst();
1229
+ const l3 = lights.element( i * 4 + 3 ).toConst();
1230
+
1231
+ const lightDirection = vec3( 0 ).toVar();
1232
+ const lightDistance = float( 1e10 ).toVar();
1233
+ const attenuation = float( 1 ).toVar();
1234
+
1235
+ if ( type === 0 ) {
1236
+
1237
+ lightDirection.assign( l1.xyz );
1238
+
1239
+ } else {
1240
+
1241
+ const lightVector = l0.xyz.sub( position ).toConst();
1242
+ lightDistance.assign( length( lightVector ) );
1243
+ lightDirection.assign( lightVector.div( lightDistance ) );
1244
+ attenuation.assign( getDistanceAttenuation( { lightDistance, cutoffDistance: l1.w, decayExponent: l2.w } ) );
1245
+
1246
+ if ( type === 2 ) {
1247
+
1248
+ attenuation.mulAssign( smoothstep( l3.x, l3.y, dot( l1.xyz, lightDirection.negate() ) ) );
1249
+
1250
+ }
1251
+
1252
+ }
1253
+
1254
+ const ndl = dot( normal, lightDirection ).toVar();
1255
+ ndl.assign( select( side.equal( 2 ), abs( ndl ), max( ndl, 0 ) ) );
1256
+
1257
+ If( ndl.greaterThan( 0 ).and( attenuation.greaterThan( 0 ) ), () => {
1258
+
1259
+ const visibility = float( 1 ).toVar();
1260
+ const shadowPosition = position.add( normal.mul( voxelSize ) ).toConst();
1261
+
1262
+ if ( shadowTexture === null ) {
1263
+
1264
+ // no shadow map: trace a visibility cone through the volume
1265
+
1266
+ const origin = position.add( normal.mul( voxelSize.mul( 1.5 ) ) );
1267
+ const occlusion = trace( origin, lightDirection, shadowTanHalfAngle, lightDistance.sub( voxelSize ) );
1268
+ visibility.assign( occlusion.alpha.oneMinus() );
1269
+
1270
+ } else if ( type === 1 ) {
1271
+
1272
+ // point light: cube shadow map addressed by the light-to-voxel vector
1273
+
1274
+ const params = shadowParams[ i ];
1275
+ const bias = params.x, near = params.y, far = params.z;
1276
+
1277
+ const lightToVoxel = shadowPosition.sub( l0.xyz ).toConst();
1278
+ const absVector = abs( lightToVoxel ).toConst();
1279
+ const viewZ = max( max( absVector.x, absVector.y ), absVector.z ).toConst();
1280
+
1281
+ If( viewZ.greaterThanEqual( near ).and( viewZ.lessThanEqual( far ) ), () => {
1282
+
1283
+ const depth = nodeObject( new PlainCubeTextureNode( shadowTexture, normalize( lightToVoxel ) ) ).r; // depth cubes need an integer level, which the compute path emits by default
1284
+
1285
+ if ( reversedDepth ) {
1286
+
1287
+ const reference = viewZToReversedPerspectiveDepth( viewZ.negate(), near, far ).sub( bias );
1288
+ visibility.assign( select( reference.greaterThanEqual( depth ), 1, 0 ) );
1289
+
1290
+ } else {
1291
+
1292
+ const reference = viewZToPerspectiveDepth( viewZ.negate(), near, far ).add( bias );
1293
+ visibility.assign( select( reference.lessThanEqual( depth ), 1, 0 ) );
1294
+
1295
+ }
1296
+
1297
+ } );
1298
+
1299
+ } else {
1300
+
1301
+ // directional and spot lights: 2D shadow map addressed by the shadow matrix
1302
+
1303
+ const bias = shadowParams[ i ].x;
1304
+
1305
+ const clip = shadowMatrices[ i ].mul( vec4( shadowPosition, 1 ) ).toConst();
1306
+ const coord = clip.xyz.div( clip.w ).toConst();
1307
+ const uv = vec2( coord.x, coord.y.oneMinus() ).toConst();
1308
+
1309
+ const inside = uv.x.greaterThanEqual( 0 ).and( uv.x.lessThanEqual( 1 ) ).and( uv.y.greaterThanEqual( 0 ) ).and( uv.y.lessThanEqual( 1 ) ).and( coord.z.greaterThanEqual( 0 ) ).and( coord.z.lessThanEqual( 1 ) ).toConst();
1310
+
1311
+ If( inside, () => {
1312
+
1313
+ const depth = texture( shadowTexture, uv ).r;
1314
+
1315
+ if ( reversedDepth ) {
1316
+
1317
+ visibility.assign( select( coord.z.sub( bias ).greaterThanEqual( depth ), 1, 0 ) );
1318
+
1319
+ } else {
1320
+
1321
+ visibility.assign( select( coord.z.add( bias ).lessThanEqual( depth ), 1, 0 ) );
1322
+
1323
+ }
1324
+
1325
+ } );
1326
+
1327
+ }
1328
+
1329
+ irradiance.addAssign( l2.xyz.mul( attenuation.mul( ndl ).mul( visibility ) ) );
1330
+
1331
+ } );
1332
+
1333
+ }
1334
+
1335
+ const radiance = albedo.mul( irradiance ).div( PI ).add( emissive );
1336
+ const occupied = float( countOneBits( bits ) ).div( 8 );
1337
+
1338
+ result.assign( vec4( radiance.mul( occupied ), occupied ) );
1339
+
1340
+ } );
1341
+
1342
+ textureStore( directTexture, coords, result );
1343
+ textureStore( radianceTexture, coords, result );
1344
+
1345
+ if ( encodedNormal !== null ) textureStore( normalTexture, coords, encodedNormal );
1346
+
1347
+ } );
1348
+
1349
+ } )().compute( this._voxelCount ).setName( 'VXGI.Inject' );
1350
+
1351
+ }
1352
+
1353
+ _createBounceKernel( index ) {
1354
+
1355
+ const occupancy = storage( this._occupancyAttribute, 'uint', this._voxelCount ).toReadOnly();
1356
+ const triangleIds = storage( this._triangleIdAttribute, 'uint', this._voxelCount ).toReadOnly();
1357
+ const voxelCount = this._voxelCountNode;
1358
+ const size = this._getLevelSize( 0 );
1359
+ const voxelSize = this.voxelSizeNode;
1360
+
1361
+ const directTexture = this._directTexture;
1362
+ const sourceTexture = this._getRadianceTexture( 1 - index );
1363
+ const targetTexture = this._getRadianceTexture( index );
1364
+
1365
+ const trace = createConeTracer( this, {
1366
+ radianceNode: texture3D( sourceTexture ),
1367
+ directionalNode: this._directional === true ? texture3D( this._getDirectionalTexture( 1 - index ) ) : null
1368
+ } );
1369
+ const traceDistance = this._traceDistance;
1370
+ const tanHalfAngle = this.bounceConeAngle.mul( 0.5 ).radians().tan();
1371
+
1372
+ return Fn( () => {
1373
+
1374
+ If( instanceIndex.lessThan( voxelCount ), () => {
1375
+
1376
+ const coords = this._coords( instanceIndex, size ).toConst();
1377
+ const bits = occupancy.element( instanceIndex ).toConst();
1378
+ const result = vec4( 0 ).toVar();
1379
+
1380
+ If( bits.notEqual( uint( 0 ) ), () => {
1381
+
1382
+ const { position, normal, albedo } = this._surface( coords, triangleIds.element( instanceIndex ) );
1383
+
1384
+ const direct = texture3D( directTexture, vec3( coords ).add( 0.5 ).div( vec3( size[ 0 ], size[ 1 ], size[ 2 ] ) ), float( 0 ) ).toConst();
1385
+
1386
+ // tangent frame
1387
+
1388
+ const up = select( abs( normal.y ).lessThan( 0.99 ), vec3( 0, 1, 0 ), vec3( 1, 0, 0 ) );
1389
+ const tangent = normalize( cross( normal, up ) ).toConst();
1390
+ const bitangent = cross( normal, tangent ).toConst();
1391
+
1392
+ const rotation = hash( instanceIndex ).toConst();
1393
+ const gathered = vec3( 0 ).toVar();
1394
+
1395
+ Loop( { start: 0, end: BOUNCE_CONE_COUNT, type: 'int', condition: '<', name: 'c' }, ( { c } ) => {
1396
+
1397
+ // cosine-weighted directions, rotated per voxel
1398
+
1399
+ const u1 = float( c ).add( 0.5 ).div( BOUNCE_CONE_COUNT ).toConst();
1400
+ const u2 = fract( float( c ).mul( 0.618034 ).add( rotation ) ).toConst();
1401
+ const sinTheta = sqrt( u1 ).toConst();
1402
+ const cosTheta = sqrt( u1.oneMinus() ).toConst();
1403
+ const phi = u2.mul( PI.mul( 2 ) ).toConst();
1404
+
1405
+ const direction = normalize( tangent.mul( cos( phi ).mul( sinTheta ) ).add( bitangent.mul( sin( phi ).mul( sinTheta ) ) ).add( normal.mul( cosTheta ) ) ).toConst();
1406
+ const origin = position.add( normal.mul( voxelSize.mul( 1.5 ) ) ).toConst();
1407
+
1408
+ const cone = trace( origin, direction, tanHalfAngle, traceDistance );
1409
+
1410
+ gathered.addAssign( cone.color );
1411
+
1412
+ } );
1413
+
1414
+ const bounce = albedo.mul( gathered.div( BOUNCE_CONE_COUNT ) );
1415
+
1416
+ result.assign( vec4( direct.rgb.add( bounce.mul( direct.a ) ), direct.a ) );
1417
+
1418
+ } );
1419
+
1420
+ textureStore( targetTexture, coords, result );
1421
+
1422
+ } );
1423
+
1424
+ } )().compute( this._voxelCount ).setName( 'VXGI.Bounce' );
1425
+
1426
+ }
1427
+
1428
+ }
1429
+
1430
+ export { VXGIVolume };