Ответ 1
Первое, что вы должны знать, это то, что в OpenGL матрицы преобразования умножаются справа. Что это значит? Это означает, что последнее преобразование, которое вы пишете, сначала применяется к объекту.
Итак, давайте посмотрим на ваш код:
gl.glScalef(0.8f, 0.8f, 0.8f);
gl.glTranslatef(0.0f, 0.0f, -z);
gl.glRotatef(xrot, 1.0f, 0.0f, 0.0f); //X
gl.glRotatef(yrot, 0.0f, 1.0f, 0.0f); //Y
gl.glRotatef(zrot, 0.0f, 0.0f, 1.0f); //Z
gl.glTranslatef(0.0f, 0.0f, z);
square.draw(gl);
Это означает, что, во-первых, объект перемещается в (0.0f, 0.0f, z)
. Затем он вращается вокруг Z, затем вокруг Y, затем вокруг X, затем перемещается на (0.0f, 0.0f, -z)
и, наконец, масштабируется.
У вас есть правильное масштабирование. Вы ставите его первым, поэтому он применяется последним. Вы также получили
gl.glTranslatef(0.0f, 0.0f, -z);
в нужном месте, потому что сначала вы хотите повернуть объект, а затем переместите его. Обратите внимание, что при повороте объекта он ВСЕГДА вращается вокруг базовой координаты, то есть (0, 0, 0). Если вы хотите повернуть объект вокруг своих собственных осей, сам объект должен быть в (0, 0, 0).
Итак, прямо перед тем, как вы напишете
square.draw(gl);
у вас должны быть вращения. Как ваш код сейчас, вы перемещаете объект далеко (написав
gl.glTranslatef(0.0f, 0.0f, z);
до square.draw(gl);
) и THEN вращаются, что испортит вещи. Удаление этой линии приближает вас к тому, что вам нужно. Итак, ваш код будет выглядеть так:
gl.glScalef(0.8f, 0.8f, 0.8f);
gl.glTranslatef(0.0f, 0.0f, -z);
gl.glRotatef(xrot, 1.0f, 0.0f, 0.0f); //X
gl.glRotatef(yrot, 0.0f, 1.0f, 0.0f); //Y
gl.glRotatef(zrot, 0.0f, 0.0f, 1.0f); //Z
square.draw(gl);
Теперь квадрат должен вращаться на месте.
Примечание.. После этого вы увидите, что поворот квадрата будет довольно неудобным. Например, если вы вращаетесь вокруг z на 90 градусов, то поворот вокруг x будет выглядеть вращением вокруг y из-за предыдущего вращения. На данный момент это может быть хорошо для вас, но если вы хотите, чтобы он выглядел действительно хорошо, вы должны сделать это следующим образом:
Представьте себе, что вы не вращаете объект, а вращаете камеру вокруг объекта, глядя на объект. Изменяя xrot
, yrot
и zrot
, вы перемещаете камеру на сфере вокруг объекта. Затем, узнав местоположение камеры, вы можете либо выполнить математику, либо получить правильные параметры для вызова glRotatef
и glTranslatef
или использовать gluLookAt
.
Это требует некоторого понимания математики и 3d-фантазии. Поэтому, если вы не понимаете это правильно в первый день, не расстраивайтесь.
Изменить: Это идея о том, как вращаться по координатам вращающихся объектов;
Во-первых, скажем, вы делаете поворот вокруг z. Поэтому вы
gl.glRotatef(zrot, 0.0f, 0.0f, 1.0f); //Z
Теперь глобальный Y-единичный вектор, очевидно, равен (0, 1, 0), но объект повернулся и, таким образом, его единичный вектор Y//" > также повернулся. Этот вектор задается следующим образом:
[cos(zrot) -sin(zrot) 0] [0] [-sin(zrot)]
[sin(zrot) cos(zrot) 0] x [1] = [ cos(zrot)]
[0 0 1] [0] [ 0 ]
Следовательно, ваше вращение вокруг y должно быть таким:
gl.glRotatef(yrot, -sin(zrot), cos(zrot), 0.0f); //Y-object
Вы можете попробовать это до сих пор (отключить вращение вокруг x) и увидеть, что он выглядит так, как вы хотите (я сделал это, и он сработал).
Теперь для x это становится очень сложным. Зачем? Поскольку вектор-единица X не только сначала вращается вокруг вектора z, но после его вращения вокруг вектора (-sin(zrot), cos(zrot), 0)
.
Итак, теперь единичный вектор X в объекте cooridnate равен
[cos(zrot) -sin(zrot) 0] [1] [cos(zrot)]
Rot_around_new_y * [sin(zrot) cos(zrot) 0] x [0] = Rot_around_new_y * [sin(zrot)]
[0 0 1] [0] [0 ]
Позвольте называть этот вектор (u_x, u_y, u_z). Тогда ваше окончательное вращение (одно вокруг X) будет выглядеть так:
gl.glRotatef(xrot, u_x, u_y, u_z); //X-object
Итак! Как найти матрицу Rot_around_new_y
? См. здесь о вращении вокруг произвольной оси. Перейдите к разделу 6.2, первая матрица, получите поворот подматрицы 3 * 3 (который игнорирует самый правый столбец, который связан с переводом) и поместите (-sin(zrot), cos(zrot), 0)
в качестве оси (u, v, w)
и theta
как yrot
.
Я не буду заниматься математикой здесь, потому что это требует больших усилий, и в конечном итоге я все равно ошибаюсь. Однако, если вы очень осторожны и готовы дважды проверить их пару раз, вы можете записать его и сделать матричные умножения.
Дополнительное примечание: один способ вычисления Rot_around_new_y
также может использовать Quaternions. Кватернион определяется как 4d-вектор [xs, ys, zs, c]
, который соответствует вращению вокруг [x, y, z]
на угол, который sin
равен s
и cos
равен c
.
Этот [x, y, z]
является нашим "новым Y", т.е. [-sin(zrot), cos(zrot), 0]
. Угол равен yrot
. Таким образом, кватернион для вращения вокруг Y задается как:
q_Y = [-sin(zrot)*sin(yrot), cos(zrot)*sin(yrot), 0, cos(yrot)]
Наконец, если у вас есть кватернион [a, b, c, d]
, соответствующая матрица вращения задается как:
[1 - 2b^2 - 2c^2 2ab + 2cd 2ac - 2bd ]
[ 2ab - 2cd 1 - 2a^2 - 2c^2 2bc - 2ad ]
[ 2ac - 2bd 2bc + 2ad 1 - 2a^2 - 2b^2]