Опубликован: 13.07.2010 | Уровень: специалист | Доступ: платный
Самостоятельная работа 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()
  • Запустите приложение, выберите режим рисования конуса и получите следующий результат при управлении стрелками и с разными установками


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


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

   


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


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

Александр Очеретяный
Александр Очеретяный
Украина, Киев
Анастасия Балыбердина
Анастасия Балыбердина
Украина, Киев, НТУУ КПИ