Course outline
Framing an Object
In one line
To fit an object in the picture, compute the camera distance from the field of view: d = (size / 2) / tan(fov / 2), using whichever of height and width is tighter. Then set near, far and the controls' limits from the same size, and do it again when the window changes shape.
Think of it like this
When you photograph a painting you step back until all of it fits in the viewfinder. A wide painting needs you farther back than a tall one if your phone is held upright. The camera's field of view and the shape of the screen decide how far.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- bounding box
- The smallest box, aligned with the axes, that holds an object.
Box3in three.js. - bounding sphere
- The smallest sphere that holds an object. It is the same from every side, which makes it good for orbiting.
- margin
- Extra room around the object so it does not touch the edges of the picture.
- tan(fov / 2)
- Half the visible height at distance 1. Multiply by a distance to get half the visible height there.
- tight side
- The side of the picture (height or width) that runs out of room first, and so sets the distance.
Step by step
01Compute the distance
The first example prints a table. For each object it gives the "from any side" distance and the "face on" distance, then places a camera there and projects the 8 corners of the object's box. The largest corner coordinate shows how much of the picture it fills (1.00 would touch the edge).
02Read the table
The bike on 16:9 fills 0.87 of the height. On the upright phone it fills 0.87 of the width but only 0.26 of the height: width is the tight side. The screw prints near must go below 0.1, and the tower far must go above 100.
03Frame and refit
The second example has a frameObject() that sets the target, the position, near, far and the controls' limits from one bounding sphere. It runs for three window shapes, then for an empty group and for a scale-0 object.
04Guard before you frame
Read the last lines of the second example: the empty group and the scale-0 object are refused before any camera number changes, so the old view stays. That guard (box.isEmpty() and radius > 0) is the difference between "nothing to show yet" and a camera parked at NaN with a blank screen.
Try it yourself
- 1
Remove the margin
In the first example change
margin = 1.15tomargin = 1infitFaceOn. The face-on bike on 16:9 prints1.09 m fills x 0.88 y 1.00: the height now touches the top and bottom edges. - 2
A square window
Add
["bike, square window", [1.4, 0.9, 0.12], 1],tothings. The face-on distance is 1.94 m and it fills x 0.87, y 0.56: with aspect 1 the width is the tight side.
Code & diagrams
No canvas here, only the maths and a check: the projected corners prove the fit. Face on means looking straight at the long side.
Console
Run the scene to see its console.Expected console output
bike, 16:9 screen from any side 2.51 m | face on 1.25 m fills x 0.76 y 0.87 | near 0.1 is fine, far 100 is fine
bike, phone upright from any side 4.98 m | face on 4.10 m fills x 0.87 y 0.26 | near 0.1 is fine, far 100 is fine
bike, ultra-wide 3.5:1 from any side 2.51 m | face on 1.25 m fills x 0.39 y 0.87 | near 0.1 is fine, far 100 is fine
a 2 cm screw from any side 0.0319 m | face on 0.0156 m fills x 0.87 y 0.39 | near must go below 0.1, far 100 is fine
a 400 m tall tower from any side 614 m | face on 555 m fills x 0.07 y 0.87 | near 0.1 is fine, far must go above 100The bike here is two simple shapes. Drag to orbit: the limits came from the object's size. The last two lines are the guard for empty and scale-0 objects.
Console
Run the scene to see its console.Expected console output
16:9 screen distance 2.65 m, limits 0.53 to 7.96 m
phone upright distance 5.28 m, limits 0.53 to 15.83 m
phone turned sideways distance 2.65 m, limits 0.53 to 7.96 m
nothing to frame: the object is empty or has scale 0
nothing to frame: the object is empty or has scale 0Break it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Forget the aspect
In fitFaceOn change the width term to width / 2 / tan (remove · aspect).
Myth vs fact
Myth
Set the camera to z = 5 and it will fit everything.
Fact
Five metres fits the bike on a laptop and is far too near for the tall phone or the tower, and too far for the screw. Compute the distance from the size and the field of view.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
Box3.setFromObject(object, true)uses each mesh's real vertices (precise = true) instead of the faster bounding boxes. It is more accurate for rotated parts and costs a pass over every vertex, so call it once, not every frame. - ▸
A box is measured at one moment. A model that animates or moves can leave it later, so frame with a margin that covers the whole movement.
Remember this
- 1
The problem this solves: a fixed camera position fits one object on one screen. A 2 cm screw, the bike and a 400 m tower, on a laptop and on a phone, each need a different distance. The distance must be computed, not guessed.
- 2
The maths. Half the visible height at distance
disd · tan(fov / 2). So a heighthfits whend = (h / 2) / tan(fov / 2). The visible width is the height times the aspect, so a widthwfits whend = (w / 2) / (tan(fov / 2) · aspect). Use the larger of the two: the tighter side decides. Multiply by 1.15 to leave a margin. - 3
Real numbers. The bike is about 1.4 m long and 0.9 m tall. Seen from the side with
fov45° on a 16:9 screen it needs 1.25 m (the height is the tight side). On a phone held upright, aspect 9:19, the width is tight, and it needs 4.10 m. Same bike, same camera, more than three times farther. - 4
From any side. If the visitor orbits, the bike can face the camera at any angle, so fit its bounding sphere:
d = radius · margin / sin(smaller / 2), wheresmalleris the smaller of the vertical and horizontal field of view (horizontal =2 · atan(tan(fov / 2) · aspect)). For the bike this gives 2.51 m on 16:9 and 4.98 m on the upright phone. It is safe from every direction, a little loose from the side. - 5
Measure, do not hard-code.
new THREE.Box3().setFromObject(object)gives the world-space box of an object and all its children (Topic 4.5).box.getBoundingSphere(new THREE.Sphere())gives the centre and radius. Aim at the centre:controls.target.copy(sphere.center). - 6
Near, far and limits follow the size. A 2 cm screw seen from 1.6 cm needs
nearbelow the default 0.1, or the camera is inside thenearplane. A 400 m tower needs afarabove 100. Setnear = distance / 100,far = distance * 100,minDistanceto a share of the radius andmaxDistanceto a multiple of the distance, as the second example does. Keep the ratiofar / nearmoderate (Topic 7.2). - 7
Edge cases. An empty group has an empty
Box3whose limits areInfinity: framing it givesNaNpositions and an empty screen. An object with scale 0 has radius 0, so the distance is 0 and the camera lands inside it. Checkbox.isEmpty()andradius > 0first. When the window changes shape, fit again, but only if the user has not moved the camera since (Topic 7.6).
Explain it without notes
Derive the distance at which an object h tall just fits, for a camera with vertical field of view fov.
Why does the same bike need a different camera distance on a phone held upright?
Practice
A shelf 2.4 m wide and 0.4 m tall is seen by a camera with fov 40° and aspect 2. How far must the camera be, with no margin?
Write the guard at the top of frameObject that stops it from framing an empty object, in one if.
Done when you can
Derive the fit distance from the field of view and the size
Choose the tight side from the aspect
Set
near,farand the controls' limits from the object's sizeGuard against empty and scale-0 objects and refit when the window changes shape