- 3D공간을 어느 시점에서 어떤 방향으로 바라볼 것인가를 결정
- 카메라를 조작하기 위한 파라미터로 부터 View transform을 유도
- Camera 파라미터
- camera position : 카메라의 위치
- camera targer : 카메라가 바라보고자 하는 타겟 위치
- camera up vector : 카메라 화면의 세로 출 방향
기존 코드
auto view = glm::translate(glm::mat4(1.0f); glm::vec3(0.0f, 0.0f, -0.3f);
auto cameraPos = glm::vec3(0.0f, 0.0f, 3.0f);
auto cameraTarget = glm::vec3(0.0f, 0.0f, 0.0f);
auto cameraUp = glm::vec3(0.0f, 1.0f, 0.0f);
auto cameraZ = glm::normalize(cameraPos - cameraTarget);
auto cameraX = glm::normalize(glm::cross(cameraUp, cameraZ));
auto cameraY = glm::cross(cameraZ, cameraX);
auto cameraMat = glm::mat4(
glm::vec4(cameraX, 0.0f),
glm::vec4(cameraY, 0.0f),
glm::vec4(cameraZ, 0.0f),
glm::vec4(cameraPos, 1.0f));
auto view = glm::inverse(cameraMat);
위의 '3축 결정 과정'처럼 구현되어 있다.
연산과정을 해주는 glm::lookAt함수의 활용
auto cameraPos = glm::vec3(0.0f, 0.0f, 3.0f);
auto cameraTarget = glm::vec3(0.0f, 0.0f, 0.0f);
auto cameraUp = glm::vec3(0.0f, 1.0f, 0.0f);
auto view = glm::lookAt(cameraPos, cameraTarget, cameraUp);
class Context {
public:
static ContextUPtr Create();
void Render(); // 추가된 항목
void ProcessInput(GLFWwindow* window); // 추가된 항목
private:
// ...
// camera parameter
glm::vec3 m_cameraPos { glm::vec3(0.0f, 0.0f, 3.0f) }; // 추가된 항목
glm::vec3 m_cameraFront { glm::vec3(0.0f, 0.0f, -1.0f) }; // 추가된 항목
glm::vec3 m_cameraUp { glm::vec3(0.0f, 1.0f, 0.0f) }; // 추가된 항목
};
void Context::ProcessInput(GLFWwindow* window) {
const float cameraSpeed = 0.05f;
if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) // 전
m_cameraPos += cameraSpeed * m_cameraFront;
if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) // 후
m_cameraPos -= cameraSpeed * m_cameraFront;
auto cameraRight = glm::normalize(glm::cross(m_cameraUp, -m_cameraFront));
if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) // 우
m_cameraPos += cameraSpeed * cameraRight;
if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) // 좌
m_cameraPos -= cameraSpeed * cameraRight;
auto cameraUp = glm::normalize(glm::cross(-m_cameraFront, cameraRight));
if (glfwGetKey(window, GLFW_KEY_E) == GLFW_PRESS) // 상
m_cameraPos += cameraSpeed * cameraUp;
if (glfwGetKey(window, GLFW_KEY_Q) == GLFW_PRESS) // 하
m_cameraPos -= cameraSpeed * cameraUp;
}
auto projection = glm::perspective(glm::radians(45.0f), (float)640 / (float)480, 0.01f, 20.0f);
auto view = glm::lookAt(m_cameraPos, m_cameraPos + m_cameraFront, m_cameraUp); // 추가된 항목
//기존의 auto view로 변경
while (!glfwWindowShouldClose(window))
{
glfwPollEvents();
context->ProcessInput(window); // 추가된 항목
context->Render();
glfwSwapBuffers(window);
}
class Context {
public:
static ContextUPtr Create();
void Render();
void ProcessInput(GLFWwindow* window);
void Reshape(int width, int height);
void MouseMove(double x, double y); // 추가된 항목
//...
// camera parameter
float m_cameraPitch { 0.0f }; // 추가된 항목
float m_cameraYaw { 0.0f }; // 추가된 항목
glm::vec3 m_cameraFront { glm::vec3(0.0f, -1.0f, 0.0f) };
glm::vec3 m_cameraPos { glm::vec3(0.0f, 0.0f, 3.0f) };
glm::vec3 m_cameraUp { glm::vec3(0.0f, 1.0f, 0.0f) };
};
void Context::MouseMove(double x, double y) {
static glm::vec2 prevPos = glm::vec2((float)x, (float)y);
auto pos = glm::vec2((float)x, (float)y);
auto deltaPos = pos - prevPos;
const float cameraRotSpeed = 0.8f;
m_cameraYaw -= deltaPos.x * cameraRotSpeed;
m_cameraPitch -= deltaPos.y * cameraRotSpeed;
if (m_cameraYaw < 0.0f) m_cameraYaw += 360.0f;
if (m_cameraYaw > 360.0f) m_cameraYaw -= 360.0f;
if (m_cameraPitch > 89.0f) m_cameraPitch = 89.0f;
if (m_cameraPitch < -89.0f) m_cameraPitch = -89.0f;
prevPos = pos;
}
m_cameraFront =
glm::rotate(glm::mat4(1.0f),
glm::radians(m_cameraYaw), glm::vec3(0.0f, 1.0f, 0.0f)) * glm::rotate(glm::mat4(1.0f),
glm::radians(m_cameraPitch), glm::vec3(1.0f, 0.0f, 0.0f)) * glm::vec4(0.0f, 0.0f, -1.0f, 0.0f);
auto projectio = ...
// 콜백함수 정의
void OnCursorPos(GLFWwindow* window, double x, double y)
{
auto context = (Context*)glfwGetWindowUserPointer(window);
context->MouseMove(x, y);
}
//...
void main(...)
{
//...
glfwSetWindowUserPointer(window, context.get());
OnFramebufferSizeChange(window, WINDOW_WIDTH, WINDOW_HEIGHT);
glfwSetFramebufferSizeCallback(window, OnFramebufferSizeChange);
glfwSetKeyCallback(window, OnKeyEvent);
glfwSetCursorPosCallback(window, OnCursorPos); // 추가된 항목
//...
class Context {
// ...
void MouseMove(double x, double y);
void MouseButton(int button, int action, double x, double y); // 추가된 항목
// ...
bool m_cameraControl { false }; // 추가된 항목
glm::vec2 m_prevMousePos { glm::vec2(0.0f) }; // 추가된 항목
float m_cameraPitch { 0.0f };
// ...
};
void Context::MouseButton(int button, int action, double x, double y)
{
if (button == GLFW_MOUSE_BUTTON_RIGHT)
{
if (action == GLFW_PRESS) {
// 마우스 조작 시작 시점에 현재 마우스 커서 위치 저장
m_prevMousePos = glm::vec2((float)x, (float)y);
m_cameraControl = true;
}
else if (action == GLFW_RELEASE)
{
m_cameraControl = false;
}
}
}
void Context::ProcessInput(GLFWwindow* window)
{
if (!m_cameraControl)
return;
//...
// 콜백함수
void OnMouseButton(GLFWwindow* window, int button, int action, int modifier)
{
auto context = (Context*)glfwGetWindowUserPointer(window);
double x, y;
glfwGetCursorPos(window, &x, &y);
context->MouseButton(button, action, x, y);
}
void Main(...)
{
//...
OnFramebufferSizeChange(window, WINDOW_WIDTH, WINDOW_HEIGHT);
glfwSetFramebufferSizeCallback(window, OnFramebufferSizeChange);
glfwSetKeyCallback(window, OnKeyEvent);
glfwSetCursorPosCallback(window, OnCursorPos);
glfwSetMouseButtonCallback(window, OnMouseButton); // 추가된 항목
//...
}
class Context {
public:
static ContextUPtr Create();
void Render();
void ProcessInput(GLFWwindow* window);
void Reshape(int width, int height); // 추가된 항목
private:
// ...
int m_width {WINDOW_WIDTH}; // 추가된 항목
int m_height {WINDOW_HEIGHT}; // 추가된 항목
// ...
void Context::Reshape(int width, int height)
{
m_width = width;
m_height = height;
glViewport(0, 0, m_width, m_height);
}
//...
void Context::Render(){
//...
auto projection = glm::perspective(glm::radians(45.0f), (float)m_width / (float)m_height, 0.01f, 20.0f);
//...
//...
void main(...)
{
//...
glfwSetWindowUserPointer(window, context.get()); // 추가된 항목
OnFramebufferSizeChange(window, WINDOW_WIDTH, WINDOW_HEIGHT);
glfwSetFramebufferSizeCallback(window, OnFramebufferSizeChange);
glfwSetKeyCallback(window, OnKeyEvent);
- glfwSetWindowUserPointer(window, context.get())
- 창 크기가 변경되면 뒤의 context.get()함수를 호출한다.auto pointer = (Context*)glfwSetWindowUserPointer(window);
- 윈도우의 창이 바뀌면 Context*형 pointer로 반환된다. 이러한 기능을 사용
-코드 추가 , src / main.cpp / OnFramebufferSizeChange() 작성
//...
auto context = (Context*)glfwGetWindowUserPointer(window);
context->Reshape(width, height);