OpenGL移动物体并保持变换

4
我有一个对象,它被转换了(沿着 Y 轴旋转 45 度)。 目标是在 x 和 y 轴上移动(平移)该对象,并保持其旋转效果不变。 很难解释,所以我画了一张图:
desired result 我知道 OpenGL 中相机的概念,我知道我实际上不能移动相机,但事实上一切都是围绕相机移动的。有人知道如何实现吗?
我的代码:
//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();

2
请参考[理解4x4齐次变换矩阵](https://dev59.com/eOo6XIcBkEYKwwoYGwYw#28084380),这样您就可以将相机矩阵和模型矩阵分开...在渲染之前仅进行组合...请查看其中最后链接中的示例。顺便说一句,您的图像并不太有意义,因为所需结果与当前结果之间的差异不明显。您想沿着相机本地轴或对象本地轴或世界全局轴移动吗? - Spektre
你能给我们一些代码吗?展示一下你是如何应用不同的转换的。 - Paltoquet
@DraykoonD 我已经添加了一些代码。 - Met
你知道透视投影在物体移动时会改变它们的轮廓吗?如果不想出现这些变化,可以使用正交投影。 - Amadeus
1
你可以在投影矩阵中预先乘以一个平移矩阵,这样就可以在应用透视后将物体移动。从概念上讲,这将导致不对称的视锥体(这是投影仪所实现的镜头移位原理)。然而,如果场景中有多个对象,并且您想要为每个对象应用不同的变换(如果只是在x和y方向上进行平移,则仍将很好地工作,但如果还修改z,则会破坏深度测试),则必须小心处理。 - derhass
显示剩余3条评论
1个回答

4

您需要改变指令的顺序。通过将物体的平移矩阵乘以旋转矩阵,执行绕物体轴的旋转。

这意味着您需要先进行平移,然后再进行旋转。

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

请注意,翻译矩阵如下所示:
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轴的旋转矩阵如下:

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 ) 

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

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];
< p >
translate * rotate 的结果如下:

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 )

翻译 * 旋转


请注意,旋转 * 翻译 的结果将是:

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 )

旋转*平移


回答的扩展:

透视投影矩阵如下所示:

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

在哪里:

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)

如果你想通过偏移量(x, y)来改变视野,那么你需要按照以下方式进行操作:

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

按照以下方式设置透视投影矩阵:

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,可以通过像这样应用像素偏移:

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

非常感谢您详细的回答,但是这并没有给我想要的结果。它给了我一个不同的结果(显然),但不是我想要的结果。问题不在于操作的顺序,我认为问题在于投影矩阵。- 我需要以某种方式翻译视锥体,使得消失点不在[0,0]处,而是在翻译后的四边形中心的位置。 - Met
基本上你的扩展回答是有效的,你是一个数学魔术师。唯一的问题是:我如何使用像素单位(或者我通常在3D空间中移动对象的单位)来移动它?- 如果我用0.5移动它,它会移动很多。 - Met
哦耶,现在它正在工作了!你真的是个天才!不幸的是,我的“gluProject”方法没有识别到这一点,并返回值,就好像投影矩阵没有移动一样。有没有快速简单的解决方法,或者唯一的解决方法是将xy偏移值添加到返回的顶点中?-“gluProject”方法是我自己编写的:https://i.imgur.com/YafMMgO.png - Met

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接