Skip to content
Tayakorn
← All handbooks
devUpdated 2026-08-30Free

คู่มือ Blender Animation

เข้าใจว่าค่าที่เห็นบนจอมาจากไหน — keyframe, armature, constraint, driver จนถึงลำดับที่ Blender คำนวณหนึ่งเฟรม อ่านจบแล้วของไม่ขยับตามที่คีย์ก็ไล่เองเป็น

#blender#animation#rigging#keyframes#depsgraph

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

  • ระดับ: เริ่มจากศูนย์ → ระดับเทพ
  • เวลาอ่าน: ~75 นาที
  • ภาษา: ไทย พร้อมไดอะแกรมและผลรันจริงจาก Blender

ทุกผลลัพธ์ในเล่มนี้มาจากการรัน Blender 5.2.1 LTS จริงในโหมด background ไม่มีตัวเลขไหนพิมพ์เอง คุณรันซ้ำเองได้ทุกอัน

LEVEL 0 · ปูพื้นจากศูนย์

แอนิเมชันไม่ใช่การขยับ แต่คือค่าที่เปลี่ยน#

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

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

ของจริงตรงข้ามเลย Blender ไม่เคยขยับอะไรทั้งนั้น มันแค่ คำนวณค่าชุดหนึ่งใหม่ทุกเฟรม แล้ววาดตามค่านั้น

ทุกอย่างที่ขยับได้ คือค่าของ property ตัวหนึ่ง ที่ถูกคำนวณใหม่ทุกเฟรม

เลขเฟรมที่ถามframe 1frame 13frame 25Blender คำนวณใหม่ทุกครั้ง0.00005.000010.0000location.xlocation.xlocation.xแล้วค่อยวาดภาพที่เห็นคือผลลัพธ์ไม่ใช่ต้นเหตุหนึ่งภาพต่อหนึ่งเฟรม
FIG 1.1 ไม่มีอะไร "ขยับ" — ทุกเฟรมคือการคำนวณค่าชุดหนึ่งใหม่แล้ววาดตามนั้น · เปลี่ยนเลขเฟรม = เปลี่ยนคำถามที่ถาม ไม่ใช่การสั่งให้ของเคลื่อน

ประโยคนี้คือแกนของทั้งเล่ม ทุกบทหลังจากนี้คือการตอบคำถามเดียว: ใครเป็นคนคำนวณค่านั้น และคำนวณตามลำดับไหน

  • keyframe คำนวณจากเส้นโค้ง — บทที่ 3, 4
  • กระดูกคำนวณจากพ่อของมัน — บทที่ 7
  • constraint คำนวณทับผลลัพธ์ที่ได้มาแล้ว — บทที่ 10
  • driver คำนวณจาก property ตัวอื่น — บทที่ 11

💡 property คือช่องเก็บค่าหนึ่งช่องของวัตถุ เช่นตำแหน่งแกน X, ขนาดแกน Z, ความทึบของวัสดุ ทุกช่องที่คุณเห็นในแผงด้านขวาของ Blender คือ property หนึ่งตัว และเกือบทุกตัวแอนิเมตได้

1.1 ทำไมการแยกสองอย่างนี้ถึงสำคัญ

เพราะมันเปลี่ยนคำถามที่คุณถามเวลาแก้ปัญหา

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

ปริศนาข้อแรกที่จะติดค้างไปจนถึงบทที่ 10: คุณคีย์ค่าไว้ค่าหนึ่ง แล้วของไปอยู่อีกที่ ทั้งที่ค่าที่คุณคีย์ยังอยู่ครบไม่ถูกแตะเลย เป็นไปได้ยังไง

Data path — ชื่อจริงของสิ่งที่ขยับได้#

ถ้าทุกอย่างคือ property แล้ว Blender เรียกชื่อ property แต่ละตัวว่ายังไง

คำตอบคือ data path — สตริงหนึ่งเส้นที่บอกทางไปหาช่องเก็บค่านั้น เหมือนที่อยู่บ้าน ไม่ใช่ชื่อเล่น

ลองคีย์ตำแหน่งกล่องหนึ่งใบสองจุด แล้วเปิดดูว่า Blender เก็บอะไรไว้จริง ๆ:

cube.location.x = 0.0
cube.keyframe_insert(data_path="location", index=0, frame=1)
cube.location.x = 10.0
cube.keyframe_insert(data_path="location", index=0, frame=25)

ผลที่ได้:

action name      : BoxAction
จำนวน F-Curve     : 1
  data_path        : 'location' | array_index: 0    ← ชื่อจริงของสิ่งที่ถูกคีย์
  จำนวน keyframe   : 2

สังเกตสองอย่าง: ชื่อคือ location ไม่ใช่ location.x และมี array_index: 0 แยกออกมาต่างหาก

2.1 ทำไมต้องแยก path กับ index

เพราะ location เป็นค่าสามช่องรวมกัน (x, y, z) Blender จึงเก็บ path ไว้เส้นเดียวแล้วใช้ตัวเลข index ชี้ว่าเป็นช่องไหน — 0 คือ X, 1 คือ Y, 2 คือ Z

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

💡 data path เขียนตามโครงสร้างข้อมูลจริงของ Blender เช่น location (ตำแหน่งวัตถุ) · rotation_euler (การหมุน) · scale (ขนาด) · ยิ่งของอยู่ลึก path ยิ่งยาว แต่หลักการเดิม คือทางเดินจากวัตถุไปถึงช่องเก็บค่า:

location                                    ← ตำแหน่งของวัตถุ
pose.bones["forearm"].rotation_quaternion   ← การหมุนของกระดูกชื่อ forearm

2.2 ทำไมคุณต้องรู้เรื่องนี้ตั้งแต่บทที่ 2

เพราะ data path เป็นสิ่งที่โผล่ซ้ำในทุกกลไกที่เหลือของเล่ม

  • ตอนคีย์ — มันคือสิ่งที่บอกว่าคีย์อะไร (บทที่ 3)
  • ตอนตั้ง driver — มันคือปลายทางที่ค่าจะไปลง (บทที่ 11)
  • ตอนไล่หาว่าใครเขียนทับ — มันคือชื่อที่คุณใช้ค้น (บทที่ 13)

พูดอีกแบบ: ถ้าตอบไม่ได้ว่ากำลังจะแอนิเมต data path ไหน แปลว่ายังไม่รู้ว่ากำลังจะทำอะไร

LEVEL 1 · พื้นฐานที่ใช้ทุกวัน

Keyframe คือจุดบนเส้นโค้ง ไม่ใช่ท่าที่จำไว้#

ความเข้าใจผิดที่แพงที่สุดในเล่มนี้: คิดว่า keyframe คือ "ท่า" ที่ Blender ถ่ายรูปเก็บไว้ แล้วค่อยเล่นสลับให้ดู

ถ้าเป็นแบบนั้นจริง เฟรมที่อยู่ระหว่างสองคีย์ก็ควรจะว่างเปล่า แต่มันไม่ว่าง ลองดูของจริงจากไฟล์เดียวกับบทที่แล้ว — คีย์แค่สองจุด คือเฟรม 1 อยู่ที่ 0 และเฟรม 25 อยู่ที่ 10:

    จุด (frame, value) = (1.0, 0.0)  | interpolation: BEZIER
    จุด (frame, value) = (25.0, 10.0) | interpolation: BEZIER

แล้วถามค่าที่เฟรมที่ ไม่ได้คีย์ไว้เลย:

  frame  1 -> location.x = 0.0000
  frame  5 -> location.x = 0.7407     ← ไม่มีใครคีย์เฟรมนี้ แต่มีค่า
  frame  7 -> location.x = 1.5625
  frame 13 -> location.x = 5.0000     ← กึ่งกลางเวลาพอดี ได้กึ่งกลางระยะพอดี
  frame 19 -> location.x = 8.4375
  frame 25 -> location.x = 10.0000

มีค่าทุกเฟรม ทั้งที่คีย์ไว้แค่สองจุด

keyframe ไม่ใช่ท่าที่ถูกจำ แต่คือจุดที่คุณปักลงบนเส้นโค้ง — ส่วนที่เหลือของเส้น Blender คำนวณให้

100ค่าเฟรมframe 1frame 25คีย์เองคีย์เอง0.74071.56255.00008.4375จุดกลวง = เฟรมที่ไม่ได้คีย์ แต่มีค่าเสมอ
FIG 3.1 คีย์ไว้ 2 จุด แต่ทุกเฟรมมีค่า — ค่าที่เฟรม 5, 7, 19 ไม่มีใครปัก มันถูกอ่านจากเส้น · ตัวเลขทั้งหมดมาจาก fcurve.evaluate() ของจริง

3.1 สิ่งที่เกิดขึ้นจริงตอนคุณกด I

กด I หนึ่งครั้ง Blender ทำสามอย่างเรียงกัน:

  1. อ่านค่าปัจจุบันของ property นั้น
  2. หา F-Curve ที่ผูกกับ data path นั้น ถ้ายังไม่มีก็สร้างใหม่
  3. ปักจุดหนึ่งจุดลงบนเส้นนั้น ที่พิกัด (เฟรมปัจจุบัน, ค่าที่อ่านได้)

💡 F-Curve คือ "เส้นโค้งฟังก์ชัน" — กราฟที่รับเลขเฟรมเข้าไป แล้วคืนค่าของ property ออกมาหนึ่งค่า ทุก property ที่ถูกคีย์จะมี F-Curve ของตัวเองหนึ่งเส้นเสมอ

3.2 ทำไมความเข้าใจนี้ถึงเปลี่ยนวิธีทำงาน

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

และนั่นคือบทถัดไป

F-Curve กับ interpolation — รูปทรงของเวลา#

กลับไปดูตัวเลขในบทที่แล้วอีกครั้ง แล้วคิดเลขตาม

เฟรม 5 ผ่านไป 4 เฟรมจากทั้งหมด 24 เฟรม = 17% ของเวลา แต่ค่าที่ได้คือ 0.7407 จากระยะทั้งหมด 10 = 7.4% ของระยะ

เดินทางไป 17% ของเวลา แต่ได้ระยะแค่ 7.4% แปลว่ามันออกตัวช้า

ทีนี้ดูเฟรม 13 ที่เป็นกึ่งกลางเวลาพอดี ได้ค่า 5.0000 = กึ่งกลางระยะพอดีเป๊ะ และเฟรม 19 (75% ของเวลา) ได้ 8.4375 = 84% ของระยะ คือช่วงกลางมันวิ่งเร็วกว่าค่าเฉลี่ย

นี่คือ ease-in / ease-out ที่มองเห็นเป็นตัวเลข — ออกตัวช้า เร่งกลางทาง ชะลอตอนจบ Blender ให้มาเป็นค่าเริ่มต้นโดยที่คุณไม่ต้องทำอะไร เพราะ interpolation ของทุกคีย์ตั้งต้นเป็น BEZIER

CONSTANTค้างแล้วกระโดดLINEARความเร็วคงที่BEZIERช้า–เร็ว–ช้าจุดทึบ = keyframe เหมือนกันทุกช่อง · เส้นคือสิ่งที่ต่างกัน
FIG 4.1 คีย์สองจุดเหมือนกันทั้งสามช่อง ต่างกันแค่รูปเส้นระหว่างจุด · BEZIER เป็นค่าเริ่มต้นเพราะของจริงไม่มีอะไรออกตัวเต็มความเร็วทันที

4.1 สามทรงที่ต้องแยกออก

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

ตารางข้างบนเป็นคำบรรยาย ทีนี้ดูเป็นตัวเลข — คีย์สองจุดเหมือนกันเป๊ะทั้งสามช่อง เปลี่ยนแค่ interpolation:

 frame |   CONSTANT |     LINEAR |     BEZIER
----------------------------------------------
     1 |     0.0000 |     0.0000 |     0.0000
     7 |     0.0000 |     2.5000 |     1.5625
    13 |     0.0000 |     5.0000 |     5.0000
    19 |     0.0000 |     7.5000 |     8.4375
    25 |    10.0000 |    10.0000 |    10.0000

อ่านทีละคอลัมน์แล้วเห็นนิยามของมันเลย:

  • CONSTANT ค้างที่ 0 ทุกเฟรม แล้วกระโดดเป็น 10 ตอนจบ
  • LINEAR ได้ 2.5 แล้ว 5.0 แล้ว 7.5 — ระยะห่างเท่ากันทุกช่วง นั่นคือความเร็วคงที่
  • BEZIER ออกตัวช้ากว่า (ได้ 1.5625 ตอนที่ LINEAR ไปถึง 2.5 แล้ว) ไล่ทันพอดีตรงกลางที่ 5.0 แล้วชะลอตอนท้าย

4.2 คำถามออกแบบ — ทำไมค่าเริ่มต้นถึงเป็น Bezier

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

💡 อุปมาที่ใช้ได้: ทรงของเส้นโค้ง ≈ การเหยียบคันเร่งกับเบรกของรถ ชันมาก = เร่งแรง แบน = จอดนิ่ง — แต่จุดที่อุปมานี้ใช้ไม่ได้ คือ F-Curve ไม่ได้แทน "ความเร็ว" มันแทน ค่า ความเร็วคือความชันของมันต่างหาก เส้นที่แบนไม่ได้แปลว่าช้า แต่แปลว่า ไม่เปลี่ยนค่าเลย

4.3 เคล็ดลับที่ใช้ได้ทันที

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

Dope Sheet กับ Graph Editor — ข้อมูลชุดเดียว สองมุมมอง#

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

Dope SheetGraph Editor
แสดงเฉพาะ เวลา ของแต่ละคีย์ทั้งเวลาและค่า
แกนตั้งคือรายการช่อง (คนละ property คนละแถว)ค่าของ property
เหมาะกับจัดจังหวะ ขยับคีย์ไปมา เทียบหลายช่องพร้อมกันปรับรูปทรง ปรับความเร็ว แก้ค่าที่เกิน

5.1 ทำไมต้องมีสองอัน

เพราะสองงานนี้ต้องการให้ทิ้งข้อมูลคนละอย่าง

ตอนจัดจังหวะ คุณอยากเห็นว่าคีย์ของแขน ขา และหัวเรียงกันยังไงในเวลา — ถ้าเอาแกนค่ามาแสดงด้วย ทุกช่องจะทับกันจนอ่านไม่ออก Dope Sheet จึงบีบทุกช่องให้เหลือแถวละเส้น แลกกับการมองไม่เห็นค่า

ตอนแก้ความรู้สึกของการเคลื่อนไหว คุณต้องเห็นรูปเส้น แต่ Dope Sheet ให้ไม่ได้เลย

💡 อุปมา: Dope Sheet เหมือนตารางเวลารถไฟ (ขบวนไหนออกกี่โมง) · Graph Editor เหมือนกราฟความเร็วของขบวนเดียว (เร่งตรงไหน เบรกตรงไหน) — คนละคำถาม ข้อมูลชุดเดียวกัน

5.2 ลองพิสูจน์ด้วยตาเอง

วิธีเปิดสองหน้าต่างพร้อมกันแบบเร็วที่สุด: กดแท็บ Animation ที่แถบบนสุดของ Blender — มันจัดหน้าจอให้เสร็จมาแล้ว มี Dope Sheet รออยู่ด้านล่าง

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

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

Action — กล่องเก็บเส้นโค้ง และทำไมต้องมี#

ย้อนกลับไปดูผลรันในบทที่ 2 อีกครั้ง มีบรรทัดหนึ่งที่ยังไม่ได้อธิบาย:

action name      : BoxAction

ไม่มีใครสั่งให้สร้าง "BoxAction" เลย มันโผล่มาเองตอนกดคีย์ครั้งแรก

Action คือกล่องที่รวม F-Curve หลายเส้นไว้ด้วยกันเป็นชุดเดียว กล่องนี้ผูกกับวัตถุ แต่แยกออกจากวัตถุได้ ซึ่งเป็นจุดสำคัญทั้งหมด

6.1 คำถามออกแบบ — ทำไมต้องมีชั้นนี้

ถ้า F-Curve ผูกติดกับวัตถุตรง ๆ โดยไม่มีกล่องคั่น สิ่งเหล่านี้จะทำไม่ได้เลย:

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

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

💡 Action ≈ ไฟล์เอกสารหนึ่งไฟล์ · วัตถุ ≈ โปรแกรมที่เปิดไฟล์นั้นอยู่ — เปิดไฟล์อื่นก็ได้ ให้โปรแกรมอื่นเปิดไฟล์เดียวกันก็ได้

6.2 โครงข้างในของ Action ใน Blender รุ่นใหม่

ตั้งแต่ Blender 4.4 เป็นต้นมา ข้างใน Action มีชั้นเพิ่มขึ้น: layersstripschannelbags → แล้วถึงจะเจอ fcurves

นี่คือเหตุผลที่โค้ดตัวอย่างในเล่มนี้ต้องไล่ลงไปหลายชั้นกว่าจะถึงเส้นโค้ง แทนที่จะเรียก action.fcurves ตรง ๆ แบบรุ่นเก่า (ทางเรียกแบบเก่ายังใช้ได้ถึง 4.x แต่ถูกถอดออกจริงใน Blender 5.0):

for layer in action.layers:
    for strip in layer.strips:
        for cb in strip.channelbags:
            fcurves.extend(cb.fcurves)

ชั้นที่เพิ่มมาไม่ได้ทำหน้าที่เดียวกันทั้งหมด แยกให้ชัดจะได้ไม่สับสน:

  • Slot คือตัวที่ทำให้ Action หนึ่งกล่องเก็บงานของ วัตถุหลายตัว ได้พร้อมกัน ซึ่งรุ่นเก่าทำไม่ได้ — นี่คือของใหม่ตัวจริงของ 4.4
  • Layer กับ Strip คือโครงสำหรับ ซ้อนชั้นเพื่อผสมกัน ในอนาคต ตอนนี้ Blender ยังล็อกไว้ที่ 1 layer และ 1 strip ต่อ Action เพราะฟีเจอร์ยังไม่เสร็จ
  • Channelbag คือกล่องที่ถือ F-Curve จริง ๆ — พูดง่าย ๆ คือ Action แบบเดิมทั้งก้อน ถูกยัดลงมาอยู่ตรงนี้

⚠️ อย่าเสียเวลาลองสร้าง layer ตัวที่สอง มันยังทำไม่ได้ในตอนนี้ และไม่ใช่เพราะคุณทำผิด

6.3 NLA — ชั้นที่อยู่เหนือ Action

เมื่อ Action กลายเป็นก้อนที่หยิบยกได้แล้ว ขั้นต่อไปคือเอาหลายก้อนมาวางเรียงและผสมกันบนแทร็ก เหมือนตัดต่อวิดีโอ นั่นคือ NLA (Non-Linear Animation) เล่มนี้แตะแค่ให้รู้จักในบทที่ 14 เพราะมันเป็นเรื่องของการ ประกอบ ไม่ใช่กลไกของ ค่า

LEVEL 2 · ระดับกลาง

Armature — ทำไมกระดูกเป็นแค่ระบบพิกัด#

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

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

ท่าพักupper_armforearmหัว forearm = (0, 0, 1)หมุนพ่อ 90°หลังหมุน upper_armupper_armforearmหัว forearm = (0, -1, 0)ค่าหมุนที่เก็บของ forearm(0, 0, 0) — ไม่เปลี่ยน
FIG 7.1 หมุนแค่ upper_arm · forearm ไม่ถูกแตะเลย ค่าหมุนของมันยัง (0, 0, 0) แต่หัวกระดูกย้ายจาก (0, 0, 1) ไป (0, -1, 0) — ค่าของลูกคือค่าเทียบพ่อ เสมอ

ลองสร้างกระดูกสองท่อนต่อกัน แล้วหมุนแค่ท่อนล่าง:

=== ท่าพัก — ยังไม่หมุนอะไร ===
  upper_arm  rotation_euler ที่เก็บไว้ = (0.0, 0.0, 0.0) องศา
  upper_arm  หัวกระดูกในพิกัดโลก      = (0.0, 0.0, 0.0)
  forearm    rotation_euler ที่เก็บไว้ = (0.0, 0.0, 0.0) องศา
  forearm    หัวกระดูกในพิกัดโลก      = (0.0, 0.0, 1.0)

=== หมุนเฉพาะ upper_arm 90 องศารอบแกน X ===
  upper_arm  rotation_euler ที่เก็บไว้ = (90.0, -0.0, 0.0) องศา
  upper_arm  หัวกระดูกในพิกัดโลก      = (0.0, 0.0, 0.0)
  forearm    rotation_euler ที่เก็บไว้ = (0.0, 0.0, 0.0) องศา    ← ยัง 0 อยู่
  forearm    หัวกระดูกในพิกัดโลก      = (0.0, -1.0, -0.0)        ← แต่ย้ายที่แล้ว

forearm ไม่เคยถูกแตะ ค่าหมุนของมันยังเป็นศูนย์ทุกแกน แต่มันย้ายจาก (0, 0, 1) ไปอยู่ (0, -1, 0)

ค่าของกระดูกลูกไม่เคยเป็นตำแหน่งจริง มันคือตำแหน่ง เทียบกับพ่อ เสมอ

7.1 ทำไมออกแบบแบบนี้

เพราะมันตรงกับสิ่งที่คุณอยากสั่งจริง ๆ เวลาขยับร่างกาย

ตอนคุณงอศอก คุณไม่ได้คิดว่า "ย้ายมือไปพิกัด (0.3, -0.5, 1.2)" คุณคิดว่า "งอศอก 40 องศา" — แล้วมือก็ไปเอง เพราะมือติดอยู่กับปลายแขน ระบบพิกัดซ้อนกันจึงเป็นการเข้ารหัสความจริงข้อนี้ลงในโครงสร้างข้อมูล

7.2 ผลที่ตามมา ที่ต้องรู้ล่วงหน้า

เมื่อค่าของลูกเป็นค่าเทียบพ่อ สองอย่างนี้จะตามมาเสมอ:

  • ความผิดพลาดสะสมลงข้างล่าง — หมุนไหล่ผิด 5 องศา ปลายนิ้วเคลื่อนไปไกลกว่านั้นมาก
  • แก้ปลายทางไม่ได้ตรง ๆ — อยากให้มือไปแตะจุดหนึ่ง ต้องไล่หมุนทีละข้อจากต้นทาง ซึ่งช้าและน่ารำคาญ

ข้อหลังนี่เองคือปัญหาที่ IK เกิดมาเพื่อแก้ (บทที่ 9) แต่ก่อนจะไปถึงตรงนั้น ต้องผ่านจุดที่มือใหม่พังบ่อยที่สุดก่อน

Skinning กับ weight — จุดที่มือใหม่พังบ่อยที่สุด#

ตอนนี้มีกระดูกแล้ว แต่กระดูกกับเนื้อยังไม่รู้จักกัน คำถามคือ จุดแต่ละจุดบนผิวรู้ได้ยังไงว่าต้องตามกระดูกตัวไหน

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

ของจริงคือ แต่ละจุดฟังกระดูกหลายตัวพร้อมกัน โดยมีน้ำหนักคนละเท่าไหร่ นั่นคือ weight

ลองผูกแท่งทรงกระบอกเข้ากับกระดูกสองท่อน (ข้อต่ออยู่ที่ความสูง 1.0) แล้วอ่านค่า weight จริงไล่จากล่างขึ้นบน:

  z=+0.00  lower=1.000  upper=0.000
  z=+0.50  lower=0.995  upper=0.000
  z=+0.83  lower=0.896  upper=0.104
  z=+1.00  lower=0.500  upper=0.500     ← ตรงข้อต่อพอดี ฟังคนละครึ่งเป๊ะ
  z=+1.17  lower=0.104  upper=0.896
  z=+1.50  lower=0.000  upper=0.995
  z=+2.00  lower=0.000  upper=1.000

ตรงข้อต่อได้ 0.500 / 0.500 พอดี แล้วไล่ระดับออกไปทั้งสองทางอย่างสมมาตร นี่ไม่ใช่การแบ่งเจ้าของ แต่คือการผสม

weight ไล่จากล่างขึ้นบน (ข้อต่ออยู่ที่ z = 1.0)z=+0.001.000 / 0.000z=+0.500.995 / 0.000z=+0.830.896 / 0.104z=+1.000.500 / 0.500z=+1.170.104 / 0.896z=+1.500.000 / 0.995กระดูก lowerกระดูก upperผลตอนหมุน upper 90°ข้อพับโค้ง ไม่หักมุม0.5 / 0.5จุดล่างไม่ขยับ · จุดบนตามไปเต็ม
FIG 8.1 จุดตรงข้อต่อได้ 0.500 / 0.500 พอดี จึงเคลื่อนครึ่งทางระหว่างอยู่นิ่งกับตามไปเต็ม — นั่นคือเหตุผลที่ข้อพับโค้ง ไม่ใช่หักเป็นมุม

8.1 ผลของการผสม เห็นเป็นตัวเลข

ทีนี้หมุนกระดูกบน 90 องศา แล้ววัดว่าแต่ละความสูงเคลื่อนไปไหน:

  ก่อน z=+0.50  ->  หลัง (x=+0.141, y=+0.141, z=+0.500)   ← ไม่ขยับตามเลย
  ก่อน z=+1.00  ->  หลัง (x=+0.141, y=+0.071, z=+1.071)   ← ขยับครึ่งทาง
  ก่อน z=+1.50  ->  หลัง (x=+0.000, y=-0.500, z=+1.200)   ← ตามไปเต็ม
  ก่อน z=+2.00  ->  หลัง (x=+0.000, y=-1.000, z=+1.200)   ← ตามไปเต็ม

จุดที่ weight เป็น 0.5/0.5 ไปหยุดครึ่งทางระหว่าง "อยู่ที่เดิม" กับ "ตามไปเต็ม" พอดี — นี่คือเหตุผลที่ข้อพับดูโค้งแทนที่จะหักเป็นมุม

8.2 ทำไมมันถึงพังบ่อย

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

อาการที่เห็นคือ "ขยับแขนแล้วเอวยุบตาม" ซึ่งไม่ใช่บั๊ก แต่คือ weight บนเอวติดค่าของกระดูกแขนมานิดหน่อย

เคล็ดลับ: เวลาเจออาการแบบนี้ อย่าเพิ่งไปแก้กระดูก ให้เข้าโหมด Weight Paint แล้วเลือกกระดูกที่ต้องสงสัย จุดที่ติดสีอยู่ผิดที่จะโผล่ให้เห็นทันที ปัญหาเกือบทั้งหมดของ "เนื้อบิดประหลาด" อยู่ที่ weight ไม่ใช่ที่โครงกระดูก

IK กับ FK — สองวิธีตอบคำถามเดียวกัน#

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

วิธีไล่หมุนจากต้นทางลงปลายทางนั้นมีชื่อ เรียกว่า FK (Forward Kinematics) — คุณให้มุม ระบบคำนวณตำแหน่งปลายทางให้

IK (Inverse Kinematics) กลับทิศ — คุณให้ตำแหน่งปลายทาง ระบบคำนวณมุมทุกข้อย้อนกลับให้

ลองของจริง สร้างแขนสองท่อน ใส่ IK ชี้ไปที่เป้า แล้วย้ายเป้าอย่างเดียว ไม่หมุนกระดูกสักตัว:

  เป้าที่ (1.0, 0, 1.2)
    ปลายแขนไปหยุดที่ : (1.0, 0.0, 1.2)
    upper_arm  ค่าที่เก็บใน property = (0.0, -0.0, 0.0)   มุมจริงหลังแก้สมการ = (90.0, 1.2, 0.0)
    forearm    ค่าที่เก็บใน property = (0.0, -0.0, 0.0)   มุมจริงหลังแก้สมการ = (90.0, 78.4, 0.0)

  เป้าที่ (1.8, 0, 0.5)
    ปลายแขนไปหยุดที่ : (1.8, 0.0, 0.5)
    upper_arm  ค่าที่เก็บใน property = (0.0, -0.0, 0.0)   มุมจริงหลังแก้สมการ = (90.0, 53.6, 0.0)
    forearm    ค่าที่เก็บใน property = (0.0, -0.0, 0.0)   มุมจริงหลังแก้สมการ = (90.0, 95.4, -0.0)

ปลายแขนไปหยุดตรงเป้าเป๊ะทุกครั้ง และมุมที่ Blender แก้สมการได้ก็เปลี่ยนไปทุกครั้ง — 78.4 องศา แล้ว 95.4 องศา

แต่ดูคอลัมน์กลางให้ดี: ค่าที่เก็บใน property เป็นศูนย์ตลอด ทุกครั้ง ทุกกระดูก

9.1 ปริศนาที่เพิ่งโผล่

Blender คำนวณมุมได้ 78.4 องศา แล้วเอาไปวาดจริง แต่ไม่เคยเขียนตัวเลขนั้นกลับลงในช่องเก็บค่า ทำไม

นี่คือเบาะแสชิ้นแรกของกฎที่จะไขในบทที่ 10 และปิดจบในบทที่ 13 — IK คือ constraint ตัวหนึ่ง และ constraint ไม่มีสิทธิ์เขียนทับค่าที่คุณเก็บไว้

9.2 ขีดจำกัดที่มองเห็นได้

บรรทัดสุดท้ายของการทดลองบอกอะไรบางอย่าง:

  เป้าที่ (0.0, 0, 3.0)
    ปลายแขนไปหยุดที่ : (0.0, 0.0, 2.0)

เป้าอยู่ที่ระยะ 3.0 แต่แขนยาวรวมแค่ 2.0 มันจึงเหยียดสุดแล้วหยุด ไม่ยืดตาม IK ไม่ใช่เวทมนตร์ มันแก้สมการภายใต้ข้อจำกัดที่มีอยู่จริง

9.3 เลือกอันไหน

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

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

LEVEL 3 · ระดับสูง

Constraint — ค่าที่ถูกเขียนทับหลังคุณคีย์ไปแล้ว#

ถึงเวลาไขปริศนาที่ปลูกไว้ตั้งแต่บทที่ 1

สร้างกล่องหนึ่งใบ คีย์ตำแหน่งไว้ที่จุดกำเนิดชัด ๆ แล้วใส่ constraint ชนิด Copy Location ชี้ไปที่เป้าหมายที่ (7, 3, 2):

=== ก่อนใส่ constraint ===
  ค่าใน property (box.location)        : (0.0, 0.0, 0.0)
  ตำแหน่งจริงหลังประเมิน (matrix_world) : (0.0, 0.0, 0.0)

=== หลังใส่ Copy Location -> Target(7, 3, 2) ===
  ค่าใน property (box.location)        : (0.0, 0.0, 0.0)     ← ไม่ขยับเลยสักนิด
  ตำแหน่งจริงหลังประเมิน (matrix_world) : (7.0, 3.0, 2.0)     ← แต่ของไปอยู่โน่นแล้ว

นี่คือคำตอบ กล่องอยู่ที่ (7, 3, 2) จริง แต่ค่าที่คุณคีย์ไว้ยังเป็น (0, 0, 0) ครบถ้วนไม่ถูกแตะ

constraint ไม่แก้ค่าของคุณ มันทำงานทีหลัง บนผลลัพธ์ที่คำนวณเสร็จแล้ว

10.1 ทำไมนี่คือคำอธิบายของอาการยอดฮิต

อาการที่ทุกคนเคยเจอ: เลื่อนค่าใน N-panel แล้วของไม่ขยับ หรือคีย์ตำแหน่งไว้แล้วของดื้อไปอยู่ที่อื่น

เกือบทุกครั้งคำตอบคือมี constraint ตัวหนึ่งทำงานอยู่ท้ายสาย และมันชนะเสมอ เพราะมันได้คิวหลัง

สิ่งที่ควรทำเมื่อเจออาการนี้ ไม่ใช่การไล่ลบคีย์ แต่คือเปิดแท็บ Object Constraint Properties ดูว่ามีอะไรอยู่ในนั้น

10.2 ทำไมออกแบบให้ไม่เขียนทับ

เพราะถ้า constraint เขียนกลับลง property ค่าที่คุณตั้งใจคีย์จะถูกทำลายถาวรทันทีที่เปิดไฟล์ และคุณจะย้อนกลับไม่ได้เลย

การแยกสองชั้นนี้ออกจากกันทำให้ ปิด constraint แล้วของกลับไปที่เดิมทันที ซึ่งเป็นสิ่งที่ทำให้มันปลอดภัยพอจะใช้จริง

💡 อุปมา: ค่าที่คุณคีย์ = สิ่งที่คุณพิมพ์ลงในเอกสาร · constraint = ตัวกรองที่แสดงผลทับตอนพิมพ์ออกกระดาษ — ต้นฉบับไม่เคยเปลี่ยน จุดที่อุปมานี้ใช้ไม่ได้ คือ constraint ไม่ได้ทำงานแค่ตอน "พิมพ์ออก" มันทำงานทุกเฟรม และผลของมันเป็นข้อมูลที่ constraint ตัวถัดไปเอาไปใช้ต่อได้

10.3 ลำดับใน stack มีความหมาย

constraint หลายตัวบนวัตถุเดียวกันทำงานเรียงจากบนลงล่าง ตัวล่างรับผลของตัวบนมาแล้วทำต่อ สลับลำดับ = ได้ผลคนละอย่าง

นี่คือเหตุผลที่ constraint stack เป็น "รายการที่เรียงได้" ไม่ใช่ "กล่องติ๊กถูก"

Driver — property ตัวหนึ่งอ่านค่าจากอีกตัว#

จนถึงตอนนี้ ค่ามาจากสองทาง: เส้นโค้งที่ผูกกับเวลา (บทที่ 3) และ constraint ที่ทำงานทับทีหลัง (บทที่ 10)

ยังมีทางที่สาม — ให้ property ตัวหนึ่งเลิกเก็บค่าของตัวเอง แล้วไปอ่านจาก property ตัวอื่นแทน นั่นคือ driver

ตั้งให้ scale.z ของกล่อง อ่านจาก location.x ของตัวควบคุม ด้วยสูตร 1 + lift * 0.5:

  Ctrl.location.x = 0.0  -> Box.scale.z = 1.000
  Ctrl.location.x = 1.0  -> Box.scale.z = 1.500
  Ctrl.location.x = 2.0  -> Box.scale.z = 2.000
  Ctrl.location.x = 4.0  -> Box.scale.z = 3.000

ในไฟล์นี้ไม่มี keyframe แม้แต่จุดเดียว แต่ค่าเปลี่ยนทุกครั้งที่ขยับตัวควบคุม

11.1 ต่างจาก keyframe ตรงไหน

keyframedriver
ค่าขึ้นกับเวลา (เลขเฟรม)property ตัวอื่น
หยุดเวลาแล้วค่าค้างนิ่งยังเปลี่ยนได้ ถ้าต้นทางเปลี่ยน
ปลายทางระบุด้วยdata pathdata path เหมือนกัน

แถวสุดท้ายคือจุดที่บทที่ 2 กลับมาจ่ายคืน — ตอนตั้ง driver คุณต้องบอกให้ได้ว่าปลายทางคือ data path ไหน index อะไร ในตัวอย่างนี้คือ 'scale' index 2

11.2 ใช้จริงตอนไหน

เมื่อมี "ความสัมพันธ์" ที่ควรเป็นจริงตลอดเวลา ไม่ใช่แค่ในช่วงเวลาหนึ่ง เช่น

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

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

เคล็ดลับ: ถ้าพบว่าตัวเองกำลังจะคีย์ของสองอย่างให้ตรงกันเป๊ะ ๆ นั่นคือสัญญาณว่าควรใช้ driver แทน

11.3 ข้อควรระวัง

driver ที่อ้างวนกลับมาหาตัวเอง (A อ่าน B, B อ่าน A) จะทำให้ Blender ไม่รู้ว่าต้องคำนวณใครก่อน — และนั่นคือคำถามที่บทถัดไปตอบทั้งบท

Shape key กับ Lattice — เปลี่ยนรูปโดยไม่แตะกระดูก#

ไม่ใช่ทุกการเปลี่ยนรูปที่เหมาะกับกระดูก การยิ้ม การขมวดคิ้ว การพองของลูกโป่ง — สิ่งเหล่านี้ไม่มี "ข้อต่อ" ให้หมุน

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

=== ข้อมูลที่ถูกเก็บ ===
  key 'Basis' : จุดที่ 0 อยู่ที่ (-1.0, -1.0, 0.0)
  key 'Smile' : จุดที่ 0 อยู่ที่ (-1.0, -1.0, 1.0)

=== เลื่อนสไลเดอร์ตัวเดียว แล้วดูจุดจริง ===
  Smile = 0.0   -> จุดที่ 0 อยู่ที่ z = 0.000
  Smile = 0.25  -> จุดที่ 0 อยู่ที่ z = 0.250
  Smile = 0.5   -> จุดที่ 0 อยู่ที่ z = 0.500
  Smile = 0.75  -> จุดที่ 0 อยู่ที่ z = 0.750
  Smile = 1.0   -> จุดที่ 0 อยู่ที่ z = 1.000

เป็นการผสมเชิงเส้นตรงไปตรงมา ไม่มีอะไรซับซ้อนกว่านั้น

12.1 จุดที่มันเชื่อมกับสองบทก่อน

ค่าของ shape key คือ property ตัวหนึ่ง — แปลว่ามันคีย์ได้ (บทที่ 3) และมันถูก driver ขับได้ (บทที่ 11)

นั่นคือวิธีที่ระบบใบหน้าของงานจริงทำงาน: กระดูกตัวควบคุมหนึ่งตัวเลื่อนขึ้น → driver อ่านค่านั้น → shape key หลายตัวขยับตามสัดส่วนที่กำหนด กลไกสามอย่างจากสามบทต่อกันเป็นสายเดียว

12.2 Lattice — กรงที่บีบของข้างในตาม

Lattice คือกรงจุดควบคุมหยาบ ๆ ที่ครอบวัตถุไว้ ขยับจุดบนกรง แล้วทุกอย่างข้างในบิดตามอย่างนุ่มนวล

ลองพิสูจน์ด้วยตาเอง — ครอบท่อด้วย lattice แล้วขยับจุดควบคุมบนสุดไปทางแกน X:

จุดบนสุดใน mesh ต้นฉบับ         = (0.0, 0.3, 1.0)
ผลหลังประเมิน ก่อนขยับ lattice  = (0.0, 0.3, 1.0)   ← ยังไม่มีอะไรเกิดขึ้น
ผลหลังประเมิน หลังขยับจุดควบคุม  = (0.172, 0.3, 1.0) ← เนื้อบิดตามแล้ว
mesh ต้นฉบับหลังทุกอย่าง        = (0.0, 0.3, 1.0)   ← ไม่เคยถูกแตะเลย
จำนวนกระดูกในซีน                = 0

สองบรรทัดสุดท้ายคือหัวใจ เนื้อบิดจริงแต่ข้อมูล mesh ต้นฉบับไม่เปลี่ยนเลยสักจุด และทำได้โดยไม่มีกระดูกอยู่ในซีนแม้แต่ตัวเดียว — กฎเดียวกับ constraint ในบทที่ 10 เป๊ะ คือทำงานบนผลลัพธ์ ไม่ใช่บนค่าที่เก็บ

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

LEVEL 4 · ระดับเทพ

หนึ่งเฟรมถูกคำนวณยังไง#

ทั้งเล่มปลูกคำถามไว้หลายข้อ บทนี้ไขพร้อมกันทั้งหมดด้วยคำตอบเดียว

ทุกครั้งที่เฟรมเปลี่ยน Blender ไม่ได้ "อัปเดตของที่เปลี่ยน" แบบสุ่ม มันมีแผนผังชื่อ dependency graph (depsgraph) ที่บอกว่าอะไรต้องคำนวณก่อนอะไร แล้วเดินตามแผนนั้นทุกเฟรม

ลูกศร = ทิศทางที่ค่าไหล · ปลายลูกศรคือคนที่ต้องรอเวลา (เลขเฟรม)ต้นทางที่ไม่รอใครF-Curveเขียนลง propertydriverอ่านจาก property อื่นรอค่านี้เมทริกซ์พ่อ → ลูกลูกอ่านของพ่อconstraint / IKทำงานบนผลลัพธ์Armature modifierอ่านกระดูกหลัง constraintวาดกฎข้อเดียวที่ตายตัวมีแค่ F-Curve ที่เขียนลงค่าที่คุณเก็บไว้ชั้นอื่นแตะไม่ได้เลยปิดมันแล้วของกลับที่เดิม
FIG 13.1 ลูกศรอ่านว่า "คนนี้รอค่าจากคนนั้น" — ใครมีลูกศรชี้เข้า คนนั้นถูกประเมินทีหลัง · ลำดับจึงเกิดจากวิธีที่คุณต่อของไม่ได้ถูกกำหนดมาก่อน · มีแค่ F-Curve ที่เขียนลง property จริง ที่เหลือทำงานบนผลลัพธ์

13.1 ดูทั้งสายพร้อมกัน

สร้างกล่องหนึ่งใบที่มีครบสามชั้นทับกันอยู่:

  1. keyframe บน location.x — วิ่งจาก 0 ไป 2 ระหว่างเฟรม 1 ถึง 41
  2. driver บน scale.z — อ่านจากตัวควบคุม ไม่เกี่ยวกับเวลา
  3. constraint Limit Location — ห้าม x เกิน 1.5

แล้วไล่ดูทีละเฟรม:

 frame |  F-Curve ให้ x |  property x |  x จริงหลังประเมิน |  scale.z
--------------------------------------------------------------------------
     1 |         0.0000 |      0.0000 |             0.0000 |    1.750
    11 |         0.3125 |      0.3125 |             0.3125 |    1.750
    21 |         1.0000 |      1.0000 |             1.0000 |    1.750
    31 |         1.6875 |      1.6875 |             1.5000 |    1.750
    41 |         2.0000 |      2.0000 |             1.5000 |    1.750

อ่านตารางนี้ทีละคอลัมน์ แล้วทั้งเล่มจะประกอบร่างพร้อมกัน:

  • คอลัมน์ 2 กับ 3 ตรงกันเสมอ — F-Curve เขียนลง property จริง นี่คือชั้นเดียวที่มีสิทธิ์แตะค่าที่คุณเก็บไว้
  • คอลัมน์ 4 แยกออกจากคอลัมน์ 3 ตั้งแต่เฟรม 31 — พอค่าเกิน 1.5 constraint เข้ามาตัด ค่าใน property ยังเป็น 1.6875 และ 2.0000 ครบถ้วน แต่ผลจริงถูกล็อกไว้ที่ 1.5
  • คอลัมน์ 5 ไม่เปลี่ยนเลยตลอด 41 เฟรม — เพราะ driver อ่านจากตัวควบคุมที่ไม่ได้ขยับ เวลาผ่านไปเท่าไหร่ก็ไม่เกี่ยว

13.2 ลำดับมาจากไหน — และทำไมมันไม่ใช่ท่อคงที่

ตรงนี้คือจุดที่คนอธิบายผิดกันบ่อยที่สุด รวมถึงคำอธิบายที่หาเจอทั่วไปบนอินเทอร์เน็ต

ความเข้าใจผิดคือคิดว่า Blender มี ท่อคงที่ — ทำอย่างนี้ก่อน แล้วอย่างนั้น เรียงเหมือนกันทุกครั้ง ถ้าเป็นแบบนั้นจริง คุณจะท่องลำดับได้ แต่คุณจะทำนายผิดทันทีที่เจอไฟล์ที่ต่อของกันคนละแบบ

ของจริง: depsgraph สร้างลำดับขึ้นใหม่จากคำถามเดียว — ใครอ่านค่าจากใคร

ไม่มีใครได้คิวเพราะเป็นชนิดนั้น ทุกคนได้คิวเพราะมีคนรอค่าของตัวเองอยู่

ลองพิสูจน์ด้วยตาเอง สามการทดลอง:

หนึ่ง — ให้ driver อ่านค่าที่ถูกคีย์

  frame  1: F-Curve ให้ Ctrl.x =  0.0000  ->  driver ได้ scale.z =  1.0000
  frame 13: F-Curve ให้ Ctrl.x =  5.0000  ->  driver ได้ scale.z =  6.0000
  frame 25: F-Curve ให้ Ctrl.x = 10.0000  ->  driver ได้ scale.z = 11.0000

driver เห็นค่าของเฟรมปัจจุบัน ไม่ใช่ค่าเฟรมก่อน ⇒ มันถูกประเมินหลัง F-Curve ที่ป้อนมัน เพราะมันรออยู่

สอง — ให้ constraint เล็งเป้าที่ถูก driver ขับ

  Target.x หลังประเมิน   = 7.0000   ← มาจาก driver ล้วน ไม่มี keyframe เลย
  Follower.x หลังประเมิน = 7.0000   ← constraint เห็นผลของ driver แล้ว

⇒ constraint ตัวนี้ถูกประเมินหลัง driver ตัวนั้น ด้วยเหตุผลเดียวกันเป๊ะ

สาม — constraint สองตัวเดิม สลับลำดับใน stack

  Limit(max 2) แล้วค่อย CopyLocation(9) -> x = 9.0000
  CopyLocation(9) แล้วค่อย Limit(max 2) -> x = 2.0000

ของสองชิ้นเดิม ค่าเดิม ได้คนละคำตอบ เพราะตัวล่างรับผลของตัวบนมาทำต่อ

13.3 กฎที่เชื่อได้ แทนลำดับที่ท่องไว้

จากสามการทดลองข้างบน สิ่งที่เชื่อได้จริงมีสามข้อ ไม่ใช่ลำดับ 1-2-3-4:

  1. มีที่เดียวที่เขียนลง property จริง คือ F-Curve — คอลัมน์ 2 กับ 3 ในตารางบทนี้ตรงกันทุกแถวเพราะเหตุนี้ ชั้นอื่นทั้งหมดทำงานบนผลลัพธ์ ไม่ใช่บนค่าที่คุณเก็บ
  2. ใครอ่านค่าจากใคร คนนั้นมาทีหลัง — ครอบทั้ง driver, constraint, พ่อ-ลูก และ modifier โดยไม่ต้องจำแยก
  3. ในกองเดียวกัน ลำดับบนลงล่างคือลำดับจริง — constraint stack และ modifier stack เรียงได้ เพราะลำดับมีความหมาย

ค่าที่คุณเก็บ กับค่าที่คุณเห็น เป็นคนละอย่าง และมีชั้นคั่นระหว่างมันเสมอ

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

13.4 ปริศนาทั้งเล่ม ไขพร้อมกัน

ทุกข้อใช้กฎข้อ 1 กับข้อ 2 ข้างบนตอบได้หมด:

  • ทำไมคีย์ไว้ (0,0,0) แล้วของไปอยู่ (7,3,2)? — constraint อ่านผลลัพธ์ของ property นั้นเพื่อทำงาน มันจึงมาทีหลัง (กฎ 2) และมันไม่มีสิทธิ์เขียนกลับ (กฎ 1) ค่าที่คุณคีย์จึงอยู่ครบ
  • ทำไม IK แก้มุมได้ 78.4 องศา แต่ค่าใน property ยัง 0? — IK คือ constraint ตัวหนึ่ง กฎ 1 ห้ามมันเขียนกลับเหมือนกันทุกตัว ไม่มีข้อยกเว้น
  • ทำไมหมุนพ่อแล้วลูกย้ายที่ทั้งที่ค่าลูกเป็นศูนย์? — ลูกอ่านเมทริกซ์ของพ่อ (กฎ 2) พ่อจึงเสร็จก่อนเสมอ และค่าที่เก็บของลูกไม่ต้องเปลี่ยนเลย
  • ทำไม weight ที่ดูถูกแล้วเนื้อยังบิด? — Armature modifier อ่านตำแหน่งกระดูกหลัง constraint ทำงานแล้ว ถ้ากระดูกไปผิดที่ก่อนหน้านั้น เนื้อก็บิดตามอย่างซื่อสัตย์ ปัญหาไม่ได้อยู่ที่ weight
  • ทำไม driver ที่อ้างวนกันถึงพัง? — นี่คือคำตอบที่ชัดที่สุดว่าทำไมมันเป็นกราฟไม่ใช่ท่อ: ถ้า A รอ B และ B รอ A ก็ไม่มีลำดับไหนที่ถูก ท่อคงที่จะไม่มีปัญหานี้เลย เพราะมันไม่เคยถามว่าใครรอใคร

13.5 วิธีไล่ปัญหาที่ใช้ได้จริงตั้งแต่วันนี้

เมื่อ "ของไม่เป็นอย่างที่คิด" ให้ไล่ถอยจากปลายทางกลับมาต้นทาง เพราะคนที่ได้คิวหลังคือคนที่ชนะ:

#ดูอะไรเปิดดูที่ไหน
1มี modifier อยู่ไหม (Armature, Lattice)แผงขวา แท็บรูปประแจ
2มี constraint อยู่ไหม และเรียงยังไงแผงขวา แท็บรูปโซ่ (กระดูกใช้แท็บโซ่ที่มีรูปกระดูก)
3วัตถุนี้มีพ่อหรือเปล่าแผงขวา แท็บสี่เหลี่ยมส้ม › Relations › Parent
4ค่าใน property คืออะไร ตรงกับที่คีย์ไหมกด N ในวิวพอร์ต แถบด้านขวาจะเด้งออกมา
5มี driver อยู่บนช่องนั้นไหมช่องที่มี driver จะเป็นสีม่วง — คีย์เฟรมเป็นสีเหลือง/เขียว

💡 จำสีให้แม่นแล้วประหยัดเวลาได้มาก: เขียว = มีคีย์เฟรมแต่ไม่ใช่เฟรมนี้ · เหลือง = มีคีย์ตรงเฟรมนี้พอดี · ม่วง = ถูก driver ขับอยู่ ไม่ใช่คีย์เฟรม สีบอกให้รู้ตั้งแต่ยังไม่ต้องคลิกว่าใครเป็นเจ้าของค่านั้น

เคล็ดลับ: ถ้าเปิดดูแล้วค่าในช่องถูกต้อง แต่ของไปอยู่ผิดที่ แปลว่าปัญหาไม่ได้อยู่ที่ข้อ 4–5 เลย ให้ไปดูข้อ 1–3 ทันที — ค่าถูกแต่ผลผิด คือนิยามของ "มีชั้นอื่นทำงานทับอยู่" ไม่ต้องเสียเวลาไล่ keyframe

ส่งท้าย — และไปต่อตรงไหน#

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

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

14.1 ของที่เหลือในเอกสารต้นทาง และมันอยู่ตรงไหนของแผนที่

เอกสาร Animation and Rigging ของ Blender ยังมีอีกหลายหัวข้อที่เล่มนี้ไม่ลงลึก ตอนนี้คุณมีแผนที่พอจะวางมันเองได้แล้ว:

หัวข้อมันคืออะไรในภาษาของเล่มนี้
Markersป้ายบอกเวลาบนไทม์ไลน์ ไม่ได้ให้ค่าอะไรกับใคร เป็นเครื่องมือของคนทำงาน ไม่ใช่ของกลไก
Motion Pathsเส้นที่วาดจากการประเมินหลายเฟรมมาต่อกัน — เป็นการ แสดงผล ไม่ใช่แหล่งของค่า
In-Betweensเครื่องมือช่วยปักคีย์ระหว่างสองคีย์ ทำงานอยู่ที่ชั้น F-Curve ล้วน ๆ
NLAชั้นที่เอา Action หลายก้อนมาเรียงและผสมกัน — มันตัดสินว่า F-Curve ชุดไหนถูกใช้ จึงอยู่ ต้นทาง ของชั้นที่เขียนลง property (กฎข้อ 1 บทที่ 13)
Bone Constraints ตัวอื่น ๆเป็น constraint ทั้งหมด จึงอยู่ใต้กฎข้อ 1 เหมือนกันทุกตัว — ทำงานบนผลลัพธ์ ไม่เขียนกลับลง property ไม่มีข้อยกเว้น
Keying Setsทางลัดสำหรับคีย์หลาย data path พร้อมกัน — เป็นเรื่องความสะดวก ไม่ใช่กลไกใหม่

สังเกตว่าไม่มีอันไหนเลยที่ต้องใช้กฎใหม่ — ทุกอันตอบได้ด้วยกฎสามข้อในบทที่ 13 คือ ใครเขียนลง property ได้ · ใครอ่านค่าจากใคร · และในกองเดียวกันเรียงยังไง นั่นคือสัญญาณว่าแบบจำลองความคิดที่คุณมีตอนนี้ ใหญ่พอจะรับของที่เหลือได้

14.2 ลองด้วยตัวเองต่อ

ทุกผลรันในเล่มนี้ทำซ้ำได้จากเครื่องคุณเอง โดยไม่ต้องเปิดหน้าต่าง Blender เลย:

blender --background --factory-startup --python script.py

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

อ่านต่อ: HyperFrames Handbook — เมื่อคุณเข้าใจแล้วว่าเฟรมคือค่าที่ถูกคำนวณใหม่ทุกครั้ง คำถามถัดไปคือ ต้องลาก 3D เข้ามาเมื่อไหร่ และเมื่อไหร่เขียนเป็น HTML ก็พอ เล่มนั้นตอบด้านที่เบากว่าของคำถามเดียวกัน

If this was useful —buy me a coffee