কীভাবে একটি কঙ্কাল থেকে অন্য কঙ্কাল থেকে অ্যানিমেশনগুলিকে পুনর্গঠন করা যায়?


23

আমি একটি কঙ্কালের জন্য অন্য কঙ্কালের জন্য সঠিক দেখতে অ্যানিমেশনগুলি স্থানান্তর করতে কোড লেখার চেষ্টা করছি। উত্স অ্যানিমেশনগুলিতে মূলের অনুবাদগুলি বাদে কেবলমাত্র আবর্তনের সমন্বয়ে গঠিত হয় (সেগুলি সিএমইউ মোশন ক্যাপচার ডাটাবেস থেকে এমক্যাপ অ্যানিমেশন )। অনেকগুলি 3D অ্যাপ্লিকেশন (যেমন মায়া) এর মধ্যে অন্তর্নির্মিত এই সুবিধা রয়েছে তবে আমি আমার গেমটির জন্য এটির একটি (খুব সাধারণ) সংস্করণ লেখার চেষ্টা করছি।

আমি হাড়ের ম্যাপিংয়ের জন্য কিছু কাজ করেছি, এবং কঙ্কালগুলি হায়ারারাক্লিকভাবে একই রকম (বাইপিড) হওয়ায় আমি মেরুদণ্ড ছাড়া সমস্ত কিছুর জন্য 1: 1 হাড়ের ম্যাপিং করতে পারি (পরে এটি কাজ করতে পারে)। তবে সমস্যাটি হ'ল বেস কঙ্কাল / বাঁধন পোজগুলি আলাদা এবং হাড়গুলি বিভিন্ন স্কেল (ছোট / দীর্ঘ) হয় তাই আমি যদি সরাসরি ঘূর্ণনটি অনুলিপি করি তবে এটি খুব অদ্ভুত দেখাচ্ছে।

নীচে লরানচোর সমাধানের মতো অনেকগুলি বিষয় চেষ্টা করেছি (কোনও অস্থি-নির্দিষ্ট গুণক দ্বারা অ্যানিমেশনটিতে প্রতিটি ফ্রেমকে গুণ করা)। কারও কাছে যদি এই জাতীয় স্টাফগুলিতে (কাগজপত্র, উত্স কোড, ইত্যাদি) এর কোনও সংস্থান থাকে তবে তা সত্যই সহায়ক হবে।


আপনি কীভাবে আমার কাছে লেজ এবং পায়ের মধ্যে থাকা জিনিসটিকে উপেক্ষা করবেন? : পি
কওডি

2
@ কেএওডি যদি আপনাকে জিজ্ঞাসা করতে হয় তবে কঙ্কালটি মূল (0,0) এ রয়েছে তাই সেখানে একটি জাল হাড় রয়েছে। লেজ হিসাবে ... সবাই জানেন আপনার লেজ থাকলে জীবন আরও ভাল। আমি সর্বদা ভেবেছিলাম এটি কফি কাপ বহন এবং গাছের অঙ্গগুলিতে ভারসাম্য রাখার মতো কাজের জন্য কার্যকর।
রবার্ট ফ্রেজার 23

আমি এর বাস্তব সময়ের ডেমো দেখেছি যেখানে এক্সএনএতে প্রদর্শিত একটি মডেল সঞ্চার করার জন্য একটি আত্মীয় ব্যবহৃত হয়েছিল। মনে করুন কোডটি একটি মুক্ত উত্স সাইটে ছিল। অনুসন্ধান করবে ...
জর্জ ডেকেট


আমি সন্দেহ করি যে আপনার সমস্যা হাড়ের স্কেলিংয়ের চেয়ে স্বতন্ত্র বাঁধা পোষাকের সাথে বেশি, আপনি এটি আলাদা করার চেষ্টা করতে পারেন। উদাহরণস্বরূপ, আসল কঙ্কাল থেকে শুরু করুন, একটি নতুন কঙ্কাল তৈরি করতে এর উপর কয়েকটি হাড় স্কেল করুন এবং দেখুন যে এটির সাথে আপনার অ্যালগোরিদম ভেঙে গেছে কিনা। যদি এটি না হয়, তবে আসল কঙ্কাল থেকে পুনরায় চালু করুন তবে এবার হাড়গুলি স্কেল করবেন না, কেবল তাদের ঘোরান এবং দেখুন আপনার অ্যালগোরিদম ব্রেক হয়েছে কিনা। যদি এটি হয় তবে হ্যাঁ সম্ভবত কোথাও সঞ্চালনের জন্য একটি অতিরিক্ত রূপান্তর রয়েছে।
লরেন্ট কুইভিডউ

উত্তর:


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

আমি বিশ্বাস করি যে আপনার সবচেয়ে সহজ বিকল্পটি যদি আপনার সম্ভাবনাটি পেয়ে থাকে তবে আপনার নতুন কঙ্কালের সাথে মূল বাঁধন পোজের সাথে মিলে যাওয়া (যদি আপনার নতুন কঙ্কালটি এখনও চামড়াযুক্ত না হয়)।

যদি আপনি এটি না করতে পারেন তবে এখানে কিছু চেষ্টা করার চেষ্টা করুন। এটি কেবল স্বজ্ঞাততা, আমি সম্ভবত প্রচুর জিনিসকে উপেক্ষা করছি তবে এটি আপনাকে আলো খুঁজে পেতে সহায়তা করবে। প্রতিটি হাড়ের জন্য:

  • আপনার "পুরাতন" বাইন্ড পোজে আপনি একটি চৌম্বক পেয়েছেন যা তার পিতামাতার হাড়ের তুলনায় এই হাড়ের আপেক্ষিক ঘূর্ণন বর্ণনা করে । এটি কীভাবে সন্ধান করা যায় তার একটি ইঙ্গিত এখানে's এটি কল করুন ।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
রবার্ট ফ্রেজার

আপনি কি নিশ্চিত যে আপনি সর্বদা আপনার ঘূর্ণনগুলি পিতামাতার হাড়ের তুলনায় তুলনামূলকভাবে প্রকাশ করছেন? আপনার ভিডিওটি দেখে মনে হচ্ছে আপনি কোথাও একটি নিখুঁত / বিশ্ব ঘূর্ণন ব্যবহার করছেন, যেখানে পরিবর্তে আপনার পিতামাতার থেকে আত্মীয় ঘূর্ণন ব্যবহার করা উচিত।
লরেন্ট কুইভিডু

হ্যাঁ, আমি নিশ্চিত যে আমি এখানে আপেক্ষিক রূপান্তরগুলি ব্যবহার করছি (আমি বিস্মৃতকরূপে চেষ্টা করেছি, এটি অনেকটা অপরিচিত দেখাচ্ছে) I আমি এই ভিডিওটির জন্য আমি যে কোডটি ব্যবহার করছিলাম তা দিয়ে ওপি আপডেট করেছি। এইভাবে এটি ডিবাগ করার চেষ্টা করার পরিবর্তে, আমি বরং কিছু উত্স কোড বা টিউটোরিয়াল দেখতে পেয়েছি যেখানে এটি সফলভাবে করা হয়েছিল, তবে আমি কী ভুল করছি তা বুঝতে পারি।
রবার্ট ফ্রেজার

অবশ্যই, তবে সম্ভবত এটি করার কোনও টিউটোরিয়াল নেই :) আমি মনে করি আপনি উপরের কোডটিতে কিছু উল্টিয়ে দিয়েছেন, আমি আমার উত্তরটি সম্পাদনা করব।
লরেন্ট কুইভিডু

আমি অনেক উপায়ে চেষ্টা করেছি, এবং কোনওটিই কাজ করে নি। আমি আসলে কী ভুল হচ্ছে তা দেখার জন্য প্রতি ফ্রেম ভিত্তিতে বিশ্বব্যাপী ঘূর্ণন গণনা করার চেষ্টা করতে যাচ্ছি। তারপরও আপনার সাহায্যের জন্য ধন্যবাদ; আমি আপনাকে 100 প্রতিনিধি দেব
রবার্ট ফ্রেজার
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.