Real Time Rendering 3장 Transformation 쿼터니언을 제외한 내용 정리
1. 기본 변환
여기서 다룰 변환이란 물체의 이동, 크기, 회전 등의 변환 행렬의 결합을 말한다.
벡터만 있으면 위치는 어떻게 표시하는가
벡터는 방향과 크기만 같으면 같은 벡터이다.
그래서 위치를 표현하기 위해서는 점을 표기할 필요가 있었다.
그래서 이 벡터와 점을 동일한 형태로 표현하기로 약속했는데, 이 방법을 바로 동차 표기법이라고 한다.
기본적으로 3D rendering pipe line에서는 기하 공간을 표현하기 위해 동차 표기법을 사용하며 이렇게 표현한 공간을 Affine Space라고 한다.
동차 표기법에서 점과 벡터는 (x, y, z, w) 형태, 그러니까 4x1 혹은 1x4 행렬로 표현될 수 있음
ex ) (1,2,3,0) (1,2,3,1)
w가 0이면 벡터이고, 0이 아니면(보통 1) 점.
이렇게 표현하면 점과 벡터의 모든 변환을 4x4 행렬로 표현할 수 있고, 그렇기 때문에 연산 시간을 최소화 시킬 수 있어서 효율적
1.1. 평행 이동 (Translation)
평행 이동은 한마디로 x축, y축, z축에 평행하게 이동시키는 것이다
그런데 벡터에는 평행이동이 의미가 없다. 벡터는 크기와 방향만 가지기때문.
수학적으로도 이건 뚜렷하게 드러나는데, 평행이동 행렬은 다음과 같다
T =
변환될 점을 P`라고 할 때 PT = P`
(x, y, z, 0) 의 형태를 가진 벡터를 아무리 이 행렬에 곱하더라도, 4번째 성분인 w가 0이니, T행렬에 영향을 받지 않는다.
평행이동의 역행렬은 당연히 역행렬은 직관적으로 T(-t)
1만큼 이동시킨걸 -1만큼 다시 이동시키면 원래 위치로 돌아옴.
그런데 왜 평행이동에서 단순히 덧셈을 하지않고 사람에겐 더 복잡해보이는 행렬 곱셈을 할까?
그건 하드웨어, 그러니까 그래픽카드가 행렬의 곱셈에 최적화되어있기 때문.
1.2. 회전 (Rotation)
회전 이동은 x축, y축, z축을 기준으로 회전하는 것을 말한다.
변환될 점을 P`라고 할 때 PR = P`
여기서 φ는 항상 반시계 방향으로 정의된 각
당연히 x축에 대해 회전할 때는 x값은 변하지 않으니까 첫번째 행은 그대로.
z축에 대한 회전변환 행렬도 짐작할 수 있다.
sin cos 합공식을 이용해서 증명 가능
회전 행렬의 역행렬은, R^-1(φ) = R(-φ).
그리고 이건 R행렬을 Transpose, 즉 전치시킨 것과 같다.
전치 행렬과 역행렬이 같은 행렬은 직교 행렬이라고 하는데, 직교 행렬은 역행렬을 쉽게 구할 수 있기 때문에 의미가 있다.
직교 행렬의 정의는 행렬의 각 행벡터들이 서로 직교적인 단위 벡터로 이루어진, 즉 두 행벡터의 내적이 항상 0이고 각 행벡터의 길이가 1인 행렬.
단위벡터인 행벡터끼리 직교한다는 뜻.
이 세개의 회전 변환은 항상 원점이 기준이라는 것이 문제인데, 만약 우리가 원점이 아닌 특정 점에 대해서 회전 변환을 하고싶다면 어떻게 해야할까
특정 점을 P라고 할 때, T(-P)로 특정 점을 원점으로 위치시킨 후 원하는 회전 변환을 적용하고 T(P)를 다시 적용시키면 됨.
특정 점에 대한 회전 변환 행렬 X = T(p)R(φ)T(-p)
1.3. 크기 조정 (Scaling)
크기 조정은 x축, y축, z축에 대해서 크기를 늘리거나 줄이는 걸 말한다.
변환될 점을 P`라고 할 때 PS = P`
Sx, Sy, Sz가 같을 때 등방 크기 조정이라고 함. (uniform scaling)
크기 조정 행렬 S의 역행렬은 S(1/ Sx, 1/Sy, 1/Sz) 겠죠. 5배 키웠으면 1/5배 다시 줄이면 원래대로 돌아옴
이 세개의 성분 중 하나라도 -1 값을 가지면 반사 행렬이라고 하는데, 만약 하나 혹은 세개가 -1 이면 거울 행렬이라고 한다.
만약 이러한 반사 행렬들이 검출되면 조심스럽게 다루어야 하는데, 감기 방향이 반대로 되어서 backface culling이나 조명 효과 계산이 잘못될 수 있다고한다. 행렬의 판별자가 음수라면 반사 행렬이라고 하니 참고.
이 행렬 또한 x축, y축, z축에 대해서만 크기 조정을 수행하는데, 만약 이 세 축이 아닌 임의의 축으로 크기 조정을 적용하고 싶다면 어떻게 해야할까? 우선 직교하는 세개의 행렬이 있어야함. 이 세개의 행벡터(fx, fy, fz)를 이용해 만든 4x4 행렬 F를
[ fx fy fz 0 ]
[ 0 0 1 0 ] 라고 정의 할 때, 이 행렬의 역행렬( 직교 행렬이기 때문에 Transpose 전치한 행렬과 같다) 을 곱해 x축, y축, z축으로 일치시킨 후 원하는 크기조정 변환을 적용하고, 다시 F행렬을 곱함.
특정 벡터에 대한 크기 조정 변환 행렬 X= FS(s)FT
1.4. (쉬어) 전단 변환 (Shear)
전단 변환이란, 특정 축을 기준으로 어떤 한 축을 밀어내는 것을 말함
그러니까 손으로 민 것 처럼 찌그러트린다는 건데, 그림으로 보면 다음과 같음
그림과 같이 x축 좌표를 y축에 대해서 밀었을 때, 변환 행렬은 다음과 같다.
x` = x + s * y 와 같은 관계식을 가질 때 Hxy
Hyz, 혹은 Hzx와 같은 변환 행렬들도 짐작 가능
Hij와 같이 표현 할 때, i는 변경될 좌표, j는 변환이 수행될 좌표이다.
Hij(s)의 역행렬은 Hij(-s).
1.5. 변환의 결합 (Concatenation of Transforms)
변환의 결합은 순서에 종속적이다.
교환법칙이 성립하지 않는다. -> 비가환성
이건 단순한 변환 몇개만 가지고만 곱해봐도 알 수 있는데, 평행이동 후 x축에대한 회전과 x축에대한 회전 후 평행이동은 완전히 다른 결과를 불러온다는 것을 직관적으로 알 수 있다.
여러 변환 행렬을 하나로 곱해서 적용하는 이유는 연산 효율을 높이기 위해서인데, 결과적으로 모든 변환 행렬이 곱해진 후에는 어떤 변환이 적용된 것인지 알기 힘들다는 단점이 있다.
1.6. 강체 변환 (The Rigid-Body Transform)
강체는 이름처럼 단단한 물질이란 뜻인데, 외력을 가해도 형태가 변하지 않는 이상적인 물질을 말한다.
이 강체 변환은 그러니까 형태를 변화시키지 않고 위치나 회전값만 바꾼다는 뜻
강체 변환의 주된 특징은 길이와 각도가 보존된다는 것이고, 평행이동 변환과 회전 변환이 포함된다.
1.7. 법선 벡터 변환 (Normal Transform)
일반적으로 사용되는 변환 행렬은 법선 벡터를 변환시키는데 적용될 수 없다.
결론부터 말하자면 법선 벡터 변환 행렬은, 일반 변환 행렬의 역행렬의 전치행렬
(증명추가바람)
1.8. 역변환 계산
단순, 단일 변환의 역변환일 경우에, 변환 행렬 M이 T(t)R(φ) 일때, 역변환 M^-1 은 R(-φ)T(-t)와 같이 쉽게 구할 수가 있고,
변환 행렬 M이 직교 행렬(이를테면 회전 변환)일 때에는 그냥 전치해주면 쉽게 구할 수 있다.
그외에는 가우스 소거법이라던지 LU 분해법, 가우스 조던 방법 등이 있다. 상황에 따라 적합한 방법이 다르다. 최적화 문제를 고려할 때에는, 벡터 변환을 위한 행렬의 역을 계산할 때에는 3x3부분만 계산하면 된다.
2. 특수한 행렬 변환과 연산
2.1. 오일러 변환
오일러 변환은 오일러가 고안해낸 방향을 정하는 직관적 방법이다.
가장 많이 쓰는 기본적인 방안은 머리가 향하는 방향을 up벡터로, 즉 y축으로 정하고 시야의 음의 방향을 z축으로 정하는 것이다.
head(yaw), roll, pitch로 많이 설명되는데 head는 도리도리하는 회전, roll은 갸웃갸웃하는 회전, pitch는 끄덕끄덕하는 회전.
오일러 변환 행렬 E(h, p, r) = Rz(r)Rx(p)Ry(h) 로 정의될 수 있다.
직교 행렬인 회전 행렬로만 이루어져 있으므로 역행렬은 전치행렬의 거꾸로 구성으로 쉽게 구할 수 있다.
2.2. 오일러 변환으로부터 매개변수 추출
오일러 변환 행렬로부터 매개변수를 추출할 수 있다.
오일러 변환 행렬 E(h, p, r)이 주어졌을때 이것을 식으로 풀어보면 다음과 같다.
p은 2번째 행 1번째 열 성분 F21 = sin p 를 arcsin해서 구할 수 있고,
r은 F01/F11 = -sin r cos p / cos r cos p = -tan r 을 atan해서 구할 수 있고,
h은 F20/F22 = -tan h 을 atan해서 구할 수 있다.
cos p가 0일 때가 문제인데, 이때엔 오히려 식이 다 소거되면서 더 간단해지기 때문에 쉽게 구할 수 있다.
오일러 변환에는 치명적인 문제점이 있는데, 그것은 세 축이 회전에 대해 서로 종속적이라는 것이다.
그 때문에 자연스러운 회전 보간을 구현하기 어렵고, Gimbal Lock 짐벌락 현상이 발생하는데, 이것은 동영상을 보면 어떤 현상인지 쉽게 이해할 수 있다.
https://www.youtube.com/watch?v=zc8b2Jo7mno
이 짐벌락 현상은 쿼터니언, 즉 사원수를 이용해서 해결할 수 있다고 한다. 사원수에 대한 포스팅은 다음에..
예전에는 오일러 변환을 사용하면서 짐벌락 현상을 피하기 위해서 0.01도씩 살짝살짝 돌려서 락을 회피하는 꼼수를 쓰기도 했다고 한다.
2.3. 행렬 분해
지금까지 소개한 변환 행렬들은 비교적 명확하게 생겨서, 그 행렬이 의도하는 바를 쉽게 알 수 있었지만 실제로 이런 경우는 극소수이다. 그러나 결과적으로 생성된 변환 행렬이 어떤 변환을 수행하는지 아는 것은 상당히 중요한 일일 수 있다. 그 변환 행렬을 추출해 내는 것을 행렬 분해라고 한다. 지금까지 배운 것들로는 제한적인 상황에서만 행렬 분해가 가능하다.
행렬 분해는 어떤 경우에는 상당히 복잡한 문제일 수 있다.
2.4. 임의의 축 주위로 회전 변환
x,y,z축이 아닌 임의의 축으로 변환을 하는 법은 우선 두가지가 있는데, 결론적으로는 같은 방법이다.
첫번째 방법은, 임의의 축을 기준으로 x, y, z와 같은 직교 단위 벡터 3개를 구하는 것이다.
첫번째 벡터의 성분 하나를 제거하고, 나머지 성분 두개의 위치를 바꿔치기하고 -1 을 곱한다. 그러면 내적이 0이 되니까 직교하는 벡터가 됨. 그리고 세번째 벡터는 그 두개를 외적하면 됨.
직교하는 세개의 벡터를 구했다면 그 축이 x, y, z 축과 일치하도록 변환한 후, 원하는 회전 변환을 적용. 그리고 처음에 x, y, z 축과 일치하도록 하기위해 했던 변환의 역변환을 다시 적용해주면 된다.
두번째 방법은 x, y, z 세개의 벡터를 구하는 것이 아니라 해당 벡터를 정규화한 후 하나의 축에 일치시키고 그 축에 대해 회전을 적용하고, 다시 첫번째 변환의 역변환을 적용하면 된다. 사실상 같은 방법.
3. 사원수
사원수 파트는 다음에 따로 포스팅..
4. 정점 혼합 (Vertex Blending)
정점 혼합은 보통 캐릭터의 애니메이션에 많이 쓰이는 기법.
캐릭터의 팔이 다음과 같이 팔꿈치를 기준으로 두개의 메쉬로 이루어져있다고 생각을 해보면
팔은 이와같이 움직이면서 애니메이션 된다.
팔꿈치 부분을 자연스럽게 보간하기 위해 고안된 기법이 정점 혼합.
피부를 씌우는 기법이라 해서 스키닝이라고도 불리고, 엔빌로핑, 골격-부분 공간 변형이라고도 불린다고 한다.
그러니까 팔꿈치를 피부처럼 유연한 정점으로 만들어보면,
이런식으로 주황색 피부부분이 위쪽 팔과 아랫쪽 팔에 자연스럽게 영향을 받는다.
위쪽 팔에 달려있는 뼈의 변환 행렬과, 아랫쪽 팔에 달려있는 뼈의 변환 행렬이 있다고 치면,
저 관절 부분은 위치에 따라서 위쪽 1/3, 아랫쪽 2/3. 윗쪽 1/2, 아랫쪽 2/1 이런식으로 보간되면서 영향을 받는다고 보면 되요.
그러니까 관절을 이루고 있는 삼각형 폴리곤들이, 여기저기에서 영향을 받는 정점으로 구성됨. 이 기법을 stitching이라고도 함
정점 블렌딩을 수행할 수 있는 방법은 정점 쉐이더를 사용하는것. 하드웨어에 의해 가속됨.
문제는 충돌을 구현하거나 그림자를 만들어내는 건데, 그렇게 되면 정점에 접근하기 위해 혼합연산을 CPU에서 해야한다.
눈에 보이는 문제점은, 생각보다 이렇게 해서 만들어낸 팔꿈치가 사람 팔꿈치같지가 않다는 건데,
고무장갑이 접힌 듯이 고무인형 팔꿈치처럼 보이는 현상을 folding 접힘 현상이라고 하고 등등의 비틀림, 자기교차 현상이 일어나기도.
5. 투영 (Projection)
투영이라는 건 말그대로 3차원 공간을 2차원에 투영시켜서 그림을 만들어내는 건데,
그래픽 렌더링 파이프라인에서 보면 투영 단계를 기준으로 3d 와 2d로 나누어진다.
우리가 보는 모니터는 2차원이기때문에 3차원 이미지는 투영을 거쳐야만 그려질 수 있다.
단순한 볼륨상에 투영시킨 후, 디바이스의 해상도에 맞게 조정해준 후 픽셀을 밝히는 작업이 필요하다.
투영 변환을 제외한 변환들에서는, 점은 변환하면 점이되고, 벡터는 변환하면 벡터가 되었지만 투영변환은 그렇지 않다.
투영에는 perspective 프로젝션과 orthographic 프로젝션이 있다.
먼저 오른손 좌표계와 왼손 좌표계에 대해 언급하자면
결국 z축이 어디를 향해 나아가느냐가 차이점인데, 내가 보는 방향으로 나아가는 것이 왼손 좌표계, 그 반대가 오른손 좌표계.
OpenGL을 포함해서 대부분의 그래픽스 시스템은 오른손 좌표계를 사용하고(보편적), DirectX는 왼손좌표계를 사용한다.
5.1. 직교 투영 (Orthographic Projection)
투영을 한 후에도 평행성을 유지하는 투영을 직교 투영이라고 한다.
실제로 우리 눈에는 멀게 있는것은 작게 보는데 직교투영은 그걸 무시한다.
이렇게 딱 떨어지는게 직교 투영
이렇게 투영하려면 z축 성분을 단순히 제거하면 된다.
여기서 문제는, 뒤에 있는 물체던지 앞에 있는 물체던지간에 전부다 한 평면으로 투영되기 때문에 3차원 공간으로 다시 복원시킬 수 없다는 것. 렌더링 할 때에는 Z버퍼가 있기 때문에 괜찮음
무한한 공간을 전부다 투영할 수는 없기 때문에 뷰 프러스텀, 즉 시야 절두체라는 직육면체를 만들어서 어느 공간까지 볼건지를 정한다.
(l, r, b, t, n, f) : 왼쪽, 오른쪽, 아래, 위, 근평면, 원평면
이런식으로 직육면체를 정의한다.
오른손 좌표계를 사용하고 있기 때문에 근평면이 +2가 되어야하고 원평면이 -2가 된다.
그러니까 (-10, 10, -20, 20, 2, -2) 이렇게 써줘야함
다이렉트X는 게임을 만들기 위해 만들어진 API이고, 게임에서는 공간 좌표가 직관적인 것(멀리 있는 놈 좌표 값이 더 큰 것)을 선호하기 때문에 왼손 좌표계를 사용한다는 말이 있음.
괜히 OpenGL과 차별화를 두고싶었는지도 모른다는 말도 있음
다행히도 OpenGL에서는 glOrtho에 우리가 -2, 2 라고 반대로 써도, 내부적으로 그걸 음수로 바꿔서 수행한다고 한다
어쨌든 그래서 직교투영 변환 행렬은 근평면이 더 크기때문에 항상 거울 변환을 포함한다고 함.
그래서 이 뷰 프러스텀을 만들고 나면
canonical view volume이라는 정규화된 장치 좌표로 바꿔줘야 함.
이 정규화된 장치 좌표라는 건 뭐냐면 OpenGL에서는 (-1, -1, -1), (1, 1, 1)의 꼭짓점을 가진 정육면체, DirectX는 (-1,-1,0)과 (1,1,1)의 꼭짓점을 가지는 직육면체인데, 이렇게 변이 2 혹은 1인 직육면체로 바꾸면 연산이 훨씬 간편해지고, 클리핑에도 효율적이고 좋은점이 많다.
시야 절두체를 정규화된 장치 좌표로 바꿔주는 과정을 책에서는 이런 그림을 가지고 표현함
먼저 중심점이 원점에 오도록 평행이동 후, 변의 길이를 scaling해서 변을 2로 맞춰줌!
그러면 이제 최종 직교투영 식은 이렇게 됨!
뭔가 복잡해 보이지만 원리를 이해하고 보면 단순함!
5.1. 원근 투영 (Perspective Projection)
어쨌든간에 원근투영은 직교 투영보다 훨씬 더 많이 쓰이는 데, 이게 더 실제적이기 때문.
평행선은 투영 후에는 더이상 평행하지 않고, 계속 진행하면 한 점으로 수렴하도록 변한다!
시야 절두체가 시점으로부터 근평면 꼭지점으로 무한히 퍼져나가는 형태를 띄고, 이것을 나중에 정규화된 장치 좌표로 바꾸기 때문.
사각뿔대 같은 모양임 이 모양을 직육면체로 바꾸면 한 점으로 수렴하도록 찌그러든다.
그리고 시야가 너무 넓으면은 광각 렌즈로 찍은 사진처럼 모서리 부분이 왜곡되는 현상을 경험할 수가 있다.
또 먼 물체일수록 축소 효과가 극대화되기 때문에, 원평면은 너무 멀게 잡지 않는 게 좋다.
또 가장 가까운 물체는 카메라에 가까이 붙이는 게 원근 효과가 뚜렷이 나타난다.
책에서는 일단 z = -d ( d>0 ) 평면에 투영하는 경우를 간단하게 설명하고 있다.
요런 모양임
p를 z = -d 상에 있는 점 q로 투영하는 모양.
이제 그러면 오른쪽 그림을 통해서 q좌표를 얻는다
수식을 써보면
px/qx = pz/-d
여기서 qx = -d * px / pz
그러면 qy = -d * py / pz
qz = -d
임의의 점 p를 곱해보면
( px, py, pz, -pz/d )가 나오는데, 원근 투영 변환에서는 항상 마지막 w 성분을 1로 만들어주는 동차화 과정이 있어야 한다.
그래서 결론적으로는 ( -d * px / pz, -d * py / pz, -d, 1 )
사각뿔대같은 시야 절두체를 정규화된 장치 좌표로 바꾸는 건 직교 투영에서보다 훨씬 어려운데,
결론부터 말하면 이런 모양을 띈다.
여기에는 시점을 원점으로 옮기고, 직육면체가 되도록 쉬어하고, 평행 이동시키는 등등의 과정들이 들어있음.
RTR 3장 요약 끝
출처: https://hoodymong.tistory.com/2?category=628799 [handhp1.tistory.com 으로 블로그를 이전하였습니다.]
'book report' 카테고리의 다른 글
Computer Networking - A Top Down Approach (COSE342 컴퓨터 네트워크) (0) | 2021.09.06 |
---|
댓글