Fitting objects in camera view

Started by
6 comments, last by Zakwayda 18 years, 9 months ago
Hi everyone again! Let's say I have the camera's direction and I want to move it along this vector until certain [bounding] spheres are completely visible on the screen. I also have the projection and view matrices from which I can extract the projection parameters (left, right, top, bottom, znear, zfar) and the view frustum planes. How would I achieve this apart from implementing the brute force method of stepping back along the vector and testing the visiblity of the spheres at each step? Cheers then, thanks then...
Advertisement
The distance from the bounding sphere in order to fit it exactly is this:

distance = bs_radius / sin( fov/2 )

... but you say bounding spheres. Fitting multiple bounding volumes is a much more complicated problem.
John BoltonLocomotive Games (THQ)Current Project: Destroy All Humans (Wii). IN STORES NOW!
Find out which of the four frustum side planes culls each sphere the most (by using dot products), and divide that distance by how much the dot product of the plane changes when moving the camera. Take the maximum. Done.

It looks something like this:
  foreach fp in frustumPlanes    fp.invMovement = 1 / (fp.planeNormal dotProduct camera.frontVector);  movement = 0;  foreach sp in spheres    maxMovementForSphere = max(sp.pos dot each fp.planeNormal-fp.planeD) + sp.radius    movment = max(movement, maxMovementForSPhere)

enum Bool { True, False, FileNotFound };
Quote:Original post by jyk
For a sphere directly in front of the camera, to fit it horizontally:

distance = radius/tan(horizontalFov/2)

To fit it vertically:

distance = radius/tan(verticalFov/2)



That works fine for the example in the other thread (a flat rectangle) but for a solid 3D sphere the maths is slightly different.

To see what I mean try drawing a top down view of the fustrum on a bit of paper, then draw a circle around the point as calculated above and you will see it goes slightly outside the fustrum area (and hence outside the screen).

For the sphere to fit exactly on screen (in either the horizontal or vertical) then the distance becomes the hypotenuse of a right angled triangle from the view point to the point where the sphere touches the frustum and then at a right angle to the sphere centre point.

The equation then becomes...

distance = bs_radius / sin( fov/2 )

as stated earlier by JohnBolton
Quote:That works fine for the example in the other thread (a flat rectangle) but for a solid 3D sphere the maths is slightly different.
Yup, the thread I linked to doesn't apply directly to this problem, so I deleted the reference. I'm still curious though as to whether the OP is referring to multiple arbitrarily-positioned spheres? (I think he is...)
Ok, here is my code from earlier, fixed to incorporate the correct equation. I tested this in 2d and it seemed to work perfectly, although it's still possible that I've overlooked something somewhere. The pseudocode iterates through the spheres (which have already been transformed into camera space and culled if necessary) and returns the amount the camera must back up in order for all spheres to be in view.

float delta = 0;// Spheres have been transformed into local space (those behind camera are ignored)for (each sphere){    float dist;    dist = (fabs(sphere.position.x) + sphere.radius / cos(hFov / 2)) / tan(hFov / 2);    if (dist - sphere.position.z > delta)         delta = dist - sphere.position.z;    dist = (fabs(sphere.position.y) + sphere.radius / cos(vFov / 2)) / tan(vFov / 2);    if (dist - sphere.position.z > delta)         delta = dist - sphere.position.z;}// Delta is now how much you need to 'back up' the camera for all spheres to be in view

Again, no guarantees of correctness, but it seemed to work fine in my little demo.
Thanks a lot everyone for your help! [smile]

Thanks for the formulae John Boulton, and for the frustum explanation hplus0603.

Can someone explain the 'fp.invMovement = 1 / (fp.planeNormal dotProduct camera.frontVector);' please? Is this to calculate the change in the dot products? It looks more general than the solutions using the trig in case you use a skewed frustum, is much simpler to understand and probably much faster too instead of using 4 trig functions and 2 inverse trig functions to find the horizontal and vertical FOVs!

I'd still like to understand how jyk's solution works...what is he doing with the sphere radius?! Thanks for taking the time to test it in your 'little demo' too.

*Rates everyone*

This should be useful for most people programming a camera system...perhaps it should be put somewhere?

I'll try it when I can...but at the moment I can't start my game up because the physics simulation somehow creates a _really_ small quaternion (like exponent 23) which goes to NaN when normalized :( I started a thread about that too.

Cheers!
Quote:It looks more general than the solutions using the trig in case you use a skewed frustum, is much simpler to understand and probably much faster too instead of using 4 trig functions and 2 inverse trig functions to find the horizontal and vertical FOVs!
Don't worry about the expense of the trig functions. Assuming you store your fovs with the camera, in actual code it's only two trig calls and two divides. If this is something you'll be doing often, these values can be pre-calculated and stored, at which point I would think it would be no more expensive than the other method (perhaps less, because you don't have to extract the frustum planes). The skewed frustum is another issue, though.
Quote:I'd still like to understand how jyk's solution works...what is he doing with the sphere radius?!
Assuming I did the math right, it's a generalization of the equation posted by John and WillC that takes the position of the sphere into account. I can try to describe the derivation, but it's just some triangles and trig when you draw it out. (Again, although it seems to work perfectly in practice, I hesitate to guarantee that the equation is correct...)

[Edit: Just adding that (a+b/cos(theta))/tan(theta) can be simplified (I think) to (a*cos(theta)+b)/sin(theta). It really doesn't make much difference performance-wise; I just mention it for completeness.]

[Edited by - jyk on July 11, 2005 6:29:58 PM]

This topic is closed to new replies.

Advertisement