구에 대한 임의의 회전


12

사용자가 구 표면 주위를 이동할 수있는 정비공을 코딩하고 있습니다. 구의 위치는 현재 theta및로 저장되며 phi, 여기서 theta현재 위치의 z 축과 xz 투영 사이의 각도 (즉, y 축을 중심으로 한 회전)이며 y 축 phi에서 위치까지의 각도입니다. 나는 그것을 잘못 설명했지만 본질적 theta = yaw으로phi = 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;

나는 이것이 정확하다고 생각하지만 틀릴 수 있습니다. 반경이있는 월드 공간의 원점에서 구의 표면을 중심으로 임의의 의사 2 차원 방향으로 움직일 수 있어야합니다 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;
    }
}

플레이어가 극에 도달하면 어떻게됩니까? 나는 당신이 "상향 방향"을 썼다는 것을 알아 차 렸습니다. 당신은 문자 그대로 "상향"(즉, 구의 표면에서 멀어짐), "직진"또는 "북쪽으로"를 의미 합니까? 방향을 변경할 수 없으며 화면의 "앞쪽"또는 "위쪽"이 항상 북쪽입니다)?
Martin Sojka

어쩌면 그것은 잘못 표현되었을 것입니다. 플레이어는 구의 표면을 떠나서는 안되며 기본 축을 인식해서는 안됩니다. 따라서 "위로"움직이면 구의 표면을 따라 플레이어의 방향을 기준으로 위쪽으로 움직입니다. 예를 들어 (r, 0,0)에 있고 위로 누르면 z + 극쪽으로 이동하지만 계속 진행하면 줄 바꿈하고 계속 진행해야합니다.
azz

여전히 한 가지 질문이 남아 있습니다. 플레이어가 방향을 바꿀 수 있습니까 ( "왼쪽"과 "오른쪽"회전)?
Martin Sojka

아마도 내가 가고있는 것에 대한 더 좋은 예 일 것입니다 : (1,1,1)왼쪽 을 잡고있는 플레이어 는 구 주위를 회전하고 (~1.2,0,~-1.2), 다음 (-1,-1,-1), 그리고을 통과 (~-1.2,0,~1.2)합니다 (1,1,1).
azz

1
항상 추적 theta하고 phi위치가 업데이트되면 문제가 불필요하게 복잡해집니다. 각 프레임 (하나는 변경되지 않음)과 Vector3.Transorm구 주위의 2 개의 회전 축을 간단히 계산하는 것이 훨씬 쉽습니다 . 이렇게하면 문제가 간단 해지지 만 phi& 연결을 끊을 수 있습니다 theta.
Steve H

답변:


5

실제로, 당신이 그것을 '두 가지 방법으로'가질 수 없다는 것이 밝혀졌습니다. 만약 당신의 의도가 구체에 '절대적인 방향'을 갖지 않으려면 (즉, 플레이어가 항상 극을 향하지 않는 경우) ), 선수 오리엔테이션 개념이 필요합니다. 직관 제안 할 수 것과는 달리, 구에 운동이 때문이다 하지 정확하게 비행기의 움직임처럼, 심지어 로컬 (매우); 구의 고유 곡률은 플레이어가 스스로 회전하는 작업을 수행 할 수 있음을 의미합니다!

내가 말하는 것에 대한 가장 극단적 인 예를 들어, 플레이어가 적도의 한 지점에서 시작한다고 상상해보십시오 (편의상 우리는 위에서 적도에 시계면이 매핑되어 6시 방향에 놓이는 것을 상상할 것입니다) ), '위로'즉, 북극쪽으로 향합니다. 플레이어가 북극까지 걸어 간다고 가정하자. 12시 방향으로 향하게됩니다. 이제 플레이어가 북극에서 적도로 바로 오른쪽으로 이동하게하십시오. 그들은 3시 지점에 감을 것입니다-그러나 그들이 오른쪽으로 움직일 때 그들의 얼굴은 변하지 않기 때문에(아이디어가 어떻게 움직여도 얼굴이 바뀌지 않는다는 아이디어가 있습니다), 그들은 여전히 ​​12시 방향을 향하고 있습니다-그들은 이제 적도를 향하고 있습니다! 이제 '뒤로'시작 (6시) 지점으로 다시 이동합니다. 그런 다음 여전히 적도를 향하고 3시 방향을 향하게됩니다. '개인적'방향을 바꾸지 않고 구를 따라 움직이면 북극을 향한 회전에서 적도를 따라 어떤 의미에서 이것은 옛 사냥꾼이 남쪽으로, 서쪽으로, 그리고 북쪽으로 1 마일 움직 인 농담의 정교함입니다. 그러나 여기서 우리는 방향의 변화에 ​​영향을 미치기 위해 구면의 곡률을 이용합니다. 훨씬 더 작은 스케일에서도 동일한 효과가 여전히 발생합니다.

다행스럽게도 쿼터니언은이 상황을 직접 처리합니다. 쿼터니언은 임의의 회전을 나타 내기 때문에 구의 임의의 '포인트 + 방향'을 효과적으로 나타냅니다. 원점에서 '삼각'으로 시작하여 임의의 회전을 한 다음 회전 된 축의 어느 방향 으로든 한 단위 이동 Z 축 포인트; 약간의 생각은 이것이 당신에게 '방향'(즉, 3 축의 X 및 Y 축의 일부 배열)이있는 단위 구의 점을 가져오고, 모든 점 + 방향을 얻을 수 있음을 확신시켜 줄 것입니다. 이 방법으로 단위 구를 지정하십시오 (구의 점을 통해 원점에서 선을 따라 Z 축을 지정한 다음 3 축을 해당 선을 따라 원점으로 다시 전송하십시오). 또 뭔데, 쿼터니언의 곱셈은 회전의 구성에 해당하므로 설명하는 각 작업은 '현재 방향'에 적절히 선택된 쿼터니언을 곱하여 나타낼 수 있습니다. 구체적으로 (단위) 쿼터니언 (qx, qy, qz, qw) '(arx (qw)에 의해 (qx, qy, qz) 축을 중심으로 회전합니다'를 의미 한 다음 (좌표계의 특정 선택에 따라 c_a를 cosα, s_a를 sin α로 함) 3 개의 쿼터니언 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에서 '회전 (즉 이동)'을 나타냅니다. '현재 알파를 향하고 있습니다'및 '현재 알파를 향하고있는 방향과 직교하는 방향으로 회전합니다'. (이 쿼터니언 중 세 번째는 '자신의 축을 중심으로 내 캐릭터를 회전시킵니다'를 나타냅니다Cur_q = M_x * Cur_q플레이어가 위를 눌렀거나 플레이어가 Cur_q = M_y * Cur_q오른쪽을 눌렀을 경우 (또는 Cur_q = M_yinv * Cur_q플레이어가 왼쪽을 눌렀을 때 와 같은 것) M_yinv는 다른 방법으로 회전을 나타내는 M_y 쿼터니언의 '역'입니다. 미리 곱하기 또는 포스트 곱하기 여부에 관계없이 회전을 적용 할 '면'에주의해야합니다. 솔직히 말해서 시행 착오로 곱셈을 시도하고 어느 것이 효과가 있는지 보는 것이 가장 쉬운 방법 일 수 있습니다.

업데이트 된 쿼터니언에서 구의 점 (및 캐릭터의 방향)으로 이동하는 것도 비교적 간단합니다. 마지막 단락의 대응으로 쿼터니언을 기본 벡터 (1, 'quaternion에 의한 벡터 회전'연산을 통한 프레임의 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

@Der 정확하지 않음-세부 사항으로 게시물을 업데이트하지만 짧은 버전은 쿼터니언을 사용하여 일반적인 벡터 (예 : (0,0,1))를 사용하여 단위 벡터를 변환한다는 것입니다-> qvq <sup> -1 </ sup> 연산; 간단한 벡터를 변환한다는 것은 (당연히) 여기에는 지름길이 있지만 최종 좌표는 선형이 아니라 쿼터니언의 값에서 2 차입니다.
Steven Stadnicki

1

http://www.youtube.com/watch?v=L2YRZbRSD1k 와 비슷한 것을 원한다고 생각합니다 .

나는 48h 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;
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.