마우스 움직임을 기준으로 회전하는 카메라를 의미한다고 가정합니다.
이를 구현하는 한 가지 방법은 카메라 위치와 공간에서의 회전을 추적하는 것입니다. 구면 좌표는 각도를 직접 나타낼 수 있기 때문에 편리합니다.
float m_theta;
float m_phi;
float m_radius;
float3 m_target;
카메라는 m에 위치하며 P 는 m_theta, m_phi 및 m_radius로 정의됩니다. 이 세 가지 값을 변경하여 원하는 곳 어디든 자유롭게 회전하고 이동할 수 있습니다. 그러나 우리는 항상 m_target을보고 회전합니다. m_target은 구의 로컬 원점입니다. 그러나 우리는이 원점을 세계 공간에서 원하는 곳으로 자유롭게 이동할 수 있습니다.
세 가지 주요 카메라 기능이 있습니다.
void Rotate(float dTheta, float dPhi);
void Zoom(float distance);
void Pan(float dx, float dy);
가장 간단한 형태로 Rotate () 및 Zoom ()은 사소한 것입니다. m_theta, m_phi 및 m_radius를 각각 수정하십시오.
void Camera::Rotate(float dTheta, float dPhi) {
m_theta += dTheta;
m_phi += dPhi;
}
void Camera::Zoom(float distance) {
m_radius -= distance;
}
패닝은 조금 더 복잡합니다. 카메라 팬은 카메라를 현재 카메라 뷰에 따라 왼쪽 / 오른쪽 및 / 또는 위 / 아래로 이동하는 것으로 정의됩니다. 가장 쉬운 방법은 현재 카메라 뷰를 구형 좌표에서 직교 좌표로 변환하는 것입니다. 이것은 우리에게 줄 것이다 최대 및 권리 벡터를.
void Camera::Pan(float dx, float dy) {
float3 look = normalize(ToCartesian());
float3 worldUp = float3(0.0f, 1.0f, 0.0f, 0.0f);
float3 right = cross(look, worldUp);
float3 up = cross(look, right);
m_target = m_target + (right * dx) + (up * dy);
}
inline float3 ToCartesian() {
float x = m_radius * sinf(m_phi) * sinf(m_theta);
float y = m_radius * cosf(m_phi);
float z = m_radius * sinf(m_phi) * cosf(m_theta);
float w = 1.0f;
return float3(x, y, z, w);
}
먼저 구형 좌표계를 직교 좌표계로 변환하여 모양 벡터 를 얻습니다 . 다음 으로 올바른 벡터 를 얻기 위해 월드 업 벡터 와 벡터 교차 곱을 수행합니다 . 카메라 뷰의 바로 오른쪽을 가리키는 벡터입니다. 마지막으로, 우리는 카메라를 얻을 수있는 또 다른 벡터 외적 할 최대 벡터.
팬을 완료하기 위해, 우리는 함께 m_target 이동 위로 하고 바로 벡터.
묻는 질문 중 하나는 항상 데카르트와 구형을 변환해야하는 이유입니다 (뷰 매트릭스를 만들려면 변환해야 함).
좋은 질문. 나도이 질문을 가지고 독점적으로 데카르트를 사용하려고했습니다. 회전 문제가 생깁니다. 부동 소수점 연산이 정확하게 정확하지 않기 때문에 다중 회전으로 인해 누적 오류가 발생하여 카메라에 느리게 대응하고 의도하지 않게 롤링됩니다.
결국, 나는 구형 좌표를 고수했습니다. 추가 계산에 대응하기 위해 뷰 매트릭스를 캐싱하고 카메라가 움직일 때만 계산했습니다.
마지막 단계는 이 카메라 클래스 를 사용 하는 것입니다. 앱의 MouseDown / Up / Scroll 함수 내에서 적절한 멤버 함수를 호출하십시오.
void MouseDown(WPARAM buttonState, int x, int y) {
m_mouseLastPos.x = x;
m_mouseLastPos.y = y;
SetCapture(m_hwnd);
}
void MouseUp(WPARAM buttonState, int x, int y) {
ReleaseCapture();
}
void MouseMove(WPARAM buttonState, int x, int y) {
if ((buttonState & MK_LBUTTON) != 0) {
if (GetKeyState(VK_MENU) & 0x8000) {
// Calculate the new phi and theta based on mouse position relative to where the user clicked
float dPhi = ((float)(m_mouseLastPos.y - y) / 300);
float dTheta = ((float)(m_mouseLastPos.x - x) / 300);
m_camera.Rotate(-dTheta, dPhi);
}
} else if ((buttonState & MK_MBUTTON) != 0) {
if (GetKeyState(VK_MENU) & 0x8000) {
float dx = ((float)(m_mouseLastPos.x - x));
float dy = ((float)(m_mouseLastPos.y - y));
m_camera.Pan(-dx * m_cameraPanFactor, dy * m_cameraPanFactor);
}
}
m_mouseLastPos.x = x;
m_mouseLastPos.y = y;
}
void MouseWheel(int zDelta) {
// Make each wheel dedent correspond to a size based on the scene
m_camera.Zoom((float)zDelta * m_cameraScrollFactor);
}
m_camera * Factor 변수는 카메라의 회전 / 팬 / 스크롤 속도를 변경하는 스케일 요소입니다.
위의 코드는 보조 프로젝트를 위해 만든 카메라 시스템의 단순화 된 의사 코드 버전입니다 : camera.h 및 camera.cpp . 카메라가 Maya 카메라 시스템을 모방하려고합니다. 코드는 무료이며 오픈 소스이므로 자신의 프로젝트에서 자유롭게 사용할 수 있습니다.