Zoom to fit screen

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.

Does that help?

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.

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.

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?

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();

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

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

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

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

Is it possible to see the code that finally worked? I have the same problem to solve.

Thanks much!

‘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;
}