본문 바로가기
컴터/OpenGL

[OpenGL] Look-At, Orthographic Projection

by 나 진짜 못차마 2023. 6. 10.
728x90

경북대 '컴퓨터그래픽스' 강의 백낙훈 교수님의 강의를 듣고 복습하기 위한 게시글입니다.

게시글의 내용은 강의 또는 구글링을 통해 공부하였습니다.

이미지 자료는 출처를 밝히거나 직접 그려 사용하였습니다

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 내의 물체를 촬영한다는 말이 되어

잘 수행되는 걸 볼 수 있다.

728x90

'컴터 > 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