将相机旋转到一个点

时间:2013-02-16 15:29:36

标签: java math opengl

我正在制作一款使用整个星球来制作地图的游戏。我已经对球形行星using this technique进行了细分,现在正在添加相机控件。

球体的尺寸为1到-1,因此球体上的每个点也是标准化矢量。在任何时候,构成球体的六边形瓷砖之一是“选定的”瓷砖。然后,玩家可以使用d-pad将选择移动到相邻的区块。他们还可以使用模拟棒独立旋转相机

我需要做两件与所选瓷砖和相机有关的事情。首先,我需要能够将选择切换到最靠近相机的图块。其次,我需要将相机置于高亮显示的瓷砖上

球体位于原点,相机位于点(0,0,1)。图形引擎只允许我围绕X轴和Y轴旋转摄像机,所以为了解决第一个问题,我使用四元数旋转x(x)周围的点(0,0,1),然后找到y轴。相机所在的3D空间:

    private Quaternion quat = new Quaternion(0,0,0,0);
    private double[] output = new double[4];
    private double[] cam = new double[3];

    private double camX = 0;
    private double camY = 0;
    private double camZ = 1;


    private double[] getCamPosition(){

        quat.setAxisAngle(1, 0, 0, Math.toRadians(-graphicsEngine.getRotationX()));
        quat.RotateVector(camX, camY, camZ, output);            
        cam[0] = output[0];
        cam[1] = output[1];
        cam[2] = output[2];   

        quat.setAxisAngle(0, 1, 0, Math.toRadians(-graphicsEngine.getRotationY()));
        quat.RotateVector(cam[0], cam[1], cam[2], output);          
        cam[0] = output[0];
        cam[1] = output[1];
        cam[2] = output[2];

        return cam;
    }
然后,我比较每个图块的质心与相机位置之间的距离,并将最接近的图块作为新选择的图块。

但是,要解决第二个问题,我想反过来。我想取质心(已经是标准化矢量的形式),并找出围绕X的旋转和绕Y所需的旋转以使相机居中于其上

此刻,我正在将相机旋转回(0,0,1),然后在X轴和Y轴之间获得角度(0,0,1)和质心并使用它旋转相机第二次:

private double[] outputArray = new double[2];

/**
 * Cam is always (0,0,1)
 */
public void centreOnSelected(double camX, double camY, double camZ){        

    selectedTile.getCentroidAngles(outputArray);

    outputArray[0] -= Math.atan2(camZ, camY);
    outputArray[1] -= Math.atan2(camX, camZ);

    // this determines if the centroid is pointing away from the camera
    // I.e. is on the far side of the sphere to the camera point (0,0,1)
    if(!selected.getCentroidDirectionY(camX, camZ)){
        outputArray[0] = -Math.PI - outputArray[0];         
    }

    graphicsEngine.rotateCam(Math.toDegrees(outputArray[0]), Math.toDegrees(outputArray[1]));


}

selected(图块类)

void getCentroidAngles(double[] outputArray){
    outputArray[0] = Math.atan2(centroidZ, centroidY);
    outputArray[1] = Math.atan2(centroidX, centroidZ);

}

问题是这不起作用,(x轴似乎总是出来),我很确定它是用于获取角度和旋转的数学

注意:图形引擎首先围绕X轴旋转,然后围绕Y:

旋转
        gl.glRotatef(mRotateX, 1, 0, 0);
        gl.glRotatef(mRotateY, 0, 1, 0);

质心都在正确的位置,相机肯定会以正确的数量旋转,所以我确定问题不在于图形引擎。这也不是将相机重新定位回(0,0,1),因为我通过逐步完成程序检查了这个功能

我还制作了一个视频来说明问题:

http://www.youtube.com/watch?v=Uvka7ifZMlE

这已经困扰了我好几天了,所以任何帮助我解决这个问题都会非常感激!

由于 詹姆斯

1 个答案:

答案 0 :(得分:2)

不幸的是,我并不完全理解这里发生了什么,也无法提供完整的解决方案,但至少可以指出一些问题需要注意。

我从未使用过glRotatef,但我对这里的转换顺序和符号感到有点困惑 - 例如,你是否真的按照glRotatef调用的顺序进行x旋转?

无论如何,至少部分问题来自这些方程式

outputArray[0] = Math.atan2(centroidZ, centroidY);
outputArray[1] = Math.atan2(centroidX, centroidZ);

首先,你的意思是第一个(centroidY, centroidZ)吗?更严重的是,你在这里获得的角度不能用于建立一个携带(0,0,1)到你的质心的旋转,反之亦然。例如,假设您要将质心向量旋转到(0,0,1)。您可以单独使用2个旋转中的每个旋转来围绕单个轴旋转,将一个组件设置为零。例如,围绕适当符号的x轴旋转outputArray[0]会将y分量设置为零(假设atan2的参数被交换)。或者,通过outputArray[1]关于y轴的右侧符号旋转可以将x分量设置为0.但是在首先进行x旋转(比如说)以将y分量设置为0之后,质心向量发生变化 - 现在旋转outputArray[1]不再描述将x分量设置为0的y轴周围。

这些内容的正确公式对于一个角度始终为atan2,对另一个角度为acosasin。例如,如果您想要将向量(1,0,0)带到(x,y,z)的有效旋转的角度,则可以使用

first_angle_around_x  = -asin(y)
second_angle_around_y = atan2(x, z)

要将(x,y,z)带到(1,0,0),您可以使用

first_angle_around_x  = atan2(y, z)
second_angle_around_y = -asin(x)

(这些角度描述了x轴和y轴周围的旋转,当轴“刺入你的眼睛”时,它们是逆时针方向。)

另一个问题在这里

outputArray[0] -= Math.atan2(camZ, camY);
outputArray[1] -= Math.atan2(camX, camZ);

这样减去角度仅适用于围绕单个轴的旋转。一旦你围绕不同轴旋转构建复合变换,关系变得更加复杂 - 这就是矩阵和四元数派上用场的原因。我猜这个代码可能无关紧要,如果camX-Z输入是冗余的,正如方法前面的注释所建议的那样(虽然目前Z和Y分量的方式是圆形的,它们会在这里给出一个非零结果,可能是在我提到的第一个等式中补偿它们是“错误”的方式;我不确定这是否是有意的。)

您将相机移动到所选图块​​的方式也存在一个根本问题,尽管它实际上是一个问题有点主观。因为您只围绕x和y轴进行旋转,所以对于地球上的每个点都有一个独特的摄像头方向。问题在于没有办法连续选择这样的方向 - 看这个想象一个行星上的矢量场,其中每个点处的矢量是一个单位矢量,当摄像机高于该点时,该矢量指向屏幕x方向。 Hairy Ball Theorem无法连续显示此字段。这在实践中的意义在于,在这个星球上会有一些点,在这个位置上,对相机位置进行小幅度调整会使行星在屏幕上旋转180度。如果您想避免这种情况,您可以选择当前相机位置的方向,以最大限度地减少旋转量。这确实意味着,例如,如果相机在闭环中移动,行星最终会在屏幕上“滚动”,这可能是您游戏中不可取的。它还要求您的引擎能够在所有3个轴上进行旋转。