본문 바로가기

컴퓨터그래픽스

[OpenGL 공부] Transformations

학습을 위해 참고한 사이트

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

 

LearnOpenGL - Transformations

Transformations Getting-started/Transformations We now know how to create objects, color them and/or give them a detailed appearance using textures, but they're still not that interesting since they're all static objects. We could try and make them move by

learnopengl.com

 

GLM

OpenGL Mathematics의 약자로, 벡터 연산을 위해 필요한 수학 라이브러리를 제공한다.

GLM을 다운로드 받아 Visual Studio의 속성에서 추가 포함 디렉터리에 루트 경로를 추가해준다.

 

다음과 같은 헤더를 추가한다. 대부분의 기능은 다음의 세 가지의 헤더 파일에서 찾을 수 있다.

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

 

Translate

벡터를 평행 이동시키는 변환으로, glm을 사용해서 다음과 같이 벡터를 선언하고 초기화한다.

glm::vec4 vec(1.0f, 0.0f, 0.0f, 1.0f);
// 단위 행렬로 초기화
glm::mat4 trans = glm::mat4(1.0f);
// 단위 행렬과 변환 벡터를 translate 함수에 전달
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;

3차원 공간에서 벡터 (1, 1, 0)은 (1, 1, 0)만큼 평행이동하여 (2, 1, 0)가 된다.

 

Rotate, Scale

물체에 회전을 적용시키거나, 크기를 조절하는 변환으로, 각각 glm의 rotate와 scale 함수를 사용해서 적용할 수 있다. 다음 코드는 z축 기준 반시계 방향으로 90도 회전한 후, 0.5배 스케일하는 변환 행렬을 생성하는 코드이다.

// 4x4 단위 행렬로 초기화
glm::mat4 trans = glm::mat4(1.0f);
// z축을 반시계 방향으로 90도 만큼 회전시키는 변환 행렬 생성
trans = glm::rotate(trans, glm::radians(90.0f), glm::vec3(0.0, 0.0, 1.0));
// 이어서, 모든 축을 기준으로 0.5배로 크기를 조절하는 변환 행렬 생성
trans = glm::scale(trans, glm::vec3(0.5, 0.5, 0.5));

 

셰이더와 연결

생성한 변환 행렬을 셰이더와 연결해서 변환 결과를 직접적으로 확인하기 위해 변환 행렬의 정보를 셰이더에 넘겨줘야 한다. 따라서 정점 셰이더에 변환 행렬을 저장하기 위한 uniform 변수를 생성하고, 외부 애플리케이션에서 행렬 정보를 넘겨준다.

/* transformations.cpp */
// 셰이더 프로그램에서 "transform" 이름으로 선언된 uniform 변수를 탐색하여 해당 위치를 저장한다.
unsigned int transformLoc = glGetUniformLocation(ourShader.ID, "transform");
// 생성했던 변환행렬을 "transform" 변수의 위치에 값을 할당한다.
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(trans));

 

정점 셰이더에서는 받아온 변환 행렬을 정점 데이터의 position 속성으로부터 읽어온 각각의 정점(vec4)에 변환 행렬을 곱하여 변환을 적용시킨다. glUniformMatrix4fv의 경우 네 개의 파라미터를 받는데, 순서대로 uniform 변수의 위치, 보내려는 행렬의 개수, 전치 여부, 마지막으로 행렬 내의 데이터를 사용하기 위해 value_ptr 함수를 통해 첫번째 요소의 포인터를 반환하도록 한다.

/* vertShader.glsl */
#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.0);
	texCoord = vec2(aTexCoord.x, aTexCoord.y);
}

 

아래 결과를 통해, 변환이 잘 적용되었음을 확인할 수 있다.

변환 행렬 적용 전
변환 행렬 적용 후

 

다음은 시간에 따라 물체가 회전하도록 하는 예제이다. glfwGetTime 함수는 GLFW가 초기화된 이후의 경과된 시간을 측정하므로, 지속적으로 값을 받아와야 하기 때문에 그리기 작업을 수행하는 반복문에 다음과 같은 코드를 작성한다.

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));
unsigned int transformLoc = glGetUniformLocation(ourShader.ID, "transform");
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(trans));

변환 행렬은 x 방향으로 0.5, y 방향으로 -0.5만큼 이동 후, z축을 중심으로 시간에 따라 회전하는 변환이 되었다. 

 

실행 결과는 다음과 같다.

 

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

[OpenGL 공부] Camera (1)  (0) 2024.02.02
[OpenGL 공부] Coordinates Systems  (2) 2024.02.01
[OpenGL 공부] Textures (exercises)  (0) 2024.01.16
[OpenGL 공부] Textures (2)  (1) 2024.01.15
[OpenGL 공부] Textures (1)  (1) 2024.01.13