Unity 人物捏臉的實(shí)現(xiàn)

(圖片來(lái)自于網(wǎng)絡(luò)蟋恬,如有侵權(quán)涤久,請(qǐng)聯(lián)系作者刪除)

捏臉:

不知什么時(shí)候涡尘,捏臉幾乎已經(jīng)成為游戲的標(biāo)配,用戶通過捏臉系統(tǒng)創(chuàng)建出個(gè)性化角色响迂,彰顯獨(dú)特的形象考抄,增強(qiáng)了沉浸感。從技術(shù)角度講蔗彤,捏臉也有很多種實(shí)現(xiàn)方式川梅,比如:

  • BlendShape
  • 骨骼捏臉
  • AI智能捏臉

BlendShape可以很好的表現(xiàn)不同的面部表情,但是制作工作量大然遏,一般CG,影視等應(yīng)用較多贫途。
AI智能捏臉現(xiàn)在發(fā)展勢(shì)頭很猛,通過對(duì)輸入的圖片做AI智能圖像識(shí)別啦鸣,提取出面部特征潮饱,再根據(jù)特征定制角色Mesh,從而快速完美的還原現(xiàn)實(shí)世界中的人物形象来氧。具體實(shí)現(xiàn)可以參考 面由 AI 生|虛擬偶像“捏臉”技術(shù)解析

骨骼捏臉

今天我們主要討論骨骼捏臉以及在Unity中的實(shí)現(xiàn)诫给。說(shuō)到骨骼捏臉,就要先了解一下骨骼啦扬,骨骼動(dòng)畫中狂,蒙皮Mesh,權(quán)重這些名詞。這里假設(shè)讀者已經(jīng)知道這些概念扑毡,從而可以盡快進(jìn)入主題胃榕。
骨骼捏臉也有很多實(shí)現(xiàn)方式,比如:

  • 每幀動(dòng)畫后瞄摊,給骨骼一個(gè)變化補(bǔ)償值勋又。
  • 給骨骼掛載一個(gè)不參與動(dòng)畫的子骨骼苦掘,Mesh的權(quán)重僅受這些子骨骼影響。
  • 修改骨骼的Bindposes實(shí)現(xiàn)楔壤。

第一種 是先計(jì)算好捏臉骨骼和原始骨骼的差異值鹤啡,然后在每幀動(dòng)畫后,再給每根骨骼應(yīng)用一次這個(gè)差異值蹲嚣,比如捏臉的時(shí)候縮小了臉部骨骼0.5倍递瑰。 當(dāng)骨骼動(dòng)畫的每幀完成后,動(dòng)畫數(shù)據(jù)會(huì)把臉部骨骼設(shè)置回原本的值隙畜,這時(shí)候在LateUpdate中抖部,把臉部骨骼再縮小0.5倍,從而達(dá)到還原捏臉效果的目的议惰。

優(yōu)點(diǎn): 實(shí)現(xiàn)簡(jiǎn)單
缺點(diǎn): 由于每幀都要做額外的補(bǔ)償計(jì)算慎颗,所以效率不高。

第二種 由于把影響蒙皮的骨骼和驅(qū)動(dòng)動(dòng)畫的骨骼進(jìn)行了分離言询,動(dòng)畫數(shù)據(jù)不直接改變蒙皮骨骼的數(shù)據(jù)哗总,捏臉時(shí)指定的變化,不會(huì)被動(dòng)畫數(shù)據(jù)覆蓋倍试,所以讯屈,只要調(diào)整蒙皮骨骼就能達(dá)到捏臉效果。

優(yōu)點(diǎn): 實(shí)現(xiàn)更簡(jiǎn)單
缺點(diǎn): 增加了額外的骨骼县习,影響效率
參與動(dòng)畫的骨骼不能進(jìn)行調(diào)整(會(huì)被骨骼動(dòng)畫還原)涮母,從而限制了捏臉的自由度,對(duì)捏臉效果產(chǎn)生不利影響躁愿。

第三種: 修改骨骼Bindposes達(dá)到捏臉效果叛本。

優(yōu)點(diǎn): 只計(jì)算一次,效率高彤钟。
缺點(diǎn): 實(shí)現(xiàn)稍微復(fù)雜来候,需要良好的數(shù)學(xué)基礎(chǔ)。

由于第三種優(yōu)點(diǎn)最為突出逸雹,我們今天就來(lái)討論它是如何實(shí)現(xiàn)的营搅。

Bindpose:

首先說(shuō)一下對(duì)骨骼的簡(jiǎn)單理解:

  • 骨骼就是一系列具有父子關(guān)系的Transform。由于骨骼具有父子關(guān)系梆砸,所以對(duì)父骨骼的修改會(huì)影響其子骨骼转质,比如抬手臂會(huì)帶動(dòng)手指骨骼。


    骨骼示意圖(圖1)
  • 骨骼動(dòng)畫就是在關(guān)鍵幀中指定骨骼的transform帖世。 位置休蟹,旋轉(zhuǎn),縮放.。關(guān)鍵幀之間通過插值得到中間狀態(tài)赂弓,從而提高動(dòng)畫流暢度绑榴。


    骨骼動(dòng)畫(圖2)
  • 骨骼的bindpose是骨骼在TPose下,從模型空間轉(zhuǎn)到該骨骼空間的矩陣盈魁。

前兩者很好理解彭沼,最后一點(diǎn)不是那么直觀,而且是我們捏臉的關(guān)鍵备埃,所以我們?cè)敿?xì)介紹一下姓惑。
TPose是骨骼的一個(gè)初始狀態(tài),模型和骨骼的綁定就是在這個(gè)狀態(tài)下進(jìn)行的按脚,所謂的綁定于毙,就是指定模型的頂點(diǎn)受哪些骨骼影響,每個(gè)骨骼影響的權(quán)重是多少辅搬,這些一般都是美術(shù)在美術(shù)工具軟件中做好唯沮,導(dǎo)入到Unity中直接使用,我們?yōu)榱苏f(shuō)明原理堪遂,直接用一個(gè)藍(lán)色小球代表模型上的一個(gè)頂點(diǎn)介蛉。把他和骨骼BN_20綁定。權(quán)重為1溶褪。也就是它僅受BN_20影響币旧。


Mesh綁定(圖3)

現(xiàn)在這個(gè)頂點(diǎn)(藍(lán)色小球),位于BN_20上方一個(gè)單位猿妈,但是看右邊面板注意到吹菱,Mesh以及其上的Vertex和骨骼并沒有父子關(guān)系,所以現(xiàn)在變換骨骼彭则,頂點(diǎn)vertex并不會(huì)跟隨移動(dòng)鳍刷。我們需要把兩者關(guān)聯(lián)起來(lái)(綁定).
假設(shè)BN_10向右移動(dòng)了一個(gè)單位(會(huì)帶動(dòng)子骨骼BN_20一起向右),那藍(lán)色小球也應(yīng)該跟隨綁定的骨骼BN_20一起向右移動(dòng)一個(gè)單位俯抖,但是如果BN_10圍繞Z軸旋轉(zhuǎn)90度输瓜,藍(lán)色小球又該在什么位置呢?他們是怎么綁定的芬萍,有什么規(guī)律呢尤揣?
答案就是頂點(diǎn)相對(duì)于綁定的骨骼,相對(duì)位置不變担忧,也可以描述為芹缔,頂點(diǎn)在綁定骨骼的坐標(biāo)系下坯癣,坐標(biāo)不變瓶盛。這樣BN_20.transform.position.x += 1.那Vertex.transform.position += 1(世界坐標(biāo)系下). 如果BN_10繞z軸旋轉(zhuǎn)了,那BN_20的坐標(biāo)系也跟著旋轉(zhuǎn)了,但是頂點(diǎn)在該坐標(biāo)系下惩猫,位置還是不變芝硬。


骨骼旋轉(zhuǎn)后頂點(diǎn)的新位置(圖4)

上圖是理想的位置,它是怎么計(jì)算來(lái)的呢轧房?上面說(shuō)了頂點(diǎn)和骨骼相對(duì)位置不變拌阴,就是頂點(diǎn)在骨骼坐標(biāo)系下坐標(biāo)位置不變。那我們先求出在初始位置(TPose)下奶镶,頂點(diǎn)在骨骼BN_20坐標(biāo)系下的坐標(biāo)位置迟赃。直接轉(zhuǎn)換坐標(biāo)系不好轉(zhuǎn)換,但是我們可以先把vertext坐標(biāo)轉(zhuǎn)換到世界空間厂镇,然后再由世界空間轉(zhuǎn)換到BN_20空間纤壁。


坐標(biāo)轉(zhuǎn)換公式(圖5)

對(duì)應(yīng)代碼如下:
Vector3 vertexPositionInBone_20 = (BN_20.transform.worldToLocalMatrix * mesh.transform.localToWorldMatrix).MultiplyPoint3x4(vertext.transform.localPosition);

好了,現(xiàn)在頂點(diǎn)相對(duì)于綁定骨骼的相對(duì)位置已經(jīng)計(jì)算出來(lái)了捺信,接下來(lái)就保證骨骼在各種變換后酌媒,頂點(diǎn)的vertextPositionInBone不變就好了。反過來(lái)想迄靠,假設(shè)BN_20變換到了新的位置秒咨,只要把BN_20本地坐標(biāo)下的vertexPositionInBone轉(zhuǎn)換到世界坐標(biāo)系下得到位置A,然后掌挚,把vertext的世界坐標(biāo)移動(dòng)到A,就能保障相對(duì)位置不變了雨席。


新位置轉(zhuǎn)換公式(圖6)

對(duì)應(yīng)代碼如下:

vertext.transform.position = bone.transform.localToWorldMatrix.MultiplyPoint3x4(vertexPositionInBone_20);

這里要明確一點(diǎn),圖5中bone的矩陣是TPose時(shí)的矩陣吠式,這個(gè)矩陣是固定不變的舅世,圖6中是骨骼變換后的矩陣,這個(gè)矩陣隨著骨骼的變換而不斷變化奇徒,從而驅(qū)動(dòng)頂點(diǎn)不斷變化雏亚。

說(shuō)到這里基本把骨骼動(dòng)畫,蒙皮摩钙,權(quán)重(特例只受一根骨骼影響)的原理講清楚了罢低,但是讀者還是不明白什么是Bindposes. 其實(shí)圖5中最后一行,用括號(hào)括起來(lái)的部分就是骨骼的Bindpose, 很意外吧胖笛,原來(lái)早就認(rèn)識(shí)了网持,只是不知道對(duì)方的名字,它的數(shù)學(xué)意義是 在TPose下长踊,從模型的本地空間到骨骼的本地空間的轉(zhuǎn)換矩陣功舀,這里面每個(gè)詞都是有意義的,首先一定是骨骼的初始狀態(tài)(TPose下), 再就是從一個(gè)坐標(biāo)系身弊,到另一個(gè)坐標(biāo)系的轉(zhuǎn)換矩陣.

捏臉:
好了辟汰,啰里啰嗦一大堆列敲,終于知道bindpose是啥了,那它和捏臉有什么關(guān)系呢帖汞?下面我們來(lái)說(shuō)明戴而,假設(shè)vertext是鼻子上的一個(gè)頂點(diǎn),現(xiàn)在TPose下翩蘸,這個(gè)頂點(diǎn)離骨骼BN_20一個(gè)單位遠(yuǎn)所意,如果我在TPose下,把vertex再多遠(yuǎn)離BN_20一個(gè)單位催首,會(huì)發(fā)生什么呢扶踊?發(fā)現(xiàn)鼻子變高了,vertext在BN_20坐標(biāo)系下郎任,坐標(biāo)位置由(0,1,0)變成(0,2,0)了姻檀。 按照上面說(shuō)的骨骼動(dòng)畫的原理,骨骼動(dòng)起來(lái)涝滴,頂點(diǎn)相對(duì)于骨骼坐標(biāo)位置不變绣版,所以骨骼動(dòng)畫動(dòng)的過程中,vertex相對(duì)于BN_20始終位于(0,2,0)的位置歼疮,這樣杂抽,角色怎么動(dòng),鼻子始終是高的韩脏。這就是捏臉?biāo)枨蟮乃豸铩O旅婵纯淳唧w計(jì)算過程:

移動(dòng)頂點(diǎn),得到新的坐標(biāo)(圖7)

圖7中括號(hào)里面赡矢,就是骨骼新的bindpose.只要用這個(gè)bindpose,頂點(diǎn)就相對(duì)于骨骼始終在新的相對(duì)位置杭朱。
給Mesh賦值新的bindpose的代碼如下:

//Dictionary<string,Matrix4x4> newBindposes; //這里面存儲(chǔ)根據(jù)用戶捏臉后計(jì)算出的新的Bindpose

SkinnedMeshRenderer smr = face.GetComponent<SkinnedMeshRenderer>();

//實(shí)例化一份新的mesh
Mesh mesh = GameObject.Instantiate<Mesh>(smr.sharedMesh);

Matrix4x4[] bindposes = mesh.bindposes;
Transform[] bones = smr.bones;
for (int i = 0; i < bones.Length; ++i)
{
    bindposes[i] = newBindposes[bones[i].name];
}

mesh.bindposes = bindposes;
smr.sharedMesh = mesh;

OK,現(xiàn)在看看捏臉后的效果吧:

原來(lái)的眼睛
捏過的小眼睛

今天就講到這里吹散,這里面還有一個(gè)難點(diǎn)就是如何計(jì)算用戶捏臉后骨骼的差異矩陣弧械,就是圖7中的
差異矩陣

感興趣的朋友可以自行推導(dǎo)一下,你會(huì)發(fā)現(xiàn)其中數(shù)學(xué)的樂趣空民。如果疑惑較多刃唐,我可以單開一章用來(lái)說(shuō)明這塊。
另外吐槽一下界轩,Unity資源商店竟然沒有找到輕量級(jí)捏臉的插件画饥,是我的搜索方式不對(duì)嗎?有知道的推薦個(gè)好用的給我唄浊猾,先行謝過抖甘!
【轉(zhuǎn)載請(qǐng)注明出處】

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市葫慎,隨后出現(xiàn)的幾起案子衔彻,更是在濱河造成了極大的恐慌薇宠,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件米奸,死亡現(xiàn)場(chǎng)離奇詭異昼接,居然都是意外死亡爽篷,警方通過查閱死者的電腦和手機(jī)悴晰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)逐工,“玉大人铡溪,你說(shuō)我怎么就攤上這事±岷埃” “怎么了棕硫?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)袒啼。 經(jīng)常有香客問我哈扮,道長(zhǎng),這世上最難降的妖魔是什么蚓再? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任滑肉,我火速辦了婚禮,結(jié)果婚禮上摘仅,老公的妹妹穿的比我還像新娘靶庙。我一直安慰自己,他們只是感情好娃属,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布六荒。 她就那樣靜靜地躺著,像睡著了一般矾端。 火紅的嫁衣襯著肌膚如雪掏击。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天秩铆,我揣著相機(jī)與錄音铐料,去河邊找鬼。 笑死豺旬,一個(gè)胖子當(dāng)著我的面吹牛钠惩,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播族阅,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼篓跛,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了坦刀?” 一聲冷哼從身側(cè)響起愧沟,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤蔬咬,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后沐寺,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體林艘,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年混坞,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了狐援。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡究孕,死狀恐怖啥酱,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情厨诸,我是刑警寧澤镶殷,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站微酬,受9級(jí)特大地震影響绘趋,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜颗管,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一陷遮、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧忙上,春花似錦拷呆、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至梗逮,卻和暖如春项秉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背慷彤。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工娄蔼, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人底哗。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓岁诉,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親跋选。 傳聞我的和親對(duì)象是個(gè)殘疾皇子涕癣,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容