球体的任意旋转


12

我正在编写一种机制,允许用户在球体表面周围移动。球面上的位置当前存储为thetaphi,其中theta是当前位置的z轴和xz投影之间的角度(即围绕y轴的旋转),并且phi是从y轴到该位置的角度。我解释得很差,但实际上theta = yawphi = pitch

Vector3 position = new Vector3(0,0,1);
position.X = (float)Math.Sin(phi) * (float)Math.Sin(theta);
position.Y = (float)Math.Sin(phi) * (float)Math.Cos(theta);
position.Z = (float)Math.Cos(phi);
position *= r;

我相信这是正确的,但是我可能是错的。我需要能够在半径为的世界空间的起点处围绕球体表面在任意伪二维方向上移动r。例如,握持区W应相对于玩家的方向在球体上方向上移动。

我相信我应该使用四元数来表示球体上的位置/方向,但是我想不出正确的方法。球形几何不是我的强项。

本质上,我需要填写以下内容:

public void Move(Direction dir)
{   
    switch (dir)
    {
        case Direction.Left:
            // update quaternion to rotate left
            break;
        case Direction.Right:   
            // update quaternion to rotate right
            break;
        case Direction.Up:
            // update quaternion to rotate upward
            break;
        case Direction.Down:
            // update quaternion to rotate downward
            break;
    }
}

如果玩家到达杆位会怎样?我注意到您写了“向上方向”,您的字面意思是“向上”(即远离球体表面),“笔直向前”还是“朝北极”(如果玩家无法更改它们的方向,并且屏幕上的“在它们前面”或“向上”始终是北方)?
马丁·索卡

也许措辞不佳。运动员不应离开球体表面,也不应注意基轴。因此,当您“向上”移动时,您将沿球体表面相对于播放器的方向向上移动。例如,如果您在(r,0,0)处并向上按,则将朝着z +极点前进,但如果继续前进,则应环绕并继续前进。
azz

还有一个问题:玩家可以改变方向(旋转“左”和“右”)吗?
马丁·索伊卡

也许是我想要的一个更好的例子:(1,1,1)握住左手的玩家将围绕球体旋转,经过(~1.2,0,~-1.2),然后(-1,-1,-1),然后再(~-1.2,0,~1.2)返回(1,1,1)
azz

1
如果您打算随时跟踪thetaphi更新您的职位,那么会使您的问题变得不必要地复杂。简单地计算每帧(Vector3.Transorm围绕其中一个(偏航)不变)的2个旋转轴会容易得多。这将简化您的问题,但会导致您与phi&断开连接theta
史蒂夫·H

Answers:


5

实际上,事实证明,您不能“双向”使用它:如果您的意图是在球体上没有任何“绝对定向”感(也就是说,如果玩家并不总是例如面对杆位) ),那么您需要具有玩家取向的概念。这是因为,与直觉可能暗示的相反,球体上的运动并不完全像飞机上的运动,甚至完全是局部(完全)。球面的固有曲率意味着玩家可以采取会自行旋转的动作!

对于我正在谈论的最极端的示例,假设玩家在赤道上的某个点开始(为方便起见,我们将想象一个钟面从上方映射到赤道上,并将玩家置于6点钟位置)。 ),朝上-也就是朝北极。假设玩家一直走到北极;那么他们将直接面对12点。现在,让玩家直接从北极向右移至赤道;他们会在三点钟结束-但是因为当他们向右移动时他们的脸不会改变(他们的想法是无论他们如何移动他们的朝向都不会改变),他们仍将面对12点钟-他们现在正沿着赤道面对!现在,让他们“后退”回到起点(6点);然后他们仍将面对赤道,因此他们将面对3点钟-沿球体移动而没有改变他们的“个人”方位已导致他们从面向北极旋转到面对赤道!从某种意义上讲,这是对老“猎人向南移动一英里,向西移动一英里,然后向北移动一英里的笑话”的详细说明-但在这里,我们利用球体的曲率来改变方向。请注意,即使在较小的比例尺上,仍然会发生相同的效果。

幸运的是,四元数确实可以(如您所指出的那样)处理这种情况。由于四元数表示任意旋转,因此它有效地表示球体上的任意“点加方向”:想象从原点的“三轴”开始并使其任意旋转,然后沿旋转轴的任意方向移动一个单位” Z轴点;稍加思考,就会说服您,这使您以某种“方向”(即,三轴的X轴和Y轴的某种排列)将其带到单位球面上的一个点,并且您可以到达该点上的每个点+方向。以这种方式单位球面(只需将Z轴分配为沿着从原点到球体上的点的直线指向的点,然后将三轴沿着该线传回原点即可)。更重要的是,由于四元数的乘法对应于旋转的组成,因此您描述的每个操作都可以通过将“当前方向”乘以适当选择的四元数来表示:具体而言,由于四元数(单位)(qx,qy,qz,qw)意思是“通过arccos(qw)绕(qx,qy,qz)轴旋转”,然后(取决于您对坐标系的特定选择,并令c_a为cos(alpha)和s_a为sin(alpha))中的两个三个四元数M_x =(s_a,0,0,c_a),M_y =(0,s_a,0,c_a),M_z =(0,0,s_a,c_a)将表示'在方向I旋转(即移动) “我当前面对的是Alpha”和“以与我当前面对的Alpha正交的方向旋转”。(这些四元数中的第三个表示“围绕我的角色旋转我的角色”Cur_q = M_x * Cur_q如果玩家按下了按钮,或者Cur_q = M_y * Cur_q玩家按下了右Cur_q = M_yinv * Cur_q按钮(或者可能是类似的话,如果玩家按下了左按钮,则M_yinv是M_y四元数的“倒数”,以另一种方式表示旋转)。请注意,无论是预乘还是后乘,都必须注意将旋转应用于哪一侧。坦率地说,最简单的方法是通过试错法来解决这一问题,同时尝试乘法并查看哪种方法可行。

从更新的四元数到球面上的一个点(以及角色的方向)也相对简单:通过最后一段的对应关系,您要做的就是在基向量上使用四元数(1, 0,0),(0,1,0)和(0,0,1)通过“通过四元数旋转矢量”操作v→qvq -1(此处的乘法是四元数乘法,我们确定矢量v ='(x,y,z)和'简并四元数'(x,y,z,0))。例如,只需变换z向量即可得到单位球面上的位置:pos =(qx,qy,qz,qw)*(0,0,1,0)*(-qx,-qy,-qz, qw)=(qx,qy,qz,qw)*(qy,-qx,qw,qz)=(2(qy * qw + qz * qx),2(qz * qy-qw * qx),(qz ^ 2 + qw ^ 2)-(qx ^ 2 + qy ^ 2),0),所以(2(qy*qw+qz*qx), 2(qz*qy-qw*qx), (qz^2+qw^2)-(qx^2+qy^2))将是单位球体上“已转换”用户的坐标(当然,要获得任意球体上的坐标,只需将其乘以球体的半径)即可;其他轴也进行类似的计算,以定义例如用户的朝向。


正是我想要实现的目标。我只是想不出从方向四元数中获得位置的正确方法。使用您提供的内容,我可以编写Move()过程,但是要获取归一化的轴(即我的位置),我是否愿意(sin(qx),sin(qy),sin(qw)) * r
azz 2012年

@Der不完全是-我将用详细信息更新我的帖子,但简短的版本是您使用四元数通过通常的v-> qvq <sup>来转换单位矢量,例如(0,0,1)。 -1 </ sup>操作;您正在转换一个简单矢量的事实意味着(自然地)这里有一条捷径,但是最终坐标在四元数的值中是二次方的,不是线性的。
史蒂文·斯塔德尼基

1

我认为您需要与此http://www.youtube.com/watch?v=L2YRZbRSD1k类似的内容

我开发了一个48小时的gamejam ...您可以在此处下载代码... http://archive.globalgamejam.org/2011/evil-god

我使用了与您的代码类似的方法来获取3D坐标...但是我旋转了行星并且玩家处于同一位置,我想您对生物运动感兴趣,是这样的:

    // To add movement
    protected override void LocalUpdate(float seconds)
    {
        Creature.Alfa += Direction.X * seconds * Speed;
        Creature.Beta += Direction.Y * seconds * Speed;            
    }


    // To calculate position
       World.Planet.GetCartesian(Alfa, Beta, out Position); // as you do
       Matrix PositionMatrix = Matrix.CreateTranslation(Position) * World.Planet.RotationMatrix;           
       LastPositionAbsolute = PositionAbsolute;
       Vector3 Up = PositionAbsolute = Vector3.Transform(Vector3.Zero, PositionMatrix);           
       Up.Normalize();
       // This is to add and offset to the creature model position
       PositionAbsolute += Up * 8;  
      // calculate new forward vector if needed

       if ((PositionAbsolute - LastPositionAbsolute).Length() > 0.1f) {
           Forward = PositionAbsolute - LastPositionAbsolute;
           Forward.Normalize();
       }

       // Calculate the world transform with position, forward vector and up vector
       Matrix LocalWorld = Matrix.CreateWorld(PositionAbsolute, Forward, Up); 

       Transform = Matrix.CreateScale(Scale * ScaleFactor) * LocalWorld;
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.