4

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();
Met
  • 103
  • 1
  • 1
  • 7
  • 2
    see [Understanding 4x4 homogenous transform matrices](https://stackoverflow.com/a/28084380/2521214) so you can have camera matrix and model matrix separate ... and combine only before rendering ... take a look at the examples in the last links in there. btw your images does not make much sense as the difference between desired and current result is not obvious. You want to move along camera local axises or object local axises or world global axises ? – Spektre Oct 09 '17 at 10:38
  • Can you give us some code, how are you applying your different transformations. – Paltoquet Oct 09 '17 at 11:06
  • @DraykoonD I've added some code – Met Oct 09 '17 at 11:53
  • Do you know that perspective projection changes objects silhouettes when they move around, don't you? If you do not want those changes, use an orthographic projection – Amadeus Oct 09 '17 at 13:15
  • @Amadeus I know, the thing is, I want to keep this look of perspective. – Met Oct 09 '17 at 13:44
  • 1
    You can pre-multiply a translation matrix to the projection matrix, resulting in the the effect of moving the object around after perspective was applied. Conceptually, this will result in an asymmetric frustum (and is the same principle that the lens shift implements for projectors). However, you must be careful if there is more than one object in your scene, and you want apply different such transformations to each (it should still work nicely if you only translate in x and y, but will screw up depth test if you also modify z). – derhass Oct 09 '17 at 15:52
  • @derhass Thank you for your suggestion. I've tried it and unfortunately it doesnt give me the desired result. The result is very similar as the current result but it seems like the perspective effect is a bit stronger. – Met Oct 09 '17 at 18:49
  • Maybe you did it wrong. You will have to work in clip space, meaning the units will be completely different. – derhass Oct 09 '17 at 19:14

1 Answers1

4

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);
Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • Thank you for your very detailed answer, but this doesn't give me the desired result. It gives me a different result (obviously) but not the desired one. The problem is not the order of the oprations, I think the problem is the projection matrix. - I need to translate the frustum somehow, so the point of vanishing is not at [0, 0] but there where the center of the translated quad is. – Met Oct 09 '17 at 18:43
  • Basically your extended answer is working, youre a math magican. The only problem is: how do i move it with pixel units (or the units I usually move my objects in 3D space)? - If i move it with 0.5, its moves really a lot. – Met Oct 09 '17 at 20:13
  • Oh yeah, it is working now!! You are really a beast! Unfortunately my "gluProject" method is not recognising this and returns the values as if the projection matrix wouldn't be shifted. Is there a quick and simple fix to this, or is the only fix to add the xy shift values to the returned vertex? - The "gluProject" method is written by myself: https://i.imgur.com/YafMMgO.png – Met Oct 09 '17 at 21:35