Припустимо, ви маєте на увазі камеру, яка обертається на основі руху миші:
Один із способів її здійснення - це відслідковувати положення камери та її обертання в просторі. Сферичні координати виявляються зручними для цього, оскільки ви можете представляти кути безпосередньо.
float m_theta;
float m_phi;
float m_radius;
float3 m_target;
Камера розташована на 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);
У своїх найпростіших формах обертання () та масштабування () є тривіальними. Просто модифікуйте 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 по векторам вгору та вправо .
Одне питання, яке ви можете задати, це: навіщо весь час перетворювати між декартовою та сферичною (вам також доведеться конвертувати, щоб створити матрицю перегляду).
Гарне питання. У мене теж було це питання і намагався використовувати виключно декартовий. У вас виникають проблеми з обертаннями. Оскільки операції з плаваючою точкою не зовсім точні, багаторазове обертання закінчується накопиченням помилок, які відповідали камері повільно і ненавмисно котиться.
Отже, врешті-решт, я застряг із сферичними координатами. Щоб протистояти додатковим обчисленням, я закінчив кешування матриці перегляду і обчислював її лише тоді, коли камера рухається.
Останнім кроком є використання цього класу Camera. Просто зателефонуйте до відповідної функції учасника у функціях 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 . Камера намагається наслідувати систему камер Майя. Код є безкоштовним та відкритим кодом, тому сміливо використовуйте його у власному проекті.