Непрограммируемый конвейер в OpenGL
Упражнение 5. Моделирование системы с относительными вращениями (Solar)
Рассмотрим еще один пример, демонстрирующий взаимодействие матрицы наблюдения модели и матрицы перспективной проекции. Мы смоделируем систему "Солнце-Земля-Луна", использующую вложенные преобразования, когда объекты преобразовываются относительно друг друга с использованием стека матриц. Чтобы сделать картину более эффектной, мы, пока без подробных объяснений, просто применим к модели функции освещения и затенения (текстурирования).
В нашей модели Земля движется вокруг Солнца, а Луна - вокруг Земли. Источник света находится в центре Солнца, которое нарисовано без освещения, создавая иллюзию сияющего источника света. Пример демонстрирует, насколько просто с помощью OpenGL получать сложные эффекты.
- Добавьте к приложению новый заголовочный файл с именем Solar.h
- Наполните файл Solar.h следующим кодом
//********************************************************** // Прототипы void Solar(); void SetupLightSolar(bool); void ChangeSizeSolar(int, int); void RenderSceneSolar(void); void ShowOrbitSolar(GLfloat); //********************************************************** // Упражнение 5: "5) Система \"Солнце-Земля-Луна\"" void Solar() { GLfloat lightPos[] = { 0.0f, 0.0f, 0.0f, 1.0f }; // Начальные значения углов поворота Землм и Луны static float fMoonRot = 0.0f; // Поворот Луны static float fEarthRot = 0.0f;// Поворот Земли GLfloat radius; // Сбрасываем буфер цвета и глубины glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Сохраняем матрицу модели перед выполнением поворотов glMatrixMode(GL_MODELVIEW); glPushMatrix(); // Отодвигаем всю сцену вниз оси z в поле зрения // и рисуем Солнце glTranslatef(0.0f, 0.0f, -300.0f); glDisable(GL_LIGHTING); glColor3ub(255, 255, 0); // Желтый glutSolidSphere(15.0f, 30, 17); // Рисуем Солнце glEnable(GL_LIGHTING); // Поворачиваем сцену на угол, определяемый клавишами-стрелками glRotatef(xRot, 1.0f, 0.0f, 0.0f); // Помещаем в точку Солнца источник света glLightfv(GL_LIGHT0,GL_POSITION,lightPos); // Поворачиваем систему координат, связанную с Солнцем // и рисуем Землю radius = 105.0f; ShowOrbitSolar(radius); // Рисуем орбиту glRotatef(fEarthRot, 0.0f, 1.0f, 0.0f); glTranslatef(radius,0.0f,0.0f); glColor3ub(0,0,255); // Синий glutSolidSphere(15.0f, 30, 17);// Рисуем Землю // Поворачиваем систему координат, связанную с Землей // и рисуем Луну radius = 30.0f; ShowOrbitSolar(radius); // Рисуем орбиту glRotatef(fMoonRot,0.0f, 1.0f, 0.0f); glTranslatef(radius, 0.0f, 0.0f); glColor3ub(200,200,200); // Серый fMoonRot+= 15.0f; fMoonRot = fMoonRot < 360.0f ? fMoonRot : 0.0; glutSolidSphere(6.0f, 30, 17); // Рисуем Луну // Восстанавливаем матрицу модели glPopMatrix(); // Увеличиваем угол поворота на орбите Земли fEarthRot += 5.0f; fEarthRot = fEarthRot < 360.0f ? fEarthRot : 0.0; // Переключить буфер и показать изображение glutSwapBuffers(); } //********************************************************** // Устанавливаем источник света сцены void SetupLightSolar(bool flag) { if(!flag) { glDisable(GL_LIGHTING); return; } // Определяем характеристики источника света для сцены GLfloat whiteLight[] = { 0.2f, 0.2f, 0.2f, 1.0f }; GLfloat sourceLight[] = { 0.8f, 0.8f, 0.8f, 1.0f }; GLfloat lightPos[] = { 0.0f, 0.0f, 0.0f, 1.0f }; // Включаем поддержку освещения glEnable(GL_LIGHTING); // Устанавливаем и включаем источник 0 glLightModelfv(GL_LIGHT_MODEL_AMBIENT,whiteLight); glLightfv(GL_LIGHT0,GL_DIFFUSE,sourceLight); glLightfv(GL_LIGHT0,GL_POSITION,lightPos); glEnable(GL_LIGHT0); // Включаем поддержку цвета материала glEnable(GL_COLOR_MATERIAL); // Устанавливаем свойства материала для отражаемого цвета glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE); } //********************************************************** void ChangeSizeSolar(int width, int height) { // Предотвращаем деление на нуль if(height == 0) height = 1; // Устанавливаем поле просмотра с размерами окна glViewport(0, 0, width, height); // Устанавливаем размеры перспективы (отсекающего объема) // (left, right, bottom, top, near, far) GLfloat aspectRatio = (GLfloat)width / (GLfloat)height;// Для коррекции // Устанавливает матрицу преобразования в режим проецирования glMatrixMode(GL_PROJECTION); glLoadIdentity(); // Устанавливаем перспективную проекцию gluPerspective(45.0f, aspectRatio, 1.0, 425.0); // Восстановливает матрицу преобразования в исходный режим вида glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } //********************************************************** // Функция обратного вызова для рисования сцены void RenderSceneSolar(void) { // Сбрасываем буферы цвета и глубины glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Рисуем сцену Solar(); // Прокачка сообщений glFlush(); } //********************************************************** #define GL_PI 3.1415f #include <math.h> // Для тригонометрических функций void ShowOrbitSolar(GLfloat radius) { // Переменные орбиты GLfloat x, y = 0.0f, z, angle; glColor3ub(255, 255, 0); // Желтый glBegin(GL_LINE_LOOP); for(angle = 0.0f; angle < 2.0f * GL_PI; angle += 0.1f) { x = radius * sin(angle); z = radius * cos(angle); glVertex3f(x, y, z); } glEnd(); }Листинг 21.18. Код файла Solar.h для моделирования системы "Солнце-Земля-Луна"
//********************************************************** // Подключение стандартного файла с библиотекой OpenGL #include "stdafx.h" //********************************************************** // Прототипы функций void ExecuteMenu(int); // Контекстное меню первого уровня void TimerFunc(int); // Обработчик события таймера void SetupRC(void); // Начальные настройки OpenGL void SpecialKeys(int, int, int); // Обработка нажатия клавиш void RenderScene(void); void ChangeSize(int, int); // Глобальная переменная выбранного варианта основного меню int choice = 1; // Глобальные переменные для создания вращения // в градусах GLfloat xRot = 0.0f; GLfloat yRot = 0.0f; GLint w, h; // Ширина и высота экрана //********************************************************** // Подключение файлов с упражнениями #include "Atom.h" // Упражнение: "1) Простая модель атома" #include "Ortho.h" // Упражнение 2: "2) Брусок в ортогональной проекции" #include "Perspect.h" // Упражнение 3: "3) Брусок в перспективной проекции" #include "AtomPerspect.h" // Упражнение 4: "4) Модель атома в перспективе" #include "Solar.h" // Упражнение 5: "5) Система \"Солнце-Земля-Луна\"" //********************************************************** // Функция обратного вызова обработки выбора пользователя void ExecuteMenu(int choice) { // Сбрасываем углы вращения прежнего варианта xRot = yRot = 0; // Запоминаем выбор в глобальную переменную ::choice = choice; switch(::choice) { case 1: ChangeSizeAtom(w, h); break; case 2: ChangeSizeOrtho(w, h); break; case 3: ChangeSizePerspect(w, h); break; case 4: ChangeSizeAtomPerspect(w, h); break; case 5: ChangeSizeSolar(w, h); break; } // Вызвать принудительно визуализацию glutPostRedisplay(); } //********************************************************** // Функция обратного вызова для рисования сцены void RenderScene(void) { switch(::choice) { case 1: SetupLight(false); RenderSceneAtom(); break; case 2: SetupLight(true); RenderSceneOrtho(); break; case 3: SetupLight(true); RenderScenePerspect(); break; case 4: SetupLight(false); RenderSceneAtomPerspect(); break; case 5: SetupLightSolar(true); RenderSceneSolar(); break; } } //********************************************************** // Вызывается библиотекой GLUT при изменении размеров окна void ChangeSize(int width, int height) { w = width; h = height; switch(::choice) { case 1: ChangeSizeAtom(width, height); break; case 2: ChangeSizeOrtho(width, height); break; case 3: ChangeSizePerspect(width, height); break; case 4: ChangeSizeAtomPerspect(width, height); break; case 5: ChangeSizeSolar(width, height); break; } } //********************************************************** // Обработчик события таймера void TimerFunc(int value) { glutPostRedisplay(); // Перерисовка сцены glutTimerFunc(100, TimerFunc, 1); // Заряжаем новый таймер } //********************************************************** // Устанавливается состояние инициализации void SetupRC(void) { glClearColor(0.0F, 0.0F, 0.0F, 1.0F);// Фон черный непрозрачный glEnable(GL_DEPTH_TEST); // Включили проверку глубины glFrontFace(GL_CCW); // Лицевыми будем считать те грани, вершины // которых обходятся против часовой стрелки glEnable(GL_CULL_FACE); // Включили режим отсечения сторон } //********************************************************** // Управление с клавиатуры стрелками // для задания новых значений матрицы поворота void SpecialKeys(int key, int x, int y) { if(key == GLUT_KEY_UP) // Стрелка вверх xRot -= 5.0f; if(key == GLUT_KEY_DOWN)// Стрелка вниз xRot += 5.0f; if(key == GLUT_KEY_LEFT)// Стрелка влево yRot -= 5.0f; if(key == GLUT_KEY_RIGHT)// Стрелка вправо yRot += 5.0f; xRot = (GLfloat)((const int)xRot % 360); yRot = (GLfloat)((const int)yRot % 360); // Вызвать принудительно визуализацию с помощью RenderScene() glutPostRedisplay(); } //********************************************************** void main(int argc, char* argv[]) { glutInit(&argc, argv); // Двойная буферизация, цветовая модель RGB, буфер глубины glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); glutInitWindowSize(300, 300); // Начальные размеры окна glutCreateWindow("Матричные преобразования"); // Заголовок окна glutDisplayFunc(RenderScene); // Обновление сцены при разрушении окна glutReshapeFunc(ChangeSize); // При изменении размера окна glutTimerFunc(100, TimerFunc, 1); // Создали таймер SetupRC(); glutSpecialFunc(SpecialKeys); // Для управления с клавиатуры // Создание меню и добавление опций выбора glutCreateMenu(ExecuteMenu); glutAddMenuEntry("1) Простая модель атома", 1); glutAddMenuEntry("2) Брусок в ортогональной проекции", 2); glutAddMenuEntry("3) Брусок в перспективной проекции", 3); glutAddMenuEntry("4) Модель атома в перспективе", 4); glutAddMenuEntry("5) Система \"Солнце-Земля-Луна\"", 5); glutAttachMenu(GLUT_RIGHT_BUTTON);// Присоединяем // Конец создания меню glutMainLoop(); // Цикл сообщений графического окна }Листинг 21.19. Код файла ProjectionMatrix.cpp после добавления пятого упражнения
- Разберитесь с кодом, запустите упражнение, поуправляйте изображением с помощью клавиш-стрелок
На динамической модели в перспективной проекции мгновенные снимки экрана могут быть такими