φυβλαςのβλογ
บล็อกของ phyblas



maya python เบื้องต้น บทที่ ๔๑: การทำเบลนด์เชป
เขียนเมื่อ 2017/03/16 09:58
แก้ไขล่าสุด 2021/09/28 16:42
ใบบทที่ ๑๒-๑๕ ได้พูดถึงการทำภาพเคลื่อนไหวไปแล้ว แต่ภาพเคลื่อนไหวในนั้นเกิดจากการเปลี่ยนแปลงคุณสมบัติโดยรวมต่างๆ เช่น ตำแหน่ง มุมหมุน หรือมาตราส่วนของทั้งวัตถุไปเรื่อยๆตามเวลา

แต่หากต้องการความเคลื่อนไหวเปลี่ยนแปลงในระดับย่อยกว่านั้น คือเปลี่ยนรูปร่างของวัตถุด้วยการขยับจุดบนโพลิกอน กรณีแบบนั้นก็อาจทำได้โดยการตั้งคีย์เฟรมหรือสร้างเอ็กซ์เพรชชันให้กับทีละจุด เพียงแต่ว่าการทำแบบนั้นยุ่งยาก เพราะวัตถุหนึ่งประกอบด้วยจุดมากมายนับพันนับหมื่น ดังนั้นโดยทั่วไปแทนที่จะตั้งคีย์เฟรมหรือใส่เอ็กซ์เพรชชันให้กับทีละจุดโดยตรง จะใช้วิธีที่เรียกว่า เบลนด์เชป (blend shape)

เบลนด์เชป คือการเอาวัตถุสองชิ้นที่คล้ายกันมาผสมกัน โดยรูปร่างของวัตถุจากการผสมจะขึ้นกับสัดส่วนของวัตถุสองชิ้นที่ใช้ การปรับค่าสัดส่วนจะทำให้รูปร่างเปลี่ยนไป

วัตถุสองชิ้นที่ว่านั้นแบ่งออกเป็นวัตถุต้นแบบกับวัตถุที่แปลงรูปแล้ว วัตถุต้นแบบคือรูปร่างตั้งต้นตอนที่ยังไม่มีการเปลี่ยนแปลงใดๆ ส่วนวัตถุที่แปลงแล้วก็คือรูปร่างที่ต้องการให้เปลี่ยนแปลงไปเป็น

ซึ่งโดยทั่วไปแล้วเราจะเริ่มจากการสร้างวัตถุต้นแบบขึ้นมาก่อน ส่วนวัตถุที่ทำการแปลงรูปแล้วจะได้จากการสำเนา (ใช้ duplicate) จากวัตถุต้นแบบ แล้วก็เอามาขยับแปลงรูปให้เป็นตามที่ต้องการเอา



ยกตัวอย่าง สมมุติว่าเราต้องการสร้างปลาปักเป้าขึ้นมาสักตัว อยากให้มันสามารถงอกหนามและหุบหนามได้

เริ่มแรกสร้างตัวปลาปักเป้าขึ้นมา เอาแบบง่ายๆโดยใช้เป็นทรงกลมแล้วใช้จินตนาการทึกทักเอาว่ามันเป็นปลาปักเป้า พร้อมกับสร้างลูกตาให้มันไปด้วย
mc.polySphere(r=10,sx=32,sy=16,n='pakkapao')
mc.polySphere(r=1.2,n='tasai')
mc.move(10,0,-4)
mc.polySphere(r=1.2,n='takhwa')
mc.move(10,0,4)



(อนึ่ง ในตัวอย่างจะเห็นสีสันสวยงาม เพราะยังคงใช้แสงไฟที่สร้างขึ้นต่อเนื่องมาจากบทที่ ๓๙ เนื่องจากเห็นว่าสวยดี ถ้าไม่มีแสงก็ควรจะเห็นเป็นสีเทาธรรมดา)

จากนั้นก็ทำให้ยื่นหนามเฉพาะด้านบนได้ด้วยโค้ดนี้
mc.scale(2,2,2,['pakkapao.vtx[%d]'%(i+j-(i/64)%2) for i in range(257,450,64) for j in range(0,32,2)])



จะเห็นว่าได้ปักเป้าที่มีร่างครึ่งบนกลายเป็นหนามแทงออกมาสวยงาม อย่างไรก็ตามในการทำเบลนด์เชปเราต้องใช้ทั้งวัตถุต้นฉบับและวัตถุที่แปลงแล้ว ดังนั้นต้องเก็บวัตถุเดิมก่อนเปลี่ยนแปลงไว้ด้วย ดังนั้นจึงต้องทำสำเนาก่อน แล้วค่อยมาแก้ไขที่ตัวสำเนาอันนั้น

ให้ลบแล้วสร้างใหม่ตามนี้ (ขอละส่วนลูกตาทิ้งไปก่อน ไม่สำคัญ)
mc.polySphere(r=10,sx=32,sy=16,n='pakkapao')
mc.duplicate('pakkapao',n='nambon')
mc.scale(2,2,2,['nambon.vtx[%d]'%(i+j-(i/64)%2) for i in range(257,450,64) for j in range(0,32,2)])



ก็จะได้วัตถุต้นแบบชื่อ pakkapao กับวัตถุที่แปลงรูปแล้ว ชื่อ nambon (ในที่นี้จับวัตถุที่แปลงแล้วเลื่อนห่างไปให้ไม่ซ้อนกัน การทำแบบนี้เพื่อให้เห็นภาพชัดแต่ไม่มีผลอะไรกับการทำเบลนด์เชป)

จากนั้นก็ทำการสร้างให้วัตถุ nambon กลายเป็นเบลนด์เชปของ pakkapao โดยคำสั่งที่ใช้ทำก็คือ mc.blendShape() โดยให้ใส่ชื่อของวัตถุที่แปลงแล้วเป็นอันแรก ตามด้วยวัตถุที่เป็นต้นแบบ

ดังนั้นก็คือใส่ตามนี้ โดยแฟล็ก n คือชื่อโหนดเบลนด์เชปที่ต้องการ สามารถใส่เพิ่มเพื่อตั้งชื่อตามที่ต้องการได้
mc.blendShape('nambon','pakkapao',n='bs_pakkapao')

เพียงเท่านี้เบลนด์เชปการงอกหนามด้านบนของปักเป้าก็จะถูกสร้างขึ้นมา

จากนั้นตัววัตถุ nambon ก็จะไม่จำเป็นต้องใช้แล้ว สามารถลบทิ้งไปได้เลย หรือถ้าหากคิดว่าอาจต้องแก้ภายหลังอีกก็เก็บไว้ก็ได้ แค่ซ่อนไว้ไม่ให้เห็นหรือเอาไปวางไว้ห่างๆจะได้ไม่ซ้อนทับกันก็พอ

การควบคุมเบลนด์เชปทำได้โดยเปิดหน้าต่างเบลนด์เชปขึ้นมาดู
> Windows (ウィンドウ) > Animation Editor (アニメーション エディタ) > Blend Shape (ブレンド シェイプ)



จากนั้นเลื่อนแถบขึ้นลงก็จะเห็นความเปลี่ยนแปลง กรณีนี้คือหนามยืดยาวและหดสั้น ขึ้นกับค่าสัดส่วนน้ำหนัก ซึ่งจะมีค่าระหว่าง 0 ถึง 1 โดย 0 คือเหมือนต้นฉบับ และ 1 คือเหมือนรูปร่างของวัตถุที่แปลงแล้ว



หากเราแค่เลื่อนตามลูกเลื่อนนี้ก็จะปรับค่าได้แค่ในระหว่าง 0 กับ 1 แต่จริงๆจะพิมพ์ปรับค่าให้สูงกว่า 1 ก็ได้ ผลที่ได้ก็จะเป็นว่าวัตถุถูกแปลงรูปไปเลยยิ่งกว่า นั่นคือหนามงอกยาวกว่าเดิมไปอีก

เช่นลองปรับเป็น 2 ก็จะได้แบบนี้



หรือถ้าใส่ค่าติดลบก็จะได้การเปลี่ยนแปลงในทิศตรงกันข้าม นั่นคือหนามหุบเข้าด้านในแทน เช่นลอง -0.8 ได้แบบนี้



การปรับค่าน้ำหนักโดยใช้โค้ดก็อาจทำได้โดยใช้ฟังก์ชัน blendShape() อันเดิมนี้เอง โดยใส่แฟล็ก e (edit) ลงไปเป็น e=1 เพื่อแสดงว่าเราต้องการแก้ไขค่าของเบลนด์เชป จากนั้นก็ตามด้วยแฟล็ก w (weight) ซึ่งกำหนดน้ำหนักที่ต้องการใส่

โดยค่าที่ต้องใส่ในแฟล็ก w นั้นเป็นลิสต์ของทูเพิลคู่ของดัชนีลำดับของรูปแบบและน้ำหนักที่ต้องการปรับ

ในที่นี้เราเพิ่งสร้างไปแค่รูปแบบเดียว w=[(0,น้ำหนัก)]

ตัวอย่างเช่น
mc.blendShape('bs_pakkapao',e=1,w=[(0,0.5)])

ในที่นี้เรามีรูปแบบแปลงแค่อันเดียวจึงใส่ทูเพิลแค่อันเดียวลงในลิสต์ แต่จะสามารถปรับน้ำหนักของหลายรูปแบบในคราวเดียวก็ได้ โดยใส่ทูเพิลดัชนีและค่าเพิ่มลงไปในลิสต์ตามที่ต้องการ

นอกจากนี้ยังมีอีกวิธีก็คือจะแค่ใช้ mc.setAttr() ก็ได้เช่นกัน แต่จะปรับได้ทีละค่า เช่น พิมพ์แบบนี้จะได้ผลแบบเดียวกัน
mc.setAttr('bs_pakkapao.nambon',0.5)

หากจะตั้งคีย์เฟรมเพื่อทำภาพเคลื่อนไหวก็ทำได้ เช่นถ้าต้องการให้หนามค่อยๆยื่นออกภายในเวลา ๕๐ เฟรม
mc.setKeyframe('bs_pakkapao',at='nambon',v=0,t=0)
mc.setKeyframe('bs_pakkapao',at='nambon',v=1,t=50)



วัตถุหนึ่งจะทำเบลนด์เชปสักกี่อันก็ได้ โดยตอนใช้ฟังก์ชัน blendShape() ให้ใส่ชื่อวัตถุรูปแบบที่แปลงแล้วทั้งหมดกี่อันก็ได้แล้วปิดท้ายด้วยวัตถุต้นแบบ

ดังนั้นเราอาจทำการสร้างเบลนด์เชปให้ปักเป้างอกหนามด้านล่างดูด้วย โดยลองสร้างใหม่ตั้งแต่ต้น เป็นดังนี้
mc.polySphere(r=10,sx=32,sy=16,n='pakkapao')
mc.polySphere(r=1.2,n='tasai')
mc.move(10,0,-4)
mc.polySphere(r=1.2,n='takhwa')
mc.move(10,0,4)

mc.duplicate('pakkapao',n='nambon') # งอกหนามด้านบน
mc.scale(2,2,2,['nambon.vtx[%d]'%(i+j-(i/64)%2) for i in range(257,450,64) for j in range(0,32,2)])

mc.duplicate('pakkapao',n='namlang') # งอกหนามด้านล่าง
mc.scale(2,2,2,['namlang.vtx[%d]'%(i+j-(i/64)%2) for i in range(1,241,64) for j in range(0,32,2)])

mc.blendShape('nambon','namlang','pakkapao',n='bs_pakkapao')
mc.delete('nambon','namlang') # ใช้สร้างเบลนด์เชปเสร็จก็ลบทิ้งได้

แล้วก็จะได้เบลนด์เชปที่มีรูปแบบการแปลงสองแบบ สามารถใช้พร้อมกันหรือแยกใช้ก็ได้





โดยปกติแล้วในขณะที่ค่าสัดส่วนน้ำหนักเปลี่ยนแปลงจาก 0 ไป 1 นั้นรูปร่างของวัตถุก็แค่เปลี่ยนผ่านจากวัตถุต้นแบบไปเป็นวัตถุที่แปลงรูปแล้ว โดยการเปลี่ยนแปลงนั้นเป็นไปในทิศทางที่แน่นอนจนจบ แต่ก็มีวิธีที่จะทำให้สามารถกำหนดรูปร่างระหว่างทางได้

เช่น สมมุติว่าการงอกหนามของปักเป้าตัวนี้ควรจะเริ่มจากงอกในแนวนอนก่อนค่อนพุ่งขึ้นในแนวตั้ง เราอาจสร้างรูปร่างระหว่างทาง

การกำหนดรูปร่างระหว่างกลางทำได้โดยใช้แฟล็ก ib (inBetween) ใส่เป็น ib=1 แล้วก็กำหนดค่าด้วยแฟล็ก t (target)

ค่าที่ใส่ในแฟล็ก t คือทูเพิลที่มีสมาชิก ๔ ตัว คือ
(ชื่อวัตถุต้นแบบ
,ดัชนีลำดับของรูปแบบที่ต้องการแทรก
,ชื่อวัตถุที่จะใช้เป็นรูปร่างแทรกกลาง
,ค่าน้ำหนักตรงจุดที่ต้องการแทรก)

เช่น ลองปรับแก้เพิ่มเติมโดยกำหนดรูปแบบในขณะที่น้ำหนักเป็น 0.5 ดังนี้
mc.blendShape('bs_pakkapao',e=1,w=[(0,0),(1,0)]) # ปรับน้ำหนักกลับเป็น 0 ก่อน
mc.duplicate('pakkapao',n='nambon0')
mc.scale(2,1,2,['nambon0.vtx[%d]'%(i+j-(i/64)%2) for i in range(257,450,64) for j in range(0,32,2)])
mc.blendShape('bs_pakkapao',e=1,ib=1,t=('pakkapao',0,'nambon0',0.5))
mc.delete('nambon0')

ผลที่ได้ก็จะเป็นแบบนี้ หนามจะยื่นในแนวนอนก่อนค่อยพุ่งขึ้น





นอกจากนี้ฟังก์ชัน blendShape หากใส่แฟล็ก q=1 ยังใช้ในการดูข้อมูลต่างๆของเบลนด์เชปนั้นๆได้ด้วย โดยใส่แฟล็กที่เกี่ยวข้อง =1 ตามมา

เช่น q=1,w=1 จะได้ลิสต์ของค่าน้ำหนักของแต่ละรูปแบบ เรียงตามลำดับ
และ q=1,g=1 จะได้ชื่อโหนดรูปร่างที่โหนดเบลนด์เชปตัวนั้นคุมอยู่
print(mc.blendShape('bs_pakkapao',q=1,w=1)) # ได้ [0.0, 0.0]
print(mc.blendShape('bs_pakkapao',q=1,g=1)) # [u'pakkapaoShape']

หากต้องการได้ชื่อของรูปแบบการเบลด์เชปก็อาจใช้ listAttr()
print(mc.listAttr('bs_pakkapao.w',m=1)) # ได้ [u'nambon', u'namlang']

หรือใช้ฟังก์ชัน aliasAttr() ก็ได้เช่นกัน
print(mc.aliasAttr('bs_pakkapao',q=1)) # ได้ [u'nambon', u'weight[0]', u'namlang', u'weight[1]']

ที่จริงแล้วฟังก์ชัน aliasAttr() มีไว้ใช้ตั้งชื่อเล่นให้กับค่าองค์ประกอบ แต่ถ้าใช้ q=1 จะคืนค่าชื่อเล่นขององค์ประกอบที่ตั้งขึ้น คู่กับชื่อจริงขององค์ประกอบนั้น

ในที่นี้จะเห็นได้ว่า nambon เป็นชื่อเล่นของ weight[0] ส่วน namlang เป็นชื่อเล่นของ weight[1] และถ้าหากใส่รูปแบบเพิ่มเติมไปอีกก็จะเป็น weight[2]

ปกติเวลาที่สร้างเบลนด์เชปขึ้นมา ค่าน้ำหนักของแต่ละรูปแบบจะถูกเก็บอยู่ในแอตทริบิวต์ .weight (หรือย่อเป็น .w) ซึ่งเป็นลิสต์มีหลายตัว แต่พร้อมกันนั้นก็จะถูกตั้งชื่อเล่นเป็นชื่อเดิมของวัตถุที่ถูกใช้เป็นต้นแบบไปด้วย ดังนั้นเราจึงสามารถใช้ getAttr เพื่อหาค่าน้ำหนักได้โดยใช้ชื่อเล่นนั้นเป็นชื่อค่าองค์ประกอบ
print(mc.getAttr('bs_pakkapao.nambon'))

ฟังก์ชัน aliasAttr ไม่ได้ใช้แค่กับเบลนด์เชป แต่อาจใช้ตั้งชื่อเล่นให้กับองค์ประกอบอะไรของโหนดชนิดไหนก็ได้ เช่น ลองตั้งชื่อให้มุมหมุนทิศ z ของปลาปักเป้าเป็น "มุมเงย"
mc.aliasAttr('pakkapao',q=1) # ได้ []   (เริ่มต้นมายังไม่ได้มีการตั้งชื่อเล่นใดๆ)
mc.aliasAttr('mum_ngoei','pakkapao.rz')
mc.setAttr('pakkapao.mum_ngoei',30) # ปรับค่า rz โดยผ่านชื่อที่ตั้งใหม่นี้ได้เลย
mc.aliasAttr('pakkapao',q=1) # ได้ [u'mum_ngoei', u'rotateZ']



ทั้งหมดนี้เป็นแค่เบื้องต้นของการทำเบลนด์เชปเท่านั้น นอกจากนี้แล้วฟังก์ชัน blendShape() ยังปรับแต่งอะไรได้เพิ่มเติมอีกหลายอย่าง สามารถลองไปศึกษากันต่อได้



อ้างอิง

<< บทที่แล้ว
     บทถัดไป >>
หน้าสารบัญ


-----------------------------------------

囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧

ดูสถิติของหน้านี้

หมวดหมู่

-- คอมพิวเตอร์ >> 3D >> maya
-- คอมพิวเตอร์ >> เขียนโปรแกรม >> python >> mayapython

ไม่อนุญาตให้นำเนื้อหาของบทความไปลงที่อื่นโดยไม่ได้ขออนุญาตโดยเด็ดขาด หากต้องการนำบางส่วนไปลงสามารถทำได้โดยต้องไม่ใช่การก๊อปแปะแต่ให้เปลี่ยนคำพูดเป็นของตัวเอง หรือไม่ก็เขียนในลักษณะการยกข้อความอ้างอิง และไม่ว่ากรณีไหนก็ตาม ต้องให้เครดิตพร้อมใส่ลิงก์ของทุกบทความที่มีการใช้เนื้อหาเสมอ

สารบัญ

รวมคำแปลวลีเด็ดจากญี่ปุ่น
มอดูลต่างๆ
-- numpy
-- matplotlib

-- pandas
-- manim
-- opencv
-- pyqt
-- pytorch
การเรียนรู้ของเครื่อง
-- โครงข่าย
     ประสาทเทียม
ภาษา javascript
ภาษา mongol
ภาษาศาสตร์
maya
ความน่าจะเป็น
บันทึกในญี่ปุ่น
บันทึกในจีน
-- บันทึกในปักกิ่ง
-- บันทึกในฮ่องกง
-- บันทึกในมาเก๊า
บันทึกในไต้หวัน
บันทึกในยุโรปเหนือ
บันทึกในประเทศอื่นๆ
qiita
บทความอื่นๆ

บทความแบ่งตามหมวด



ติดตามอัปเดตของบล็อกได้ที่แฟนเพจ

  ค้นหาบทความ

  บทความแนะนำ

ตัวอักษรกรีกและเปรียบเทียบการใช้งานในภาษากรีกโบราณและกรีกสมัยใหม่
ที่มาของอักษรไทยและความเกี่ยวพันกับอักษรอื่นๆในตระกูลอักษรพราหมี
การสร้างแบบจำลองสามมิติเป็นไฟล์ .obj วิธีการอย่างง่ายที่ไม่ว่าใครก็ลองทำได้ทันที
รวมรายชื่อนักร้องเพลงกวางตุ้ง
ภาษาจีนแบ่งเป็นสำเนียงอะไรบ้าง มีความแตกต่างกันมากแค่ไหน
ทำความเข้าใจระบอบประชาธิปไตยจากประวัติศาสตร์ความเป็นมา
เรียนรู้วิธีการใช้ regular expression (regex)
การใช้ unix shell เบื้องต้น ใน linux และ mac
g ในภาษาญี่ปุ่นออกเสียง "ก" หรือ "ง" กันแน่
ทำความรู้จักกับปัญญาประดิษฐ์และการเรียนรู้ของเครื่อง
ค้นพบระบบดาวเคราะห์ ๘ ดวง เบื้องหลังความสำเร็จคือปัญญาประดิษฐ์ (AI)
หอดูดาวโบราณปักกิ่ง ตอนที่ ๑: แท่นสังเกตการณ์และสวนดอกไม้
พิพิธภัณฑ์สถาปัตยกรรมโบราณปักกิ่ง
เที่ยวเมืองตานตง ล่องเรือในน่านน้ำเกาหลีเหนือ
ตระเวนเที่ยวตามรอยฉากของอนิเมะในญี่ปุ่น
เที่ยวชมหอดูดาวที่ฐานสังเกตการณ์ซิงหลง
ทำไมจึงไม่ควรเขียนวรรณยุกต์เวลาทับศัพท์ภาษาต่างประเทศ

ไทย

日本語

中文