PDA

View Full Version : Zoom to fit screen



JRGlide
01-27-2010, 01:56 PM
I am displaying point cloud data. How do I determine the zoom amount in order to fill the screen? It seems like the data is always either too close or too far away when displayed. Thank you.

dletozeun
01-27-2010, 02:04 PM
Does that (http://www.opengl.org/resources/faq/technical/viewing.htm#view0070) help?

JRGlide
01-27-2010, 02:47 PM
Thanks, but that didn't seem to work. I just get a blank screen.

I noticed that the example uses glOrtho, whereas my program is using gluPerspective. Does that matter?

Also, the way I currently implement this is that when I zoom I modify a z_position (or range) variable based on the mouse wheel. I then call glTranslate to zoom in or out. Since the example doesn't provide me with an initial z_position I'm not sure what my initial range should be.

JRGlide
01-27-2010, 03:41 PM
After doing some research I verified that the example in the link above will only work in ortho view. I need to find a way to find the proper range to fill the screen using projections. Thank you.

dletozeun
01-28-2010, 02:14 PM
Thanks, but that didn't seem to work. I just get a blank screen


You can adapt the code to perspective projection. Use glFrustum instead of gluPerspective.

Could you post the code of your projection setup?

JRGlide
01-28-2010, 02:55 PM
Here is how I set it up. The xOffset, yOffset and zOffset is just to center the point cloud. Although in my case I need to modify z to fill the screen. Thanks.


glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective (fov, aspect_3D, nearClipping, farClipping);
glMatrixMode(GL_MODELVIEW);
glViewport(0, 0, width, height);

xOffset = -(point_cloud->maxx + point_cloud->minx)/2;
yOffset = -(point_cloud->maxy + point_cloud->miny)/2;
zOffset = -(point_cloud->maxz + point_cloud->minz)/2;

glPushMatrix();
glLoadIdentity();
glTranslatef(x_position, y_position, z_position);
glMultMatrixf(modelMatrix);
glGetFloatv( GL_MODELVIEW_MATRIX, modelMatrix);
glPopMatrix();

pjcozzi
01-28-2010, 06:18 PM
Let's say you have a bounding sphere (center and radius) for your point cloud. Initially set your camera so your target is the point cloud's center. You want to zoom in/out such that the bounding sphere fills the view frustum. First compute a normalized vector from the target (point cloud center) to the eye:

Vector3d toEye = Vector3d.Normalize(eye - target);

Next determine the distance to move along this vector based on the field of view and the point cloud's radius:

double sin = Math.Sin(Math.Min(fieldOfViewX, fieldOfViewY) * 0.5);
double distance = (radius / sin);

Finally position the eye:

eye = target + (distance * toEye);

Eye, target, and whatever your up is can be input to gluLookAt.

Regards,
Patrick

JRGlide
01-29-2010, 10:45 AM
pjcozzi,

Thank you, that did the trick. I do transformations of sensors mounted on gimbals that rotate in azimuth and elevation, and yaw, roll, pitch of the plane, but I couldn't figure this out...

I let myself get confused because I thought I had to incorporate screen locations and near and far clipping planes into the equation.

By the way, for the benefit of future readers, there is a slight error in your equations. Instead of a sin it should be a tangent. The sin would give you slant range to the outside of the sphere. Tangent gives you the range from the eye to the center of the sphere. Therefore, it should be:

double tangent = Math.tan(Math.Min(fieldOfViewX, fieldOfViewY) * 0.5);
double distance = (radius / tangent);

Thanks,

Jim

pjcozzi
01-29-2010, 04:32 PM
Thank you, that did the trick...

By the way, for the benefit of future readers, there is a slight error in your equations...
Excellent.

The first time I coded this routine, I used tan. When I coded it again, without the luxury of having access to my original implementation, I experimented and sin wound up working. Just now I quickly tried to replace sin with tan, and it didn't zoom far back enough. I see what you are saying though. Hopefully, I can dig into it more at some point.

Regards,
Patrick

JRGlide
02-01-2010, 10:08 AM
For small angles sin and tangent are almost identical. As the angle increases sin and tanget start to diverge until at 90 degrees (fov = 180) the tangent is infinity and the equation would blow up. Also, since sin produces a smaller number it would tend to zoom further back as you said.

So although tangent is more mathmatically correct, sin is probably a better choice so you don't have to worry about it blowing up, and apparently gives more pleasing results. So I will probably change my code back to using sin as you originally suggested.

Thanks,
Jim

Sparks
02-23-2010, 10:26 PM
Is it possible to see the code that finally worked? I have the same problem to solve.

Thanks much!

Ceniza
01-07-2011, 12:08 AM
'sin' does not just give better results, it is the right trigonometric function to use.

For the code you need to know:

* width:height aspect ratio (width / height as floating point number as used by gluPerspective)
* center and radius of the bounding sphere for the scene
* direction of the camera (unit length)
* fov in y (as used by gluPerspective)

The code would be as follows:



half_min_fov_in_radians = 0.5 * (fov * PI / 180);

if (aspect < 1.0)
{
// fov in x is smaller
half_min_fov_in_radians = atan(aspect * tan(half_min_fov_in_radians));
}

distance_to_center = radius / sin(half_min_fov_in_radians);
eye = center - dir * distance_to_center;


You may also take advantage of that information to fit your zfar clipping plane (if you do not want to keep it too far).



zfar = distance_to_center + radius;

if (zfar < 1.5 * znear)
{
// Keep zfar always bigger than znear
zfar = 1.5 * znear;
}