Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ThreeJS: Better way to create and render a forest of individual trees?

I am trying to render a large forest of 100,000+ very simple-looking trees in ThreeJS. Creating many individual meshes is of course out of the question. My current method uses GeometryUtils.merge to create one large geometry which reduces the number of draw calls and this works pretty well. But approaching 100k, it bogs down. I need to improve performance further and I have a feeling there may be another trick or two to increase performance by another factor of 10 or more.

The code is below and I've also created a JSFiddle demonstrating my current technique: http://jsfiddle.net/RLtNL/

//tree geometry (two intersecting y-perpendicular triangles)
var triangle = new THREE.Shape();
triangle.moveTo(5, 0);
triangle.lineTo(0, 12);
triangle.lineTo(-5, 0);
triangle.lineTo(5, 0);
var tree_geometry1 = new THREE.ShapeGeometry(triangle);

var matrix = new THREE.Matrix4();
var tree_geometry2 = new THREE.ShapeGeometry(triangle);
tree_geometry2.applyMatrix(matrix.makeRotationY(Math.PI / 2));


//tree material
var basic_material = new THREE.MeshBasicMaterial({color: 0x14190f});
basic_material.color = new THREE.Color(0x14190f);
basic_material.side = THREE.DoubleSide;


//merge into giant geometry for max efficiency
var forest_geometry = new THREE.Geometry();
var dummy = new THREE.Mesh();
for (var i = 0; i < 1000; i++) 
{
    dummy.position.x = Math.random() * 1000 - 500;
    dummy.position.z = Math.random() * 1000 - 500;
    dummy.position.y = 0;

    dummy.geometry = tree_geometry1;
    THREE.GeometryUtils.merge(forest_geometry, dummy);

    dummy.geometry = tree_geometry2;
    THREE.GeometryUtils.merge(forest_geometry, dummy);
}


//create mesh and add to scene
var forest_mesh = new THREE.Mesh(forest_geometry, basic_material);
scene.add(forest_mesh);

Can anyone suggest further techniques to make this load and render more quickly?

like image 986
learnworkplay Avatar asked Oct 17 '13 18:10

learnworkplay


1 Answers

How about using billboards to render the trees? The 2D nature of billboards seem to suit this particular problem. Create a simple png tree texture with transparency, and add each tree as a PointCloud object - http://threejs.org/docs/#Reference/Objects/PointCloud

Most low-end graphics cards can render well over 10,000 billboard objects without a drop in framerate. I've updated your code using the billboards technique (changing the number of trees to 10,000 and using a 100 pixel high tree texture): http://jsfiddle.net/wayfarer_boy/RLtNL/8/ - extract of the code below:

geometry = new THREE.Geometry();
sprite = new THREE.Texture(image);
sprite.needsUpdate = true;
for (var i = 0; i < 10000; i++) {
    var vertex = new THREE.Vector3();
    vertex.x = Math.random() * 1000 - 500;
    vertex.y = 0;
    vertex.z = Math.random() * 1000 - 500;
    geometry.vertices.push(vertex);
}
material = new THREE.PointCloudMaterial({
    size: 50,
    sizeAttenuation: true,
    map: sprite,
    transparent: true,
    alphaTest: 0.5
});
particles = new THREE.PointCloud(geometry, material);
// particles.sortParticles = true;
// Removed line above and using material.alphaTest instead
// Thanks @WestLangley
scene.add(particles);
renderer = new THREE.WebGLRenderer({
    clearAlpha: 1,
    alpha: true
});
like image 114
wayfarer_boy Avatar answered Oct 18 '22 22:10

wayfarer_boy