@unikia/upe-renderer 0.186.110 → 0.187.11
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.
- package/README.md +5 -7
- package/UPSTREAM.md +4 -4
- package/build/three.core.js +853 -277
- package/build/three.module.js +1098 -1280
- package/build/three.tsl.js +41938 -686
- package/build/three.webgpu.js +10604 -49055
- package/build/three.webgpu.nodes.js +9249 -5788
- package/examples/jsm/Addons.js +2 -0
- package/examples/jsm/animation/CCDIKSolver.js +17 -5
- package/examples/jsm/controls/FirstPersonControls.js +1 -1
- package/examples/jsm/controls/OrbitControls.js +27 -14
- package/examples/jsm/csm/CSMShader.js +74 -3
- package/examples/jsm/curves/CurveExtras.js +4 -4
- package/examples/jsm/curves/NURBSCurve.js +14 -0
- package/examples/jsm/exporters/EXRExporter.js +673 -16
- package/examples/jsm/exporters/GLTFExporter.js +4 -4
- package/examples/jsm/exporters/KTX2Exporter.js +1 -1
- package/examples/jsm/exporters/OBJExporter.js +48 -5
- package/examples/jsm/exporters/PLYExporter.js +22 -11
- package/examples/jsm/generators/CityGenerator.js +3 -2
- package/examples/jsm/generators/city/SkyscraperGenerator.js +63 -21
- package/examples/jsm/gpgpu/BitonicSort.js +30 -23
- package/examples/jsm/helpers/VertexTangentsHelper.js +1 -1
- package/examples/jsm/inspector/Extension.js +1 -1
- package/examples/jsm/inspector/Inspector.js +6 -46
- package/examples/jsm/inspector/RendererInspector.js +43 -8
- package/examples/jsm/inspector/Storage.js +49 -0
- package/examples/jsm/inspector/extensions/color-grading/ColorGrading.js +32 -27
- package/examples/jsm/inspector/tabs/Parameters.js +5 -1
- package/examples/jsm/inspector/tabs/Performance.js +22 -7
- package/examples/jsm/inspector/tabs/Settings.js +1 -1
- package/examples/jsm/inspector/tabs/Timeline.js +83 -7
- package/examples/jsm/inspector/tabs/Viewer.js +25 -10
- package/examples/jsm/inspector/ui/Profiler.js +1 -1
- package/examples/jsm/inspector/ui/Style.js +21 -53
- package/examples/jsm/inspector/ui/Values.js +28 -3
- package/examples/jsm/libs/fflate.module.js +83 -56
- package/examples/jsm/libs/mikktspace.README.md +27 -0
- package/examples/jsm/libs/mikktspace.module.js +721 -109
- package/examples/jsm/lighting/ClusteredLighting.js +100 -2
- package/examples/jsm/lighting/LightMapper.js +295 -0
- package/examples/jsm/lighting/LightProbeGrid.js +242 -139
- package/examples/jsm/lighting/LightProbeGridWebGL.js +30 -14
- package/examples/jsm/lighting/vxgi/VXGIConeTracer.js +54 -5
- package/examples/jsm/lighting/vxgi/VXGIIrradiance.js +43 -0
- package/examples/jsm/lighting/vxgi/VXGINode.js +13 -56
- package/examples/jsm/lighting/vxgi/VXGISceneCollector.js +316 -1
- package/examples/jsm/lighting/vxgi/VXGIVolume.js +69 -59
- package/examples/jsm/lights/SunLight.js +9 -4
- package/examples/jsm/lights/SunLightNode.js +3 -3
- package/examples/jsm/lines/LineMaterial.js +26 -10
- package/examples/jsm/loaders/3DMLoader.js +10 -1
- package/examples/jsm/loaders/3MFLoader.js +5 -1
- package/examples/jsm/loaders/AMFLoader.js +2 -0
- package/examples/jsm/loaders/BVHLoader.js +2 -0
- package/examples/jsm/loaders/ColladaLoader.js +8 -0
- package/examples/jsm/loaders/DDSLoader.js +1 -1
- package/examples/jsm/loaders/DRACOLoader.js +5 -1
- package/examples/jsm/loaders/EXRLoader.js +120 -104
- package/examples/jsm/loaders/FBXLoader.js +4 -0
- package/examples/jsm/loaders/FontLoader.js +2 -0
- package/examples/jsm/loaders/GCodeLoader.js +5 -9
- package/examples/jsm/loaders/GLTFLoader.js +112 -31
- package/examples/jsm/loaders/GaussianSplatPLYLoader.js +2 -0
- package/examples/jsm/loaders/HDRCubeTextureLoader.js +3 -0
- package/examples/jsm/loaders/IESLoader.js +46 -12
- package/examples/jsm/loaders/JXLLoader.js +159 -0
- package/examples/jsm/loaders/KMZLoader.js +6 -0
- package/examples/jsm/loaders/KSPLATLoader.js +2 -0
- package/examples/jsm/loaders/KTX2Loader.js +49 -4
- package/examples/jsm/loaders/KTXLoader.js +5 -3
- package/examples/jsm/loaders/LDrawLoader.js +15 -22
- package/examples/jsm/loaders/LUT3dlLoader.js +5 -1
- package/examples/jsm/loaders/LUTCubeLoader.js +6 -2
- package/examples/jsm/loaders/LUTImageLoader.js +2 -0
- package/examples/jsm/loaders/LWOLoader.js +6 -0
- package/examples/jsm/loaders/MD2Loader.js +2 -0
- package/examples/jsm/loaders/MDDLoader.js +4 -0
- package/examples/jsm/loaders/MTLLoader.js +8 -0
- package/examples/jsm/loaders/MaterialXLoader.js +57 -5
- package/examples/jsm/loaders/NRRDLoader.js +4 -2
- package/examples/jsm/loaders/OBJLoader.js +10 -4
- package/examples/jsm/loaders/PCDLoader.js +6 -2
- package/examples/jsm/loaders/PDBLoader.js +5 -3
- package/examples/jsm/loaders/PLYLoader.js +39 -15
- package/examples/jsm/loaders/PVRLoader.js +2 -2
- package/examples/jsm/loaders/SPLATLoader.js +2 -0
- package/examples/jsm/loaders/SPZLoader.js +2 -0
- package/examples/jsm/loaders/STLLoader.js +3 -1
- package/examples/jsm/loaders/SVGLoader.js +27 -49
- package/examples/jsm/loaders/TDSLoader.js +4 -0
- package/examples/jsm/loaders/TGALoader.js +6 -5
- package/examples/jsm/loaders/TTFLoader.js +2 -0
- package/examples/jsm/loaders/USDLoader.js +14 -0
- package/examples/jsm/loaders/UltraHDRLoader.js +2 -0
- package/examples/jsm/loaders/VOXLoader.js +3 -0
- package/examples/jsm/loaders/VRMLLoader.js +4 -0
- package/examples/jsm/loaders/VTKLoader.js +2 -0
- package/examples/jsm/loaders/XYZLoader.js +2 -0
- package/examples/jsm/loaders/collada/ColladaComposer.js +15 -8
- package/examples/jsm/loaders/materialx/MaterialXDocument.js +610 -138
- package/examples/jsm/loaders/materialx/MaterialXInterfaceValidation.js +12 -95
- package/examples/jsm/loaders/materialx/MaterialXLog.js +4 -0
- package/examples/jsm/loaders/materialx/MaterialXNodeDefs.js +99 -0
- package/examples/jsm/loaders/materialx/MaterialXNodeInterfaceRegistry.js +760 -1
- package/examples/jsm/loaders/materialx/MaterialXNodeLibrary.js +222 -372
- package/examples/jsm/loaders/materialx/MaterialXSurfaceMappings.js +163 -259
- package/examples/jsm/loaders/materialx/MaterialXUtils.js +25 -0
- package/examples/jsm/loaders/materialx/compile/MaterialXCompileRegistry.js +161 -159
- package/examples/jsm/loaders/usd/USDAParser.js +56 -47
- package/examples/jsm/loaders/usd/USDComposer.js +25 -30
- package/examples/jsm/math/ColorConverter.js +7 -3
- package/examples/jsm/math/Lut.js +2 -2
- package/examples/jsm/math/OBB.js +3 -3
- package/examples/jsm/math/Octree.js +20 -17
- package/examples/jsm/objects/GaussianSplat.js +5 -4
- package/examples/jsm/objects/Reflector.js +5 -0
- package/examples/jsm/objects/SkyMesh.js +14 -7
- package/examples/jsm/objects/Water2Mesh.js +2 -2
- package/examples/jsm/physics/RapierPhysics.js +2 -2
- package/examples/jsm/pipelines/MeshletDrawingPipeline.js +5571 -0
- package/examples/jsm/pipelines/VisibilityBufferDrawingPipeline.js +954 -0
- package/examples/jsm/postprocessing/BokehPass.js +1 -1
- package/examples/jsm/postprocessing/GTAOPass.js +26 -18
- package/examples/jsm/renderers/SVGRenderer.js +3 -2
- package/examples/jsm/shaders/GTAOShader.js +7 -4
- package/examples/jsm/shaders/PoissonDenoiseShader.js +1 -10
- package/examples/jsm/tsl/WebGLNodesHandler.js +21 -9
- package/examples/jsm/tsl/display/AfterImageNode.js +3 -3
- package/examples/jsm/tsl/display/BilateralBlurNode.js +3 -3
- package/examples/jsm/tsl/display/BloomNode.js +3 -3
- package/examples/jsm/tsl/display/ChromaticAberrationNode.js +8 -19
- package/examples/jsm/tsl/display/DenoiseNode.js +5 -5
- package/examples/jsm/tsl/display/DepthOfFieldNode.js +3 -3
- package/examples/jsm/tsl/display/DotScreenNode.js +3 -3
- package/examples/jsm/tsl/display/FSR1Node.js +3 -3
- package/examples/jsm/tsl/display/FXAANode.js +3 -10
- package/examples/jsm/tsl/display/FilmNode.js +3 -3
- package/examples/jsm/tsl/display/GTAONode.js +33 -25
- package/examples/jsm/tsl/display/GaussianBlurNode.js +3 -3
- package/examples/jsm/tsl/display/GodraysNode.js +5 -19
- package/examples/jsm/tsl/display/ImportanceSampledEnvironment.js +8 -4
- package/examples/jsm/tsl/display/LensflareNode.js +3 -3
- package/examples/jsm/tsl/display/Lut3DNode.js +3 -3
- package/examples/jsm/tsl/display/OITPassNode.js +0 -11
- package/examples/jsm/tsl/display/OutlineNode.js +107 -5
- package/examples/jsm/tsl/display/PixelationPassNode.js +85 -193
- package/examples/jsm/tsl/display/RGBShiftNode.js +3 -3
- package/examples/jsm/tsl/display/RecurrentDenoiseNode.js +23 -126
- package/examples/jsm/tsl/display/SMAANode.js +3 -3
- package/examples/jsm/tsl/display/SSAONode.js +6 -20
- package/examples/jsm/tsl/display/SSGINode.js +36 -29
- package/examples/jsm/tsl/display/SSRNode.js +135 -63
- package/examples/jsm/tsl/display/SSSNode.js +6 -20
- package/examples/jsm/tsl/display/SharpenNode.js +3 -3
- package/examples/jsm/tsl/display/SobelOperatorNode.js +3 -3
- package/examples/jsm/tsl/display/TAAUNode.js +10 -42
- package/examples/jsm/tsl/display/TRAANode.js +11 -43
- package/examples/jsm/tsl/display/TemporalReprojectNode.js +42 -47
- package/examples/jsm/tsl/display/TransitionNode.js +3 -3
- package/examples/jsm/tsl/display/lensDirt.js +30 -0
- package/examples/jsm/tsl/lighting/ClusteredLightsNode.js +482 -121
- package/examples/jsm/tsl/lighting/DynamicLightsNode.js +41 -140
- package/examples/jsm/tsl/lighting/LightProbeGridNode.js +72 -25
- package/examples/jsm/tsl/lighting/data/AmbientLightDataNode.js +6 -0
- package/examples/jsm/tsl/lighting/data/DirectionalLightDataNode.js +6 -0
- package/examples/jsm/tsl/lighting/data/HemisphereLightDataNode.js +6 -0
- package/examples/jsm/tsl/lighting/data/PointLightDataNode.js +6 -0
- package/examples/jsm/tsl/lighting/data/SpotLightDataNode.js +6 -0
- package/examples/jsm/tsl/shadows/TileShadowNode.js +102 -14
- package/examples/jsm/tsl/utils/Raymarching.js +1 -1
- package/examples/jsm/tsl/utils/SoftParticles.js +5 -12
- package/examples/jsm/tsl/utils/SpecularHelpers.js +47 -70
- package/examples/jsm/tsl/utils/TAAUtils.js +6 -17
- package/examples/jsm/utils/BufferGeometryUtils.js +110 -88
- package/examples/jsm/utils/MeshletGeometryUtils.js +417 -0
- package/examples/jsm/utils/SceneOptimizer.js +2 -1
- package/examples/jsm/utils/SceneUtils.js +1 -1
- package/examples/jsm/utils/unwrapUVs.js +865 -0
- package/examples/jsm/webxr/XREstimatedLight.js +11 -4
- package/package.json +3 -3
- package/src/Three.TSL.js +2 -683
- package/src/Three.WebGPU.Nodes.js +2 -1
- package/src/Three.WebGPU.js +2 -1
- package/src/animation/AnimationAction.js +1 -1
- package/src/animation/AnimationUtils.js +1 -3
- package/src/animation/KeyframeTrack.js +19 -0
- package/src/cameras/ArrayCamera.js +12 -0
- package/src/cameras/CubeCamera.js +3 -3
- package/src/constants.js +1 -9
- package/src/core/BufferAttribute.js +57 -21
- package/src/core/BufferGeometry.js +5 -13
- package/src/core/Clock.js +1 -1
- package/src/core/InterleavedBuffer.js +28 -18
- package/src/core/InterleavedBufferAttribute.js +54 -20
- package/src/core/Object3D.js +19 -9
- package/src/core/Raycaster.js +2 -0
- package/src/core/RenderTarget.js +11 -5
- package/src/core/UniformsGroup.js +7 -3
- package/src/extras/PMREMGenerator.js +392 -685
- package/src/extras/core/Curve.js +4 -0
- package/src/extras/core/CurvePath.js +13 -0
- package/src/extras/core/ShapePath.js +1 -1
- package/src/geometries/EdgesGeometry.js +12 -10
- package/src/geometries/LatheGeometry.js +2 -0
- package/src/helpers/PolarGridHelper.js +1 -1
- package/src/lights/Light.js +11 -0
- package/src/lights/LightProbe.js +2 -0
- package/src/lights/LightShadow.js +2 -0
- package/src/loaders/AnimationLoader.js +2 -0
- package/src/loaders/AudioLoader.js +2 -0
- package/src/loaders/BufferGeometryLoader.js +2 -0
- package/src/loaders/CompressedTextureLoader.js +2 -0
- package/src/loaders/CubeTextureLoader.js +2 -0
- package/src/loaders/DataTextureLoader.js +2 -0
- package/src/loaders/FileLoader.js +2 -0
- package/src/loaders/ImageBitmapLoader.js +3 -1
- package/src/loaders/ImageLoader.js +1 -0
- package/src/loaders/Loader.js +47 -0
- package/src/loaders/MaterialLoader.js +2 -0
- package/src/loaders/ObjectLoader.js +27 -8
- package/src/loaders/TextureLoader.js +5 -3
- package/src/loaders/nodes/NodeLoader.js +2 -0
- package/src/materials/Material.js +21 -2
- package/src/materials/MeshDepthMaterial.js +1 -1
- package/src/materials/MeshDistanceMaterial.js +1 -1
- package/src/materials/MeshLambertMaterial.js +1 -1
- package/src/materials/MeshMatcapMaterial.js +1 -1
- package/src/materials/MeshNormalMaterial.js +1 -1
- package/src/materials/MeshPhongMaterial.js +1 -1
- package/src/materials/MeshPhysicalMaterial.js +90 -11
- package/src/materials/MeshStandardMaterial.js +1 -1
- package/src/materials/MeshToonMaterial.js +1 -1
- package/src/materials/nodes/Line2NodeMaterial.js +32 -10
- package/src/materials/nodes/MeshLambertNodeMaterial.js +13 -0
- package/src/materials/nodes/MeshPhongNodeMaterial.js +13 -0
- package/src/materials/nodes/MeshPhysicalNodeMaterial.js +40 -5
- package/src/materials/nodes/MeshToonNodeMaterial.js +13 -0
- package/src/materials/nodes/NodeMaterial.js +36 -16
- package/src/materials/nodes/NodeMaterialInstance.js +193 -0
- package/src/materials/nodes/NodeMaterials.js +1 -0
- package/src/materials/nodes/PointsNodeMaterial.js +1 -1
- package/src/materials/nodes/SpriteNodeMaterial.js +3 -3
- package/src/materials/nodes/manager/NodeMaterialObserver.js +191 -22
- package/src/math/Box3.js +2 -2
- package/src/math/Color.js +8 -8
- package/src/math/ColorManagement.js +2 -21
- package/src/math/FrustumArray.js +12 -0
- package/src/math/Interpolant.js +1 -1
- package/src/math/Line3.js +2 -4
- package/src/math/MathUtils.js +33 -46
- package/src/math/Matrix2.js +1 -1
- package/src/math/Matrix3.js +1 -1
- package/src/math/Matrix4.js +1 -1
- package/src/math/Triangle.js +2 -1
- package/src/math/Vector2.js +1 -1
- package/src/math/Vector3.js +1 -1
- package/src/math/Vector4.js +1 -1
- package/src/nodes/Nodes.js +1 -0
- package/src/nodes/TSL.js +2 -0
- package/src/nodes/accessors/Arrays.js +1 -1
- package/src/nodes/accessors/Batch.js +29 -23
- package/src/nodes/accessors/BufferAttributeNode.js +21 -1
- package/src/nodes/accessors/BuiltinNode.js +6 -0
- package/src/nodes/accessors/Camera.js +100 -93
- package/src/nodes/accessors/ClippingNode.js +17 -6
- package/src/nodes/accessors/CubeTextureNode.js +12 -24
- package/src/nodes/accessors/Instance.js +198 -160
- package/src/nodes/accessors/Lights.js +19 -6
- package/src/nodes/accessors/MaterialNode.js +33 -4
- package/src/nodes/accessors/Morph.js +81 -22
- package/src/nodes/accessors/Normal.js +0 -42
- package/src/nodes/accessors/Object3DNode.js +6 -0
- package/src/nodes/accessors/PointUVNode.js +6 -0
- package/src/nodes/accessors/ReferenceAttributeNode.js +279 -0
- package/src/nodes/accessors/ReferenceBaseNode.js +6 -0
- package/src/nodes/accessors/ReferenceNode.js +6 -0
- package/src/nodes/accessors/Skinning.js +57 -52
- package/src/nodes/accessors/StorageBufferNode.js +3 -12
- package/src/nodes/accessors/TextureBicubic.js +30 -27
- package/src/nodes/accessors/TextureNode.js +164 -78
- package/src/nodes/accessors/TextureSizeNode.js +6 -0
- package/src/nodes/accessors/UniformArrayNode.js +8 -15
- package/src/nodes/accessors/VelocityNode.js +13 -8
- package/src/nodes/code/CodeNode.js +6 -0
- package/src/nodes/code/ExpressionNode.js +6 -0
- package/src/nodes/code/FunctionCallNode.js +3 -3
- package/src/nodes/code/FunctionNode.js +15 -13
- package/src/nodes/core/ArrayNode.js +3 -3
- package/src/nodes/core/AssignNode.js +10 -9
- package/src/nodes/core/AttributeNode.js +39 -0
- package/src/nodes/core/BypassNode.js +6 -0
- package/src/nodes/core/ConstNode.js +31 -1
- package/src/nodes/core/ContextNode.js +11 -6
- package/src/nodes/core/IndexNode.js +6 -0
- package/src/nodes/core/InputNode.js +6 -0
- package/src/nodes/core/InspectorNode.js +17 -31
- package/src/nodes/core/IsolateNode.js +8 -0
- package/src/nodes/core/Node.js +367 -67
- package/src/nodes/core/NodeBuilder.js +664 -222
- package/src/nodes/core/NodeUtils.js +25 -0
- package/src/nodes/core/NodeVar.js +10 -1
- package/src/nodes/core/OutputStructNode.js +6 -0
- package/src/nodes/core/OverrideContextNode.js +38 -37
- package/src/nodes/core/PropertyNode.js +14 -0
- package/src/nodes/core/StackNode.js +33 -21
- package/src/nodes/core/StructNode.js +6 -0
- package/src/nodes/core/StructTypeNode.js +6 -0
- package/src/nodes/core/SubBuildNode.js +8 -0
- package/src/nodes/core/TempNode.js +10 -35
- package/src/nodes/core/UniformGroupNode.js +6 -0
- package/src/nodes/core/VarNode.js +43 -135
- package/src/nodes/core/VaryingNode.js +33 -2
- package/src/nodes/display/BlendModes.js +5 -40
- package/src/nodes/display/BumpMapNode.js +17 -7
- package/src/nodes/display/ColorAdjustment.js +22 -10
- package/src/nodes/display/ColorSpaceFunctions.js +2 -14
- package/src/nodes/display/ColorSpaceNode.js +3 -3
- package/src/nodes/display/FrontFacingNode.js +6 -0
- package/src/nodes/display/NormalMapNode.js +3 -3
- package/src/nodes/display/PassNode.js +75 -16
- package/src/nodes/display/RenderOutputNode.js +22 -5
- package/src/nodes/display/ScreenNode.js +6 -0
- package/src/nodes/display/ToneMappingFunctions.js +9 -51
- package/src/nodes/display/ToneMappingNode.js +3 -3
- package/src/nodes/display/ToonOutlinePassNode.js +66 -2
- package/src/nodes/display/ViewportDepthNode.js +34 -4
- package/src/nodes/functions/BSDF/BRDF_EON.js +65 -0
- package/src/nodes/functions/BSDF/BRDF_GGX.js +5 -3
- package/src/nodes/functions/BSDF/BRDF_Sheen.js +2 -16
- package/src/nodes/functions/BSDF/D_GGX.js +1 -8
- package/src/nodes/functions/BSDF/D_GGX_Anisotropic.js +7 -10
- package/src/nodes/functions/BSDF/LTC.js +20 -47
- package/src/nodes/functions/BSDF/Schlick_to_F0.js +1 -9
- package/src/nodes/functions/BSDF/V_GGX_SmithCorrelated.js +1 -9
- package/src/nodes/functions/BSDF/V_GGX_SmithCorrelated_Anisotropic.js +10 -13
- package/src/nodes/functions/PhysicalLightingModel.js +84 -51
- package/src/nodes/functions/VolumetricLightingModel.js +1 -1
- package/src/nodes/functions/material/getAlphaHashThreshold.js +3 -9
- package/src/nodes/functions/material/getParallaxCorrectNormal.js +10 -11
- package/src/nodes/functions/material/getRoughness.js +3 -1
- package/src/nodes/geometry/RangeNode.js +52 -69
- package/src/nodes/gpgpu/AtomicFunctionNode.js +11 -6
- package/src/nodes/gpgpu/BarrierNode.js +6 -0
- package/src/nodes/gpgpu/ComputeBuiltinNode.js +6 -0
- package/src/nodes/gpgpu/ComputeNode.js +6 -0
- package/src/nodes/gpgpu/SubgroupFunctionNode.js +38 -29
- package/src/nodes/gpgpu/WorkgroupInfoNode.js +6 -0
- package/src/nodes/lighting/AnalyticLightNode.js +7 -14
- package/src/nodes/lighting/BasicEnvironmentNode.js +5 -1
- package/src/nodes/lighting/EnvironmentNode.js +58 -8
- package/src/nodes/lighting/IESSpotLightNode.js +19 -4
- package/src/nodes/lighting/LightProbeNode.js +2 -1
- package/src/nodes/lighting/LightUtils.js +2 -2
- package/src/nodes/lighting/LightingNode.js +6 -0
- package/src/nodes/lighting/LightsNode.js +26 -32
- package/src/nodes/lighting/PointShadowNode.js +92 -48
- package/src/nodes/lighting/ProjectorLightNode.js +1 -1
- package/src/nodes/lighting/RectAreaLightNode.js +13 -14
- package/src/nodes/lighting/ShadowBaseNode.js +9 -17
- package/src/nodes/lighting/ShadowFilterNode.js +26 -3
- package/src/nodes/lighting/ShadowNode.js +13 -11
- package/src/nodes/materialx/MaterialXColor.js +2 -14
- package/src/nodes/materialx/MaterialXColorTransform.js +1 -7
- package/src/nodes/materialx/MaterialXNodes.js +8 -11
- package/src/nodes/materialx/MaterialXNoise.js +195 -454
- package/src/nodes/math/BitcastNode.js +5 -5
- package/src/nodes/math/BitcountNode.js +18 -46
- package/src/nodes/math/ConditionalNode.js +92 -22
- package/src/nodes/math/Hash.js +1 -1
- package/src/nodes/math/MathNode.js +86 -64
- package/src/nodes/math/MathUtils.js +2 -2
- package/src/nodes/math/OperatorNode.js +42 -59
- package/src/nodes/math/PackFloatNode.js +9 -9
- package/src/nodes/math/Packed4x8IntegerNode.js +20 -52
- package/src/nodes/math/TriNoise3D.js +3 -23
- package/src/nodes/math/UnpackFloatNode.js +9 -9
- package/src/nodes/pmrem/PMREMNode.js +38 -75
- package/src/nodes/pmrem/PMREMUtils.js +108 -272
- package/src/nodes/procedural/Checker.js +1 -1
- package/src/nodes/tsl/TSLCore.js +199 -74
- package/src/nodes/utils/ArrayElementNode.js +19 -1
- package/src/nodes/utils/CubeMapNode.js +3 -3
- package/src/nodes/utils/DebugNode.js +2 -2
- package/src/nodes/utils/EventNode.js +6 -0
- package/src/nodes/utils/FlipNode.js +3 -3
- package/src/nodes/utils/FunctionOverloadingNode.js +7 -1
- package/src/nodes/utils/JoinNode.js +3 -3
- package/src/nodes/utils/LoopNode.js +14 -9
- package/src/nodes/utils/MemberNode.js +18 -0
- package/src/nodes/utils/PostProcessingUtils.js +20 -25
- package/src/nodes/utils/RTTNode.js +2 -0
- package/src/nodes/utils/ReflectorNode.js +24 -0
- package/src/nodes/utils/RotateNode.js +3 -3
- package/src/nodes/utils/SampleNode.js +6 -0
- package/src/nodes/utils/SetNode.js +3 -3
- package/src/nodes/utils/SplitNode.js +6 -0
- package/src/nodes/utils/UVUtils.js +2 -2
- package/src/objects/BatchedMesh.js +146 -15
- package/src/objects/InstancedMesh.js +12 -4
- package/src/objects/Line.js +20 -0
- package/src/objects/Mesh.js +8 -0
- package/src/objects/Points.js +20 -0
- package/src/objects/Skeleton.js +26 -0
- package/src/objects/SkinnedMesh.js +3 -3
- package/src/objects/Sprite.js +10 -0
- package/src/renderers/WebGLRenderer.js +104 -70
- package/src/renderers/common/Animation.js +5 -1
- package/src/renderers/common/Attributes.js +67 -57
- package/src/renderers/common/Backend.js +78 -3
- package/src/renderers/common/BindGroup.js +11 -0
- package/src/renderers/common/Binding.js +0 -11
- package/src/renderers/common/Bindings.js +116 -10
- package/src/renderers/common/Buffer.js +1 -1
- package/src/renderers/common/ClippingContext.js +12 -1
- package/src/renderers/common/ComputePipeline.js +17 -1
- package/src/renderers/common/DirectRenderPipeline.js +7 -3
- package/src/renderers/common/Geometries.js +170 -132
- package/src/renderers/common/Info.js +1 -1
- package/src/renderers/common/InspectorBase.js +5 -5
- package/src/renderers/common/Lighting.js +59 -0
- package/src/renderers/common/Pipelines.js +26 -12
- package/src/renderers/common/ProgrammableStage.js +1 -17
- package/src/renderers/common/RenderContext.js +21 -0
- package/src/renderers/common/RenderContexts.js +13 -4
- package/src/renderers/common/RenderObject.js +111 -35
- package/src/renderers/common/RenderObjects.js +37 -19
- package/src/renderers/common/Renderer.js +537 -295
- package/src/renderers/common/RendererUtils.js +15 -0
- package/src/renderers/common/StandardDrawingPipeline.js +343 -0
- package/src/renderers/common/Storage3DTexture.js +11 -0
- package/src/renderers/common/StorageArrayTexture.js +10 -0
- package/src/renderers/common/StorageTexture.js +0 -9
- package/src/renderers/common/Textures.js +144 -37
- package/src/renderers/common/UniformsGroup.js +19 -0
- package/src/renderers/common/XRManager.js +10 -91
- package/src/renderers/common/extras/PMREMGenerator.js +298 -576
- package/src/renderers/common/nodes/NodeBuilderState.js +7 -5
- package/src/renderers/common/nodes/NodeLibrary.js +11 -35
- package/src/renderers/common/nodes/NodeManager.js +80 -31
- package/src/renderers/common/nodes/NodeSampledTexture.js +14 -0
- package/src/renderers/common/nodes/NodeSampler.js +14 -0
- package/src/renderers/common/nodes/NodeStorageBuffer.js +1 -1
- package/src/renderers/common/nodes/NodeUniformBuffer.js +1 -14
- package/src/renderers/shaders/ShaderChunk/envmap_common_pars_fragment.glsl.js +13 -4
- package/src/renderers/shaders/ShaderChunk/envmap_physical_pars_fragment.glsl.js +8 -8
- package/src/renderers/shaders/ShaderChunk/lightprobes_pars_fragment.glsl.js +23 -11
- package/src/renderers/shaders/ShaderChunk/lights_fragment_begin.glsl.js +21 -2
- package/src/renderers/shaders/ShaderChunk/lights_fragment_maps.glsl.js +1 -1
- package/src/renderers/shaders/ShaderChunk/lights_physical_fragment.glsl.js +18 -2
- package/src/renderers/shaders/ShaderChunk/lights_physical_pars_fragment.glsl.js +104 -7
- package/src/renderers/shaders/ShaderChunk/morphcolor_vertex.glsl.js +11 -2
- package/src/renderers/shaders/ShaderChunk/shadowmap_pars_fragment.glsl.js +36 -20
- package/src/renderers/shaders/ShaderChunk/uv_pars_fragment.glsl.js +5 -0
- package/src/renderers/shaders/ShaderChunk/uv_pars_vertex.glsl.js +6 -0
- package/src/renderers/shaders/ShaderChunk/uv_vertex.glsl.js +5 -0
- package/src/renderers/shaders/ShaderChunk.js +0 -2
- package/src/renderers/shaders/ShaderLib/backgroundCube.glsl.js +4 -18
- package/src/renderers/shaders/ShaderLib/meshlambert.glsl.js +0 -1
- package/src/renderers/shaders/ShaderLib/meshphong.glsl.js +0 -1
- package/src/renderers/shaders/ShaderLib/meshphysical.glsl.js +8 -1
- package/src/renderers/shaders/ShaderLib.js +3 -0
- package/src/renderers/shaders/UniformsLib.js +4 -5
- package/src/renderers/webgl/WebGLBackground.js +3 -3
- package/src/renderers/webgl/WebGLBindingStates.js +4 -2
- package/src/renderers/webgl/WebGLCapabilities.js +7 -1
- package/src/renderers/webgl/WebGLEnvironments.js +2 -2
- package/src/renderers/webgl/WebGLGeometries.js +42 -14
- package/src/renderers/webgl/WebGLMaterials.js +15 -2
- package/src/renderers/webgl/WebGLOutput.js +37 -1
- package/src/renderers/webgl/WebGLProgram.js +13 -54
- package/src/renderers/webgl/WebGLPrograms.js +22 -8
- package/src/renderers/webgl/WebGLRenderStates.js +11 -8
- package/src/renderers/webgl/WebGLShadowMap.js +6 -4
- package/src/renderers/webgl/WebGLState.js +10 -6
- package/src/renderers/webgl/WebGLTextures.js +129 -77
- package/src/renderers/webgl/WebGLUniforms.js +44 -6
- package/src/renderers/webgl/WebGLUniformsGroups.js +75 -69
- package/src/renderers/webgl-fallback/WebGLBackend.js +206 -278
- package/src/renderers/webgl-fallback/nodes/GLSLNodeBuilder.js +154 -20
- package/src/renderers/webgl-fallback/utils/WebGLAttributeUtils.js +45 -42
- package/src/renderers/webgl-fallback/utils/WebGLState.js +29 -4
- package/src/renderers/webgl-fallback/utils/WebGLTextureUtils.js +129 -49
- package/src/renderers/webgl-fallback/utils/WebGLVertexArrayUtils.js +346 -0
- package/src/renderers/webgpu/WebGPUBackend.js +140 -82
- package/src/renderers/webgpu/WebGPURenderer.js +1 -1
- package/src/renderers/webgpu/nodes/BasicNodeLibrary.js +10 -10
- package/src/renderers/webgpu/nodes/StandardNodeLibrary.js +10 -10
- package/src/renderers/webgpu/nodes/WGSLNodeBuilder.js +135 -60
- package/src/renderers/webgpu/utils/WebGPUAttributeUtils.js +128 -55
- package/src/renderers/webgpu/utils/WebGPUBindingUtils.js +1 -1
- package/src/renderers/webgpu/utils/WebGPUPipelineUtils.js +15 -12
- package/src/renderers/webgpu/utils/WebGPUTextureUtils.js +55 -19
- package/src/renderers/webgpu/utils/WebGPUUtils.js +20 -3
- package/src/renderers/webxr/WebXRController.js +3 -3
- package/src/renderers/webxr/WebXRManager.js +5 -90
- package/src/scenes/Scene.js +4 -4
- package/src/textures/CompressedTexture.js +2 -2
- package/src/textures/DepthTexture.js +2 -2
- package/src/textures/FramebufferTexture.js +11 -0
- package/src/textures/Texture.js +29 -1
- package/src/textures/TextureSource.js +1 -1
- package/src/upe.js +1 -1
- package/src/utils.js +4 -4
- package/examples/jsm/libs/surfaceNet.js +0 -201
- package/src/renderers/shaders/ShaderChunk/cube_uv_reflection_fragment.glsl.js +0 -186
|
@@ -1,20 +1,30 @@
|
|
|
1
|
-
import { DataTexture, FloatType, RGBAFormat, Vector2, Vector3, LightsNode, NodeUpdateType } from 'three/webgpu';
|
|
1
|
+
import { DataTexture, FloatType, RGBAFormat, Vector2, Vector3, LightsNode, NodeUpdateType, NodeUtils, PointLightNode, SpotLightNode } from 'three/webgpu';
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
|
-
|
|
5
|
-
Fn, If,
|
|
4
|
+
instancedArray, nodeProxy, int, float, vec3, vec4, ivec2, ivec4, uniform, Break, Loop, positionView,
|
|
5
|
+
Fn, If, textureLoad, instanceIndex, screenCoordinate, directPointLight,
|
|
6
6
|
renderGroup,
|
|
7
|
-
min, max, pow, log, clamp, dot
|
|
7
|
+
min, max, pow, log, clamp, dot, smoothstep
|
|
8
8
|
} from 'three/tsl';
|
|
9
9
|
|
|
10
10
|
const _vector3 = /*@__PURE__*/ new Vector3();
|
|
11
|
+
const _direction = /*@__PURE__*/ new Vector3();
|
|
12
|
+
const _targetPosition = /*@__PURE__*/ new Vector3();
|
|
11
13
|
const _size = /*@__PURE__*/ new Vector2();
|
|
12
14
|
|
|
15
|
+
// light data texture rows
|
|
16
|
+
|
|
17
|
+
const POSITION_ROW = 0; // world position, cutoff distance
|
|
18
|
+
const COLOR_ROW = 1; // color * intensity, decay
|
|
19
|
+
const SPOT_ROW = 2; // scaled world spot direction, scaled cone cosine
|
|
20
|
+
const BOUNDS_ROW = 3; // world bounding sphere center, radius ( negative if unbounded )
|
|
21
|
+
const ROWS = 4;
|
|
22
|
+
|
|
13
23
|
/**
|
|
14
24
|
* A custom version of `LightsNode` implementing Forward+ clustered shading:
|
|
15
25
|
* the view frustum is subdivided into a 3D grid of clusters (X × Y screen tiles
|
|
16
26
|
* times an exponentially-spaced set of Z depth slices), and each cluster holds
|
|
17
|
-
* only the point lights whose spheres intersect it. At shading time each fragment
|
|
27
|
+
* only the point and spot lights whose bounding spheres intersect it. At shading time each fragment
|
|
18
28
|
* looks up its cluster and loops over just that cluster's lights. Unlike 2D tiled
|
|
19
29
|
* lighting, clustered shading culls lights that share screen pixels but lie at
|
|
20
30
|
* different depths — suitable for 3D scenes with real depth complexity.
|
|
@@ -33,7 +43,7 @@ class ClusteredLightsNode extends LightsNode {
|
|
|
33
43
|
/**
|
|
34
44
|
* Constructs a new clustered lights node.
|
|
35
45
|
*
|
|
36
|
-
* @param {number} [maxLights=1024] - Maximum number of point lights.
|
|
46
|
+
* @param {number} [maxLights=1024] - Maximum number of point and spot lights.
|
|
37
47
|
* @param {number} [tileSize=32] - Screen tile size in pixels (cluster XY size).
|
|
38
48
|
* @param {number} [zSlices=24] - Number of exponential depth slices.
|
|
39
49
|
* @param {number} [maxLightsPerCluster=64] - Per-cluster light-list capacity.
|
|
@@ -42,6 +52,15 @@ class ClusteredLightsNode extends LightsNode {
|
|
|
42
52
|
|
|
43
53
|
super();
|
|
44
54
|
|
|
55
|
+
/**
|
|
56
|
+
* This flag can be used for type testing.
|
|
57
|
+
*
|
|
58
|
+
* @type {boolean}
|
|
59
|
+
* @readonly
|
|
60
|
+
* @default true
|
|
61
|
+
*/
|
|
62
|
+
this.isClusteredLightsNode = true;
|
|
63
|
+
|
|
45
64
|
this.materialLights = [];
|
|
46
65
|
this.clusteredLights = [];
|
|
47
66
|
|
|
@@ -53,12 +72,16 @@ class ClusteredLightsNode extends LightsNode {
|
|
|
53
72
|
this._chunksPerCluster = Math.ceil( maxLightsPerCluster / 4 );
|
|
54
73
|
|
|
55
74
|
this._bufferSize = null;
|
|
75
|
+
this._isWebGLBackend = false;
|
|
76
|
+
this._needsRebuild = false;
|
|
77
|
+
this._invocationsPerCluster = 1;
|
|
56
78
|
this._lightIndexes = null;
|
|
79
|
+
this._renderer = null;
|
|
57
80
|
this._screenClusterIndex = null;
|
|
58
81
|
this._compute = null;
|
|
59
82
|
|
|
60
|
-
const lightsData = new Float32Array( maxLights * 4 *
|
|
61
|
-
this._lightsTexture = new DataTexture( lightsData,
|
|
83
|
+
const lightsData = new Float32Array( maxLights * 4 * ROWS );
|
|
84
|
+
this._lightsTexture = new DataTexture( lightsData, maxLights, ROWS, RGBAFormat, FloatType );
|
|
62
85
|
|
|
63
86
|
// Per Z-slice light range for Z-culling (CPU-sorted, uploaded each frame)
|
|
64
87
|
|
|
@@ -83,30 +106,44 @@ class ClusteredLightsNode extends LightsNode {
|
|
|
83
106
|
|
|
84
107
|
}
|
|
85
108
|
|
|
109
|
+
/**
|
|
110
|
+
* Overwrites the default {@link LightsNode#customCacheKey} implementation by
|
|
111
|
+
* including the node identity and resource version into the cache key.
|
|
112
|
+
*
|
|
113
|
+
* @return {number} The custom cache key.
|
|
114
|
+
*/
|
|
86
115
|
customCacheKey() {
|
|
87
116
|
|
|
88
|
-
|
|
117
|
+
// First use allocates the grid after material cache keys have been computed.
|
|
118
|
+
// Keep that allocation stable, but never share bindings between lights nodes.
|
|
119
|
+
return NodeUtils.hash( this.id, this.version, super.customCacheKey() );
|
|
89
120
|
|
|
90
121
|
}
|
|
91
122
|
|
|
92
|
-
|
|
123
|
+
/**
|
|
124
|
+
* Updates the light data and the light ranges of the depth slices.
|
|
125
|
+
*
|
|
126
|
+
* @private
|
|
127
|
+
* @param {Camera} camera - The camera.
|
|
128
|
+
*/
|
|
129
|
+
_updateLightsTexture( camera ) {
|
|
93
130
|
|
|
94
131
|
const { _lightsTexture: lightsTexture, clusteredLights } = this;
|
|
95
132
|
|
|
96
133
|
const data = lightsTexture.image.data;
|
|
97
134
|
const lineSize = lightsTexture.image.width * 4;
|
|
98
|
-
const count = clusteredLights.length;
|
|
135
|
+
const count = Math.min( clusteredLights.length, this.maxLights );
|
|
99
136
|
|
|
100
137
|
this._lightsCount.value = count;
|
|
101
138
|
|
|
102
|
-
// Sort lights by view-space depth for Z-culling
|
|
139
|
+
// Sort lights by the view-space depth of their bounding spheres for Z-culling
|
|
103
140
|
|
|
104
141
|
const viewZ = this._lightViewZ;
|
|
105
142
|
const order = this._lightSortOrder;
|
|
106
143
|
|
|
107
144
|
for ( let i = 0; i < count; i ++ ) {
|
|
108
145
|
|
|
109
|
-
|
|
146
|
+
this._getBoundingSphereCenter( clusteredLights[ i ], _vector3 );
|
|
110
147
|
_vector3.applyMatrix4( camera.matrixWorldInverse );
|
|
111
148
|
viewZ[ i ] = _vector3.z;
|
|
112
149
|
order[ i ] = i;
|
|
@@ -122,19 +159,60 @@ class ClusteredLightsNode extends LightsNode {
|
|
|
122
159
|
|
|
123
160
|
const light = clusteredLights[ order[ i ] ];
|
|
124
161
|
|
|
162
|
+
const offset = i * 4;
|
|
163
|
+
|
|
125
164
|
_vector3.setFromMatrixPosition( light.matrixWorld );
|
|
126
165
|
|
|
127
|
-
|
|
166
|
+
let rowOffset = lineSize * POSITION_ROW + offset;
|
|
167
|
+
|
|
168
|
+
data[ rowOffset + 0 ] = _vector3.x;
|
|
169
|
+
data[ rowOffset + 1 ] = _vector3.y;
|
|
170
|
+
data[ rowOffset + 2 ] = _vector3.z;
|
|
171
|
+
data[ rowOffset + 3 ] = light.distance;
|
|
172
|
+
|
|
173
|
+
rowOffset = lineSize * COLOR_ROW + offset;
|
|
174
|
+
|
|
175
|
+
data[ rowOffset + 0 ] = light.color.r * light.intensity;
|
|
176
|
+
data[ rowOffset + 1 ] = light.color.g * light.intensity;
|
|
177
|
+
data[ rowOffset + 2 ] = light.color.b * light.intensity;
|
|
178
|
+
data[ rowOffset + 3 ] = light.decay;
|
|
179
|
+
|
|
180
|
+
// The spot attenuation smoothstep( coneCos, penumbraCos, dot( L, direction ) ) is stored
|
|
181
|
+
// as smoothstep( 0, 1, dot( L, direction * scale ) - coneCos * scale ) with
|
|
182
|
+
// scale = 1 / ( penumbraCos - coneCos ). Point lights store a constant attenuation of 1.
|
|
183
|
+
|
|
184
|
+
rowOffset = lineSize * SPOT_ROW + offset;
|
|
185
|
+
|
|
186
|
+
if ( light.isSpotLight === true ) {
|
|
187
|
+
|
|
188
|
+
const coneCos = Math.cos( light.angle );
|
|
189
|
+
const penumbraCos = Math.cos( light.angle * ( 1 - light.penumbra ) );
|
|
190
|
+
const scale = 1 / Math.max( penumbraCos - coneCos, 1e-4 );
|
|
191
|
+
|
|
192
|
+
this._getSpotDirection( light, _direction ).multiplyScalar( scale );
|
|
193
|
+
|
|
194
|
+
data[ rowOffset + 0 ] = _direction.x;
|
|
195
|
+
data[ rowOffset + 1 ] = _direction.y;
|
|
196
|
+
data[ rowOffset + 2 ] = _direction.z;
|
|
197
|
+
data[ rowOffset + 3 ] = coneCos * scale;
|
|
198
|
+
|
|
199
|
+
} else {
|
|
128
200
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
201
|
+
data[ rowOffset + 0 ] = 0;
|
|
202
|
+
data[ rowOffset + 1 ] = 0;
|
|
203
|
+
data[ rowOffset + 2 ] = 0;
|
|
204
|
+
data[ rowOffset + 3 ] = - 1;
|
|
133
205
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
this._getBoundingSphereCenter( light, _vector3 );
|
|
209
|
+
|
|
210
|
+
rowOffset = lineSize * BOUNDS_ROW + offset;
|
|
211
|
+
|
|
212
|
+
data[ rowOffset + 0 ] = _vector3.x;
|
|
213
|
+
data[ rowOffset + 1 ] = _vector3.y;
|
|
214
|
+
data[ rowOffset + 2 ] = _vector3.z;
|
|
215
|
+
data[ rowOffset + 3 ] = this._getBoundingSphereRadius( light );
|
|
138
216
|
|
|
139
217
|
}
|
|
140
218
|
|
|
@@ -160,10 +238,10 @@ class ClusteredLightsNode extends LightsNode {
|
|
|
160
238
|
for ( let i = 0; i < count; i ++ ) {
|
|
161
239
|
|
|
162
240
|
const vz = viewZ[ order[ i ] ];
|
|
163
|
-
const r =
|
|
164
|
-
const radius = r
|
|
241
|
+
const r = data[ lineSize * BOUNDS_ROW + i * 4 + 3 ];
|
|
242
|
+
const radius = r >= 0 ? r : far;
|
|
165
243
|
|
|
166
|
-
//
|
|
244
|
+
// Bounding sphere Z: [vz - radius, vz + radius]
|
|
167
245
|
// Slice Z: [sliceFar, sliceNear] (both negative, sliceFar < sliceNear)
|
|
168
246
|
if ( vz + radius >= sliceFar && vz - radius <= sliceNear ) {
|
|
169
247
|
|
|
@@ -190,13 +268,86 @@ class ClusteredLightsNode extends LightsNode {
|
|
|
190
268
|
|
|
191
269
|
}
|
|
192
270
|
|
|
271
|
+
/**
|
|
272
|
+
* Returns the normalized world-space direction pointing from the spot light's
|
|
273
|
+
* target to the spot light.
|
|
274
|
+
*
|
|
275
|
+
* @private
|
|
276
|
+
* @param {SpotLight} light - The spot light.
|
|
277
|
+
* @param {Vector3} target - The target vector.
|
|
278
|
+
* @return {Vector3} The direction.
|
|
279
|
+
*/
|
|
280
|
+
_getSpotDirection( light, target ) {
|
|
281
|
+
|
|
282
|
+
_targetPosition.setFromMatrixPosition( light.target.matrixWorld );
|
|
283
|
+
|
|
284
|
+
return target.setFromMatrixPosition( light.matrixWorld ).sub( _targetPosition ).normalize();
|
|
285
|
+
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Returns the world-space center of the light's bounding sphere. For spot lights,
|
|
290
|
+
* this is the center of the smallest sphere enclosing the cone.
|
|
291
|
+
*
|
|
292
|
+
* @private
|
|
293
|
+
* @param {PointLight|SpotLight} light - The light.
|
|
294
|
+
* @param {Vector3} target - The target vector.
|
|
295
|
+
* @return {Vector3} The bounding sphere center.
|
|
296
|
+
*/
|
|
297
|
+
_getBoundingSphereCenter( light, target ) {
|
|
298
|
+
|
|
299
|
+
target.setFromMatrixPosition( light.matrixWorld );
|
|
300
|
+
|
|
301
|
+
if ( light.isSpotLight === true && light.distance > 0 ) {
|
|
302
|
+
|
|
303
|
+
const angle = Math.min( light.angle, Math.PI / 2 );
|
|
304
|
+
const offset = angle > Math.PI / 4 ? light.distance * Math.cos( angle ) : light.distance / ( 2 * Math.cos( angle ) );
|
|
305
|
+
|
|
306
|
+
// _getSpotDirection() points away from the cone, so step backwards
|
|
307
|
+
|
|
308
|
+
target.addScaledVector( this._getSpotDirection( light, _direction ), - offset );
|
|
309
|
+
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
return target;
|
|
313
|
+
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Returns the radius of the light's bounding sphere.
|
|
318
|
+
*
|
|
319
|
+
* @private
|
|
320
|
+
* @param {PointLight|SpotLight} light - The light.
|
|
321
|
+
* @return {number} The bounding sphere radius. `-1` if the light has no cutoff distance.
|
|
322
|
+
*/
|
|
323
|
+
_getBoundingSphereRadius( light ) {
|
|
324
|
+
|
|
325
|
+
if ( light.distance <= 0 ) return - 1;
|
|
326
|
+
|
|
327
|
+
if ( light.isSpotLight === true ) {
|
|
328
|
+
|
|
329
|
+
const angle = Math.min( light.angle, Math.PI / 2 );
|
|
330
|
+
|
|
331
|
+
return angle > Math.PI / 4 ? light.distance * Math.sin( angle ) : light.distance / ( 2 * Math.cos( angle ) );
|
|
332
|
+
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return light.distance;
|
|
336
|
+
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Updates the light data and assigns the lights to the clusters.
|
|
341
|
+
*
|
|
342
|
+
* @param {NodeFrame} frame - A reference to the current node frame.
|
|
343
|
+
*/
|
|
193
344
|
updateBefore( frame ) {
|
|
194
345
|
|
|
195
346
|
const { renderer, camera } = frame;
|
|
196
347
|
|
|
197
|
-
this.
|
|
348
|
+
this._initGrid( renderer );
|
|
198
349
|
|
|
199
|
-
this.
|
|
350
|
+
this._updateLightsTexture( camera );
|
|
200
351
|
|
|
201
352
|
this._cameraNear.value = camera.near;
|
|
202
353
|
this._cameraFar.value = camera.far;
|
|
@@ -207,6 +358,15 @@ class ClusteredLightsNode extends LightsNode {
|
|
|
207
358
|
|
|
208
359
|
}
|
|
209
360
|
|
|
361
|
+
/**
|
|
362
|
+
* Configures this node with an array of lights. Point and spot lights without
|
|
363
|
+
* shadows are clustered (except lights with a `colorNode`, spot lights with a
|
|
364
|
+
* projected `map` and light types with their own light node, like `IESSpotLight`
|
|
365
|
+
* or `ProjectorLight`), all other lights use the default lighting.
|
|
366
|
+
*
|
|
367
|
+
* @param {Array<Light>} lights - An array of lights.
|
|
368
|
+
* @return {ClusteredLightsNode} A reference to this node.
|
|
369
|
+
*/
|
|
210
370
|
setLights( lights ) {
|
|
211
371
|
|
|
212
372
|
const { clusteredLights, materialLights } = this;
|
|
@@ -216,7 +376,7 @@ class ClusteredLightsNode extends LightsNode {
|
|
|
216
376
|
|
|
217
377
|
for ( const light of lights ) {
|
|
218
378
|
|
|
219
|
-
if (
|
|
379
|
+
if ( this._isClusteredLight( light ) ) {
|
|
220
380
|
|
|
221
381
|
clusteredLights[ clusteredIndex ++ ] = light;
|
|
222
382
|
|
|
@@ -235,19 +395,48 @@ class ClusteredLightsNode extends LightsNode {
|
|
|
235
395
|
|
|
236
396
|
}
|
|
237
397
|
|
|
238
|
-
|
|
398
|
+
/**
|
|
399
|
+
* Returns `true` if the given light can be clustered.
|
|
400
|
+
*
|
|
401
|
+
* @private
|
|
402
|
+
* @param {Light} light - The light.
|
|
403
|
+
* @return {boolean} Whether the light is clustered or not.
|
|
404
|
+
*/
|
|
405
|
+
_isClusteredLight( light ) {
|
|
239
406
|
|
|
240
|
-
|
|
407
|
+
// Shadows, color nodes (any light) and projected maps (spot lights) are not supported.
|
|
408
|
+
|
|
409
|
+
if ( light.castShadow === true || light.colorNode ) return false;
|
|
410
|
+
|
|
411
|
+
// Only lights shaded by the plain point and spot light nodes are clustered. Subclasses
|
|
412
|
+
// registered with their own light node (e.g. IESSpotLight, ProjectorLight) keep the
|
|
413
|
+
// default lighting, since clustering would shade them as plain spot lights.
|
|
414
|
+
|
|
415
|
+
if ( light._lightNode === PointLightNode ) return true;
|
|
416
|
+
|
|
417
|
+
return light._lightNode === SpotLightNode && ! light.map;
|
|
241
418
|
|
|
242
419
|
}
|
|
243
420
|
|
|
244
|
-
|
|
421
|
+
/**
|
|
422
|
+
* Returns the lights that are not clustered.
|
|
423
|
+
*
|
|
424
|
+
* @return {Array<Light>} The lights using the default lighting.
|
|
425
|
+
*/
|
|
426
|
+
getBuiltinLights() {
|
|
245
427
|
|
|
246
|
-
return this.
|
|
428
|
+
return this.materialLights;
|
|
247
429
|
|
|
248
430
|
}
|
|
249
431
|
|
|
250
|
-
|
|
432
|
+
/**
|
|
433
|
+
* Returns the light index stored at the given slot of the fragment's cluster.
|
|
434
|
+
*
|
|
435
|
+
* @private
|
|
436
|
+
* @param {number|Node<int>} element - The slot.
|
|
437
|
+
* @return {Node<int>} The 1-based light index. `0` marks the end of the list.
|
|
438
|
+
*/
|
|
439
|
+
_getTile( element ) {
|
|
251
440
|
|
|
252
441
|
element = int( element );
|
|
253
442
|
|
|
@@ -259,6 +448,12 @@ class ClusteredLightsNode extends LightsNode {
|
|
|
259
448
|
|
|
260
449
|
}
|
|
261
450
|
|
|
451
|
+
/**
|
|
452
|
+
* Returns the number of lights in the fragment's cluster. Useful for debugging.
|
|
453
|
+
*
|
|
454
|
+
* @param {Node<int>} zSliceNode - The depth slice to inspect. A negative value selects the fragment's own cluster.
|
|
455
|
+
* @return {Node<int>} The light count.
|
|
456
|
+
*/
|
|
262
457
|
getClusterLightCount( zSliceNode ) {
|
|
263
458
|
|
|
264
459
|
const getCount = Fn( ( [ zSliceNode ] ) => {
|
|
@@ -308,32 +503,66 @@ class ClusteredLightsNode extends LightsNode {
|
|
|
308
503
|
|
|
309
504
|
}
|
|
310
505
|
|
|
311
|
-
|
|
506
|
+
/**
|
|
507
|
+
* Returns the shading data of the light at the given index.
|
|
508
|
+
*
|
|
509
|
+
* @private
|
|
510
|
+
* @param {number|Node<int>} index - The light index.
|
|
511
|
+
* @return {Object} The light's view position, distance, color, decay, view-space spot direction and spot offset.
|
|
512
|
+
*/
|
|
513
|
+
_getLightData( index ) {
|
|
312
514
|
|
|
313
515
|
index = int( index );
|
|
314
516
|
|
|
315
|
-
const dataA = textureLoad( this._lightsTexture, ivec2( index,
|
|
316
|
-
const dataB = textureLoad( this._lightsTexture, ivec2( index,
|
|
517
|
+
const dataA = textureLoad( this._lightsTexture, ivec2( index, POSITION_ROW ) );
|
|
518
|
+
const dataB = textureLoad( this._lightsTexture, ivec2( index, COLOR_ROW ) );
|
|
519
|
+
const dataC = textureLoad( this._lightsTexture, ivec2( index, SPOT_ROW ) );
|
|
317
520
|
|
|
318
|
-
const
|
|
319
|
-
const viewPosition = this._cameraViewMatrix.mul( vec4( position, 1.0 ) ).xyz;
|
|
521
|
+
const viewPosition = this._cameraViewMatrix.mul( vec4( dataA.xyz, 1.0 ) ).xyz;
|
|
320
522
|
const distance = dataA.w;
|
|
321
523
|
const color = dataB.rgb;
|
|
322
524
|
const decay = dataB.w;
|
|
525
|
+
const spotDirection = this._cameraViewMatrix.mul( vec4( dataC.xyz, 0.0 ) ).xyz;
|
|
526
|
+
const spotOffset = dataC.w;
|
|
323
527
|
|
|
324
528
|
return {
|
|
325
|
-
position,
|
|
326
529
|
viewPosition,
|
|
327
530
|
distance,
|
|
328
531
|
color,
|
|
329
|
-
decay
|
|
532
|
+
decay,
|
|
533
|
+
spotDirection,
|
|
534
|
+
spotOffset
|
|
330
535
|
};
|
|
331
536
|
|
|
332
537
|
}
|
|
333
538
|
|
|
539
|
+
/**
|
|
540
|
+
* Returns the culling data of the light at the given index.
|
|
541
|
+
*
|
|
542
|
+
* @private
|
|
543
|
+
* @param {number|Node<int>} index - The light index.
|
|
544
|
+
* @return {Object} The view-space center and the radius of the light's bounding sphere. A negative radius marks an unbounded light.
|
|
545
|
+
*/
|
|
546
|
+
_getLightBounds( index ) {
|
|
547
|
+
|
|
548
|
+
const data = textureLoad( this._lightsTexture, ivec2( int( index ), BOUNDS_ROW ) );
|
|
549
|
+
|
|
550
|
+
const center = this._cameraViewMatrix.mul( vec4( data.xyz, 1.0 ) ).xyz;
|
|
551
|
+
const radius = data.w;
|
|
552
|
+
|
|
553
|
+
return { center, radius };
|
|
554
|
+
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/**
|
|
558
|
+
* Sets up the default lights and the clustered point and spot lights.
|
|
559
|
+
*
|
|
560
|
+
* @param {NodeBuilder} builder - A reference to the current node builder.
|
|
561
|
+
* @param {Array<LightingNode>} lightNodes - An array of lighting nodes.
|
|
562
|
+
*/
|
|
334
563
|
setupLights( builder, lightNodes ) {
|
|
335
564
|
|
|
336
|
-
this.
|
|
565
|
+
this._initGrid( builder.renderer );
|
|
337
566
|
|
|
338
567
|
//
|
|
339
568
|
|
|
@@ -348,7 +577,7 @@ class ClusteredLightsNode extends LightsNode {
|
|
|
348
577
|
|
|
349
578
|
Loop( this.maxLightsPerCluster, ( { i } ) => {
|
|
350
579
|
|
|
351
|
-
const lightIndex = this.
|
|
580
|
+
const lightIndex = this._getTile( i );
|
|
352
581
|
|
|
353
582
|
If( lightIndex.equal( int( 0 ) ), () => {
|
|
354
583
|
|
|
@@ -356,15 +585,20 @@ class ClusteredLightsNode extends LightsNode {
|
|
|
356
585
|
|
|
357
586
|
} );
|
|
358
587
|
|
|
359
|
-
const { color, decay, viewPosition, distance } = this.
|
|
588
|
+
const { color, decay, viewPosition, distance, spotDirection, spotOffset } = this._getLightData( lightIndex.sub( 1 ) );
|
|
360
589
|
|
|
361
590
|
const lightVector = viewPosition.sub( positionView );
|
|
362
591
|
|
|
363
|
-
//
|
|
364
|
-
|
|
592
|
+
// spot cone attenuation, always 1 for point lights
|
|
593
|
+
const spotAttenuation = smoothstep( 0, 1, dot( lightVector.normalize(), spotDirection ).sub( spotOffset ) ).toVar();
|
|
594
|
+
|
|
595
|
+
// Early-out: skip full BRDF if fragment is beyond the light's cutoff or outside the spot cone
|
|
596
|
+
const inRange = distance.equal( 0 ).or( dot( lightVector, lightVector ).lessThanEqual( distance.mul( distance ) ) );
|
|
597
|
+
|
|
598
|
+
If( inRange.and( spotAttenuation.greaterThan( 0 ) ), () => {
|
|
365
599
|
|
|
366
600
|
builder.lightsNode.setupDirectLight( builder, this, directPointLight( {
|
|
367
|
-
color,
|
|
601
|
+
color: color.mul( spotAttenuation ),
|
|
368
602
|
lightVector,
|
|
369
603
|
cutoffDistance: distance,
|
|
370
604
|
decayExponent: decay
|
|
@@ -378,7 +612,14 @@ class ClusteredLightsNode extends LightsNode {
|
|
|
378
612
|
|
|
379
613
|
}
|
|
380
614
|
|
|
381
|
-
|
|
615
|
+
/**
|
|
616
|
+
* Rounds the given size up to a multiple of the tile size.
|
|
617
|
+
*
|
|
618
|
+
* @private
|
|
619
|
+
* @param {number} value - The size.
|
|
620
|
+
* @return {number} The fitted size.
|
|
621
|
+
*/
|
|
622
|
+
_getBufferFitSize( value ) {
|
|
382
623
|
|
|
383
624
|
const multiple = this.tileSize;
|
|
384
625
|
|
|
@@ -386,98 +627,161 @@ class ClusteredLightsNode extends LightsNode {
|
|
|
386
627
|
|
|
387
628
|
}
|
|
388
629
|
|
|
630
|
+
/**
|
|
631
|
+
* Sets the size of the cluster grid. Must be called outside of rendering. Not required
|
|
632
|
+
* with {@link ClusteredLighting}, which fits the grid at the beginning of each render.
|
|
633
|
+
*
|
|
634
|
+
* @param {number} width - The drawing buffer width.
|
|
635
|
+
* @param {number} height - The drawing buffer height.
|
|
636
|
+
* @return {ClusteredLightsNode} A reference to this node.
|
|
637
|
+
*/
|
|
389
638
|
setSize( width, height ) {
|
|
390
639
|
|
|
391
|
-
width
|
|
392
|
-
height = this.getBufferFitSize( height );
|
|
640
|
+
this._resize( this._getBufferFitSize( width ), this._getBufferFitSize( height ) );
|
|
393
641
|
|
|
394
|
-
|
|
642
|
+
return this;
|
|
395
643
|
|
|
396
|
-
|
|
644
|
+
}
|
|
397
645
|
|
|
398
|
-
|
|
646
|
+
/**
|
|
647
|
+
* Fits the cluster grid to the framebuffer of the render that is about to start. Called by
|
|
648
|
+
* {@link ClusteredLighting} before the render objects of the render are prepared. Since each
|
|
649
|
+
* render depth has its own node, no pending command buffer uses the resources of this node at
|
|
650
|
+
* this point, so a rebuild can free the replaced resources immediately.
|
|
651
|
+
*/
|
|
652
|
+
updateSize() {
|
|
399
653
|
|
|
400
|
-
|
|
654
|
+
if ( this._renderer !== null ) this._fitToRenderer( this._renderer );
|
|
401
655
|
|
|
402
656
|
}
|
|
403
657
|
|
|
404
|
-
|
|
658
|
+
/**
|
|
659
|
+
* Creates the cluster grid on first use. While rendering, the grid is never rebuilt: the
|
|
660
|
+
* replaced resources could still be used by a command buffer that was not submitted yet.
|
|
661
|
+
*
|
|
662
|
+
* @private
|
|
663
|
+
* @param {Renderer} renderer - The renderer.
|
|
664
|
+
*/
|
|
665
|
+
_initGrid( renderer ) {
|
|
666
|
+
|
|
667
|
+
this._renderer = renderer;
|
|
405
668
|
|
|
406
|
-
|
|
669
|
+
if ( this._compute === null ) this._fitToRenderer( renderer );
|
|
670
|
+
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
/**
|
|
674
|
+
* Fits the cluster grid to the framebuffer the renderer is rendering to.
|
|
675
|
+
*
|
|
676
|
+
* @private
|
|
677
|
+
* @param {Renderer} renderer - The renderer.
|
|
678
|
+
*/
|
|
679
|
+
_fitToRenderer( renderer ) {
|
|
407
680
|
|
|
408
|
-
|
|
409
|
-
|
|
681
|
+
// the grid must match the framebuffer being rendered: a render target (e.g. a reflector
|
|
682
|
+
// with a resolution scale) can differ from the drawing buffer
|
|
410
683
|
|
|
411
|
-
|
|
684
|
+
const renderTarget = renderer.getRenderTarget();
|
|
412
685
|
|
|
413
|
-
|
|
686
|
+
if ( renderTarget !== null ) {
|
|
414
687
|
|
|
415
|
-
|
|
688
|
+
_size.set( renderTarget.width, renderTarget.height );
|
|
416
689
|
|
|
417
|
-
|
|
690
|
+
} else {
|
|
691
|
+
|
|
692
|
+
renderer.getDrawingBufferSize( _size );
|
|
418
693
|
|
|
419
694
|
}
|
|
420
695
|
|
|
696
|
+
// the compute differs per backend, so a backend change requires a rebuild
|
|
697
|
+
|
|
698
|
+
const isWebGLBackend = renderer.backend.isWebGLBackend === true;
|
|
699
|
+
|
|
700
|
+
if ( this._compute !== null && this._isWebGLBackend !== isWebGLBackend ) this._needsRebuild = true;
|
|
701
|
+
|
|
702
|
+
this._isWebGLBackend = isWebGLBackend;
|
|
703
|
+
|
|
704
|
+
this._resize( this._getBufferFitSize( _size.width ), this._getBufferFitSize( _size.height ) );
|
|
705
|
+
|
|
421
706
|
}
|
|
422
707
|
|
|
423
|
-
|
|
708
|
+
/**
|
|
709
|
+
* Resizes the cluster grid. The grid dimensions are uniforms, so resizing only updates
|
|
710
|
+
* them and the compute count; the compute and the storage are rebuilt only when the
|
|
711
|
+
* storage has to grow (or the backend changed).
|
|
712
|
+
*
|
|
713
|
+
* @private
|
|
714
|
+
* @param {number} width - The fitted width.
|
|
715
|
+
* @param {number} height - The fitted height.
|
|
716
|
+
*/
|
|
717
|
+
_resize( width, height ) {
|
|
424
718
|
|
|
425
|
-
|
|
719
|
+
if ( this._needsRebuild === false && this._bufferSize !== null && this._bufferSize.width === width && this._bufferSize.height === height ) return;
|
|
426
720
|
|
|
427
|
-
const
|
|
721
|
+
const NX = Math.floor( width / this.tileSize );
|
|
722
|
+
const NY = Math.floor( height / this.tileSize );
|
|
723
|
+
const clusterCount = NX * NY * this.zSlices;
|
|
428
724
|
|
|
429
|
-
const
|
|
430
|
-
const NY = Math.floor( bufferSize.height / tileSize );
|
|
431
|
-
const NZ = zSlices;
|
|
432
|
-
const clusterCount = NX * NY * NZ;
|
|
725
|
+
const requiredElements = clusterCount * this._chunksPerCluster;
|
|
433
726
|
|
|
434
|
-
this.
|
|
727
|
+
if ( this._needsRebuild === true || this._compute === null || this._lightIndexes.value.array.length / 4 < requiredElements ) {
|
|
435
728
|
|
|
436
|
-
|
|
729
|
+
this._create( clusterCount );
|
|
437
730
|
|
|
438
|
-
|
|
731
|
+
}
|
|
439
732
|
|
|
440
|
-
|
|
733
|
+
this._gridDimensions.value.set( NX, NY );
|
|
734
|
+
this._compute.count = clusterCount * this._invocationsPerCluster;
|
|
441
735
|
|
|
442
|
-
|
|
736
|
+
if ( this._bufferSize === null ) this._bufferSize = new Vector2();
|
|
443
737
|
|
|
444
|
-
|
|
738
|
+
this._bufferSize.set( width, height );
|
|
445
739
|
|
|
446
|
-
|
|
740
|
+
}
|
|
447
741
|
|
|
448
|
-
|
|
449
|
-
|
|
742
|
+
/**
|
|
743
|
+
* Creates the light index storage, the compute node and the fragment cluster lookup.
|
|
744
|
+
* The storage is allocated with headroom so that most resizes do not need a rebuild.
|
|
745
|
+
*
|
|
746
|
+
* @private
|
|
747
|
+
* @param {number} clusterCount - The number of clusters of the current grid.
|
|
748
|
+
*/
|
|
749
|
+
_create( clusterCount ) {
|
|
450
750
|
|
|
451
|
-
|
|
452
|
-
lightIndexes = attributeArray( lightIndexesArray, 'ivec4' ).setName( 'lightIndexes' );
|
|
751
|
+
const { tileSize, zSlices, _chunksPerCluster: chunksPerCluster } = this;
|
|
453
752
|
|
|
454
|
-
|
|
753
|
+
const NZ = zSlices;
|
|
455
754
|
|
|
456
|
-
//
|
|
755
|
+
// grid dimensions as uniforms: shaders do not depend on the drawing buffer size
|
|
457
756
|
|
|
458
|
-
const
|
|
757
|
+
const grid = this._gridDimensions;
|
|
758
|
+
const NX = int( grid.x );
|
|
759
|
+
const NY = int( grid.y );
|
|
459
760
|
|
|
460
|
-
|
|
761
|
+
// Per-cluster light-index storage (ivec4 chunks), grown geometrically.
|
|
461
762
|
|
|
462
|
-
|
|
763
|
+
const requiredElements = clusterCount * chunksPerCluster;
|
|
764
|
+
const previousElements = this._lightIndexes !== null ? this._lightIndexes.value.array.length / 4 : 0;
|
|
765
|
+
const elements = previousElements === 0 ? Math.ceil( requiredElements * 1.5 ) : Math.max( requiredElements, previousElements * 2 );
|
|
463
766
|
|
|
464
|
-
|
|
767
|
+
// rebuilds only happen outside of rendering (see _initGrid()), so the replaced resources can be freed
|
|
465
768
|
|
|
466
|
-
|
|
769
|
+
if ( this._compute !== null ) {
|
|
467
770
|
|
|
468
|
-
|
|
771
|
+
// Replacing storage requires new material bindings. The initial allocation does not.
|
|
772
|
+
this.needsUpdate = true;
|
|
469
773
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
const idx = instanceIndex.mul( int( chunksPerCluster ) ).add( chunkOffset );
|
|
774
|
+
this._compute.dispose();
|
|
775
|
+
this._lightIndexes.value.dispose();
|
|
473
776
|
|
|
474
|
-
|
|
777
|
+
}
|
|
475
778
|
|
|
476
|
-
|
|
779
|
+
// instanced storage: WebGL computes use gl_InstanceID as instanceIndex; PBO: random access in the fragment stage
|
|
780
|
+
const lightIndexes = instancedArray( new Int32Array( elements * 4 ), 'ivec4' ).setPBO( true ).setName( 'lightIndexes' );
|
|
477
781
|
|
|
478
|
-
//
|
|
782
|
+
// view-space AABB of the given cluster
|
|
479
783
|
|
|
480
|
-
const
|
|
784
|
+
const getClusterBounds = ( clusterIndex ) => {
|
|
481
785
|
|
|
482
786
|
// view-space scale factors derived from the projection matrix:
|
|
483
787
|
// view_x = ndc_x * (-view_z) / focal_x = ndc_x * (-view_z) * invFocalX
|
|
@@ -486,17 +790,17 @@ class ClusteredLightsNode extends LightsNode {
|
|
|
486
790
|
const invFocalX = float( 1 ).div( this._cameraProjectionMatrix.element( 0 ).element( 0 ) );
|
|
487
791
|
const invFocalY = float( 1 ).div( this._cameraProjectionMatrix.element( 1 ).element( 1 ) );
|
|
488
792
|
|
|
489
|
-
// 3D cluster coordinates
|
|
490
|
-
const cx =
|
|
491
|
-
const cy =
|
|
492
|
-
const cz =
|
|
793
|
+
// 3D cluster coordinates
|
|
794
|
+
const cx = clusterIndex.mod( NX );
|
|
795
|
+
const cy = clusterIndex.div( NX ).mod( NY );
|
|
796
|
+
const cz = clusterIndex.div( NX.mul( NY ) );
|
|
493
797
|
|
|
494
798
|
// NDC X/Y bounds of the cluster.
|
|
495
799
|
// Y is flipped: cy=0 is the top screen row (fragment y=0), which is NDC y=+1.
|
|
496
|
-
const ndcXmin = float( cx ).mul( 2.0
|
|
497
|
-
const ndcXmax = float( cx.add( int( 1 ) ) ).mul( 2.0
|
|
498
|
-
const ndcYmax = float( 1 ).sub( float( cy ).mul( 2.0
|
|
499
|
-
const ndcYmin = float( 1 ).sub( float( cy.add( int( 1 ) ) ).mul( 2.0
|
|
800
|
+
const ndcXmin = float( cx ).mul( 2.0 ).div( grid.x ).sub( 1.0 );
|
|
801
|
+
const ndcXmax = float( cx.add( int( 1 ) ) ).mul( 2.0 ).div( grid.x ).sub( 1.0 );
|
|
802
|
+
const ndcYmax = float( 1 ).sub( float( cy ).mul( 2.0 ).div( grid.y ) );
|
|
803
|
+
const ndcYmin = float( 1 ).sub( float( cy.add( int( 1 ) ) ).mul( 2.0 ).div( grid.y ) );
|
|
500
804
|
|
|
501
805
|
// View-space Z bounds (negative, exponential slicing)
|
|
502
806
|
const farOverNear = this._cameraFar.div( this._cameraNear );
|
|
@@ -527,13 +831,43 @@ class ClusteredLightsNode extends LightsNode {
|
|
|
527
831
|
const aabbMin = vec3( aabbMinX, aabbMinY, zFarCluster );
|
|
528
832
|
const aabbMax = vec3( aabbMaxX, aabbMaxY, zNearCluster );
|
|
529
833
|
|
|
530
|
-
|
|
531
|
-
Loop( chunksPerCluster, ( { i } ) => {
|
|
834
|
+
return { cz, aabbMin, aabbMax };
|
|
532
835
|
|
|
533
|
-
|
|
836
|
+
};
|
|
534
837
|
|
|
535
|
-
|
|
838
|
+
// sphere-AABB intersection in view space
|
|
839
|
+
|
|
840
|
+
const intersectsCluster = ( lightIdx, aabbMin, aabbMax ) => {
|
|
841
|
+
|
|
842
|
+
const { center, radius } = this._getLightBounds( lightIdx );
|
|
843
|
+
|
|
844
|
+
const closest = max( aabbMin, min( center, aabbMax ) );
|
|
845
|
+
const diff = center.sub( closest );
|
|
846
|
+
const distSq = dot( diff, diff );
|
|
847
|
+
|
|
848
|
+
return radius.lessThan( 0 ).or( distSq.lessThanEqual( radius.mul( radius ) ) );
|
|
849
|
+
|
|
850
|
+
};
|
|
851
|
+
|
|
852
|
+
// Each invocation fills `chunksPerInvocation` ivec4 chunks of one cluster. WebGL computes
|
|
853
|
+
// run via transform feedback and can only write their own element, so there each
|
|
854
|
+
// invocation fills a single chunk; WebGPU uses one invocation per cluster.
|
|
855
|
+
|
|
856
|
+
const chunksPerInvocation = this._isWebGLBackend ? 1 : chunksPerCluster;
|
|
857
|
+
const invocationsPerCluster = chunksPerCluster / chunksPerInvocation;
|
|
536
858
|
|
|
859
|
+
this._invocationsPerCluster = invocationsPerCluster;
|
|
860
|
+
|
|
861
|
+
const compute = Fn( () => {
|
|
862
|
+
|
|
863
|
+
const clusterIndex = int( instanceIndex.div( invocationsPerCluster ) );
|
|
864
|
+
const firstChunk = int( instanceIndex.mod( invocationsPerCluster ) ).mul( chunksPerInvocation );
|
|
865
|
+
const firstSlot = firstChunk.mul( 4 );
|
|
866
|
+
const lastSlot = firstSlot.add( chunksPerInvocation * 4 );
|
|
867
|
+
|
|
868
|
+
const { cz, aabbMin, aabbMax } = getClusterBounds( clusterIndex );
|
|
869
|
+
|
|
870
|
+
const chunks = ivec4( 0 ).toArray( chunksPerInvocation ).toVar();
|
|
537
871
|
const index = int( 0 ).toVar();
|
|
538
872
|
|
|
539
873
|
// Z-culling: only test lights that can reach this cluster's Z-slice
|
|
@@ -545,30 +879,56 @@ class ClusteredLightsNode extends LightsNode {
|
|
|
545
879
|
|
|
546
880
|
const lightIdx = rangeStart.add( i );
|
|
547
881
|
|
|
548
|
-
If( index.greaterThanEqual(
|
|
882
|
+
If( index.greaterThanEqual( lastSlot ).or( lightIdx.greaterThanEqual( rangeEnd ) ), () => {
|
|
549
883
|
|
|
550
|
-
|
|
884
|
+
Break();
|
|
551
885
|
|
|
552
886
|
} );
|
|
553
887
|
|
|
554
|
-
|
|
888
|
+
If( intersectsCluster( lightIdx, aabbMin, aabbMax ), () => {
|
|
555
889
|
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
const closest = max( aabbMin, min( pos, aabbMax ) );
|
|
559
|
-
const diff = pos.sub( closest );
|
|
560
|
-
const distSq = dot( diff, diff );
|
|
890
|
+
// slots before firstSlot belong to other invocations of this cluster
|
|
891
|
+
If( index.greaterThanEqual( firstSlot ), () => {
|
|
561
892
|
|
|
562
|
-
|
|
893
|
+
const slot = index.sub( firstSlot );
|
|
894
|
+
|
|
895
|
+
chunks.element( slot.div( 4 ) ).element( slot.mod( 4 ) ).assign( lightIdx.add( int( 1 ) ) );
|
|
896
|
+
|
|
897
|
+
} );
|
|
563
898
|
|
|
564
|
-
getClusterSlot( index ).assign( lightIdx.add( int( 1 ) ) );
|
|
565
899
|
index.addAssign( int( 1 ) );
|
|
566
900
|
|
|
567
901
|
} );
|
|
568
902
|
|
|
569
903
|
} );
|
|
570
904
|
|
|
571
|
-
|
|
905
|
+
const firstElement = clusterIndex.mul( chunksPerCluster ).add( firstChunk );
|
|
906
|
+
|
|
907
|
+
const writeChunks = () => {
|
|
908
|
+
|
|
909
|
+
Loop( chunksPerInvocation, ( { i } ) => {
|
|
910
|
+
|
|
911
|
+
lightIndexes.element( firstElement.add( i ) ).assign( chunks.element( i ) );
|
|
912
|
+
|
|
913
|
+
} );
|
|
914
|
+
|
|
915
|
+
};
|
|
916
|
+
|
|
917
|
+
if ( this._isWebGLBackend === true ) {
|
|
918
|
+
|
|
919
|
+
// WebGL binds the transform feedback buffers per compute program, and programs are shared by
|
|
920
|
+
// identical code. The (always true) capacity and node id constants give each node and storage
|
|
921
|
+
// allocation its own program, so a compute never writes to the storage of another one.
|
|
922
|
+
|
|
923
|
+
If( firstElement.lessThan( int( elements ) ).and( int( this.id ).notEqual( int( - 1 ) ) ), writeChunks );
|
|
924
|
+
|
|
925
|
+
} else {
|
|
926
|
+
|
|
927
|
+
writeChunks();
|
|
928
|
+
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
} )().compute( clusterCount * invocationsPerCluster ).setName( 'Update Clustered Lights' );
|
|
572
932
|
|
|
573
933
|
// shading-side: fragment → cluster index
|
|
574
934
|
|
|
@@ -585,8 +945,8 @@ class ClusteredLightsNode extends LightsNode {
|
|
|
585
945
|
const zSlice = clamp( sliceFloat.floor(), float( 0 ), float( NZ - 1 ) );
|
|
586
946
|
|
|
587
947
|
return int( screenTile.x )
|
|
588
|
-
.add( int( screenTile.y ).mul(
|
|
589
|
-
.add( int( zSlice ).mul(
|
|
948
|
+
.add( int( screenTile.y ).mul( NX ) )
|
|
949
|
+
.add( int( zSlice ).mul( NX.mul( NY ) ) );
|
|
590
950
|
|
|
591
951
|
} );
|
|
592
952
|
|
|
@@ -594,10 +954,10 @@ class ClusteredLightsNode extends LightsNode {
|
|
|
594
954
|
|
|
595
955
|
// assigns
|
|
596
956
|
|
|
597
|
-
this._bufferSize = bufferSize;
|
|
598
957
|
this._lightIndexes = lightIndexes;
|
|
599
958
|
this._screenClusterIndex = screenClusterIndex;
|
|
600
959
|
this._compute = compute;
|
|
960
|
+
this._needsRebuild = false;
|
|
601
961
|
|
|
602
962
|
}
|
|
603
963
|
|
|
@@ -607,6 +967,7 @@ class ClusteredLightsNode extends LightsNode {
|
|
|
607
967
|
dispose() {
|
|
608
968
|
|
|
609
969
|
if ( this._compute !== null ) this._compute.dispose();
|
|
970
|
+
if ( this._lightIndexes !== null ) this._lightIndexes.value.dispose();
|
|
610
971
|
|
|
611
972
|
this._lightsTexture.dispose();
|
|
612
973
|
this._zSliceRangesTexture.dispose();
|
|
@@ -624,7 +985,7 @@ export default ClusteredLightsNode;
|
|
|
624
985
|
*
|
|
625
986
|
* @tsl
|
|
626
987
|
* @function
|
|
627
|
-
* @param {number} [maxLights=1024] - Maximum number of point lights.
|
|
988
|
+
* @param {number} [maxLights=1024] - Maximum number of point and spot lights.
|
|
628
989
|
* @param {number} [tileSize=32] - Screen tile size in pixels.
|
|
629
990
|
* @param {number} [zSlices=24] - Depth slice count.
|
|
630
991
|
* @param {number} [maxLightsPerCluster=64] - Per-cluster light-list capacity.
|