将相机旋转到一个点

Jam*_*ote 6 java opengl math

我正在制作一个使用整个行星来制作地图的游戏.我使用这种技术对球形星球进行了镶嵌,现在正在添加相机控制.

球体的尺寸为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;
    }
Run Code Online (Sandbox Code Playgroud)

然后,我比较每个图块的质心和相机位置之间的距离,并将最接近的图块作为新选择的图块.

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

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

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


}
Run Code Online (Sandbox Code Playgroud)

selected(瓷砖类)

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

}
Run Code Online (Sandbox Code Playgroud)

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

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

        gl.glRotatef(mRotateX, 1, 0, 0);
        gl.glRotatef(mRotateY, 0, 1, 0);
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

谢谢詹姆斯

Mik*_*ale 2

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

我从未使用过 glRotatef,但我对此处转换的顺序和符号有点困惑 - 例如,您是否真的首先按照 glRotatef 调用的顺序进行 x 旋转?

无论如何,这里的问题至少有一部分来自这些方程

outputArray[0] = Math.atan2(centroidZ, centroidY);
outputArray[1] = Math.atan2(centroidX, centroidZ);
Run Code Online (Sandbox Code Playgroud)

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

这些事物的正确公式总是有一个atan2代表一个角度和一个acosasin代表另一个角度。(1,0,0)例如,如果您想要将矢量携带到的主动旋转的角度,(x,y,z)您将使用

first_angle_around_x  = -asin(y)
second_angle_around_y = atan2(x, z)
Run Code Online (Sandbox Code Playgroud)

继续你会(x,y,z)使用(1,0,0)

first_angle_around_x  = atan2(y, z)
second_angle_around_y = -asin(x)
Run Code Online (Sandbox Code Playgroud)

(这些角度描述了绕 x,y 轴的旋转,当轴“戳你的眼睛”时,这些角度是逆时针旋转。)

另一个问题在这里

outputArray[0] -= Math.atan2(camZ, camY);
outputArray[1] -= Math.atan2(camX, camZ);
Run Code Online (Sandbox Code Playgroud)

像这样减去角度仅适用于绕单个轴的旋转。一旦您通过围绕不同轴的旋转构建复合变换,关系就会变得更加复杂 - 这就是矩阵和四元数派上用场的原因。我想如果 camX-Z 输入是多余的(如该方法之前的注释所建议的那样),此代码可能并不重要(尽管目前 Z 和 Y 分量在此处的方式将给出非零结果,可能是补偿它们在我提到的第一个方程中的“错误”方式;我不确定这是否是故意的)。

将相机移动到所选图块​​的方式也存在一个基本问题,尽管这是否真的是一个问题有点主观。因为您只绕 x 和 y 轴旋转,所以您星球上的每个点都有一个独特的相机方向。问题是,没有办法连续选择这样的方向 - 想象一下地球上的矢量场,其中每个点的矢量都是当相机位于该点上方时沿着屏幕 x 方向指向的单位矢量。根据毛球定理,该场不能连续。这实际上意味着,在地球上的某个点上,对相机位置进行看似微小的调整,就会使行星在屏幕上旋转最多 180 度。如果您想避免这种情况,您可以选择基于当前相机位置的方向,以最大限度地减少旋转量。例如,这确实意味着,如果摄像机以闭环方式移动,则行星最终可能会在屏幕上“滚动”,这在您的游戏中可能是不理想的。它还要求您的引擎能够在所有 3 个轴上旋转。