Làm cách nào để triển khai trackball trong OpenGL?


15

Sau khi đọc rất nhiều về các biến đổi, đã đến lúc triển khai một trackball cho ứng dụng của tôi. Tôi hiểu rằng tôi phải tạo một vectơ từ gốc đến nơi chuột được nhấp và sau đó là một vectơ khác từ gốc đến nơi chuột được thả ra.

Câu hỏi của tôi là, tôi có phải chuyển các chuỗi pixel (x, y) sang các thế giới hay tôi chỉ nên làm mọi thứ trong không gian hình ảnh (coi không gian hình ảnh là hình chiếu 2D của cảnh được đo bằng pixel)?

BIÊN TẬP

Câu trả lời của Richie Sams là một câu hỏi rất hay. Tuy nhiên, tôi nghĩ rằng tôi đang theo một cách tiếp cận hơi khác, xin vui lòng sửa cho tôi nếu tôi sai hoặc tôi đang hiểu sai điều gì đó.

Trong ứng dụng của tôi, tôi có một SimplePerspectiveCameralớp học mà nhận được positioncủa máy ảnh, position of the targetchúng tôi đang xem xét, các upvéc tơ, các fovy, aspectRatio, nearfarkhoảng cách.

Với những người tôi xây dựng ma trận Chế độ xem và Chiếu của mình. Bây giờ, nếu tôi muốn phóng to / thu nhỏ, tôi cập nhật trường nhìn và cập nhật ma trận chiếu của mình. Nếu tôi muốn xoay, tôi di chuyển vị trí của máy ảnh và nhìn vào vùng đồng bằng mà chuột tạo ra.

Cuối cùng, đối với phép quay, tôi có thể sử dụng phép biến đổi trục góc hoặc bậc bốn. Đối với điều này, tôi lưu các cuộn dây pixel nơi chuột được nhấn và sau đó khi chuột di chuyển tôi cũng lưu các cuộn dây pixel.

Đối với mỗi cặp hợp đồng, tôi có thể tính giá trị Z được đưa ra công thức cho một hình cầu, nghĩa là sqrt (1-x ^ 2-y ^ 2), sau đó tính toán các vectơ đi từ targetđến PointMousePressedtargetđi PointMouseMoved, làm sản phẩm chéo để có được trục quay và sử dụng bất kỳ phương pháp nào để tính toán vị trí camera mới.

Tuy nhiên, nghi ngờ lớn nhất của tôi là các giá trị (x, y, z) được đưa ra trong các co-pixel và khi tính toán các vectơ tôi đang sử dụng targetlà một điểm trong các coords thế giới. Đây có phải là sự pha trộn hệ thống tọa độ ảnh hưởng đến kết quả của vòng quay mà tôi đang cố gắng thực hiện không?


1
"Trackball" có nghĩa là một camera quay quanh một vật thể, như trong các ứng dụng mô hình 3D không? Nếu vậy, tôi nghĩ nó thường được thực hiện bằng cách chỉ theo dõi tọa độ chuột 2D và ánh xạ x = yaw, y = pitch cho xoay camera.
Nathan Reed

1
@NathanReed tùy chọn khác là dựa trên góc trục , Bạn chiếu 2 điểm chuột lên một quả cầu (ảo) và sau đó tìm góc xoay từ điểm này sang điểm khác.
ratchet freak

@NathanReed vâng, đó là những gì tôi muốn nói về trackball, tôi nghĩ đó là một tên phổ biến trong cộng đồng CG.
BRmus27

@ratchetfreak có, cách tiếp cận của tôi xem xét xoay vòng dựa trên góc trục. Tôi nghi ngờ rằng liệu có cần thiết để ánh xạ các dây chuột 2D theo tọa độ thế giới hay không. Tôi biết tôi có thể sử dụng (x, y) để tính zgiá trị của một hình cầu bán kính r, tuy nhiên tôi không chắc hình cầu đó sống trong không gian thế giới hay không gian hình ảnh và ý nghĩa của nó là gì. Có lẽ tôi đang xem xét vấn đề.
BRmus27

2
Về chỉnh sửa của bạn: Có. Bạn sẽ cần phải chuyển đổi các giá trị (x, y, z) của mình sang không gian thế giới bằng cách sử dụng ma trận Xem.
RichieSams

Câu trả lời:


16

Giả sử bạn có nghĩa là một camera xoay dựa trên chuyển động của chuột:

Một cách để thực hiện nó là theo dõi vị trí camera và góc quay của nó trong không gian. Các tọa độ hình cầu xảy ra thuận tiện cho việc này, vì bạn có thể biểu diễn các góc trực tiếp.

Hình ảnh tọa độ hình cầu

float m_theta;
float m_phi;
float m_radius;

float3 m_target;

Máy ảnh được đặt tại P được xác định bởi m_theta, m_phi và m_radius. Chúng ta có thể xoay và di chuyển tự do bất cứ nơi nào chúng ta muốn bằng cách thay đổi ba giá trị đó. Tuy nhiên, chúng tôi luôn nhìn và xoay quanh, m_target. m_target là nguồn gốc địa phương của hình cầu. Tuy nhiên, chúng tôi có thể tự do di chuyển nguồn gốc này bất cứ nơi nào chúng tôi muốn trong không gian thế giới.

Có ba chức năng camera chính:

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

Trong các hình thức đơn giản nhất của họ, Rotate () và Zoom () là tầm thường. Việc sửa đổi lần lượt m_theta, m_phi và m_radius:

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

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

Panning phức tạp hơn một chút. Một pan camera được định nghĩa là di chuyển camera sang trái / phải và / hoặc lên / xuống tương ứng với chế độ xem camera hiện tại. Cách dễ nhất chúng ta có thể thực hiện điều này là chuyển đổi chế độ xem camera hiện tại của chúng ta từ tọa độ hình cầu sang tọa độ cartesian. Điều này sẽ cho chúng ta một vectơ lênphải .

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

Vì vậy, trước tiên, chúng tôi chuyển đổi hệ tọa độ hình cầu của chúng tôi sang cartesian để có được vector nhìn của chúng tôi . Tiếp theo, chúng tôi làm sản phẩm chéo vector với thế giới lên vector, để có được một ngay vector. Đây là một vectơ chỉ trực tiếp bên phải của chế độ xem camera. Cuối cùng, chúng tôi làm một sản phẩm chéo vector khác để đưa máy ảnh lên vector.

Để hoàn thành pan, chúng tôi di chuyển m_target dọc theo vectơ lênphải .

Một câu hỏi bạn có thể hỏi là: Tại sao phải chuyển đổi giữa cartesian và hình cầu mọi lúc (bạn cũng sẽ phải chuyển đổi để tạo ma trận View).

Câu hỏi hay. Tôi cũng có câu hỏi này và đã cố gắng sử dụng độc quyền cartesian. Bạn kết thúc với các vấn đề với luân chuyển. Do các hoạt động của dấu phẩy động không chính xác chính xác, nhiều lần quay cuối cùng sẽ tích lũy các lỗi, tương ứng với máy ảnh một cách chậm chạp và vô tình lăn.

nhập mô tả hình ảnh ở đây

Vì vậy, cuối cùng, tôi bị mắc kẹt với tọa độ hình cầu. Để chống lại các tính toán bổ sung, cuối cùng tôi đã lưu vào bộ đệm ma trận khung nhìn và chỉ tính toán khi máy ảnh di chuyển.

Bước cuối cùng là sử dụng lớp Camera này. Chỉ cần gọi chức năng thành viên phù hợp bên trong các chức năng MouseDown / Up / Scroll của ứng dụng của bạn:

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

Các biến m_camera * Các yếu tố chỉ là các yếu tố tỷ lệ thay đổi tốc độ quay / pans / cuộn của máy ảnh của bạn

Mã tôi có ở trên là phiên bản mã giả đơn giản hóa của hệ thống camera tôi đã tạo cho một dự án phụ: camera.hcamera.cpp . Máy ảnh cố gắng bắt chước hệ thống camera Maya. Mã này là mã nguồn mở và miễn phí, vì vậy hãy sử dụng nó trong dự án của riêng bạn.


1
Tôi đoán sự phân chia cho 300 chỉ là một tham số cho độ nhạy của vòng quay cho sự dịch chuyển của chuột?
BRmus27

Chính xác. Đó là những gì đã xảy ra để làm việc tốt với độ phân giải của tôi tại thời điểm đó.
RichieSams

-2

Trong trường hợp bạn muốn xem xét một giải pháp sẵn sàng. Tôi có một cổng THREE.JS TrackBall điều khiển trong C ++ và C #


Tôi có xu hướng nghĩ rằng nó. Anh ta có thể học từ mã bên trong cách hoạt động của trackball.
Michael IV

1
@MichaelIV Tuy nhiên, Alan Wolfe có một điểm. Bạn có thể cải thiện đáng kể câu trả lời của mình bằng cách bao gồm mã có liên quan trong chính câu trả lời để làm cho nó khép kín và bằng chứng trong tương lai chống lại liên kết sẽ chết vào một ngày nào đó.
Martin Ender
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.