Bagaimana cara menerapkan trackball di OpenGL?

15

Setelah banyak membaca tentang transformasi, sekarang saatnya menerapkan trackball untuk aplikasi saya. Saya mengerti saya harus membuat vektor dari asal ke tempat mouse diklik dan kemudian dari asal ke tempat mouse dilepaskan.

Pertanyaan saya adalah, apakah saya harus mengubah (x, y) pixel coords ke world-coords atau haruskah saya melakukan semuanya dalam ruang gambar (mengingat ruang gambar adalah proyeksi 2D dari adegan yang diukur dalam piksel)?

EDIT

Jawaban Richie Sams adalah jawaban yang sangat bagus. Namun, saya pikir saya mengikuti pendekatan yang sedikit berbeda, perbaiki saya jika saya salah atau saya salah mengerti sesuatu.

Dalam aplikasi saya saya memiliki SimplePerspectiveCamerakelas yang menerima positiondari kamera, position of the targetkita melihat, yang upvektor, yang fovy, aspectRatio, neardan farjarak.

Dengan itu saya membangun matrik Lihat dan Proyeksi saya. Sekarang, Jika saya ingin memperbesar / memperkecil, saya memperbarui bidang tampilan dan memperbarui matriks proyeksi saya. Jika saya ingin menggeser saya memindahkan posisi kamera dan melihat delta yang dihasilkan mouse.

Akhirnya, untuk rotasi saya bisa menggunakan transformasi sudut-sumbu atau angka empat. Untuk ini, saya menyimpan pixel-coords di mana mouse ditekan dan kemudian ketika mouse bergerak saya juga menyimpan pixel-coords.

Untuk setiap pasangan koordinat saya dapat menghitung nilai Z yang diberikan rumus untuk bola, yaitu, sqrt (1-x ^ 2-y ^ 2), kemudian menghitung vektor yang pergi dari targetke PointMousePresseddan dari targetke PointMouseMoved, lakukan lintas produk untuk mendapatkan sumbu rotasi dan menggunakan metode apa pun untuk menghitung posisi kamera baru.

Namun, keraguan terbesar saya adalah bahwa nilai (x, y, z) diberikan dalam pixel-coords, dan ketika menghitung vektor yang saya gunakan targetyang merupakan titik dalam world-coords. Bukankah pencampuran sistem koordinat ini mempengaruhi hasil rotasi yang saya coba lakukan?

BRabbit27
sumber
1
Apakah "trackball" berarti kamera yang mengorbit di sekitar objek, seperti di aplikasi pemodelan 3D? Jika demikian, saya pikir itu biasanya dilakukan dengan hanya melacak koordinat mouse 2D dan pemetaan x = yaw, y = pitch untuk rotasi kamera.
Nathan Reed
1
@NathanReed opsi lainnya adalah berbasis sudut sumbu , Anda memproyeksikan 2 titik mouse ke bola (virtual) dan kemudian menemukan rotasi dari satu ke yang lain.
ratchet freak
@NathanReed ya itu yang saya maksud dengan trackball, saya pikir itu nama umum di komunitas CG.
BRabbit27
@ scratchetfreak ya pendekatan saya mempertimbangkan rotasi berbasis sumbu-sudut. Keraguan saya adalah apakah diperlukan untuk memetakan koordinat mouse 2D ke world-coord atau tidak. Saya tahu saya bisa menggunakan (x, y) untuk menghitung znilai sebuah bola jari-jari r, namun saya tidak yakin apakah bola itu hidup di ruang dunia atau ruang gambar dan apa implikasinya. Mungkin saya terlalu memikirkan masalah.
BRabbit27
2
Pada hasil edit Anda: Ya. Anda perlu mengubah nilai (x, y, z) Anda ke ruang dunia menggunakan matriks Lihat.
RichieSams

Jawaban:

16

Asumsikan yang Anda maksudkan adalah kamera yang berputar berdasarkan pergerakan mouse:

Salah satu cara untuk mengimplementasikannya adalah melacak posisi kamera dan rotasinya di ruang angkasa. Koordinat bola kebetulan cocok untuk ini, karena Anda dapat mewakili sudut secara langsung.

Gambar Koordinat Bulat

float m_theta;
float m_phi;
float m_radius;

float3 m_target;

Kamera terletak di P yang didefinisikan oleh m_theta, m_phi, dan m_radius. Kita dapat memutar dan bergerak dengan bebas ke mana pun kita inginkan dengan mengubah ketiga nilai tersebut. Namun, kami selalu melihat, dan memutar, m_target. m_target adalah asal usul bola. Namun, kita bebas untuk memindahkan asal ini ke mana pun kita inginkan di ruang dunia.

Ada tiga fungsi kamera utama:

void Rotate(float dTheta, float dPhi);
void Zoom(float distance);
void Pan(float dx, float dy);

Dalam bentuknya yang paling sederhana, Putar () dan Zoom () sepele. Hanya memodifikasi m_theta, m_phi, dan m_radius masing-masing:

void Camera::Rotate(float dTheta, float dPhi) {
    m_theta += dTheta;
    m_phi += dPhi;
}

void Camera::Zoom(float distance) {
    m_radius -= distance;
}

Panning sedikit lebih rumit. Pan kamera didefinisikan sebagai menggerakkan kamera ke kiri / kanan dan / atau atas / bawah masing-masing ke tampilan kamera saat ini. Cara termudah yang dapat kita lakukan ini adalah mengubah tampilan kamera kita saat ini dari koordinat bola ke koordinat kartesius. Ini akan memberi kita vektor ke atas dan kanan .

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);
}

Jadi, pertama, kita mengonversi sistem koordinat bola kita menjadi cartesian untuk mendapatkan vektor tampilan kita . Berikutnya, kita lakukan vektor lintas produk dengan dunia atas vektor, untuk mendapatkan hak vektor. Ini adalah vektor yang menunjuk langsung ke kanan dari tampilan kamera. Terakhir, kita lakukan vektor lain silang produk untuk mendapatkan kamera up vektor.

Untuk menyelesaikan panci, kami memindahkan m_target di sepanjang vektor ke atas dan kanan .

Satu pertanyaan yang mungkin Anda tanyakan adalah: Mengapa mengkonversi antara kartesius dan bola sepanjang waktu (Anda juga harus mengonversi untuk membuat matriks View).

Pertanyaan bagus. Saya juga punya pertanyaan ini dan mencoba menggunakan kartesian secara eksklusif. Anda berakhir dengan masalah dengan rotasi. Karena operasi floating point tidak tepat tepat, beberapa rotasi berakhir dengan akumulasi kesalahan, yang terkait dengan kamera secara perlahan, dan bergulir secara tidak sengaja.

masukkan deskripsi gambar di sini

Jadi, pada akhirnya, saya terjebak dengan koordinat bola. Untuk melawan perhitungan ekstra, saya akhirnya melakukan caching view matrix, dan hanya menghitungnya saat kamera bergerak.

Langkah terakhir adalah menggunakan kelas Kamera ini. Panggil saja fungsi anggota yang sesuai di dalam fungsi MouseDown / Atas / Gulir aplikasi Anda:

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);
}

Variabel faktor m_camera * hanyalah faktor skala yang mengubah seberapa cepat kamera Anda memutar / menggeser / menggulir

Kode yang saya miliki di atas adalah versi kode semu yang disederhanakan dari sistem kamera yang saya buat untuk proyek sampingan: camera.h dan camera.cpp . Kamera mencoba meniru sistem kamera Maya. Kode ini gratis dan open source, jadi silakan menggunakannya dalam proyek Anda sendiri.

RichieSams
sumber
1
Saya kira pembagian dengan 300 hanyalah parameter untuk sensitivitas rotasi mengingat perpindahan mouse?
BRabbit27
Benar. Itu yang terjadi bekerja dengan baik dengan resolusi saya pada saat itu.
RichieSams
-2

Jika Anda ingin melihat ke solusi yang siap. Saya memiliki port THREE.JS TrackBall mengontrol di C ++ dan C #

Michael IV
sumber
Saya cenderung berpikir begitu. Dia bisa belajar dari kode di dalam cara kerja trackball.
Michael IV
1
@MichaelIV Namun demikian, Alan Wolfe benar. Anda dapat sangat meningkatkan jawaban Anda dengan memasukkan kode yang relevan dalam jawaban itu sendiri untuk membuatnya mandiri dan tahan di masa depan terhadap tautan yang mati suatu hari.
Martin Ender 3-15