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

Геометрические примитивы в OpenGL

Упражнение 6. Рисование конуса (Triangle)

В качестве иллюстрации к сказанному построим простую программу, в которой с помощью двух вееров треугольников создается конус в наблюдаемом объеме. Первый веер задает форму конуса, используя первую точку в качестве вершины конуса, а все остальные - как точки на окружности, удаленной от наблюдателя в отрицательном направлении оси z. Второй веер формирует окружность и целиком лежит в плоскости x0y, образуя основание конуса. Сочлененные треугольники веера выделим разным цветом.

  • Добавьте к проекту новый файл с именем Triangle.h и наполните его следующим кодом (который тщательно изучите!!!)
void Triangle()
{
  GLfloat x,y,angle;  // Переменные для координат и угла
  int iColor = 1;    // Флаг переключения цвета
  
  // Устанавливаем неструктурированный цвет модели затенения
  glShadeModel(GL_FLAT);
  
  // Устанавливаем направление обхода по часовой стрелке
  glFrontFace(GL_CW);
  
  // Очистить окно и буфер глубины
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  
  // Включить/Выключить отбор
  if(bCull)
    glEnable(GL_CULL_FACE);
  else
    glDisable(GL_CULL_FACE);
  
  // Включить/Выключить глубину
  if(bDepth)
    glEnable(GL_DEPTH_TEST);
  else
    glDisable(GL_DEPTH_TEST);
  
  // Включить/Выключить каркас задней стенки
  if(bOutline)
    glPolygonMode(GL_BACK,GL_LINE);
  else
    glPolygonMode(GL_BACK,GL_FILL);
  
  // Запомнить первоначальное состояние матрицы вращения
  glPushMatrix();
  
  // Выполнить два последовательных поворота
  // для будущей визуализации сцены
  glRotatef(xRot, 1.0f, 0.0f, 0.0f);// Новое состояние матрицы вращения
  glRotatef(yRot, 0.0f, 1.0f, 0.0f);// Следующее новое состояние
  
  //************  Рисовать конус  ***********************************
  // Рисовать веер треугольников
  glBegin(GL_TRIANGLE_FAN);
    // Устанавливаем первую точку - вершину конуса ближе к наблюдателю
    glVertex3f(0.0f, 0.0f, 75.0f);
  
    // Проходим оборот в плоскости x0y
    for(angle = 0.0f; angle <= 2.0f * GL_PI; angle += GL_PI / 8.0f){
      // Вычисляем очередную точку на окружности
      x = 50.0f * sin(angle);
      y = 50.0f * cos(angle);
  
      // Чередуем цвета
      if(iColor % 2)  // Деление по модулю
        glColor3f(0.0f, 1.0f, 0.0f);  // Зеленый
      else
        glColor3f((GLfloat)0xFF, (GLfloat)0xFF, (GLfloat)0x00); // Желтый
  
      iColor++; // Для переключения цвета
  
      // Установливаем вычисленную точку на плоскости x0y
      glVertex2f(x, y);
    }
  glEnd(); // Рисуем веер, имитирующий конус
  
  //***********  Рисовать основание конуса  *************************
  // Начинаем строить новый веер в плоскости x0y
  // Имитирующий основание конуса в начале координат
  glBegin(GL_TRIANGLE_FAN);
    // Устанавливаем первую точку центра основания при z=0
    glVertex2f(0.0f, 0.0f);
  
    // Проходим оборот в плоскости x0y
    for(angle = 0.0f; angle <= 2.0f * GL_PI; angle += GL_PI / 8.0f){
      // Вычисляем очередную точку на окружности
      x = 50.0f * sin(angle);
      y = 50.0f * cos(angle);
  
      // Чередуем цвета
      if(!(iColor % 2))  // Деление по модулю
        glColor3f(0.0f, 0.0f, 1.0f);  // Синий
      else
        glColor3f(1.0f, 0.0f, 0.0f);  // Красный
  
      iColor++; // Для переключения цвета
  
      // Установливаем вычисленную точку на плоскости x0y
      glVertex2f(x, y);
    }
  glEnd(); // Рисуем веер, имитирующий основание
  
  // Восстанавливаем измененные состояния в исходные значения
  glPopMatrix();
  glDisable(GL_CULL_FACE);
  glDisable(GL_DEPTH_TEST);
  glPolygonMode(GL_BACK,GL_FILL);
  glColor3f(0.0f, 1.0f, 0.0f);  // Зеленый
}
Листинг 21.43. Код функции построения конуса Triangle()
  • Включите этот файл инструкцией
#include "triangle.h"

в конец файла Primitives.cpp

#include "Points.h"    // Включение перенесенного кода
#include "PointsZ.h"  // Включение кода рисования точек с увеличением размера
#include "Lines.h"    // Веер линий  
#include "LinesW.h"    // Проверяется изменение ширины линий
#include "LinesStipple.h" // Линии с разной фактурой
#include "triangle.h"  // Рисовать конус
#include "Prompt.h"    // Вспомогательный код для начального окна
Листинг 21.45. Включение файла triangle.h в приложение
  • Добавьте в начало файла Primitives.cpp прототип функции Triangle()
// Прототипы функций
...................................................
void Triangle();
Листинг 21.46. Добавление прототипа функции Triangle() в начало файла Primitives.cpp
  • Настройте переключатель switch функции RenderScene() на вызов функции Triangle()
//****************************************************
// Функция обратного вызова для рисования сцены
void RenderScene(void)
{
  // Окно очищается текущим цветом,
  // установленным функцией glClearColor()
  glClear(GL_COLOR_BUFFER_BIT);
  
  // Отработка выбора пользователя
  switch(::choice){
    case 1:
      Points();
      break;
    case 2:
      PointsZ();
      break;
    case 3:
      Lines();
      break;
    case 4:
      LinesW();
      break;
    case 5:
      LinesStipple();
      break;
    case 6:
      Triangle();
      break;
    default:
      Prompt();
  }
  
  // Исполнить (очистить) очередь команд
  glFlush();
}
Листинг 21.47. Корректировка функции рендеринга в файле Primitives.cpp

В функции построения конуса Triangle() выделен код, обеспечивающий установки трех флагов для управления отбором, глубиной и рисованием каркаса. Эти флаги bCull, bDepth, bOutline нужно сделать глобальными, чтобы они виделись во всех требуемых местах программы.

  • Поместите перед функцией main() файла Primitives.cpp объявление глобальных флагов
.......................
// Глобальная переменная выбранного варианта основного меню
int choice = 0;
  
// Переменные для управления отбором, глубиной и каркасом в Triangle()
bool bCull = false, bDepth = true, bOutline = false; 
  
// Точка входа приложения
void main(void)
{
  ....................................
}
Листинг 21.48. Объявление глобальных флагов включения отбора, глубины и каркаса

Для того, чтобы иметь возможность управлять этими параметрами при рисовании конуса, воспользуемся средством библиотеки GLUT для создания подменю.

  • Дополните код функции main() возможностью создания подменю
// Точка входа приложения
void main(void)
{
  glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA);
  glutCreateWindow("Primitives");  // Заголовок окна
  glutDisplayFunc(RenderScene);  // Обновление сцены при разрушении окна
  glutReshapeFunc(ChangeSize);  // При изменении размера окна
  SetupRC();
  
  // Создаем окно пунктов  подменю для рисования конуса
  int triangle_menu = glutCreateMenu(TriangleExecuteMenu);
  glutAddMenuEntry("Показать с начальными установками", 0);
  glutAddMenuEntry("Отбор Вкл/Выкл", 1);
  glutAddMenuEntry("Глубина Вкл/Выкл", 2);
  glutAddMenuEntry("Каркас задней стенки Вкл/Выкл", 3);
  
  // Создание меню и добавление опций выбора
  glutCreateMenu(ExecuteMenu);
  glutAddSubMenu("Рисование конуса", triangle_menu);// Именно на первом месте !!!
  glutAddMenuEntry("Рисование функцией Points", 1);
  glutAddMenuEntry("Точки с увеличением размера", 2);
  glutAddMenuEntry("Веер линий", 3);
  glutAddMenuEntry("Управление шириной линий", 4);
  glutAddMenuEntry("Управление фактурой линий", 5);
  
  glutAttachMenu(GLUT_RIGHT_BUTTON);// Присоединяем 
  // Конец создания меню
  
  glutSpecialFunc(SpecialKeys); // Для управления с клавиатуры
  
  glutMainLoop();  // Цикл сообщений графического окна
}
Листинг 21.49. Добавление в функцию main() кода поддержки подменю
  • Добавьте вначало файла Primitives.cpp объявление прототипа функции обратного вызова подменю TriangleExecuteMenu()
#include "stdafx.h" // Искать в текущем каталоге проекта
  
// Прототипы функций
void RenderScene(void);
void SetupRC(void);
void ExecuteMenu(int);
void ChangeSize(int, int);
void Points(); // Пустой аргумент необязателен
void SpecialKeys(int key, int x, int y);// Можно указать только типы
void PointsZ();
void Lines();
void LinesW();
void LinesStipple(); 
void Prompt();
void Triangle();
void TriangleExecuteMenu(int);
  
// Глобальная переменная выбранного варианта основного меню
int choice = 0;
  
// Переменные для управления отбором, глубиной и каркасом в Triangle()
bool bCull = false, bDepth = true, bOutline = false; 
  
// Точка входа приложения
void main(void)
{
  ....................................
}
Листинг 21.50. Объявление прототипа для TriangleExecuteMenu()
  • Разместите после функции main() код заготовки функции TriangleExecuteMenu() обработки выбора пользователя в подменю
//****************************************************
// Функция обратного вызова обработки выбора пользователя в подменю
void TriangleExecuteMenu(int choice)
{
}
Листинг 21.51. Код заготовки функции TriangleExecuteMenu()
  • Запустите приложение - теперь меню пользователя должно выглядеть так

  • Заполните заготовку функции TriangleExecuteMenu() следующим кодом
//****************************************************
// Функция обратного вызова обработки выбора пользователя в подменю
void TriangleExecuteMenu(int choice)
{
  switch(choice){
    case 0:
      bCull = false;
      bDepth = true;
      bOutline = false;
      break;
    case 1: 
      bCull = !bCull;
      break;
    case 2: 
      bDepth = !bDepth;
      break;
    case 3:
      bOutline = !bOutline;
      break;
    default: // Просто так! - можно не вводить
      ;
  }
  
  // Устанавливаем режим и вызываем принудительно 
  // функцию визуализации, где размещен
  // вызов функции рисования конуса
  ::choice = 6;
    
  // Вызвать принудительно визуализацию
  glutPostRedisplay();
}
Листинг 21.52. Код функции TriangleExecuteMenu()
  • Запустите приложение, выберите режим рисования конуса и получите следующий результат при управлении стрелками и с разными установками


Включена глубина


Включен отбор

   


Включен каркасный режим отображения невидимых граней (с противоположным направлением обхода)


Основание конуса при выключенном отборе