Первое, что вы должны знать, это то, что в 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);
), а затем вращаются, что все портит. Удаление этой линии делает вас намного ближе к тому, что вам нужно. Итак, ваш код будет выглядеть так:
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
.
.
Это требует некоторого понимания математики и трехмерного воображения. Так что, если вы не поняли это правильно в первый день, не расстраивайтесь.
Редактировать: Это идея о том, как вращаться вдоль повернутых координат объекта;
Сначала, скажем, вы делаете вращение вокруг 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 не только сначала вращается вокруг вектора z, но и после того, как он вращается вокруг вектора (-sin(zrot), cos(zrot), 0)
.
Так что теперь вектор X-единицы в координатном объекте равен
[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 . Кватернион определяется как 4-мерный вектор [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]