OpenGL'de bir hareket topu nasıl uygulanır?


15

Dönüşümler hakkında çok fazla okuma yaptıktan sonra uygulamam için bir topunu uygulama zamanı. Anladığım kadarıyla farenin tıklatıldığı noktadan başlayıp farenin serbest bırakıldığı noktadan bir vektör oluşturmalıyım.

Benim sorum, (x, y) piksel koordinatlarını dünya koordinatlarına dönüştürmek zorunda mıyım yoksa sadece görüntü alanındaki her şeyi yapmalı mıyım (görüntü alanı pikselin ölçülen sahnenin 2B projeksiyonu olarak düşünülürse)?

DÜZENLE

Richie Sams'in cevabı çok iyi. Ancak, biraz farklı bir yaklaşım izlediğimi düşünüyorum, eğer yanlışsam ya da bir şeyi yanlış anlıyorsam lütfen beni düzeltin.

Benim uygulamada bir var SimplePerspectiveCameraaldığında sınıf position, kameranın position of the targetbiz bakıyoruz upvektörü, fovy, aspectRatio, nearve farmesafeler.

Bunlarla Görüş ve Projeksiyon matrislerimi oluşturuyorum. Şimdi, yakınlaştırmak / uzaklaştırmak istersem görüş alanını ve projeksiyon matrisimi güncelliyorum. Kaydırmak istersem kameranın konumunu hareket ettiririm ve farenin oluşturduğu deltaya bakarım.

Son olarak, dönüşler için açı ekseni dönüşümü veya kuaterniyonları kullanabilirim. Bunun için farenin basıldığı piksel kablolarını kaydediyorum ve sonra fare hareket ettiğinde piksel kablolarını da kaydediyorum.

Coords her çifti için, bir gitmek vektörlere hesaplamak sonra, örneğin,, sqrt (1-x ^ 2-il ^ 2) bir küre için, formül bir Z değeri hesaplayabilir targetiçin PointMousePressedve gelen targetiçin PointMouseMovedçapraz ürün yapmak dönme eksenini almak ve yeni kamera konumunu hesaplamak için herhangi bir yöntemi kullanmak.

Bununla birlikte, en büyük şüphe (x, y, z) değerlerinin piksel koordinatlarında verildiği ve kullandığım vektörleri hesaplarken targetdünya koordinatlarında bir nokta. Bu koordinat sisteminin karıştırılması yapmaya çalıştığım rotasyonun sonucunu etkilemiyor mu?


1
"Hareket topu", 3D modelleme uygulamalarında olduğu gibi, bir nesnenin etrafında dönen bir kamera anlamına mı geliyor? Eğer öyleyse, genellikle 2D fare koordinatlarını takip ederek ve kamera döndürme için x = yaw, y = pitch eşleştirerek yapıldığını düşünüyorum.
Nathan Reed

1
Diğer seçenek eksen açısı temelindedir , 2 fare noktasını (sanal) bir küreye yansıtırsınız ve sonra birinden diğerine dönüşü bulursunuz.
cırcır ucube

@NathanReed evet topunu kastettiğim budur, CG topluluğunda ortak bir isim olduğunu düşündüm.
BRabbit27

@ratchetfreak evet yaklaşımım eksen açısı tabanlı bir dönüşü göz önünde bulundurur. Şüphem, 2B fare koordinatlarını dünya koordinatıyla eşleştirmek gerekip gerekmediği. zYarıçap kürenin değerini hesaplamak için (x, y) kullanabileceğimi biliyorum r, ancak bu kürenin dünya-uzayında mı yoksa görüntü-uzayında mı yaşadığını ve etkilerinin ne olduğundan emin değilim. Belki de problemi düşünmüyorum.
BRabbit27

2
Düzenlemenizde: Evet. Görünüm matrisini kullanarak (x, y, z) değerlerinizi dünya boşluğuna dönüştürmeniz gerekir.
RichieSams

Yanıtlar:


16

Farenin hareketine göre dönen bir kamera anlamına geldiğinizi varsayarsak:

Uygulamanın bir yolu, kamera konumunu ve uzayda dönüşünü takip etmektir. Küresel koordinatlar bunun için uygun olur, çünkü açıları doğrudan temsil edebilirsiniz.

Küresel Koordinatlar Görüntüsü

float m_theta;
float m_phi;
float m_radius;

float3 m_target;

Kamera bulunmaktadır P m_theta, m_phi ve m_radius ile tanımlanır. Bu üç değeri değiştirerek istediğimiz yerde serbestçe dönebilir ve hareket edebiliriz. Ancak, her zaman m_target'a bakar ve döneriz. m_target kürenin yerel kaynağıdır. Bununla birlikte, bu kökeni dünya uzayında istediğimiz yere hareket ettirmekte özgürüz.

Üç ana kamera işlevi vardır:

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

En basit formlarında, Rotate () ve Zoom () önemsizdir. Sadece m_theta, m_phi ve m_radius değişikliklerini yapın:

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

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

Kaydırma biraz daha karmaşıktır. Kamera yatay kaydırma, kamerayı geçerli kamera görünümüne göre sola / sağa ve / veya yukarı / aşağı hareket ettirmek olarak tanımlanır. Bunu yapmanın en kolay yolu, mevcut kamera görünümümüzü küresel koordinatlardan kartezyen koordinatlara dönüştürmektir. Bu bize yukarı ve doğru vektörler verecektir .

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

İlk olarak, küresel vektör koordinat sistemimizi görünüm vektörümüzü elde etmek için kartezyen'e dönüştürdük . Sonra, dünya ile vektör çarpımı yapmak kadar bir almak için, vektör sağ vektör. Bu, doğrudan kamera görünümünün sağını gösteren bir vektördür. Son olarak, kamerayı almak için başka vektör çarpımı yapmak kadar vektörü.

Tavayı bitirmek için m_target'ı yukarı ve sağ vektörler boyunca hareket ettiririz .

Sormanız gereken bir soru şudur: Neden her zaman kartezyen ve küresel arasında dönüştürme (Görünüm matrisini oluşturmak için de dönüştürmeniz gerekir).

İyi soru. Ben de bu soruyu sordum ve sadece kartezyen kullanmaya çalıştım. Sonunda rotasyon sorunları var. Kayan nokta işlemleri tam olarak kesin olmadığından, birden fazla rotasyon kameraya yavaşça karşılık gelen ve istemeden yuvarlanan hataları biriktirir.

resim açıklamasını buraya girin

Sonunda, küresel koordinatlarla takıldım. Ekstra hesaplamalara karşı koymak için, görünüm matrisini önbelleğe aldım ve sadece kamera hareket ettiğinde hesapladım.

Son adım, bu Camera sınıfını kullanmaktır . Uygulamanızın MouseDown / Yukarı / Kaydırma işlevlerinin içindeki uygun üye işlevini aramanız yeterlidir:

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 * Faktör değişkenleri sadece kameranızın dönme / tava / kaydırma hızını değiştiren ölçek faktörleridir

Yukarıdaki kod, bir yan proje için yaptığım kamera sisteminin basitleştirilmiş bir sahte kod sürümüdür: camera.h ve camera.cpp . Kamera Maya kamera sistemini taklit etmeye çalışıyor. Kod ücretsiz ve açık kaynaklıdır, bu yüzden kendi projenizde kullanmaktan çekinmeyin.


1
300 ile bölme sadece farenin yer değiştirme verilen dönme hassasiyeti için bir parametre olduğunu tahmin?
BRabbit27

Doğru. O zamanki kararımla iyi sonuç verdi.
RichieSams

-2

Hazır bir çözüme göz atmak istiyorsanız , C ++ ve C # 'de THREE.JS TrackBall denetimlerinin bir bağlantı noktasına sahibim


İztopunun nasıl çalıştığını içindeki koddan öğrenebilir.
Michael IV

1
@MichaelIV Yine de Alan Wolfe'nin bir anlamı var. Bir gün içinde bağlantı kopmasına karşı kendi kendine yeten ve geleceğe dönük hale getirmek için cevabın kendisine ilgili kodu ekleyerek cevabınızı büyük ölçüde geliştirebilirsiniz.
Martin Ender
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.