Урок 4. Вращение полигонов

В прошлом уроке я научил Вас как закрашивать треугольники и четырехугольники. В этом уроке я научу Вас как вращать эти закрашенные объекты вдоль их осей.

Мы будем использовать код из последнего урока, добавляя новые строчки кода. Я перепишу целую секцию кода ниже, чтобы вы могли понять, что добавлено, а что заменено.

Вначале мы добавим две переменные для хранения угла вращения каждого объекта. Мы сделаем это вначале программы. Посмотрите ниже, я добавил две строки после объявления переменной BOOL keys[256]. В этих строках объявляются две переменные с плавающей запятой, которые мы можем использовать для очень точного поворота объектов. Числа с плавающей запятой учитывают значения меньше единицы. Вместо использования 1, 2, 3 для угла, мы можем использовать 1.1, 1.7, 2.3 или даже 1.015 для точности. Вы увидете, что числа с плавающей запятой неотемлимая часть программирования на OpenGL.

#include <windows.h>      // Заголовочный файл для Windows
#include <gl\gl.h>        // Заголовочный файл для OpenGL32 библиотеки
#include <gl\glu.h>       // Заголовочный файл для GLu32 библиотеки
#include <gl\glaux.h>     // Заголовочный файл для GLaux библиотеки

static HGLRC hRC;       // Постоянный контекст рендеринга
static HDC hDC;         // Приватный контекст устройства GDI

BOOL    keys[256];      // Массив для процедуры обработки клавиатуры

GLfloat rtri;           // Угол для треугольник
GLfloat rquad;          // Угол для четырехугольника

Необходимо модифицировать код в DrawGLScene(). Я буду переписывать всю процедуру. Это будет сделать легко для Вас, так как Вы увидите какие изменения я сделал. Я объясню почему некоторые строки были модифицированы, и какие линии добавлены. Следующая секция кода, такая же как в последнем уроке.

GLvoid DrawGLScene(GLvoid)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);     // Очистка экрана
                                                        //      и буфера глубины
glLoadIdentity();                                       // Сброс просмотра
glTranslatef(-1.5f,0.0f,-6.0f);                         // Сдвиг в глубь экрана и влево

Следующая строка новая. glRotatef(Angle,Xtrue,Ytrue,Ztrue) отвечает за вращения объекта вдоль оси. Вы многое получите от использования этой команды. Угол некоторое число (обычно переменная), которое задает насколько Вы хотите повернуть объект. Xtrue, Ytrue и Ztrue или 0.0f или 1.0f. Если один из параметров равен 1.0f, OpenGL будет вращать объект вдоль соответствующей оси. Поэтому если Вы имеете glRotatef(10.0f,0.0f,1.0f,0.0f), объект будет поварачиваться на 10 градусов по оси Y. Если glRotatef(5.0f,1.0f,0.0f,1.0f), объект будет поварачиваться на 5 градусов по обеим осям X и Z.

Чтобы лучше понять вращения по осям X, Y и Z я объясню это на примерах.

Ось X - предположим Вы работаете за токарным станком. Заготовка перемещается слева направо (также как ось X в OpenGL). Заготовка вращается вокруг оси X. Также мы вращаем что-то вокруг оси X в OpenGL.

Ось Y- Представьте что Вы стоите посреди поля. Огромный торнадо приближается к Вам. Центр торнадо перемещается от земли в небо (верх и низ, подобно оси Y в OpenGL). Предметы захваченные торнадо кружаться вдоль оси Y (центр торнадо) слева направо или справо налево. Когда вы вращаете что-то вокруг оси Y в OpenGL, это что-то будет вращаться также.

Ось Z - Вы смотрите на переднюю часть вентилятора. Передняя часть вентилятора ближе к Вам, а дальняя часть дальше от Вас (также как ось Z в OpenGL). Лопасти вентилятора вращаются вдоль оси Z (центр вентилятора) по часовой или против часовой стрелки. Когда Вы вращаете что-то вокруг оси Z в OpenGL, это что-то будет вращаться также.

В следующей строке кода, если rtri равно 7, мы будем вращать на 7 градусов по оси Y (слева направо). Вы можете поэксперементировать с кодом. Изменяйте от 0.0f до 1.0f, и от 1.0f до 0.0f вращение треугольника по осям X и Y одновременно.

        glRotatef(rtri,0.0f,1.0f,0.0f);         // Вращение треугольника по оси Y

Следующая секция кода не изменена. Здесь будет нарисован закрашенный сглаженный треугольник. Треугольник будет нарисован с левой стороны экрана, и будет вращаться по оси Y слева направо.

        glBegin(GL_TRIANGLES);                  // Начало рисования треугольника
                glColor3f(1.0f,0.0f,0.0f);      // Верхняя точка - красная
                glVertex3f( 0.0f, 1.0f, 0.0f);  // Первая точка
                glColor3f(0.0f,1.0f,0.0f);      // Левая точка - зеленная
                glVertex3f(-1.0f,-1.0f, 0.0f);  // Вторая
                glColor3f(0.0f,0.0f,1.0f);      // Правая - синия
                glVertex3f( 1.0f,-1.0f, 0.0f);  // Третья
        glEnd();                                // Конец рисования

Посмотрите на код ниже, там мы добавим вызов glLoadIdentity(). Мы сделаем это для инициализации просмотра. Что будет если мы не сбросим просмотр? Если мы сдвинули объект после вращения, Вы получите очень неожиданные результаты. Поскольку оси вращаются, они будут указывать не в тех направлениях, о каких Вы думаете. Поэтому если мы сдвинулись влево по оси X (для установки треугольника), мы можем переместить квадрат в глубь экрана или вперед, в зависимости от того как много мы вращали по оси Y. Попробуйте убрать glLoadIdentity() и вы поймете о чем я говорю. Квадрат будет вращаться вокруг своей оси X, но и вокруг центра координат синхронно вращению треугольника.

Так как сцена сброшена, поэтому X идет слева направо, Y сверху вниз, Z от нас и далее. Теперь мы перемещаем. Как Вы видите мы сдвигаем на 1.5 вправо, вместо 3.0, как мы делали в последнем уроке. Когда мы сбрасываем экран, наш фокус перемещается в центр экрана, это означает, что мы находимся не 1.5 единицы слева, мы вернулись в 0.0. Поэтому мы не должны сдвигаться на 3.0 единицы вправо (если бы не было сброса), мы должны только сдвинуться от центра вправо на 1.5 единицы.

После того как мы сдвинулись в новое место на правой стороне экрана, мы вращаем квадрат по оси X. Квадрат будет вращаться верх и вниз.

        glLoadIdentity();
        glTranslatef(1.5f,0.0f,-6.0f);          // Сдвиг вправо на 1.5
        glRotatef(rquad,1.0f,0.0f,0.0f);        // Вращение по оси X

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

        glColor3f(0.5f,0.5f,1.0f);              // Синий цвет
        glBegin(GL_QUADS);                      // Начнем
                glVertex3f(-1.0f, 1.0f, 0.0f);  // Верх лево
                glVertex3f( 1.0f, 1.0f, 0.0f);  // Верх право
                glVertex3f( 1.0f,-1.0f, 0.0f);  // Низ право
                glVertex3f(-1.0f,-1.0f, 0.0f);  // Низ лево
        glEnd();                                // Окончим

Следующие две строки новые. Думайте о rtri и rquad как о контейнерах. Вначале нашей программы мы сделали контейнеры (GLfloat rtri и GLfloat rquad). Когда мы построили контейнеры они были пусты. В первой строке ниже ДОБАВЛЯЕМ 0.2 в контейнер. Если мы проверим значение контейнера rtri после этой секции кода, мы увидим что оно увеличилось на 0.2. Контейнер rquad уменьшиться на 0.15. Если мы проверим значение контейнера rquad после этой секции кода, мы увидим что оно уменьшилось на 0.15. Отрицательные значения вращения приводят к тому, что объект вращается в противоположную сторону. Как если бы значения были положительные.

Попробуйте изменить + на - в строке ниже и объект будет вращаться в другом направлении. Попробуйте изменить значение с 0.2 до 1.0. С увеличением значения объект будет вращаться быстрее. С уменьшением значения будет вращаться медленее.

        rtri+=0.2f;             // Увеличение переменной вращения для треугольника
        rquad-=0.15f;           // Уменьшение переменной вращения для квадрата
}

В этом уроке я попробывал рассказать как можно детальнее о том как вращаются объекты вокруг осей. Поиграйте с этим кодом, попробуйте вращать объекты по оси Z, X & Y, или по всем трем ;). Если у Вас есть комментарии или вопросы пожалуйста вышлите мне письмо. Если Вы нашли ошибки или улучшения дайте мне об этом знать. Я хочу сделать уроки по OpenGL хорошими насколько смогу. Я заинтересован в обратной связи.

© Jeff Molofee (NeHe)

 21 сентября 2001 (c)  Сергей Анисимов