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

Started by
20 comments, last by Gamer Pro 11 years, 1 month ago

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).

Advertisement
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.

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?

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.

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

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).

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?

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.

+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'.

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.

Heading: 0°, elevation 0°

[attachment=13751:0 heading, 0 elevation.png]

Heading: 90°, elevation 0°

[attachment=13752:90 heading, 0 elevation.png]

Heading 0°, elevation 90°

[attachment=13753:0 heading, 90 elevation.png]

Heading 90°, elevation 90°

[attachment=13754:90 heading, 90 elevation.png]

Heading 45°, elevation 45°

[attachment=13755:45 heading, 45 elevation.png]

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.

This topic is closed to new replies.

Advertisement