ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [11] - Space and transforms - 1
    Graphics 2021. 7. 13. 21:58

    D3D를 향해 무작정 달리는 것도 나쁘지 않다고 생각합니다. 지금까지는 D3D의 아주 기본적인 구성에 대해 이해하려는 시도가 있었습니다. Graphics Pipeline을 비롯한 각종 D3D의 본편으로 들어가기 전에 몇 가지 Game Graphics와 관련한 정보를 검토해보는 편이 좋을 것 같습니다. 수업을 들었을 때, OpenGL ES를 바탕으로 공부했기 때문에 D3D와 약간의 차이가 있을 수 있습니다. 하지만 보편적인 수학엔 차이가 없습니다.

    화면의 좌상단을 기준으로 수평선을 x axis, 수직선을 y axis로 설정합니다. x는 화면의 우측이, y는 화면의 하단이 양의 방향입니다. 좌표를 표현함에 있어서 행렬과 벡터를 사용하게 될 것입니다. 가령, 보편적으로 이차원 공간 상의 한 점(Vertex)을 (x, y)로 표현할 수도 있지만 다음과 같이 열벡터[Column Vector]로 표현할 수도 있습니다.

    왜 이렇게 표현해야만 할까요? 계산하기가 쉽기 때문입니다. 언젠가 피보나치의 수를 행렬을 통해 계산할 수 있다고 했습니다. 행렬은 반복적인 계산에도 특화되어 있는 유용한 수학입니다. 예를 들어서, 한 벡터(좌표)를 두 배 늘린다고 생각해봅니다. 보이는 대로 행렬에 적당한 행렬을 곱하여 원하는 값을 금방 만들어 낼 수 있습니다. 일단은 이차원 공간 상에서만 이야기를 제한하겠습니다.

    간단한 선형대수를 활용하며 벡터의 크기를 조절할 수 있습니다

    평면 좌표계에 표현한 모든 벡터에 대해 같은 연산을 수행할 수 있습니다. 따라서 점들로 이어져서 만든 평면 도형에 같은 행렬을 곱하는 것으로 원하는 만큼 사이즈를 조절할 수 있게 됩니다.

    Rotation

    회전은 중요합니다. 다음과 같은 그림을 토대로 생각을 해봅시다. 벡터 p가 각 theta만큼 이동하여 p`으로 간다고 생각해봅시다. 벡터 p의 크기가 r이라고 하면 간단한 삼각법에 의해서 벡터 p의 x,y성분을 구할 수 있습니다.

    그러면 p`의 성분들은 PHI와 Theta의 각을 합한 대로 계산하면 됩니다. 미분적분학 시간에 배웠던 코사인 분해/사인 분해 공식을 활용하여 p`의 성분들을 구합니다. 그러면 x`과 y`을 x와 y에 관한 식으로 풀 수가 있는데 이를 다시 행렬과 벡터의 곱의 형태로 표현하면 회전행렬이라고 이름이 붙은 행렬을 하나 얻을 수 있습니다. 이 행렬은 어떤 벡터에 곱해도 그 theta만큼 회전을 시켜줍니다.

    예시를 들어 이해할 수 있습니다. 그리고 여기서 말하는 회전이라는 것은 Anti-Clockwise가 기본입니다.

    (3, 0)을 90 도 회전하면 (0, 3)이 잘 나옵니다

    Translation

    Scaling이나 Rotation과 다르게 Translation은 단순히 벡터의 덧셈으로 표현할 수 있습니다.

    그러나 우리의 목표는 이 모든 과정을 곱으로 표현하는 것입니다. 왜냐하면 앞으로 어떤 물체의 움직임을 기술할 때 일반적으로 식을 구성하게 됐을 때 하나로 합칠 생각이기 때문입니다. 좋은 방법이 있습니다. 2D Cartesian Coordinates (x, y)를 Homogeneous coordinates 에서 (x, y, 1)의 형태인 삼차원 벡터를 상상하는 겁니다. 아래의 그림을 참조하면 translation을 행렬의 곱으로도 표현할 수 있다는 것을 확인할 수 있습니다.

     

    그나저나 Homogeneous coordinates란 어디에 쓰는 개념일까요? 한 마디로 정리할 수가 있는데 한 지점에서 다른 지점을 바라보거나 거리를 체크할 때 사용합니다. 위에서 이차원 공간의 벡터를 삼차원 공간의 벡터로 바꾸어 계산을 했습니다. 그 근거는 바로 한 지점에서 완전히 같은 시선상에 있는 물체는 거리에 상관 없이 같은 위치에 있는 것으로 보인다는 점입니다. (원근법은 고려하지 않습니다.)

    그래서 사실 (x, y)에서 (x, y, 1)로 가는 것은 (2x, 2y, 2)로가도 상관이 없었습니다. 단순히 w != 0일때 (wx, wy, w)를 만족해주기만 하면 됩니다. 이에 관해서는 후에 Camera에 대한 개념이 나오면 다시 등장할 개념입니다.

    이제, Translation을 벡터의 덧셈이 아니라 행렬에 벡터를 곱하는 꼴로 만들기 위해 이차원 벡터를 삼차원으로 끌어 올렸습니다. 따라서, 어떤 물체의 translation, rotation, scaling을 하나의 식에 표현하기 위해서 다른 벡터들도 삼차원으로 끌고 와야할 필요가 있습니다.

    이제 모든 변환 행렬이 3x3 행렬이 되었으니 하나로 합쳐서 쓸 수 있습니다. 여기서 주의해야 할 점 하나는 Translation과 Rotation이 함께 수행될 경우에 반드시 순서를 지켜야합니다. 왜냐하면 matrix가 noncommutative하기 때문입니다. 특히, 원점을 중심으로 할 때는, 회전하고 움직이는 것과 움직이고 회전하는 것이 완전히 똑같은 것 같지만 반드시 계산 순서에 주의해야만 합니다.

    이제 이차원 공간 상에서는 원점을 기준으로 회전하지 않아도 충분히 대응을 할 수 있게 되었습니다. 예를 들어서 (5,2)를, (3,2) 기준으로 +90 도 회전한다고 가정해 봅니다.

    1. 기준점인 (3,2)를 원점 (0,0)에 놓기 위해서 Translation 을 할 겁니다.

    2. (5,2)->(2,0) / (3,2) -> O(0,0)

    3. +90도 회전

    4. 다시 O(0,0)을 기준점 (3,2)에 translation

    Affine Transform

    지금까지 총 세 개의 Transform matrix을 확인했습니다. 이들은 Affine transform이라고 불리는데 그 중에서도 특히 Scaling과 Rotation은 Linear transform에 속합니다. Translation은 Linear transform에 속하지 않습니다.

    Linear transform의 머리인 L을 따서, Linear transform으로만 구성된 Affine transform을 L이라고 하고 translation을 t라고 하면 Affine Transform Matrix는 [L|t]라고 표현할 수도 있습니다. 이는 해당 행렬을 분해할 수 있다는 사실을 암시하고 있습니다. 예를 들어서 이해합니다.

    분명, 행렬의 계산은 non-commutative하기 때문에 순서에 주의하라고 했습니다. 그럼에도 불구하고 이 경우엔 두 행렬을 교환하여 곱했음에도 같은 결과를 얻었습니다. 이처럼 linear transform들로 구성된 L과 translation t가 합쳐져 [L|t]꼴의 Affine Transform 을 얻었다면 다시 분해를 할 수 있습니다. 이 변환을 말로 풀어 보면 어떤 포인트를 (7,0)만큼 이동하고 원점을 기준으로 +90도 만큼 회전하라는 뜻이었습니다. 하지만 분해를 진행하고 난 이후의 행렬을 해석해보면 원점을 기준으로 +90도만큼 회전하고 (0,7)만큼 이동하라는 뜻이 되어 있습니다!

    Rigid motion

    어떤 딱딱한 상자를 대충 경사면에 굴리면 데굴데굴 구르며 위치가 변하고 회전을 하지만 상자 자체의 형태엔 변화가 없습니다. 이런 운동을 Rigid motion이라고 합니다. 위에서 L과 t로 구성된 행렬을 만들었던 것처럼 여기서도 회전으로만 구성된 R과 translation으로만 구성된 t로 [R|t]형태의 affine matrix를 얻을 수 있습니다. 이건 물리엔진 구현에 자주 사용할 것 같은 느낌이 듭니다.

     

    'Graphics' 카테고리의 다른 글

    [13] - Vertex processing - 1  (0) 2021.07.15
    [12] - Space and transforms - 2  (0) 2021.07.14
    [10] - App class  (0) 2021.07.13
    [9] - Mouse  (0) 2021.07.12
    [8] - Keyboard  (0) 2021.07.12
Designed by Tistory.