OpenGL में ट्रैकबॉल कैसे लागू करें?


15

परिवर्तनों के बारे में इतना पढ़ने के बाद, यह मेरे ऐप के लिए एक ट्रैकबॉल लागू करने का समय है। मैं समझता हूं कि मुझे माउस से क्लिक की गई उत्पत्ति के लिए एक वेक्टर बनाना होगा और फिर मूल से दूसरे स्थान पर जहां माउस को छोड़ा गया है।

मेरा सवाल यह है कि क्या मुझे विश्व-कॉर्ड्स में x (x, y) पिक्सेल कोर्ड्स को ट्रांसफ़ॉर्म करना है या क्या मुझे इमेज स्पेस में सब कुछ करना चाहिए (इमेज स्पेस को पिक्सल में मापा गया दृश्य का 2 डी प्रोजेक्शन माना जाता है)?

संपादित करें

रिची सैमस का जवाब बहुत अच्छा है। हालांकि, मुझे लगता है कि मैं थोड़ा अलग दृष्टिकोण का पालन कर रहा हूं, कृपया मुझे सही करें अगर मैं गलत हूं या मैं कुछ गलत समझ रहा हूं।

अपने आवेदन में मैं एक है SimplePerspectiveCameraवर्ग को मिलने वाले positionकैमरे के, position of the targetहम पर, देख रहे हैं upवेक्टर, fovy, aspectRatio, nearऔर farदूरी।

उन लोगों के साथ मैं अपना व्यू और प्रोजेक्शन मैट्रीस बनाता हूं। अब, यदि मैं ज़ूम इन / आउट करना चाहता हूं तो मैं देखने के क्षेत्र को अपडेट करता हूं और अपने प्रोजेक्शन मैट्रिक्स को अपडेट करता हूं। यदि मैं पैन करना चाहता हूं तो मैं कैमरे की स्थिति को स्थानांतरित करता हूं और माउस द्वारा निर्मित डेल्टा को देखता हूं।

अंत में, घुमाव के लिए मैं कोण-अक्ष परिवर्तन या चतुर्धातुक का उपयोग कर सकता हूं। इसके लिए, मैं पिक्सेल-कोर्ड्स को बचाता हूँ जहाँ माउस को दबाया गया था और फिर जब माउस चलता है तो मैं पिक्सेल-कॉर्ड को भी बचाता हूँ।

Coords की प्रत्येक जोड़ी के लिए मैं जेड मूल्य की गणना कर सकते हैं एक क्षेत्र के लिए सूत्र को देखते हुए, यानी, sqrt (1-x ^ 2-y ^ 2), तो वैक्टर कि से जाने के लिए गणना targetकरने के लिए PointMousePressedऔर से targetकरने के लिए PointMouseMoved, पार उत्पाद कर रोटेशन की धुरी पाने के लिए और नए कैमरे की स्थिति की गणना करने के लिए किसी भी विधि का उपयोग करें।

हालांकि, मेरा सबसे बड़ा संदेह यह है कि पिक्सेल-कॉर्ड में x (y, z) मान दिए गए हैं, और जब मैं उपयोग कर रहा हूं तो वैक्टर की गणना करते हुए targetजो विश्व-कॉर्ड में एक बिंदु है। क्या मैं जो कोशिश कर रहा हूं, उसके परिणाम को प्रभावित करने वाली समन्वित प्रणाली का यह मिश्रण नहीं है?


1
क्या "ट्रैकबॉल" का अर्थ एक कैमरा है जो किसी वस्तु के चारों ओर परिक्रमा करता है, जैसे 3D मॉडलिंग ऐप्स में? यदि ऐसा है, तो मुझे लगता है कि यह आमतौर पर 2 डी माउस निर्देशांक का ट्रैक रखने और कैमरा रोटेशन के लिए x = yaw, y = पिच को मैप करके किया जाता है।
नाथन रीड

1
@NathanReed का दूसरा विकल्प है अक्ष-कोण आधारित , आप एक (आभासी) क्षेत्र पर 2 माउस पॉइंट्स को प्रोजेक्ट करते हैं और फिर एक से दूसरे में रोटेशन पाते हैं।
शाफ़्ट फ्राक

@NathanReed हाँ जो ट्रैकबॉल से मेरा मतलब है, मुझे लगा कि यह CG समुदाय में एक सामान्य नाम है।
BRBbit27

@ratchetfreak हाँ मेरा दृष्टिकोण एक धुरी-कोण आधारित रोटेशन पर विचार करता है। मेरा संदेह यह है कि अगर 2D माउस कॉर्ड को विश्व-समन्वय के लिए मैप करना आवश्यक है या नहीं। मुझे पता है कि मैं zत्रिज्या के एक क्षेत्र के मूल्य की गणना करने के लिए (x, y) का उपयोग कर सकता हूं r, हालांकि मुझे यकीन नहीं है कि अगर यह क्षेत्र विश्व-अंतरिक्ष या छवि-अंतरिक्ष में रहता है और इसके क्या निहितार्थ हैं। शायद मैं समस्या को खत्म कर रहा हूं।
BRBbit27

2
आपके संपादन पर: हाँ View मैट्रिक्स का उपयोग करके आपको अपने (x, y, z) मानों को विश्व अंतरिक्ष में बदलना होगा।
रिचीसेम्स

जवाबों:


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 को ऊपर और दाएं वैक्टर के साथ घुमाते हैं ।

एक सवाल जो आप पूछ रहे हैं, वह यह है: क्यों हर समय कार्टेशियन और गोलाकार के बीच में कनवर्ट करें (व्यू मैट्रिक्स बनाने के लिए आपको भी कन्वर्ट करना होगा)।

अच्छा प्रश्न। मेरे पास भी यह प्रश्न था और विशेष रूप से कार्टेशियन का उपयोग करने की कोशिश की। आप रोटेशन के साथ समस्याओं का अंत करते हैं। चूंकि फ्लोटिंग पॉइंट ऑपरेशंस बिल्कुल सटीक नहीं होते हैं, इसलिए कई घुमाव संचित त्रुटियों को समाप्त करते हैं, जो धीरे-धीरे कैमरे के अनुरूप होते हैं, और अनजाने में रोलिंग करते हैं।

यहाँ छवि विवरण दर्ज करें

इसलिए, अंत में, मैं गोलाकार निर्देशांक के साथ फंस गया। अतिरिक्त गणनाओं का मुकाबला करने के लिए, मैंने व्यू मैट्रिक्स को कैशिंग करने के लिए समाप्त किया, और केवल जब कैमरा चलता है, तो इसकी गणना करें।

अंतिम चरण इस कैमरा वर्ग का उपयोग करना है । बस अपने ऐप के माउसडाउन / अप / स्क्रॉल कार्यों के अंदर उपयुक्त सदस्य फ़ंक्शन को कॉल करें:

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 * फैक्टर चर केवल पैमाने के कारक हैं जो बदलते हैं कि आपका कैमरा कितनी जल्दी घूमता है / पैन / स्क्रॉल करता है

मेरे पास ऊपर दिया गया कोड कैमरा सिस्टम का एक सरलीकृत छद्म-कोड संस्करण है जिसे मैंने एक साइड प्रोजेक्ट के लिए बनाया है: camera.h और camera.cpp । कैमरा माया कैमरा सिस्टम की नकल करने की कोशिश करता है। कोड स्वतंत्र और खुला स्रोत है, इसलिए इसे अपने प्रोजेक्ट में उपयोग करने के लिए स्वतंत्र महसूस करें।


1
मुझे लगता है कि 300 का विभाजन माउस के विस्थापन को देखते हुए रोटेशन की संवेदनशीलता के लिए सिर्फ एक पैरामीटर है?
BRBbit27

सही बात। यह उस समय मेरे संकल्प के साथ अच्छी तरह से काम करने के लिए हुआ।
रिचीसेम्स

-2

मामले में आप एक तैयार समाधान में एक नज़र रखना चाहते हैं। मेरे पास एक बंदरगाह है । मैं C ++ और C # में THREE.JS TrackBall नियंत्रक का ।


मुझे लगता है कि यह करता है। वह ट्रैकबॉल कैसे काम करता है के अंदर कोड से सीख सकता है।
माइकल IV

1
@MichaelIV फिर भी, एलन वोल्फ के पास एक बिंदु है। आप उत्तर में प्रासंगिक कोड को शामिल करके अपने जवाब में बहुत सुधार कर सकते हैं ताकि किसी दिन मृत होने वाली लिंक के खिलाफ इसे आत्म-निहित और भविष्य-प्रमाण बना सकें।
मार्टिन एंडर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.