Nesneyi sabit eksen etrafında döndürme


12

Uygulamamın kullanıcısının parmağını ekranda sürükleyerek ekranın ortasına çizilen bir 3B nesneyi döndürmesine izin vermeye çalışıyorum. Ekrandaki yatay bir hareket sabit bir Y ekseni etrafında dönme anlamına gelir ve dikey bir hareket X ekseni etrafında dönme anlamına gelir. Sahip olduğum sorun, sadece bir eksen etrafında dönmeye izin verirseniz, nesne iyi döner, ancak ikinci bir döndürme tanıttığımda nesne beklendiği gibi dönmüyor olmasıdır.

İşte olanların bir resmi:

resim açıklamasını buraya girin

Mavi eksen sabit eksenimi temsil ediyor. Bu sabit mavi eksene sahip ekranı hayal edin. Nesnenin buna göre dönmesini istiyorum. Olan şey kırmızı.

İşte bildiklerim:

  1. Y (0, 1, 0) etrafındaki ilk döndürme, modelin mavi alandan (bu boşluk A olarak adlandırılır) başka bir boşluğa (bu boşluk B olarak adlandırılır) hareket etmesine neden olur
  2. Vektörü (1, 0, 0) kullanarak tekrar döndürmeye çalışmak, B boşluğundaki x ekseni etrafında döner.

Bildiğim (düşündüğüm) göz önüne alındığında, denediğim şey (kısaca W koordinatını bırakarak):

  1. İlk önce bir Kuaterniyon kullanarak Y (0, 1, 0) etrafında döndürün.
  2. Dönüş Y Kuaterniyonunu bir Matrise dönüştürün.
  3. X eksenini yeni boşluğa göre almak için Y dönüş matrisini sabit eksenim x Vector (1, 0, 0) ile çarpın.
  4. Bir Kuvaterniyon kullanarak bu yeni X Vector etrafında döndürün.

İşte kod:

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

Bu beklediğim gibi çalışmıyor. Dönme çalışıyor gibi görünüyor, ancak bir noktada yatay hareket Y ekseni etrafında dönmüyor, Z ekseni etrafında dönüyor gibi görünüyor.

Anlayışımın yanlış olup olmadığından veya başka bir şeyin soruna neden olup olmadığından emin değilim. Döndürmenin yanı sıra nesneye yaptığım başka dönüşümler de var. Döndürme uygulamadan önce nesneyi merkeze taşıyorum. Yukarıdaki fonksiyonumdan döndürülen matrisi kullanarak döndürüyorum, sonra nesneyi görebilmek için Z yönünde -2'ye çeviriyorum. Ben bu benim rotasyonları berbat olduğunu sanmıyorum, ama yine de bunun için kod:

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

Birkaç gündür bunun üzerinde kaldım. Yardım çok takdir edilmektedir.

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

GÜNCELLEME:

Bunu Unity'de çalıştırmak kolaydır. Başlangıçta ortalanmış bir küpü döndüren bazı kodlar şunlardır:

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

Dönüşleri beklediğim gibi yapan kısım , dönüşümdeki fonksiyonun Space.Worldparametresidir Rotate.

Birlik kullanabilseydim, maalesef bu davranışı kendim kodlamalıyım.


1
Cevabım burada gamedev.stackexchange.com/questions/67199/… size yardımcı olabilir ..
concept3d

Cevabınızın arkasındaki kavramı anlıyorum, ama nasıl uygulanacağı kaçıyor.
Christopher Perry

Diğer cevapları işaretlediyseniz syntac answer, açıkladığım fikri uygular.
concept3d

Hayır, farklı eksenler üzerinde birden fazla dönüş yapıyor. Tek bir rotasyon yapmanızı öneririz.
Christopher Perry

Yanıtlar:


3

Sahip olduğunuz soruna hile kilidi denir . Bence aradığınız şeye arcball döndürme deniyor . Arcball için matematik karmaşık olabilir.

Bunu yapmanın daha basit bir yolu, ekrandaki 2d kaydırma hareketine dik bir 2d vektör bulmaktır.

Vektörü alın ve dünya uzayında bir 3B vektör almak için uçağın yanındaki kameraya yansıtın. Ekran alanı Dünya uzayına .

Sonra bu vektör ile bir kuaterniyon oluşturun ve bunu gameobject ile çarpın. Muhtemelen biraz sürtünme veya lerp geçişi ile.

Düzenle:

Birlik Örneği: Birlik örneğinde, gameobjects rotasyonunun iç durumu bir matris değil bir kuaterniyondur. Transform.rotation yöntemi, sağlanan vektör ve açıya göre bir kuaterniyon üretir ve bu kuaternasyonu gameobjects rotasyon kuaterniyonu ile çarpar. Yalnızca sonraki bir noktada renderleme veya fizik için rotasyon matrisini oluşturur. Kuaterniyonlar toplanır ve kıkırdama kilidini önler.

Kodunuz şöyle görünmelidir:

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

ArcBall Rotasyon Opengl Eğitimi


Gimbal kilit almıyorum, ilk dönüş ekseni hareket ettirir, böylece ikinci dönüş hareket edilen eksene dayanır. Lütfen verdiğim resme ikinci bir göz atın.
Christopher Perry

Umarım çözmüşsün. Kısacası. Kuaterniyonlar rotasyon uygulamak için birlikte çoğaltılabilir. Matrisi yalnızca tüm döndürme hesaplamalarının sonunda oluşturmalısınız. Ayrıca xQ * yQ, yQ * xQ değerine eşit değildir. Kuaterniyonlar, Christopher Perry'nin dediği gibi değişmez.
Anthony Raimondo

TÜM kodumu buraya koydum . Her şeyi denemiş gibi hissediyorum. Belki başka birinin gözü benim hatamı yakalar.
Christopher Perry

Kabul etmedim, yığın borsaları algoritması otomatik olarak size puan verdi. : /
Christopher Perry

Bu adaletsizlik için özür dilerim.
Anthony Raimondo

3

Birikmiş rotasyon matrisini döndürerek beklenen rotasyonları elde edebildim.

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

Görüntünüz, rotationMatrix kodunuza karşılık gelir, x eksenini önceki y dönüşünüzle döndürerek, yerel x eksenini alırsınız, daha sonra nesneyi döndürdüğünüzde, görüntünüzde gösterdiğiniz sonucu elde edersiniz. Döndürmenin kullanıcılar açısından mantıklı olması için, nesneyi bunun yerine dünya koordinat eksenini kullanarak döndürmek istersiniz.

Kullanıcının nesnenizi birçok kez döndürmesini istiyorsanız, dönüşünüzü bir matris yerine bir kuaterniyonda saklamak mantıklıysa, zamanla birden fazla dönüş (ve kayan nokta yanlışlıkları) matrisin daha az ve daha az benzemesine neden olur. bir dönme matrisi, tabii ki bir kuaterniyonda olur, ama kuaternionun normalleştirilmesi onu iyi bir dönüşe geri getirir.

Kimlik dördüncülüğünü başlangıç ​​değeri olarak kullanmanız yeterlidir ve kullanıcı ekranı her kaydırdığında kuaterniyonunuzu Quaternion.fromAxisAngle(yAxis, -angleX)kodla döndürürsünüz . Her zaman x dönüş için (1,0,0,1) ve y dönüş için (0,1,0,1) kullanın.

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

Dili veya belirli bir çerçeveyi belirtmediğiniz için, Quaternion'daki yöntemlere elbette farklı bir şey denilebilir ve bunu sık sık çağırmak için normalleştirme gerekli değildir, ancak döndürme, ekranı kaydıran bir kullanıcıdan geldiğinden, şeyleri çok yavaşlatıyor ve bu şekilde Kuaterniyonun birim kuaterniyondan kayma şansı yok.


Bunu yaparken aynı davranışı alıyorum.
Christopher Perry

1
@ChristopherPerry Döndürme sırasını tersine çevirmeyi deneyin: myRotation = rotationX.rotate(rotationY).rotate(myRotation).normalize()Değişmezler, bu nedenle sizin yaptığınız sipariş önemlidir. Çerçeveniz / dilinizle işe yaramadıysa başka bir yorum ekleyin ve biraz daha kazacağım.
Daniel Carlsson

Bu da işe yaramıyor, aynı davranışı alıyorum.
Christopher Perry

Kodumu TÜMÜ buraya
Christopher Perry
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.