Опубликован: 13.07.2010 | Уровень: специалист | Доступ: платный
Самостоятельная работа 21:

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

Упражнение 2. Модель полого бруска в ортогональной проекции (Ortho)

Для иллюстрации преобразований модели и проекции приведем два примера, которые не будем рассматривать подробно с точки зрения их исходного кода. Мы просто будем моделировать прямоугольный полый брусок. А те новые для нас возможности управления освещением и текстурой мы пока только используем без подробных комментариев. Для нас сейчас важно проиллюстрировать вышесказанное, как один и тот же объект отображается на экране при установленном ортогональном или перспективном отсекающем объеме видимости. Начнем с ортогонального отсекающего объема.

  • Через панель Solution Explorer вызовите для корневого узла проекта контекстное меню и командой Add/New Item добавьте новый заголовочный файл с именем Ortho.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) Брусок в ортогональной проекции"  
  
//**********************************************************
// Функция обратного вызова обработки выбора пользователя
void ExecuteMenu(int choice)
{
  // Сбрасываем углы вращения прежнего варианта
  xRot = yRot = 0;
  
  // Запоминаем выбор в глобальную переменную
  ::choice = choice; 
  
  switch(::choice)
  {
    case 1:
      ChangeSizeAtom(w, h);
      break;
    case 2:
      ChangeSizeOrtho(w, h);
      break;
  }
  
  // Вызвать принудительно визуализацию
  glutPostRedisplay();
}
  
//**********************************************************
// Функция обратного вызова для рисования сцены
void RenderScene(void)
{
  switch(::choice)
  {
    case 1:
      SetupLight(false);
      RenderSceneAtom();
      break;
    case 2:
      SetupLight(true);
      RenderSceneOrtho();
      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;
  }
}
  
//**********************************************************
// Обработчик события таймера
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);
  
  glutAttachMenu(GLUT_RIGHT_BUTTON);// Присоединяем 
  // Конец создания меню
  
  glutMainLoop();  // Цикл сообщений графического окна
}
Листинг 21.12. Код файла ProjectionMatrix.cpp после добавления второго упражнения
  • Заполните файл Ortho.h следующим кодом, реализующим второе упражнение
//**********************************************************
// Прототипы
void Ortho();        
void RenderSceneOrtho(void);
void ChangeSizeOrtho(int, int);  // При изменении размеров окна
void SetupLight(bool);        // Устанавливаем источник света сцены
  
//**********************************************************
// Упражнение 2: "2) Брусок в ортогональной проекции"
void Ortho() 
{
  float fZ,bZ;
  
  fZ = 100.0f;
  bZ = -100.0f;
  
  // Сбрасываем матрицу преобразования модели в значение единичной матрицы
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  
  // Поворачиваем сцену на угол, определяемый клавишами-стрелками
  glRotatef(xRot, 1.0f, 0.0f, 0.0f);
  glRotatef(yRot, 0.0f, 1.0f, 0.0f);
  
  // Устанавливаем цвет рисования красный
  glColor3f(1.0f, 0.0f, 0.0f);
  
  glBegin(GL_QUADS); // Рисуем наружнюю сторону
    // Нормаль по оси Z
    glNormal3f(0.0f, 0.0f, 1.0f);  
  
    // Левая грань
    glVertex3f(-50.0f, 50.0f, fZ);
    glVertex3f(-50.0f, -50.0f, fZ);
    glVertex3f(-35.0f, -50.0f, fZ);
    glVertex3f(-35.0f,50.0f,fZ);
  
    // Правая грань
    glVertex3f(50.0f, 50.0f, fZ);
    glVertex3f(35.0f, 50.0f, fZ);
    glVertex3f(35.0f, -50.0f, fZ);
    glVertex3f(50.0f,-50.0f,fZ);
  
    // Верхняя грань
    glVertex3f(-35.0f, 50.0f, fZ);
    glVertex3f(-35.0f, 35.0f, fZ);
    glVertex3f(35.0f, 35.0f, fZ);
    glVertex3f(35.0f, 50.0f,fZ);
  
    // Нижняя грань
    glVertex3f(-35.0f, -35.0f, fZ);
    glVertex3f(-35.0f, -50.0f, fZ);
    glVertex3f(35.0f, -50.0f, fZ);
    glVertex3f(35.0f, -35.0f,fZ);
  
    // Левая верхняя секция
    // Нормаль по оси Y
    glNormal3f(0.0f, 1.0f, 0.0f);
    glVertex3f(-50.0f, 50.0f, fZ);
    glVertex3f(50.0f, 50.0f, fZ);
    glVertex3f(50.0f, 50.0f, bZ);
    glVertex3f(-50.0f,50.0f,bZ);
  
    // Верхняя секция
    glNormal3f(0.0f, -1.0f, 0.0f);
    glVertex3f(-50.0f, -50.0f, fZ);
    glVertex3f(-50.0f, -50.0f, bZ);
    glVertex3f(50.0f, -50.0f, bZ);
    glVertex3f(50.0f, -50.0f, fZ);
  
    // Левая секция
    glNormal3f(1.0f, 0.0f, 0.0f);
    glVertex3f(50.0f, 50.0f, fZ);
    glVertex3f(50.0f, -50.0f, fZ);
    glVertex3f(50.0f, -50.0f, bZ);
    glVertex3f(50.0f, 50.0f, bZ);
  
    // Правая секция
    glNormal3f(-1.0f, 0.0f, 0.0f);
    glVertex3f(-50.0f, 50.0f, fZ);
    glVertex3f(-50.0f, 50.0f, bZ);
    glVertex3f(-50.0f, -50.0f, bZ);
    glVertex3f(-50.0f, -50.0f, fZ);
  glEnd();
  
  glFrontFace(GL_CW);    // Лицевыми будем считать те грани, вершины
              // которых обходятся по часовой стрелки
              // для рисования задней стенки
  
  glBegin(GL_QUADS);    // Рисуем внутреннюю сторону
    // Нормаль по оси Z
    glNormal3f(0.0f, 0.0f, -1.0f);  
  
    // Левая грань
    glVertex3f(-50.0f, 50.0f, bZ);
    glVertex3f(-50.0f, -50.0f, bZ);
    glVertex3f(-35.0f, -50.0f, bZ);
    glVertex3f(-35.0f,50.0f,bZ);
  
    // Правая грань
    glVertex3f(50.0f, 50.0f, bZ);
    glVertex3f(35.0f, 50.0f, bZ);
    glVertex3f(35.0f, -50.0f, bZ);
    glVertex3f(50.0f,-50.0f,bZ);
  
    // Верхняя грань
    glVertex3f(-35.0f, 50.0f, bZ);
    glVertex3f(-35.0f, 35.0f, bZ);
    glVertex3f(35.0f, 35.0f, bZ);
    glVertex3f(35.0f, 50.0f,bZ);
  
    // Нижняя грань
    glVertex3f(-35.0f, -35.0f, bZ);
    glVertex3f(-35.0f, -50.0f, bZ);
    glVertex3f(35.0f, -50.0f, bZ);
    glVertex3f(35.0f, -35.0f,bZ);
  
    // Внутренняя часть
    glColor3f(0.75f, 0.75f, 0.75f); // Серый
  
    // Нормаль по оси Y
    glNormal3f(0.0f, 1.0f, 0.0f);
    glVertex3f(-35.0f, 35.0f, fZ);
    glVertex3f(35.0f, 35.0f, fZ);
    glVertex3f(35.0f, 35.0f, bZ);
    glVertex3f(-35.0f,35.0f,bZ);
  
    // Нижняя секция
    glNormal3f(0.0f, 1.0f, 0.0f);
    glVertex3f(-35.0f, -35.0f, fZ);
    glVertex3f(-35.0f, -35.0f, bZ);
    glVertex3f(35.0f, -35.0f, bZ);
    glVertex3f(35.0f, -35.0f, fZ);
  
    // Левая секция
    glNormal3f(1.0f, 0.0f, 0.0f);
    glVertex3f(-35.0f, 35.0f, fZ);
    glVertex3f(-35.0f, 35.0f, bZ);
    glVertex3f(-35.0f, -35.0f, bZ);
    glVertex3f(-35.0f, -35.0f, fZ);
  
    // Правая секция
    glNormal3f(-1.0f, 0.0f, 0.0f);
    glVertex3f(35.0f, 35.0f, fZ);
    glVertex3f(35.0f, -35.0f, fZ);
    glVertex3f(35.0f, -35.0f, bZ);
    glVertex3f(35.0f, 35.0f, bZ);
  glEnd();
  
  // Возвращаем обход сторон к значению по умолчанию
  glFrontFace(GL_CCW);    
  
  // Переключаем буфер кадра
  glutSwapBuffers();
}
  
//**********************************************************
// Вызывается библиотекой GLUT при изменении размеров окна
// для второго упражнения Ortho
void ChangeSizeOrtho(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;// Для коррекции
  const GLfloat nRange = 120; // Размеры отсекающих плоскостей
  
  // Первоначально задали куб, теперь его корректируем
  // в зависимости от искажения графического окна
  if (width <= height)
    glOrtho (-nRange,
          nRange, 
          -nRange / aspectRatio,
          nRange / aspectRatio, 
          -nRange * 2.0f,
          nRange * 2.0f);
  else 
    glOrtho (-nRange * aspectRatio, 
          nRange * aspectRatio, 
          -nRange, 
          nRange, 
          -nRange * 2.0f,
          nRange * 2.0f);
  
  // Восстановливает матрицу преобразования в исходный режим вида
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
}
  
//**********************************************************
// Устанавливаем источник света сцены
void SetupLight(bool flag)
{
  if(!flag)
  {
    glDisable(GL_LIGHTING);
    return;
  }
  
  // Определяем характеристики источника света для сцены
  GLfloat  whiteLight[] = { 0.45f, 0.45f, 0.45f, 1.0f };
  GLfloat  sourceLight[] = { 0.25f, 0.25f, 0.25f, 1.0f };
  GLfloat   lightPos[] = { -50.f, 25.0f, 250.0f, 0.0f };
  
  // Включаем поддержку освещения
  glEnable(GL_LIGHTING);
  
  // Устанавливаем и включаем источник 0
  glLightModelfv(GL_LIGHT_MODEL_AMBIENT,whiteLight);
  glLightfv(GL_LIGHT0,GL_AMBIENT,sourceLight);
  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);
}
  
//**********************************************************
// Функция обратного вызова для рисования сцены
// для второго упражнения Ortho
void RenderSceneOrtho(void)
{
  // Сбрасываем буферы цвета и глубины
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  
  // Рисуем сцену
  Ortho();
  
  // Прокачка сообщений
  glFlush();
}
Листинг 21.13. Код файла Ortho.h реализации модели полого бруска в ортогональной проекции
  • Запустите приложение, повращайте брусок и отметьте, что при ортогональной проекции изделие кажется неестественным


  • Попытайтесь разобраться с кодом!!!
Иван Циферблат
Иван Циферблат
Россия, Таганрог, 36, 2000