φυβλαςのβλογ
phyblas的博客



maya python เบื้องต้น บทที่ ๓๘: การจัดแสงและเงา
เขียนเมื่อ 2016/03/19 12:00
แก้ไขล่าสุด 2021/09/28 16:42
ในบทที่ผ่านๆมาเราพูดถึงแต่การสร้างวัตถุรูปร่างต่างๆรวมถึงการแต่งพื้นผิวแต่งสีสันไปแล้ว

แต่ยังมีอีกองค์ประกอบหนึ่งที่สำคัญสำหรับการทำงานกับภาพสามมิติ นั่นคือเรื่องของการเล่นแสงสีและเงา

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

แต่พอต้องการจะมายุ่งเรื่องแสงไฟก็จะต้องเปลี่ยนโหมดการแสดงผลทางหน้าจอ ให้กดที่ปุ่มตามในภาพนี้เพื่อให้แสดงผลแสงตามจริง



หรือไม่ก็แค่กดเลข 7 บนคีย์บอร์ดเท่านั้นก็ได้

หรืออาจทำได้ด้วยการใช้โค้ดไพธอน โดยพิมพ์
mc.modelEditor(mc.getPanel(wf=1),e=1,dl='all')

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

การสร้างแสงไฟขึ้นมานั้นอาจทำได้โดยสร้างโหนดที่อยู่ในหมวดหมู่แสงไฟขึ้นมาโดยใช้ฟังก์ชัน mc.shadingNode() แล้วใส่แฟล็ก al

การใส่แสงไฟทำได้โดยใช้ฟังก์ชันสำหรับสร้างไฟชนิดต่างๆ แสงไฟในมายามีอยู่ ๖ ชนิดด้วยกัน

แอมเบียนต์ไลต์ (アンビエントライト) แสงที่ส่องตลอดทั่วทั้งฉาก
mc.shadingNode('ambientLight',al=1)

ดีเร็กชันนัลไลต์ (ディレクショナル ライト) แสงที่มีทิศทางแน่นอน
mc.shadingNode('directionalLight',al=1)

พอยนต์ไลต์ (ポイント ライト) แสงที่มีจุดกำเนิดจากจุดหนึ่งและส่องออกไปรอบทิศทาง
mc.shadingNode('pointLight',al=1)

สป็อตไลต์ (スポット ライト) แสงที่ออกจากจุดหนึ่งและส่องออกไปเฉพาะขอบเขตในทิศทางหนึ่ง
mc.shadingNode('spotLight',al=1)

แอเรียไลต์ (エリア ライト) แสงที่ออกมาจากพื้นที่หนึ่งแล้วกระจายออกไป
mc.shadingNode('areaLight',al=1)

วอลุมไลต์ (ボリューム ライト) แสงที่ถูกจำกัดอยู่ภายในอาณาเขตปริมาตรหนึ่ง
mc.shadingNode('volumeLight',al=1)

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

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

การสร้างแสงแบบดีเร็กชันนัลไลต์นั้นนอกจากจะใช้ฟังก์ชัน shadingNode() แล้วก็ยังมีอีกฟังก์ชันหนึ่งที่ใช้เพื่อสร้างไฟชนิดนี้โดยเฉพาะ นั่นคือ directionalLight()

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

ตัวอย่าง เริ่มแรกลองใส่แสงลงไปโดยตั้งชื่อให้ง่ายๆว่า "แสงไฟ"
mc.directionalLight(n='saengfai')

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



หากเข้ามาที่แอตทริบิวต์อีดิเตอร์จะเห็นโหนดชื่อ saengfai ซึ่งเป็นโหนดหลัก กับอีกโหนดซึ่งเป็นโหนดกำหนดโครงสร้างคุณสมบัติของแสง ชื่อโหนดจะเป็นชื่อตามที่เราตั้งตามด้วยคำว่า Shape ดังนั้นโหนดนี้จึงชื่อว่า saengfaiShape

ในขณะที่ถ้าใช้ shadingNode() สร้างชื่อที่ใส่ลงไปจะกลายเป็นชื่อของโหนดรูปร่างแสง เช่น
mc.shadingNode('directionalLight',al=1,n='saengfai')

จะได้โหนดชื่อ directionalLight1 กับ saengfai

เพื่อให้เห็นภาพชัดขอยกตัวอย่างโดยจำลองสร้างภูมิประเทศที่มีลักษณะเป็นแท่งเสา ตะปุ่มตะป่ำคล้ายจางเจียเจี้ย (张家界) ที่มณฑลหูหนาน ประเทศจีน (ลบไฟอันเก่าออกก่อน)
import random
mc.polyPlane(w=1000,h=1000,sx=1,sy=1) # สร้างพื้นฉากหลัง
mc.polyPlane(w=200,h=200,sx=20,sy=20) # สร้างพื้นที่ราบสูง
for i in range(mc.polyEvaluate(v=1)):
    if((i+1)%21>1 and i>20 and i <420):
        mc.move(random.gauss(100,20)%100,'.vtx[%d]'%i,y=1) # ปรับความสูงจุดบนพื้นผิวแบบสุ่ม
mc.directionalLight(n='saengfai') # สร้างแสงไฟ

ตอนนี้จะเห็นว่าแสงส่องมาจากฝั่งเดียว



ลองปรับมุมของแสงดู
mc.setAttr('saengfai.rx',-30)
mc.setAttr('saengfai.ry',30)

จะเห็นแสงเปลี่ยนแปลงไป



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

สำหรับการปรับรายละเอียดอื่นๆของแสงนั้นจะต้องไปปรับที่ตัวโหนด saengfaiShape

เช่น ความสว่างของแสง สามารถปรับได้ด้วยการปรับค่า in (intensity) โดยเริ่มแรกจะมีค่าเป็น 1 หากปรับให้สูงขึ้นก็จะสว่างขึ้น เช่น
mc.setAttr('saengfaiShape.in',2.5)

ทำแบบนี้แล้วจะพบว่าแสงสว่างจ้าขึ้นมามาก



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

ก่อนอื่นต้องทำความเข้าใจก่อนว่าในโปรแกรมมายามีเงาอยู่ ๒ ชนิดคือ
- เด็ปธ์แม็ปชาโดว (深度マップ シャドウ, depth map shadow)
- เรย์เทรซชาโดว (レイ トレース シャドウ, ray trace shadow)

๒ แบบนี้ไม่ได้ต่างกันมากมายนัก แต่เรย์เทรซชาโดวจะให้เงาที่ดูเป็นธรรมชาติมากกว่า แต่ก็จะใช้เวลาในการประมวลผลภาพนานกว่าด้วย

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

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

ให้ตั้งที่ค่าองค์ประกอบ useDepthMapShadows หรือชื่อย่อว่า dms ปกติตอนแรกจะมีค่าเป็น 0 คือไม่ทำงาน ถ้าจะให้ทำงานก็ตั้งค่าเป็น 1
mc.setAttr('saengfaiShape.dms',1)

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



หรือพิมพ์โค้ด
mc.modelEditor(mc.getPanel(wf=1),e=1,sdw=1)

แล้วจะเห็นเงาปรากฏขึ้น อย่างไรก็ตามจะเห็นว่าเงานั้นดูขาดเป็นห้วงๆเหมือนภาพแตก นั่นเพราะกำลังแยกภาพต่ำเกินไป ต้องไปปรับค่าองค์ประกอบกำลังแยกภาพซึ่งชื่อ dr (dmapResolution) ให้สูงขึ้น



ลองพิมพ์
mc.setAttr('saengfaiShape.dr',8192)

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



สีของเงาโดยปกติจะเป็นสีดำ แต่ก็สามารถปรับเปลี่ยนได้ หากต้องการ โดยแก้ที่ค่าองค์ประกอบ sc (shadowColor) ซึ่งเป็นค่าของแม่สีทั้ง ๓ หรือถ้าจะปรับแยกทีละสีก็ใช้ scr (scrshadColorR), scg (shadColorG), และ scb (shadColorB)

เช่น ลองปรับเงาเป็นสีเขียว ซึ่งก็คงจะดูแปลกดี
mc.setAttr('saengfaiShape.sc',0,1,0,typ='double3')



สีของแสงไฟเองก็สามารถปรับได้เช่นกัน ค่าองค์ประกอบของสีแสงคือ cl (color)

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

นอกจากนี้ยังปรับอะไรได้อีกหลายอย่าง ลองพิมพ์
mc.listAttr('saengfaiShape')

ก็จะเห็นค่าองค์ประกอบอีกมากมายหลายอย่าง สามารถลองปรับๆกันดูได้

สรุปค่าต่างๆของแสงที่ได้พูดถึงไป
ชื่อในรายการ ชื่อเต็ม ชื่อย่อ ความหมาย
カラー color cl สีของแสง แบ่งย่อยเป็น cr cg cb
強度 intensity in ความเข้มแสง
深度マップシャドウの使用 useDepthMapShadows dms เปิดปิดเงาเด็ปธ์แม็ปชาโดว
レイ トレース シャドウの使用 useRayTraceShadows urs เปิดปิดเงาแบบเรย์เทรซชาโดว
シャドウ カラー shadowColor sc สีของเงา  แบ่งย่อยเป็น scr scg scb
解像度 dmapResolution dr กำลังแยกภาพของเงา

และอีกมากมายที่ไม่ได้พูดถึง

ค่าสี, ความเข้มแสง, เรย์เทรซชาโดว และสีของเงา สามารถตั้งได้ตั้งแต่ตอนที่ใช้ฟังก์ชัน directionalLight() สร้างแสงขึ้นมา ชื่อย่อแฟล็กเป็น rgb, i, rs และ rc ตามลำดับ



ค่ามุมของแสง, ความเข้มแสง, สีของแสง, สีของเงา, ฯลฯ สามารถตั้งคีย์เฟรมให้เปลี่ยนไปตามเวลาได้

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

หากสมมุติว่าที่นี่เป็นจางเจียเจี้ยจริงๆ ซึ่งจางเจียเจี้ยนั้นตั้งอยู่เส้นรุ้ง ๒๘ องศาเหนือ และสมมุติว่าเป็นวันครีษมายัน (วันที่ซีกโลกเหนือกลางวันยาวที่สุด ประมาณ 21 มิ.ย.) ดวงอาทิตย์จะอยู่ห่างจากระนาบศูนย์สูตรฟ้า ๒๓.๕ องศา ดังนั้นดวงอาทิตย์จะขึ้นไปได้สูงสุด ๙๐ - ๒๘ + ๒๓.๕ = ๘๕.๕ องศา
import random
mc.polyPlane(w=1000,h=1000,sx=1,sy=1)
# สร้างหุบเขา
mc.polyPlane(w=200,h=200,sx=20,sy=20,n='zhangjiajie')
for i in range(mc.polyEvaluate(v=1)):
    if((i+1)%21>1 and i>20 and i <420):
        mc.move(random.gauss(100,20)%100,'.vtx[%d]'%i,y=1)
mc.shadingNode('blinn',asShader=1,n='phiuhupkhao')
mc.shadingNode('ramp',at=1,n='khiao_namtan') # ทำไล่สี
mc.setAttr('khiao_namtan.cel[0].ec',0.54,0.43,0.36,typ='double3') # น้ำตาล
mc.setAttr('khiao_namtan.cel[0].ep',0.58)
mc.setAttr('khiao_namtan.cel[1].ec',0.03,0.12,0.03,typ='double3') # เขียว
mc.setAttr('khiao_namtan.cel[1].ep',0.82)
mc.connectAttr('khiao_namtan.oc','phiuhupkhao.c')
mc.select('zhangjiajie')
mc.hyperShade(a='phiuhupkhao')
mc.polyProjection('zhangjiajie.f[0:399]',md='x') # ตั้งแนวฉายให้ไล่สีตามแนวตั้ง
# สร้างแสงอาทิตย์
mc.directionalLight(n='saeng')
mc.setAttr('.rx',-23.5) # มุมเอียงของแกนโลก
mc.setAttr('.rz',28) # ละติจูดของจางเจียเจี้ย
mc.setAttr('saengShape.dms',1) # ใส่เงา
mc.setAttr('saengShape.dr',8192) # เพิ่มกำลังแยกภาพของเงา
mc.setKeyframe(at='.ry',v=0,t=0) # มุมของแสงตอนเช้า
mc.setKeyframe(at='.ry',v=180,t=50) # มุมของแสงตอนเย็น
# ปรับสีของแสงให้เปลี่ยนไปตามเวลา
mc.setKeyframe('saengShape',at='.cg',v=0.7,t=0)
mc.setKeyframe('saengShape',at='.cb',v=0.4,t=0)
mc.setKeyframe('saengShape',at='.cg',v=1,t=10)
mc.setKeyframe('saengShape',at='.cb',v=1,t=10)
mc.setKeyframe('saengShape',at='.cg',v=1,t=40)
mc.setKeyframe('saengShape',at='.cb',v=1,t=40)
mc.setKeyframe('saengShape',at='.cg',v=0.7,t=50)
mc.setKeyframe('saengShape',at='.cb',v=0.4,t=50)





ลองเล่นแสงกับดวงจันทร์ที่สร้างขึ้นจากบทที่แล้วดูบ้าง ลองใช้โค้ดบทที่แล้วสร้างดวงจันทร์ขึ้นมาใหม่แล้วใส่แสงเพิ่มไปตามนี้
mc.directionalLight(n='saeng_athit')
mc.expression(s='saeng_athit.ry = 90+time*180')
mc.setAttr('saeng_athit.dms',1)

อย่างที่รู้กันว่าดวงจันทร์ข้างขึ้นข้างแรมเกิดจากการที่แสงอาทิตย์ส่องในมุมที่ ต่างกัน ดังนั้นจึงเห็นเต็มดวงบ้างเป็นเสี้ยวบ้างดังที่เห็นในรูปนี้





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



อ้างอิง

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


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

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

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

หมวดหมู่

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

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

目录

从日本来的名言
模块
-- numpy
-- matplotlib

-- pandas
-- manim
-- opencv
-- pyqt
-- pytorch
机器学习
-- 神经网络
javascript
蒙古语
语言学
maya
概率论
与日本相关的日记
与中国相关的日记
-- 与北京相关的日记
-- 与香港相关的日记
-- 与澳门相关的日记
与台湾相关的日记
与北欧相关的日记
与其他国家相关的日记
qiita
其他日志

按类别分日志



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

  查看日志

  推荐日志

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