Непрограммируемый конвейер в 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 после добавления пятого упражнения
-
Разберитесь
с кодом, запустите упражнение, поуправляйте изображением
с помощью клавиш-стрелок
На динамической модели в перспективной проекции мгновенные снимки экрана могут быть такими

