2013-02-16 53 views
6

我正在制作一个使用整个星球作为其地图的游戏。我已经镶嵌了球形星球using this technique,现在我正在添加相机控件。将相机旋转到一个点

球体的维数为1至-1,因此球体上的每个点也是一个归一化的向量。在任何时候,构成球体的六边形瓷砖之一是“选定”瓷砖。玩家然后可以使用d-pad将选择移动到邻近的瓷砖。他们也可以使用模拟棒独立旋转摄像头。我需要做两件事情来关联选定的贴图和相机。首先,我需要能够将选择切换到距离相机最近的图块。其次,我需要将相机置于高亮显示的图块上

球体位于原点,相机位于点(0,0,1)处。图形引擎只允许我围绕X轴和Y轴旋转相机,因此为了解决第一个问题,我使用四元数来旋转x轴和y轴上的点(0,0,1)以找到点三维空间,其中照相机是:

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),然后在(0,0,1)和质心之间获取X轴和Y轴的角度,并使用它再次旋转相机:

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

这一直困扰着我很多天,所以任何帮助获得这个固定将非常感激!

感谢 詹姆斯

回答

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的参数)。或者,关于y轴的右边标志的旋转可以将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) 

开展(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个轴上进行旋转。

+0

有趣,谢谢!我理解随着转身,但我从来没有意识到它有一个名字(更不用说,像毛球定理那样热闹起来)。这种转动行为在我玩过的电脑游戏中并不少见,而且在玩我的游戏时可能仅仅是一个小小的图形烦恼。听起来像是我必须暂时放下对焦/旋转相机到质心 – 2013-02-18 17:39:44