Course outline
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.
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
minand amaxcorner. - 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
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
Use geometry.center()
In the measuring example replace the line
geometry.translate(-centre.x, -box.min.y, -centre.z);withgeometry.center();.geometry.center()centres in y too, soafter translate, box minreads(-0.3, -0.5, -0.2). After the hand lift andcomputeBoundingBox()the box runs from y = 0 to 1.
Code & diagrams
From the left: the whole picture, the right half, v flipped (the F upside down), and no uv attribute (one flat colour).
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: noneTry 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.
The blue outline is the world-space box of the turned shape. Read the console for the three boxes: before, stale and recomputed.
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].
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 = falsehides the symptom; callingcomputeBoundingSphere()fixes the cause. - ▸
Box3.setFromObject(object)by default (withoutprecise) 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°. Passtrueas the second argument for an exact fit at the cost of visiting every vertex.
Remember this
- 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
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
No UVs. Without a
uvattribute 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
Bounding boxes. A
Box3is the smallest box (with sides along x, y and z) that holds a shape.geometry.computeBoundingBox()fillsgeometry.boundingBoxfrom 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
Stale boxes.
translate(),rotateX(),scale()andapplyMatrix4()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 callcomputeBoundingBox()/computeBoundingSphere()again. The second example shows it.
Explain it without notes
What are u and v, and what happens if a geometry has no uv attribute?
How would you put a loaded model's feet on the floor and its middle on the centre line?
Practice
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?
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