OpenGL ArcBall для вращающейся сетки - PullRequest
0 голосов
/ 03 февраля 2019

Я использую устаревший OpenGL для рисования меша.Сейчас я пытаюсь реализовать класс arcball, чтобы вращать объект с помощью мыши.Однако, когда я двигаю мышь, объект либо не вращается, либо поворачивается на слишком большой угол.

Этот метод вызывается при нажатии мыши:

void ArcBall::startRotation(int xPos, int yPos) {
    int x = xPos - context->getWidth() / 2;
    int y = context->getHeight() / 2 - yPos;
    startVector = ArcBall::mapCoordinates(x, y).normalized();
    endVector = startVector;
    rotating = true;
}

Этот метод предназначен для простого сопоставления координат мыши по центру экрана и их сопоставления с ограничивающей сферой, в результате чего получается начальный вектор

Этот метод вызывается, когда мышьдвижения:

void ArcBall::updateRotation(int xPos, int yPos) {
    int x = xPos - context->getWidth() / 2;
    int y = context->getHeight() / 2 - yPos;
    endVector = mapCoordinates(x, y).normalized();
    rotationAxis = QVector3D::crossProduct(endVector, startVector).normalized();
    angle  = (float)qRadiansToDegrees(acos(QVector3D::dotProduct(startVector, endVector)));
    rotation.rotate(angle, rotationAxis.x(), rotationAxis.y(), rotationAxis.z());
    startVector = endVector;
}

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

Затем я использую

glMultMatrixf(ArcBall::rotation.data());

, чтобы применить вращение

1 Ответ

0 голосов
/ 03 февраля 2019

Я рекомендую сохранить положение мыши в точке, где вы изначально щелкнули в представлении.Рассчитать количество движения мыши в координатах окна.Расстояние движения должно быть отображено на угол.Ось вращения перпендикулярна (в норме) направлению движения мыши.В результате получается поворот объекта, похожего на this WebGL demo.

Сохранение текущей позиции мыши в startRotation.Примечание. Сохраняйте координаты положения мыши, а не нормализованного вектора:

// xy normalized device coordinates:
float ndcX = 2.0f * xPos / context->getWidth() - 1.0f;
float ndcY = 1.0 - 2.0f * yPos / context->getHeight();

startVector = QVector3D(ndcX, ndcY, 0.0);

Получите текущую позицию в updateRotation:

// xy normalized device coordinates:
float ndcX = 2.0f * xPos / context->getWidth() - 1.0f;
float ndcY = 1.0 - 2.0f * yPos / context->getHeight();

endVector = QVector3D(ndcX, ndcY, 0.0);

Рассчитайте вектор от начальной позиции до концаположение:

QVector3D direction = endVector - startVector;

Ось вращения нормальна к направлению движения:

rotationAxis = QVector3D(-direction.y(), direction.x(), 0.0).normalized();

Обратите внимание, что даже если тип direction равен QVector3D, он по-прежнему равен 2мерный вектор.Это вектор в плоскости XY области просмотра, представляющий движение мыши в области просмотра.Координата z равна 0. Двумерный вектор (x, y) , может быть повернут на 90 градусов против часовой стрелки, на (- y, x) .

Длина вектора направления представляет угол поворота.Движение мыши по всему экрану приводит к вектору с длиной 2,0 .Поэтому, если перетаскивание на весь экран должно привести к полному повороту, длину вектора нужно умножить на PI .Если необходимо выполнить вращение hlf, то PI / 2 :

angle = (float)qRadiansToDegrees(direction.length() * 3.141593);

Наконец, новое вращение должно быть применено к существующему вращению, а не к модели:

QMatrix4x4 addRotation;
addRotation.rotate(angle, rotationAxis.x(), rotationAxis.y(), rotationAxis.z());
rotation = addRotation * rotation; 

Окончательный список кодов методов startRotation и updateRotation:

void ArcBall::startRotation(int xPos, int yPos) {

    // xy normalized device coordinates:
    float ndcX = 2.0f * xPos / context->getWidth() - 1.0f;
    float ndcY = 1.0 - 2.0f * yPos / context->getHeight();

    startVector = QVector3D(ndcX, ndcY, 0.0);
    endVector   = startVector;
    rotating    = true;
}
void ArcBall::updateRotation(int xPos, int yPos) {

    // xy normalized device coordinates:
    float ndcX = 2.0f * xPos / context->getWidth() - 1.0f;
    float ndcY = 1.0 - 2.0f * yPos / context->getHeight();

    endVector = QVector3D(ndcX, ndcY, 0.0);

    QVector3D direction = endVector - startVector;
    rotationAxis        = QVector3D(-direction.y(), direction.x(), 0.0).normalized();
    angle               = (float)qRadiansToDegrees(direction.length() * 3.141593);

    QMatrix4x4 addRotation;
    addRotation.rotate(angle, rotationAxis.x(), rotationAxis.y(), rotationAxis.z());
    rotation = addRotation * rotation; 

    startVector = endVector;
}

Если вы хотите вращение вокруг оси объекта вверх, наклонитеобъект вдоль области просмотра ось х, то расчет отличается.Сначала примените матрицу вращения вокруг оси y (вектор вверх), затем текущую матрицу вида и, наконец, вращение по оси x:

view-matrix = rotate-X * view-matrix * rotate-Y

Вращение обновления функции должно выглядеть следующим образом:

void ArcBall::updateRotation(int xPos, int yPos) {

    // xy normalized device coordinates:
    float ndcX = 2.0f * xPos / context->getWidth() - 1.0f;
    float ndcY = 1.0 - 2.0f * yPos / context->getHeight();

    endVector = QVector3D(ndcX, ndcY, 0.0);

    QVector3D direction = endVector - startVector;

    float angleY = (float)qRadiansToDegrees(-direction.x() * 3.141593);
    float angleX = (float)qRadiansToDegrees(-direction.y() * 3.141593);

    QMatrix4x4 rotationX;
    rotationX.rotate(angleX, 1.0f 0.0f, 0.0f);

    QMatrix4x4 rotationUp;
    rotationX.rotate(angleY, 0.0f 1.0f, 0.0f);

    rotation = rotationX * rotation * rotationUp; 

    startVector = endVector;
}
...