Three.js Geometry

Geometry defines the shape of the objects we draw in Three.js. Geometry is made up of a collection of vertices and often faces which combine three vertices into a triangle face. You can create your own custom geometry by defining these vertices and faces yourself, but Three.js also has a variety of common shapes for you to access and set properties of built in.

Base Geometry

There are two base types of geometry, the first is Geometry. You can add vertices to this by pushing vertices into it’s vertices array. We can do the same thing with faces by referencing the vertices used in each face and pushing them into the geometry’s faces array. In the example below we are just creating a single face made up of three vertices, so a triangle.

var geometry = new THREE.Geometry();
geometry.vertices.push(
	new THREE.Vector3(-10, 10, 0),
	new THREE.Vector3(-10, -10, 0),
	new THREE.Vector3(10, -10, 0)
);
geometry.faces.push( new THREE.Face3(0, 1, 2));


The other base geometry type is BufferGeometry which is more optimized for performance but the vertices aren’t as directly accessible. In order to add vertices to BufferGeometry we need to create a Float32Array of the vertices and then add it to the position attribute of the geometry utilizing BufferAttribute.

var geometry = new THREE.BufferGeometry();
var vertices = new Float32Array((
	new THREE.Vector3(-10, 10, 0),
	new THREE.Vector3(-10, -10, 0),
	new THREE.Vector3(10, -10, 0)
]);
geometry.addAttribute('position', new THREE.BufferAttribute(vertices, 3));


Primitives

As mentioned Three.js has a bunch of built in geometric primitives and there are versions that extend both Geometry and BufferGeometry depending on your needs. The parameters for the regular and buffer versions are identical.

var geometry = new THREE.PlaneGeometry(width, height, widthSegments, heightSegments);

PlaneGeometry takes the dimensions, width and height and the number of segments along each access the faces are broken into.

var geometry = new THREE.BoxGeometry(width, height, depth, widthSegments, heightSegments, depthSegments);

BoxGeometry works the same as a plane, only including depth for both the dimensions and segments that can be divided.

var geometry = new THREE.CircleGeometry(radius, segments, thetaStart, thetaLength);

CircleGeometry allows you to define the radius of the circle and the number of segments it is divided into. As well as the start angle and the angle defining the drawn segments.

var geometry = new THREE.SphereGeometry(radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength);

Sphere works the same as circle only adding two dimensions for segments as well as the ability to set the start angle and angle drawn along both dimensions.

var geometry = new THREE.ConeGeometry(radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength);

ConeGeometry takes the radius, height, then segments for both as well as a boolean definining whether or not it is open ended or closed. Then the start and end angle for only drawing segments of the cone.

var geometry = new THREE.Cylinder(radiusTop, radiusBottom, height, radiusSegments, heightSegments, openEnded, thetaStart, thetaLength);

Cylinders allow you to specify the radius of the top and bottom independently as well as the height. Then the segments along the radius and height, a boolean defining whether it’s open ended or not and the start and end degrees for drawing only segments of the circumference.

var geometry = new THREE.RingGeometry(innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength);

RingGeometry specifies the inner and outer radius. The segments broken into around the circumference as well as out from width of the ring. You can also specify the start and end angle for drawing partial sections of the ring.

var geometry = new THREE.TorusGeometry(radius, tube, radialSegments, tubularSegments, arc);

TorusGeometry is a tubular ring. It allows you to specify the radius of the ring, as well as the diameter of the tube. You can then specify the segments along the radius and tube. You can also specify the arc angle of the circumference to draw.

var geometry = new THREE.TorusKnotGeometry(radius, tube, tubularSegments, radialSegments, p, q);

TorusKnotGeometry draws a knot shape defined by p and q. It takes in a radius for the whole as well as the diameter of the tube. Then the segments along the tube and segments along the radius. p determines, how many times the geometry winds around its axis of rotational symmetry. determines, how many times the geometry winds around a circle in the interior of the torus.

 

Polyhedrons

The Polyhedron class is the base class for shapes like Dodecahedron or Tetrahedron. It takes an array of vertices and projects them onto a sphere, dividing them up to the desired level of detail.

  var verticesOfCube = [
    -1,-1,-1,    1,-1,-1,    1, 1,-1,    -1, 1,-1,
    -1,-1, 1,    1,-1, 1,    1, 1, 1,    -1, 1, 1,
];
var indicesOfFaces = [
    2,1,0,    0,3,2,
    0,4,7,    7,3,0,
    0,1,5,    5,4,0,
    1,2,6,    6,5,1,
    2,3,7,    7,6,2,
    4,5,6,    6,7,4
];
var geometry = new THREE.PolyhedronGeometry(vertices, faces, 6, 2);

These different Polyhedrons also have their own geometry which can be seen in the snippets below. The first attribute is the radius and the second is the level of detail.

var geometry = new THREE.DodecahedronGeometry(100, 0);

var geometry = new THREE.IcosahedronGeometry(100, 0);

var geometry = new THREE.OctahedronGeometry(100, 0);

var geometry = new THREE.TetrahedronGeometry(100, 0);

Text

Three.js also supports TextGeometry, but in order to do this we need to load in a JSON file that describes the geometry of the font we want to use. Fortunately Three.js comes packaged with some fonts on github. After including the JSON as a fontJSON variable we can then parse it with a FontLoader. Then we can proceed to use TextGeometry passing in the words we want to use as well as an object defining the font geometry, size, and height of our 3D text.

var loader = new THREE.FontLaoder();
var loadedFont = loader.parse(fontJSON);
var geometry = new THREE.TextGeometry("my custom text here", {font: loadedFont, size: 120, height: 10});

Custom Shapes

Three.js allows you to create custom shapes using a 2D drawing API with the Shape class. Below is an example of how we can define a heart shape using moveTo and bezierCurveTo. There are also other simple drawing commands we can call on our shape, like lineTo.

var x = 0;
var y = 0;
var heartShape = new THREE.Shape();
heartShape.moveTo( x + 25, y + 25 );
heartShape.bezierCurveTo( x + 25, y + 25, x + 20, y, x, y );
heartShape.bezierCurveTo( x - 30, y, x - 30, y + 35,x - 30,y + 35 );
heartShape.bezierCurveTo( x - 30, y + 55, x - 10, y + 77, x + 25, y + 95 );
heartShape.bezierCurveTo( x + 60, y + 77, x + 80, y + 55, x + 80, y + 35 );
heartShape.bezierCurveTo( x + 80, y + 35, x + 80, y, x + 50, y );
heartShape.bezierCurveTo( x + 35, y, x + 25, y + 25, x + 25, y + 25 );

We can then use this 2D shape and pass it into ShapeGeometry to draw this shape as a flat mesh.

var geometry = new THREE.ShapeGeometry(heartShape);

We can also extrude the shape into 3D by passing it into ExtrudeGeometry along with some parameters specifying how we want it to extrude.

var geometry = new THREE.ExtrudeGeometry(heartShape, {
	amount: 8, 
	bevelEnabled: true, 
	bevelSegments: 2, 
	steps: 2, 
	bevelSize: 1, 
	bevelThickness: 1 
});

Lathe

LatheGeometry is useful for shapes that have axial symmetry, so vase like forms with a symmetrical radius along the y-axis.

var points = [];
for ( var i = 0; i < 10; i ++ ) {
	points.push( new THREE.Vector2( 5 + Math.sin( i * 0.2 ) * 20, i * 10) );
}
var geometry = new THREE.LatheGeometry( points );

Parametric

ParemetricGeometery is useful for rendering a flat surface that distortes based on a chart or graph function. It takes the value of a method which returns vectors and uses the UV values as parameters.

var points = [];
var paramFunc = function(u, v){
    var x = -100 + 200 * u;
    var y = 0;
    var z = (Math.sin(u* Math.PI) + Math.sin(v* Math.PI)) * (-60);
    return new THREE.Vector3(x,y,z);
}; 

var geometry = new THREE.ParametricGeometry(paramFunc, 10, 10);

Tube

We can also create TubeGeometry by passing in a path as the first parameter. Here we are creating a path with SplineCurve3 which will create a curvy path through the vector’s I pass into it. Then we can set properties on the TubeGeometry like the number of segments for the tube, the radius of the tube, the number of segments for the tube’s radius and whether it’s open or closed.

var curve = new THREE.SplineCurve3([
    new THREE.Vector3( -10, 0, 10 ),
    new THREE.Vector3( -5, 5, 5 ),
    new THREE.Vector3( 0, 0, 0 ),
    new THREE.Vector3( 5, -5, 5 ),
	new THREE.Vector3( 10, 0, 10 )
]);
var geometry = new THREE.TubeGeometry(curve, 20, 2, 8, false);

Updating Vertices

We can access vertices within our Geoemetry through the vertices array. After updating the value of one vertex we need to then set verticesNeedUpdate to true so that the renderer knows to use the updated geometry.

geometry.vertices[0].x = -100;
geometry.verticesNeedUpdate = true;

Click here to download the demo.

Note: these examples were created using Three.js v79. Three.js is known to update frequently and sometimes cause breaking changes to the API so it may be worth checking the version you are using.