Матрица вращения 3D деформируется с течением времени в Processing / Java

Я работаю над проектом, в котором я хочу создать трехмерную сетку для представления определенного количества данных.

Чтобы создать эту сетку, я хочу использовать матрицы преобразования, поэтому я создал класс, основанный на математических алгоритмах, найденных на нескольких веб-сайтах.

Кажется, что все работает, масштабирование / перемещение, но как только я поворачиваю сетку по оси x, она начинает деформироваться после 2-3 полных оборотов. Такое ощущение, что мои значения масштаба увеличиваются, что трансформирует мою сетку. Я борюсь с этой проблемой пару дней, но не могу понять, что не так.

Чтобы прояснить ситуацию, вы можете загрузить мою полную установку здесь.

Я определил координаты коробки и пропустил их через матрицу преобразования перед записью на экран

Это формула поворота моего объекта

    void appendRotation(float inXAngle, float inYAngle, float inZAngle, PVector inPivot ) {

    boolean setPivot = false;

    if (inPivot.x != 0 || inPivot.y != 0 || inPivot.z != 0) {
        setPivot = true;
    }

    // If a setPivot = true, translate the position
    if (setPivot) {

        // Translations for the different axises need to be set different
        if (inPivot.x != 0) { this.appendTranslation(inPivot.x,0,0); }
        if (inPivot.y != 0) { this.appendTranslation(0,inPivot.y,0); }
        if (inPivot.z != 0) { this.appendTranslation(0,0,inPivot.z); }

    }

    // Create a rotationmatrix
    Matrix3D rotationMatrix = new Matrix3D();

    // xsin en xcos
    float xSinCal = sin(radians(inXAngle));
    float xCosCal = cos(radians(inXAngle));      
    // ysin en ycos
    float ySinCal = sin(radians(inYAngle));
    float yCosCal = cos(radians(inYAngle));     
    // zsin en zcos
    float zSinCal = sin(radians(inZAngle));
    float zCosCal = cos(radians(inZAngle));           

    // Rotate around x
    rotationMatrix.setIdentity();
    // --
    rotationMatrix.matrix[1][1] = xCosCal;
    rotationMatrix.matrix[1][2] = xSinCal;
    rotationMatrix.matrix[2][1] = -xSinCal;
    rotationMatrix.matrix[2][2] = xCosCal;
    // Add rotation to the basis matrix
    this.multiplyWith(rotationMatrix);

    // Rotate around y
    rotationMatrix.setIdentity();
    // --
    rotationMatrix.matrix[0][0] = yCosCal;
    rotationMatrix.matrix[0][2] = -ySinCal;
    rotationMatrix.matrix[2][0] = ySinCal;
    rotationMatrix.matrix[2][2] = yCosCal;
    // Add rotation to the basis matrix
    this.multiplyWith(rotationMatrix);

    // Rotate around z
    rotationMatrix.setIdentity();
    // --
    rotationMatrix.matrix[0][0] = zCosCal;
    rotationMatrix.matrix[0][1] = zSinCal;
    rotationMatrix.matrix[1][0] = -zSinCal;
    rotationMatrix.matrix[1][1] = zCosCal;
    // Add rotation to the basis matrix
    this.multiplyWith(rotationMatrix);

    // Untranslate the position
    if (setPivot) {

        // Translations for the different axises need to be set different
        if (inPivot.x != 0) { this.appendTranslation(-inPivot.x,0,0); }
        if (inPivot.y != 0) { this.appendTranslation(0,-inPivot.y,0); }
        if (inPivot.z != 0) { this.appendTranslation(0,0,-inPivot.z); }

    }

}

У кого-нибудь есть ключ?


person Muhneer    schedule 26.04.2011    source источник
comment
Что вы имеете в виду, когда говорите, что он начинает деформироваться после 2–3 полных оборотов? Сколько это умножений матриц? Достаточно приблизительной оценки.   -  person Ali    schedule 27.04.2011


Ответы (5)


Вы никогда не захотите преобразовывать матрицы кумулятивно. Это внесет ошибку в ваши матрицы и вызовет такие проблемы, как масштабирование или перекос ортогональных компонентов.

Правильный метод - отслеживать совокупные углы тангажа, рыскания и крена. Затем при каждом обновлении восстанавливайте матрицу преобразования с этих углов.

person don    schedule 26.04.2011

Если есть возможность: избегайте умножения матриц вращения. Следите за совокупным вращением и вычисляйте новую матрицу вращения на каждом шаге.

Если невозможно избежать умножения матриц вращения, перенормируйте их (стр. 16). У меня он отлично работает для более чем 10 тысяч умножений.

Однако я подозреваю, что это вам не поможет, числовые ошибки обычно требуют более 2 шагов, чтобы проявить себя. Мне кажется, причина вашей проблемы в другом.

Рыскание, тангаж и крен не подходят для произвольных поворотов. Углы Эйлера страдают особенностями и нестабильностью. Посмотрите на 38:25 (презентация Дэвида Сакса)

http://www.youtube.com/watch?v=C7JQ7Rpwn2k

Удачи!

person Ali    schedule 26.04.2011
comment
Спасибо за ссылки, нормализация также является интересным аспектом, чтобы все было в порядке. На данный момент я использую PMatrix3D, но собираюсь глубже погрузиться в эти произвольные методы вращения. - person Muhneer; 28.04.2011

Как упоминает @don, старайтесь избегать кумулятивных преобразований, так как вы можете столкнуться со всевозможными проблемами. Вращение по одной оси за раз может привести к проблемам с Gimbal Lock. Попробуйте сделать все вращения за один присест.

Также помните, что Processing поставляется с собственным классом Matrix3D под названием PMatrix3D с rotate (), который принимает угол (в радианах) и значения x, y, z для оси вращения.

Вот пример функции, которая будет вращать кучу PVectors:

PVector[] rotateVerts(PVector[] verts,float angle,PVector axis){
  int vl = verts.length;
  PVector[] clone = new PVector[vl];
  for(int i = 0; i<vl;i++) clone[i] = verts[i].get();
  //rotate using a matrix
  PMatrix3D rMat = new PMatrix3D();
  rMat.rotate(angle,axis.x,axis.y,axis.z);
  PVector[] dst = new PVector[vl];
  for(int i = 0; i<vl;i++) {
    dst[i] = new PVector();
    rMat.mult(clone[i],dst[i]);
  }
  return dst;
}

а вот пример его использования.

матрица вращения

HTH

person George Profenza    schedule 26.04.2011
comment
Ах, это очень полезно, по какой-то причине я проигнорировал этот класс, но он решает большинство моих проблем. идеально! - person Muhneer; 28.04.2011

Снимок в темноте: я не знаю правил или названия используемого вами языка программирования, но эта процедура выглядит подозрительно:

void setIdentity() {  
    this.matrix = identityMatrix;
}

Вы уверены, что делаете копию identityMatrix? Если это просто ссылка, которую вы копируете, тогда identityMatrix будет изменен более поздними операциями, и вскоре ничего не будет иметь смысла.

person antonakos    schedule 26.04.2011

Хотя предложенная перенормировка матрицы, вероятно, хорошо работает на практике, с математической точки зрения она немного специфична. Лучший способ сделать это - представить совокупные повороты с помощью кватернионов, которые преобразуются в матрицу вращения только при применении. Кватернионы также будут медленно отклоняться от ортогональности (хотя и медленнее), но важно то, что они имеют четко определенную перенормировку.

Хорошей стартовой информацией для реализации этого может быть:

Полезным академическим справочником может быть:

  • К. Шумейк, «Анимация вращения с помощью кватернионных кривых», ACM SIGGRAPH Comput. График., Т. 19, нет. 3. С. 245–254, 1985. DOI: 10.1145 / 325165.325242
person Ludvig    schedule 13.01.2015