Issue:
I am facing significant performance problems while rendering a scene using Three JS. The main issue arises from rendering a large number of simple geometries (11,107).
(edit)
Each building has a unique height based on elevation data, a distinct shape based on their outline, and a material selected from 5 options depending on the area they cover.
I have identified the problem in the initial scene provided below, while the second link offers context. Disabling the polygon buildings in the second link demonstrates the decrease in framerate caused by that layer.
- View 3JS Render (Polygons Only)
- View Entire Scene
Determining Polygon Attributes:
Each polygon has a uniform height but varies in shape based on the building footprint. Moreover, every building is assigned a color gradient relative to its area size (large=yellow, medium=red, small=purple). This was done in a geographic information system before being passed to ThreeJS (QGIS, with QGIStoTHREEJS plugin).
Possible Solutions Attempted:
My attempts have been focused on merging polygon geometry to reduce render calls. However, due to different colors for each polygon, applying appropriate materials and mesh has proved challenging. I'm struggling with the logic of managing this within the existing loops.
Relevant Code Snippet:
The complete source code can be found here, and a downloadable version of the code is available here. This snippet includes lines 1935 to 1987.
I've extracted only the relevant part of the ThreeJS source code related to my issue.
///////////////// WIP /////////////////
Q3D.PolygonLayer.prototype.build = function(parent) {
var materials = this.materials,
project = this.project;
if (this.objType == "Extruded") {
// (3) Function for creating the individual building polygons
var createSubObject = function(f, polygon, z) {
var shape = new THREE.Shape(Q3D.Utils.arrayToVec2Array(polygon[0]));
for (var i = 1, l = polygon.length; i < l; i++) {
shape.holes.push(new THREE.Path(Q3D.Utils.arrayToVec2Array(polygon[i])));
}
// Where the problems start...
// Here each geometry is created turned into a mesh with its unqiue material
var geom = new THREE.ExtrudeGeometry(shape, {
bevelEnabled: false,
amount: f.h
});
var mesh = new THREE.Mesh(geom, materials[f.m].m);
mesh.position.z = z;
return mesh;
//I am not sure how I can merge each polygons geometry with the others whilst allowing each polygon to hold onto its unique colouring...
};
// (2) Function for creating polygon layer
var createObject = function(f) {
if (f.polygons.length == 1) { // TRUE for building polygons
return createSubObject(f, f.polygons[0], f.zs[0]); // calls function to create each building
}
};
}
// (1) function for manufacturing each layer
this.f.forEach(function(f, fid) {
f.objs = []; // create array of objects
var obj = createObject(f); // call polygon creation method
obj.userData.layerId = this.index;
obj.userData.featureId = fid;
this.addObject(obj);
f.objs.push(obj);
}, this);
if (parent) parent.add(this.objectGroup);
};
///////////////// END OF WIP /////////////////
Edit: The structure of each geometry is as follows(f).
GEOMETRY
f[A] = {h, m, polygons, zs};
Given that f represents one of the 11,000 geometries, A is an index (0 to 1106), h is a float, m is an integer (0 to 5) serving as an index for selecting one of the five color categories, polygons consists of coordinate values for building footprints, and zs indicates extrusion height.e.g.
f[11106] = {h:0.0302738130622,m:1,polygons:[[[[-23.0863540568,-1.57556646762],[-23.1968547585,-1.56551240558],[-23.1928481251,-1.49924919288],[-23.0803253841,-1.50930323633],[-23.0863540568,-1.57556646762]]]],zs:[0.0695352124961]};MATERIAL
There are five color categories. the index serves as a reference for a specific geometry to retrieve its associated material.e.g.
f[11106].m points to
m[1] = {c:0xcb4778,type:0};
We seek advice on optimizing the rendering process for these buildings without overwhelming draw calls. Any guidance would be greatly appreciated.