Геометрические примитивы в 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"
#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 в приложение
// Прототипы функций ................................................... void Triangle();Листинг 21.46. Добавление прототипа функции Triangle() в начало файла Primitives.cpp
//**************************************************** // Функция обратного вызова для рисования сцены 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()
- Запустите приложение, выберите режим рисования конуса и получите следующий результат при управлении стрелками и с разными установками