Command Palette

Search for a command to run...

Hectal
Course outline
Topic 4.5Beginner ~21 min · step 5 of 7

UVs and Bounding Boxes

In one line

UV coordinates say where each vertex sits on a picture, so a flat image can wrap a 3D shape. Bounding boxes and spheres say how big and where a shape is, so you can centre it, fit the camera to it and let three.js skip it when it is off screen.

0/7 · 0%

Think of it like this

A gift-wrap sheet. To wrap a box you decide which part of the paper covers which face. A UV is that decision for one corner: "this corner of the shape sits at this spot on the picture". The picture stays flat; the shape is pinned to it.

Words you'll meet

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

UV coordinates
Two numbers per vertex, u (across) and v (up), each 0 to 1, saying which spot of a picture the vertex sits on.
Box3
The three.js class for an axis-aligned box with a min and a max corner.
bounding sphere
The smallest ball (around the box's centre) that holds a shape. Used to skip things that are off screen.
frustum culling
Three.js not drawing an object whose bounding sphere is completely outside the camera's view.

Step by step

01Four sets of UVs

Each square has the same four positions. Only the uv array changes. [0, 0, 1, 0, 1, 1, 0, 1] lists the corners bottom-left, bottom-right, top-right, top-left of the picture. Using only 0.5 to 1 across shows the right half stretched over the whole square.

02Measure a model

The second example makes a box that is off to the side, the way a badly exported file might arrive. computeBoundingBox() finds its minimum and maximum corners, and getSize and getCenter give 0.6 × 1 × 0.4 centred at (1.5, 0.5, 0.2). The bounding sphere radius is 0.616.

03Centre it, then edit by hand

geometry.translate(-centre.x, -box.min.y, -centre.z) moves the shape so it is centred and its bottom sits at y = 0. Then a loop lifts every vertex by 0.5 by writing into the array. The box still shows the old numbers until it is recomputed.

04Measure in the world

new Box3().setFromObject(mesh) takes the mesh's position and rotation into account: after moving the mesh to x = 2 and turning it a quarter turn about y the box is 0.4 wide and 0.6 deep (width and depth swapped). Box3Helper draws the box in blue.

Try it yourself

  1. 1

    Use a quarter of the picture

    In the UV example change the second case's uvs to [0.5, 0.5, 1, 0.5, 1, 1, 0.5, 1]. The second square now shows a 4 × 4 checker, the top-right quarter of the picture stretched to fill it, with a small black sliver of the letter F in its bottom-left corner.

  2. 2

    Use geometry.center()

    In the measuring example replace the line geometry.translate(-centre.x, -box.min.y, -centre.z); with geometry.center();. geometry.center() centres in y too, so after translate, box min reads (-0.3, -0.5, -0.2). After the hand lift and computeBoundingBox() the box runs from y = 0 to 1.

Code & diagrams

Four UV maps on one square New tab

From the left: the whole picture, the right half, v flipped (the F upside down), and no uv attribute (one flat colour).

idle

Console

Run the scene to see its console.

Expected console output

whole picture            uv: 0 0 1 0 1 1 0 1
right half only          uv: 0.5 0 1 0 1 1 0.5 1
upside down (v flipped)  uv: 0 1 1 1 1 0 0 0
no uv attribute          uv: none

Try it: interactive

The UV layout next to the shape

Switch between the plane, box, cylinder and sphere. Look at how the flat picture on the left is cut and bent to wrap the shape on the right, and find the seams.

Measuring, centring and a stale box New tab

The blue outline is the world-space box of the turned shape. Read the console for the three boxes: before, stale and recomputed.

idle

Console

Run the scene to see its console.

Expected console output

box min: (1.2, 0, 0) max: (1.8, 1, 0.4)
size: (0.6, 1, 0.4) centre: (1.5, 0.5, 0.2)
sphere radius: 0.616
after translate, box min: (-0.3, 0, -0.2)
stale box after editing by hand: (-0.3, 0, -0.2) (0.3, 1, 0.2) <- still the old numbers
after computeBoundingBox():        (-0.3, 0.5, -0.2) (0.3, 1.5, 0.2)
world box, moved and turned: (1.8, 0.5, -0.3) (2.2, 1.5, 0.3)

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

Turn the picture upside down by flipping v

In the UV example change the first case's uvs to [0, 1, 1, 1, 1, 0, 0, 0].

terminal
$ Run
── what you'll see ──
whole picture uv: 0 1 1 1 1 0 0 0
No error: the letter F is simply upside down. If an image you load looks flipped, the cause is one of two flips: the loader's `flipY` or your v values.

Myth vs fact

Myth

UVs are measured in pixels.

Fact

They are fractions of the picture: 0 to 1 on both sides, whatever the picture's size. The same UVs work with a 256-pixel image and a 4,096-pixel one.

Pro corner

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

  • ▸

    A stale bounding sphere is a classic "my mesh vanishes" bug. Three.js culls by the sphere computed the first time the mesh was drawn. If you later move vertices far away by hand and never recompute it, the mesh disappears as soon as the old position leaves the view, although the new position is on screen. mesh.frustumCulled = false hides the symptom; calling computeBoundingSphere() fixes the cause.

  • ▸

    Box3.setFromObject(object) by default (without precise) transforms the eight corners of the geometry's box, which can be looser than the true shape when the object is turned by an angle that is not a multiple of 90°. Pass true as the second argument for an exact fit at the cost of visiting every vertex.

Remember this

  1. 1

    The problem this solves: a vertex has a place in space (x, y, z) but a picture is flat. To paint a tyre pattern on a tyre or a logo on a frame, each vertex needs a second address, a spot on the picture. That address is two numbers, u (across) and v (up), each from 0 to 1. (0, 0) is the picture's bottom-left, (1, 1) its top-right. The picture itself is the subject of Phase 6; here you make the addresses.

  2. 2

    The uv attribute. It has item size 2 and one entry per vertex, just like position. The GPU blends the three UVs of a triangle across its pixels and looks up the picture at each spot. The first example gives one square four sets of UVs: the whole picture, the right half only, with v flipped (the letter F goes upside down), and no uv attribute at all.

  3. 3

    No UVs. Without a uv attribute every pixel reads the same spot, the bottom-left texel of the picture, so the shape is one flat colour (the fourth square). No error is printed. It is a common bug with hand-made geometry or a model exported without UVs.

  4. 4

    Bounding boxes. A Box3 is the smallest box (with sides along x, y and z) that holds a shape. geometry.computeBoundingBox() fills geometry.boundingBox from the vertices, in the geometry's own coordinates. geometry.boundingBox.getSize(v) gives width, height and depth; getCenter(v) the middle. computeBoundingSphere() gives a ball instead: its centre is the box's centre and its radius is the distance to the farthest vertex.

  5. 5

    Stale boxes. translate(), rotateX(), scale() and applyMatrix4() keep an existing box up to date. Editing the position array yourself does not: the box and sphere still describe the old shape until you call computeBoundingBox() / computeBoundingSphere() again. The second example shows it.

Explain it without notes

01

What are u and v, and what happens if a geometry has no uv attribute?

02

How would you put a loaded model's feet on the floor and its middle on the centre line?

Practice

01

Make a UV set that tiles the picture 3 times across a square. Which numbers go in, and what must the texture's wrap mode be?

02

Write a function placeOnFloor(mesh) that moves a mesh so its lowest world point is at y = 0.

Done when you can

  • Write a uv attribute for a quad and show part of a picture

  • Say what u and v mean and what a missing uv attribute looks like

  • Measure a geometry with Box3, centre it and put it on the floor