Home 12. 행렬 MVP변환
Post
Cancel

12. 행렬 MVP변환

맥스스크립트

맥스 스크립트 도큐먼트

https://help.autodesk.com/view/MAXDEV/2021/ENU/?guid=GUID-4C14F474-CD23-4001-93DF-0F0F9A6025C7#GUID-4C14F474-CD23-4001-93DF-0F0F9A6025C7

Selection

$’오브젝트이름’ 으로 선택

$는 셀렉션. 단일객체면 단일객체를 반환, 다중오브젝트면 셀렉션을 반환

$[2]형식으로 다중셀렉션의 특정객체를 유추 가능

예를 들어,

$.transform으로 트랜스폼 행렬을 가져올 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$.transform.pos

$.material

$PinkTPod.transform

//global variable
transG = $'Green'.transform

//local scope
(
    transG = $'Green'.transform
)

//Transformation
(
    transG = $'Green'.transform
    transP = $'Pink'.transform
		$'Pink'.transform = transP * transG  
		//transform은 그 자체로 행렬이며, 
		//G만큼을 적용하였기 때문에 행렬자체가 결과값으로 처리됨.
)

assign 방식은 structType과 유사하게 파티클시스템, 컬러 등에서 사용하던 것과 같이

변경한 값을 다시 속성으로 넣어줘야합니다.

1
2
3
4
5
transG = $'Green'.transform
transP = $'Pink'.transform
transG = transP * transG
//assign
$'Pink'.transform = transG

행렬::이동 (아핀공간)

행렬의 곱을 통한 이동

이것을 이해하기 위해서는 아핀 공간과 밀기변환에 대해 알아야 합니다.

이동’이라는 것은 각각의 좌표특정값더한다고 볼 수 있습니다.

그렇다면 선형 변환형태의 행렬꼴로 나타내면 다음과 같습니다.

Av=[abcd][xy]=[x+ey+f]=u\bold A\vec v =\begin{bmatrix} a & b\\ c & d \end{bmatrix} \begin{bmatrix} x\\ y \end{bmatrix} = \begin{bmatrix} x+e\\ y+f \end{bmatrix} = \bold u

해당 식을 만족하는 행렬 A\bold A 가 있을 수 있을까요? 아니요 불가능합니다.

그러면 해당 식을 만족할려면 어떡해야할까요?

간단합니다. 차원을 하나 추가합니다.

Av=[ab0cd0001][xy0]=[x+ey+f0]=u\bold A\vec v =\begin{bmatrix} a & b & 0 \\ c & d & 0 \\ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} x\\ y\\ 0 \end{bmatrix} = \begin{bmatrix} x+e\\ y+f\\ 0 \end{bmatrix} = \bold u

이렇게 구조를 짠다면 해결할 수 있을까요?

기본적으로 identity 행렬에서 부터 생각해봅시다.

Av=[100010001][xy0]=[1x+0y+000x+1y+0000+00+10]=u\bold A\vec v =\begin{bmatrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} x\\ y\\ 0 \end{bmatrix} = \begin{bmatrix} 1 \cdot x + 0 \cdot y + 0 \cdot 0\\ 0 \cdot x + 1 \cdot y + 0 \cdot 0\\ 0 \cdot 0 + 0 \cdot 0 + 1 \cdot 0 \end{bmatrix} = \bold u

즉 이 형태에서 x+ex+ey+fy+f 를 나타낼려면 a13=ea_{13}=ea23=fa_{23}=f 입니다.

다시 이걸 행렬로 바꾸게 된다면,

Av=[10e01f001][xy1]=[1x+0y+e10x+1y+f100+00+11]=[x+ey+f1]=u\bold A\vec v =\begin{bmatrix} 1 & 0 & e \\ 0 & 1 & f \\ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} x\\ y\\ 1 \end{bmatrix} = \begin{bmatrix} 1 \cdot x + 0 \cdot y + e \cdot 1\\ 0 \cdot x + 1 \cdot y + f \cdot 1\\ 0 \cdot 0 + 0 \cdot 0 + 1 \cdot 1 \end{bmatrix} = \begin{bmatrix} x+e\\ y+f\\ 1 \end{bmatrix} = \bold u

즉 정리하자면 밀기변환은 하나의 축만은 움직일 수 없으므로, 가상의 축을 추가하여

해당 축을 제외한 밀기변환을 행한다고 볼 수 있습니다.

여기서 zz 가 1인 평면만을 사용하는 이유는, ( e,fe,f )만큼 밀기변환을 하였을 때,

( 1e,1f1\cdot e,1\cdot f )만큼만 이동하기 때문입니다. 다른 값 nn 을 가진다면 밀기변환이

( ne,nfn\cdot e,n\cdot f ) 만큼 이동을 하기 때문에 부정확해지기 때문이죠

아핀공간 및 점의 정의

z=1z=1 인 밀기변환이 정확히 평행이동되는 공간을 아핀 공간(Affine Space)이라고 부릅니다.

원점이 어디인지 알 수 없으며, 2차원 아핀공간의 경우, 3차원 벡터공간의 부분공간이라고 볼 수 있습니다.

여기서 그 아핀공간의 원소를 점(Point)이라고 하며, 점과 벡터를 같은군으로 해석합니다.

아핀공간에서의 벡터의 존재이유

아핀공간은 마지막차원, 편의상 z=1z=1 인 공간을 의미한다 하였습니다.

하지만 연산은 벡터와 같이 적용됩니다.

따라서 점과 점의 단순덧셈은 zz 값이 1이 아니게 되므로, 점간의 연산은 불가합니다.

그러므로 점의 이동을 행할 때에는 점이 아닌 이동벡터( z=0z=0 )을 통해 결과가 여전히 z=1z=1 인 점을 이동할 수 있습니다. 이동벡터( z=0z=0 ) 간의 연산 역시 가능하겠죠. 이후 이동벡터를 잠시 단순히 벡터라고 서술하겠습니다.

정리해본다면

  • 점 간의 덧셈은 불가능하며,
  • 점과 벡터와의 연산은 가능하며 벡터와 벡터간의 연산도 가능합니다.

따라서 아핀공간에서도 아핀공간의 원소인 점 뿐만 아니라 벡터가 필요하게 됩니다.

점과 벡터의 연산

아까 점과 점의 단순 덧셈 연산은 불가능 하다고 하였습니다.

점과 벡터의 연산은 점을 만듭니다.

그러면 점에서 점을 빼면 벡터가 나오지 않을까요?

P1=(x1,y1,1), P2=(x2,y2,1)P1P2=v(x1x2,y1y2,0)P_1 = (x_1,y_1,1),\space P_2 = (x_2,y_2,1)\\ P_1 - P_2 = v(x_1 - x_2,y_1-y_2,0)

두 점간의 위치 차이. 전 이것을 조금 더 정확하게 이해하기 위해 ‘변위(displacement,變位)’라고 표현하겠습니다.

두 점간의 변위는 한 점에 다른점을 빼면 됩니다. 그 결과로 변위벡터(displacement Vector), 즉 아까 말한 이동벡터가 나오게 됩니다.

그리고 이 변위벡터는 아까 서술하였듯, 마지막 차원의 성분이 0을 갖습니다.

점에서 원점이라고 정의한 기준점(이하 원점)을 뺀다면 (원점의 역원을 더한다면) 변위벡터가 나타나게 되고, 원점에 변위벡터를 더하게 된다면 해당 변위만큼 이동한 점이 생성되어 두 변환이 자유롭게 될 수 있습니다. 이제 저희가 바라보는 게임 세상의 시작이 발생하는것이죠.

정리하자면

  • 원점 + 변위벡터 = 해당변위만큼 이동한 점
  • 특정 점 - 원점 = 원점에서 특정 점까지의 변위벡터

이제 이동을 위해 차원의 성분보다 하나 더 큰 행렬, 아핀공간을 통해 세상을 표현하고,

이를 사용하기 위해 동차 좌표계의 변환을 이해하고, 점과 벡터라는 개념을 숙지하며, 세상을 구성하기 시작합니다.

행렬::회전

2차원 공간에서의 임의의 각 𝜃에 대한 회전 행렬 구성

2차원 공간에서 두 기저벡터 e1\vec{e_1} , e2\vec{e_2} 에 대해 삼각함수로 표현해보면,

e1(right)=(cos(θ),sin(θ))\vec{e_1}(\vec{right}) = (cos(\theta),sin(\theta))

e2(up)=(sin(θ),cos(θ))\vec{e_2}(\vec{up}) = (-sin(\theta),cos(\theta)) 이므로,

두 기저벡터를 열벡터로 표현한다면 다음과 같습니다.

e1=[cos(θ)sin(θ)]e2=[sin(θ)cos(θ)]\vec e_1 = \begin{bmatrix} cos(\theta) \\ sin(\theta) \end{bmatrix} \\ \vec e_2 = \begin{bmatrix} -sin(\theta) \\ cos(\theta) \end{bmatrix}

그리고 두 기저벡터를 통한 행렬을 만든다면,

Av=[cos(θ)sin(θ)sin(θ)cos(θ)][xy]=[cos(θ)xsin(θ)ysin(θ)x+cos(θ)y]=u\bold A\vec v =\begin{bmatrix} cos(\theta) & -sin(\theta) \\ sin(\theta) & cos(\theta) \end{bmatrix} \begin{bmatrix} x\\ y \end{bmatrix} = \begin{bmatrix} cos(\theta) x - sin(\theta)y \\ sin(\theta) x + cos(\theta)y \end{bmatrix} = \bold u

계산해봅시다.

θ=90(degree)\theta = 90(degree) 으로 잡고,

Av=[cos(90)sin(90)sin(90)cos(90)][xy]=[0110][xy]=[yx]=u\bold A\vec v =\begin{bmatrix} cos(90) & -sin(90) \\ sin(90) & cos(90) \end{bmatrix} \begin{bmatrix} x\\ y \end{bmatrix} =\begin{bmatrix} 0 & -1 \\ 1 & 0 \end{bmatrix} \begin{bmatrix} x\\ y \end{bmatrix} = \begin{bmatrix} - y \\ x \end{bmatrix} = \bold u

뭐랑 비슷하지 않나요? 맞습니다. 반시계방향 90 ^\circ 회전과 같습니다.

따라서 임의의 각 𝜃에 대한 회전행렬은

[cos(θ)sin(θ)sin(θ)cos(θ)]\begin{bmatrix} cos(\theta) & -sin(\theta) \\ sin(\theta) & cos(\theta) \end{bmatrix}

입니다.

3차원 공간에서의 회전 행렬 구성

😱😱😱😱😱

표준기저벡터를 통한 회전 변환 행렬

일단 회전 역시 결국 차원만큼의 기저벡터를 통해 기저를 나타낸다면 회전량을 알 수 있습니다.

그리고 모든 방향은 벡터로 나타 낼 수 있죠. 예를들어 Y-UP 왼손 좌표계에서 정면은 (0,0,1)입니다.

이게 z기저벡터가 되는 것이죠. 그전에 잠깐, 회전행렬은 직교행렬이므로 조건이 존재합니다

Rotation matrices are square matrices, with real entries. More specifically, they can be characterized as orthogonal matrices with determinant 1; that is, a square matrix RR is a rotation matrix if and only if RTR^T = R1R^{−1} and det RR = 1.

https://en.wikipedia.org/wiki/Rotation_matrix

정리하면 다음과 같습니다.

Untitled

다음과 같은 세 기저 벡터를 가진 행렬을 작성한다면 다음과 같습니다.

M=[1000010000100001]M= \left[ \begin{matrix} 1&0&0&0\\ 0&1&0&0\\ 0&0&1&0\\ 0&0&0&1\\ \end{matrix} \right]

이렇게 생성한 세 기저 벡터의 값을 변경하여 보겠습니다. (벡터의 길이는 1이되어야하지만, 일단 소숫점 3번째 자리에서 반올림하겠습니다)

Untitled

M=[0.920.310.2500.250.930.2600.310.170.9300001]M= \left[ \begin{matrix} 0.92&0.31&-0.25&0\\ -0.25&0.93&0.26&0\\ 0.31&-0.17&0.93&0\\ 0&0&0&1\\ \end{matrix} \right]

와! 정말 보기 흉합니다. 이걸로 어느정도의 각 회전이 발생했는지 쉽게 알 수 있을까요?

저는 각 축마다 몇도씩 회전시킨걸까요?

오일러각 (Euler Angles)


오일러각의 필요성


아까보았던 세개의 표준기저벡터를 사용한 행렬은 보기만 해도 끔찍합니다.

Untitled

마치 각이 아닌 유사도를 보는 것 같아요.

2차원에서는 저희는 어떻게 회전을 표현하였었죠?

2차원에서는 값이 아니라 각 θ\theta 에 대한 식으로 나타내었었군요!

즉 회전행렬은 따지고 보자면 수치 자체를 θ\theta 에 대한 식으로 나타낸 것입니다.

마찬가지로 오일러 각 또한 각 축을 제외한 평면에 대한 회전 값을 식으로 나타낸 것입니다.

그렇다면 연산은 좀 하더라도 더 나은 방법으로 볼 수 있겠네요.

오일러각. 요로코롬 절차적으로 회전시킵니다. 오일러각. 요로코롬 절차적으로 회전시킵니다.

오일러 각 회전을 통해 얻는 장점


아무래도 사람이 보기에 ‘직관적’ 이라는 부분이 가장 크다고 생각합니다.

아까전에 봤던 끔찍한 3개의 단순 기저벡터의 값은 오일러각으로 본다면 다음과 같습니다

rotation:(15,15,15)rotation : (-15,-15,-15)

다시 한 번 값을 확인해볼까요?

M=[0.920.310.2500.250.930.2600.310.170.9300001]M= \left[ \begin{matrix} 0.92&0.31&-0.25&0\\ -0.25&0.93&0.26&0\\ 0.31&-0.17&0.93&0\\ 0&0&0&1\\ \end{matrix} \right]

Untitled

Untitled

Untitled

Untitled

\therefore 『직관성』

표현방법


이에 대해서 엔진에서 각 회전을 표현할 때, Unity는 단순 부동소숫점 3개(Vector3)로 표현을, Unreal은 rotator이라는 구조체를 사용합니다.

자 여기서 rotator을 조금 더 파고들어 봅시다.

Rotator


Aircraft principal axes

왜 Rotator라는 것이 필요하였을까?


축에 대한 회전은 좌표계가 달라지면 어떻게 회전되었는지 알 수 없습니다.

위가 아래가 되고, 위아래가 뒤집혀 오른쪽이라 생각하는게 왼쪽이 되어버리면 알 수 없습니다.

로컬 수준에서 알 수 있는 좌표계가 필요한 것이죠.

마치 저희가 어떠한 회전을 하더라도 ‘오른손’은 바뀌지 않듯 말입니다.

따라서 회전에 대해 Roll, Pitch, Yaw 회전을 사용합니다.

Aircraft principal Axes

Aircraft principal Axes

이것 또한 이동 회전 크기 행렬의 곱과 마찬가지로 각 축의 회전을 세번 곱할 것인데,

왜 Roll pitch Yaw 순으로 적용하는지에 대해 조금 생각해보았습니다.

해당 Roll Pitch Yaw 회전법은 항공학에서 시작되었다고 하는데, 거기서 생각해본다면 모든 날개의 Flap을 사용해서 얻을 수 있는 회전의 크기를 기준으로 설정하였다고 봅니다.

Untitled

Pitch : 승강타(Elevator) 조작, 핸들의 밀기 혹은 당기기

Pitch : 승강타(Elevator) 조작, 핸들의 밀기 혹은 당기기

Yaw : 방향타(Rudder) 조작, 좌우 페달

Yaw : 방향타(Rudder) 조작, 좌우 페달

Roll : 보조익(Aileron) 조작, 핸들의 회전

Roll : 보조익(Aileron) 조작, 핸들의 회전

Roll의 경우는 보조익(Aileron, 주익의 끝 플랩)을 통해,

Pitch의 경우 승강타(Elevator, 미익의 수평 플랩),

yaw의 경우 방향타(Rudder, 미익의 수직 플랩)를 통해 회전이 이루어 질 수 있기 때문에

플립 혹은 회전의 크기순 내림차순 정렬을 했다고 생각을 합니다.

그리고 중요한점. 이렇게 생각을 한번 하고 나면 Roll - Pitch - Yaw가 헷갈리지 않습니다.

순서는 기억했고, 이게 이제 어떤플랩으로 인하여 회전하는가를 기억한다면 단순하게 플랩 크기로 매핑하면 되기 때문이죠 하하.

오일러 각 회전을 구성하는 세개의 회전 변환


이걸 이제 세 개의 회전변환 행렬로써 표현해 보겠습니다.

기준은 다음과 같이 하겠습니다.

  1. 오른손 좌표계에서 볼때
  2. +방향에서 -방향을 보았을 때
Roll=Rotz=[cosθsinθ0sinθcosθ0001]Roll = Rot_z= \left[ \begin{matrix} cos{\theta}&-sin{\theta}&0\\ sin{\theta}&cos{\theta}&0\\ 0&0&1\\ \end{matrix} \right]

xy평면을 기준으로 x to y 회전을 표현합니다.

Pitch=Rotx=[1000cosθsinθ0sinθcosθ]Pitch = Rot_x= \left[ \begin{matrix} 1&0&0\\ 0&cos{\theta}&-sin{\theta}\\ 0&sin{\theta}&cos{\theta}\\ \end{matrix} \right]

yz평면을 기준으로 y to z 회전을 표현합니다.

Yaw=Roty=[cosθ0sinθ010sinθ0cosθ]Yaw = Rot_y= \left[ \begin{matrix} cos{\theta}&0&sin{\theta}\\ 0&1&0\\ -sin{\theta}&0&cos{\theta}\\ \end{matrix} \right]

xz평면을 기준으로 z to x 회전을 표현합니다. 즉 생각하는 평면(xz)과 실제 바라보는 평면(zx)가 달라, 여기서 뒤집히게(전치) 되어, 상식적으로 생각하는 회전행렬의 기저와 달라지게 됩니다.

최종 회전 행렬


이를 통해 최종적으로 오일러 각 정보를 받아 세 로컬축을 계산한 다음, 기저 벡터를 회전행렬로 사용함으로써 기존과 같이 TRS행렬을 만들어 적용하면 됩니다.

그러나 그 세 로컬축의 합성이 힘들죠.

한번 시작해 봅시다.

저는 3차원 게임세상에서 Roll은 거의 발생하지 않고,

좌우 회전을 많이 한다고 믿는 사람이기 때문에

(또한 팔의 좌우 움직임이 상하 움직임 보다 쉽죠.

롤에서 블루팀 바텀 라인전이 퍼플팀보다 쉽게 느껴지는 이유입니다.)

Yaw→Pitch→Roll 이라는 형태로 합성해 보겠습니다.

하하 Roll→Pitch→Yaw 로 사용하실려면 단순하게 전치하시면 됩니다.

TRS 뒤집는거 해보셨잖아요?

RotzRotxRoty=[cosθzsinθz0sinθzcosθz0001][1000cosθxsinθx0sinθxcosθx][cosθy0sinθy010sinθy0cosθy]=[cosθzcosθxsinθzsinθxsinθzsinθzcosθxcosθzsinθx(cosθz)0sinθxcosθx][cosθy0sinθy010sinθy0cosθy]=[cosθycosθzsinθxsinθysinθzcosθxsinθzsinθxcosθysinθz+sinθycosθzsinθxsinθycosθz+cosθysinθzcosθxcosθzsinθysinθzsinθxcosθycosθzcosθxsinθysinθxcosθxcosθy]Rot_z \cdot Rot_x \cdot Rot_y \\ = \left[\begin{matrix} cos{\theta_z}&-sin{\theta_z}&0\\sin{\theta_z}&cos{\theta_z}&0\\0&0&1\\ \end{matrix}\right] \cdot \left[\begin{matrix} 1&0&0\\0&cos{\theta_x}&-sin{\theta_x}\\0&sin{\theta_x}&cos{\theta_x}\\ \end{matrix}\right] \cdot \left[\begin{matrix} cos{\theta_y}&0&sin{\theta_y}\\0&1&0\\-sin{\theta_y}&0&cos{\theta_y}\\ \end{matrix}\right] \\= \left[\begin{matrix} cos\theta_z & -cos\theta_x sin\theta_z & sin\theta_x sin\theta_z \\sin\theta_z & cos\theta_x cos\theta_z & sin\theta_x (-cos\theta_z)\\0 & sin\theta_x & cos\theta_x \end{matrix}\right] \cdot \left[\begin{matrix}cos{\theta_y}&0&sin{\theta_y}\\0&1&0\\-sin{\theta_y}&0&cos{\theta_y}\\\end{matrix}\right] \\ = \left[\begin{matrix}cos\theta_y cos\theta_z - sin\theta_x sin\theta_y sin\theta_z & -cos\theta_x sin\theta_z & sin\theta_x cos\theta_y sin\theta_z + sin\theta_y cos\theta_z\\sin\theta_x sin\theta_y cos\theta_z + cos\theta_y sin\theta_z & cos\theta_x cos\theta_z & sin\theta_y sin\theta_z - sin\theta_x cos\theta_y cos\theta_z\\-cos\theta_x sin\theta_y & sin\theta_x & cos\theta_x cos\theta_y\end{matrix}\right]

오…

후 아무튼, 비교를 한번 해봅시다.

링크 : https://en.wikipedia.org/wiki/Euler_angles#Tait–Bryan_angles

저는 Yaw→Pitch→Roll로 곱했기 때문에 ZXY이며, 따라서 다음 값을 보면 됩니다.

Untitled

[cosθycosθzsinθxsinθysinθzcosθxsinθzsinθxcosθysinθz+sinθycosθzsinθxsinθycosθz+cosθysinθzcosθxcosθzsinθysinθzsinθxcosθycosθzcosθxsinθysinθxcosθxcosθy]\left[\begin{matrix}cos\theta_y cos\theta_z - sin\theta_x sin\theta_y sin\theta_z & -cos\theta_x sin\theta_z & sin\theta_x cos\theta_y sin\theta_z + sin\theta_y cos\theta_z\\sin\theta_x sin\theta_y cos\theta_z + cos\theta_y sin\theta_z & cos\theta_x cos\theta_z & sin\theta_y sin\theta_z - sin\theta_x cos\theta_y cos\theta_z\\-cos\theta_x sin\theta_y & sin\theta_x & cos\theta_x cos\theta_y\end{matrix}\right]

흠 교환법칙을 고려하면 맞군요.

아주 좋습니다.

두가지 검토가 필요합니다.

하지만 이거 손수 계산하는 검토는

이미 예전에 충분히 빡시게 해보았기 때문에 이번엔 과거의 자료를 들고오며 패스하겠습니다.

검토1. 행렬식이 1.(determine이 1)

장장 3시간에 거친 연산작업... 장장 3시간에 거친 연산작업…

계산기도 돌려봤습니다.

Untitled

제가 작성했던 검토식이 더 이쁘네요 하하.

검토2. 직교행렬이여야함( RTR^T = R1R^{−1} )

이건 아래식을 이용해서 처리하면 됩니다.

ifRT=R1thenRRT=Iif\quad R^T = R^{-1} \quad then \quad R \cdot R^{T} = I

Untitled

Untitled

통합 행렬을 구현할 때의 행렬의 곱셈순서


일반적으로 생각할 때, 크게 상관없다고 느낄 수 있습니다.

하지만 아주 간단한 예시를 들어보죠.

크기변환이 만약 x축이나 y축에 대하여 음수를 갖게 된다면 어떨까요?

예시를 하나 가져봅시다. 크기변환이 1,1 → -1,1 인 S=[1001]\bold S = \begin{bmatrix} -1 & 0 \\ 0 & 1 \end{bmatrix} 행렬과,

회전변환의 각( θ\theta ) 이 45도인 R=[cosθsinθsinθcosθ]\bold R = \begin{bmatrix} cos\theta & -sin\theta \\ sin\theta & cos\theta \end{bmatrix} 행렬 이

v=[10]\vec{v} = \begin{bmatrix} 1\\ 0 \end{bmatrix} 를 변환시키려 합니다.

이하의 케이스는 열기반(column major order) 행렬계산 기준입니다.

SR의 케이스를 생각해보죠. 1사분면과 4사분면의 사이, x축에 얹어져있는 v는

(R)시계방향, 즉 1사분면으로 45도 변환한 다음

(S)2사분면으로 변환합니다

RS의 케이스를 생각해보죠. 1사분면과 4사분면의 사이, x축에 얹어져있는 v는

(S)2사분면과 3사분면의 사이, x축으로 변환된 다음,

(R)시계방향, 즉 3사분면으로 45도 변환합니다.

여기서 R의 경우는 크기가 어떠하든 상관없이 시계방향으로 45도 변환하는 것을 알 수 있습니다.

하지만 S는 회전을 언제 하였나에 따라 최종 결과값이 바뀌게 됩니다.

여기서 볼 수 있듯, 크기는 회전의 영향을 받습니다.

그리고 위에서 말했듯 회전은 이동의 영향을 받습니다.

그렇기 때문에 RS, 이동을 포함한 TRS가

열기반 행렬(column major order) 에서 수행해야 할 옳은 순서 입니다.

유니티는 열기반(column major) 이므로 TRSv 순서로 곱합니다. 행 기반은 vSRT 로 곱하죠. (v = vector)

예를 들어 하나의 물체를 생각해 봅시다.

  • 45도 돌아간 사각형
  • 좌우가 상하에 비해 2배 김
  • 오른쪽로 N만큼 이동

만약 스케일을 하기 전에 회전을 한다면, 옆으로 늘어난 마름모같은 모양이 될 것이고,

이는 아마 저희가 생각하는 결과물과 다를 것 입니다.

또한 회전하기전에 이동을 하여 물체의 중심이아닌 물체의 아래 기준으로 회전한듯한 모양역시

저희가 생각하는 결과물과는 다른 형태입니다.

조금 더 쉽게 보기위해 예제를 준비했습니다.

사실 이것도 예전에 구현했습니다만, 소프트렌더러라 유니티로 변환해보았습니다.

GIF.gif

행렬곱에따라 모든것이 바뀌는걸 볼 수 있습니다.

결론적으로 직관적으로 생각할 때

크기는 회전에 영향을 받으며,

회전은 이동에 영향을 받기 때문에 (어딜 기준으로 커졌음)

영향을 받지 않는 순서인

(영향을 끼치지 않는 순서인)

크기 - 회전 - 이동 순서로 진행을 하게 됩니다.

행렬::역행렬과 전치

강의중에 잠시 말이 나와서 추가로 작성해봅니다.

HLSL에서는 역행렬 함수를 제공하지 않습니다. 그러면 역행렬을 어떻게 구할까요?

직교행렬이라는 개념이 있습니다.

대충 각 축이 모두 직교한다는 뜻인데요,

아까 살짝 위에서 설명했던 식에서 중요한 정보가있습니다.

ifRT=R1thenRRT=Iif\quad R^T = R^{-1} \quad then \quad R \cdot R^{T} = I

이 식입니다.

즉 어떠한 행렬이 직교행렬이라면, 이 행렬의 전치행렬이 역행렬과 같다는 뜻이죠.

이를 통해 전치(transpose)라는 가벼운 계산을 통해 역행렬을 구할 수 있습니다.

약식 실습 - 아웃라인

자 간단하게, 패스를 하나 작성하고 lit에 추가한 다음,

pass tag를 이용하여 opaque를 그리기 전에 렌더링 할 것입니다.

제약에 제약에 제약입니다.

정상적으로 동작하지 않는 실험실 버전이지만, 시간이 없으니

(다른거 하다가 실패해서 시간이 없음)

빠르게 빠르게 해봅시다.

Untitled

패스 작성 했으면 lit 쉐이더 복붙해서

Untitled

아웃라인 패스 추가해주고,

Untitled

커스텀 에디터 삭제해서

Untitled

프로퍼티에 원하는 값을 넣을 수 있도록 합니다.

여기까지 1분!!

Untitled

렌더파이프라인에 피처 하나를 추가해서

OutlinePass를 그리도록 설정합니다.

불투명 객체를 그리기 전에요!

여기까지 10초!

언제든지 다시 그리기 위해서 깊이를 검사하진 않습니다! 이게 야매의 맛이죠!!!

그럼 70초만에 끝!

GIF.gif

원리는 간단합니다.

모델링공간에서 버텍스의 위치를 모델링 좌표의 원점 기준에서 늘려줍니다.

그것을 단순한 색상으로 한번 그린다음, 원래대로 다시 그린 것 입니다.

두번 그린것이죠.

여기서 버텍스의 위치를 늘리는 방법이,

원점과의 거리, 즉 모델링공간에서의 position 값을 곱하거나,

노멀방향으로 늘리거나,

화면공간에서의 범람연산을 통해(Jump floold algorithm) 구하거나,

등등의 방법이 있습니다.

하드엣지, 노말이 다른 같은위치의 버텍스 가 존재하는 큐브에 제대로된 아웃라인을 그리기 위해선

노말방향으로 곱하면 면이 분리되기 때문에 단순히 곱해서 처리합니다.

여담

JFA는 터졌습니다!!!

오프셋을 준 텍스처의 누적을 통한 거리 도출,

Screen Space Distance Field를 학습하며 구현하는 도중…

Untitled

Untitled

Untitled

Untitled

이거짜고있었는데!!!!

URP로 변환을 끝끝내 하지 못해 최종적으로 과제제출의 압박으로 터졋습니다….

This post is licensed under CC BY 4.0 by the author.
Contents