OpenGL ArcBall для вращения сетки

Я использую устаревший 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());

применить вращение


person Callum Perks    schedule 02.02.2019    source источник


Ответы (1)


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

Сохраните текущую позицию мыши в 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, это все равно двумерный вектор. Это вектор в плоскости XY окна просмотра, представляющий движение мыши в окне просмотра. Координата z равна 0. Двумерный вектор (x, y), может быть повернут на 90 градусов против часовой стрелки на (-y, x).

Длина вектора направления представляет угол поворота. Движение мыши по всему экрану приводит к созданию вектора длиной 2,0. Поэтому, если перетаскивание на весь экран должно привести к полному вращению, длину вектора необходимо умножить на PI. Если необходимо выполнить ротацию полутонов, то с помощью 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;
}

Если вы хотите, чтобы вращение вокруг восходящей оси объекта наклонило объект по оси x пространства обзора, то расчет будет другим. Сначала примените матрицу вращения вокруг оси 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;
}
person Rabbid76    schedule 03.02.2019
comment
Я реализовал это, но похоже, что мой код выбора мыши каким-то образом этому мешает. Вы помогли мне решить проблему с помощью моего кода выбора мыши в этом вопросе stackoverflow.com/questions/54462354/. Кажется, что вращение происходит правильно, когда я нажимаю на вершину, а затем перемещаю мышь, но не когда я нажимаю где-нибудь еще - person Callum Perks; 03.02.2019
comment
@CallumPerks На самом деле я не понимаю, что вы имеете в виду. Но посмотрите ответ на вопрос _1 _, может быть, и вам это поможет. - person Rabbid76; 03.02.2019
comment
Я больше не использую этот метод. Я просто отображаю координаты между 0 и 1 с помощью следующих строк кода - person Callum Perks; 03.02.2019
comment
float mappedX = ((2.0f * x) / context- ›width () - 1.0f); float mappedY = - ((2.0f * y) / context- ›height () - 1.0f); - person Callum Perks; 03.02.2019
comment
Я все еще не вижу вращения. Время от времени модель просто перескакивает в другую ориентацию. - person Callum Perks; 03.02.2019
comment
@CallumPerks Что это значит это не очень естественно? Вы хотите, чтобы вектор вверх объекта всегда был направлен вверх? Следующий вопрос относится к python, но, возможно, это то, что вы хотите - Как реализовать вращение камеры alt + MMB, как в 3ds max? - person Rabbid76; 03.02.2019