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

นั่นคือจะเห็นได้ว่าภายในโปรแกรมนี้โดยหลักแล้วประกอบขึ้นจากหน่วยย่อยๆซึ่งเรียกว่าโหนด (node) ต่างๆมากมาย

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



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

เพื่อให้เห็นภาพขอเริ่มจากยกตัวอย่าง ลองสร้างวัตถุโพลิกอนขึ้นมาอันนึง
mc.polyCube()

ในที่นี้จะได้วัตถุชื่อ pCube1 เริ่มแรกดูในแอตทริบิวต์อีดิเตอร์ก็จะปรากฏ ๕ แถบ



pCube1, pCubeShape1, polyCube1, initialShadingGroup และ lambert1
(ระวังสับสน pCube1 กับ polyCube1 ชื่อคล้ายกันแต่เป็นคนละอันกัน)

ซึ่งแต่ละแถบก็แทน ๑ โหนด ซึ่งโหนดทางซ้ายสุดคือโหนดหลักของตัววัตถุ ส่วนโหนดถัดๆมาก็เป็นโหนดที่มีความเกี่ยวข้องเชื่อมโยงกับวัตถุนั้น

แต่ละโหนดนั้นยังประกอบด้วยค่าองค์ประกอบต่างๆ เช่นโหนด pCube1 กำหนดตำแหน่งและมุมหมุนของวัตถุ โหนด polyCube1 กำหนดความกว้างยาวสูงของวัตถุ โหนด lambert1 กำหนดลักษณะพื้นผิวและสี เป็นต้น

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

โหนดแบ่งออกเป็นชนิดต่างๆซึ่งมีหน้าที่แตกต่างกันไป

การจะหาว่าโหนดไหนเป็นชนิดไหนก็ทำได้โดยฟังก์ชัน nodeType()

ลองพิมพ์ตามนี้เพื่อหาชนิดของโหนดทั้งหมดที่เกี่ยวข้องกับ pCube1
for n in ['pCube1','pCubeShape1','polyCube1','initialShadingGroup','lambert1']:
    print(n+u' เป็นโหนดชนิด '+mc.nodeType(n))

จะได้ว่า
pCube1 เป็นโหนดชนิด transform
pCubeShape1 เป็นโหนดชนิด mesh
polyCube1 เป็นโหนดชนิด polyCube
initialShadingGroup เป็นโหนดชนิด shadingEngine
lambert1 เป็นโหนดชนิด lambert

อันแรกคือชนิด transform คือตัวกำหนดรูปร่างปรากฏขั้นสุดท้ายของวัตถุ เก็บค่าตำแหน่ง, มุมหมุน, สัดส่วน, ฯลฯ เป็นโหนดหลักของตัววัตถุที่เราเห็นเป็นรูปเป็นร่างปรากฏอยู่ในฉาก

ส่วน mesh นั้นก็เป็นโหนดที่กำหนดรูปแบบการแสดงผลของวัตถุ ถูกสร้างตามติดมากับวัตถุชนิดโพลิกอน

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

เกี่ยวกับเรื่องนี้ได้อธิบายไปแล้วในบทที่ ๘

shadingEngine กับ lambert นั้นเกี่ยวข้องกับพื้นผิว โดยนี่เป็นสีที่กำหนดมาให้เป็นสีตั้งต้นถ้ายังไม่ได้มีการกำหนดสีใหม่

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

เช่นลองสร้างวัตถุใหม่แล้วใส่สีตามขั้นตอนเช่นเดียวกับที่ทำในบทที่ ๒๗
a = mc.shadingNode('blinn',asShader=1)
mc.setAttr(a+'.c',0.26,0.9,0.1,typ='double3')
mc.polySphere(r=1)
mc.hyperShade(a=a)

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



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

ยิ่งทำอะไรกับวัตถุมากก็จะมีโหนดที่เกี่ยวข้องกับตัววัตถุเพิ่มขึ้นมากตามมา คำสั่งที่จัดการกับโพลิกอนในแบบพิเศษเช่นการดึงผิวด้วย polyExtrudeFacet() (บทที่ ๒๔) หรือการปรับขอบโค้งมนด้วย polyBevel() (บทที่ ๒๕) ก็ทำให้เกิดโหนดใหม่เช่นกัน

ลอง
mc.polyPrism()
mc.polyBevel()
mc.polyExtrudeFacet()

จะได้วัตถุที่มีโหนดชื่อ polyExtrudeFace1 และ polyBevel1 อยู่





การดูโหนดทั้งหมดที่ปรากฏอยู่ในโปรแกรมตอนนี้สามารถทำได้โดยใช้ฟังก์ชัน ls()

ลองพิมพ์
print(mc.ls())

จะพบรายชื่อโหนดต่างๆโผล่มามากมายซึ่งมีชื่อแปลกๆที่ไม่คุ้นเคยเต็มไปหมด

ลองนับจำนวนโหนดดูได้ด้วย
print(len(mc.ls())

เพิ่งสร้างวัตถุมาแค่แค่นิดเดียวทำไมถึงมีโหนดอยู่เต็มไปหมดแบบนี้?

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

ยกตัวอย่างเช่น ลองเปิดเอาต์ไลเนอร์ขึ้นมา จะเห็นว่ามีอะไรอยู่นอกเหนือจากสิ่งที่เราสร้าง



ที่เห็นเป็นรูปกล้องนั้นคือโหนดของกล้อง ซึ่แบ่งเป็นมุมต่างๆ ได้แก่ persp คือมุมกล้องที่ปรากฏอยู่บนหน้าจอที่เรามองนี่เอง แล้วก็ตามแนวแกน ๓ ทิศ top, front, side

ส่วนที่เหลือคือ defaultLightSet กับ defaultObjectSet

ของพวกนี้แม้กด delete ก็ไม่สามารถลบทิ้งได้เพราะเป็นสิ่งติดตัวที่จำเป็นต้องมี

สามารถจำเพาะโหนดที่จะเลือกดูได้โดยกำหนดแฟล็กในฟังก์ชัน ls ใช้แฟล็ก typ (type) ซึ่งใส่ค่าเป็นชนิดของโหนดที่ต้องการ เช่นหากจะหาโหนดชนิด transform
mc.ls(typ='transform')

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

หรือถ้าใส่แฟล็ก sl (selection) เป็น mc.ls(sl=1) ก็จะแสดงเฉพาะโหนดที่ถูกเลือกอยู่

ถ้าใส่แฟล็ก ca (cameras) เป็น mc.ls(ca=1) ก็จะแสดงเฉพาะโหนดที่เป็นกล้อง

และยังมีแฟล็กอีกหลายตัวที่ใช้จำกัดชนิดของโหนดที่จะเลือก

ถ้าต้องการรู้ชนิดของโหนดก็ให้ใส่แฟล็ก st (showType) ก็จะมีชนิดของโหนดวางเรียงอยู่ต่อจากชื่อโหนดนั้นๆ



นอกจากนี้มีอีกหลายกรณีที่เราสร้างโหนดขึ้นมาโดยไม่รู้ตัว เช่นเวลาที่ตั้งคีย์เฟรมให้กับค่าไหนก็จะมีโหนดเกิดขึ้นมาใหม่ด้วย โดยแต่ละค่าองค์ประกอบจะมีโหนดสำหรับเก็บค่าความเปลี่ยนแปลงแยกออกจากกัน เช่น
mc.polyCone(r=1)
mc.setKeyframe(at='rx',v=0,t=1)
mc.setKeyframe(at='ty',v=0,t=1)
mc.setKeyframe(at='tz',v=0,t=1)
mc.setKeyframe(at='rx',v=90,t=11)
mc.setKeyframe(at='ty',v=10,t=11)
mc.setKeyframe(at='tz',v=4,t=11)

ในนี้มีการตั้งคีย์เฟรมให้กับ rx, ty และ tz จึงมีโหนดใหม่เกิดขึ้นใหม่ ๓ โหนด ส่วนองค์ประกอบที่ไม่ได้ตั้งคีย์เฟรมจะยังไม่มีโหนดเกิดขึ้น

โหนดต่างๆนั้นมีความสัมพันธ์เชื่อมโยงอยู่ในลักษณะต่างๆ โยงกันโดยผ่านองค์ประกอบต่างๆ การจะดูว่าแต่ละโหนดมีความสัมพันธ์กันอยู่อย่างไรก็ทำได้โดยฟังก์ชัน listConnections()

เช่นลองกับวัตถุ pCone1 ที่เพิ่งตั้งคีย์เฟรมไปนี้
print(mc.listConnections('pCone1'))

จะได้
[u'pCone1_rotateX', u'pCone1_translateY', u'pCone1_translateZ']

ซึ่งจะเห็นว่าเชื่อมโยงอยู่กับโหนดชื่อ pCone1_rotateX, pCone1_translateY และ pCone1_translateZ

เมื่อลองดูชนิดของโหนดเหล่านี้ก็จะเห็นว่า pCone1_rotateX เป็นชนิด animCurveTA ส่วน pCone1_translateY และ pCone1_translateZ เป็นชนิด animCurveTL โหนดนี้จะเก็บลักษณะความเปลี่ยนแปลงที่เกิดขึ้นในแต่ละเฟรม

แต่ในนี้แสดงแค่ว่าเชื่อมต่อกันอยู่ แต่ไม่ได้บอกว่าเชื่อมกันที่องค์ประกอบไหน หากต้องการให้แสดงก็ทำได้โดยเพิ่มแฟล้ก p (plugs)
print(mc.listConnections('pCone1',p=1))

จะได้
[u'pCone1_rotateX.output', u'pCone1_translateY.output', u'pCone1_translateZ.output']

ก็จะเห็นว่าเชื่อมโยงกันอยู่ที่ค่าองค์ประกอบที่ชื่อ output ของแต่ละโหนดนั่นเอง

แต่ในนี้ก็ยังไม่ได้แสดงว่าเชื่อมต่ออยู่กับองค์ประกอบไหนของวัตถุ pCone1 หากจะให้แสดงก็ต้องเพิ่มอีกแฟล็กคือ c (connections)
print(mc.listConnections('pCone1',c=1,p=1))

ผลที่ได้คือ
[u'pCone1.rotateX', u'pCone1_rotateX.output', u'pCone1.translateY', u'pCone1_translateY.output', u'pCone1.translateZ', u'pCone1_translateZ.output']

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

จะเห็นว่าเชื่อมต่อกับองค์ประกอบ rotateX, translateY และ translateZ ตามลำดับ

ตรงนี้จะมีความหมายว่าโหนด pCone1_rotateX นั้นควบคุมองค์ประกอบ rotateX ของโหนด pCone1 อยู่นั่นเอง



เพื่อ ให้เห็นภาพความสัมพันธ์ระหว่างโหนดชัดเจนสามารถเข้าไปดูที่ไฮเพอร์กราฟแบบ เชื่อมโยงโดยกดเลือกที่ตัววัตถุแล้วเข้าไปที่แถบเมนูด้านบน เลือก Windows (ウィンドウ) แล้วเลือก Hypergraph: Connections (ハイパーグラフ: 接続)



กดเลือกที่ปุ่มตามรูปนี้ (入力と出力接続)



ก็จะเห็นว่าโหนด pCone1 เชื่อมต่ออยู่กับโหนด pCone1_rotateX, pCone1_translateY และ pCone1_translateZ แล้วก็ยังเห็นโหนดอื่นๆเช่น pConeShape1 กับ polyCone1 เชื่อมต่อกันอยู่ด้วย

แต่พอดูแล้วก็จะสงสัยว่าโหนด pCone1 กับ pConeShape1 หรือ polyCone1 ไม่ได้เชื่อมต่อกันอยู่หรือ ไม่เห็นมีลากเส้นในภาพนี้ แล้วก็ไม่มีรายชื่อขึ้นตอนใช้ฟังก์ชัน listConnections() ด้วย

ความจริงแล้วก็คือ pCone1 กับ pConeShape1 มีความสัมพันธ์ในลักษณะพ่อแม่ลูกกันอยู่ เช่นเดียวกับความสัมพันธ์ระหว่างวัตถุที่ถูกผูกติดกันซึ่งพูดถึงไปในบทที่ ๑๙ ซึ่งจะไม่แสดงให้เห็นในนี้

การจะหาความสัมพันธ์แบบพ่อแม่ลูกต้องใช้ฟังก์ชัน listRelatives() ซึ่งจะคืนค่าชื่อโหนดลูกของวัตถุที่เลือก
print(mc.listRelatives('pCone1'))

จะได้
[u'pConeShape1']

ในทางกลับกันจะให้แสดงโหนดพ่อแม่ต้องใส่แฟล็ก p (parent)
print(mc.listRelatives('pConeShape1',p=1))

จะได้
[u'pCone1']



จะเห็นได้ว่าวัตถุในมายาประกอบด้วยโหนดชนิดต่างๆมากมายซึ่งแต่ละโหนดก็เชื่อมโยงกันด้วยความสัมพันธ์ในลักษณะต่างๆ

เรื่องของโหนดและความเชื่อมโยงระหว่างโหนดที่ยกมานี้เป็นแค่ส่วนหนึ่งเท่านั้น ยังมีอีกมากมาย ยังไงก็คงไม่อาจกล่าวถึงได้หมดได้

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



อ้างอิง