एक कंकाल से दूसरे में जाने के लिए कैसे प्रोग्रामेटिक रूप से रिटारगेट करें?


23

मैं उन एनिमेशन को स्थानांतरित करने के लिए कोड लिखने की कोशिश कर रहा हूं जो एक कंकाल के लिए डिज़ाइन किए गए थे जो दूसरे कंकाल पर सही दिखते हैं। स्रोत एनिमेशन में केवल रूट पर अनुवाद के अलावा (वे CMU गति कैप्चर डेटाबेस से mocap एनिमेशन हैं ) रोटेशन शामिल हैं । कई 3D एप्लिकेशन (जैसे माया) में यह सुविधा अंतर्निहित है, लेकिन मैं अपने गेम के लिए इसका (बहुत सरल) संस्करण लिखने की कोशिश कर रहा हूं।

मैंने बोन मैपिंग पर कुछ काम किया है, और क्योंकि कंकाल hierarchically (bipeds) समान हैं, मैं हर चीज के लिए 1: 1 बोन मैपिंग कर सकता हूं लेकिन रीढ़ (बाद में उस पर काम कर सकता है)। हालाँकि, समस्या यह है कि आधार कंकाल / बाँध पोज़ अलग हैं, और हड्डियाँ अलग-अलग तराजू (छोटी / लम्बी) हैं, इसलिए अगर मैं सिर्फ रोटेशन को सीधे कॉपी करता हूँ तो यह बहुत अजीब लगता है।

मैंने बिना किसी लाभ के नीचे दिए गए लोरैंको के समाधान के समान कई चीजों का प्रयास किया है (यानी एनीमेशन में प्रत्येक फ्रेम को एक हड्डी-विशिष्ट गुणक द्वारा गुणा करना)। अगर किसी के पास इस तरह के (कागजात, स्रोत कोड, आदि) सामान पर कोई संसाधन हैं, तो यह वास्तव में मददगार होगा।


आप मुझसे पूंछ और पैरों के बीच की बात को अनदेखा करने की उम्मीद कैसे करते हैं? : P
kaoD

2
@kaoD अगर आपको पूछना है, तो कंकाल (0,0) पर निहित है, इसलिए वहां एक नकली हड्डी है। पूंछ के लिए के रूप में ... हर कोई जानता है कि अगर आपके पास पूंछ है तो जीवन बेहतर है। मैंने हमेशा सोचा है कि यह कॉफी कप ले जाने और पेड़ के अंगों पर संतुलन रखने जैसी चीजों के लिए कुशल होगा।
रॉबर्ट फ्रेजर

मैंने इसका एक वास्तविक समय डेमो देखा है, जहां एक्स में प्रदर्शित एक मॉडल को चेतन करने के लिए एक किनेक्ट का उपयोग किया गया था। लगता है कि कोड एक ओपन सोर्स साइट पर था। खोज करेंगे ...
जॉर्ज डकेट


मुझे संदेह है कि आपकी समस्या हड्डियों के स्केलिंग की तुलना में अलग-अलग बाँध पोज़ के साथ अधिक है, आप इसे अलग करने की कोशिश कर सकते हैं। उदाहरण के लिए, मूल कंकाल से शुरू करके, एक नया कंकाल बनाने के लिए उस पर कुछ हड्डियों को स्केल करें, और देखें कि आपका एल्गोरिथ्म किसी एक के साथ टूटता है या नहीं। यदि ऐसा नहीं होता है, तो मूल कंकाल से पुनरारंभ करें, लेकिन इस बार हड्डियों को स्केल न करें, बस उन्हें घुमाएं और देखें कि क्या आपका एल्गोरिथ्म टूट जाता है। यदि ऐसा होता है, तो हाँ, शायद कहीं प्रदर्शन करने के लिए एक अतिरिक्त परिवर्तन है।
लॉरेंट कौविदो

जवाबों:


8

समस्या संख्यात्मक स्थिरता में से एक थी। 2 महीने के दौरान इस पर 30 घंटे का काम करें, केवल यह पता लगाने के लिए कि मैं शुरू से ही इसे सही कर रहा था। जब मैंने रिटारगेट कोड में प्लग करने से पहले रोटेशन मेट्रिसेस को ऑर्थो-सामान्य किया, तो स्रोत * व्युत्क्रम (लक्ष्य) को गुणा करने का सरल उपाय पूरी तरह से काम किया। बेशक, वहाँ एक बहुत कुछ है कि (विशेष रूप से, कंकाल के विभिन्न आकार, यानी कंधे की चौड़ाई, आदि को ध्यान में रखते हुए) की तुलना में अधिक है। यहाँ कोड मैं सरल, भोली दृष्टिकोण के लिए उपयोग कर रहा हूँ, अगर कोई उत्सुक है:

    public static SkeletalAnimation retarget(SkeletalAnimation animation, Skeleton target, string boneMapFilePath)
    {
        if(animation == null) throw new ArgumentNullException("animation");
        if(target == null) throw new ArgumentNullException("target");

        Skeleton source = animation.skeleton;
        if(source == target) return animation;

        int nSourceBones = source.count;
        int nTargetBones = target.count;
        int nFrames = animation.nFrames; 
        AnimationData[] sourceData = animation.data;
        Matrix[] sourceTransforms = new Matrix[nSourceBones];
        Matrix[] targetTransforms = new Matrix[nTargetBones];
        AnimationData[] temp = new AnimationData[nSourceBones];
        AnimationData[] targetData = new AnimationData[nTargetBones * nFrames];

        // Get a map where map[iTargetBone] = iSourceBone or -1 if no such bone
        int[] map = parseBoneMap(source, target, boneMapFilePath);

        for(int iFrame = 0; iFrame < nFrames; iFrame++)
        {
            int sourceBase = iFrame * nSourceBones;
            int targetBase = iFrame * nTargetBones;

            // Copy the root translation and rotation directly over
            AnimationData rootData = targetData[targetBase] = sourceData[sourceBase];

            // Get the source pose for this frame
            Array.Copy(sourceData, sourceBase, temp, 0, nSourceBones);
            source.getAbsoluteTransforms(temp, sourceTransforms);

            // Rotate target bones to face that direction
            Matrix m;
            AnimationData.toMatrix(ref rootData, out m);
            Matrix.Multiply(ref m, ref target.relatives[0], out targetTransforms[0]);
            for(int iTargetBone = 1; iTargetBone < nTargetBones; iTargetBone++)
            {
                int targetIndex = targetBase + iTargetBone;
                int iTargetParent = target.hierarchy[iTargetBone];
                int iSourceBone = map[iTargetBone];
                if(iSourceBone <= 0)
                {
                    targetData[targetIndex].rotation = Quaternion.Identity;
                    Matrix.Multiply(ref target.relatives[iTargetBone], ref targetTransforms[iTargetParent], out targetTransforms[iTargetBone]);
                }
                else
                {
                    Matrix currentTransform, inverseCurrent, sourceTransform, final, m2;
                    Quaternion rot;

                    // Get the "current" transformation (transform that would be applied if rot is Quaternion.Identity)
                    Matrix.Multiply(ref target.relatives[iTargetBone], ref targetTransforms[iTargetParent], out currentTransform);
                    Math2.orthoNormalize(ref currentTransform);
                    Matrix.Invert(ref currentTransform, out inverseCurrent);
                    Math2.orthoNormalize(ref inverseCurrent);

                    // Get the final rotation
                    Math2.orthoNormalize(ref sourceTransforms[iSourceBone], out sourceTransform);
                    Matrix.Multiply(ref sourceTransform, ref inverseCurrent, out final);
                    Math2.orthoNormalize(ref final);
                    Quaternion.RotationMatrix(ref final, out rot);

                    // Calculate this bone's absolute position to use as next bone's parent
                    targetData[targetIndex].rotation = rot;
                    Matrix.RotationQuaternion(ref rot, out m);
                    Matrix.Multiply(ref m, ref target.relatives[iTargetBone], out m2);
                    Matrix.Multiply(ref m2, ref targetTransforms[iTargetParent], out targetTransforms[iTargetBone]);
                }
            }
        }

        return new SkeletalAnimation(target, targetData, animation.fps, nFrames);
    }

क्या इस पृष्ठ पर कोड तब से अपडेट किया गया है जब से इसे लिखा गया है? उस इंजन के संदर्भ के बिना समझने की कोशिश करने में कठिन समय होने के नाते जो इसका उपयोग करता है। मैं एनीमेशन रिटारगेटिंग भी करने की कोशिश कर रहा हूं। रिटारगेटिंग को कैसे संभालना है, इसके चरणों के कुछ छद्म कोड होना बहुत अच्छा होगा।
स्केचपंकलैब्स

4

मेरा मानना ​​है कि यदि आप संभावना प्राप्त कर चुके हैं (यदि आपका नया कंकाल अभी तक चमकाया नहीं गया है) तो आपका सबसे आसान विकल्प अपने नए कंकाल के साथ मूल बाँध मुद्रा से मेल खाना है।

यदि आप ऐसा नहीं कर सकते हैं, तो यहां कुछ आप कोशिश कर सकते हैं। यह सिर्फ अंतर्ज्ञान है, मैं शायद बहुत सारी चीजों को देख रहा हूं, लेकिन यह आपको प्रकाश को खोजने में मदद कर सकता है। प्रत्येक हड्डी के लिए:

  • आपके "पुराने" बंधन में, आपको एक चतुष्कोण मिला है जो इस हड्डी के सापेक्ष रोटेशन का वर्णन इसकी मूल हड्डी की तुलना में करता है। यहाँ कैसे इसे खोजने के लिए एक संकेत है। चलो बुलावा आया q_old

  • Ibid। अपने "नए" बाँधने की मुद्रा के लिए, आइए इसे कॉल करें q_new

  • आप "नया" बाइंड पोज़ से लेकर "पुराने" बिन पोज़ तक रिश्तेदार रोटेशन पा सकते हैं, जैसा कि यहां वर्णित है । वह है q_new_to_old = inverse(q_new) * q_old

  • फिर एक एनीमेशन कुंजी में, आपको अपना एक बटेरियन मिला है जो उस हड्डी को "पुरानी" बाइंड पोज़ से एक एनिमेटेड बोस में बदल देता है। चलो यह एक कॉल करें q_anim

q_animसीधे उपयोग करने के बजाय, उपयोग करने का प्रयास करें q_new_to_old * q_anim। यह एनीमेशन को लागू करने से पहले, बाइंड पोज़ के बीच अभिविन्यास अंतर को "रद्द" करना चाहिए।

यह चाल हो सकता है।

संपादित करें

ऊपर दिया गया आपका कोड यहां बताए गए तर्क का पालन करता है, लेकिन कुछ उल्टा है। ऐसा करने के बजाय:

multipliers[iSourceBone] = Quaternion.Invert(sourceBoneRot) * targetBoneRot;

आप कोशिश कर सकते हैं कि:

multipliers[iSourceBone] = Quaternion.Invert(targetBoneRot) * sourceBoneRot;

मुझे लगता है कि समान एनीमेशन को प्राप्त करने के लिए, स्रोत एनीमेशन को लागू करने से पहले आपको अपने लक्ष्य से अपने स्रोत में बदलने की आवश्यकता है।


दोनों स्रोतों और लक्ष्यों के बंधन अलग-अलग हो रहे हैं, यही वजह है कि मैं इस :-) को लागू कर रहा हूं। वास्तव में, लक्ष्य रोटेशन के व्युत्क्रम से गुणा करना पहली चीज थी जो मैंने कोशिश की थी। मैंने आपके सुझाव के अनुसार हड्डी के घूमने की कोशिश की, लेकिन नतीजा वही निकला। यहाँ क्या गलत हो रहा है का एक वीडियो है: youtube.com/watch?v=H6Qq37TM4Pg
रॉबर्ट फ्रेजर

क्या आप सुनिश्चित हैं कि आप हमेशा अपने घुमावों को अपेक्षाकृत मूल माता-पिता के लिए व्यक्त कर रहे हैं? आपके वीडियो को देखकर ऐसा लगता है कि आप कहीं एक निरपेक्ष / विश्व रोटेशन का उपयोग कर रहे हैं, जहाँ आपको एक रिश्तेदार-से-जनक रोटेशन का उपयोग करना चाहिए।
लॉरेंट कौविदो

हाँ, मुझे पूरा यकीन है कि मैं यहाँ के सापेक्ष परिवर्तनों का उपयोग कर रहा हूँ (मैंने निरपेक्षता के साथ प्रयास किया है, यह बहुत अधिक अजनबी दिखता है)। मैंने इस वीडियो के लिए उपयोग किए जा रहे कोड के साथ ओपी को अद्यतन किया। इस तरह इसे डिबग करने की कोशिश करने के बजाय, मैं कुछ स्रोत कोड या ट्यूटोरियल देखूंगा जहां यह सफलतापूर्वक किया गया था, फिर मैं यह पता लगा सकता हूं कि मैं क्या गलत कर रहा हूं।
रॉबर्ट फ्रेजर

ज़रूर, लेकिन शायद ऐसा करने के लिए कोई ट्यूटोरियल नहीं है :) मुझे लगता है कि आप ऊपर दिए गए अपने कोड में कुछ उल्टा करते हैं, मैं अपना जवाब संपादित करूंगा।
लॉरेंट कौविदो

मैंने कई तरह की कोशिश की है, और कोई भी काम नहीं किया है। मैं वास्तव में वैश्विक घूर्णन की गणना करने की कोशिश कर रहा हूं, जो कि गलत हो रहा है, यह देखने के लिए प्रति-फ्रेम आधार पर गणना करता है। यद्यपि कि आपकी इस सहायता के लिए धन्यवाद; मैं आपको 100 प्रतिनिधि दूंगा।
रॉबर्ट फ्रेजर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.