【Maya Workshop Rigging The Jaw with Python in Maya】筆記

【Maya Workshop Rigging The Jaw with Python in Maya】筆記

 






這是一個在Gnomon發行的教學,內容是有關如何使用Python來撰寫Jaw Rig,主要功能包含嘴唇控制器以及拉鍊效果,雖然只有嘴巴部分,但是整體教學還滿受用的!


有興趣的人可以來這。




有關Jaw的額外位移


這是一個可以在下顎張開時,調整旋轉幅度的用法,透過幾個remap Value節點就可以達到,滿好用的,尤其是如果jaw joint位置沒放好或是動畫師想要調整預設的張嘴幅度時都很好用!



首先,先在jaw ctrl上新增兩個attr,待會要用來調整Y、Z位移。




接著,使用底下的連接方式,要注意的是,將最終數值輸出到jaw ctrl的上層group,它的原理是,我們剛剛新增的attr會決定,下顎可以位移的幅度(當控制器啟動時,這邊啟動的軸向依照你的控制器來決定,不一定是X),那麼,當動畫師調整這個數值時,下顎就可以進行位移,而且,只要控制器被歸零,那麼下顎也就會回復到原始的狀態。









最後完成如下,可以看到,只有當jaw ctrl被啟動時,新增的attr數值才能影響位移!!









Zip拉鍊做法


這應該是這個教學最有趣的部分,就是將上下嘴唇加上拉鍊閉合的功能,而且是從左右兩邊都能夠啟動這個function。





首先,需要準備好lip jnt,以及seal用的grp。






然後,將兩邊corner jnt constraint到所有seal_grp上,這麼做是為了記錄嘴唇拉鍊的位置!

這邊需要設定corner jnt對每個seal_grp的數值,越靠近某一邊corner,數值就會越高,反之亦然。

總之,兩個相加起來必須要是1,這樣才能維持正常的運作!






接下來,要處理lip_jnt的部分。

可以看到每個lip jnt都分別被『C_upper』、『LorR_Corner』、『seal_grp』給constraint,也就是說,在拉鏈功能沒有啟動時,lip jnt就只會受到『C_upper』、『LorR_Corner』的控制,但是一旦拉鏈功能啟動了,那麼這兩個constraint數值就會歸零,然後『seal_grp』數值會變成1。








底下的圖是為了方便理解每個lip jnt對應的constraint物件,可以看到嘴唇會被分成四個象限,左上左下、右上右下,各自會被不同的物件給constraint。











在開始串連結點之前,還需要一個額外控制器來控制拉鏈功能,這邊會需要四個額外的attributes,分別是『L_seal』、『R_seal』、『L_seal_delay』、『R_seal_delay』。

『L_seal_delay』、『R_seal_delay』的預設數值會是4,這邊沒有硬性規定,只要是大於0都可以,目的是為了控制拉鍊閉合的速率。







第一個部分會用到四個節點,左右兩邊(L跟R),然後上下兩個節點的數值都是一樣的,所以就不多截圖了,需要注意的是,L_seal_upper_delay_DIV這個節點的input2,這個數值是經過計算得來的,公式如下:

value=len(lip_jnt)=======>也就是上排所有lip jnt的數量,而且是左右兩邊加起來。
lerp=1/float(value-1)======>最終得到的數字就是input2的數值。


另外,這個拉鏈功能是分成上下兩排來區分,所以可以看到底下的結點都包含Upper。






接著,繼續接上其他節點,這邊需要注意的是,jaw_00_L_seal_upper_delay_MULT 以及 jaw_05_R_seal_upper_delay_MULT的input1數值需要個別輸入,為什麼L是0,R會是5,這就牽扯到他們在上排lip jnt的序列號。

簡單來說,這個拉鍊lip jnt的排序是從左到右(不包含corner lip jnt),從0開始,每一個lip jnt的拉鍊節點都會包含另外一邊的數值,按照當前的例子就是L這邊會有R的節點,這些數值會在後面相加起來,如此一來才能夠達到『不管哪邊都能驅動另一邊』的功能。

更詳細的解釋看下一張圖。






承上圖,這個拉鍊結構的分配如下,每個lip jnt都會跟相對位置的lip jnt編號配對,所以才有上面的jaw_00_L_seal_upper_delay_MULT 以及 jaw_05_R_seal_upper_delay_MULT的input1數值的差異!

這邊要注意的是,目前的範例都是從左邊開始,若是要製作右邊的拉鍊,那麼這個序列號就會相反,0會出現在右邊第一個lip jnt上!











下一個階段就是,加上一個remapValue節點,並且需要接上seal數值,這個節點的用意就是在接收拉鏈功能的啟動數值,並且加上seal_Delay的數值,並且限制將out put數值限制在0到1。


另外要注意的是,這邊L remap value會輸出數值到R_seal_upper_offset_seal_upper_00_SUB,然後再接到R reamap Value節點上,這個用意是為了整合左右兩邊拉鍊的數值,也就是說,如果L這邊的數值超過一定的量之後,就會R邊也一起啟動,這樣才能做到完整的拉鍊閉合功能!












最後,將這些數值合併到seal_upper_00_SUM以及seal_upper_10_CLAMP上,並且跟其他上排lip jnt的數值連接到C_seal_upper_DRV(這是一個名為 light info的節點,主要是用來當作群組收納各個拉鍊的數值,使用add attritubtes就可以新增我們要用的接頭),最終輸出到lip jnt的constraint 節點上。











接續上面,將C_seal_upper_DRV輸出到lip jnt constraint節點上的seal節點上,並且利用reverse節點讓其他兩個constraint節點在拉鍊啟動時,數值降為0。

multipliDivide節點主要用來記錄原始的,constraint節點的數值,就是C_upper以及L_Corner的數值,這是用來控制lip jnt主要的來源,而這兩個數值會隨著lip jnt跟他們的遠近關係而不同,也就是說,越靠近L_Corner時,其數值就會越大,反之亦然!









下圖是每個lip jnt constraint的數值,可以看到上排跟下排數值的排序是一樣的,另外lip jnt的序列號跟上面zip 功能的排序不同,這是因為拉鏈功能需要左右兩邊合併一起計算數值,而lip jnt本身的序列號則不用如此!












那麼以上就是整個拉鏈功能的會用到的節點,需要注意的地方是jaw_00_(L or R)_seal_upper_delay_MULT的input1數值,因為它需要根據lip jnt在拉鏈功能排序上的序列號來決定,而且左右兩邊會有不同的起始點!



設定上來說就是每個lip jnt(不管L R)都會搭配另一邊、相對位置序列號的節點。

上下排的節點數值基本上是一樣的。







以上就差不多是拉鏈功能的節點運用,其實概念很簡單,就是需要很多remapValue來做數值轉換!






Dictionary字典功能



這是python很常見的資料結構,可以用來分類複雜的資訊,以前不太懂如何去運這個功能,這次透過這個教學有稍微了解一些。



從list中移除特定序列號物件
my_dictq = {"text1":{ 'QQ':["Hello"]}, "text2": "World"}

# 向 "text1" 的值中添加整数
my_dictq["text1"]['QQ'].append(5)

my_dictq['text1']['QQ'].pop(0)

=======># Result: [5] #Hello被移除了!


在字典中新增values用法



#想要很快在字典中新增物件,最好的方法就是將values設定成list!這樣一來就能很輕易新增或指定物件!

my_dictq = {"text1":{ 'QQ':["Hello"]}, "text2": "World"}

# 向 "text1" 的值中添加整数
my_dictq["text1"]['QQ'].append(5)


print(my_dictq)

=====>{'text2': 'World', 'text1': {'QQ': ['Hello', 5]}}


my_dictq["text1"]['QQ'][0] #可以直接指定序列號!

=====># Result: 'Hello' #

my_dictq["text1"]['QQ'][1]
======># Result: 5 #


存取字典特定的items,以及指定序列號



my_dict = {
    "person1": {
        "name": "Alice",
        "age": 30
    },
    "person2": {
        "name": "Bob",
        "age": 25
    }
}


for i in my_dict.values():#.values()只存取字典的value,而略過key
    
    for j,q in i.items():#.items()會輸出所有key跟values,在這個範例,原本的key:Person1已經被略過,
所以.items()命令只會輸出 J跌代出來出來的第一位數
        
        print(j)


#====> 
age
name
age
name




存取字典特定的values





my_dict = {
    "person1": {
        "name": "Alice",
        "age": 30
    },
    "person2": {
        "name": "Bob",
        "age": 25
    }
}

======>my_dict["person1"]

# Result: {'age': 30, 'name': 'Alice'} #







字典裡面還能有字典



QQ={"C_UP":{},'C_LO':{},
        'L_UP':{},'L_LO':{},
        'R_UP':{},'R_LO':{},
        'L_CO':{},'R_CO':{},


}

C_UPP='CUPP_Dict'
C_Ulow='C_Ulow'

QQ["C_UP"]["gg"]=[C_UPP,C_Ulow]

#字典裡面可以再有字典結構,['gg']就是在指定新的key
======>QQ["C_UP"]["gg"]

# Result: ['CUPP_Dict', 'C_Ulow']




獲取paretnConstraint的索引物件



要存取constraint節點裡面的物件可以用listAttr來辦到!

如下圖,constraint節點內的物件通常用W0、W1...來排列!




import maya.cmds as cmds

# 指定constraint名称
constraint_node = "L_jawUpper_lip_12_Ctrl_GRP3_parentConstraint1"

# 获取constraint节点的所有用户定义属性
user_defined_attrs = cmds.listAttr(constraint_node, userDefined=True) or [] user_defined_attrs[0]---->會輸出W0物件!




設定最大最小值來constraint物件



這是一個滿好用的公式,就是透過決定最大以及最小值來分配特定數量的物件某個數值,可能是translate數值、旋轉或是其他的東西!


 list=['a','b','c','d']
    CentreZ=5
    
    CornerZ=2
    
    min_z = CornerZ
    max_z = CentreZ
    
    spacing = (max_z - min_z) / (len(list) - 1)







漸變的constraint數值寫法





#讓r_corner從0開始,而l_corner則是1
#接著便會慢慢遞減,總之兩邊加起來都會是1

value = 5

for index in range(value):
    r_corner_value = float(index) / float(value - 1)
    l_corner_value = 1.0 - r_corner_value
    
    



簡化的python if寫法







guides=[loc for loc in mc.listRelatives(grp) if mc.objExists(grp)]

#也可以這樣寫

guides=list() #也可以用 guides=[] 結果是一樣的
    
    if mc.objExists(grp):
        for loc in mc.listRelatives(grp):
            guides.append(loc)









如果覺得這篇文章有幫助你在動畫之路走得更長遠,可以使用 Google 或 Facebook 帳號快速登入,按Like五下,就可以幫助我從Likecoin得到回饋,完全不用任何費用! 一點點的鼓勵都會成為我寫作的動力,感激不盡! If you found this article helpful in your journey in animation, you can use your Google or Facebook account to quickly log in and click Like five times to help me earn rewards from Likecoin, without any cost! Your encouragement means a lot to me and will become the motivation behind my writing. Thank you so much!


 




張貼留言

0 留言