Primitives and batched rendering
Quick answer
This tutorial explains how the Primitive API batches geometry into a single GPU draw call using GeometryInstance and modelMatrix. It builds thousands of boxes from one BoxGeometry, colours them through ColorGeometryInstanceAttribute and PerInstanceColorAppearance, and compares that with PointPrimitiveCollection and BillboardCollection. It also shows updating instance attributes in place versus rebuilding, plus readiness, allowPicking and destroy semantics. Readers can scale from a few thousand to a hundred thousand features.
Key points
- Reuse one geometry object across all GeometryInstance entries, or batching silently fails.
- Position instances with Matrix4.multiplyByTranslation relative to an eastNorthUpToFixedFrame.
- Pass the scene to BillboardCollection to keep distance scaling and ground clamping working.
Example
var origin = Cesium.Cartesian3.fromDegrees(116.391, 39.907, 0);
var enu = Cesium.Transforms.eastNorthUpToFixedFrame(origin);
var box = Cesium.BoxGeometry.fromDimensions({
dimensions: new Cesium.Cartesian3(18, 18, 30),
vertexFormat: Cesium.PerInstanceColorAppearance.VERTEX_FORMAT
});
var instances = [];
for (var i = 0; i < 40; i++) {
for (var j = 0; j < 40; j++) {
var offset = new Cesium.Cartesian3(i * 24 - 480, j * 24 - 480, 15);
instances.push(new Cesium.GeometryInstance({
geometry: box,
modelMatrix: Cesium.Matrix4.multiplyByTranslation(enu, offset, new Cesium.Matrix4()),
attributes: {
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
Cesium.Color.fromHsl((i + j) / 80, 0.55, 0.55, 0.9)
)
},
id: { row: i, col: j }
}));
}
}
var primitive = new Cesium.Primitive({
geometryInstances: instances,
appearance: new Cesium.PerInstanceColorAppearance({
flat: true,
translucent: false
}),
asynchronous: false,
allowPicking: true
});
viewer.scene.primitives.add(primitive);var points = new Cesium.PointPrimitiveCollection();
for (var i = 0; i < 5000; i++) {
points.add({
position: Cesium.Cartesian3.fromDegrees(
116.20 + Math.random() * 0.40,
39.80 + Math.random() * 0.24,
Math.random() * 120
),
color: Cesium.Color.fromCssColorString('#4d9bff').withAlpha(0.85),
pixelSize: 4 + Math.random() * 4
});
}
viewer.scene.primitives.add(points);
var billboards = new Cesium.BillboardCollection({ scene: viewer.scene });
billboards.add({
position: Cesium.Cartesian3.fromDegrees(116.391, 39.907, 60),
image: '/images/facility.png',
scale: 0.6,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
scaleByDistance: new Cesium.NearFarScalar(800, 1.0, 60000, 0.35),
id: { code: 'BJ-001' }
});
viewer.scene.primitives.add(billboards);
Notes
- Removing a primitive from scene.primitives without destroy leaks GPU memory.
- Mixed vertexFormat or appearance splits the batch and negates the performance gain.
English edition written for Atlaslog. Code is taken from the original Chinese page and runs unchanged.
Atlaslog (2026). Primitives and batched rendering. https://www.jingweizhi.com/en/cesium/tutorials/primitives.html (accessed 2026-11-03).