Як реалізувати трекбол у OpenGL?


15

Після стількох читань про перетворення настав час реалізувати трекбол для мого додатка. Я розумію, що мені потрібно створити вектор від початкового місця до місця, де натискається миша, а потім іншого від початку, до якого випускається миша.

Моє запитання полягає в тому, чи потрібно мені переводити (x, y) піксельні координати на світові, або я повинен просто робити все у просторі зображення (враховуючи, що простір зображення - це двовимірна проекція сцени, виміряна в пікселях)?

EDIT

Відповідь Річі Самса дуже хороша. Однак я думаю, що я дотримуюся трохи іншого підходу, будь ласка, виправте мене, якщо я помиляюся чи щось не розумію.

У моєму додатку у мене є SimplePerspectiveCameraклас , який отримує positionвід камери, position of the targetми дивимося, в upвекторі fovy, aspectRatio, nearі farвідстань.

З ними я будую матриці перегляду та проекції. Тепер, якщо я хочу збільшити чи зменшити масштаб, я оновлюю поле зору та оновлюю матрицю проекцій. Якщо я хочу панорумувати, я переміщу положення камери і дивлюся на дельту, яку виробляє миша.

Нарешті, для обертання я можу використовувати або перетворення кута-осі, або кватерніони. Для цього я зберігаю піксельні координати там, де натискали мишу, а потім, коли миша рухається, я також зберігаю піксельні координати.

Для кожної пари координат можна обчислити Z-значення дається формулою для кулі, тобто SQRT (1-х ^ 2-у ^ 2), а потім обчислити для векторів , які йдуть від targetдо PointMousePressedі від targetдо PointMouseMoved, робить поперечний продукт щоб отримати вісь обертання і використовувати будь-який метод для обчислення нового положення камери.

Однак мої найбільші сумніви полягають у тому, що значення (x, y, z) задаються в піксельних координатах, а при обчисленні векторів, які я використовую, targetє крапкою у світових координатах. Чи не змішання системи координат впливає на результат обертання, який я намагаюся зробити?


1
Чи означає "трекбол" камера, яка обертається навколо об'єкта, як у додатках для 3D моделювання? Якщо так, я думаю, що це зазвичай робиться просто відстеженням 2D координат миші та відображенням x = yaw, y = крок для обертання камери.
Натан Рід

1
@NathanReed інший варіант заснований на куті осі , ви проектуєте 2 точки миші на (віртуальну) сферу, а потім знаходите обертання від однієї до іншої.
щурячий вирод

@NathanReed так, саме це я мав на увазі під трекболом, я вважав, що це загальна назва у спільноті CG.
BRabbit27

@ratchetfreak Так, мій підхід враховує обертання, засноване на осі. Мої сумніви в тому, що якщо це потрібно, щоб зіставити 2D-координати миші на світову координату чи ні. Я знаю, що можу використовувати (x, y) для обчислення zзначення сфери радіуса r, однак я не впевнений, чи живе ця сфера у світовому просторі чи просторі зображення та які наслідки це. Можливо, я долаю проблему.
BRabbit27

2
Під час редагування: Так. Вам потрібно буде перетворити свої значення (x, y, z) у світовий простір за допомогою матриці View.
RichieSams

Відповіді:


16

Припустимо, ви маєте на увазі камеру, яка обертається на основі руху миші:

Один із способів її здійснення - це відслідковувати положення камери та її обертання в просторі. Сферичні координати виявляються зручними для цього, оскільки ви можете представляти кути безпосередньо.

Зображення сферичних координат

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 . Камера намагається наслідувати систему камер Майя. Код є безкоштовним та відкритим кодом, тому сміливо використовуйте його у власному проекті.


1
Я думаю, поділ на 300 - це лише параметр чутливості обертання з урахуванням зміщення миші?
BRabbit27

Правильно. Це те, що сталося, добре співпрацювало з моєю резолюцією в той час.
RichieSams

-2

У випадку, якщо ви хочете розглянути готове рішення. У мене є порт THREE.JS TrackBall контролює в C ++ і C #


Я схильний вважати, що це робить. Він може дізнатися з коду всередині, як працює трекбол.
Михайло IV

1
@MichaelIV Тим не менш, Алан Вулф має свою точку. Ви можете значно покращити свою відповідь, включивши відповідний код у саму відповідь, щоб зробити його самодостатнім і захищеним від майбутнього проти посилання, якесь колись загине.
Мартін Ендер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.