关于矩阵:OpenGL移动对象并保持转换

OpenGL move object and keep transformation

我有一个物体,它被转换了(在Y轴上旋转45度)。目标是在X轴和Y轴上移动(平移)对象,并保持变换效果不变。很难解释,所以我拍了张照片:
desired result

我知道OpenGL中相机的概念,我知道我不能真正移动相机,但事实上,所有东西都在相机周围移动。有人真的知道如何做到这一点吗?

我的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//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();


你必须改变指令的顺序。通过将对象的平移矩阵乘以旋转矩阵,围绕对象的轴执行旋转。这意味着您必须先进行平移,然后进行旋转。

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

注意,翻译矩阵如下:

1
2
3
4
5
6
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 )

围绕y轴的旋转矩阵如下:

1
2
3
4
5
6
7
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 )

矩阵乘法的工作原理如下:

1
2
3
4
5
6
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];

< BR>translate * rotate的结果是:

1
2
3
4
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

< BR>注:rotate * translate的结果为:

1
2
3
4
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

< BR>

答案的扩展:

透视投影矩阵如下:

1
2
3
4
5
6
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

在哪里?

1
2
3
4
5
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)

如果要用偏移量(xy替换视场,则必须这样做:

1
2
3
4
5
6
7
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

设置透视投影矩阵如下:

1
2
3
4
5
6
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);

对于glFrustum而言,像素偏移可以这样应用:

1
2
3
4
5
6
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);