Опубликован: 13.07.2010 | Доступ: свободный | Студентов: 891 / 20 | Оценка: 4.40 / 4.20 | Длительность: 77:34:00
Самостоятельная работа 21:

Непрограммируемый конвейер в OpenGL

Упражнение 4. Построение модели атома в перспективнойпроекции (Atom Perspect)

Переделаем нашу модель атома, построенную в " Упражнении 1 ", с использованием перспективной проекции.

  • Через панель Solution Explorer добавьте к проекту новый заголовочный файл с именем AtomPerspect.h
  • Скопируйте в файл AtomPerspect.h содержимое файла Atom.h и откорректируйте его следующим образом
//**********************************************************
// Прототипы
void AtomPerspect();        
void RenderSceneAtomPerspect(void);
void ChangeSizeAtomPerspect(int, int);  // При изменении размеров окна
void ShowOrbitPerspect(GLfloat radius);
  
//**********************************************************
// Упражнение 4: "4) Модель атома в перспективе"
void AtomPerspect() 
{
  // Угол поворота вокруг ядра
  static GLfloat fElect = 0.0f;
  
  GLfloat radius;
  
  // Сбрасываем буфер цвета и глубины
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  
  // Сбрасываем матрицу наблюдения модели в значение единичной матрицы
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  
  // Отодвигаем всю сцену назад по оси z
  // чтобы видеть ее
  glTranslatef(0.0f, 0.0f, -250.0f);  
  
  // Управление видом
  glRotatef(xRot, 1.0f, 0.0f, 0.0f);
  glRotatef(yRot, 0.0f, 1.0f, 0.0f);
  
  // Красное ядро прямо по центру линии наблюдения
  glColor3ub(255, 0, 0);
  glutSolidSphere(10.0f, 15, 15);
  
  // Задаем желтый цвет для всех электронов
  glColor3ub(255,255,0);
  
  // ПЕРВЫЙ ЭЛЕКТРОН
  radius = 90.0f;
  // Сохраняем исходное преобразование наблюдения в стеке матриц
  glPushMatrix();
  ShowOrbit(radius);  // Рисуем орбиту 
  // Формируем точку центра первого электрона
  // поворотом на угол относительно оси 0y
  glRotatef(fElect, 0.0f, 1.0f, 0.0f);
  // Сдвинули точку по оси 0x на radius единиц
  glTranslatef(radius, 0.0f, 0.0f);
  // Рисуем электрон
  glutSolidSphere(6.0f, 15, 15);
  // Восстанавливаем исходную матрицу преобразования наблюдения
  glPopMatrix();
  
  // ВТОРОЙ ЭЛЕКТРОН
  radius = 70.0f;
  glPushMatrix();
  glRotatef(120.0f, 0.0f, 0.0f, 1.0f);
  ShowOrbit(radius);  // Рисуем орбиту 
  glRotatef(fElect + 90.0f, 0.0f, 1.0f, 0.0f);
  glTranslatef(-radius, 0.0f, 0.0f);
  glutSolidSphere(6.0f, 15, 15);
  glPopMatrix();
  
  // ТРЕТИЙ ЭЛЕКТРОН
  radius = 60.0f;
  glPushMatrix();
  glRotatef(-120.0f,0.0f, 0.0f, 1.0f);
  ShowOrbit(radius);  // Рисуем орбиту 
  glRotatef(fElect - 90.0f, 0.0f, 1.0f, 0.0f);
  glTranslatef(0.0f, 0.0f, radius);
  glutSolidSphere(6.0f, 15, 15);
  glPopMatrix();
  
  // Увеличиваем угол поворота
  fElect += 10.0f;
  fElect = fElect > 360.0f ? 0.0f : fElect;
  
  // Переключаем буферы рисования
  glutSwapBuffers();
}
  
//**********************************************************
#define GL_PI 3.1415f
#include <math.h> // Для тригонометрических функций
  
void ShowOrbitPerspect(GLfloat radius)
{
  // Переменные орбиты
  GLfloat x, y = 0.0f, z, angle;
  
  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();
}
  
//**********************************************************
// Функция обратного вызова для рисования сцены
void RenderSceneAtomPerspect(void)
{
  // Сбрасываем буфер цвета и буфер глубины
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  
  // Рисуем сцену
  AtomPerspect();
  
  // Прокачка сообщений
  glFlush();
}
  
//**********************************************************
// Вызывается библиотекой GLUT при изменении размеров окна
void ChangeSizeAtomPerspect(int width, int height)
{  
  // Предотвращаем деление на нуль
  if(height == 0)
    height = 1;
  
  // Устанавливаем поле просмотра с размерами окна
    glViewport(0, 0, width, height);
  
  // Устанавливает матрицу преобразования в режим проецирования
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  
  // Устанавливаем размеры перспективы (отсекающего объема)
  // (left, right, bottom, top, near, far)
  GLfloat aspectRatio = (GLfloat)width / (GLfloat)height;// Для коррекции
  
    gluPerspective(45.0, aspectRatio, 1.0, 500.0);
  
  // Восстановливает матрицу преобразования в исходный режим вида
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
}
Листинг 21.16. Файл AtomPerspect.h модели атома в перспективной проекции
  • Скорректируйте файл ProjectionMatrix.cpp для четвертого упражнения
//**********************************************************
// Подключение стандартного файла с библиотекой 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) Модель атома в перспективе"
  
//**********************************************************
// Функция обратного вызова обработки выбора пользователя
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;
  }
  
  // Вызвать принудительно визуализацию
  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;
  }
}
  
//**********************************************************
// Вызывается библиотекой 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;
  }
}
  
//**********************************************************
// Обработчик события таймера
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);
  
  glutAttachMenu(GLUT_RIGHT_BUTTON);// Присоединяем 
  // Конец создания меню
  
  glutMainLoop();  // Цикл сообщений графического окна
}
Листинг 21.17. Код файла ProjectionMatrix.cpp после добавления четвертого упражнения
  • Разберитесь с кодом, запустите упражнение, поуправляйте изображением с помощью клавиш-стрелок.

На динамической модели атома в перспективной проекции мгновенные снимки экрана могут быть такими