Creating A Point Cloud System
Creating an empty PCS requires three parameters: its name, the size of every particle, and the scene. For example:
import { PointsCloudSystem } from "@babylonjs/core/Particles/pointsCloudSystem";var pcs = new PointsCloudSystem("pcs", 5, scene);After creating the PCS, there are a number of options for adding particles.
Add Points
By default, point particles are randomly added within a unit cube.
pcs.addPoints(10000);will add 10000 points.
You can use your own function as a second parameter to set particle properties such as position and color. This function must have a signature like this:
var myFunc = function (particle, i, s) { // particle is the current particle, the i-th one in the PCS and the s-th one in its group};For example, using i:
import { Color4 } from "@babylonjs/core/Maths/math.color";import { Vector3 } from "@babylonjs/core/Maths/math.vector";var myfunc = function (particle, i, s) { particle.position = new Vector3(0.5 + 0.25 * Math.random(), i / 5000, 0.25 * Math.random()); particle.color = new Color4(Math.random(), Math.random(), Math.random(), Math.random());};pcs.addPoints(10000, myfunc);pcs.addPoints(10000, myfunc);will produce a tall block of points whose heights cover the full range of 20,000 points. By contrast, using s:
import { Color4 } from "@babylonjs/core/Maths/math.color";import { Vector3 } from "@babylonjs/core/Maths/math.vector";var myfunc = function (particle, i, s) { particle.position = new Vector3(0.5 + 0.25 * Math.random(), s / 5000, 0.25 * Math.random()); particle.color = new Color4(Math.random(), Math.random(), Math.random(), Math.random());};pcs.addPoints(10000, myfunc);pcs.addPoints(10000, myfunc);will produce a denser block of points whose heights cover only a range of 10,000 points, as you can see in the picture below.

In addition, the use of particle.groupId can affect all particles within a group. For example:
import { Color4 } from "@babylonjs/core/Maths/math.color";import { Vector3 } from "@babylonjs/core/Maths/math.vector";var myfunc = function (particle, i, s) { particle.position = new Vector3(particle.groupId * 0.5 + 0.25 * Math.random(), i / 5000, 0.25 * Math.random()); particle.color = new Color4(Math.random(), Math.random(), Math.random(), Math.random());};pcs.addPoints(10000, myfunc);pcs.addPoints(10000, myfunc);will displace the second group of points along the x-axis.

Add Surface / Volume Points
You can use a mesh such as this one as a model

and convert it to particle points on its surface or within its volume.

The points are evenly and randomly distributed based on the size of the individual triangular facets of the mesh model. The density of points is the same for all facets.
For both surface and volume, the default is for the points to be colored randomly. For example, the following are equivalent for the two surface additions and the two volume additions.
import { PointColor } from "@babylonjs/core/Particles/pointsCloudSystem";pcs.addSurfacePoints(model, 10000);pcs.addSurfacePoints(model, 10000, PointColor.Random);pcs.addVolumePoints(model, 10000);pcs.addVolumePoints(model, 10000, PointColor.Random);Note: Additional calculations in addVolumePoint mean that it takes longer than addSurfacePoints for the same number of points. For a large number of points, this can be noticeable.
There are four available methods for coloring the points.
| Method | Effect |
|---|---|
| BABYLON.PointColor.Random | Colors are assigned randomly to each point; this is the default method. |
| BABYLON.PointColor.Stated | This method requires two extra parameters: the base color to use (white by default) and a range from 0 to 1 to randomize both the shade and tone of the stated color. A value of 0, the default, gives no variation, and 1 gives the largest variation. |
| BABYLON.PointColor.Color | When the model has a texture material applied, the color of each point is determined by the texture color at the matching point on a facet. When the material used has color but no texture, the material color is used. When the model has no material applied, random coloring is used. |
| BABYLON.PointColor.UV | The model uv values for each facet corner are used to determine the uv values for the points. An emissive texture can be applied to the pcs.mesh.material to color the PCS mesh. |
For example:
import { Color3 } from "@babylonjs/core/Maths/math.color";import { PointColor } from "@babylonjs/core/Particles/pointsCloudSystem";pcs.addSurfacePoints(box, 1000, PointColor.Stated, new Color3(1, 0, 0), 0.5);pcs.addVolumePoints(box, 10000, PointColor.Color);pcs.addSurfacePoints(box, 100000, PointColor.UV);Note: Using BABYLON.PointColor.UV can be limiting. Several models can be added to the PCS. These models may all have different textures. However only one emissive texture can be applied to a PCS mesh. In this case a separate PCS mesh is needed for each model. This is not a restriction when using BABYLON.PointColor.Color.
Imported Models and Multiple Textures
In some cases (for example, PBRMaterial), more than one texture can be applied to a model. In that case, adding surface or volume points with BABYLON.PointColor.Color will, by default, use the first texture in the model's texture array. Although that is often the color map, it may instead be a normal map. To specify which texture to use, add its position in the texture array as a second parameter.
import { PointColor } from "@babylonjs/core/Particles/pointsCloudSystem";pcs.addSurfacePoints(model, 10000, PointColor.Color, 1);pcs.addVolumePoints(model, 10000, PointColor.Color, 3);Of course, when you import a model, you may not know how many child meshes it is made up of, nor the order of textures for each mesh. Using the inspector, you can check the loaded textures and see whether their names give you a clue. If not, use trial and error from 0 to the number of textures. Alternatively, you can inspect meshes and textures once loaded along the lines of
import { ImportMeshAsync } from "@babylonjs/core/Loading/sceneLoader";ImportMeshAsync("location" + "file", scene).then(function ({ meshes }) { var n = meshes.length; var p; var t; for (let i = 0; i < n; i++) { if (meshes[i].material !== null) { console.log("Mesh", i); t = meshes[i].material.getActiveTextures(); p = t.length; for (let j = 0; j < p; j++) { console.log("Texture", j, "Name", t[j].name); } } }});Examples
- Random Surface
- Stated Surface
- Surface Color from Mesh Color
- Surface Color from Mesh Texture
- Surface UV from Mesh Texture
- Surface Color from Imported Mesh Texture
- Random Volume
- Stated Volume
- Volume Color from Mesh Color
- Volume Color From Mesh Texture
- Volume UV from Mesh Texture
- Volume Color From Imported Mesh Texture
Building the Mesh
The PCS mesh cannot be built until all relevant data is collected. Because this can involve ensuring that the material applied to a model used for adding surface or volume points is fully loaded, building the mesh is an asynchronous process.
For example, when a mesh model is used to determine the points, the model cannot be disposed of until PCS construction is complete. This can be achieved as follows:
import { PointColor } from "@babylonjs/core/Particles/pointsCloudSystem";pcs.addSurfacePoints(box, 10000, PointColor.Color);pcs.addPoints(10000, myFunc);pcs.buildMeshAsync().then(() => box.dispose());If you never want the particle properties of your PCS to change, that is, you want it to be immutable, then you do not need to do anything more. Alternatively, you can set the PCS as immutable when you create it by setting the updatable option. (Currently, updatable is the only available item in the options list, but the list is open to future expansion.)
import { PointsCloudSystem } from "@babylonjs/core/Particles/pointsCloudSystem";var pcs = new PointsCloudSystem("pcs", 5, scene, { updatable: false });After setting updatable to false, the following methods will no longer have any effect: initParticles(), updateParticle(particle), and setParticles().