旋转后的翻译

5
我正在使用OpenGL ES 2.0进行Android开发,通过触摸屏幕实现模型的平移和旋转。我的平移只在(x,y)平面内进行,旋转只围绕z轴进行。可以想象一下,就像俯视桌子上的地图,移动到地图上的各个坐标,并能够围绕您所看到的点旋转地图。
问题是,在旋转后,我的后续平移与屏幕指针的运动不再匹配,轴也不同了。
我尝试过的所有方法都会出现以下两种行为之一:
Matrix.setIdentityM(mModelMatrix, 0);
Matrix.translateM(mModelMatrix, 0, Xposition, Yposition, 0.0f);
Matrix.rotateM(mModelMatrix, 0, rotationAngle, 0.0f, 0.0f, 1.0f);

这使得我可以按预期进行翻译(屏幕上下移动模型,左右移动模型左右),无论旋转如何。问题是旋转是围绕对象中心进行的,而我需要旋转是围绕我正在查看的点进行的,这与对象的中心不同。

我可以获得的其他行为等同于:

Matrix.setIdentityM(mModelMatrix, 0);
Matrix.rotateM(mModelMatrix, 0, rotationAngle, 0.0f, 0.0f, 1.0f);
Matrix.translateM(mModelMatrix, 0, Xposition, Yposition, 0.0f);

这使我得到了想要的旋转,总是围绕着我所看的点旋转。问题在于旋转后,翻译是错误的。屏幕上的左/右会以不同的角度将对象翻译,沿着旋转的轴线。
我需要一种同时获得这两个行为的方法。它需要围绕我所看的点旋转并且沿着手指在屏幕上移动的方向进行翻译。
这是否可能?我基本上是试图调和量子力学与牛顿物理学,注定要失败吗?
我不想列出我尝试过的所有技巧,因为我想用新的视角考虑所有可能性。
编辑: 我仍然完全卡住了。
我有一个在世界坐标系中从(0,0,0)开始的对象。我的视图正在沿着z轴朝向该对象,并且我希望将翻译限制在x/y平面内。我还想仅围绕z轴旋转对象。旋转的中心必须始终是屏幕的中心。
我正在使用触摸屏控制翻译,因此无论如何旋转,都需要将对象移动与手指相同的方式,而不管它旋转的方式如何。
一旦我旋转,那么所有的翻译都开始在旋转的坐标系上发生,这意味着对象不随屏幕上的指针移动。我尝试了像Hugh Fisher建议的第二个翻译,但我无法计算第二个翻译。还有其他方法吗?
4个回答

1

我也遇到了同样的问题。不过我使用的是C#和OpenGL(SharpGL),并且使用了旋转矩阵。

在需要保持旋转点在屏幕中心的情况下,需要对旋转后的翻译进行处理。就像CAD类型的应用程序一样。问题是,在旋转后,鼠标移动不总是与屏幕平行。

我在这里找到了一个解决方法。

(Xposition, Yposition) = (Xposition, Yposition) + mRotation.transposed() * (XIncr, YIncr)

或者

NewTranslationPosition = oldTranslationPosition + rotationMatrix.Transposed * UserTranslationIncrement.

非常感谢reto.koradi(在OpenGL中)!
所以我大致编写了以下3D代码:
double gXposition = 0;
double gYposition = 0;
double gZposition = 0;

double gXincr = 0;
double gYincr = 0;
double gZincr = 0;

float[] rotMatrix = new float[16]; //Rotational matrix

private void openGLControl_OpenGLDraw(object sender, PaintEventArgs e)
{

  OpenGL gl = openGLControl.OpenGL;

  gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT);

  gl.LoadIdentity();
  gl.MultMatrix(rotMatrix); //This is my rotation, using a rotation matrix
  gl.Translate(gXposition, gYposition, gZposition); //translate second to keep rotation at center of screen

  DrawCube(ref  gl);

}

private void buttonTransLeft_Click(object sender, EventArgs e)
{
        double tX = -0.1;
        double tY = 0;
        double tZ = 0;

        TransposeRotMatrixFindPoint(ref tX, ref tY, ref tZ);

        gXposition = gXposition + tX;
        gYposition = gYposition + tY;
        gZposition = gZposition + tZ;

 }

 private void buttonTransRight_Click(object sender, EventArgs e)
 {

        double tX = 0.1;
        double tY = 0;
        double tZ = 0;

        TransposeRotMatrixFindPoint(ref tX, ref tY, ref tZ);


        gXposition = gXposition + tX;
        gYposition = gYposition + tY;
        gZposition = gZposition + tZ;

  }

public void TransposeRotMatrixFindPoint(ref double x, ref double y, ref double z)
    {
        //Multiply [x,y,z] by Transpose Rotation matrix to generate new [x,y,z]
        double Xt = 0; //Tempoary variable
        double Yt = 0; //Tempoary variable
        Xt = (x * rotMatrix[0, 0]) + (y * rotMatrix[0, 1]) + (z * rotMatrix[0, 2]);
        Yt = (x * rotMatrix[1, 0]) + (y * rotMatrix[1, 1]) + (z * rotMatrix[1, 2]);
        z = (x * rotMatrix[2, 0]) + (y * rotMatrix[2, 1]) + (z * rotMatrix[2, 2]);

        //or try this 
        //Xt = (x * rotMatrix[0, 0]) + (y * rotMatrix[1, 0]) + (z * rotMatrix[2, 0]);
        //Yt = (x * rotMatrix[0, 1]) + (y * rotMatrix[1, 1]) + (z * rotMatrix[2, 1]);
        //z = (x * rotMatrix[0, 2]) + (y * rotMatrix[1, 2]) + (z * rotMatrix[2, 2]);


        x = Xt;
        y = Yt;
    }

1
这是一篇旧帖,但我为了记录下来,发布了对我最有效的解决方案。
解决方案是保留一个单独的模型矩阵来累加转换,并在onDrawFrame()方法中将每个转换乘以此矩阵。
//Initialize the model matrix for the current transformation
Matrix.setIdentityM(mModelMatrixCurrent, 0);
//Apply the current transformations
Matrix.translateM(mModelMatrixCurrent, 0, cameraX, cameraY, cameraZ);
Matrix.rotateM(mModelMatrixCurrent, 0, mAngle, 0.0f, 0.0f, 1.0f);
//Multiply the accumulated transformations by the current transformations
Matrix.multiplyMM(mTempMatrix, 0, mModelMatrixCurrent, 0, mModelMatrixAccumulated, 0);
System.arraycopy(mTempMatrix, 0, mModelMatrixAccumulated, 0, 16);

然后使用累积矩阵来定位对象。

0

你应该使用第一种方法,尽管从数学上讲第二种方法更有意义。OpenGL和Android存储矩阵的方式是不同的。它们毕竟是数组,但前4个值是行还是列呢?

这就是为什么它是“反向”的原因。查看this获取更多信息,或者阅读关于行主序与列主序矩阵操作的内容。

我注意到第一种方法“反向”可以正常工作。

从数学上讲:

假设您想围绕点(x1,y1,z1)旋转。您对象的原点是(Ox,Oy,Oz)。

设置原点:

Matrix.setIdentityM(mModelMatrix, 0);

然后将您想要围绕旋转的点移动到原点:

Matrix.translateM(mModelMatrix, 0, -x1, -y1, -z1);

然后旋转矩阵:Matrix.rotateM(mModelMatrix, 0, rotationAngle, 0.0f, 0.0f, 1.0f);

然后将其移回:

Matrix.translateM(mModelMatrix, 0, x1, y1, z1);

然后将其移动到您想要的位置:

Matrix.translateM(mModelMatrix, 0, x, y, z);

然而,在反向思考时,你需要按相反的顺序进行。

尝试: 设置原点:

Matrix.setIdentityM(mModelMatrix, 0);

然后按相反的顺序做一些事情:

Matrix.translateM(mModelMatrix, 0, x, y, z);

Matrix.translateM(mModelMatrix, 0, x1, y1, z1);

Matrix.rotateM(mModelMatrix, 0, rotationAngle, 0.0f, 0.0f, 1.0f);

Matrix.translateM(mModelMatrix, 0, -x1, -y1, -z1);

我希望这可以帮助到你。

编辑

我可能误解了问题:对我有效的是:

Matrix.setIdentityM(mModelMatrix, 0);

Matrix.translateM(mModelMatrix, 0, x1, y1, z1);

Matrix.rotateM(mModelMatrix, 0, rotationAngle, 0.0f, 0.0f, 1.0f);

Matrix.Multiply(mViewProjection, 0, mProjection, 0, mCameraView, 0);  //something like this, but do you have the right order?

在我的着色器中,我有mViewProjection * mModelMatrix * a_Position;
你是使用顶点着色器进行最终乘法运算吗?
尝试使用静态的平移/旋转(使用常量值)而不是通过触摸屏控制平移。如果它能正常工作,那么可能是其他地方出现了错误。

所以,您正在引用两个不同的点,(x, y, z) 和 (x1, y1, z1)。它们之间有什么区别?我想要围绕的旋转点与我想要对象平移的点相同。 - Jason Powell
x,y,z可以是任何值。那么当你说:“问题在于旋转是关于对象的中心,而我需要旋转是关于我所看到的点,这与对象的中心不同。” 时,你的意思是什么?如果您想围绕相同的点旋转并想要进行翻译,则是围绕中心旋转,不是吗? - Makketronix
最初,物体的中心与世界空间的原点相对应,但是然后我进行移动,物体的中心就不再位于原点处了。我的意思是,该对象围绕其局部中心(在模型空间中)旋转,而不是围绕原点旋转。我需要它围绕世界空间中的原点旋转,并同时允许将来的平移与屏幕坐标对齐。 - Jason Powell
好的,我已经更改了我的着色器,使得所有最终矩阵乘法都在那里完成:(gl_Position = u_ProjMatrix * u_ViewMatrix * u_ModelMatrix * a_Position; 但是,在对象旋转后,我仍然无法正确地进行平移。它朝错误的方向平移。 - Jason Powell
不行。如果我反转翻译,那么我的翻译只是被反转了,也就是说,物体移动的方向相反。每次我尝试翻译、旋转,然后再回到起点的技巧,最终都会导致物体卡在中心位置,因为我所做的任何平移都被取消了。我是做错了吗?我仍然不知道你帖子中 (x, y, z) 和 (x1, y1, z1) 的区别。(x, y, z) 是我想要平移的位置吗?那么 (x1, y1, z1) 又是什么呢? - Jason Powell
显示剩余3条评论

0
这是我对翻译和旋转的理解:你不是在移动物体,而是在移动坐标系的原点。以这种方式思考,您将需要在第一个行为上进行额外的平移。
手指动作是一种平移,应该与屏幕的XY轴对齐,因此正如您所了解的,应该在旋转之前完成。然后进行旋转,这会使对象的坐标系围绕该点旋转。如果您希望相对于该点在其他位置绘制对象,则需要首先进行另一个平移以将原点移动到那里。
因此,我认为您的最终序列应该类似于
translate(dx, dy) ; rotate(A) ; translate(cx, cy) ; draw()
其中cx和cy是地图中心与正在查看的点之间的距离。(可能简化为-dx,-dy)
希望这可以帮助您。

谢谢!我绝对感觉现在走在正确的轨道上了。我相信我需要第二次翻译,但似乎简单地反转第一次翻译是行不通的。我需要一种翻译方法,将我想要查看的点返回到摄像机所在的位置。我正在尝试计算第二次翻译的向量,但它变得非常复杂。有没有简单的方法来找出cx和cy应该是什么? - Jason Powell
太依赖于你用于地图和屏幕的坐标系,以至于我无法精确回答这个问题。(而且,我很懒。)我建议找一个矩形房间,在里面走来走去,假装自己是地图。说真的,通过身体动作模拟转换可以帮助许多程序员。 - Hugh Fisher

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