Command Palette

Search for a command to run...

Hectal
Course outline
Topic 1.2Beginner ~21 min · step 2 of 6

Everything Is Triangles

In one line

Every 3D shape is a mesh of triangles joined at corner points called vertices. Triangles are used because three points are always flat, and the number of triangles is the first thing that decides how much a scene costs.

0/6 · 0%

Think of it like this

A paper-craft model, such as a folded paper animal, is a lot of small flat paper pieces joined at the edges. The more pieces, the rounder and more detailed it looks, and the longer it takes to build. A 3D model on the GPU is the same, and its flat pieces are all triangles.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

vertex
A corner point of a shape, with a position (x, y, z) and sometimes a colour or a direction. The plural is vertices.
edge
A line between two vertices.
face
A flat surface bounded by edges. On the GPU every face is a triangle.
mesh
A shape made of many triangles joined at shared or matching vertices.
segments
The number of straight pieces used to make a curve. More segments look rounder and cost more triangles.
index
A list of numbers that says which three vertices form each triangle, so a vertex can be reused by many triangles.

Step by step

01One triangle

Three vertices, nine numbers: for example (−0.7, −0.5, 0), (0.7, −0.5, 0) and (0, 0.7, 0). Put them in a Float32Array (a list of 32-bit decimal numbers, the format the GPU likes) and wrap it in a BufferAttribute with 3 numbers per vertex. That is a complete shape.

02A square from two triangles

A square has four corners. The two triangles share a diagonal, so with an index you store 4 vertices and say "triangle 1 is vertices 0, 1, 2; triangle 2 is vertices 2, 1, 3". Without an index you would store 6 vertices and repeat two of them.

03A cube from twelve

Six squares, two triangles each. three.js's BoxGeometry makes 24 vertices (4 per face) and an index of 36 numbers (12 triangles × 3). geometry.index.count / 3 gives the triangle count.

04A sphere from segments

SphereGeometry(radius, widthSegments, heightSegments) builds rings from the top to the bottom. The rings next to the poles are made of triangles that meet at a point, the middle rings of squares cut in two. The result is w × (2h − 2) triangles.

05Ask the renderer

renderer.info.render.triangles is the number of triangles the last render() call sent to the GPU. It must match your own count. When it doesn't, something you didn't expect is in the scene (or something expected was skipped; Topic 1.4 shows how).

Try it yourself

  1. 1

    Predict, then check

    In "Count the triangles", add "Sphere 16 x 8": new THREE.SphereGeometry(0.5, 16, 8), to shapes. Predict 16 × (2 × 8 − 2) = 224 triangles, then run: the new last line reads 153 vertices, 224 triangles and renderer.info: 224. The vertices are (16 + 1) × (8 + 1) = 153, because each ring repeats its first vertex to close the seam.

  2. 2

    Make it round

    Change the last scene to the 32 × 16 sphere (shapes["Sphere 32 x 16 (default)"]) and watch the wireframe. The 8 × 6 sphere (80 triangles) has a visibly faceted outline made of straight edges. At 960 triangles the outline looks round and the lines are so dense that the ball looks almost solid orange.

Code & diagrams

Count the triangles in seven shapes New tab

Each count comes from the geometry's index and again from the renderer. The wireframe sphere (8 × 6) shows its 80 triangles.

idle

Console

Run the scene to see its console.

Expected console output

Plane 1 x 1                   4 vertices      2 triangles | renderer.info: 2
Box 1 x 1 x 1                24 vertices     12 triangles | renderer.info: 12
Cylinder, 8 sides            52 vertices     32 triangles | renderer.info: 32
Cylinder, 64 sides          388 vertices    256 triangles | renderer.info: 256
Sphere 8 x 6                 63 vertices     80 triangles | renderer.info: 80
Sphere 32 x 16 (default)    561 vertices    960 triangles | renderer.info: 960
Sphere 64 x 32             2145 vertices   3968 triangles | renderer.info: 3968
One triangle, one million triangles New tab

The last line builds a sphere of about a million triangles. On a very old phone this example may take a moment to start. It is one draw call either way: a draw call is not a triangle.

idle

Console

Run the scene to see its console.

Expected console output

1 triangle                      draw calls 1, triangles 1, geometry data 36 bytes
12 triangles (a box)            draw calls 1, triangles 12, geometry data 840 bytes
960 triangles (default sphere)  draw calls 1, triangles 960, geometry data 23,712 bytes
about 1,000,000 triangles       draw calls 1, triangles 1001112, geometry data 28,099,136 bytes

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Count a shape that has no index

In "Count the triangles", add "Bare": new THREE.BufferGeometry().setAttribute("position", new THREE.BufferAttribute(new Float32Array(9), 3)), to shapes.

terminal
$ Run
── what you'll see ──
Uncaught TypeError: Cannot read properties of null (reading 'count')
The lines for the earlier shapes print first, then the error.

Break #2

Ask for a sphere with too few segments

In "Count the triangles", add "Tiny": new THREE.SphereGeometry(0.5, 1, 1), to shapes.

terminal
$ Run
── what you'll see ──
Tiny 12 vertices 6 triangles | renderer.info: 6

Myth vs fact

Myth

More triangles always means a better picture.

Fact

Only up to the point where you can no longer see the corners at the size the shape is shown. Beyond that you only pay more.

Myth

A cube has 8 vertices on the GPU.

Fact

In three.js it has 24, because each face needs its own copy of every corner (they point different ways for lighting). The shape has 8 corners; the GPU data has 24 vertices.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    Vertex count matters as much as triangle count. The vertex program (Topic 1.3) runs once per vertex, and the data for every vertex is stored. An indexed mesh shares vertices between triangles, so it has fewer than 3 × triangles of them; renderer.info reports triangles, so also look at geometry.attributes.position.count.

  • ▸

    The index is a Uint16Array when the mesh has under 65,536 vertices and a Uint32Array above that. The million-triangle sphere here needs 32-bit indices, which is why its data is about 28 MB (positions, normals and UVs at 4 bytes per number, plus the index).

  • ▸

    Geometry data lives twice: in JavaScript arrays and, after the first render(), in GPU buffers. The 28,099,136 bytes above is the JavaScript side; renderer.info.memory.geometries counts GPU copies. Freeing them is geometry.dispose() (Topic 17.4).

Remember this

  1. 1

    The problem this solves: when you ask for "a sphere" you get a shape made of flat pieces, and with too few you can see the corners. Knowing that every shape is a list of triangles tells you why detail has a price, why a model can be slow, and what the number in the status bar means.

  2. 2

    Vertices, edges, faces. A vertex is one corner point with a position (x, y, z) and often more information such as a colour or a direction. An edge joins two vertices. A face is a flat surface bounded by edges. In the GPU's world every face is a triangle: three vertices and the flat surface between them.

  3. 3

    Why triangles. Three points always lie in one flat plane, so a triangle can never be bent or twisted. Four points can be bent out of plane, and then it is not clear what "the surface between them" means. A flat triangle is easy for hardware to fill with pixels, and any other flat shape can be cut into triangles. That is why graphics chips are built around triangles.

  4. 4

    Counting. A cube has 6 square faces, and each square is 2 triangles, so 12 triangles. In three.js BoxGeometry has 24 vertices, not 8, because each face keeps its own four corners (the corner is shared by three faces that need different directions for lighting). A sphere with w segments around and h from top to bottom has w × (2h − 2) triangles: the default 32 × 16 gives 960.

  5. 5

    Round things are not round. A cylinder with 8 sides is an octagon prism; with 64 sides it looks round. More segments means a better silhouette and more triangles, quickly: a sphere with twice the segments each way has about four times the triangles. You choose a number that looks round enough at the size it is shown on screen.

  6. 6

    Cost. Each triangle must be processed by the GPU every frame, and its data (positions, colours, directions) sits in GPU memory. A few thousand triangles are nothing for any device; hundreds of thousands start to matter on a phone; a million makes a cheap phone struggle. The second live example prints real numbers for one triangle and for about one million.

Explain it without notes

01

Why does the GPU use triangles and not squares or other polygons?

02

A sphere has 32 segments around and 16 from top to bottom. How many triangles is that and how do you get the number?

Practice

01

A cylinder has 12 sides. Compute its triangles (sides plus two caps) and check with geometry.index.count / 3.

02

A designer's model has 2 million triangles and runs at 15 frames per second on a phone. Name two things you would try before asking for a new model.

Done when you can

  • Say what a vertex, an edge, a face and a mesh are

  • Explain why every GPU shape is made of triangles

  • Count the triangles of a box, a cylinder and a sphere and check them with renderer.info

  • Say why a cube has 24 vertices in three.js