Putar objek di sekitar sumbu tetap


12

Saya mencoba untuk membiarkan pengguna aplikasi saya memutar objek 3D yang ditarik di tengah layar dengan menyeret jari mereka di layar. Gerakan horizontal di layar berarti rotasi di sekitar sumbu Y yang tetap, dan gerakan vertikal berarti rotasi di sekitar sumbu X. Masalah yang saya alami adalah bahwa jika saya hanya mengizinkan rotasi di sekitar satu sumbu objek berputar dengan baik, tetapi segera setelah saya memperkenalkan rotasi kedua objek tidak berputar seperti yang diharapkan.

Ini adalah gambar dari apa yang terjadi:

masukkan deskripsi gambar di sini

Sumbu biru mewakili sumbu tetap saya. Bayangkan layar memiliki sumbu biru tetap ini. Inilah yang saya ingin objek untuk memutar sehubungan dengan. Apa yang terjadi berwarna merah.

Inilah yang saya tahu:

  1. Rotasi pertama di sekitar Y (0, 1, 0) menyebabkan model bergerak dari ruang biru (panggil ruang ini A) ke ruang lain (sebut ruang ini B)
  2. Mencoba memutar lagi menggunakan vektor (1, 0, 0) berputar di sekitar sumbu x di ruang B TIDAK di ruang A yang bukan apa yang saya maksudkan.

Inilah yang saya coba, mengingat apa yang saya (pikirkan) saya tahu (tidak mengikutkan kata W untuk singkatnya):

  1. Putar pertama di sekitar Y (0, 1, 0) menggunakan Quaternion.
  2. Konversi rotasi Y Quaternion ke Matrix.
  3. Kalikan matriks rotasi Y dengan sumbu tetap saya x Vektor (1, 0, 0) untuk mendapatkan sumbu X dalam kaitannya dengan ruang baru.
  4. Putar sekitar X Vector baru ini menggunakan Quaternion.

Berikut kodenya:

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;
  }

Ini tidak berfungsi seperti yang saya harapkan. Rotasi tampaknya bekerja, tetapi pada beberapa titik gerakan horizontal tidak berputar tentang sumbu Y, tampaknya berputar tentang sumbu Z.

Saya tidak yakin apakah pemahaman saya salah, atau jika ada hal lain yang menyebabkan masalah. Saya memiliki beberapa transformasi lain yang saya lakukan pada objek selain rotasi. Saya memindahkan objek ke tengah sebelum menerapkan rotasi. Saya memutarnya menggunakan matriks yang dikembalikan dari fungsi saya di atas, lalu saya menerjemahkan -2 ke arah Z sehingga saya bisa melihat objek. Saya tidak berpikir ini mengacaukan rotasi saya, tetapi di sini adalah kode untuk itu:

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;
  }

Saya sudah terjebak dalam hal ini selama beberapa hari. Bantuan sangat dihargai.

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

MEMPERBARUI:

Membuat ini berfungsi di Unity sangatlah mudah. Berikut ini beberapa kode yang merotasi sebuah kubus yang berpusat pada titik asal:

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);
    }
}

Bagian yang membuat rotasi berperilaku seperti yang saya harapkan adalah Space.Worldparameter Rotatefungsi pada transformasi.

Jika saya bisa menggunakan Unity saya akan, sayangnya saya harus kode perilaku ini sendiri.


1
Jawaban saya di sini gamedev.stackexchange.com/questions/67199/… mungkin membantu Anda ..
concept3d

Saya mengerti konsep di balik jawaban Anda, tetapi bagaimana cara mengimplementasikannya lolos dari saya.
Christopher Perry

Jika Anda memeriksa jawaban lain, sintaks jawaban mengimplementasikan gagasan yang saya jelaskan.
concept3d

Tidak, itu melakukan banyak rotasi tentang sumbu yang berbeda. Anda menyarankan melakukan rotasi tunggal.
Christopher Perry

Jawaban:


3

Masalah Anda memiliki disebut kunci gimble . Saya pikir apa yang ingin Anda lakukan disebut rotasi busur . Matematika untuk arcball bisa sangat rumit.

Cara yang lebih sederhana untuk melakukannya adalah menemukan vektor 2d tegak lurus terhadap gesekan 2d di layar.

Ambil vektor dan proyeksikan ke kamera di dekat pesawat untuk mendapatkan vektor 3d di ruang dunia. Ruang layar ke ruang Dunia .

Lalu buat angka empat dengan vektor ini dan gandakan ke gameobject. Mungkin dengan beberapa transisi slurp atau lerp.

Edit:

Unity Example: Dalam contoh unity, keadaan internal rotasi objek game adalah angka empat bukan matriks. Metode transform.rotation menghasilkan angka empat berdasarkan vektor dan sudut yang disediakan dan mengalikan angka empat itu dengan angka empat rotasi objek game. Ini hanya menghasilkan matriks rotasi untuk rendering atau fisika pada titik selanjutnya. Kuota adalah aditif dan menghindari kunci gimble.

Kode Anda akan terlihat seperti ini:

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. 
  }

Tutorial ArcBall Rotation Opengl


Saya tidak mendapatkan kunci gimbal, rotasi pertama menggerakkan sumbu sehingga rotasi kedua didasarkan pada sumbu yang dipindahkan. Silakan lihat gambar yang saya berikan.
Christopher Perry

Saya harap Anda menemukan jawabannya. Pendeknya. Kuota dapat dikalikan bersama untuk menerapkan rotasi. Anda seharusnya hanya menghasilkan matriks di akhir semua perhitungan rotasi. Juga xQ * yQ tidak sama dengan yQ * xQ. Kuarter tidak komutatif seperti yang dikatakan Christopher Perry.
Anthony Raimondo

Saya menempatkan SEMUA kode saya di sini . Saya merasa seperti saya sudah mencoba segalanya. Mungkin mata orang lain tentang ini akan menangkap kesalahan saya.
Christopher Perry

Saya tidak menerimanya, menumpuk algoritma pertukaran otomatis memberikan poin kepada Anda. : /
Christopher Perry

Maaf atas ketidakadilan ini.
Anthony Raimondo

3

Saya bisa mendapatkan rotasi yang diharapkan dengan memutar matriks rotasi terakumulasi.

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

Gambar Anda sesuai dengan kode matrixMatrix Anda, dengan memutar sumbu x dengan rotasi y Anda sebelumnya, Anda mendapatkan sumbu x lokal, ketika Anda kemudian memutar objek di sekitar Anda akan mendapatkan hasil yang Anda tunjukkan dalam gambar Anda. Agar rotasi menjadi logis dari sudut pandang pengguna, Anda ingin memutar objek menggunakan sumbu koordinat dunia.

Jika Anda ingin pengguna Anda dapat memutar objek Anda berkali-kali, masuk akal untuk menyimpan rotasi Anda dalam angka empat bukan matriks, seiring waktu beberapa putaran (dan ketidaktepatan titik mengambang) akan menyebabkan matriks terlihat semakin tidak mirip sebuah matriks rotasi, hal yang sama terjadi dalam angka empat tentu saja, tetapi hanya dengan menormalkan angka empat akan membawanya kembali ke rotasi yang baik.

Cukup gunakan angka empat identitas sebagai nilai awal, dan setiap kali pengguna menggesek layar Anda memutar angka empat Anda dengan Quaternion.fromAxisAngle(yAxis, -angleX)kode. Selalu menggunakan (1,0,0,1) untuk rotasi x dan (0,1,0,1) untuk rotasi 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();
}

Karena Anda tidak menyebutkan bahasa atau kerangka kerja tertentu, metode pada Quaternion mungkin disebut sesuatu yang berbeda tentu saja, dan normalisasi tidak diperlukan untuk memanggil itu sering, tetapi karena rotasi berasal dari pengguna menggesek layar mereka tidak akan memperlambat banyak hal dan dengan demikian tidak ada kemungkinan Quaternion menyelinap pergi dari unit angka empat.


Saya mendapatkan perilaku yang sama persis melakukan hal ini.
Christopher Perry

1
@ChristopherPerry Cobalah membalik urutan rotasi: myRotation = rotationX.rotate(rotationY).rotate(myRotation).normalize()Mereka tidak komutatif sehingga urutan yang Anda lakukan diperhitungkan. Tambahkan komentar lain dengan kerangka / bahasa Anda jika itu tidak berhasil dan saya akan menggali lebih dalam lagi.
Daniel Carlsson

Itu juga tidak berhasil, saya mendapatkan perilaku yang sama.
Christopher Perry

Saya menaruh SEMUA kode saya di sini
Christopher Perry
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.