# How to fit a box in the camera's view frustum

This topic is 1784 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Hey guys, I'm having a problem with some basic math for a basic concept.

Idea:

I have a rectanglar box in my scene and I want to distance the camera such that the box just fits.

Background:

So right now I have my camera set up so that I can select a heading and an elevation. The software then calculates the camera's position using that data as if the camera is connected to a piece of string attached to the middle of the cube, tethering it to a spherical area. EG: I give it a heading of 45 degress and an elevation of 30 degrees, the camera is positioned 'Southwest' of the cube so it's looking 'Northeast' (45 degrees) and has an elevation of 30 degrees from the ground. If I give it a heading of 180 and an elevation of 90, the top of the screen is 'South' and the camera is directly above the box. (The x-axis is East/West, the y-axis is North/South). Now I'm trying to figure out to calculate the distance the camera needs to be so the box fills the screen. I know how to calculate the distance the camera needs to be so that no matter how the cube is rotated it fits (distance = boundRadius / sin(FOV / 2)). The problem with this is that the box is a wide, narrow rectangle, with little depth so using this method the box fills the screen only at a specific heading and elevation, otherwise there is a lot of empty space.

Current idea:

Since, ultimately, there's no difference between positioning the camera and rotating the mesh, I can think of the camera as being stationary and the mesh is being rotated so it's displayed with the correct heading and elevation. I was thinking that if I calculate the width and height of the box after it's rotated I can use trigonometry to calculate the distance (distance = halfWidth / tan(FOV / 2)). (If there's an easier/better idea, by all means, please let me know). I know how to calculate a 2D square's height and width after a 1D rotation, but I don't know how to calculate a 3D box's height and width after a 2D rotation.

What I know:

The box's height, width and depth; the camera's field of view, elevation (IE: x-axis rotation) and heading (IE: z-axis rotation).

Edited by Gamer Pro

##### Share on other sites
It's the same principle in 2D or 3D. I'll assume you have a rotation matrix, so after you rotate the box, you have three vectors for the three axes. Take the dot product of each one with the direction you want, multiply by the size of the box on that axis and add them together.

##### Share on other sites

Thanks for the responce EWClay, I found out that the API I'm using can be set up to calculate this for me but I appreciate the answer.

But now I have a math problem. My trigonometry isn't working.

My box's dimensions are: 1213, 623, 21; I have the heading at 0° and the elevation at 90° so the camera is giving me a bird's eye view with North to the top of my screen (IE: there's no rotations). My vertical field of view is 60°, the area I'm rendering to is 1920x850, which calculates my horiztonal field of view to be 105° (2 * atan(tan(FIELD_OF_VIEW_RADIANS / 2) * aspectRatio)). I use: distance = halfDimension / tan(FOV / 2) for both vertical and horizontal dimensions which gives me 646 & 818, respectively. Now this tells me that I need to position my camera 646 units away to fit the cube's vertically and 818 units away to fit the cube horizontally. I choose horizontally (so that the entire cube is visible). This should be right, right?

The problem is that if I position my camera 818 units away the cube is smaller than the frustum. The cube is centered on the origin and on my screen, so the above math (assuming it's right) states that the very right of my screen is 606.5 positive units on the x-axis, making the cube just fit horizontally. However I can place a small cube at 1065 units on the x-axis and it's on the edge of my screen (I just picked numbers till the cube was just on my screen).

I've looked over my math and it looks right. Maybe I'm missing something? Could their be something about the perspective camera I didn't take into account?

Edited by Gamer Pro

##### Share on other sites
Tan(105/2) = 1.3 and 1.3 x 818 is 1063, close to your edge of screen, so that all seems to fit.

I can't see where your distances come from. I can only assume a calculation error.

##### Share on other sites

It was a calculation error, instead of using tan I was using atan

But I think my solution is flawed. Lets ignore the above example and work with this one. Both heading and elevation is 0°, this makes my box's  dimensions 1213, 21, 623. I'm choosing to fit the box horizontally. The opposite side is half x (606.5) and my half FOV is 52.5° (from the calculated horizontal FOV of 105°). distance = opposite / tan(halfFOV) = 465.

Now the math is right but the fustrum is too small and part of the cube is outside of it.

* Edit

I found out what my problem is. I'm calculating how far FROM THE ORIGIN the camera needs to be to fit something so wide/tall in it's frustum, I forgot to acount for the box's width/height (well, half width / half height).

Edited by Gamer Pro

##### Share on other sites

Alright final question. I have everything setup, I have 1 last thing to do. I want to maximise the box in my view (it's not as easy as it sounds). Insofar, I have my camera creation broken down into 3 steps (if I'm doing anything wrong or weird feel free to set me straight). Here's my pseudo code.

Step 1, I initialize the camera such that the entire box is in my viewing frustum.

// Calculate box's dimensions after rotatations.
vector dimensions = box dimensions
matrix rotation = elevation rotation * heading rotation // XYZ euler order.
dimensions = dimensions * rotation

// Figure out how to size box so all if it fits in view.
horizontalPercent = dimensions.x / screenX
verticalPercent = dimensions.y / screenY

// Calculate distance.
if horizontalPercent > verticalPercent then
opposite = dimensions.x / 2
distance = opposite / tan(horizontalFoV / 2)
otherwise
opposite = dimensions.y / 2
distance = opposite / tan(verticalFoV / 2)

// Distance lets me see entire box if side is at origin, box is centered on origin so add half its depth.
distance = distance + dimensions.z / 2

// And create camera....


Step 2, figure out where the rendered box's center is

// Because the entire box fits on screen, I can calculate each corner's Normalized Device Coordinates.
For each corner
point = projectPointToScreen(point)
// update the max and min x & y values.

centerX = (maxX + minX) / 2
centerY = (maxY + minY) / 2


Step 3, figure out where in world space the rendered box's center is and look at that.

camera.lookAt = projectPointToWorld(centerX, centerY)


Now this works great; the box is perfectly centered on screen and when I'm looking straight down (elevation = 90) the box is maximised. But if I'm looking at it at an angle (elevation != 90) there is space on the top and bottom of it. I would like to calculate the camera distance so that the box is maximised. I'm not sure how to do that that and it would have to be back in step 1; because if I move the camera closer (or further) then the gap at the bottom of the box is going to close quicker than the gap at the top (due to perspective) and the box is no longer centered in my view.

Any ideas?

Edited by Gamer Pro

##### Share on other sites
I suspect it is because you are rotating the box dimensions rather than calculating the size of the box along the axis.

Find the box axes and the camera x and y axes in world space. Then the size of the box along the camera x axis is:

Box size X * abs(dot(Box X, Camera X))+
Box size Y * abs(dot(Box Y, Camera X))+
Box size Z * abs(dot(Box Z, Camera X))

And equivalently for y.

##### Share on other sites

+1 for the proper way to calculate the box's dimensions (I have no idea why I didn't think of that), but that wasn't my problem; I'm still calculating distances that are too big in the end. It must be my trig? Maybe it's more complicated than simply thinking of it as a 2D problem, after all it's the 3rd D that's causing my persepective 'issue'.

##### Share on other sites
It's a triangle problem, and the triangles happen to be in 3D, but the maths is the same.

Screenshots? I can't visualise what the problem is.

##### Share on other sites

As you can see the edge cases work but not the middle. Which kind of makes sense if you think about it, at the edge cases the box is perpendicular to the camera and everything is great.

[attachment=13756:perp.png]

(Grey is box, orange is camera, red is distance)

But in the middle cases, the box is no longer perpendicular to the camera and gaps are introduced. Not only that but the distance isn't the same.

[attachment=13757:slant.png]

(Grey is box, orange is camera, red is distance if the camera was perpendicular, pink is actual distance and black is gaps introduced from the box's rotatation).

The black gaps aren't an issue since I'll be moving the camera closer and therefore eliminate them; however the trig I'm using is assuming I'm perpendicular to the box. So the distance I'm calculating is the red line but I'm applying it to the pink line.

Edited by Gamer Pro

##### Share on other sites
Ok. You have to do the trig calculation a little differently to allow for perspective.

Find the normals of the top and bottom culling planes. Find the size of the box along those normal directions, using the formula I gave previously. If the sizes are s1 and s2, then the distance is

D = (s1 + s2) / (2 sin(half FOV))

Do the same for left and right. Also, you don't want to be looking at the box centre or part of the box will be off screen. You'll need to pan the camera to make it fit. The distance to pan is

P = (s1 - s2) / (2 sin(half FOV))

I'll leave the derivation as an exercise for the reader ;)

##### Share on other sites

I want to make sure I'm on the same page. Math and I aren't exactly on the best of terms.

Find the normals of the top and bottom culling planes.

I'm reading this as find the normals of the far and near planes of the camera. In which case there's only one and it's the inverse of the direction the camera is looking at.

Find the size of the box along those normal directions, using the formula I gave previously.

This is the box's depth, so: Box size X * abs(dot(Box X, Camera Z)) + Box size Y * abs(dot(Box Y, Camera Z)) + Box size Z * abs(dot(Box Z, Camera Z))

If the sizes are s1 and s2...

I don't understand how their can be 2 sizes. Do you mean the 'starting' and 'ending' point of the box & therefore using those points to determine the box size? Wouldn't the above forumla do that for me so D = halfDepth * sin(half FOV) (I'm assuming I'm multiplying, you missed the operator in your post.)

Do the same for left and right.

This is so I can determine if I should be sizing vertical or horizontally, right?

You'll need to pan the camera to make it fit.

I'm currently doing this by determining the center of box when it's rendered on screen and finding that point in the world. I like your way better since the code is much smaller (the most efficent code is the one you don't write, afterall). But I need clarification on the sizes thing.

##### Share on other sites
There are six culling planes: top, bottom, left, right, front and back. Top and bottom have different normals and so do left and right.

I missed some brackets, see above.

Yes, horizontal and vertical will give different distances.

Due to perspective the centre of the box won't be centred on screen if the box touches the edges on both sides. That's the reason for the offset.

##### Share on other sites

Okay, I've calculated the distance. It works for the 90° cases and for the middle cases it looks right (I won't know until I complete the panning). But I do have a couple of questions regarding the panning.

For the panning distance, I'm going: size calculated from the top normal - size calculated from the bottom normal & size calculated from the left normal - size calculated from the right normal. Is that correct or should they be the other way around?

Finally, I have no idea how to calculate the vector I need to pan along so that box fits and is centered.

And thank you for all the help; I've been trying to figure this out for a few months now and all the progress I've made in the last month has been directly because of you.

##### Share on other sites
No problem, I just hope I'm getting it right.

The formula gives you horizontal and vertical panning distances (camera x and camera y). You have it the right way round and combining the two components gives the vector to move along.

For the one that fits exactly you should pan the full distance in the respective direction. For the other direction I'd guess you should scale it down by the ratio of the distances, but I haven't worked that through.

Also, note that the panning distance is signed and this tells you which way to go along each axis.

##### Share on other sites

Oooh. I thought the distance was a radius thing. 'You have the pan the camera this much in some direction' kind of thing.

However, it looks like distance calculate is coming up short.

Before panning:

[attachment=13886:before pan.png]

After panning:

[attachment=13887:after pan.png]

Now ignoring that the panning is wrong, the box should fit the screen, or at worse, spill past either the top or bottom of the screen due to bad/no panning. The distance calculation seems fine.

bottom size = half map x * absolute(x-axis • bottom normal) +
half map y * absolute(y-axis • bottom normal) +
half map z * absolute(z-axis • bottom normal)
left size = half map x * absolute(x-axis • left normal) +
half map y * absolute(y-axis • left normal) +
half map z * absolute(z-axis • left normal)
and etc..

vertical distance = (top size + bottom size) / (2 * sin(vertical FoV / 2))
horizontal distance = (leftSize + rightSize) / (2 * sin(horizontal FoV / 2))

distance = max(vertical distance, horizontal distance)

camera position = normalized camera direction * distance

Edited by Gamer Pro

##### Share on other sites
No idea what happened in the before and after shots. The camera should move in the plane of the screen keeping the same rotation. In this case it should have moved mostly downwards to centre the box.

##### Share on other sites

I'm not too worry about the panning yet, I probably did something wrong. I'm just trying to figure out the distance calculation.

##### Share on other sites
I'm not sure the distance is wrong, looking at that. Without the panning it's hard to say.

##### Share on other sites

Okay, lets deal with the panning first.

So I've calculated a horizontal panning distance of 147 and a vertical panning distance of -448. So this means I need to pan along the camera's x-axis 147 units to the right and along the camera's y-axis 448 units down (first flag, it looks like I should be panning to the left, not the right). I calculate the normalized vectors that represent the camera's x and y-axis and multiply those vectors with the corresponding magnitudes and now I have the world coordinates.

Question, when you say pan do you mean move the camera? That's what I think when I hear pan. The problem with this is that the camera is positioned such that it is viewing the box at the given heading and elevation, if I move the camera this is now no longer the case. IE: The camera is something like 545 units up in the air as part of the 45° elevation, if I move the camera down the world version of 448 units, the camera is know, like, 15° elevated. The above picture is the camera LOOKING AT the calculated world coordinates of (147, -448).

Edited by Gamer Pro

##### Share on other sites
I'm not a photographer but I guess I actually mean tracking rather than panning, i.e the camera translates rather than rotates. I'll call it that from now on.

If you wanted to do it the other way, keeping the camera on the same line from the origin, you would have to rotate the camera to re-centre the box. I don't think there is a direct solution for the distance, but you could try an iterative method.

Do the calculation as before, but instead of translating, rotate the camera to look at the box centre plus the tracking vector. Then recalculate the distance using the new camera rotation. Move to that distance. Repeat a few times. I think the error will get smaller each time.

I'd recommend getting it working with camera tracking first.

##### Share on other sites

I was hoping to avoid that but it looks like that's what I have to do.

So for future reference, should anyone have the same question, what I did to solve this was, in a loop, center the box and take a step closer to the box. If the box is too big (being cutt off) take a step back so the entire box is on screen and make your steps smaller. Keep doing this until the box is big enough (99% or 99.9% of the screen or whatever you want; you can even make it 50% if you don't want to maximze it).

Edited by Gamer Pro