I'm looking for an algorithm to fit a bounding box inside a viewport (in my case a DirectX scene). I know about algorithms for centering a bounding sphere in a orthographic camera but would need the same for a bounding box and a perspective camera. I can not just change the FOV because this app has FOV as a user editable variable, so it must move the camera.
I have most of the data:
- I have the up-vector for the camera
- I have the center point of the bounding box
- I have the look-at vector (direction and distance) from the camera point to the box center
- I have projected the points on a plane perpendicular to the camera and retrieved the coefficients describing how much the max/min X and Y coords are within or outside the viewing plane.
Problems I have:
- Center of the bounding box isn't necessarily in the center of the viewport (that is, it's bounding rectangle after projection).
- Since the field of view "skew" the projection (see http://en.wikipedia.org/wiki/File:Perspective-foreshortening.svg) I cannot simply use the coefficients as a scale factor to move the camera because it will overshoot/undershoot the desired camera position
How do I find the camera position so that it fills the viewport as pixel perfect as possible (exception being if the aspect ratio is far from 1.0, it only needs to fill one of the screen axis)?
I've tried some other things:
- Using a bounding sphere and Tangent to find a scale factor to move the camera. This doesn't work well, because, it doesn't take into account the perspective projection, and secondly spheres are bad bounding volumes for my use because I have a lot of flat and long geometries.
- Iterating calls to the function to get a smaller and smaller error in the camera position. This has worked somewhat, but I can sometimes run into weird edge cases where the camera position overshoots too much and the error factor increases. Also, when doing this I didn't recenter the model based on the position of the bounding rectangle. I couldn't find a solid, robust way to do that reliably.
Help please!
This is copied straight from my engine, it creates 6 planes which represent each of the six sides of the frutsum. I hope it comes in useful.
There are many possible camera positions + orientations where the bounding box would fit inside the view frustum. But any procedure would select one specific camera position and orientation.
If you would consider bounding spheres, one solution could be to
With bounding boxes you could consider an earlier step of first positioning the camera at perpendicular to the center of the largest (or smallest, whatever you prefer) cube face.
I have no experience with DirectX, but moving and changing the looking direction of the camera to center a certain point should be easy. The hard part is to do the math of deciding how far to move to view the object.
Math
If you know the bounding size
s
of the object in world coordinates (we are not interested in pixels or camera coordinates, since those are dependent on your distance) from the orientation of the camera, you can compute the required distanced
of the camera to the bounding shape if you know the x and y Field-Of-View anglea
of the perspective projection.So, the math is
tan(a/2) = (s/2) / d
=>d = (s/2) / tan(a/2)
Which will give you the distance the camera should be placed from the closest bounding surface.I know there are some excellent answers above, but I wanted to add a rediculously simple solution to fit the bounding sphere inside the camera frustrum. It makes the assumption that you want to keep the camera Target and Forward vector the same, and simply adjust camera distance to target.
Note, this won't give you the best fit but it will give you an approximate fit, showing all geometry, and only in a few lines of code, and without screen to world transformations
The implementation of BoundingBox in C# is found below. The important points are the Centre and Corners properties. Vector3 is a pretty standard implementation of a 3 component (X,Y,Z) vector
I don't have it at hand at the moment but the book you want is http://www.amazon.com/Jim-Blinns-Corner-Graphics-Pipeline/dp/1558603875/ref=ntt_at_ep_dpi_1
He has a whole chapter on this
Since you have a bounding box, you should have a basis describing it's orientation. It seems that you want to position the camera on the line coincident with the basis vector describing the smallest dimension of the box, then roll the camera so that the largest dimension is horizontal (assuming you have OBB and not AABB). This assumes that the aspect ratio is greater than 1.0; if not you'll want to use the vertical dimension.
What I would attempt:
boxWidth / (2 * tan(horizontalFov / 2))
. Note thatboxWidth
is the width of the largest dimension of the box.boxCenter + scaledBasis
looking at theboxCenter
.Edit:
So I think what you're getting at is that you have the camera at an arbitrary position looking somewhere, and you have an AABB at another position. Without moving the camera to face a side of the box, you want to:
If this is the case you'll have a bit more work; here's what I suggest:
Unproject
two opposing corners of the screen space bounding box into world space. For a Z value use the closest world space points of your AABB to the camera.Check this link https://msdn.microsoft.com/en-us/library/bb197900.aspx
float distance = sphere.radius / sin(fov / 2);
float3 eyePoint = sphere.centerPoint - distance * camera.frontVector;