Geometry and buffers
Quick answer
This tutorial treats BufferGeometry as the real building block behind built-in shapes. It generates a 64 by 64 grid terrain with position, uv and index attributes, calls computeVertexNormals, and writes elevation-derived vertex colors for a MeshStandardMaterial with vertexColors enabled. It also updates an existing position attribute using DynamicDrawUsage, needsUpdate and a rebuilt bounding sphere. Readers can construct and edit custom terrain meshes efficiently.
Key points
- Build a grid with position, uv and index attributes, then call computeVertexNormals.
- Use Uint32BufferAttribute once vertex counts exceed the 65535 limit.
- Set DynamicDrawUsage and needsUpdate when rewriting position data in place.
Example
const cols = 64;
const rows = 64;
const step = 20; // 每个格 20 米
const positions = [];
const uvs = [];
const indices = [];
function heightAt(x, z) {
return 18 * Math.sin(x * 0.015) * Math.cos(z * 0.02) + 40;
}
for (let r = 0; r <= rows; r++) {
for (let c = 0; c <= cols; c++) {
const x = c * step;
const z = r * step;
positions.push(x, heightAt(x, z), z);
uvs.push(c / cols, r / rows);
}
}
for (let r = 0; r < rows; r++) {
for (let c = 0; c < cols; c++) {
const a = r * (cols + 1) + c;
const b = a + 1;
const d = a + cols + 1;
const e = d + 1;
indices.push(a, d, b, b, d, e);
}
}
const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
geometry.setAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2));
geometry.setIndex(indices);
geometry.computeVertexNormals();const pos = geometry.attributes.position;
const colors = new Float32Array(pos.count * 3);
const low = new THREE.Color('#1f5fd0');
const mid = new THREE.Color('#f0a020');
const high = new THREE.Color('#d0453a');
for (let i = 0; i < pos.count; i++) {
const h = pos.getY(i);
const t = THREE.MathUtils.clamp((h - 20) / 50, 0, 1);
const color = t < 0.5
? low.clone().lerp(mid, t * 2)
: mid.clone().lerp(high, (t - 0.5) * 2);
colors[i * 3] = color.r;
colors[i * 3 + 1] = color.g;
colors[i * 3 + 2] = color.b;
}
geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
const terrain = new THREE.Mesh(geometry, new THREE.MeshStandardMaterial({
vertexColors: true,
roughness: 0.95,
metalness: 0.0
}));
Notes
- Index winding decides the front face, so reversed triangles become invisible from above.
- The second BufferAttribute argument is item size, and a wrong value corrupts memory silently.
English edition written for Atlaslog. Code is taken from the original Chinese page and runs unchanged.
Atlaslog (2026). Geometry and buffers. https://www.jingweizhi.com/en/three/tutorials/geometry.html (accessed 2026-11-03).