OpenGL Transform library GLM 변환 라이브러리 적용 [Learn OpenGL 쉬운 번역]

728x90
반응형
※ 본 포스팅은 learnopengl.com을 번역 및 가감한 포스팅이며, learnopengl에서는 번역 작업 참여를 적극 장려하고 있습니다!
아래 링크에서 원문을 확인할 수 있습니다.

 

In practice

이제 transformation에 대한 이론은 모두 알았습니다. 이걸 opengl에서 코드로 적용하는 법을 알아봅시다. opengl에는 기본적으로 matrix와 vector에 대해서 내장되어있는 기능은 없기 때문에 수학과 관련된 라이브러리를 사용하는 것이 좋습니다. 우리는 opengl을 위해 설계된 수학 라이브러리로 GLM을 사용할 것입니다.

 

GLM

GLM은 OpenGL Mathematics의 약자이고 header only 라이브러리입니다. 즉 헤더 파일만 포함하면 딱히 링크나 컴파일을 안해도 쓸 수 있는 구조입니다. 아래 사이트에서 다운 받아서 헤더 파일의 root 디렉토리를 우리 프로젝트의 includes 폴더에 복사하면 됩니다.

 

 

GLM의 기능은 다음 3개의 헤더파일에 우리가 필요한 대부분이 들어있습니다.

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

 

이제 (1,0,0) vector를 (1,1,0)으로 translation하는 작업으로 transform 기능을 활용 가능한지 확인해봅시다. 

vector는 homogeneous cooridnate가 1.0으로 설정도니 glm::vec4로 정의합니다.

glm::vec4 vec(1.0f, 0.0f, 0.0f, 1.0f);
glm::mat4 trans = glm::mat4(1.0f);
trans = glm::translate(trans, glm::vec3(1.0f, 1.0f, 0.0f));
vec = trans * vec;
std::cout << vec.x << vec.y << vec.z << std::endl;

먼저 GLM의 내장 vector class로 vector와 matrix를 정의합니다. mat4를 생성할 때 1.0을 입력하면 대각선으로 1.0값이 적용되어 identity matrix가 됩니다. 처음에 identity matrix로 정의하지 않고 0을 입력하면 어떤 matrix 연산을 해도 0인 matrix가 되어버리겠죠.

 

다음으로 만든 identity matrix를 gl::translate함수를 이용해서 입력한 vector 만큼 이동하는 translation matrix를 생성합니다. 이렇게 생성한 translation matrix를 vector에 곱하면 translation 적용되어서 vector가 (2,1,0)으로 이동할 것입니다. 

 

이번에는 이전에 다룬 container object를 scaling 및 rotation 해보도록 합시다.

glm::mat4 trans = glm::mat4(1.0f);
trans = glm::rotate(trans, glm::radians(90.0f), glm::vec3(0.0, 0.0, 1.0));
trans = glm::scale(trans, glm::vec3(0.5, 0.5, 0.5));

일단 각 축에 대해 container object를 0.5배 scaling하고 z축 기준으로 90도 rotation시키려고 합니다. rotation에 대해서 GLM은 기본 radians 단위로 받기 때문에, glm::radians를 사용해서 degrees를 radians으로 변환해서 계산해야합니다. 

texture는 z축 중심으로 회전시킵니다. 이때 회전축은 unit vector이어야 합니다. 만약 x,y,z 축 이외의 다른 축을 기준으로 rotation한다면 normalize해야한다는 사실을 잊으면 안됩니다. GLM 함수에 matrix를 전달하면 GLM은 하나씩 matrix를 결합한 transformation matrix를 만들어냅니다.

 

이제 다음 작업은 만들어낸 transformation matrix를 shader에 전달하는 방법입니다. GLSL에도 mat4 type이 있으니 uniform 변수를 vertex shader에 선언해서 전달한 후 position vector에 적용합시다.

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoord;

out vec2 TexCoord;
  
uniform mat4 transform;

void main()
{
    gl_Position = transform * vec4(aPos, 1.0f);
    TexCoord = vec2(aTexCoord.x, aTexCoord.y);
}
GLSL에는 mat2, mat3 도 있어서 vec type처럼 swizzling 연산이 가능합니다. 여태까지 이론상 다루었던 연산(scalar * matrix, matrix * vector, matrix * matrix 등)들은 모두 동일하게 matrix에도 적용할 수 있습니다. 특별하게 다른 matrix 연산이 있다면 그때 설명이 필요할 것 같네요.

 

이제 uniform으로 matrix를 전달하고 이를 position vector에 곱해 transformation을 적용했으니, contanier object는 2배 줄어들고 90도 반시계 방향으로 회전했겠죠. opengl에서도 matrix를 shader에 전달하는 코드를 작성합시다. 

unsigned int transformLoc = glGetUniformLocation(ourShader.ID, "transform");
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(trans));

glGetUniformLocation으로 uniform 변수 위치를 요청하고, glUniformMatrix4fv로 matrix 데이터를 shader에 전달합니다. 인수는 순서대로 uniform location, opengl에 보낼 matrix의 개수(지금은 하나만 보냅니다), matrix를 transpose할 것인지 여부(기본적으로 column-major ordering로 되어있어 하지 않고 GL_FALSE로 설정합니다), 실제 matrix data입니다. 

GLM으로 matrix를 저장할 때 구조가 opengl과 조금 다르기 때문에 GLM에서 value_ptr로 matrix data를 변환해서 전달합니다.

 

이렇게 transformation matrix를 생성하고, vertex shader에 uniform을 선언해서 전달하고 positionv vector에 적용한 결과 다음과 같이 나올겁니다.

container가 크기도 줄어들고 반시계 방향으로 회전한 것을 볼 수 있습니다. 이걸 이제는 시간이 지남에 따라서 움직이제 한 번 해봅시다. frame 마다 transformation matrix를 업데이트 하면 됩니다. GLFW의 time함수를 이용합시다.

glm::mat4 trans = glm::mat4(1.0f);
trans = glm::translate(trans, glm::vec3(0.5f, -0.5f, 0.0f));
trans = glm::rotate(trans, (float)glfwGetTime(), glm::vec3(0.0f, 0.0f, 1.0f));

이전에는 transformation matrix를 어디서 선언해도 잘 shader로 전달되면 되었지만, 지금은 반복되는 frame마다 잘 적용되도 tranformation matrix를 새로 생성합니다. 보통 장면을 렌더링 할때는 transformation matrix를 여러개 두고 업데이트하면서 적용하게 됩니다.

 

이제는 container object를 원점 중심으로 rotation하고, 오른쪽 아래로 이동시켜봅시다. transformation matrix의 순서는 코드와는 다르게 역순으로 적용되니까 translation 과 rotation 중 rotation이 먼저 적용됩니다. 여러번 해보면서 object에 transformation matrix를 적용하는 것을 감잡게 될 겁니다.

 

코드가 정상 실행되면 이러한 결과가 됩니다.

 

 

 

 

이렇게 transformation matrix를 적용해보았습니다. matrix를 결합하는 방식을 사용하면 transformation의 횟수가 몇 번이던 간에 모두 single matrix로 단 번에 처리할 수 있을 겁니다. 때문에 vertex shader를 다시 정의할 필요도 없고, 데이터를 여러 번 전송하지 않아도 처리할 수 있어 효율적입니다. transformation uniform 만 업데이트하면 됩니다.

 

 

 

잘못된 결과가 나온다면 전체 소스 코드와 셰이더를 확인해보세요.

 

다음 포스팅에서는 좌표 공간에 대해서 다루도록 합시다. 

728x90
반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유