【问题标题】:Translation after rotation旋转后平移
【发布时间】:2014-03-16 23:04:59
【问题描述】:

我正在使用适用于 Android 的 OpenGL ES 2.0。我正在使用触摸屏平移和旋转模型。我的平移仅在 (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 的建议进行第二次翻译,但我不知道如何计算第二次翻译。还有其他方法吗?

【问题讨论】:

    标签: android opengl-es rotation opengl-es-2.0


    【解决方案1】:

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

    旋转后需要平移以使旋转点保持在屏幕中心。 就像 CAD 类型的应用程序一样。 问题是鼠标平移在旋转后并不总是与屏幕平行。

    我找到了修复here

    (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;
        }
    

    【讨论】:

      【解决方案2】:

      这是一篇旧帖子,但我发布的解决方案对我的后代最有效。

      解决方案是保留一个单独的模型矩阵,该矩阵在发生变换时累积变换,并在 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);
      

      然后用累加的矩阵来定位物体。

      【讨论】:

        【解决方案3】:

        以下是我对平移和旋转的看法:您不是在移动对象,而是在移动坐标系的原点。以这种方式考虑,您需要对您的第一个行为进行额外的翻译。

        手指运动是一种平移,应该与屏幕的 XY 轴对齐,因此正如您所计算的,应该在旋转之前完成。然后您的旋转发生,它围绕该点旋转对象的坐标系。如果您希望将对象绘制到相对于该点的其他位置,则需要先进行另一次平移以将原点移动到那里。

        所以我认为你的最终序列应该是这样的

        翻译(dx, dy) ;旋转(一);翻译(cx,cy);绘制()

        其中 cx 和 cy 是地图中心与被观察点之间的距离。 (可能简化为 -dx、-dy)

        希望这会有所帮助。

        【讨论】:

        • 谢谢!我绝对觉得我现在走在正确的轨道上。我相信我需要第二次翻译,但似乎简单地反转第一次翻译是行不通的。我需要翻译才能将我想要查看的点返回到相机正在查看的位置。我正在尝试计算第二次翻译的向量,但它变得非常复杂。有没有一种简单的方法可以找到 cx 和 cy 应该是什么?
        • 很大程度上取决于您用于地图和屏幕的坐标系,我无法准确回答这个问题。 (另外,我很懒惰。)我确实建议找一个长方形的房间,然后假装你是地图。说真的,物理上的转换可以帮助许多程序员。
        【解决方案4】:

        您应该使用第一种方法,尽管从数学上讲第二种方法更有意义。 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)。它们之间有什么区别?我想要旋转的点与我想要将对象平移到的点相同。
        • x, y, z 可以是任何东西。那你是什么意思,当你说:“问题是旋转是围绕对象的中心,我需要旋转围绕我正在看的点,这与对象的中心不同。” ?如果你想绕着你想翻译的同一点旋转,那么你就是绕着中心旋转,不是吗?
        • 最初,对象的中心对应于世界空间中的原点,但随后我进行了平移,对象的中心不再位于原点。我的意思是对象围绕其局部中心(在模型空间中)旋转,而不是围绕原点旋转。我需要它在世界空间中围绕原点旋转,同时让未来的翻译与屏幕坐标对齐。
        • 嗯,看起来您也在旋转相机或投影矩阵。您应该总共有 3 个矩阵:mvp:模型、视图、投影。最终的矩阵是:Projection * View * Model。
        • 好的,我更改了我的着色器,以便所有最终的矩阵乘法都在那里完成:(gl_Position = u_ProjMatrix * u_ViewMatrix * u_ModelMatrix * a_Position; 在对象旋转后我仍然无法正确平移。它朝错误的方向翻译。
        猜你喜欢
        • 1970-01-01
        • 2021-07-23
        • 1970-01-01
        • 2013-09-27
        • 2013-05-23
        • 2011-01-04
        • 2014-03-22
        • 2019-07-30
        相关资源
        最近更新 更多