Zoom in OpenGL ES 2.0 - object disappearing

2.7k views Asked by At

I like to know about how to correctly zoom in OpenGL ES 2.0. I've successfully drawn a model but it's pretty small and I'm not able to zoom into this model. What I like to have is to zoom "through" this model.

The model is a building with different floors - I'd like to zoom to every room of every floor. But either the object disappears because of the view frustum or I'm not able to get very "near" to this object.

I'm using the zoom-touch gesture and get a value "scale" - what should I now do with this value?

What I tried so far:

Changing the near-plane and far-plane distance and changing the eyeZ-Value within Matrix.setLookAtM(....) but what I only achieve is zooming out... It disappears in zooming in after a bit... So I'm not able to zoom in to some special parts ("THAT" far....)


How I can I achieve this?

So the biggest problem is the near-plane combined with zooming via the eyeZ-Value. It simply doesn't work. If I zoom in, the object disappears because of the nearplane. But I don't see any logic behind this.

Currently I'm using:

/*
 * Set the camera position (View matrix)
 */
Matrix.setLookAtM(mViewMatrix, offset, eyeX, eyeY, eyeZ / mZoomLevel,
                  centerX, centerY, centerZ, upX, upY, upZ);

where mZoomLevel is the factor I get through the onTouch-Zooming.

My whole Matrix-Operations are shown here:

@Override
public void onDrawFrame(GL10 unused) {

LoggerHelper.calculateFPS();

/*
 * Draw background color
 */
 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);

/*
 * scale model down to smaller values
 */
Matrix.setIdentityM(mModelMatrix, 0);
Matrix.scaleM(mModelMatrix, 0, model3d.getRatio() * scaleFactor,
                model3d.getRatio() * scaleFactor, model3d.getRatio()
                                * scaleFactor);

/*
 * rotate and translate model in dependence to the user input
 */
Matrix.translateM(mModelMatrix, 0, translateX, translateY, translateZ);
Helper.rotateModel(mModelMatrix, rotationX, rotationY, rotationZ, true,
                model3d.getWidth(), model3d.getLength(), model3d.getHeight());

/*
 * Set the camera position (View matrix)
 */
Matrix.setLookAtM(mViewMatrix, offset, eyeX, eyeY, eyeZ / mZoomLevel,
                centerX, centerY, centerZ, upX, upY, upZ);

/*
 * combine the model with the view matrix
 */
Matrix.multiplyMM(mMVMatrix, 0, mViewMatrix, 0, mModelMatrix, 0);

/*
 * this projection matrix is applied to object coordinates in the
 * onDrawFrame() method
 */
Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, 1, -1,
                nearPlaneDistance, farPlaneDistance);

/*
 * Calculate the projection and view transformation
 */
float[] mMVPMatrix = new float[16];
Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mMVMatrix, 0);

/*
 * all the drawing stuff inside the model-object (otherwise
 * translation/rotation wouldn't affect every object)
 */
model3d.draw(mMVPMatrix);

}

Any some important variables:

private float nearPlaneDistance = 1f;
private float farPlaneDistance = 200f;
private float eyeZ = -1;

I uploaded a dummy-project with only the OpenGL-part on Github - in case you want to have a better look into the sourcecode

What I have:

My current view

What I need:

My desired view

2

There are 2 answers

7
Frame91 On BEST ANSWER

One of my solutions (not working very good):

public void setZoom(float zoom) {
    // this projection matrix is applied to object coordinates
    // in the onDrawFrame() method
    float ratio = (float) width / height;
    Matrix.frustumM(mProjectionMatrix, 0, -ratio / zoom, ratio / zoom, -1
            / zoom, 1 / zoom, nearPlaneDistance, farPlaneDistance);
}

But this is not the best approach (see coments below this answer)

3
user1097185 On

I am confused about your "zooming" or you are confused about it. When you move the View matrix(changing eyeZ) you don't zoom in or out, you just move the camera. Yes, moving the camera closer to an object will make it look bigger(in case of perspective projection) but it's not zooming. You zoom by changing the focal length or view angle of the camera. You change the "persepectiveness" of your camera. Just like if you have a real camera simply walking toward your subject of photography is different than using the camera's zoom(Assuming it has a mechanical zoom).

In order to really zoom on something(if that is actually what you want) you will have to change the "ratio" in Matrix.frustumM. So you can make objects "become bigger" on the screen without having to worry about the object being clipped by the near plane. However, it will also change the perspective. The more you zoom the more it will look like orthographic projection.

I think what you are trying to achieve is flying inside a model of a building. For that you don't need to dynamically adjust the zoom of the camera. But you do need to adjust the front and end planes.

I think I know what is the source of your confusion. When you change the near plane of the frustum but keep the ratio fixed, you actually change the zoom and view angle. Because the projection matrix's x and y focal values are calculated by the near plane divided by the left/right and top/bottom. The left, right and top, bottom in Matrix.frustumM are actually the dimensions of the near plane.

Here is the source code of frustumM. You can see x and y which are the focal point are calculated using only the near plane and not the far plane. If you want to maintain the zoom or viewing angle and change the near plane, you will have to multiply left, right(ratio in your case) and top, bottom(1 in your case) by the ratio between the original near depth and the new near depth.

public static void frustumM(float[] m, int offset,
        float left, float right, float bottom, float top,
        float near, float far) {
    if (left == right) {
        throw new IllegalArgumentException("left == right");
    }
    if (top == bottom) {
        throw new IllegalArgumentException("top == bottom");
    }
    if (near == far) {
        throw new IllegalArgumentException("near == far");
    }
    if (near <= 0.0f) {
        throw new IllegalArgumentException("near <= 0.0f");
    }
    if (far <= 0.0f) {
        throw new IllegalArgumentException("far <= 0.0f");
    }
    final float r_width  = 1.0f / (right - left);
    final float r_height = 1.0f / (top - bottom);
    final float r_depth  = 1.0f / (near - far);
    final float x = 2.0f * (near * r_width);
    final float y = 2.0f * (near * r_height);
    final float A = 2.0f * ((right + left) * r_width);
    final float B = (top + bottom) * r_height;
    final float C = (far + near) * r_depth;
    final float D = 2.0f * (far * near * r_depth);
    m[offset + 0] = x;
    m[offset + 5] = y;
    m[offset + 8] = A;
    m[offset +  9] = B;
    m[offset + 10] = C;
    m[offset + 14] = D;
    m[offset + 11] = -1.0f;
    m[offset +  1] = 0.0f;
    m[offset +  2] = 0.0f;
    m[offset +  3] = 0.0f;
    m[offset +  4] = 0.0f;
    m[offset +  6] = 0.0f;
    m[offset +  7] = 0.0f;
    m[offset + 12] = 0.0f;
    m[offset + 13] = 0.0f;
    m[offset + 15] = 0.0f;
}