Search code examples
openglmatrix3dcameraprojection

OpenGL move object and keep transformation


I've a object, which is transfomred (rotated at 45deg on the Y axis). The target is to move (translate) the object on the x and y axis and keep the transformation effect as it is. Its very hard to explain, so I made a picture:
desired result

I know the concept of the camera in opengl and i know i cant really move the camera but in fact everything is moving around the camera. Does someone actually know how to achieve this?

My code:

//set mvp
matrixProj = new PerspectiveProjectionMatrix(fovy, aspect, near, far);
matrixView = new ModelMatrix();
matrixView.LookAtTarget(new Vertex3f(0, 0, 2), new Vertex3f(0, 0, 0), new Vertex3f(0, 1, 0));
matrixModel = new ModelMatrix();
matrixModel.SetIdentity();
matrixModel.RotateY(45);
matrixModel.Translate(-2, -2, 0);
Matrix4x4 mvp = matrixProj * matrixView * matrixModel;
Gl.UniformMatrix4(Gl.GetUniformLocation(shaderProgram, "MVP"), 1, false, mvp.ToArray());

//draw quad
Gl.Begin(PrimitiveType.Quads);
Gl.Vertex3(-2, 2, 0);
Gl.Vertex3(2, 2, 0);
Gl.Vertex3(2, -2, 0);
Gl.Vertex3(-2, -2, 0);
Gl.End();

Solution

  • You have to change the order of the instructions. A rotation around the axis of the object is performed, by multiplying the translation matrix of the object by the rotation matrix. This means you have to do the translation first and then the rotation.

    matrixModel = new ModelMatrix();
    matrixModel.SetIdentity();
    matrixModel.Translate(-2, -2, 0);
    matrixModel.RotateY(45);
    

    Note, the translation matrix looks like this:

    Matrix4x4 translate;
    
    translate[0] : ( 1,  0,  0,  0 )
    translate[1] : ( 0,  1,  0,  0 )
    translate[2] : ( 0,  0,  1,  0 )
    translate[3] : ( tx, ty, tz, 1 )
    

    And the rotation matrix around Y-Axis looks like this:

    Matrix4x4  rotate;
    float      angle;
    
    rotate[0] : ( cos(angle),  0, sin(angle), 0 )
    rotate[1] : ( 0,           1, 0,          0 )
    rotate[2] : ( -sin(angle), 0, cos(angle), 0 )
    rotate[3] : ( 0,           0, 0,          1 ) 
    

    A matrix multiplication works like this:

    Matrix4x4 A, B, C;
    
    // C = A * B
    for ( int k = 0; k < 4; ++ k )
        for ( int l = 0; l < 4; ++ l )
            C[k][l] = A[0][l] * B[k][0] + A[1][l] * B[k][1] + A[2][l] * B[k][2] +  A[3][l] * B[k][3];
    


    The result of translate * rotate is this:

    model[0] : ( cos(angle),  0,  sin(angle), 0 )
    model[1] : ( 0,           1,  0,          0 )
    model[2] : ( -sin(angle), 0,  cos(angle), 0 )
    model[3] : ( tx,          ty, tz,         1 )
    

    translate * rotate


    Note, the result of rotate * translate would be:

    model[0] : ( cos(angle),                     0,   sin(angle),                     0 )
    model[1] : ( 0,                              1,   0,                              0 )
    model[2] : ( -sin(angle),                    0,   cos(angle),                     0 )
    model[3] : ( cos(angle)*tx - sin(angle)*tx,  ty,  sin(angle)*tz + cos(angle)*tz,  1 )
    

    rotate * translate


    Extension to the answer:

    A perspective projection matrix looks like this:

    r = right, l = left, b = bottom, t = top, n = near, f = far
    
    2*n/(r-l)      0              0                0
    0              2*n/(t-b)      0                0
    (r+l)/(r-l)    (t+b)/(t-b)    -(f+n)/(f-n)    -1    
    0              0              -2*f*n/(f-n)     0
    

    where :

    r  = w / h
    ta = tan( fov_y / 2 );
    
    2*n / (r-l) = 1 / (ta*a)    --->  1/(r-l) = 1/(ta*a) * 1/(2*n)
    2*n / (t-b) = 1 / ta        --->  1/(t-b) = 1/ta * 1/(2*n)
    

    If you want to displace the filed of view by an offset (x, y), then you have to do it like this:

    x_disp = 1/(ta*a) * x/(2*n)
    y_disp = 1/ta * y/(2*n)
    
    1/(ta*a)  0       0               0
    0         1/t     0               0
    x_disp    y_disp  -(f+n)/(f-n)   -1    
    0         0       - 2*f*n/(f-n)   0
    

    Set up the perspective projection matrix like this:

    float x = ...;
    float y = ...;
    
    matrixProj = new PerspectiveProjectionMatrix(fovy, aspect, near, far);
    matrixProj[2][0] = x * matrixProj[0][0] / (2.0 * near); 
    matrixProj[2][1] = y * matrixProj[1][1] / (2.0 * near); 
    

    To glFrustum, a pixel offset, can be applied like this:

    float x_pixel = .....;
    float y_pixel = .....;
    
    float x_dipl = (right - left) * x_pixel / width_pixel;
    float y_dipl = (top - bottom) * y_pixel / height_pixel;
    glFrustum( left + x_dipl, right + x_dipl, top + y_dipl, bottom + y_dipl, near, far);