• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            3d Game Walkman

            3d圖形渲染,網(wǎng)絡(luò)引擎 — tonykee's Blog
            隨筆 - 45, 文章 - 0, 評論 - 309, 引用 - 0
            數(shù)據(jù)加載中……

            最近在對骨骼導(dǎo)出插件進(jìn)行重構(gòu),有了一些新的感悟

            最近嘗試把3dmax的physique骨骼系統(tǒng)導(dǎo)出插件重構(gòu)成了skin的方式,用了用skin感覺相比physique要強大的多,skin是max最老的蒙皮修改器,應(yīng)該比physique還要早把,但后續(xù)版本升級做的很強大,據(jù)說和maya的方法差不多,physique修改器更新緩慢,而且用了一下確實是skin的修改器要好用一些,尤其對于蒙皮人物骨骼按部件進(jìn)行拆分方面,skin方式要方便很多,實現(xiàn)換裝系統(tǒng)也不是問題,我最近正在實現(xiàn)一套比較好的部件裝配式的換裝系統(tǒng),大體的方法是把人物拆分,按需要進(jìn)行骨骼部件的組裝,比如手,腳,頭發(fā),身體,裙擺,都可以是獨立的骨架部件,然后組裝在一起,很多游戲其實都有這樣的功能,實現(xiàn)方法大同小異吧,不過我這里有點心得可以分享一下,人物的骨骼導(dǎo)出的時候不要圖方便只導(dǎo)出每塊骨頭的世界矩陣,而應(yīng)該導(dǎo)出這塊骨頭相對父骨節(jié)的矩陣,形成一顆顆子樹,這樣做在單副骨架上看似乎沒有什么大的優(yōu)勢,還會帶來額外的計算量,但實際上要實現(xiàn)換裝,比如一個部件從一個形體直接配置到另外一個形體上的時候,優(yōu)勢就體現(xiàn)出來了,真的必須這么干啊,我想以后做一些外界作用力的物理效果的時候,父骨架的偏移影響到子骨架的計算,或反向IK計算,應(yīng)該也容易計算了(比如自由墜落的布娃系統(tǒng))當(dāng)然這是后話了,現(xiàn)在多做點這樣工作,以后擴展起來會容易很多

            另外有個心得可以和大家分享一下,那就是關(guān)于骨骼矩陣的導(dǎo)出冗余數(shù)據(jù)的精簡方法、其實做過蒙皮的人應(yīng)該會知道,一套完整的骨骼動畫的數(shù)據(jù)量最大的并不是頂點的數(shù)據(jù),那個數(shù)量基本固定的,不會隨動作的增長而變大,真正龐大的是骨骼的關(guān)鍵幀導(dǎo)出數(shù)據(jù)
            來個簡單的計算,如果一個蒙皮角色的總骨骼有100根,1000幀的動畫
            那么占用的空間= 100 * 1000 * sizeof(D3DXMATRIX)  = 100 *1000 * 64Bytes  差不多占了6MB多的容量,一般一個角色的動畫多達(dá)幾千到上萬幀的,那么這個數(shù)量的增長是很龐大的,也許你會覺得這幾MB到10多MB的數(shù)據(jù)量不算什么,現(xiàn)在內(nèi)存不都是幾個G了嗎?但你要想想,現(xiàn)在游戲卡的現(xiàn)象不在于你cpu多塊,內(nèi)存多大,很大部分愿意是磁盤io讀取慢了,這才是瓶頸,這些年計算機的速度是提升了很多倍可就是硬盤的讀寫速度沒什么變化啊,同屏幾十個不同的角色,如果不預(yù)加載,用實時加載,那么一加載起來動不動就是幾十MB的數(shù)據(jù),不管什么機器,再怎么多線程優(yōu)化也一樣卡,即使單機都會卡
             
            所以需要想辦法來壓縮精簡這些數(shù)據(jù),其實壓縮的思路并不復(fù)雜,我們的骨骼矩陣一般都用的是線性差值計算的,max在打上關(guān)鍵幀的時候也基本上是線性差值的,這樣就好辦了,線性差值的數(shù)據(jù)過渡一般都有一個特點,那就是比較“平滑”,很多數(shù)據(jù)變化幅度不大的情況下前一幀和后一幀的矩陣平均值剛好等于當(dāng)前幀的矩陣值,就利用這個特性我們就能過濾掉相當(dāng)大數(shù)量級的矩陣了

            以下的算法針對于連續(xù)線性變換的數(shù)據(jù)精簡壓縮都是有用的,不僅僅只針對于矩陣,我在下面的例子里面用的是整數(shù),思路清楚以后換成矩陣就好了


            #include "stdafx.h"
            #include <WTypes.h>
            #include <vector>
            #include <map>
            #include <assert.h>
            using namespace std;

             

            struct  Idinfo
            {
             int id;     //原數(shù)據(jù)索引
             int id0;   //等比區(qū)間索引上界索引
             int id1;   //等比區(qū)間索引下界索引
             BOOL GetValue(map<int,int> & imap, int& val)
             {
              if(id == id0 && id == id1)
              {
               map<int, int>::iterator it0 = imap.find(id0);
               assert(it0 != imap.end());
               val = it0->second;
               return TRUE;
              }
              else if(id > id0 && id < id1)
              {
               map<int, int>::iterator it0 = imap.find(id0);
               map<int, int>::iterator it1 = imap.find(id1);
               assert(it0 != imap.end());
               assert(it1 != imap.end());
               int v0 = it0->second;
               int v1 = it1->second;
               val = v0 + ((v1 - v0) / (id1 - id0)) * (id - id0);
               return TRUE;
              }
              return FALSE;
             }
            };


            int _tmain(int argc, _TCHAR* argv[])
            {
             vector<int> arr; //假設(shè)這里面放的就是線性變換的數(shù)據(jù)
             arr.push_back(2);
             arr.push_back(4);
             arr.push_back(6);
             arr.push_back(8);
             arr.push_back(15);
             arr.push_back(16);
             arr.push_back(17);
             arr.push_back(18);
             arr.push_back(19);
             arr.push_back(20);

             map<int,int,less<int>> imap; //把非等比變化的數(shù)據(jù)導(dǎo)出(自動按原索引排序的)
             int sz = (int)arr.size();
             for(int i = 0; i < sz; ++i)
             {
              if(i == 0 || i == sz - 1)
              {
               imap.insert(pair<int, int>(i, arr[i])); //頭尾不過濾,一定要保留的
              }
              else
              {
               if(arr[i] != (arr[i - 1] + arr[i + 1]) / 2) //過濾掉前后等比的數(shù)據(jù),
                                                                        //提示一下,如果是浮點數(shù)建議不要這樣比較,浮點數(shù)有誤差的,建議有個0.0001的容差,視情況而定
               {
                imap.insert(pair<int, int>(i, arr[i]));
               }
              }
             }

             vector<Idinfo> vecIds;  //計算每個數(shù)據(jù)的索引描述

             for(int i = 0; i < sz; ++i)
             {
              map<int,int>::iterator it = imap.find(i);

              BOOL _lowBoundFind =  FALSE;
              BOOL _highBoneFind = FALSE;
              Idinfo idInfo;
              idInfo.id = i;
              for(it = imap.begin();it != imap.end(); ++it)
              {
               int id = it->first;
               if(i == id)
               {
                idInfo.id0 = id;
                idInfo.id1 = id;
                _lowBoundFind = TRUE;
                _highBoneFind = TRUE;
               }

               if(i > id)
               {
                idInfo.id0 = id;
                _lowBoundFind = TRUE;
               }

               if(i < id)
               {
                idInfo.id1 = id;
                _highBoneFind = TRUE;
               }

               if(_lowBoundFind && _highBoneFind)
               {
                vecIds.push_back(idInfo);
                break;
               }
              }
             }

             //檢驗一下能否把原線性隊列的數(shù)據(jù)完全還原出來
             for(vector<Idinfo>::iterator it = vecIds.begin(); it < vecIds.end(); ++it)
             {
              Idinfo & idInfo = *it;
              int id = 0;
              if(idInfo.GetValue(imap, id))
              {
               printf("%d \r\n", id);
              }
             }

             return 0;
            }


            //可以看到,我們實際導(dǎo)出的是imap就夠了,vecIds可以計算出來的,也就是說只需要imap就能確定arr集合的每一個元素了
            上面的例子可以看到10個元素“壓縮”成了4個元素,數(shù)據(jù)變化越平滑,壓縮的數(shù)據(jù)量將會越大

            posted on 2010-10-17 23:51 李侃 閱讀(3604) 評論(7)  編輯 收藏 引用 所屬分類: 設(shè)計思路

            評論

            # re: 最近在對骨骼導(dǎo)出插件進(jìn)行重構(gòu),有了一些新的感悟[未登錄]  回復(fù)  更多評論   

            "如果一個蒙皮角色的總骨骼有100根,1000幀的動畫那么占用的空間= 100 * 1000 * sizeof(D3DXMATRIX) = 100 *1000 * 64Bytes 差不多占了6MB多的容量,一般一個角色的動畫多達(dá)幾千到上萬幀的,....."
            難道動畫沒關(guān)鍵幀插值?為啥每幀都有一個矩陣?
            2010-10-18 13:50 | kaka

            # re: 最近在對骨骼導(dǎo)出插件進(jìn)行重構(gòu),有了一些新的感悟  回復(fù)  更多評論   

            樓主的骨骼動畫數(shù)據(jù)可能是通過采集獲得的,沒有做成關(guān)鍵幀。
            2010-10-18 20:49 | wimdys

            # re: 最近在對骨骼導(dǎo)出插件進(jìn)行重構(gòu),有了一些新的感悟  回復(fù)  更多評論   

            今天看了一下max的api,關(guān)鍵幀運動插值計算有TCB, BEZIER,和LINEAR三種方式,還要根據(jù)IKeyControl來獲取旋轉(zhuǎn)、平移、和縮放的x,y,z三個分量的關(guān)鍵幀控制器,可以說相當(dāng)于有9組控制器,通過這些控制器來得到運動軌跡曲線上標(biāo)注的關(guān)鍵幀,三種插值得到的運動軌跡的變化是不一樣的,前兩個是曲線,最后一個是直線,這里面基本意思是看明白了,我決定把這通過IKeyControl得到的關(guān)鍵幀,和前面提到的幀壓縮算法結(jié)合起來再來試驗一下效果,但不打算使用TCB和BEZIER兩種計算方法,這兩種差值算法還要折騰曲線方程,過于復(fù)雜了,還是打算使用LINEAR線性差值的方式,我想缺點就是動作也許會沒那么平滑吧,就好像行車轉(zhuǎn)彎的時候按直線轉(zhuǎn)和按弧線轉(zhuǎn),肯定是弧線自然一些,但代價似乎也不小吧,主要是計算插值的曲線方程不太容易搞
            2010-10-19 20:14 | 李侃

            # re: 最近在對骨骼導(dǎo)出插件進(jìn)行重構(gòu),有了一些新的感悟  回復(fù)  更多評論   

            @李侃
            建議樓主如果是導(dǎo)出關(guān)鍵幀數(shù)據(jù)的話還是用IGAME吧!!用IGame導(dǎo)出關(guān)鍵幀非常方便的哦!!!
            2010-10-23 01:29 | G++

            # re: 最近在對骨骼導(dǎo)出插件進(jìn)行重構(gòu),有了一些新的感悟  回復(fù)  更多評論   

            已經(jīng)搞定了,還真是不容易,拋棄矩陣,那玩意根本就不能直接拿來做線性插值,因為旋轉(zhuǎn)的產(chǎn)生是有弧度的,直接這個矩陣來做差值一定會結(jié)果變成直線位移而嚴(yán)重失真,應(yīng)該用每個骨骼原點自身的四元素旋轉(zhuǎn)和自身位移和骨骼的世界原點這三個數(shù)據(jù)來做,回頭我會寫一篇具體實現(xiàn)方法的文章,現(xiàn)在任意時間的任意頂點的位置經(jīng)過我的關(guān)鍵幀插值計算,已經(jīng)和3dmax完全能對應(yīng)上無誤差了
            2010-10-24 12:30 | 李侃

            # re: 最近在對骨骼導(dǎo)出插件進(jìn)行重構(gòu),有了一些新的感悟  回復(fù)  更多評論   

            對動作進(jìn)行采樣,然后用一定的算法從采樣數(shù)據(jù)中提出關(guān)鍵幀。

            因為Max中的關(guān)鍵幀插值算法比較復(fù)雜(看曲線編輯器)。

            一種幀壓縮算法:
            《Ogre的skeleton數(shù)據(jù)的壓縮》Azure Product
            http://www.azure.com.cn/?id=430
            2010-10-27 20:25 | funcman

            # re: 最近在對骨骼導(dǎo)出插件進(jìn)行重構(gòu),有了一些新的感悟  回復(fù)  更多評論   

            Max中的關(guān)鍵幀插值算法的確是比較復(fù)雜,可吃透它意義非同凡響啊

            之前看過這篇文章,那個幀壓縮算法我已經(jīng)不需要了,我已經(jīng)成功模擬實現(xiàn)了max的關(guān)鍵幀的差值算法,通過這個把星期的分析把曲線上的數(shù)據(jù)統(tǒng)統(tǒng)解出來而且吃透了,通過我的計算,目前是LINEAR這種方式,任意時間得到的各頂點的位置和max的一模一樣,這樣下來根本不用去做什么幀壓縮了,max的動畫曲線上有多少個節(jié)點我就導(dǎo)出多少個數(shù)據(jù)(導(dǎo)出的數(shù)據(jù)超乎想象的少,打個比方兩點是一條直線,這條直線多長,我不用關(guān)心,因為直線上的任意一點我能計算出來,我只需要這兩個關(guān)鍵點就好了,這才是真正的“關(guān)鍵幀”數(shù)據(jù)插值計算?。?,而且這其中的意義不光在幀數(shù)據(jù)的剔除(過去的認(rèn)識很膚淺,骨骼動畫不能光是“播放”的),不同動畫集動作之間的自動融合的問題用固定的幀數(shù)據(jù)去播放是無法解決的,想象一下一個動作沒播完,而打斷去播放下一個動作,這兩個動作怎么去自動連貫起來呢?如果要做到自動連貫起來,那么過渡幀的數(shù)據(jù)是要用通過關(guān)鍵幀插值來融合計算的,這能大大豐富動作的連貫和動作組合的復(fù)雜度,通過研究和實現(xiàn)max的插值算法以后正好能很好的解決這個問題

            我導(dǎo)出的數(shù)據(jù)僅僅只有各骨骼關(guān)鍵幀的旋轉(zhuǎn)四元數(shù)(連位移都不需要,我發(fā)現(xiàn)骨骼的移動全部是上層旋轉(zhuǎn)帶動的,如果不考慮縮放,那么也骨骼根本沒有自身的位移量,看曲線一目了然的),另外還有蒙皮姿勢的各骨骼初始位置,和蒙皮姿勢的Mesh各頂點,另外還有材質(zhì)等數(shù)據(jù),這些數(shù)據(jù)足以

            目前在這個基礎(chǔ)之上還實現(xiàn)了換裝,也就是更換蒙皮骨骼部件的功能,主蒙皮的骨架計算影響到次級副部件的子骨架這樣的功能,很快我差不多能實現(xiàn)蒙皮部件換裝,批量繪制,物件綁定插槽編輯,動作標(biāo)簽編輯,動作融合設(shè)置,等等...一個復(fù)雜的蒙皮配置系統(tǒng)了,應(yīng)該會比OGRE那套復(fù)雜的多
            2010-10-27 21:44 | 李侃
            久久久久久免费一区二区三区| 四虎影视久久久免费| 蜜臀av性久久久久蜜臀aⅴ| 久久夜色精品国产欧美乱| 国产99久久精品一区二区| 国产农村妇女毛片精品久久 | 国产亚洲美女精品久久久2020| 久久精品国产AV一区二区三区| 精品无码久久久久国产| 久久亚洲欧洲国产综合| 国产精品美女久久久久| 亚洲综合久久夜AV | 久久久久综合网久久| 久久精品人人做人人爽电影| 国产午夜精品久久久久九九电影 | 精品无码久久久久久尤物| 久久人人爽人人爽人人片AV麻豆 | 四虎亚洲国产成人久久精品| 国内精品久久久久伊人av| 无码人妻久久一区二区三区蜜桃 | 精品久久久久久| 久久久久亚洲AV无码永不| 久久人人爽人人人人爽AV| 色综合久久88色综合天天| MM131亚洲国产美女久久| 777午夜精品久久av蜜臀| 久久国产亚洲精品| 久久久亚洲精品蜜桃臀| 精品免费久久久久国产一区 | 精品国产91久久久久久久a| 久久99精品国产99久久| 精品久久8x国产免费观看| 亚洲国产精品无码久久久不卡| 久久乐国产综合亚洲精品| 国产午夜精品理论片久久| 久久噜噜久久久精品66| 日本高清无卡码一区二区久久| 日韩精品无码久久一区二区三| 婷婷久久综合九色综合绿巨人| 久久97久久97精品免视看秋霞| 久久久久亚洲av毛片大|