본문 바로가기

컴퓨터그래픽스

[OpenGL 공부] Camera (1)

https://learnopengl.com/Getting-started/Camera

 

LearnOpenGL - Camera

Camera Getting-started/Camera In the previous chapter we discussed the view matrix and how we can use the view matrix to move around the scene (we moved backwards a little). OpenGL by itself is not familiar with the concept of a camera, but we can try to s

learnopengl.com

 

목표

  • View matrix의 구성
  • OpenGL에서 카메라 설정 방법 및 예제 작성

카메라 / 뷰 공간

카메라를 정의하기 위해 필요한 요소

카메라를 정의하기 위해 필요한 요소는 다음과 같다.

1. Position : 월드 좌표계 내에 존재하는 카메라의 위치

2. Direction : 카메라가 바라보는 지점(0, 0, 0)으로부터 카메라의 위치로 향하는 벡터, 즉 카메라의 방향

3. Right : 카메라의 오른쪽을 가리키는 벡터(오른손 좌표계 기준)

4. Up : 카메라의 위쪽을 가리키는 벡터

 

위의 네 가지 요소를 통해 카메라가 정의되면, 카메라의 위치(0, 0, 2)를 기준으로 하는 3개의 수직 단위 축으로 한 또 하나의 좌표계가 생성되는 것을 알 수 있다. 이를 통해 월드 좌표계에서 시점 좌표계로의 변환이 이루어진다.

 

카메라의 각 요소 정의

1. Position

glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 3.0f);

카메라는 (0, 0, 3) 위치에 정의된다. 이때 기본적으로 화면을 뚫고 나오는 축이 +z축이기 때문에, 카메라를 뒤로 움직일 경우 양의 z값으로 지정해야 한다.

 

2. Direction

벡터 간의 뺄셈을 통해 카메라가 바라보는 지점으로 부터 카메라로 향하는 벡터를 구할 수 있다.  Direction은 카메라의 한 축이기 때문에 normalize 함수를 사용해 정규화해야 한다.

glm::vec3 cameraTarget = glm::vec3(0.0f, 0.0f, 0.0f);
glm::vec3 cameraDirection = glm::normalize(cameraPos - cameraTarget);

 

Direction의 방향이 카메라로 향하는 이유?

카메라의 축이 정의될 때, 카메라로부터 바라보는 지점으로 뻗어나가는 것이 아니라 반대 방향으로 정의되는 것을 알 수 있다. 이는 OpenGL에서 월드 좌표계의 z축의 양의 방향과 동일하게 카메라의 축을 구성하기 위함인데, 만약 Direction의 양의 축이 월드 좌표계의 -z 방향, 즉 카메라로부터 바라보는 지점으로 향하게 된다면 서로 상쇄되어 무효화가 되기 때문이다.

 

3. Right

벡터의 외적을 사용하면 해당 벡터에 수직인 다른 벡터를 구할 수 있다. cross 함수는 외적을 수행하며, 카메라 좌표계의 x축을 얻기 위해 서로 직교하는 두 벡터에 대한 외적을 계산한다. 외적에 대한 계산은 그람-슈미트 직교화 과정을 통해 이루어진다.

그람-슈미트 과정의 기본 원리

 

그람-슈미트 과정은 내적 공간에서 유한 개의 선형 독립 집합을 정규 직교 기저로 변환하는 방법이다.

내적 공간(inner product space) : 두 벡터의 쌍에 스칼라를 대응시키는 일종의 함수가 주어진 벡터 공간으로, 벡터의 길이나 각도를 다룰 수 있는 공간

 

첫 번째 단계로 v1(=u1)에 대해서 직교하는 벡터를 만들기 위해서 v2를 v1으로 정사영한다.

두 번째 단계로 정사영한 벡터에서 v2를 뺄셈 하면 u1에 직교하는 벡터 u2를 구한다.

u1, u2를 정규화하여 최종적으로 정규 직교 기저(e1, e2)로 만든다. 생성된 {e1, e2} 집합은 직교적이다.

 

v에 대해 u로 정사영한 벡터는 다음과 같이 구할 수 있다.

< >는 내적을 의미한다.
그람-슈미트 과정의 연산

 

아래 코드의 위의 그람-슈미트 과정을 통해 Right 축을 구하는 코드이다.

앞서 카메라의 Direction 축이 월드 좌표계의 축과 동일하므로 Direction 축에 수직으로 하는 벡터를 생각해보면 간단하게 정의될 수 있는데,  다른 벡터에 대해 수직인 벡터인 그람-슈미트 과정을 사용하여 Right 축을 계산할 수 있다. 이 경우 Up과 Direction이 이루는 평면에 대해 수직인 벡터가 나오게 되고 해당 벡터가 Right가 된다. 

glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f); 
glm::vec3 cameraRight = glm::normalize(glm::cross(up, cameraDirection));

 

4. Up

cameraRight와 cameraDirection이 서로 직교하므로, 카메라의 y축을 계산할 수 있다.

glm::vec3 cameraUp = glm::normalize(glm::cross(cameraDirection, cameraRight));

 

 

LookAt

위의 과정을 GLM에선 함수를 통해 제공하고 있으며, glm::lookAt 함수를 사용하여 view 행렬을 생성할 수 있다.

카메라를 정의하면, 이제 카메라가 바라보는 방향으로 좌표 공간을 변환할 수 있다. 변환을 위한 행렬은 다음과 같이 정의된다.

각 정점(Vertex)에 대해 적용되는 변환으로, LookAt의 결과로 view 행렬을 생성한다.
LookAt(View) 행렬의 구성

 

R, U, D는 각각 Right, Up, Direction에 대한 축에 대한 rotate 연산이고, P는 변환을 가할 정점의 위치(position)이다.

카메라가 움직이고 회전하는 방향과 반대로 세계를 움직여야 하기 때문에, 왼쪽 행렬(rotate)은 전치되고 오른쪽 행렬(translate)은 음의 부호가 붙는다. LookAt 행렬을 적용하면 모든 월드 공간이 카메라/뷰 공간으로 변환된다.

 

glm::mat4 view;
view = glm::lookAt(glm::vec3(0.0f, 0.0f, 3.0f), 
  		   glm::vec3(0.0f, 0.0f, 0.0f), 
  		   glm::vec3(0.0f, 1.0f, 0.0f));

 

 lookAt 함수의 인수는 순서대로 카메라의 위치, 바라볼 지점, 카메라의 위쪽 방향(right 벡터를 구하는 데에 사용) 벡터로, 결과로 View 행렬을 생성하여 반환한다. lookAt 함수를 사용하면 카메라의 각 요소를 정의하지 않아도 외적과 관련된 모든 연산을 수행하여 행렬을 구성해 준다.


실제 코드 적용

 

view 행렬을 glm::lookAt 함수를 사용해 생성한다.

// View matrix

/*
glm::mat4 view = glm::mat4(1.0f);
view = glm::translate(view, glm::vec3(0.0f, 0.0f, -3.0f));
*/

glm::mat4 view;
view = glm::lookAt(glm::vec3(0.0f, 0.0f, 3.0f), 
  		   glm::vec3(0.0f, 0.0f, 0.0f), 
  		   glm::vec3(0.0f, 1.0f, 0.0f));

 

실행 결과

 

 

다음은 매 프레임마다 카메라를 회전시키는 예제이다.

// view matrix
const float radius = 10.0f;
float camX = sin(glfwGetTime()) * radius;
float camZ = cos(glfwGetTime()) * radius;
glm::mat4 view;
view = glm::lookAt(glm::vec3(camX, 0.0f, camZ),	// x, z 평면에 대해 회전
    glm::vec3(0.0f, 0.0f, 0.0f),
    glm::vec3(0.0f, 1.0f, 0.0f));

 

카메라의 위치를 받는 파라미터에서, OpenGL의 라이브러리인 GLFW의 시간 함수를 사용한다. glfwGetTime()

double glfwGetTime(void)
타이머의 분해능은 시스템에 따라 다르지만 일반적으로 몇 마이크로초 또는 나노초 정도입니다. 지원되는 각 플랫폼에서 최고 해상도의 단조 시간(monotonic time) 소스를 사용합니다.

 

카메라의 x, z 위치는 삼각 함수를 통해 정의되며, glfwGetTime 함수를 적용하면 일정 시간마다 각도가 일정하게 증가하게 된다.  따라서 카메라는 바라보는 지점(0, 0, 0)과 y축을 기준으로 반지름의 길이가 10인 원을 중심으로 회전하게 된다.

 

실행 결과

'컴퓨터그래픽스' 카테고리의 다른 글

[OpenGL 공부] Camera (3)  (0) 2024.07.19
[OpenGL 공부] camera (2)  (1) 2024.02.07
[OpenGL 공부] Coordinates Systems  (2) 2024.02.01
[OpenGL 공부] Transformations  (0) 2024.01.20
[OpenGL 공부] Textures (exercises)  (0) 2024.01.16