OpenGL Camera Mouse Input 카메라 마우스 입력 [Learn OpenGL 쉬운 번역]

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

 

Mouse input

요(yaw)와 피치(pitch)값은 마우스를 움직일 때도 나옵니다. 마우스를 수평으로 움직이면 요, 수직으로 움직이는 피치값이 영향받죠. 기본적으로는 이전 프레임 마우스 위치를 저장한 후 현재 프레임 마우스 위치와의 차를 통해 변위를 구하는 겁니다. 

 

우선 GLFW에 커서를 숨기고 캡처(capture)하도록 명령합니다. 커서를 캡처한다는 게 뭐냐면 애플리케이션이 포커싱되면, 마우스 커서가 창의 중심에 유지되는 걸 말합니다. FPS 게임을 해보신 분이라면 이게 어떤 현상을 말하는지 쉽게 이해하실 겁니다. 이걸 설정해줍니다.

glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

이렇게 설정하면 마우스를 움직여도 커서가 안보이고 창을 벗어나지도 않습니다. FPS 카메라 시스템이 이렇죠.

 

이제 피치와 요 값을 계산하기 위해 GLFW에 마우스 움직임 이벤트를 감지하게 합니다. 그러기 위한 콜백 함수를 만듭시다.

void mouse_callback(GLFWwindow* window, double xpos, double ypos);

fly style camera에서 마우스 입력 처리를 할 때 카메라 방향 벡터를 계산하기 위해 아래와 같은 단계를 거칩니다.

 

1. 이전 프레임 이후 마우스 offset 계산한다

2. offset 값을 카메라의 요와 피치 값에 더한다

3. 피치 값의 최소/최대 값을 제한한다

4. 방향 벡터를 계산한다

 

첫 단계, 이전 프레임의 마우스 offset을 계산합니다. 애플리케이션에서 마우스 위치 저장을 해야하는데, 처음에는 화면의 중앙으로 설정합시다.(창의 크기는 800x600)

float lastX = 400, lastY = 300;

그 다음 마우스 콜백 함수에서 마우스가 이동한 offset을 계산합니다. 

float xoffset = xpos - lastX;
float yoffset = lastY - ypos; // y좌표는 아래에서 위로 향하기 때문에 반대로 계산합니다
lastX = xpos;
lastY = ypos;

const float sensitivity = 0.1f;
xoffset *= sensitivity;
yoffset *= sensitivity;

구한 offset 값에 감도(sensitivity)값을 곱합니다. 감도 값으로 마우스의 움직임 감도를 조정합니다.

 

그 다음 두 번째 단계, offset 값을 요와 피치에 더해줍니다. 

yaw   += xoffset;
pitch += yoffset;

세 번째 단계, 카메라의 움직임을 제한합니다. 만약 방향벡터가 world의 up 벡터와 평행하게 되면 LookAt flip이 발생합니다. 그러면 더 위로 혹은 더 아래로 돌리지 못하고 카메라 화면이 반전되며 진동하는 현상이 생기게 됩니다. 그래서 피치는 -89~89도 안쪽으로 제한합니다. 제한한 범위를 넘는 경우는 오일러 값을 사용해서 체크합니다.

if(pitch > 89.0f)
  pitch =  89.0f;
if(pitch < -89.0f)
  pitch = -89.0f;

수평 회전의 경우는 특별히 요 값의 제한을 하지 않습니다.

 

네 번째 단계, 이제 실제 방향 벡터를 계산합니다. 

glm::vec3 direction;
direction.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch));
direction.y = sin(glm::radians(pitch));
direction.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch));
cameraFront = glm::normalize(direction);

이렇게 만든 방향 벡터는 마우스 움직임을 회전으로 바꾼 결과입니다. 이전에는 cameraFront를 사용했지만 이제는 GLM의 lookAt 함수에 포함되어있기 때문에 바로 진행하면 됩니다.

 

코드를 실행하면 창이 마우스 커서 입력을 처음 받을 때 갑자기 크게 이동하는데요. 이건 커서가 창에 들어올 때 마우스 콜백 함수가 호출되서 현재 마우스의 위치가 바로 전달되면서 중심에서 금방 마우스가 위치 했던 위치까지의 offset만큼 한 번에 이동하게 된 경우입니다. 이 현상을 없애려면 마우스 입력을 처음으로 받는 경우를 체크하면 됩니다. 처음 입력 받으면 초기 마우스 위치는 현재 마우스의 xpos, ypos로 업데이트해줍니다. 그러면 처음 입력 이후 입력된 좌표로 offset을 계산합니다.

if (firstMouse) // 처음에는 true로 설정
{
    lastX = xpos;
    lastY = ypos;
    firstMouse = false;
}

최종 코드는 이렇습니다.

void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
    if (firstMouse)
    {
        lastX = xpos;
        lastY = ypos;
        firstMouse = false;
    }
  
    float xoffset = xpos - lastX;
    float yoffset = lastY - ypos; 
    lastX = xpos;
    lastY = ypos;

    float sensitivity = 0.1f;
    xoffset *= sensitivity;
    yoffset *= sensitivity;

    yaw   += xoffset;
    pitch += yoffset;

    if(pitch > 89.0f)
        pitch = 89.0f;
    if(pitch < -89.0f)
        pitch = -89.0f;

    glm::vec3 direction;
    direction.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch));
    direction.y = sin(glm::radians(pitch));
    direction.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch));
    cameraFront = glm::normalize(direction);
}

이제 3D 공간을 자유롭게 이동할 수 있습니다.

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