opengl किसी निर्दिष्ट स्थान के सापेक्ष नोड को सही ढंग से बदलना?




matrix graphics (3)

मैं वर्तमान में एक पदानुक्रमित दृश्य ग्राफ़ में नोड्स के साथ काम कर रहा हूं और मुझे एक विशिष्ट परिवर्तन स्थान (जैसे एक अभिभावक नोड) के संबंध में एक नोड का अनुवाद / घूर्णन करने में कठिनाई हो रही है।

मैं किसी दृश्य ग्राफ में किसी नोड के अपने पैतृक नोड के सापेक्ष ठीक कैसे अनुवाद / घूमता हूं?

समस्या

दृश्य नोड के माता-पिता / बच्चे की संरचना के लिए निम्नलिखित पानी के अणु आरेख (कनेक्टिंग लाइनों के बिना) पर विचार करें, मूलतक नोड होने पर हे xygen परमाणु और 2 एच याड्रोजन परमाणु बाल नोड होने के साथ।

अनुवाद समस्या

अगर आप माता-पिता ज़ीजिन परमाणु को पकड़ लेते हैं और संरचना का अनुवाद करते हैं, तो आप उम्मीद करते हैं कि एच- हाड्रोजन बच्चों का पालन करना और उनके माता-पिता से एक ही रिश्तेदार स्थिति पर रहना। यदि आप इसके बजाय बच्चे एच परमाणु को पकड़ लेते हैं और इसका अनुवाद करते हैं, तो केवल बच्चे प्रभावित होंगे। आम तौर पर यह वर्तमान में काम करता है। जब परमाणु का अनुवाद किया जाता है, एच परमाणु स्वतः इसके साथ आगे बढ़ते हैं, जैसा कि पदानुक्रमित ग्राफ से अपेक्षित होता है

हालांकि , जब माता-पिता का अनुवाद करते हैं, तो बच्चों को एक अतिरिक्त अनुवाद एकत्र करने का भी अंत होता है, जो बच्चों को उसी दिशा में 'दो बार अनुवाद' करने के लिए अनिवार्य रूप से समान रिश्तेदार दूरी पर रहने के बजाय अपने माता-पिता से दूर जाते हैं।

रोटेशन अंक

यदि आप माता-पिता नोड को पकड़ लेते हैं और इसे घुमाते हैं, तो आप उम्मीद करते हैं कि बच्चों एच नोड्स को भी घुमाने के लिए, लेकिन एक कक्षा में, क्योंकि माता-पिता द्वारा रोटेशन किया जा रहा है यह काम करता है के रूप में इरादा

हालांकि , अगर आप एक बच्चे एच नोड लेते हैं और इसे अपने माता पिता के सापेक्ष रोटेट करने के लिए कहते हैं, तो मुझे उम्मीद थी कि बच्चा उसी तरह अपने माता-पिता के चारों ओर कक्षा जाएगा, लेकिन ऐसा नहीं होता। इसके बजाय, बच्चा अपनी वर्तमान स्थिति में तेज गति से अपनी धुरी पर घूमता है (जैसे कि दो बार अपने स्वयं के स्थानीय अंतरिक्ष के मुकाबले घूमते समय)

मुझे वाकई उम्मीद है कि यह विवरण काफी उचित है, लेकिन मुझे बताएं कि क्या यह नहीं है और मैं आवश्यकतानुसार स्पष्ट कर दूँगा।

गणित

मैं 4x4 कॉलम-प्रमुख मैट्रिक्स (यानी मैट्रिक्स 4) और कॉलम वैक्टर (यानी वेक्टर 3, Vector4 ) का उपयोग कर रहा हूं।

नीचे गलत तर्क सबसे निकट है, मैं सही व्यवहार पर आया हूं। ध्यान दें कि मैंने एक जावा की तरह वाक्यविन्यास का उपयोग करने के लिए चुना है, जिसमें मैट्रिक को पढ़ने के लिए आसान बनाने के लिए ऑपरेटर ओवरलोड किया गया है। मैंने अलग चीजों की कोशिश की है जब मैंने सोचा कि मैंने इसे सोचा था, लेकिन मैं वास्तव में नहीं था।

वर्तमान अनुवाद तर्क

translate(Vector3 tv /* translation vector */, TransformSpace relativeTo):
    switch (relativeTo):
        case LOCAL:
            localTranslation = localTranslation * TranslationMatrix4(tv);
            break;
        case PARENT:
            if parentNode != null:
                localTranslation = parentNode.worldTranslation * localTranslation * TranslationMatrix4(tv);
            else:
                localTranslation = localTranslation * TranslationMatrix4(tv);
            break;
        case WORLD:
            localTranslation = localTranslation * TranslationMatrix4(tv);
            break;

वर्तमान रोटेशन लॉजिक

rotate(Angle angle, Vector3 axis, TransformSpace relativeTo):
    switch (relativeTo):
        case LOCAL:
            localRotation = localRotation * RotationMatrix4(angle, axis);
            break;
        case PARENT:
            if parentNode != null:
                localRotation = parentNode.worldRotation * localRotation * RotationMatrix4(angle, axis);
            else:
                localRotation = localRotation * RotationMatrix4(angle, axis);
            break;
        case WORLD:
            localRotation = localRotation * RotationMatrix4(angle, axis);
            break;

विश्व-स्थान परिवर्तन की गणना

पूर्णता के लिए, this नोड के लिए दुनिया को बदल दिया गया है, this प्रकार गणना की जाती है:

if parentNode != null:
    worldTranslation = parent.worldTranslation * localTranslation;
    worldRotation    = parent.worldRotation    * localRotation;
    worldScale       = parent.worldScale       * localScale;
else:
    worldTranslation = localTranslation;
    worldRotation    = localRotation;
    worldScale       = localScale;

इसके अलावा, इसके लिए एक नोड का पूर्ण / संचित परिवर्तन है:

Matrix4 fullTransform():
    Matrix4 localXform = worldTranslation * worldRotation * worldScale;

    if parentNode != null:
        return parent.fullTransform * localXform;

    return localXform;

जब एक नोड के परिवर्तन को ओपनजीएल fullTransform वर्दी में भेजने का अनुरोध किया जाता है, तो fullTransform मैट्रिक्स का उपयोग किया जाता है।


Ths बुनियादी समस्या है कि कैसे चलने मैट्रिक्स समस्या को हल करने के लिए।

मान लीजिए आपके पास मैट्रिक्स एक्स और मैट्रिक्स उत्पाद ए बी सी है और मान लीजिए आप चाहते हैं कि आप वाई को बढ़ाना चाहते हैं

X*A*B*C = A*B*Y*C

या ठीक इसके विपरीत।

कोई मैट्रिक्स नहीं मानते हैं, एकमुश्त हैं, पहले सामान्य शब्दों को समाप्त करें:

X*A*B = A*B*Y

अगला, पृथक करें बाएं बनाम सही का ट्रैक रखते हुए, इनवर्स द्वारा गुणा करें:

A^-1*X*A*B = A^-1 *A *B *Y
A^-1*X*A*B = B *Y
B^-1*A^-1*X*A*B = Y

या उस मामले में जहां आपके पास वाई है लेकिन एक्स चाहिए:

X*A*B *B^-1 *A^-1 = A*B*Y*B^-1 *A^-1
X = A*B*Y*B^-1 *A^-1

उपरोक्त सामान्य नियम का केवल एक विशेष मामला है:

X*A = A*Y

माध्यम

X=A*Y*A^-1
A^-1*X*A=Y

नोट के साथ (A*B)^-1 = B^-1 * A^-1

यह प्रक्रिया आपको परिवर्तनों की एक श्रृंखला की जांच करने और "मैं किसी विशिष्ट स्थान पर एक परिवर्तन लागू करना चाहता हूं, लेकिन इसे अन्यत्र लागू करके इसे संग्रहित करना चाहता हूं।", जो आपकी समस्या का मुख्य भाग है।

आपके द्वारा काम किए जाने वाले मैट्रिक्स की श्रृंखला में सभी परिवर्तनों को शामिल करना चाहिए - अनुवाद, रोटेशन, स्केल - न केवल एक ही तरह के परिवर्तन, क्योंकि X * B = B * Y समाधान के लिए X * A * B = A * B * Y


मुझे निकोल बोलस की प्रतिक्रिया कुछ हद तक उपयोगी थी, भले ही कुछ विवरण अभी भी थे I पर इतना स्पष्ट नहीं था। लेकिन इस प्रतिक्रिया ने मुझे जिस समस्या पर काम कर रहा था, उसके बारे में गैर-तुच्छ स्वभाव को देखने में मदद मिली, इसलिए मैंने चीजों को सरल बनाने का फैसला किया।

एक सरल समाधान - हमेशा माता-पिता अंतरिक्ष में

मैंने समस्या को आसान बनाने के लिए Node.TransformSpace को निकाल दिया है। Node.TransformSpace सभी परिवर्तनों को अब एक माता पिता Node के स्थान के सापेक्ष लागू किया जाता है, और चीजें अपेक्षित रूप में काम कर रही हैं डेटा संरचना में बदलाव होता है, जिस पर चीजों को काम करने के बाद मैं प्रदर्शन करने का इरादा रखता हूं (उदाहरण के लिए साधारण वैक्टर के लिए स्थानीय अनुवाद / स्केलिंग मैट्रिक्स की जगह) अब भी जगह में हैं

अद्यतन गणित का सारांश निम्नानुसार है।

नवीनीकृत अनुवाद

एक Node की स्थिति अब एक Matrix4 ऑब्जेक्ट द्वारा प्रदर्शित की जाती है, साथ ही Matrix4 को ऑन-डिमांड (बाद में देखें) बनाया जा रहा है।

void translate(Vector3 tv /*, TransformSpace relativeTo */):
    localPosition += tv;

अद्यतन रोटेशन

रोटेशन अब एक मैट्रिक्स 3, अर्थात 3x3 मैट्रिक्स में निहित हैं।

void rotate(Angle angle, Vector3 axis /*, TransformSpace relativeTo */):
    localRotation *= RotationMatrix3(angle, axis);

मैं अभी भी क्वाटरनीयन को देखने की योजना बना रहा हूं, इसके बाद मैं यह सत्यापित कर सकता हूं कि मेरा क्वाटरनियन <=> मैट्रिक्स रूपांतरण सही हैं I

अपडेट किया गया स्केलिंग

Node की स्थिति की तरह स्केलिंग अब भी एक Vector3 ऑब्जेक्ट है:

void scale(Vector3 sv):
    localScale *= sv;

अद्यतन स्थानीय / विश्व रूपांतरण कम्प्यूटेशंस

निम्न अद्यतन एक Node की दुनिया अपने माता पिता Node , यदि कोई हो, के Node बदलती हैं। यहां एक मुद्दा माता-पिता के पूर्ण रूपांतर (मूल पोस्ट देखें) के लिए एक अनावश्यक समाकलन को हटाकर तय किया गया था।

void updateTransforms():
    if parentNode != null:
         worldRotation = parent.worldRotation * localRotation;
         worldScale    = parent.worldScale    * localScale;
         worldPosition = parent.worldPosition + parent.worldRotation * (parent.worldScale * localPosition);
    else:
        derivedPosition = relativePosition;
        derivedRotation = relativeRotation;
        derivedScale    = relativeScale;

    Matrix4 t, r, s;

    // cache local/world transforms
    t = TranslationMatrix4(localPosition);
    r = RotationMatrix4(localRotation);
    s = ScalingMatrix4(localScale);
    localTransform = t * r * s;

    t = TranslationMatrix4(worldPosition);
    r = RotationMatrix4(worldRotation);
    s = ScalingMatrix4(worldScale);
    worldTransform = t * r * s;

worldTranslation = parentNode.worldTranslation * localTranslation;
worldRotation    = parentNode.worldRotation    * localRotation;
worldScale       = parentNode.worldScale       * localScale;

यह नहीं है कि लगातार परिवर्तनों का संचय कैसे काम करता है। और इसके स्पष्ट क्यों नहीं, यदि आप इसके बारे में सोचते हैं

मान लें कि आपके पास दो नोड हैं: माता-पिता और बच्चे माता-पिता के पास Z अक्ष के बारे में 90-डिग्री, काउंटर-घड़ी की स्थानीय रोटेशन है। बच्चे के एक्स अक्ष में ऑफसेट एक +5 है ठीक है, एक काउंटर-वाइडवर्ड रोटेशन के कारण इसे वाई अक्ष में +5 होना चाहिए, हाँ (दाएं हाथ वाले समन्वय प्रणाली संभालने)?

लेकिन यह नहीं है आपके localTranslation किसी भी रोटेशन से कभी प्रभावित नहीं होता है

यह आपके सभी परिवर्तनों के बारे में सच है अनुवाद केवल अनुवादों से प्रभावित हैं, न कि तराजू या रोटेशन द्वारा। अनुवादों से घूर्णन प्रभावित नहीं होते हैं। आदि।

आपके कोड ऐसा करने के लिए कहता है, और ऐसा नहीं है कि आप इसे कैसे करना चाहते हैं।

अपने मैट्रिस के घटकों को खिसकाते हुए एक अच्छा विचार है। इसका अर्थ है, अलग अनुवाद, रोटेशन, और स्केल (टीआरएस) घटकों का एक अच्छा विचार है। यह उचित क्रम में लगातार स्थानीय परिवर्तनों को लागू करना आसान बनाता है।

अब, घटकों को मैट्रिक्स के रूप में रखना गलत है, क्योंकि यह वास्तव में कोई अर्थ नहीं है और बिना किसी वास्तविक कारण के लिए समय और स्थान अपव्यय करता है। एक अनुवाद केवल एक vec3 , और इसके साथ 13 अन्य घटकों को संग्रहित करने के लिए कुछ भी नहीं है। जब आप स्थानीय रूप से अनुवाद संचित करते हैं, तो आप उन्हें जोड़ देते हैं

हालांकि, जिस क्षण आपको नोड के लिए अंतिम मैट्रिक्स जमा करने की जरूरत है, आपको प्रत्येक टीआरएस अपघटन को अपने स्थानीय मैट्रिक्स में बदलने की जरूरत है, फिर इसे माता-पिता के समग्र परिवर्तन में रूपांतरित कर दें , माता-पिता के व्यक्तिगत टीआरएस घटक नहीं। यही है, आपको अलग-अलग परिवर्तनों को स्थानीय रूप से लिखना है, फिर उन्हें मूल परिवर्तन मैट्रिक्स के साथ गुणा करना होगा। छद्म कोड में:

function AccumRotation(parentTM)
  local localMatrix = TranslationMat(localTranslation) * RotationMat(localRotation) * ScaleMat(localScale)
  local fullMatrix = parentTM * localMatrix

  for each child
    child.AccumRotation(fullMatrix)
  end
end

प्रत्येक माता पिता अपने स्वयं के जमा रोटेशन को बच्चे को पास करते हैं रूट नोड को एक पहचान मैट्रिक्स दिया जाता है।

अब, टीआरएस अपघटन पूरी तरह से ठीक है, लेकिन यह केवल स्थानीय परिवर्तनों के साथ काम करते समय काम करता है। यही है, माता पिता के सापेक्ष परिवर्तन। यदि आप किसी ऑब्जेक्ट को अपने स्थानीय स्थान में घुमाने के लिए चाहते हैं, तो आप अपनी ओरिएंटेशन के लिए एक क्वाटरनियन आवेदन कर सकते हैं।

लेकिन गैर-स्थानीय स्थान में परिवर्तन करना एक अलग कहानी है। उदाहरण के लिए, यदि आप चाहते हैं, तो विश्व-अंतरिक्ष में उस ऑब्जेक्ट में एक अनुवाद लागू करें जिसमें कुछ मनमाने ढंग से श्रृंखलाएं लागू होंगी ... जो कि एक गैर-तुच्छ कार्य है। वास्तव में, यह एक आसान काम है: आप ऑब्जेक्ट की विश्व-स्तरीय मैट्रिक्स की गणना करते हैं, फिर उस के बायीं ओर एक अनुवाद मैट्रिक्स लागू करते हैं, फिर माता-पिता की तुलना में माता-पिता के रिश्तेदार परिवर्तन की गणना करने के लिए माता-पिता की विश्व-अंतरिक्ष मैट्रिक्स के व्युत्क्रम का उपयोग करें।

function TranslateWorld(transVec)
  local parentMat = this->parent ? this->parent.ComputeTransform() : IdentityMatrix
  local localMat = this->ComputeLocalTransform()
  local offsetMat = TranslationMat(localTranslation)
  local myMat = parentMat.Inverse() * offsetMat * parentMat * localMat
end

पी -1 पी बात का मतलब वास्तव में एक आम निर्माण है इसका मतलब है कि सामान्य परिवर्तन O को P के स्थान में बदलना है। इस प्रकार, यह एक विश्व को पैरेंट मैट्रिक्स के स्थान में ऑफसेट करता है। तब हम अपने स्थानीय परिवर्तनों पर लागू होते हैं

myMat अब एक परिवर्तन मैट्रिक्स है, जो जब माता-पिता के रूपांतरण से गुणा करता है, transVec को लागू transVec जैसे कि वह विश्व अंतरिक्ष में था वही जो आप चाहते थे

समस्या यह है कि myMat एक मैट्रिक्स है , टीआरएस अपघटन नहीं। आप टीआरएस अपघटन में वापस कैसे आ सकते हैं? अच्छी तरह से ... इसमें वास्तव में गैर तुच्छ मैट्रिक्स गणित की आवश्यकता है। इसके लिए सिंगुलर मान अपघटन कहा जाने वाला कुछ करना आवश्यक है और भी बदसूरत गणित को लागू करने के बाद, एसवीडी विफल हो सकता है । संभव है कि एक गैर-विघटनकारी मैट्रिक्स हो।

एक दृश्य ग्राफ़ सिस्टम में मैंने लिखा, मैंने एक विशेष वर्ग बनाया जो प्रभावी ढंग से टीआरएस अपघटन और मैट्रिक्स के बीच एक संघ था जो इसे दर्शाता है। आप पूछ सकते हैं कि क्या यह विघटित हो गया था, और अगर आप टीआरएस घटकों को संशोधित कर सकते थे। लेकिन एक बार जब आप इसे सीधे 4x4 मैट्रिक्स मान दें, तो यह एक मैट्रिक्स बन गया और आप स्थानीय विघटित परिवर्तनों को लागू नहीं कर पाए। मैंने कभी भी एसवीडी को लागू करने की कोशिश नहीं की

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

किसी भी मामले में, आपके कोड में कुछ सही विचार हैं, लेकिन कुछ बहुत ही गलत हैं आपको यह तय करने की आवश्यकता है कि टीआरएस अपघटन बनाम कितना महत्वपूर्ण है। यह गैर-स्थानीय परिवर्तन लागू करने में कितना महत्वपूर्ण है।