[ 2D ] 두 점을 입력받아 특정 물체를 바라보도록 회전하는 방법
목차
- 최종 결과물
- 이론및 수식 만들기
- 의사코드
- 부록: 상용엔진에서 해당 알고리즘 사용하기
이번에는 2D상에서 두 점 A, B를 입력받아서 A가 B를 정방향으로 바라보도록 하는 방법에 관하여 연구했다.
또한, 그냥 바라보도록하는것에만 그치지 않고, 속도를 최대 약 4.2배이상 증가시키는 최적화 방법까지 연구했다.
이번 내용은 매우 쉽기때문에 간단하게 알아보고 바로 넘어갈려고 한다.
그러면 바로 포스팅을 시작하겠다.
최종 결과물
이론및 수식 만들기
사실 수식자체는 되게 간단한 편이다.
이 글을 보는 인디게임개발자분들이나 2D게임에 관심이 많은 분들이 봤으면 좋겠다.
우선 나는 Y축회전행렬과 아크탄젠트의 성질을 이용했으며,
최적화또한 비슷한 맥락으로 진행했다.
우선 다음은 Y축 회전행렬이다.
또한, 두 점을 각각 P1(x1, y1, z1), P2(x2, y2, z2) 라고할때
두 점을 두고 탄젠트를 구하면 다음과 같이 나온다.
여기서 각 theta(세타)를 구해주기 위해 아크탄젠트로 변환을 해준뒤, 아크탄젠트를 회전행렬에 넣으면 다음과 같이 바뀐다.
이제 여기서 합성된 삼각함수의 성질을 이용해 식을 간단하게 바꿀수있게된다.
다음 그림에 의하여 cos(θ), sin(θ)을 구해주면 우리의 최종적인 회전식은 다음과 같아진다.
하지만, 완성된 회전행렬을 보고있자니 이러한 의문이 들수 있다.
" 삼각함수 두번쓰나 제곱근을 쓰나 크게 차이가 날까? "
여기서 도입하는것이 Fast inverse square root 알고리즘이다.
해당 알고리즘은 제곱근의 역수를 훨씬 빠르게 계산하는 최적화 알고리즘중 하나이다.
해당 알고리즘을 사용해 기존 행렬곱의 형태보다 (per seconds)초당 4.2배 빠른속도로 작동하는 의사코드를 소개하겠다.
참고로 기존 행렬곱은 simd를 적용했으며, 나의 의사코드에는 simd가 적용되지않았음에도 다음과 같은 속도차이를 보여주었다.
의사코드
다음은 우리가 위에서 알아본 회전행렬 구축에 관한 의사코드이다.
constexpr inline float fast_inverse_sqrt(const float& number)
{
long i = 0;
const float threehalfs = 1.5F;
float x2 = number * 0.5F;
float y = number;
i = *(long*)&y;
i = 0x5f3759df - (i >> 1);
y = *(float*)&i;
y = y * (threehalfs - (x2 * y * y));
//y = y * (threehalfs - (x2 * y * y));
//y = y * (threehalfs - (x2 * y * y));
return y;
}
// P1에서의 x, z좌표: x1, z1
// P2에서의 x, z좌표: x2, z2
// 최종적으로 자신을 가리키는 물체가 위치할 포지션: tx, ty ,tz
constexpr inline Matrix build_arrow_matrix2d_with_translation(
const float& x1, const float& z1, const float& x2, const float& z2,
const float& tx, const float& ty, const float& tz)
{
const float X = x2 - x1;
const float Z = z2 - z1;
const float denom = fast_inverse_sqrt(X * X + Z * Z);
const float cos_denom = X * denom;
const float sin_denom = Z * denom;
return {
cos_denom, 0.0f, sin_denom, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
-sin_denom, 0.0f, cos_denom, 0.0f,
tx, ty, tz, 1.0f
};
}
부록: 상용엔진에서 해당 알고리즘 사용하기
이번에는 생각보다 간단했다.
만약에 여러분이 상용엔진에서 이번에 소개한 알고리즘을 사용하고싶다면, 상용엔진에서 지원하는(만약 지원한다면)
오일러각의 물체 회전함수를 통해 오브젝트를 (cos_denom - sin_denom, 0.0f, sin_denom + cos_denom)만큼 회전시켜 주면 된다.