這是一個在Gnomon發行的教學,內容是有關如何使用Python來撰寫Jaw Rig,主要功能包含嘴唇控制器以及拉鍊效果,雖然只有嘴巴部分,但是整體教學還滿受用的!
有關Jaw的額外位移
這是一個可以在下顎張開時,調整旋轉幅度的用法,透過幾個remap Value節點就可以達到,滿好用的,尤其是如果jaw joint位置沒放好或是動畫師想要調整預設的張嘴幅度時都很好用!
首先,先在jaw ctrl上新增兩個attr,待會要用來調整Y、Z位移。
接著,使用底下的連接方式,要注意的是,將最終數值輸出到jaw ctrl的上層group,它的原理是,我們剛剛新增的attr會決定,下顎可以位移的幅度(當控制器啟動時,這邊啟動的軸向依照你的控制器來決定,不一定是X),那麼,當動畫師調整這個數值時,下顎就可以進行位移,而且,只要控制器被歸零,那麼下顎也就會回復到原始的狀態。
unitconversion數值記得調成1,不然控制器會亂跳。
最後完成如下,可以看到,只有當jaw ctrl被啟動時,新增的attr數值才能影響位移!!
Zip拉鍊做法
這應該是這個教學最有趣的部分,就是將上下嘴唇加上拉鍊閉合的功能,而且是從左右兩邊都能夠啟動這個function。
首先,需要準備好lip jnt,以及seal用的grp。
然後,將兩邊corner jnt constraint到所有seal_grp上,這麼做是為了記錄嘴唇拉鍊的位置!
總之,兩個相加起來必須要是1,這樣才能維持正常的運作!
第一個部分會用到四個節點,左右兩邊(L跟R),然後上下兩個節點的數值都是一樣的,所以就不多截圖了,需要注意的是,L_seal_upper_delay_DIV這個節點的input2,這個數值是經過計算得來的,公式如下:
接下來,要處理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都可以,目的是為了控制拉鍊閉合的速率。
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)
0 留言