การคูณ
อย่างน้อยในแง่ของความเป็นเอกภาพของการดำเนินงานของ Quaternions ลำดับการคูณที่อธิบายไว้ในคำถามไม่ถูกต้อง สิ่งนี้มีความสำคัญเนื่องจากการหมุนแบบ 3 มิติไม่ได้สลับกัน
ดังนั้นถ้าฉันต้องการหมุนวัตถุโดยrotationChangeเริ่มจากวัตถุcurrentOrientationฉันจะเขียนดังนี้:
Quaternion newOrientation = rotationChange * currentOrientation;
(เช่นการแปลงสแต็คขึ้นไปทางซ้าย - เหมือนกับการประชุมเมทริกซ์ของ Unity การหมุนขวาสุดจะใช้ครั้งแรก / ที่ปลายสุด "โลคอล")
และถ้าฉันต้องการแปลงทิศทางหรือชดเชยเวกเตอร์ด้วยการหมุนฉันจะเขียนดังนี้:
Vector3 rotatedOffsetVector = rotationChange * currentOffsetVector;
(Unity จะสร้างข้อผิดพลาดในการคอมไพล์ถ้าคุณทำตรงกันข้าม)
การผสม
สำหรับกรณีส่วนใหญ่คุณสามารถหลีกเลี่ยงการหมุนแบบ Lerping นั่นเป็นเพราะมุมที่ใช้ "ภายใต้ประทุน" ใน quaternion เป็นครึ่งหนึ่งมุมของการหมุนทำให้มันอย่างมีนัยสำคัญที่ใกล้ชิดกับการประมาณเชิงเส้นของ Lerp กว่าสิ่งที่ต้องการเมทริกซ์ (ซึ่งโดยทั่วไปจะไม่ Lerp กัน!) ตรวจสอบประมาณ 40 นาทีในวิดีโอนี้สำหรับคำอธิบายเพิ่มเติม
กรณีหนึ่งเมื่อคุณต้องการ Slerp จริงๆก็คือเมื่อคุณต้องการความเร็วที่สม่ำเสมอตลอดเวลาเช่นการสอดแทรกระหว่างคีย์เฟรมบนไทม์ไลน์ของภาพเคลื่อนไหว สำหรับกรณีที่คุณสนใจว่าเอาท์พุทอยู่ตรงกลางระหว่างสองอินพุต (เช่นการผสมเลเยอร์ของภาพเคลื่อนไหว) จากนั้น Lerp จะทำหน้าที่ค่อนข้างดี
อะไรนะ?
สินค้า dotสองยูนิท quaternions ให้โคไซน์ของมุมระหว่างพวกเขาเพื่อให้คุณสามารถใช้ผลิตภัณฑ์จุดเป็นตัวชี้วัดของความคล้ายคลึงกันถ้าคุณจะต้องเปรียบเทียบผลัด นี่เป็นสิ่งที่คลุมเครือเล็กน้อยดังนั้นสำหรับรหัสที่อ่านง่ายกว่านี้ฉันมักจะใช้Quaternion.Angle (a, b)แทนซึ่งชัดเจนกว่าว่าเรากำลังเปรียบเทียบมุมในหน่วยที่คุ้นเคย (องศา)
วิธีการอำนวยความสะดวกประเภทนี้ที่ Unity นำเสนอสำหรับควอเทอร์เนียนนั้นมีประโยชน์อย่างยิ่ง ในเกือบทุกโครงการฉันใช้อันนี้อย่างน้อยสองสามครั้ง :
Quaternion.LookRotation(Vector3 forward, Vector3 up)
สิ่งนี้สร้างความเป็น quaternion ที่:
- หมุนโลคัล z + แกนเพื่อชี้ไปตาม
forwardอาร์กิวเมนต์เวกเตอร์
- หมุนแกน y + ในท้องถิ่นเพื่อชี้จุดที่ใกล้ที่สุดให้กับ
upอาร์กิวเมนต์เวกเตอร์หากมีการระบุไว้หรือ(0, 1, 0)หากไม่ได้ระบุไว้
เหตุผลที่ "up" รับเฉพาะ "ใกล้เคียงที่สุด" ก็คือระบบนั้นถูกบ่อนทำลาย หันหน้าไปทาง z + เพื่อforwardใช้สององศาอิสระ (เช่นหันเหและพิทช์) ดังนั้นเรามีอิสระเหลืออีกเพียงหนึ่งระดับ (ม้วน)
ฉันพบว่าบ่อยครั้งที่ฉันต้องการบางสิ่งบางอย่างที่มีคุณสมบัติความถูกต้องตรงข้าม: ฉันต้องการให้ y + ชี้upเฉพาะและ z + ท้องถิ่นเพื่อให้ใกล้เคียงที่สุดforwardกับอิสรภาพที่เหลืออยู่
สิ่งนี้เกิดขึ้นเมื่อพยายามสร้างกรอบพิกัดที่สัมพันธ์กับกล้องสำหรับอินพุตการเคลื่อนไหว: ฉันต้องการทิศทางในเครื่องของฉันให้อยู่ในแนวตั้งฉากกับพื้นหรือพื้นผิวที่เอียงตามปกติดังนั้นอินพุตของฉันจึงไม่ลองอุโมงค์อักขระลงในภูมิประเทศ หรือปล่อยพวกมันออกไป
คุณอาจได้รับสิ่งนี้หากคุณต้องการที่อยู่อาศัยป้อมปืนของรถถังเพื่อเผชิญหน้ากับเป้าหมายโดยไม่ต้องหลุดออกจากตัวถังเมื่อเล็งขึ้น / ลง
เราสามารถสร้างฟังก์ชั่นอำนวยความสะดวกของเราเองเพื่อใช้LookRotationในการยกน้ำหนัก:
Quaternion TurretLookRotation(Vector3 approximateForward, Vector3 exactUp)
{
Quaternion rotateZToUp = Quaternion.LookRotation(exactUp, -approximateForward);
Quaternion rotateYToZ = Quaternion.Euler(90f, 0f, 0f);
return rotateZToUp * rotateYToZ;
}
ที่นี่เราหมุนโลคอล y + เป็น z + และโลคอล z + เป็น y
จากนั้นเราก็หมุน Z + ใหม่กับทิศทางของเราขึ้น (เพื่อผลสุทธิคือ Y ท้องถิ่น + แต้มโดยตรงพร้อมexactUp) และใหม่ y + ใกล้เคียงเป็นไปได้ที่จะเมื่อตะกี้ทิศทางไปข้างหน้า (เพื่อผลสุทธิคือ Z ท้องถิ่น + จุดที่ใกล้ที่สุดเท่าที่เป็นไปได้พร้อมapproximateForward)
อีกวิธีที่สะดวกมีประโยชน์คือQuaternion.RotateTowardsซึ่งฉันมักใช้เช่น:
Quaternion newRotation = Quaternion.RotateTowards(
oldRotation,
targetRotation,
maxDegreesPerSecond * Time.deltaTime
);
สิ่งนี้ช่วยให้เราสามารถเข้าใกล้targetRotationด้วยความเร็วที่สม่ำเสมอและควบคุมได้โดยไม่คำนึงถึงอัตราเฟรม - สำคัญสำหรับการหมุนที่ส่งผลต่อผลลัพธ์ / ความเป็นธรรมของกลไกการเล่นเกม (เช่นการหมุนการเคลื่อนไหวของตัวละครหรือการติดตามป้อมปืน Lerping / Slerping อย่างไร้เดียงสาในสถานการณ์นี้สามารถนำไปสู่กรณีที่การเคลื่อนไหวได้รับปลากะพงขาวที่ความเร็วสูงส่งผลกระทบต่อความสมดุลของเกม (นั่นไม่ได้บอกว่าวิธีการเหล่านี้ผิด - มีวิธีการใช้อย่างถูกต้องโดยไม่เปลี่ยนความเป็นธรรมมันแค่ต้องการการดูแลRotateTowardsให้ทางลัดที่สะดวกสบายที่ดูแลเรื่องนี้ให้เรา)
nทิศทางที่แตกต่างกัน (ทัศนคติ, ท่าทาง, ฯลฯ ) จากนั้นคุณสามารถหาค่าเฉลี่ยได้โดยใช้ตุ้มน้ำหนักทำการสรุป slerp / lerp อย่างมีประสิทธิภาพ นอกจากนี้คุณยังสามารถแปลงควอเทอเรียนเป็นโรเตอร์ซึ่งเทียบเท่ากับการใช้ความเร็วเชิงมุมในระยะเวลาหนึ่งกับร่างกายที่แข็งเกร็ง ดังนั้นคุณสามารถอธิบายการรวมความเร็วเชิงมุมกับควอเทอร์เนียนได้เช่นกัน นอกจากนี้คุณยังสามารถประมาณได้ว่าการหมุนของทิศทางสองแบบนั้นแตกต่างกันอย่างไร