Tiling the world with babylon's Solid Particle System.
Babylon.js’ equivalent of three.js’ InstancedBufferGeometry is, in a way, the Solid Particle System. So the advice of the Babylon.js forum (a great community so far by the way!), I’ve tried playing with that.
The SolidParticleSystem uses a single vertex buffer object, which might be slow compared to a double buffer approach. At least the Khronos Wiki (see “double buffered VBO”) still seems to say this gives a performance advantage. But maybe not, with modern GPUs.
It doesn’t much matter, since ultimately I will need to implement a WebGL 2.0 version of this using Transform Feedback and the flat shader directive that will be more performant than this system can offer.
The following is babylon playground code demonstrating a flexible number of SolidParticleSystems that are each updated constantly, all the particles in one SPS are updated each frame.
If you copy/paste the following code into the Babylon Playground you can view the real-time rendering of this scene and play with the code:
var createScene = function () {
// This creates a basic Babylon Scene object (non-mesh)
var scene = new BABYLON.Scene(engine);
// This creates and positions a free camera (non-mesh)
var camera = new BABYLON.ArcRotateCamera(
"camera1", 0, 0, 10, BABYLON.Vector3.Zero(), scene);
camera.setPosition(new BABYLON.Vector3(0, 20, -10));
camera.attachControl(canvas);
var light = new BABYLON.PointLight("l", camera.position, scene);
var model = BABYLON.MeshBuilder.CreateCylinder("m", {tessellation: 6}, scene);
// Note: total particles is (nb * numSPS)
var nb = 100;
var numSPS = 25;
var spsL = [numSPS];
for(var j = 0; j < numSPS; j++){
var sps = new BABYLON.SolidParticleSystem('sps_'+j, scene);
sps.addShape(model, nb);
var mesh = sps.buildMesh();
mesh.useVertexColors = true;
sps.initParticles = function() {
var tileWidth = 10;
var patchWidth = 5;
var x,y,z;
for (var k = 0; k < nb; k++){
var par = sps.particles[k];
// x
x = 0;
// position particle in tile
x += k % tileWidth;
// position tile in patch
x += (j % patchWidth)*tileWidth*1.1;
//x += j*tileWidth*1.1;
// center patch on origin
x -= (tileWidth*patchWidth)/2;
// z
z = 0;
// position particle in tile
z += Math.floor(k/tileWidth)
// position tile in patch
z += Math.floor(j/patchWidth)*tileWidth*1.1;
// center patch on origin
z -= (tileWidth*patchWidth)/2;
// hex tile offset
z += (k % 2) * 0.5;
y = -1;
par.position.x = x;
par.position.z = z;
par.position.y = y;
}
}
sps.isAlwaysVisible = true;
sps.computeParticleColor = true;
sps.initParticles();
sps.setParticles();
sps.mesh.freezeNormals();
sps.mesh.freezeWorldMatrix();
spsL[j] = sps;
}
model.dispose();
frameNum = 0;
scene.registerBeforeRender(function() {
// iterate through list of SPS objects
iSPS = frameNum % numSPS;
// update the same SPS object each frame.
//iSPS = 0;
for(var i = 0; i < nb; i++){
var par = spsL[iSPS].particles[i];
par.position.y = 0.5 + Math.random() * 0.5;
}
spsL[iSPS].setParticles(0, nb, true);
frameNum++;
})
return scene;
};