หมุนวัตถุรอบแกนคงที่


12

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

นี่คือภาพของสิ่งที่เกิดขึ้น:

ป้อนคำอธิบายรูปภาพที่นี่

แกนสีน้ำเงินแสดงถึงแกนคงที่ของฉัน รูปภาพหน้าจอที่มีแกนสีน้ำเงินคงที่นี้ นี่คือสิ่งที่ฉันต้องการให้วัตถุหมุนในความสัมพันธ์กับ สิ่งที่เกิดขึ้นคือสีแดง

นี่คือสิ่งที่ฉันรู้:

  1. การหมุนรอบแรกรอบ Y (0, 1, 0) ทำให้โมเดลย้ายจากพื้นที่สีน้ำเงิน (เรียกพื้นที่นี้ A) ไปยังพื้นที่อื่น (เรียกพื้นที่นี้ B)
  2. พยายามหมุนอีกครั้งโดยใช้เวกเตอร์ (1, 0, 0) หมุนรอบแกน x ในอวกาศ B ไม่ได้อยู่ในอวกาศ A ซึ่งไม่ใช่สิ่งที่ฉันตั้งใจจะทำ

นี่คือสิ่งที่ฉันพยายามให้ในสิ่งที่ฉัน (คิด) ฉันรู้ (ออกจาก coord W เพราะความกะทัดรัด):

  1. หมุนไปรอบ ๆ Y (0, 1, 0) โดยใช้ Quaternion
  2. แปลงการหมุน Y Quaternion เป็นเมทริกซ์
  3. คูณเมทริกซ์การหมุน Y ด้วยแกนคงที่ x เวกเตอร์ (1, 0, 0) เพื่อให้ได้แกน X สัมพันธ์กับพื้นที่ใหม่
  4. หมุน X เวกเตอร์ใหม่โดยใช้ Quaternion

นี่คือรหัส:

private float[] rotationMatrix() {

    final float[] xAxis = {1f, 0f, 0f, 1f};
    final float[] yAxis = {0f, 1f, 0f, 1f};
    float[] rotationY = Quaternion.fromAxisAngle(yAxis, -angleX).toMatrix();

    // multiply x axis by rotationY to put it in object space
    float[] xAxisObjectSpace = new float[4];
    multiplyMV(xAxisObjectSpace, 0, rotationY, 0, xAxis, 0);

    float[] rotationX = Quaternion.fromAxisAngle(xAxisObjectSpace, -angleY).toMatrix();

    float[] rotationMatrix = new float[16];
    multiplyMM(rotationMatrix, 0, rotationY, 0, rotationX, 0);
    return rotationMatrix;
  }

นี่ไม่ทำงานอย่างที่ฉันคาดไว้ การหมุนดูเหมือนจะทำงานได้ แต่ในบางจุดการเคลื่อนที่ในแนวนอนไม่ได้หมุนรอบแกน Y มันจะหมุนรอบแกน Z

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

private float[] getMvpMatrix() {
    // translates the object to where we can see it
    final float[] translationMatrix = new float[16];
    setIdentityM(translationMatrix, 0);
    translateM(translationMatrix, 0, translationMatrix, 0, 0f, 0f, -2);

    float[] rotationMatrix = rotationMatrix();

    // centers the object
    final float[] centeringMatrix = new float[16];
    setIdentityM(centeringMatrix, 0);
    float moveX = (extents.max[0] + extents.min[0]) / 2f;
    float moveY = (extents.max[1] + extents.min[1]) / 2f;
    float moveZ = (extents.max[2] + extents.min[2]) / 2f;
    translateM(centeringMatrix, 0, //
      -moveX, //
      -moveY, //
      -moveZ //
    );

    // apply the translations/rotations
    final float[] modelMatrix = new float[16];
    multiplyMM(modelMatrix, 0, translationMatrix, 0, rotationMatrix, 0);
    multiplyMM(modelMatrix, 0, modelMatrix, 0, centeringMatrix, 0);

    final float[] mvpMatrix = new float[16];
    multiplyMM(mvpMatrix, 0, projectionMatrix, 0, modelMatrix, 0);
    return mvpMatrix;
  }

ฉันติดอยู่กับเรื่องนี้สองสามวัน ช่วยชื่นชมมาก

================================================== ================

UPDATE:

การทำให้สิ่งนี้ทำงานใน Unity นั้นตรงไปตรงมา นี่คือรหัสบางส่วนที่หมุนลูกบาศก์เป็นศูนย์กลางที่จุดกำเนิด:

public class CubeController : MonoBehaviour {

    Vector3 xAxis = new Vector3 (1f, 0f, 0f);
    Vector3 yAxis = new Vector3 (0f, 1f, 0f);

    // Update is called once per frame
    void FixedUpdate () {
        float horizontal = Input.GetAxis ("Horizontal");
        float vertical = Input.GetAxis ("Vertical");

        transform.Rotate (xAxis, vertical, Space.World);
        transform.Rotate (yAxis, -horizontal, Space.World);
    }
}

ส่วนที่ทำให้การหมุนทำงานตามที่ฉันคาดไว้คือSpace.Worldพารามิเตอร์ของRotateฟังก์ชันในการแปลง

ถ้าฉันสามารถใช้ Unity ได้ฉันก็ต้องใช้รหัสพฤติกรรมนี้ด้วยตัวเอง


1
คำตอบของฉันที่นี่gamedev.stackexchange.com/questions/67199/ .. อาจช่วยคุณได้ ..
concept3d

ฉันเข้าใจแนวคิดที่อยู่เบื้องหลังคำตอบของคุณ แต่วิธีการใช้คือหนีฉัน
Christopher Perry

หากคุณตรวจสอบคำตอบอื่น ๆ คำตอบ syntac ใช้ความคิดที่ฉันอธิบาย
แนวคิด 3

ไม่เลยมันหมุนรอบหลาย ๆ รอบเกี่ยวกับแกนที่ต่างกัน คุณแนะนำให้ทำการหมุนครั้งเดียว
Christopher Perry

คำตอบ:


3

ปัญหาที่มีของคุณเรียกว่าล็อค Gimble ฉันคิดว่าสิ่งที่คุณต้องการทำเรียกว่าการหมุนอาร์บอล คณิตศาสตร์สำหรับอาร์บอลมีความซับซ้อน

วิธีที่ง่ายกว่าในการทำคือหาเวกเตอร์ 2d ตั้งฉากกับการปัด 2d บนหน้าจอ

นำเวกเตอร์และฉายลงบนกล้องใกล้กับเครื่องบินเพื่อรับเวกเตอร์ 3 มิติในอวกาศโลก พื้นที่หน้าจอไปยังพื้นที่โลก

จากนั้นสร้าง quaternion ด้วยเวกเตอร์นี้แล้วคูณมันลงใน gameobject อาจมีการเปลี่ยนแปลง slurp หรือ lerp บางอย่าง

แก้ไข:

ตัวอย่างความสามัคคี: ในตัวอย่างความสามัคคีสถานะภายในของการหมุนของเกมโครงการคือ quaternion ไม่ใช่เมทริกซ์ วิธีการ transform.rotation สร้าง quaternion ขึ้นอยู่กับเวกเตอร์และมุมที่มีให้และคูณด้วย quaternion ที่มี gameobjects การหมุน quaternion มันสร้างเมทริกซ์การหมุนสำหรับการเรนเดอร์หรือฟิสิกส์ในภายหลังเท่านั้น Quaternions เป็นสารเติมแต่งและหลีกเลี่ยงการล็อก gimble

รหัสของคุณควรมีลักษณะเช่นนี้:

private float[] rotationMatrix() {

    final float[] xAxis = {1f, 0f, 0f, 1f};
    final float[] yAxis = {0f, 1f, 0f, 1f};

    Quaternion qY = Quaternion.fromAxisAngle(yAxis, angleX);
    Quaternion qX = Quaternion.fromAxisAngle(xAxis, -angleY);

    return (qX * qY).getMatrix(); // should probably represent the gameobjects rotation as a quaternion(not a matrix) and multiply all 3 quaternions before generating the matrix. 
  }

การสอน OpenGL สำหรับการหมุน ArcBall


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

ฉันหวังว่าคุณจะคิดออก ในระยะสั้น Quaternions สามารถคูณด้วยกันเพื่อใช้การหมุน คุณควรสร้างเมทริกซ์เมื่อสิ้นสุดการคำนวณการหมุนทั้งหมด นอกจากนี้ xQ * yQ ไม่เท่ากับ yQ * xQ Quaternions ไม่ใช่การสับเปลี่ยนเช่น Christopher Perry กล่าว
Anthony Raimondo

ฉันใส่ทั้งหมดของรหัสของฉันที่นี่ ฉันรู้สึกว่าฉันได้ลองทุกอย่างแล้ว บางทีตาของคนอื่นในเรื่องนี้อาจทำให้ฉันเข้าใจผิด
Christopher Perry

ฉันไม่ยอมรับเลยอัลกอริทึมการแลกเปลี่ยนสแต็กอัตโนมัติกำหนดคะแนนให้คุณ : /
Christopher Perry

ฉันขอโทษสำหรับความอยุติธรรมนี้
Anthony Raimondo

3

ฉันสามารถได้การหมุนตามที่คาดหวังโดยการหมุนเมทริกซ์การหมุนสะสม

setIdentityM(currentRotation, 0);
rotateM(currentRotation, 0, angleY, 0, 1, 0);
rotateM(currentRotation, 0, angleX, 1, 0, 0);

// Multiply the current rotation by the accumulated rotation,
// and then set the accumulated rotation to the result.
multiplyMM(temporaryMatrix, 0, currentRotation, 0, accumulatedRotation, 0);
arraycopy(temporaryMatrix, 0, accumulatedRotation, 0, 16);

1

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

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

เพียงใช้ quaternion เอกลักษณ์เป็นค่าเริ่มต้นและทุกครั้งที่ผู้ใช้เลื่อนหน้าจอที่คุณหมุน quaternion ของคุณด้วยQuaternion.fromAxisAngle(yAxis, -angleX)รหัส ใช้เสมอ (1,0,0,1) สำหรับการหมุน x และ (0,1,0,1) สำหรับการหมุน y

static final float[] xAxis = {1f, 0f, 0f, 1f};
static final float[] yAxis = {0f, 1f, 0f, 1f};

private void rotateObject(float angleX, float angleY) {
  Quaternion rotationY = Quaternion.fromAxisAngle(yAxis, -angleX);
  Quaternion rotationX = Quaternion.fromAxisAngle(xAxis, -angleY);

  myRotation = myRotation.rotate(rotationY).rotate(rotationX).normalize();
}
private float[] rotationMatrix() {
  return myRotation.toMatrix();
}

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


ฉันได้รับพฤติกรรมเดียวกันนี้
Christopher Perry

1
@ChristopherPerry ลองmyRotation = rotationX.rotate(rotationY).rotate(myRotation).normalize()สลับลำดับการหมุนกลับ: พวกมันไม่ใช่การสลับสับเปลี่ยนดังนั้นลำดับที่คุณทำจะนับ เพิ่มความคิดเห็นอื่นด้วยกรอบ / ภาษาของคุณหากไม่ได้ผลและไม่ควรเพิ่มเข้าไปอีก
Daniel Carlsson

แต่นั่นก็ไม่ได้ผลเช่นกันฉันก็มีพฤติกรรมแบบเดียวกัน
Christopher Perry

ฉันใส่รหัสทั้งหมดของฉันที่นี่
Christopher Perry
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.