경북대 '컴퓨터그래픽스' 강의 백낙훈 교수님의 강의를 듣고 복습하기 위한 게시글입니다.
게시글의 내용은 강의 또는 구글링을 통해 공부하였습니다.
이미지 자료는 출처를 밝히거나 직접 그려 사용하였습니다
View transform에 일환인 Look-At, Orthographic projection에 들어가기 앞서
간단하게 이걸 어디서 쓰는지 보자.
일단 Model transform을 해야되는데 이것은 우리가 World frame 좌표 기준으로 물체를 배치하는 것이다.
이 말은 우리가 조물주라면 3인칭 공간이 있으면
어 저 건물은 (1, 2, 2.5) 위치에 배치하고, 가로등은 (0, 0, 1.0) 위치에 배치해
처럼 다른 것을 고려하지 않고 가상의 공간에 원하는 좌표에 물건을 배치하는 것이다.
View transform은
world transform 을 거친 3D 공간의 물체들을 우리 화면인 2D로 볼 수 있게 우리 화면 상에서 보이는 좌표로 변환한다.
world frame 전체를 view frame으로 변환하는 것이다.
기본적으로 OpenGL default view 설정은
오른손 좌표계 기준
-z 방향으로, z = -1.0 ~ + 1.0 내의 물체를 볼 수 있다.

Viewing API 용어
p , VRP : view reference point, 카메라의 위치
n , VPN : view plane normal, view plane에 수직, 카메라 뒤쪽을 가리키는 수직인 unit 벡터
v , VUP : view up vector, view plane의 y 방향, 윗방향이 어디인지 나타내는 벡터
* view plane = window (우리가 보는 화면)
다시 그림으로 표현하면,

u vector는 n x v 하면 나온다.
이러한 벡터들이 존재해야 view transform을 할 수 있다.
이제 Look-At 방법을 보자
기존의 위와 같은 방식으로 matrix를 사용하여 구하면( view tranform은 matrix를 사용한다),
n vector는 unit vector 로 된 world frame 상의 vector 가 필요하고,
n vector와 수직이 되는 v vector가 필요하고, 둘 다 직교해야되고 등등
계산이 복잡하다.
그래서 더 직관적인 방법이 필요해 나온게 Look-At 방법이다.
Look-At Approach는
3가지 정보가 필요하다.
모두 world frame 좌표이다.

at : look at the point, 대상이 되는 물체의 좌표
eye : from the view point, 카메라 위치
up : with the film up vector, 카메라 위쪽 방향을 가리키는 벡터
* 여기서 up vector는
unit vector 일 필요 x, 수직일 필요 x, 카메라 윗방향이 이쪽이다 라는 대강의 vector이면 된다.
훨씬 직관적이다 !
근데
처음에 Viewing API 에서 나온 3가지 벡터가 있어야 view transform을 할 수 있다고 했다.
그럼 at, eye, up 정보로 VRP, VPN, VUP 를 계산해보자.
1.
VRP : p = eye
VRP도 카메라 좌표이고, eye도 카메라 좌표이기 때문에 동일하다.
2.
VPN : n = -normalize(at - eye) = normalize(eye - at)
VPN 은 카메라 뒤쪽을 가리키는 unit vector이다.
"카메라 뒤쪽을 가리키는" = 물체의 위치부터 카메라 위치의 벡터 = - (at - eye) = (eye - at)
* (at - eye) 벡터는 카메라에서 물체까지의 벡터가 되므로 ' - ' 를 곱해 반대방향을 만든다.
"unit vector" = 단위가 1인 벡터 = normalize 하면 된다.
그럼 위의 계산식이 성립이 된다.
그리고 바로 VUP 를 구하는게 아니라
u vector를 먼저 구한다.

현재 n vector(VPN)까지 구한 상태이다.
현재 up vector와 normal vector가 수직이라는 보장이 없다.
( 카메라 윗쪽 방향이 이쪽 정도이다라는 대충의 벡터기 때문)
그래서 up vector와 normal vector를 cross product 하면
up, normal 이 이루는 평면에 수직인 벡터가 나온다.
이게 u vector이다.
그럼 이 u vector는 n vector에 수직이 보장되므로
u x n 을 normalize 하여 v = VUP 를 구한다.
3.
u = normalize( up x n )
VUP : v = normalize( n x u )
GLM 에서 lookAt 함수를 제공한다.
함수를 사용해보자.
glm::mat4 glm::lookAtRH(glm::vec3 const& eye, glm::vec3 const& at, glm::vec3 const& up)
* 앞서 봐왔듯이 인자로 eye, at, up 세가지 정보를 사용한다.
함수 이름 'lookAt' 뒤에 RH는 right handed, 오른손 좌표계 기준으로 하겠다는 거다.
LH(left handed) 도 있고, 그냥 lookAt만 있는 함수도 있다.
여기서는 lookAtRH를 사용할 예정이다.

대강 요러한 환경으로 놓여져있고, ( 피라미드 대충 그린 거 아님 ㅎ)
default view setting 은 -z 방향으로 바라보고있다.

아래와 같이 출력된다.
그럼

카메라 위치를 조정해서 ( -0.3, 0.3, 0.3 ) 좌표에서 ( 0, 0, 0) 원점 방향으로 바라보자.
왼쪽 앞, 살짝 위에서 내려다 보는 그림이 될 거다.
glm::mat4 matView = glm::mat4(1.0F);
...
matView = glm::lookAtRH(
glm::vec3(-0.3F, 0.3F, 0.3F), // eye
glm::vec3(0.0F, 0.0F, 0.0F), // at
glm::vec3(0.0F, 1.0F, 0.0F) // up
);


해당 위치에서 내려다 보게 수행되는 볼 수 있다.
이번에는 정면 조금 앞쪽, 오른쪽 위에서 cube에 포커스를 맞춰 내려다보자
glm::vec3 cube_position = glm::vec3(0.4F, 0.0F, 0.0F);
matView = glm::lookAtRH(
glm::vec3(0.3F, 0.2F, 0.6F),
glm::vec3(cube_position.x, cube_position.y, cube_position.z),
// = cube_position 넣어도 됨
glm::vec3(0.0F, 1.0F, 0.0F)
);

역시 잘 수행된다.
근데 문제가 곧 생긴다!
이제 기본 정면에서 조금 오른쪽 위에서 원점을 내려다보자.
matView = glm::lookAtRH(
glm::vec3(0.5F, 0.3F, 0.3F),
glm::vec3(0.0F, 0.0F, 0.0F),
glm::vec3(0.0F, 1.0F, 0.0F)
);

화살표가 가리키는 곳을 보면
분명 피라미드 모양이라서 잘 나와야될 텐데
저렇게 잘려서 출력되는 걸 볼 수 있다.
본인은 저거 때문에 backface culling 때문인가 대체 왜그러는 거지 하면서
생각을 해봤다.
문제는 카메라 본질에 있었다.
OpenGL 카메라는
Canonical View Volume(정규 뷰 볼륨)을 사용한다.
normalized된 좌표계이다.
x, y, z : [ -1, +1] x [ -1, +1] x [ -1,+1]

하드웨어적인 세팅이다.
그럼 아까 문제가 생겼던 화면을 y축 위에서 x - z평면을 바라보면
아래와 같이 된다.

이렇게 normalize된 size다 보니
일정 거리가 넘으면 잘려서 출력되지 않는 것이다.
이를 보안하기 위해
Orthographic projection이 사용된다.
Orthographic projection
Orthographic projection 은
1. parallel projection 2. perspective projection 중
parallel projection의 일종이다. ( perspective projection은 추후에 다룸)
모든 물체를 평행하게 촬영한다.
직육면체의 영역을 평행하게 scaling해 계산이 용이하고, 길이/거리에 대해 왜곡이 없다.

좌표축에 평행하게 직육면체 영역을 설정하고,
Canonical view volume에 projection한다.
zNear, zFar 값은 camera/viewer로 부터의 거리(distance)로 설정된다.
즉, 양수 positive number여야 한다.
zNear, zFar은 좌표가 아닌 거리를 넣어야 되는 걸 주의하여야한다.
GLM은 otrhographic projection에 대한 함수도 제공해준다.
glm::mat4 glm::orthoRH(GLfloat left, right, bottom, top, zNear, zFar)
* lookat 함수처럼 RH, LH 등이 있다.
zNear, zFar에 대해서는 positive value여야한다.
아까 문제가 생긴 Look at 코드에서 projection 을 추가하였다.
glm::mat4 matProj = glm::mat4(1.0F);
...
// projection matrix
matProj = glm::orthoRH(
-1.0F, +1.0F,
-1.0F, +1.0F,
+0.1F, +3.0F
);

zNear, zFar 값을 0.1 , 3.0 을 넣어
카메라로부터 0.1 ~ 3.0 내의 물체를 촬영한다는 말이 되어
잘 수행되는 걸 볼 수 있다.
'컴터 > OpenGL' 카테고리의 다른 글
[OpenGL] Shading (0) | 2023.06.10 |
---|---|
[OpenGL] View Frustum, FOV(field of view) (2) | 2023.06.10 |
[OpenGL] VBO(Vertex Buffer Objects) (2) | 2023.06.10 |
[OpenGL] Back face Culling (0) | 2023.06.10 |
[OpenGL] Z-buffer method (0) | 2023.06.10 |