จะใช้ trackball ใน OpenGL ได้อย่างไร?


15

หลังจากอ่านมากเกี่ยวกับการแปลงมันถึงเวลาที่จะใช้ trackball สำหรับแอพของฉัน ฉันเข้าใจว่าฉันต้องสร้างเวกเตอร์จากจุดเริ่มต้นไปยังจุดที่คลิกเมาส์จากนั้นอีกอันจากจุดเริ่มต้นไปยังที่ที่ปล่อยเมาส์

คำถามของฉันคือฉันต้องเปลี่ยนพิกัดพิกเซล (x, y) เป็น coords โลกหรือฉันควรทำทุกอย่างในพื้นที่ภาพ (พิจารณาพื้นที่ภาพคือการฉายภาพ 2 มิติของฉากที่วัดเป็นพิกเซล)

แก้ไข

คำตอบของ Richie Sams นั้นดีมาก อย่างไรก็ตามฉันคิดว่าฉันกำลังทำตามวิธีที่แตกต่างกันเล็กน้อยโปรดแก้ไขฉันหากฉันผิดหรือเข้าใจผิดบางอย่าง

ในใบสมัครของฉันฉันมีSimplePerspectiveCameraระดับที่ได้รับpositionของกล้องที่position of the targetเรากำลังมองหาที่upเวกเตอร์ที่fovy, aspectRatio, nearและfarระยะทาง

กับสิ่งที่ฉันสร้างเมทริกซ์มุมมองและการฉายของฉัน ตอนนี้ถ้าฉันต้องการซูมเข้า / ออกฉันจะอัพเดทมุมมองและอัพเดทเมทริกซ์การฉายของฉัน ถ้าฉันต้องการเลื่อนฉันเลื่อนตำแหน่งของกล้องและดูที่เดลต้าที่เมาส์สร้าง

ในที่สุดสำหรับการหมุนฉันสามารถใช้การแปลงแกนมุมหรือ quaternions สำหรับสิ่งนี้ฉันบันทึก pixel-coords ที่เมาส์ถูกกดแล้วเมื่อเมาส์เคลื่อนฉันก็จะบันทึก pixel-coords

สำหรับแต่ละคู่ของคอร์ดฉันสามารถคำนวณค่า Z ที่กำหนดสูตรสำหรับทรงกลมเช่น sqrt (1-x ^ 2-y ^ 2) จากนั้นคำนวณเวกเตอร์ที่ไปจากtargetไปPointMousePressedและกลับจากtargetไปPointMouseMovedทำผลิตภัณฑ์ข้าม เพื่อให้แกนหมุนและใช้วิธีการใด ๆ เพื่อคำนวณตำแหน่งกล้องใหม่

อย่างไรก็ตามข้อสงสัยที่ใหญ่ที่สุดของฉันคือค่า (x, y, z) ให้เป็นพิกเซล - คอร์ดและเมื่อคำนวณเวกเตอร์ที่ฉันใช้targetซึ่งเป็นจุดในโลก - คอร์ด การผสมผสานของระบบพิกัดนี้มีผลต่อผลลัพธ์ของการหมุนที่ฉันพยายามทำหรือไม่?


1
"แทร็กบอล" หมายถึงกล้องที่โคจรรอบวัตถุเช่นในแอพแบบจำลอง 3 มิติหรือไม่? ถ้าเป็นเช่นนั้นฉันคิดว่ามันมักจะทำโดยเพียงแค่ติดตามพิกัดเมาส์ 2D และทำแผนที่ x = yaw, y = pitch สำหรับการหมุนกล้อง
นาธานรีด

1
@NathanReed ตัวเลือกอื่นคือมุมของแกนคุณฉายเมาส์ 2 จุดลงบนทรงกลม (เสมือน) แล้วหาการหมุนจากจุดหนึ่งไปอีกมุมหนึ่ง
วงล้อประหลาด

@NathanReed ใช่นั่นคือสิ่งที่ฉันหมายถึงแทร็กบอลฉันคิดว่ามันเป็นชื่อสามัญในชุมชน CG
BRabbit27 27

@ ratchetfreak ใช่แนวทางของฉันพิจารณาการหมุนตามแกนของมุม ข้อสงสัยของฉันคือถ้าจำเป็นต้องแมปคอร์ดเมาส์ 2 มิติกับคอร์ดของโลกหรือไม่ ฉันรู้ว่าฉันสามารถใช้ (x, y) เพื่อคำนวณzค่าของทรงกลมรัศมีrได้ แต่ฉันไม่แน่ใจว่าทรงกลมนั้นอาศัยอยู่ในอวกาศหรืออวกาศภาพหรือไม่และความหมายของมันคืออะไร บางทีฉันอาจจะคิดถึงปัญหา
BRabbit27 27

2
ในการแก้ไขของคุณ: ใช่ คุณจะต้องแปลงค่า (x, y, z) ของคุณให้เป็นอวกาศโลกโดยใช้ดูเมทริกซ์
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);

ในรูปแบบที่ง่ายที่สุด Rotate () และ Zoom () นั้นไม่สำคัญ เพียงแค่ปรับเปลี่ยน 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 พร้อมเวกเตอร์ขึ้นและลงทางขวา

คำถามหนึ่งที่คุณอาจถามคือ: ทำไมแปลงระหว่างคาร์ทีเซียนและทรงกลมตลอดเวลา (คุณจะต้องแปลงเพื่อสร้างมุมมองเมทริกซ์ด้วย)

คำถามที่ดี. ฉันมีคำถามนี้และพยายามใช้คาร์ทีเซียนโดยเฉพาะ คุณจบลงด้วยปัญหาการหมุน เนื่องจากการทำงานของจุดลอยตัวนั้นไม่แม่นยำอย่างแม่นยำการหมุนหลายครั้งจึงทำให้เกิดข้อผิดพลาดในการสะสมซึ่งตรงกับกล้องช้าและหมุนโดยไม่ตั้งใจ

ป้อนคำอธิบายรูปภาพที่นี่

ในที่สุดฉันก็ติดอยู่กับพิกัดทรงกลม เพื่อชดเชยการคำนวณพิเศษฉันลงเอยเมทริกซ์มุมมองแคชและคำนวณเฉพาะเมื่อกล้องเคลื่อนที่เท่านั้น

ขั้นตอนสุดท้ายคือการใช้คลาสกล้องนี้ เพียงเรียกใช้ฟังก์ชันสมาชิกที่เหมาะสมภายในฟังก์ชั่น 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 กล้องพยายามเลียนแบบระบบกล้องของ Maya รหัสนี้ฟรีและโอเพ่นซอร์สดังนั้นอย่าลังเลที่จะใช้มันในโครงการของคุณเอง


1
ฉันเดาว่าการหารด้วย 300 เป็นเพียงพารามิเตอร์สำหรับความไวของการหมุนที่ให้การเคลื่อนที่ของเมาส์?
BRabbit27 27

แก้ไข. มันเป็นสิ่งที่เกิดขึ้นในการทำงานได้ดีกับความละเอียดของฉันในเวลา
RichieSams

-2

ในกรณีที่คุณต้องการดูโซลูชันที่พร้อมใช้งานฉันมีพอร์ตของ THREE.JS TrackBall controlls ใน C ++ และ C #


ฉันมักจะคิดว่ามันทำเขาสามารถเรียนรู้จากรหัสภายในวิธีการทำงานของแทร็กบอล
Michael IV

1
@MichaelIV อย่างไรก็ตาม Alan Wolfe มีประเด็น คุณสามารถปรับปรุงคำตอบของคุณได้อย่างมากโดยการใส่รหัสที่เกี่ยวข้องในคำตอบเพื่อทำให้มันมีอยู่ในตัวเองและมีหลักฐานในอนาคตต่อลิงก์ที่จะตายในบางวัน
Martin Ender
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.