Войти
OpenGL communityФорумВопросы по программированию

Перенос объекта после поворота сцены

#0
18:13, 25 сен. 2014

Есть сцена, на которой размещен объект. Мне нужно вращать и переносить как всю сцену с объектом, так и объект отдельно, используя курсор мыши. Проблема в том, что после поворота сцены с объектом, корректно переносить объект уже не получается. Он начинает двигать не в том направлении, что и мышь, а будто двигается в старой, неповернутой системе координат.
Каким образом это можно реализовать максимально просто?
Сцену нужно вращать/переносить по всем осям, а двигать объект только по x, y (не отрывая от сцены по z).
Вот пример кода:

 glTranslated(x1Tran, -y1Tran, z1Tran);
 glRotated(xRot / 16.0, 1.0, 0.0, 0.0);
 glRotated(yRot / 16.0, 0.0, 1.0, 0.0);
 glRotated(zRot / 16.0, 0.0, 0.0, 1.0);
 glScaled(scale, scale, scale);

 drawScene();

 glTranslated(xTran, -yTran, 0);

 drawObject();

На всякий случай, события на нажатие и движение мыши (Qt вариант):

+ Показать

Также прилагаю рисунок проблемы. Внимание: сцена в 3D, но в данном случае вид сверху. Интересует только перемещение по x и y.
1) Многоугольник в середине - это объект. Прямоугольник вокруг - это сцена. Система координат проходит через центр сцены. Сейчас, когда сцена не повернута, объект корректно переносится (направление отмечено жирными стрелками).
1 | Перенос объекта после поворота сцены

2) Сцена повернута на 45 градусов. А объект переносится, будто поворота и не было, в старой сс. Как мне сделать так, чтобы он переносился также, как и на первом рисунке?
2 | Перенос объекта после поворота сцены

Пробовал с тангенсом. Что-то вроде:
offsetx = -xTran * tan((zRot / 16.0) * M_PI / 180);
offsety = -yTran * tan((zRot / 16.0) * M_PI / 180);
...
а потом:
glTranslated(xTran + offsety, -yTran + offsetx, 0);

Срабатывает. Но появляется куча других ошибок со скоростью переноса, объект начинает при разных углах поворота сцены "прыгать" в пространстве.


#1
18:23, 25 сен. 2014

Настраивай матрицы руками и не используй glTranslate, glRotate и т.п. Беда стандартных функций в том, что они сразу же изменяют текующую матрицу, домножая ее на матрицу сдвига/поворота. И умножают, что характерно, всегда в одном и том же порядке:
M = T*M
M = R*M

Здесь
M - текущая матрица
T - матрица сдвига
R - матрица поворота

А иногда требуется иной порядок (произведение матриц некомутативно, или, по-русски, результат умножения разный  при разном порядке множителей):
M = M*T
M = M*R

Геометрический смысл порядка множителей: преобразование (сдвиг/поворот) будет применено ДО всех, хранящихся в текущей матрице (так работают стандартные функции) или ПОСЛЕ всех преобразований из текущей матрицы. Ну, т.е. есть разница: сначала ты повернешь налево и сделаешь пять шагов или же сначала сделаешь пять шагов и только потом повернешь налево. Преобразования одни и те же, а вот порядок их выполнения - разный. И результат - тоже разный.

В твоем случае, при уже измененной матрице мира, необходимо добавить дополнительное преобразование к объекту ПОСЛЕ всех мировых преобразований. А стандартные функции так не умеют. Как вариант, можно использовать самодельные функции сдвига и поворота для объекта, как описано тут

#2
18:55, 25 сен. 2014

dub, благодарю за ответ, сейчас попробую.

#3
22:06, 25 сен. 2014

Изучил Ваш ответ. И, возможно, неправильно понял.
Т.е.
glTranslate();
glRotate();
это все равно что
glRotate();
glPostTranslate();
? По крайней мере результат у них одинаков.
Я ранее пробовал похожий на Ваш вариант, только немного по-другому, эффект с Вашим одинаков - да, объект переносится как надо, но вот вращается вокруг своей оси (своего центра)! А мне нужно, чтоб вращение было относительно центра сцены.
То есть, например, повернули сцену на 45 градусов. Затем перенесли объект, например, в верхний правый угол сцены. Затем опять повернули сцену в исходное положение. Так вот объект не повернулся вместе со сценой, а повернулся отдельно и остался там же.
Код такой:

glRotatef(angle, 0.0, 0.0, 1.0);
drawScene();
glPostTranslate(xTran, -yTran, 0);  
drawObject();

#4
7:36, 26 сен. 2014

Ответ простой. Не надо трансформировать сцену. Надо сделать камеру и трансформировать камеру.

#5
7:57, 26 сен. 2014

Если всё же хочется трансформировать сцену, то придется заморачиваться с порядком трансформаций. Т.е. для примера выше примерно так:

1. Повернуть сцену на 45 градусов.
2. Повернуть объект на 45 градусов.
3. Перенести объект в точку v.

1. Повернуть сцену на -45 градусов.
2. Перенести объект в начало координат.
3. Повернуть объект на -45 градусов.
4. Перенести объект в точку v.

В общем муторно это. Проще написать класс камеры.

#6
11:39, 26 сен. 2014

Учи матрицы и кватернионы с молоду. Не заморачивайся эти ротатами и транслатами. Вникнешь в матрицы, сам себе спасибо скажешь.

#7
12:10, 26 сен. 2014

Мда уж. Знал бы я это чуть раньше... Всем спасибо за ответы.

#8
23:57, 26 сен. 2014

А можно хоть какой-то пример подобной реализации? Или ссылку?

А то я пока себе никак не представлю, что должно выйти. Кватернионы, как мне показалось, для поворотов, а мне по сути перенос нужен. Дело в том, что после всех поворотов, переносов и масштабирований над объектом я применяю их непосредственно к нему самому (путем получения модельно-видовой матрицы и умножения вершин объекта на нее. Объект рисуется треугольниками). Кривой перенос - единственная запарка, все остальное работает как надо.

А после трансформации камеры перенос объекта действительно не будет зависеть от угла поворота камеры?

#9
8:33, 27 сен. 2014

radon6
> А можно хоть какой-то пример подобной реализации? Или ссылку?
Вот для старых версий OpenGL. Правда я не очень понимаю что там написано, потому что никогда не использовал OpenGL FFP.
http://www.swiftless.com/tutorials/opengl/camera.html
https://www.cs.umd.edu/class/spring2013/cmsc425/OpenGL/OpenGLSamp… l-3D-sample.c

Я так понял у тебя версия младше 3.x. В 3.x или старше обычно стараются не использовать функционал типа glRotate, glTranslate. В общем все трансформации пишут свои. А это матрицы и кватернионы :)

radon6
> А после трансформации камеры перенос объекта действительно не будет зависеть от
> угла поворота камеры?
Ну... как бы как раз таки будет. Но зависеть будет так как надо. Т.е. например moveObject(1, 2, 3) всегда будет двигать объект в позицию 1, 2, 3 независимо от камеры, но на экране это отображаться будет так как надо в зависимости от позиции и поворота камеры. В общем всё правильно будет.

#10
17:00, 29 сен. 2014

youtube, благодарю, изучу.

#11
0:03, 7 окт. 2014

В общем, нашел решение проблемы. В данном случае реализация камеры мне не помогла. Та же проблема с переносом не исчезла. Зато нашел на одном форуме следующее:

When the user translates, try applying the transposed rotation matrix to the incremental translation vector before adding it to your total translation vector. Say if your current translation vector is (Xposition, Yposition), your current rotation matrix mRotation, and the user translates by (XIncr, yIncr), you would update the translation vector as:
(Xposition, Yposition) = (Xposition, Yposition) + mRotation.transposed() * (XIncr, YIncr)

т.е. берем свою матрицу поворота с помощью glGetFloatv (mRoatation), вектор смещения (т.е. на сколько нужно передвинуть по x и y) и вектор предыдущих переносов (т.е. насколько уже сместили).
На Qt решение будет следующим:

    
        // извлекаем текущую матрицу поворота
        glPushMatrix();
        glLoadIdentity(); 
        glRotatef(zRot, 0, 0, 1); // zRot - это угол, на который повернута сцена по оси z (то бишь вид сверху)

        float rot[16];
        glGetFloatv(GL_MODELVIEW_MATRIX, rot);
        QGenericMatrix<4, 4, float> R;

        R(0,0) = rot[0];
        R(0,1) = rot[1];
        R(0,2) = rot[2];
        R(0,3) = rot[3];
        R(1,0) = rot[4];
        R(1,1) = rot[5];
        R(1,2) = rot[6];
        R(1,3) = rot[7];
        R(2,0) = rot[8];
        R(2,1) = rot[9];
        R(2,2) = rot[10];
        R(2,3) = rot[11];
        R(3,0) = rot[12];
        R(3,1) = rot[13];
        R(3,2) = rot[14];
        R(3,3) = rot[15];

        glPopMatrix();

        // генерируем две матрицы (в Qt так удобней производить над ними операции) из векторов с 1 столбцом и 4 строками 
        QGenericMatrix<1, 4, float> incr, res;
        // вектор смещения 
        incr(0,0) = dx;
        incr(1,0) = dy;
        incr(2,0) = 0;
        incr(3,0) = 1;

        // вектор предыдущих переносов
        res(0,0) = xTran;
        res(1,0) = yTran;
        res(2,0) = 0;
        res(3,0) = 1;

        // res - это результирующая матрица с координатами новой позиции
        res = res + R.transposed() * incr; // Внимание: матрица поворота нужна транспонированной

        xTran = res(0,0);
        yTran = res(1,0);
OpenGL communityФорумВопросы по программированию

Тема в архиве.