φυβλαςのβλογ
บล็อกของ 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)
หอดูดาวโบราณปักกิ่ง ตอนที่ ๑: แท่นสังเกตการณ์และสวนดอกไม้
พิพิธภัณฑ์สถาปัตยกรรมโบราณปักกิ่ง
เที่ยวเมืองตานตง ล่องเรือในน่านน้ำเกาหลีเหนือ
ตระเวนเที่ยวตามรอยฉากของอนิเมะในญี่ปุ่น
เที่ยวชมหอดูดาวที่ฐานสังเกตการณ์ซิงหลง
ทำไมจึงไม่ควรเขียนวรรณยุกต์เวลาทับศัพท์ภาษาต่างประเทศ

บทความแต่ละเดือน

2024年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2023年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2022年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2021年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2020年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

ค้นบทความเก่ากว่านั้น

ไทย

日本語

中文