두 축에서 객체를 회전하고 있는데 왜 세 번째 축을 중심으로 계속 왜곡합니까?


38

이 근본적인 문제가있는 질문이 자주 나오는 것을 보았지만 주어진 기능이나 도구의 세부 사항에서 모두 발견되었습니다. 다음은 애니메이션 예제가 많은 경우 사용자에게 참조 할 수있는 정식 답변을 작성하려는 시도입니다. :)


1 인칭 카메라를 제작한다고 가정 해 보겠습니다. 기본 아이디어는 왼쪽 및 오른쪽으로보기 위해 기울이고 위쪽 및 아래쪽으로보기 위해 기울어 야한다는 것입니다. 그래서 우리는 이와 같은 약간의 코드를 작성합니다 (예를 들어 Unity를 사용) :

void Update() {
    float speed = lookSpeed * Time.deltaTime;

    // Yaw around the y axis using the player's horizontal input.        
    transform.Rotate(0f, Input.GetAxis("Horizontal") * speed, 0f);

    // Pitch around the x axis using the player's vertical input.
    transform.Rotate(-Input.GetAxis("Vertical") * speed,  0f, 0f);
}

아니면

// Construct a quaternion or a matrix representing incremental camera rotation.
Quaternion rotation = Quaternion.Euler(
                        -Input.GetAxis("Vertical") * speed,
                         Input.GetAxis("Horizontal") * speed,
                         0);

// Fold this change into the camera's current rotation.
transform.rotation *= rotation;

그리고 그것은 대부분 작동하지만 시간이 지남에 따라 전망이 비뚤어지기 시작합니다. x와 y에서만 회전하도록 지시했지만 카메라가 롤 축 (z)을 켜고있는 것 같습니다!

1 인칭 카메라가 옆으로 기울어 진 애니메이션 예

카메라 앞에서 물체를 조작하려는 경우에도 발생할 수 있습니다. 지구를 둘러보고 싶은 지구라고 가정하십시오.

옆으로 기울어 진 지구본의 애니메이션 예

같은 문제-잠시 후 북극이 왼쪽이나 오른쪽으로 방황하기 시작합니다. 우리는 두 축에 입력을 제공하지만 세 번째로 혼란스러운 회전을 얻습니다. 그리고 물체의 로컬 축 주위 또는 세계의 글로벌 축 주위에 모든 회전을 적용하든간에 발생합니다.

많은 엔진의 경우 인스펙터에서도이를 볼 수 있습니다. 월드에서 오브젝트를 회전하면, 터치하지 않은 축에서 갑자기 숫자가 변경됩니다!

회전 각도 판독 값 옆에있는 객체 조작을 보여주는 애니메이션 예제.  사용자가 해당 축을 조작하지 않아도 z 각도가 변경됩니다.

이것이 엔진 버그입니까? 프로그램에 회전을 추가하고 싶지 않다는 것을 어떻게 알 수 있습니까?

오일러 각과 관련이 있습니까? 대신 쿼터니언, 회전 행렬 또는 기초 벡터를 사용해야합니까?



1
다음 질문에 대한 답변도 매우 도움이되었습니다. gamedev.stackexchange.com/questions/123535/…
트래비스 페트리

답변:


51

아니 이것은 엔진 버그 또는 특정 회전 표현 의 인공물이 아닙니다 (이것도 발생할 수 있지만이 효과는 회전, 쿼터니언 포함)을 나타내는 모든 시스템에 적용됩니다.

3 차원 공간에서 회전이 작동하는 방식에 대한 실제 사실을 발견했으며 번역과 같은 다른 변형에 대한 직관에서 출발했습니다.

다른 순서로 회전을 적용하면 결과가 다르게 표시되는 애니메이션 예제

둘 이상의 축에서 회전을 구성 할 때 결과는 각 축에 적용한 총 / 순 값이 아닙니다 (번역을 기대할 수 있음). 우리가 회전을 적용하는 순서는 각 회전이 다음 회전이 적용되는 축 (객체의 로컬 축을 중심으로 회전하는 경우) 또는 개체와 축 사이의 관계 (세계의 회전하는 경우)를 이동함에 따라 결과를 변경합니다 축).

시간이 지남에 따라 축 관계가 변경되면 각 축이 "추정 된"작업에 대한 직감을 혼동 할 수 있습니다. 특히, 요와 피치 회전의 특정 조합은 롤 회전과 동일한 결과를 제공합니다!

로컬 피치-요-피치 시퀀스를 보여주는 애니메이션 예제는 단일 로컬 롤과 동일한 출력을 제공합니다.

우리가 요청한 축에 대해 각 단계가 올바르게 회전하는지 확인할 수 있습니다. 입력을 방해하거나 두 번째 추측하는 표기법에 엔진 글리치 나 아티팩트가 없습니다. 주위에 ". 그것들은 작은 회전을 위해 국부적으로 직교 할 수 있지만, 그것들이 쌓일 때 그것들은 세계적으로 직교하지 않다는 것을 알 수 있습니다.

이것은 위와 같은 90도 회전에 대해 가장 극적이고 명확하지만, 방황 축도 문제에서 입증 된 것처럼 많은 작은 회전으로 기어 들어갑니다.

그래서 우리는 어떻게해야합니까?

피치-요 회전 시스템이 이미있는 경우 원하지 않는 롤을 제거하는 가장 빠른 방법 중 하나는 회전 중 하나를 변경하여 객체의 로컬 축 대신 전체 또는 부모 변형 축에서 작동하는 것입니다. 이렇게하면 두 축 사이의 교차 오염을 막을 수 있습니다. 하나의 축은 절대적으로 제어됩니다.

위의 예제에서 롤이 된 것과 동일한 피치-요 피치 (pitch-yaw-pitch) 시퀀스가 ​​있습니다. 그러나 이제 우리는 요오드를 오브젝트의 Y 축 대신 글로벌 Y 축 주위에 적용합니다.

롤 문제없이 로컬 피치 및 글로벌 요로 회전하는 머그의 애니메이션 예글로벌 요를 사용하는 1 인칭 카메라의 애니메이션 예

따라서 1 인칭 카메라를 "Pitch Locally, Yaw Globally"라는 문구로 고정시킬 수 있습니다.

void Update() {
    float speed = lookSpeed * Time.deltaTime;

    transform.Rotate(0f, Input.GetAxis("Horizontal") * speed, 0f, Space.World);
    transform.Rotate(-Input.GetAxis("Vertical") * speed,  0f, 0f, Space.Self);
}

곱셈을 사용하여 회전을 합성하는 경우 곱셈 중 하나의 왼쪽 / 오른쪽 순서를 뒤집어 동일한 효과를 얻을 수 있습니다.

// Yaw happens "over" the current rotation, in global coordinates.
Quaternion yaw = Quaternion.Euler(0f, Input.GetAxis("Horizontal") * speed, 0f);
transform.rotation =  yaw * transform.rotation; // yaw on the left.

// Pitch happens "under" the current rotation, in local coordinates.
Quaternion pitch = Quaternion.Euler(-Input.GetAxis("Vertical") * speed, 0f, 0f);
transform.rotation = transform.rotation * pitch; // pitch on the right.

(구체적인 순서는 환경의 곱셈 규칙에 따라 다르지만 왼쪽 = 더 전역 적 / 오른쪽 = 더 많은 로컬이 일반적인 선택입니다)

이것은 플로트 변수로 원하는 순 총 요와 총 피치를 저장 한 다음 항상 순 결과를 한 번에 적용하여이 각도에서만 단일 방향 오리엔테이션 쿼터니언 또는 행렬을 구성합니다 ( totalPitch클램프 상태를 유지 한 경우 ).

// Construct a new orientation quaternion or matrix from Euler/Tait-Bryan angles.
var newRotation = Quaternion.Euler(totalPitch, totalYaw, 0f);
// Apply it to our object.
transform.rotation = newRotation;

또는 동등하게 ...

// Form a view vector using total pitch & yaw as spherical coordinates.
Vector3 forward = new Vector3(
                    Mathf.cos(totalPitch) * Mathf.sin(totalYaw),
                    Mathf.sin(totalPitch),
                    Mathf.cos(totalPitch) * Mathf.cos(totalYaw));

// Construct an orientation or view matrix pointing in that direction.
var newRotation = Quaternion.LookRotation(forward, new Vector3(0, 1, 0));
// Apply it to our object.
transform.rotation = newRotation;

이 전역 / 로컬 분할을 사용하면 회전이 독립적 인 축 세트에 적용되므로 서로 합성하고 영향을 줄 수 없습니다.

우리가 회전하려는 세계의 물체라면 같은 아이디어가 도움이 될 수 있습니다. 지구와 같은 예를 들어, 우리는 종종 그것을 뒤집어 요를 로컬로 적용하고 (따라서 항상 극을 돌고 있습니다) 전 세계적으로 피치를가집니다 (그래서 호주를 향하거나 향하지 않고 우리의 시야를 향하여 팁에서 멀어집니다) , 그것이 가리키는 곳마다 ...)

더 나은 동작 회전을 보여주는 지구본의 애니메이션 예

한계

이 글로벌 / 로컬 하이브리드 전략이 항상 올바른 해결책은 아닙니다. 예를 들어, 3D 비행 / 수영이있는 게임에서 똑바로 위 / 똑바로 가리키면서도 모든 권한을 갖기를 원할 수 있습니다. 그러나이 설정을 사용하면 짐벌 잠금 장치를 사용할 수 있습니다. 요축 (글로벌 업)은 롤 축 (로컬 포워드)과 평행이되고 비틀 지 않고 왼쪽이나 오른쪽으로 볼 수있는 방법이 없습니다.

이와 같은 경우에 대신 할 수있는 것은 위의 질문에서 시작한 것처럼 순수한 로컬 회전을 사용하는 것입니다 (따라서 컨트롤은 원하는 위치에 상관없이 동일하게 느껴집니다). 우리는 그것을 정정합니다.

예를 들어, 로컬 회전을 사용하여 "정방향"벡터를 업데이트 한 다음 해당 정방향 벡터를 참조 "위"벡터와 함께 사용하여 최종 방향을 구성 할 수 있습니다. (예를 들어 Unity의 Quaternion.LookRotation 메서드를 사용하거나 이러한 벡터에서 직교 정규 행렬을 수동으로 구성) 업 벡터를 제어하여 롤 또는 비틀기를 제어합니다.

비행 / 수영 예제의 경우 이러한 수정 사항을 시간이 지남에 따라 점진적으로 적용하려고합니다. 너무 갑작스러운 경우 시야가 산만해질 수 있습니다. 대신 플레이어의 현재 업 벡터를 사용하여 뷰가 수평을 벗어날 때까지 프레임 단위로 세로 방향으로 힌트를 줄 수 있습니다. 턴 중에 이것을 적용하는 것은 때때로 플레이어의 컨트롤이 유휴 상태 일 때 카메라를 비틀는 것보다 메스꺼움을 덜 줄입니다.


1
GIF 제작 방법을 물어봐도 될까요? 그들은 좋아 보인다.
Bálint

1
@ Bálint LICEcap 이라는 무료 프로그램 을 사용합니다. 화면의 일부를 gif로 기록 할 수 있습니다. 그런 다음 애니메이션을 자르고 / 추가 캡션 텍스트를 추가하고 Photoshop을 사용하여 gif를 압축합니다.
DMGregory

이 답변은 Unity에만 해당됩니까? 웹에보다 일반적인 답변이 있습니까?
posfan12

2
@ posfan12 예제 코드는 구체성을 위해 Unity 구문을 사용하지만 관련된 상황과 수학은 전반적으로 동일하게 적용됩니다. 환경에 예제를 적용하는 데 어려움이 있습니까?
DMGregory

2
수학은 이미 위에 있습니다. 편집 내용으로 판단하면 잘못된 부분을 으깨는 것입니다. 여기에 정의 된 벡터 유형을 사용하고있는 것 같습니다 . 이것은 상술 한 바와 같이 일련의 각도로부터 회전 매트릭스를 구성하는 방법을 포함한다. 항등 행렬로 시작하여 호출 TCVector::calcRotationMatrix(totalPitch, totalYaw, identity)합니다. 위의 "All at once Euler"예제와 같습니다.
DMGregory

1

16 시간의 육아 및 회전 기능 후 롤.

방금 코드가 비어 있고 기본적으로 원이 바뀌고 앞면이 뒤집어지기를 원했습니다.

즉, 목표는 롤에 영향을주지 않고 요와 피치를 회전시키는 것입니다.

실제로 내 응용 프로그램에서 작동 한 몇 가지가 있습니다.

유니티에서 :

  1. 빈을 만듭니다. 그것을 ForYaw라고 부릅니다.
  2. ForPitch라는 어린이를 비 웁니다.
  3. 마지막으로 큐브를 만들어 z = 5 (앞으로)로 옮깁니다. 이제 우리가 피하려고하는 것을 볼 수 있습니다. 큐브를 비틀고있을 것입니다 (요우와 피치를하는 동안 우연한 "롤") .

이제 Visual Studio에서 스크립팅하고 설정을 수행하고 업데이트에서이를 완료하십시오.

objectForYaw.Rotate(objectForYaw.up, 1f, Space.World);
    objectForPitch.Rotate(objectForPitch.right, 3f, Space.World);

    //this will work on the pitch object as well
    //objectForPitch.RotateAround
    //    (objectForPitch.position, objectForPitch.right, 3f);

육아 순서를 바꾸려고 할 때 모든 것이 깨졌습니다. 또는 자식 피치 객체에 대해 Space.World를 변경하면 (부모 Yaw는 Space.Self를 통해 회전하는 것을 신경 쓰지 않습니다). 혼란스러운. 왜 로컬 축을 가져와야하는지 월드 스페이스를 통해 적용해야하는지에 대한 명확한 설명을 사용할 수있었습니다. Space.Self는 왜 모든 것을 망칠까요?


이것이 질문에 대한 대답인지 또는 작성한 코드가 이런 식으로 작동하는지 이해하는 데 도움이되는지 여부는 분명하지 않습니다. 후자의 경우 원하는 경우 컨텍스트를 위해이 페이지에 링크하여 새 질문을 게시해야합니다. 힌트 me.Rotate(me.right, angle, Space.World)me.Rotate(Vector3.right, angle, Space.Self와 동일하며 중첩 된 변환은 허용되는 답변의 "Pitch Locally, Yaw Globally"예제와 동일합니다.
DMGregory

둘 다 조금 답변에 부분적으로 공유하십시오 (예를 들어 여기에 그것이 저에게 효과가 있었던 방법입니다). 또한 질문을 소개합니다. 그 힌트가 생각났다. 고마워요!
Jeh

놀랍지 않은 일이 아니 었습니다. 교육은 여전히 ​​효과가 없었지만 포괄적 인 답변을 읽은 후 일반적인 Vector3.right 축 대신 객체 회전 축을 사용하는 간단한 대답만으로도 회전을 객체에 고정시키는 데 필요한 모든 것입니다. 내가 찾던 놀라운 내용이 여기에 0 투표로 표시되고 많은 유사한 질문이있는 Google의 두 번째 페이지에 묻혔습니다.
user4779
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.