iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

Geometries

A geometry is the vertices + indices that describe a shape. Three.js ships dozens (Box, Sphere, Cone, Torus, Plane, Cylinder, Lathe, Extrude, Tube) plus BufferGeometry for custom meshes.

Built-ins, BufferGeometry, custom data

EXAMPLE
import * as THREE from 'three';
import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js';

// 1) Built-in primitives (parametric)
const box      = new THREE.BoxGeometry(1, 1, 1, /* widthSegs */ 2, 2, 2);
const sphere   = new THREE.SphereGeometry(0.5, 32, 16);
const plane    = new THREE.PlaneGeometry(2, 1, 10, 10);
const cylinder = new THREE.CylinderGeometry(0.5, 0.5, 2, 32);
const cone     = new THREE.ConeGeometry(0.5, 1, 32);
const torus    = new THREE.TorusGeometry(0.6, 0.2, 16, 60);
const ring     = new THREE.RingGeometry(0.3, 0.5, 32);
const tetra    = new THREE.TetrahedronGeometry(0.5);
const icosa    = new THREE.IcosahedronGeometry(0.5, 1);
const dodeca   = new THREE.DodecahedronGeometry(0.5);

// 2) Reuse + share
const mat = new THREE.MeshStandardMaterial({ color: 0x4cc9ff });
for (let i = 0; i < 100; i++) {
    const mesh = new THREE.Mesh(box, mat);    // share geometry + material!
    mesh.position.set(randomXYZ());
    scene.add(mesh);
}
// Same geometry buffers; one draw call per mesh.

// 3) InstancedMesh — thousands of identical shapes in ONE draw call
const trees = new THREE.InstancedMesh(
    new THREE.ConeGeometry(0.2, 0.6, 8),
    new THREE.MeshStandardMaterial({ color: 0x228b22 }),
    5000,
);
const dummy = new THREE.Object3D();
for (let i = 0; i < 5000; i++) {
    dummy.position.set(Math.random() * 100 - 50, 0, Math.random() * 100 - 50);
    dummy.updateMatrix();
    trees.setMatrixAt(i, dummy.matrix);
}
scene.add(trees);

// 4) ExtrudeGeometry — extrude a 2D shape
const shape = new THREE.Shape();
shape.moveTo(0, 0);
shape.lineTo(1, 0);
shape.lineTo(1, 1);
shape.lineTo(0.5, 1.5);
shape.lineTo(0, 1);
shape.lineTo(0, 0);

const extruded = new THREE.ExtrudeGeometry(shape, {
    depth:        0.5,
    bevelEnabled: true,
    bevelSize:    0.05,
    bevelThickness: 0.05,
    curveSegments: 12,
});

// 5) LatheGeometry — rotate a 2D profile around an axis (bottles, vases)
const points = [];
for (let i = 0; i < 10; i++) {
    points.push(new THREE.Vector2(Math.sin(i * 0.2) * 0.5 + 0.3, i * 0.2));
}
const lathe = new THREE.LatheGeometry(points, 32);

// 6) TubeGeometry — sweep a circle along a curve
const curve = new THREE.CatmullRomCurve3([
    new THREE.Vector3(-1, 0, 0),
    new THREE.Vector3( 0, 1, 0),
    new THREE.Vector3( 1, 0, 0),
]);
const tube = new THREE.TubeGeometry(curve, 64, 0.1, 8, false);

// 7) BufferGeometry — build from raw arrays
const geo = new THREE.BufferGeometry();
const positions = new Float32Array([
    -1, -1, 0,   1, -1, 0,   0, 1, 0,             // triangle
]);
const colors = new Float32Array([
    1, 0, 0,   0, 1, 0,   0, 0, 1,                 // per-vertex colors
]);
const uvs = new Float32Array([
    0, 0,   1, 0,   0.5, 1,                        // UV coords for texturing
]);
const indices = [0, 1, 2];

geo.setAttribute('position', new THREE.BufferAttribute(positions, 3));
geo.setAttribute('color',    new THREE.BufferAttribute(colors, 3));
geo.setAttribute('uv',       new THREE.BufferAttribute(uvs, 2));
geo.setIndex(indices);
geo.computeVertexNormals();    // for lighting

const triMat = new THREE.MeshStandardMaterial({ vertexColors: true });
scene.add(new THREE.Mesh(geo, triMat));

// 8) Animate vertices — wave + ripple
const plane2 = new THREE.PlaneGeometry(10, 10, 64, 64);
const mesh = new THREE.Mesh(plane2, new THREE.MeshStandardMaterial({ wireframe: true }));
mesh.rotation.x = -Math.PI / 2;
scene.add(mesh);

function animate() {
    const pos = plane2.attributes.position;
    const t   = performance.now() / 1000;
    for (let i = 0; i < pos.count; i++) {
        const x = pos.getX(i);
        const y = pos.getY(i);
        const z = Math.sin(x * 2 + t) * 0.2 + Math.cos(y * 2 + t) * 0.2;
        pos.setZ(i, z);
    }
    pos.needsUpdate = true;
    plane2.computeVertexNormals();
    requestAnimationFrame(animate);
}
animate();

// 9) Merge multiple geometries — fewer draw calls
const merged = mergeGeometries([box, sphere, cylinder]);
scene.add(new THREE.Mesh(merged, mat));

// 10) Compute helpers
geo.computeBoundingBox();           // axis-aligned bounding box
geo.computeBoundingSphere();
geo.computeTangents();              // for normal-mapping shaders
geo.computeVertexNormals();         // automatic smooth shading

// 11) Transform — bake transforms into vertices (avoid runtime cost)
geo.translate(0, 1, 0);
geo.rotateY(Math.PI / 4);
geo.scale(2, 2, 2);

// 12) Dispose — geometries hold GPU buffers
geo.dispose();
// IMPORTANT: call when removing meshes; otherwise leaks GPU memory.

// 13) Load a model — glTF (the modern standard)
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
const gltf = await new GLTFLoader().loadAsync('/car.glb');
scene.add(gltf.scene);

// Other loaders: FBXLoader, OBJLoader, ColladaLoader, PLYLoader, STLLoader

// 14) Common patterns
//   Particle field            : BufferGeometry with thousands of points + PointsMaterial
//   Procedural terrain        : PlaneGeometry + setZ() per vertex from height map
//   Curves                    : CatmullRomCurve3 + TubeGeometry for rails / pipes
//   Text                      : TextGeometry from three/examples (with a font)
//   Modeller-style            : Built-in primitives + merge + Boolean ops (CSG library)

// 15) Performance tips
//   • Reuse geometries across meshes; share materials too
//   • Use InstancedMesh for hundreds-of-identical-objects
//   • Merge static geometries into one (mergeGeometries)
//   • Reduce segments — does the sphere really need 64×32?
//   • Use indexed geometries to avoid duplicating vertices
//   • Dispose geometries when removing meshes from the scene
//   • Use BufferGeometry directly; old THREE.Geometry is gone (since r125)

// 16) Common bugs
//   • Forgetting computeVertexNormals → faceted (flat) shading
//   • Mutating position attribute without setting needsUpdate = true → no visual change
//   • Not disposing → memory leak (visible in renderer.info.memory)
//   • Wrong UVs → texture appears scrambled
//   • Per-frame creation of geometries → GC churn

Why it matters

Reuse geometries + use InstancedMesh for thousands-of-the-same shape. Most performance wins come from fewer distinct geometries + materials, not from making them smaller. Always dispose() when removing meshes.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
// BoxGeometry, SphereGeometry, PlaneGeometry,
// CylinderGeometry, ConeGeometry, TorusGeometry, …
Try it Yourself »

Discussion

Loading…