Файлы к лабораторной работе Вы можете скачать здесь.
Сложные трехмерные объекты строятся в
Но для того, чтобы в одной работе можно было демонстрировать разные варианты кода, вначале нужно познакомиться со средствами пользовательского интерфейса - меню, которые позволят нам реализовать такую возможность из одного приложения без перекомпиляции множества отдельных заготовок кода.
Библиотека GLUT имеет функции поддержки работы с всплывающим меню.
Работа с меню в Simple работы №19. В дальнейшем мы будем постепенно модифицировать эту первоначальную
заготовку, наращивая меню в соответствии с возникающими задачами.
Visual Studio .NET 2005 для поддержки проектов библиотеками OpenGL и GLUT подробно изложены в работе
"Лекция 19. Простая программа на OpenGL"
Выберите
место для размещения нового проекта C++ и командой File/New/Project создайте
его с именем Primitives
Оставьте
установки мастера по умолчанию и щелкните на кнопке Finish
Откорректируйте
файл нового
проекта Primitives, чтобы код стал таким#pragma once #include <windows.h> #include <gl/gl.h> #include <gl/glu.h> #include <gl/glut.h> #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers #include <stdio.h> #include <tchar.h>
Возьмите
код из файла Simple.cpp предыдущего
проекта Simple и скопируйте его
вместо автоматически сгенерированного кода файла Primitives.cpp
проекта Primitives. Код должен быть таким#include "stdafx.h"
// Прототипы функций
void RenderScene(void);
void SetupRC(void);
// Точка входа приложения
void main(void)
{
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA);
glutCreateWindow("Primitives");
glutDisplayFunc(RenderScene);
SetupRC();
glutMainLoop();
}
//****************************************************
// Функция обратного вызова для рисования сцены
void RenderScene(void)
{
// Окно очищается текущим цветом,
// установленным функцией glClearColor()
glClear(GL_COLOR_BUFFER_BIT);
// В буфер вводятся команды рисования
glFlush();
}
//****************************************************
// Устанавливается состояние инициализации
void SetupRC(void)
{
glClearColor(0.0F, 0.0F, 1.0F, 1.0F);// RGB + alpha-канал
}
Постройте
проект Primitives ,
должен получиться такой результат
Теперь давайте на этой модели создадим контекстное меню с пока тремя опциями и их условными названиями
Добавьте
в функцию main() перед вызовом
основного программного цикла glutMainLoop() следующий
код, определяющий создание контекстного меню#include "stdafx.h"
// Прототипы функций
void RenderScene(void);
void SetupRC(void);
// Прототип функции обработки выбора пользователя
void ExecuteMenu(int);
// Точка входа приложения
void main(void)
{
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA);
glutCreateWindow("Primitives");
glutDisplayFunc(RenderScene);
SetupRC();
// Создание меню и добавление опций выбора
glutCreateMenu(ExecuteMenu);
glutAddMenuEntry("Опция 1", 1);
glutAddMenuEntry("Опция 2", 2);
glutAddMenuEntry("Опция 3", 3);
glutAttachMenu(GLUT_RIGHT_BUTTON);
glutMainLoop();
}
//****************************************************
// Функция обратного вызова обработки выбора пользователя
void ExecuteMenu(int choice)
{
switch(choice){
case 1:
// Вариант выбора 1
break;
case 2:
// Вариант выбора 2
break;
case 3:
// Вариант выбора 3
break;
default:
;
}
// Вызвать принудительно визуализацию
glutPostRedisplay();
}
//****************************************************
// Функция обратного вызова для рисования сцены
void RenderScene(void)
{
// Окно очищается текущим цветом,
// установленным функцией glClearColor()
glClear(GL_COLOR_BUFFER_BIT);
// В буфер вводятся команды рисования
glFlush();
}
//****************************************************
// Устанавливается состояние инициализации
void SetupRC(void)
{
glClearColor(0.0F, 0.0F, 1.0F, 1.0F);// RGB + alpha-канал
}
Результат работы кода будет таким

Библиотека GLUT поддерживает и возможность создания подменю
для отдельных опций, но на данном этапе нам показанных возможностей
достаточно. Теперь мы можем, не перекомпилируя приложение для
каждого нового изучаемого фрагмента кода, вызывать нужную функциональность
через контекстное меню.
Переходим к основной теме нашей работы - изучению примитивов .
Прежде всего мы должны установить отсекающий объем, который
удобно считать как канву,
в границах которой мы будем строить свою сцену. Отсекающий
объем мы сделаем точно таким же, как в предыдущей работе. Устанавливая
ортогональный отсекающий объем, мы как-бы устанавливаем углубленную
сцены с неподвижной правой x и y направлены
по ширине и высоте сцены соответственно, ось z направлена к
зрителю, а начало координат определяется заданными размерами
при создании сцены. Далее относительно этих координат мы будем
рисовать объекты, но перед тем, как их визуальное представление
дойдет до зрителя, оно может быть последовательно преобразовано
различными матрицами поворотов и сдвига. Как в кривом зеркале:
отображение искажается, а сам объект нет.
Скопируйте
из файла GLRect.cpp работы
" Лекция 19. Простая программа на OpenGL " код
функции ChangeSize(),
чтобы новое содержимое файла выглядело так#include "stdafx.h"
// Прототипы функций
void RenderScene(void);
void SetupRC(void);
void ExecuteMenu(int);
void ChangeSize(int, int);
// Точка входа приложения
void main(void)
{
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA);
glutCreateWindow("Primitives");
glutDisplayFunc(RenderScene);
glutReshapeFunc(ChangeSize);
SetupRC();
// Создание меню и добавление опций выбора
glutCreateMenu(ExecuteMenu);
glutAddMenuEntry("Опция 1", 1);
glutAddMenuEntry("Опция 2", 2);
glutAddMenuEntry("Опция 3", 3);
glutAttachMenu(GLUT_RIGHT_BUTTON);
glutMainLoop();
}
//****************************************************
// Функция обратного вызова обработки выбора пользователя
void ExecuteMenu(int choice)
{
switch(choice){
case 1:
// Вариант выбора 1
break;
case 2:
// Вариант выбора 2
break;
case 3:
// Вариант выбора 3
break;
default:
;
}
// Вызвать принудительно визуализацию
glutPostRedisplay();
}
//****************************************************
// Функция обратного вызова для рисования сцены
void RenderScene(void)
{
// Окно очищается текущим цветом,
// установленным функцией glClearColor()
glClear(GL_COLOR_BUFFER_BIT);
// В буфер вводятся команды рисования
glFlush();
}
//****************************************************
// Устанавливается состояние инициализации
void SetupRC(void)
{
glClearColor(0.0F, 0.0F, 1.0F, 1.0F);// RGB + alpha-канал
}
//**********************************************************
// Вызывается библиотекой GLUT при изменении размеров окна
void ChangeSize(int width, int height)
{
GLfloat aspectRatio;
// Замена числовых границ объема на константную переменную
const GLfloat nRange = 100;
// Предотвращаем деление на нуль
if(height == 0)
height = 1;
// Устанавливаем поле просмотра с размерами окна
glViewport(0, 0, width, height);
// Устанавливает матрицу преобразования в режим проецирования
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// Устанавливаем размеры перспективы (отсекающего объема)
// (left, right, bottom, top, near, far)
aspectRatio = (GLfloat)width / (GLfloat)height;
if (width <= height)
glOrtho (-nRange, nRange, -nRange / aspectRatio,
nRange / aspectRatio,
-nRange, nRange);
else
glOrtho (-nRange * aspectRatio,
nRange * aspectRatio,
-nRange, nRange,
-nRange, nRange);
// Восстановливает матрицу преобразования в исходный режим вида
glMatrixMode(GL_MODELVIEW);
glLoadIdentity(); // Загружаем единичную матрицу преобразований
}
Для дальнейшей работы при RenderScene ) мы будем использовать новые возможности ,
такие как
glRotate()glPushMatrix()glPopMatrix()без особых объяснений, чтобы не отвлекаться от основного материала. Скажем лишь, что данные функции позволяют рисовать в трехмерном пространстве и помогают легко визуализировать внешний вид рисунков под различными углами.
Основным примитивом, используемым при рисовании, является
точка. Для задания координат точки используется функция glVertex3f ( glVertex2f -
для точки на glVertex4f -
трехмерная точка + масштаб). Для того, чтобы рисование выполнялось
быстро как единый пакет, в принят такой механизм: вначале
точки расставляются командой glVertax3f, а затем подается сигнал
пропустить их все разом через "жернова" преобразующих матриц
и представить зрителю на экране в поле просмотра, сформировав кадр сцены. Механизм пакетной обработки устанавливается внутри
блочных функций glBegin()...glEnd().
Давайте добавим к проекту функцию, которая будет рисовать
спираль, состоящую из точек. Назовем ее Points() и
назначим ее выполнение при выборе пользователем первого пункта
контекстного меню. Для видимости переменной, устанавливающей
выбор пользователя, введем . Это плохая практика в стиле языка C, но данная версия не поддерживает классы, позволяющие инкапсулировать
данные. А другого выхода из соображений сделать программу проще,
не отвлекаясь на языковые моменты, у нас пока нет.
В функции SetupRC() установим RenderScene() введем
варианты вызова функций рисования в зависимости от выбора пользователя.
Внутри функции Poins() применим описанный
ранее механизм рисования. Таким образом, реализующий сказанное
код в составе всей программы должен выглядеть так
#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);// Можно указать только типы
// Глобальная переменная выбранного варианта
int choice = 0;
// Точка входа приложения
void main(void)
{
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA);
glutCreateWindow("Primitives"); // Заголовок окна
glutDisplayFunc(RenderScene); // Обновление сцены при разрушении окна
glutReshapeFunc(ChangeSize); // При изменении размера окна
SetupRC();
// Создание меню и добавление опций выбора
glutCreateMenu(ExecuteMenu);
glutAddMenuEntry("Рисование функцией Points", 1);
glutAddMenuEntry("Опция 2", 2);
glutAddMenuEntry("Опция 3", 3);
glutAttachMenu(GLUT_RIGHT_BUTTON);// Присоединяем
// Конец создания меню
glutSpecialFunc(SpecialKeys); // Для управления с клавиатуры
glutMainLoop(); // Цикл сообщений графического окна
}
//****************************************************
// Глобальные переменные для создания вращения
GLfloat xRot = 0.0f;
GLfloat yRot = 0.0f;
//****************************************************
// Функция обратного вызова обработки выбора пользователя
void ExecuteMenu(int choice)
{
// Сбрасываем углы вращения прежнего варианта
xRot = yRot = 0;
// Передаем выбор в глобальную переменную
::choice = choice;
// Вызвать принудительно визуализацию
glutPostRedisplay();
}
//****************************************************
// Функция обратного вызова для рисования сцены
void RenderScene(void)
{
// Окно очищается текущим цветом,
// установленным функцией glClearColor()
glClear(GL_COLOR_BUFFER_BIT);
// Отработка выбора пользователя
// Пока один вариант !
switch(::choice){
case 1:
Points();
}
// В буфер вводятся команды рисования
glFlush();
}
//****************************************************
// Устанавливается состояние инициализации
void SetupRC(void)
{
glClearColor(0.0F, 0.0F, 0.0F, 1.0F);// Фон черный непрозрачный
glColor3f(0.0f, 1.0f, 0.0f); // Цвет рисования зеленый
}
//**********************************************************
// Вызывается библиотекой GLUT при изменении размеров окна
void ChangeSize(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 = 100; // Размеры отсекающих плоскостей
// Первоначально задали куб, теперь его корректируем
// в зависимости от искажения графического окна
if (width <= height)
glOrtho (-nRange, nRange, -nRange / aspectRatio,
nRange / aspectRatio,
-nRange, nRange);
else
glOrtho (-nRange * aspectRatio,
nRange * aspectRatio,
-nRange, nRange,
-nRange, nRange);
// Восстановливает матрицу преобразования в исходный режим вида
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
//**********************************************************
// Подключаем по мере надобности
#define GL_PI 3.1415f // Число Пи
#include <math.h> // Для функций sin() и cos()
//**********************************************************
// Функция рисования пружины точками
// Все углы в радианах
void Points() // Пустой аргумент необязателен
{
GLfloat x,y,z,angle; // Переменные для координат и угла
// Запомнить первоначальное состояние матрицы вращения
glPushMatrix();
// Выполнить два последовательных поворота
// для будущей визуализации сцены
glRotatef(xRot, 1.0f, 0.0f, 0.0f);// Новое состояние матрицы вращения
glRotatef(yRot, 0.0f, 1.0f, 0.0f);// Следующее новое состояние
// Сначала расставить все точки (произвести монтаж сцены),
// потом послать сцену пакетом на визуализацию
glBegin(GL_POINTS);
// Начинаем расставлять точки
z = -50.0f;
// Задаем три оборота в радианах
for(angle = 0.0f; angle <= 2.0f * GL_PI * 3.0f; angle += 0.1f){
// Вычисляем очередную точку на окружности
x = 50.0f * sin(angle);
y = 50.0f * cos(angle);
// Определяем точку со сдвигом по оси z
glVertex3f(x, y, z);
z += 0.5f; // Приближаем к зрителю
}
glEnd(); // Закончили расставление, посылаем пакет на визуализацию
// Восстанавливаем матрицу вращения в исходное состояние
glPopMatrix();
}
//**********************************************************
// Управление с клавиатуры стрелками
// для задания новых значений матрицы поворота
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;
// Вызвать принудительно визуализацию с помощью RenderScene()
glutPostRedisplay();
}
Запустите
приложение с приведенным кодом, через контекстное меню включите
первую опцию и поуправляйте рендерингом кнопками-стрелкамиОбратите внимание, что на начальном этапе мы формируем пружину
в x0y, которая по стенкам воображаемого цилиндра
приближается к зрителю. Для того, чтобы ее увидеть, мы изменяем
матрицу поворота клавишами-стрелками. Для этого мы используем GLUT.
Код приведен с подробными комментариями. Поразбирайтесь и поэкспериментируйте с ним. Мой результат такой

Прежде, чем двигаться дальше, давайте поступим несколько нетрадиционно
- так, как не следует поступать. А сделаем вот что. Поскольку
конец нашего кода мы изменять дальше не будем, уберем его в
отдельный
Откройте
панель Solution Explorer и щелкните правой
кнопкой мыши на узле проекта
Выполните
команду контекстного меню Add/New Item и в появившемся
диалоговом окне задайте следующие настройки добавления к
проекту нового файла
Перенесите из
файла Primitives.cpp весь код, начиная с
функции SetupRC() и до конца, а на оставшееся
место поместите инструкцию включения. После этого действия
содержимое файла Primitives.cpp должно
выглядеть так#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);// Можно указать только типы
// Глобальная переменная выбранного варианта
int choice = 0;
// Точка входа приложения
void main(void)
{
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA);
glutCreateWindow("Primitives"); // Заголовок окна
glutDisplayFunc(RenderScene); // Обновление сцены при разрушении окна
glutReshapeFunc(ChangeSize); // При изменении размера окна
SetupRC();
// Создание меню и добавление опций выбора
glutCreateMenu(ExecuteMenu);
glutAddMenuEntry("Рисование функцией Points", 1);
glutAddMenuEntry("Опция 2", 2);
glutAddMenuEntry("Опция 3", 3);
glutAttachMenu(GLUT_RIGHT_BUTTON);// Присоединяем
// Конец создания меню
glutSpecialFunc(SpecialKeys); // Для управления с клавиатуры
glutMainLoop(); // Цикл сообщений графического окна
}
//****************************************************
// Глобальные переменные для создания вращения
GLfloat xRot = 0.0f;
GLfloat yRot = 0.0f;
//****************************************************
// Функция обратного вызова обработки выбора пользователя
void ExecuteMenu(int choice)
{
// Сбрасываем углы вращения прежнего варианта
xRot = yRot = 0;
// Передаем выбор в глобальную переменную
::choice = choice;
// Вызвать принудительно визуализацию
glutPostRedisplay();
}
//****************************************************
// Функция обратного вызова для рисования сцены
void RenderScene(void)
{
// Окно очищается текущим цветом,
// установленным функцией glClearColor()
glClear(GL_COLOR_BUFFER_BIT);
// Отработка выбора пользователя
// Пока один вариант !
switch(::choice){
case 1:
Points();
}
// В буфер вводятся команды рисования
glFlush();
}
#include "Points.h" // Включение перенесенного кода
Постройте
приложение и ничего не должно измениться, зато сколько свободного
места стало в файлеМы и дальше в этой работе будем так поступать: создадим очередной хвост кода в файле Primitives.cpp, а потом перекинем в другой файл, чтобы не мешал разрабатывать новые варианты.
Когда в предыдущем варианте мы рисовали точку, то по умолчанию
ее размер равен одному пикселу. Изменить величину точки можно
функцией glPointSize(GLfloat size).
Функция glPointSize() принимает
один параметр, задающий приблизительный
GLfloat sizes[2], step; glGetFloatv(GL_POINT_SIZE_RANGE, sizes); // Получаем диапазон glGetFloatv(GL_POINT_SIZE_GRANULARITY, step); // Получаем шаг
Массив размеров содержит наименьший и наибольший возможные
значения точки, поддерживаемый функцией glPointSize().
При приближении размера к краю диапазона он замораживается.
Точки, в отличие от других геометрических объектов, не меняются
при делении на коэффициент перспективы ( ).
Она позволяет сглаживать зазубренные края и округлять углы.
Реализуем увеличение размера точек на практике.
Добавьте
в PointsZ()#include "points.h" // Включение перенесенного кода
//**********************************************************
// Функция рисования пружины точками с увеличением размера
// Все углы в радианах
void PointsZ() // Здесь изменили название!!!
{
// Запомнить первоначальное состояние матрицы вращения
glPushMatrix();
// Настроить два последовательных поворота
// для будущей визуализации сцены
glRotatef(xRot, 1.0f, 0.0f, 0.0f);// Первое состояние матрицы вращения
glRotatef(yRot, 0.0f, 1.0f, 0.0f);// Следующее состояние
GLfloat sizes[2], step; // Для определения диапазона и
// шага изменения размеров точки
glGetFloatv(GL_POINT_SIZE_RANGE, sizes);// Получаем диапазон
GLfloat curSize = sizes[0]; // Сначала установим минимальный размер
glGetFloatv(GL_POINT_SIZE_GRANULARITY, step); // Получаем шаг
// Задаем три оборота в радианах
GLfloat x,y,z,angle; // Переменные для координат и угла
z = -50.0f;// Задаем начальную координату z
for(angle = 0.0f; angle <= 2.0f * GL_PI * 3.0f; angle += 0.1f){
// Вычисляем очередную точку на окружности
x = 50.0f * sin(angle);
y = 50.0f * cos(angle);
// Задаем размер точки перед указанием примитива
glPointSize(curSize);
// Посылаем на визуализацию каждую точку по отдельности
glBegin(GL_POINTS);
// Задаем точку
glVertex3f(x, y, z);
glEnd(); // Закончили визуализацию
z += 0.5f; // Приближаем к зрителю
curSize += step; // Увеличиваем размер точки
}
// Восстанавливаем матрицу вращения в исходное состояние
glPopMatrix();
// Восстанавливаем минимальный размер
glPointSize(sizes[0]);
}
Обратите внимание, что функцию glPointSize нужно вызывать
за пределами операторных скобок glBegin...glEnd. Не все функцию
допускают такую "обертку".
Добавьте
в начало файла Primitives.cpp прототип функции PointsZ()void PointsZ(void);
Добавьте
в функции main() название новой опции меню// Точка входа приложения
void main(void)
{
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA);
glutCreateWindow("Primitives"); // Заголовок окна
glutDisplayFunc(RenderScene); // Обновление сцены при разрушении окна
glutReshapeFunc(ChangeSize); // При изменении размера окна
SetupRC();
// Создание меню и добавление опций выбора
glutCreateMenu(ExecuteMenu);
glutAddMenuEntry("Рисование функцией Points", 1);
glutAddMenuEntry("Точки с увеличением размера", 2);
glutAddMenuEntry("Опция 3", 3);
glutAttachMenu(GLUT_RIGHT_BUTTON);// Присоединяем
// Конец создания меню
glutSpecialFunc(SpecialKeys); // Для управления с клавиатуры
glutMainLoop(); // Цикл сообщений графического окна
}
Добавьте
в функцию RenderScene() вызов функции PointsZ() при соответствующем
выборе пользователя//****************************************************
// Функция обратного вызова для рисования сцены
void RenderScene(void)
{
// Окно очищается текущим цветом,
// установленным функцией glClearColor()
glClear(GL_COLOR_BUFFER_BIT);
// Отработка выбора пользователя
// Пока один вариант !
switch(::choice){
case 1:
Points();
break;
case 2:
PointsZ();
break;
}
// В буфер вводятся команды рисования
glFlush();
}
Проверьте
отсутствие синтаксических ошибок в текущем файле редактирования Primitivez.cpp, выполнив команду меню оболочки Build/Compile
Добавьте
к проекту новый PointsZ() из файла Primitivez.cpp,
а на освободившееся место включите этот #include "PointsZ.h"
Проследите
за тем, чтобы эта инструкция следовала в коде после инструкции#include "Points.h"
Постройте
проект и получите примерно такой результат![]() |
![]() |
Хорошо заметно, что все отображаемые точки имеют квадратную
форму. Но можно указать сглаживать углы квадратов, за
счет чего линия будет смотреться лучше. Но это потом...
Заметьте также, что благодаря меню, мы можем теперь запускать любое упражнение, не перестраивая проект приложения.
Следующий этап наших исследований: задать две вершины в трехмерном пространстве и соединить их прямой линией. Именно для этого предназначен код
glBegin(GL_LINES);...glEnd();
Приведенный ниже короткий фрагмент кода рисует 0, 0, 0 ) и ( 50, 50, 50 )
glBegin(GL_LINES); glVertex3f(0.0f, 0.0f, 0.0f); glVertex3f(50.0f, 50.0f, 50.0f); glEnd();
Обратите внимание на то, что две вершины задают один примитив
- для двух заданных вершин рисуется одна линия. Если задать
в GL_LINES нечетное количество вершин - последняя из них будет
проигнорирована: примитив "линия" рисуется парными вершинами.
В качестве примера нарисуем ряд линий, концы которых размещаются
в одной
В панели Solution Explorer двойным щелчком левой
кнопкой мыши на файле PointsZ.h откройте
его на редактирование.
Выполните
команду File/Save PointsZ.h As меню оболочки
и запишите в 
В
панели Solution Explorer вызовите контекстное меню для узла Primitives проекта
и, выполнив команду Add/Existing Item,
присоедините этот файл к текущему проекту
Очистите
содержимое вновь полученного файла и замените его следующим
кодом//**********************************************************
// Функция рисования веера линий
void Lines()
{
// Запомнить первоначальное состояние матрицы вращения
glPushMatrix();
// Настроить два последовательных поворота
// для будущей визуализации сцены
glRotatef(xRot, 1.0f, 0.0f, 0.0f);// Первое состояние матрицы вращения
glRotatef(yRot, 0.0f, 1.0f, 0.0f);// Следующее состояние
GLfloat x,y,z,angle; // Переменные для координат и угла
z = 0.0f;// Размещаем линии в плоскости x0y
// Размещаем веер линий
glBegin(GL_LINES);
// Генерируем 20 пар точек на окружности симметрично ее центра
for(angle = 0.0f; angle <= GL_PI; angle += (GL_PI /20.0f)){
// Вычисляем очередную линию на верхней половине окружности
x = 50.0f * sin(angle);
y = 50.0f * cos(angle);
glVertex3f(x, y, z); // Первая конечная точка отрезка
// Вычисляем очередную линию на нижней половине окружности
x = 50.0f * sin(angle + GL_PI);
y = 50.0f * cos(angle + GL_PI);
glVertex3f(x, y, z); // Вторая конечная точка отрезка
}
glEnd(); // Закончили визуализацию
// Восстанавливаем матрицу вращения в исходное состояние
glPopMatrix();
}
Разместите
в конце файла Primitives .cpp включение файла Lines.h................................................. #include "Points.h" // Включение перенесенного кода #include "PointsZ.h" // Включение кода рисования точек с увеличением размера #include "Lines.h" // Веер линий
Добавьте
в начало файла Primitives .cpp Lines()................................................. // Прототипы функций 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(); .................................................
Скорректируйте
меню в функции main() .................................................
// Создание меню и добавление опций выбора
glutCreateMenu(ExecuteMenu);
glutAddMenuEntry("Рисование функцией Points", 1);
glutAddMenuEntry("Точки с увеличением размера", 2);
glutAddMenuEntry("Веер линий", 3);
glutAttachMenu(GLUT_RIGHT_BUTTON);// Присоединяем
// Конец создания меню
.................................................
Скорректируйте
обработку выбора пользователя в функции рендеринга//****************************************************
// Функция обратного вызова для рисования сцены
void RenderScene(void)
{
// Окно очищается текущим цветом,
// установленным функцией glClearColor()
glClear(GL_COLOR_BUFFER_BIT);
// Отработка выбора пользователя
switch(::choice){
case 1:
Points();
break;
case 2:
PointsZ();
break;
case 3:
Lines();
break;
}
// В буфер вводятся команды рисования
glFlush();
}
Запустите
приложение и получите следующий результат при управлении
стрелками![]() |
![]() |
Последовательность ломаных линий, соединяющих точки в порядке
их определения на сцене оператором glVertex3f задается в команде glBegin флагом GL_LINE_STRIP.
Если нужно соединить точки и замкнуть GL_LINE_LOOP (петля).
Это можно легко проверить, заменив соответствующий флаг в функции Points() файла Points.h нашего тестового проекта .
Результат будет таким
![]() |
![]() |
Такие схемы рисования удобно применять для
Точно также, как мы задавали различные размеры точек, можно задавать необходимую ширину линий. Это делается с помощью функции
void glLineWidth(GLfloat width);
Функция glLineWidth() принимает
один параметр, задающий приблизительную ширину в пикселах изображаемой
линии. Поддерживаются не все желаемые размеры, поэтому следует
проверять, доступен ли размер, который мы задаем как ширину
линии. Чтобы найти диапазон ширин и наименьший шаг изменения
ширины, применяется следующий код
GLfloat sizes[2], step; glGetFloatv(GL_POINT_WIDTH_RANGE, sizes); // Получаем диапазон glGetFloatv(GL_POINT_WIDTH_GRANULARITY, step); // Получаем шаг
Массив размеров содержит наименьший и наибольший возможные
значения ширины линий, поддерживаемый функцией glLineWidth().
При приближении ширины линии к краю диапазона изменение ширины
замораживается.
Проверим сказанное на практике.
В панели Solution
Explorer двойным щелчком левой кнопкой мыши на
файле PointsZ.h откройте
его на редактирование.
Выполните
команду File/Save PointsZ.h As меню оболочки
и запишите в 
В панели Solution
Explorer вызовите контекстное меню для узла Primitives проекта
и командой Add/Existing Item
присоедините файл LinesW.h к текущему проекту
Очистите
содержимое вновь полученного файла и замените его следующим
кодом//**********************************************************
// Функция рисования линий с увеличением ширины
void LinesW()
{
// Запомнить первоначальное состояние матрицы вращения
glPushMatrix();
// Настроить два последовательных поворота
// для будущей визуализации сцены
glRotatef(xRot, 1.0f, 0.0f, 0.0f);// Первое состояние матрицы вращения
glRotatef(yRot, 0.0f, 1.0f, 0.0f);// Следующее состояние
GLfloat sizes[2], step; // Для определения диапазона и
// шага изменения ширины линий
glGetFloatv(GL_LINE_WIDTH_RANGE, sizes);// Получаем диапазон
GLfloat curSize = sizes[0]; // Сначала установим минимальный размер
glGetFloatv(GL_LINE_WIDTH_GRANULARITY, ); // Получаем шаг
GLfloat y; // Переменная для координаты y
// Пошаговый проход по оси y с шагом 20 единиц снизу вверх
// Получится 10 линий по размеру отсекающего куба в плоскости z = 0
for(y = -90.0f; y <= 90.0f; y += 20.0f){
// Задаем ширину линии
glLineWidth(curSize);
// Посылаем на визуализацию каждую линию по отдельности
glBegin(GL_LINES);
// Задаем два конца линии в плоскости z = 0
glVertex2f(-90.0f, y);
glVertex2f(90.0f, y);
glEnd(); // Закончили визуализацию
curSize += 3.0f * step; // Увеличиваем ширину линии
// с помощью весового коэффициента
}
// Восстанавливаем матрицу вращения в исходное состояние
glPopMatrix();
// Восстанавливаем минимальный размер ширины
glLineWidth(sizes[0]);
}
Обратите внимание, что для разнообразия мы использовали glVertex2f вместо glVertex3f, поскольку мы рисуем все линии на x0y при значении координаты z равной нулю. Клавиши со стрелками
в нынешнем варианте все еще продолжают функционировать.
Разместите
в конце файла Primitives.cpp включение файла LinesW.h................................................. #include "Points.h" // Включение перенесенного кода #include "PointsZ.h" // Включение кода рисования точек с увеличением размера #include "Lines.h" // Веер линий #include "LinesW.h" // Проверяется изменение ширины линий .................................................
Добавьте
в начало файла Primitives.cpp LinesW()................................................. // Прототипы функций 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(); .................................................
Скорректируйте
меню в функции main() файла Primitives.cpp.................................................
// Создание меню и добавление опций выбора
glutCreateMenu(ExecuteMenu);
glutAddMenuEntry("Рисование функцией Points", 1);
glutAddMenuEntry("Точки с увеличением размера", 2);
glutAddMenuEntry("Веер линий", 3);
glutAddMenuEntry("Управление шириной линий", 4);
glutAttachMenu(GLUT_RIGHT_BUTTON);// Присоединяем
// Конец создания меню
.................................................
Скорректируйте
обработку выбора пользователя в функции рендеринга файла 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;
}
// Исполнить (очистить) очередь команд
glFlush();}
Запустите
приложение и получите следующий результат при управлении
стрелками
Пунктирная или штриховая форма линий называется фактурой (stippling). Фактура линий включается командой
glEnable(GL_LINE_STIPPLE);
а выключается командой
glDisable(GL_LINE_STIPPLE);
После включения режима фактуры сама фактура, которая будет применяться при рисовании, устанавливается функцией
void glLineStipple(GLint factor, GLushort pattern);
Параметр -
это 16 - битовое значение, задающее шаблон (маску), который
нужно использовать при рисовании линии. Ненулевой бит шаблона
представляет рисуемый пиксел в линии, нулевой - нерисуемый.
Параметр используется
как множитель, увеличивающий ширину шаблона. Порядок задания
значения шаблона обратно противоположный его использованию
и представлен на рисунке

Начало линии определяет самый младший бит машинного представления
шаблона. Такой запутанный порядок определения фактуры связан
с тем, что гораздо быстрее смещать шаблон на одну позицию
влево всякий раз, когда требуется новое значение маски.
Теперь рассмотрим это на практике. Зададим шаблон 0x5555
= {0101'0101'0101'0101}, который определит равными штрихи и промежутки
в фактуре линии. Нарисуем несколько линий, для каждой из которых
будем увеличивать множитель шаблона.
Через
панель Solution Explorer добавьте к проекту новый файл с
именем LinesStipple.h.
Скопируйте
в него содержимое файла LinesW.h и модифицируйте содержимое,
чтобы окончательно файл LinesStipple.h выглядел так//**********************************************************
// Функция рисования линий с разной фактурой
void LinesStipple()
{
// Запомнить первоначальное состояние матрицы вращения
glPushMatrix();
// Настроить два последовательных поворота
// для будущей визуализации сцены
glRotatef(xRot, 1.0f, 0.0f, 0.0f);// Первое состояние матрицы вращения
glRotatef(yRot, 0.0f, 1.0f, 0.0f);// Следующее состояние
GLfloat sizes[2]; // Емкость для определения диапазона ширины линий
glGetFloatv(GL_LINE_WIDTH_RANGE, sizes);// Извлекаем диапазон
// Устанавливаем минимальный размер ширины линий
glLineWidth(sizes[0]);
// Устанавливаем начальные параметры фактуры
GLint factor = 1; // Начальный множитель шаблона
GLushort pattern = 0x5555; // Шестнадцатиричное представление шаблона
// Включаем режим фактуры
glEnable(GL_LINE_STIPPLE);
// Пошаговый проход по оси y с шагом 20 единиц снизу вверх
// Получится 10 линий по размеру отсекающего куба в плоскости z = 0
for(GLfloat y = -90.0f; y <= 90.0f; y += 20.0f){
// Устанавливаем множитель и шаблон
glLineStipple(factor, pattern);
// Посылаем на визуализацию каждую линию по отдельности
glBegin(GL_LINES);
// Задаем два конца линии в плоскости z = 0
glVertex2f(-90.0f, y);
glVertex2f(90.0f, y);
glEnd(); // Закончили визуализацию
factor += 3; // Увеличиваем множитель шаблона
}
// Восстанавливаем матрицу вращения в исходное состояние
glPopMatrix();
// Отключаем режим фактуры
glDisable(GL_LINE_STIPPLE);
}
Разместите
в конце файла Primitives.cpp включение файла LinesStipple.h................................................. #include "Points.h" // Включение перенесенного кода #include "PointsZ.h" // Включение кода рисования точек с увеличением размера #include "Lines.h" // Веер линий #include "LinesW.h" // Проверяется изменение ширины линий #include "LinesStipple.h" // Линии с разной фактурой .................................................
Добавьте
в начало файла Primitives.cpp LinesStipple()................................................. // Прототипы функций 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(); .................................................
Скорректируйте
меню в функции main() .................................................
// Создание меню и добавление опций выбора
glutCreateMenu(ExecuteMenu);
glutAddMenuEntry("Рисование функцией Points", 1);
glutAddMenuEntry("Точки с увеличением размера", 2);
glutAddMenuEntry("Веер линий", 3);
glutAddMenuEntry("Управление шириной линий", 4);
glutAddMenuEntry("Управление фактурой линий", 5);
glutAttachMenu(GLUT_RIGHT_BUTTON);// Присоединяем
// Конец создания меню
.................................................
Скорректируйте
обработку выбора пользователя в функции рендеринга//****************************************************
// Функция обратного вызова для рисования сцены
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;
}
// Исполнить (очистить) очередь команд
glFlush();
}
Запустите
приложение и получите следующий результат при управлении
стрелками![]() |
![]() |
В предыдущих упражнениях были рассмотрены вопросы, как рисовать
точки, линии и даже замкнутые многоугольники в трехмерном пространстве.
Но таких примитивов достаточно, чтобы нарисовать каркас объекта,
и недостаточно, чтобы представить его объем.
Для этого нужны многоугольники, как замкнутая форма, которая
может быть (а может и не быть) закрашена внутри текущим цветом.
Многоугольник является основой .
В нашем изложении понадобятся промежуточные результаты, иллюстрирующие некоторые вспомогательные положения темы. Давайте добавим к приложению вспомогательный файл, содержимое которого будем наполнять по мере необходимости некоторым временным кодом. Пусть этот файл отображается сразу же при запуске приложения.
В панели Solution Explorer выделите узел Primitives и командой меню Project/Add New Item добавьте к проекту файл Prompt.hПусть содержимое файла будет таким
//**********************************************************
// Вспомогательный файл для примеров
void Prompt()
{
// Запомнить первоначальное состояние матрицы вращения
glPushMatrix();
// Настроить два последовательных поворота
// для будущей визуализации сцены
glRotatef(xRot, 1.0f, 0.0f, 0.0f);// Первое состояние матрицы вращения
glRotatef(yRot, 0.0f, 1.0f, 0.0f);// Следующее состояние
// Блок визуализации
glBegin(GL_TRIANGLES);
glEnd(); // Закончили визуализацию
// Восстанавливаем матрицу вращения в исходное состояние
glPopMatrix();
}
Включите
этот файл инструкцией#include "Prompt.h"
в
#include "Points.h" // Включение перенесенного кода #include "PointsZ.h" // Включение кода рисования точек с увеличением размера #include "Lines.h" // Веер линий #include "LinesW.h" // Проверяется изменение ширины линий #include "LinesStipple.h" // Линии с разной фактурой #include "Prompt.h"
Добавьте в
начало файла Primitives.cpp Prompt()void Prompt();
Настройте
функцию рендеринга на вызов Prompt() сразу
при запуске приложения без вызова меню пользователя//****************************************************
// Функция обратного вызова для рисования сцены
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;
default:
Prompt();
}
// Исполнить (очистить) очередь команд
glFlush();
}
Простейшим многоугольником является треугольник. Примитив GL_TRIANGLES рисует треугольники, соединяя
три заданные вершины.
Поместите,
например, в блок Prompt() следующий
код//**********************************************************
// Вспомогательный файл для примеров
void Prompt()
{
// Запомнить первоначальное состояние матрицы вращения
glPushMatrix();
// Настроить два последовательных поворота
// для будущей визуализации сцены
glRotatef(xRot, 1.0f, 0.0f, 0.0f);// Первое состояние матрицы вращения
glRotatef(yRot, 0.0f, 1.0f, 0.0f);// Следующее состояние
// Блок визуализации
glBegin(GL_TRIANGLES);
glVertex2f(0.0f, 0.0f); // V0
glVertex2f(25.0f, 25.0f); // V1
glVertex2f(50.0f, 0.0f); // V2
glVertex2f(-50.0f, 0.0f); // V3
glVertex2f(-75.0f, 50.0f); // V4
glVertex2f(-25.0f, 0.0f); // V5
glEnd(); // Закончили визуализацию
// Восстанавливаем матрицу вращения в исходное состояние
glPopMatrix();
}
то получим такой результат
![]() |
![]() |
Давайте разберемся с понятием порядок обхода ( обход ).
В нашем коде порядок следования вершин при формировании треугольников
показан на правом рисунке. Для двух треугольников мы его определили
разным. Давайте будем считать, как это по
умолчанию принято в OpenGL, что если вершины
треугольника добавляются в порядке, который при
Обход является признаком для системы, какую сторону представляет соответствующая плоская фигура. Очень часто задним граням нужно приписать: или другие физические характеристики, или вообще сделать невидимыми до поры - до времени, или наделить другими отражательными свойствами и цветом. Постоянно наблюдая за направлением обхода при рисовании вершин многоугольника, можно вовремя определить момент его изменения и поменять характеристики грани.
Если поведение по интерпретации обхода
по умолчанию нужно изменить на противоположное (по часовой
стрелке), следует выполнить команду
glFrontFace(GL_CW);
Параметр GL_CW сообщает , что передней гранью нужно считать многоугольники с
обходом по часовой стрелке. Чтобу вернуться к обходу по умолчанию
(против часовой стрелки), следует указать в той же команде
параметр GL_CCW.
Для представления многих сложных поверхностей и форм приходится
рисовать много соединенных треугольников, которые обеспечивают
GL_TRIANGLE_STRIP в
функции glBegin(). Схема здесь такая: первый
треугольник задается первыми тремя точками, затем каждый новый
треугольник добавляется одной вершиной и сочленяется с двумя
последними вершинами предыдущего треугольника.

Помимо лент трегольников можно, используя примитив GL_TRIANGLE_FAN в
функции glBegin(), создавать вееры треугольников,
которые расходятся из центральной точки.
В качестве иллюстрации к сказанному построим простую программу,
в которой с помощью двух вееров треугольников создается конус
в наблюдаемом объеме. Первый веер задает форму конуса, используя
первую точку в качестве вершины конуса, а все остальные - как
точки на 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); // Зеленый
}
Включите
этот файл инструкцией#include "triangle.h"
в
#include "Points.h" // Включение перенесенного кода #include "PointsZ.h" // Включение кода рисования точек с увеличением размера #include "Lines.h" // Веер линий #include "LinesW.h" // Проверяется изменение ширины линий #include "LinesStipple.h" // Линии с разной фактурой #include "triangle.h" // Рисовать конус #include "Prompt.h" // Вспомогательный код для начального окна
Добавьте
в начало файла Primitives.cpp Triangle ()// Прототипы функций ................................................... void Triangle();
Настройте
переключатель 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();
}
В функции построения конуса выделен
код, обеспечивающий установки трех флагов для управления отбором,
глубиной и рисованием каркаса. Эти флаги bCull, bDepth, bOutline нужно
сделать глобальными, чтобы они виделись во всех требуемых местах
программы.
Поместите перед функцией main() файла Primitives.cpp объявление глобальных флагов.......................
// Глобальная переменная выбранного варианта основного меню
int choice = 0;
// Переменные для управления отбором, глубиной и каркасом в Triangle()
bool bCull = false, bDepth = true, bOutline = false;
// Точка входа приложения
void main(void)
{
....................................
}
Для того, чтобы иметь возможность управлять этими параметрами
при рисовании конуса, воспользуемся средством библиотеки 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(); // Цикл сообщений графического окна
}
Добавьте
вначало файла 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)
{
....................................
}
Разместите
после функции main() код заготовки функции TriangleExecuteMenu() обработки
выбора пользователя в подменю//****************************************************
// Функция обратного вызова обработки выбора пользователя в подменю
void TriangleExecuteMenu(int choice)
{
}
Запустите
приложение - теперь меню пользователя должно выглядеть так
Заполните
заготовку функции 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();
}
Запустите
приложение, выберите режим рисования конуса и получите следующий
результат при управлении стрелками и с разными установками
Включена глубина |
Включен отбор |
Включен каркасный режим отображения невидимых граней (с противоположным направлением обхода) |
Основание конуса при выключенном отборе |
Мы использовали для закрашивания фигуры ,
многоугольник может закрашиваться сплошным цветом (используя
текущий цвет, выбранный при задании командой glVertex2f(x,
y); последней вершины) или гладко затеняться
с градиентным переходом цветов друг в друга, заданных для составляющих
его вершин (создается
Строка
// Устанавливаем неструктурированный цвет модели затенения glShadeModel(GL_FLAT);
приказывает заполнять многоугольник сплошным цветом,
который будет текущим на момент задания последней вершины многоугольника.
Именно поэтому мы смогли так просто менять текущий цвет с одного
на другой перед указанием новой вершины веера треугольников.
Если внутри указанной функции использовать ключ GL_SMOOTH,
то затенит треугольники плавно, пытаясь интерполировать
цвета точек, находящихся между вершинами с заданным цветом,
примерно так...

При включении эффекта глубины заботится о том, чтобы
наблюдатель не видел тех частей объекта, которые должны быть
заслонены частями переднего плана. Проверка глубины - это эффективная
технология удаления скрытых поверхностей и выполняет
соответствующие уточнения рисунка.
Схема проверки глубины проста. Когда пиксел рисуется, ему
присваивается значение (глубина z ), означающее расстояние до
положения наблюдателя, и это значение запоминается в буфере
глубины. Когда позже в этой же точке нужно нарисовать другой
пиксел, глубина z этого пиксела сравнивается с значением глубины
ранее записанного пиксела. Если значение z нового пиксела больше,
значит новая точка находится ближе к наблюдателю и заменяет
предыдущую. В противном случае новый пиксел не рисуется и его
значение глубины не запоминается. Глубины каждого изображенного
на экране пиксела хранятся в участке памяти, называемом буфером
глубины.
Чтобы активизировать проверку глубины, нужно вызывать такую функцию
glEnable(GL_DEPTH_TEST);
а для деактивизации - функцию
glDisable(GL_DEPTH_TEST);
В нашей программе включение/выключение
проверки глубины выполняется с помощью флага bDepth.
Буфер глубины является в некотором смысле аналогом буфера
цвета, только в буфере цвета содержатся
коды цвета отображаемых на экране пикселов, а в буфере
глубины - их глубина. При каждой новой
// Очистить окно и буфер глубины glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
Проверка глубины просто необходима при создании трехмерных
объектов из сплошных многоугольников для правильной их
Отбор ( определяет лицевую и обратную стороны
граней по направлению обхода.
Как только перед отображением грани обнаруживает, что
многоугольник имеет направление обхода, противоположное установленному
для лицевых граней, то эта грань считается невидимой и не обрабатывается
для
В нашей программе мы установили
направление обхода для видимых граней по часовой стрелке и
с таким же направлением обхода рисовали основание конуса. Но
в таком случае лицевая сторона веера всех многоугольников,
составляющих основание конуса, получилась направлена внутрь
конуса. А обратная сторона веера всех треугольников основания
направлена наружу и считается невидимой. Поэтому при включении
отбора не обрабатывается системой, то есть остается невизуализированной.
Чтобы исправить положение, нужно непосредственно
перед рисованием второго веера треугольников изменить
направление обхода командой
// Устанавливаем направление обхода против часовой стрелки glFrontFace(GL_CCW);
После этого веер треугольников основания конуса, повернутого
к зрителю, будет считаться лицевой
стороной и все будет хорошо!
Отбор включен. Веер сформирован изначально как с противоположным видимому обходом (невидимой гранью внуть конуса) |
Отбор включен. Веер основания сформирован изначально как направленный лицевой стороной внутрь конуса |
|
Отбор выключен. Веер сформирован изначально как с противоположным видимому обходом (невидимой гранью внуть конуса, а лицевой - наружу) |
Отбор выключен. Веер основания сформирован изначально как направленный лицевой стороной внутрь конуса, но для наблюдателя визуализируется так, как будто внутренняя закрашенная сторона основания просвечивается наружу через это основание |
Отбор задних граней полезен при рисовании замкнутых, сплошных
объектов или твердых тел. Технология
По умолчанию многоугольники рисуются в режиме сплошной заливки
( ). Но можно указать, чтобы они
отображались как glPolygonMode() устанавливает
эти режимы отображения. Любой из режимов можно применить отдельно
к рисованию лицевой или обратной сторонам граней или сразу
к двум сторонам каждого многоугольника. В нашем коде это выглядит
так
// Включить/Выключить каркас задней стенки
if(bOutline)
glPolygonMode(GL_BACK,GL_LINE);
else
glPolygonMode(GL_BACK,GL_FILL);
| Цель: | Установить режим |
| Включаемый файл: | <gl.h> |
| Прототип: | , void glPolygonMode(GLenum |
| Описание: | Приведенная функция позволяет менять способ |
| Параметры: | - задает, на какие стороны многоугольника распространяется
режим: GL_FRONT, GL_BACK, GL_FRONT_AND_BACK mode -
задает новый режим рисования. По умолчанию значение
равно GL_FILL,
т.е. создается закрашенный многоугольник. При выборе GL_LINE получим
GL_POINT получим
только вершины. На линии и точки, рисуемые с помощью GL_LINE и GL_POINT,
влияет метка сторон, установленная с помощью glEdgeFlag. |
Треугольники в
glBegin(GL_QUADS); ..................... glEnd();
Для
glBegin(GL_QUAD_STRIP); ..................... glEnd();
Полигоны задаются с помощью команды
glBegin(GL_POLYGON); ..................... glEnd();
При задании вершин одного прямоугольника или полигона важно
следить за тем, чтобы все вершины лежали в одной GL_TRIANGLE_FAN вместо GL_POLYGON.
Существует два способа (помимо структурированной или градиентной заливки) наложить на поверхность многоугольника какой-либо рисунок:
Здесь мы рассмотрим второй способ, а про
Фактурный узор многоугольника - это не более чем монохромное растровое изображение-маска размером 32x32 пиксела, используемое как узор-заполнитель. Включение режима заполнения многоугольника фактурой выполняется следующей последовательностью команд
glEnable(GL_POLYGON_STIPPLE); glPolygonStipple(pBitmap);
pBitmap - это указатель на узор-заполнитель
типа ( GLubyte * -
Рассмотрим маску костра, которую будем реализовывать в нашем упражнении рисования многоугольников с фактурой. Построчный порядок задания значения шаблона обратно противоположный его использованию

Через
панель Solution Explorer добавьте к проекту
новый header -файл
(
Заполните
этот файл следующим кодом// Описание битовой маски костра 32x32 снизу вверх
GLubyte fire[128] =
{
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xc0,
0x00, 0x00, 0x01, 0xf0,
0x00, 0x00, 0x07, 0xf0,
0x0f, 0x00, 0x1f, 0xe0,
0x1f, 0x80, 0x1f, 0xc0,
0x0f, 0xc0, 0x3f, 0x80,
0x07, 0xe0, 0x7e, 0x00,
0x03, 0xf0, 0xff, 0x80,
0x03, 0xf5, 0xff, 0xe0,
0x07, 0xfd, 0xff, 0xf8,
0x1f, 0xfc, 0xff, 0xe8,
0xff, 0xe3, 0xbf, 0x70,
0xde, 0x80, 0xb7, 0x00,
0x71, 0x10, 0x4a, 0x80,
0x03, 0x10, 0x4e, 0x40,
0x02, 0x88, 0x8c, 0x20,
0x05, 0x05, 0x04, 0x40,
0x02, 0x82, 0x14, 0x40,
0x02, 0x40, 0x10, 0x80,
0x02, 0x64, 0x1a, 0x80,
0x00, 0x92, 0x29, 0x00,
0x00, 0xb0, 0x48, 0x00,
0x00, 0xc8, 0x90, 0x00,
0x00, 0x85, 0x10, 0x00,
0x00, 0x03, 0x00, 0x00,
0x00, 0x00, 0x10, 0x00
};
// Рисуем полигон в форме дорожного знака
// с заливкой по битовой маске
void PolygonStipple()
{
// Очистить окно рисования
glClear(GL_COLOR_BUFFER_BIT);
// Запомнить первоначальное состояние матрицы вращения
glPushMatrix();
// Настроить два последовательных поворота
// для будущей визуализации сцены
glRotatef(xRot, 1.0f, 0.0f, 0.0f);// Первое состояние матрицы вращения
glRotatef(yRot, 0.0f, 1.0f, 0.0f);// Следующее состояние
// Включить красный цвет рисования
glColor3f(1.0f, 0.0f, 0.0f);
// Включить фактуру при заполнении полигонов
glEnable(GL_POLYGON_STIPPLE);
// Адресовать битовую маску костра для фактуры
glPolygonStipple(fire);
// Нарисовать полигон в форме дорожного знака
// "Движение без остановки запрещено",
// закрашенного по маске костра красным цветом
glBegin(GL_POLYGON);
glVertex2f(-20.0f, 50.0f);
glVertex2f(20.0f, 50.0f);
glVertex2f(50.0f, 20.0f);
glVertex2f(50.0f, -20.0f);
glVertex2f(20.0f, -50.0f);
glVertex2f(-20.0f, -50.0f);
glVertex2f(-50.0f, -20.0f);
glVertex2f(-50.0f, 20.0f);
glEnd();
glColor3f(0.0f, 1.0f, 0.0f); // Восстановить цвет рисования зеленый
// Выключить фактуру при заполнении полигонов
glDisable(GL_POLYGON_STIPPLE);
// Восстановить матрицу вращения в исходное состояние
glPopMatrix();
}
Разместите
в начале файла Primitives.cpp PolygonStipple()#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 TriangleExecuteMenu(int); void Triangle(); void PolygonStipple(); ..................................
Добавьте
в функцию main() файла Primitives.cpp новый пункт меню для
вызова нового упражнения// Точка входа приложения
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);
// Число 6 у нас задействовано для субменю
glutAddMenuEntry("Управление фактурой полигонов", 7);
glutAttachMenu(GLUT_RIGHT_BUTTON);// Присоединяем
// Конец создания меню
glutSpecialFunc(SpecialKeys); // Для управления с клавиатуры
glutMainLoop(); // Цикл сообщений графического окна
}
Добавьте
в функцию рендеринга RenderScene() файла 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;
case 7:
PolygonStipple();
break;
default:
Prompt();
}
// Исполнить (очистить) очередь команд
glFlush();
}
Добавьте
в #include "Points.h" // Включение перенесенного кода #include "PointsZ.h" // Включение кода рисования точек с увеличением размера #include "Lines.h" // Веер линий #include "LinesW.h" // Проверяется изменение ширины линий #include "LinesStipple.h" // Линии с разной фактурой #include "triangle.h" // Рисовать конус #include "PolygonStipple.h" // Рисовать полигон с закраской по маске #include "Prompt.h" // Вспомогательный код для начального окна
Запустите
приложение - должен получиться следующий результат![]() |
![]() |
Обратите внимание, что описание строк битовой маски в массиве fire[128] выполняется в обратном порядке по отношению к самой
маске, приведенной ранее на рисунке. Такой порядок заведен
разработчиками .
Если в файле PolygonStipple.h закомментировать строку
// Включить фактуру при заполнении полигонов // glEnable(GL_POLYGON_STIPPLE);
включения заливки по маске, то полигон в начальном положении будет отображаться так

Обратите также внимание на то, что при вращении плоского полигона в трехмерном пространстве маска не поворачивается вместе с ним. Узор-заполнитель применяется только для простой заливки многоугольника на экране. Если же нам необходимо отобразить рисунок на сам многоугольник, чтобы он имитировал поверхность многоугольника, то применяется технология наложения текстуры.
Мы уже использовали ранее в упражнении каркасный
режим рисования с помошью кода
// Включить/Выключить каркас задней стенки
if(bOutline)
glPolygonMode(GL_BACK,GL_LINE);
else
glPolygonMode(GL_BACK,GL_FILL);
и получали такой результат

Треугольные примитивы, составляющие фигуру, в каркасном режиме
имеют границы на сторонах. Иногда некоторые границы хочется
отключить - и это можно сделать. имеет
функцию glEdgeFlag() ( метка
стороны ), которая может помечать сторону
при рисовании как отображаемую или нет. Код glEdgeFlag(true) помечает
все следующие за ним линии примитивов как отображаемые в каркасном
режиме, код glEdgeFlag(false) задает
альтернативный режим. Естественно, что все это действует при
условии, что включен каркасный режим функцией glPolygonMode()
| Цель: | Пометить стороны многоугольника как граничные или неграничные. |
| Включаемый файл: | <gl.h> |
| Прототип: | void glEdgeFlag(GLboolean |
| Описание: | При объединении нескольких многоугольников внешние стороны формируют границы новой области. Функция помечает внутренние стороны как граничные (true - по умолчанию) или неграничные (false). Используется только в режимах многоугольников GL_LINE или GL_POINT. |
| Параметры: | - устанавливает
метку края составляющего область многоугольника
как отображаемую в режиме GL_LINE или GL_POINT функции glPolygonMode() - устанавливает
метку края составляющего область многоугольника
как неотображаемую в режиме GL_LINE или GL_POINT функции glPolygonMode () |
Проверим, как это работает, на простом примере: нарисуем пятиконечную звезду с координатами, приведенными на рисунке

Размеры
В панели Solution Explorer добавьте к приложению
новый h-файл с именем Star.h
Скопируйте
содержимое файла Triangle.h в файл Star.h и
модифицируйте код, чтобы окончательно он имел следующий видvoid Star()
{
// Очистить окно просмотра
glClear(GL_COLOR_BUFFER_BIT);
// Включить каркасный режим для обеих сторон звезды
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
// Запомнить первоначальное состояние матрицы вращения
glPushMatrix();
// Выполнить два последовательных поворота
// для будущей визуализации сцены
glRotatef(xRot, 1.0f, 0.0f, 0.0f);
glRotatef(yRot, 0.0f, 1.0f, 0.0f);
//************ Рисовать внутренние контуры звезды *************
// Рисовать веер треугольников
glBegin(GL_TRIANGLES);
// Устанавливаем переключатель метки сторон
glEdgeFlag(bEdgeFlag);
// Устанавливаем вершины
glVertex2f(0.0f, 0.0f); // Центр звезды
glVertex2f(-18.0f, 26.0f);
glVertex2f(18.0f, 26.0f);
glVertex2f(0.0f, 0.0f);
glVertex2f(18.0f, 26.0f);
glVertex2f(30.0f, -10.0f);
glVertex2f(0.0f, 0.0f);
glVertex2f(30.0f, -10.0f);
glVertex2f(0.0f, -32.0f);
glVertex2f(0.0f, 0.0f);
glVertex2f(0.0f, -32.0f);
glVertex2f(-30.0f, -10.0f);
glVertex2f(0.0f, 0.0f);
glVertex2f(-30.0f, -10.0f);
glVertex2f(-18.0f, 26.0f);
glEnd();
//*********** Рисовать внешние треугольники звезды **************
glBegin(GL_TRIANGLES);
glEdgeFlag(true);
glVertex2f(-18.0f, 26.0f);
glVertex2f(0.0f, 80.0f); glEdgeFlag(false);
glVertex2f(18.0f, 26.0f);
glEdgeFlag(true);
glVertex2f(18.0f, 26.0f);
glVertex2f(80.0f, 26.0f); glEdgeFlag(false);
glVertex2f(30.0f, -10.0f);
glEdgeFlag(true);
glVertex2f(30.0f, -10.0f);
glVertex2f(50.0f, -68.0f); glEdgeFlag(false);
glVertex2f(0.0f, -32.0f);
glEdgeFlag(true);
glVertex2f(0.0f, -32.0f);
glVertex2f(-50.0f, -68.0f); glEdgeFlag(false);
glVertex2f(-30.0f, -10.0f);
glEdgeFlag(true);
glVertex2f(-30.0f, -10.0f);
glVertex2f(-80.0f, 26.0f); glEdgeFlag(false);
glVertex2f(-18.0f, 26.0f);
glEnd();
// Восстанавливаем измененные состояния в исходные значения
glPopMatrix();
glPolygonMode(GL_BACK,GL_FILL);
}
Внесите
в файл Primitives.cpp выделенные ниже добавления
для вызова упражнения#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 TriangleExecuteMenu(int);
void Triangle();
void PolygonStipple();
void Star();
void StarExecuteMenu(int);
// Глобальная переменная выбранного варианта основного меню
int choice = 0;
// Переменные для управления отбором, глубиной и каркасом в Triangle()
bool bCull = false, bDepth = true, bOutline = false;
// Метки сторон
bool bEdgeFlag = true;
// Точка входа приложения
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);
// Создаем подменю для рисования звезды
int star_menu = glutCreateMenu(StarExecuteMenu);
glutAddMenuEntry("Метки сторон Вкл.", 0);
glutAddMenuEntry("Метки сторон Выкл.", 1);
// Создание меню и добавление опций выбора
glutCreateMenu(ExecuteMenu);
glutAddSubMenu("Рисование конуса", triangle_menu);// Именно на первом месте
glutAddMenuEntry("Рисование функцией Points", 1);
glutAddMenuEntry("Точки с увеличением размера", 2);
glutAddMenuEntry("Веер линий", 3);
glutAddMenuEntry("Управление шириной линий", 4);
glutAddMenuEntry("Управление фактурой линий", 5);
// Число 6 у нас задействовано для субменю
glutAddMenuEntry("Управление фактурой полигонов", 7);
glutAddSubMenu("Рисование звезды", star_menu);
glutAttachMenu(GLUT_RIGHT_BUTTON);// Присоединяем
// Конец создания меню
glutSpecialFunc(SpecialKeys); // Для управления с клавиатуры
glutMainLoop(); // Цикл сообщений графического окна
}
//****************************************************
// Функция обратного вызова обработки выбора пользователя в подменю
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();
}
//****************************************************
// Функция обратного вызова обработки выбора пользователя в подменю
void StarExecuteMenu(int choice)
{
switch(choice){
case 0:
bEdgeFlag = true;
break;
case 1:
bEdgeFlag = false;
}
// Устанавливаем режим и вызываем принудительно
// функцию визуализации, где размещен
// вызов функции рисования звезды
::choice = 8;
// Вызвать принудительно визуализацию
glutPostRedisplay();
}
// Глобальные переменные для создания вращения
GLfloat xRot = 0.0f;
GLfloat yRot = 0.0f;
//****************************************************
// Функция обратного вызова обработки выбора пользователя
void ExecuteMenu(int choice)
{
// Сбрасываем углы вращения прежнего варианта
xRot = yRot = 0;
// Передаем выбор в глобальную переменную
::choice = choice;
// Вызвать принудительно визуализацию
glutPostRedisplay();
}
//****************************************************
// Функция обратного вызова для рисования сцены
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;
case 7:
PolygonStipple();
break;
case 8:
Star();
break;
default:
Prompt();
}
// Исполнить (очистить) очередь команд
glFlush();
}
#include "Points.h" // Включение перенесенного кода
#include "PointsZ.h" // Включение кода рисования точек с увеличением размера
#include "Lines.h" // Веер линий
#include "LinesW.h" // Проверяется изменение ширины линий
#include "LinesStipple.h" // Линии с разной фактурой
#include "triangle.h" // Рисовать конус
#include "PolygonStipple.h" // Рисовать полигон с закраской по маске
#include "Prompt.h" // Вспомогательный код для начального окна
#include "Star.h" // Управление метками сторон
Запустите
приложение и получите результат для текущего упражнения![]() |
![]() |
Заметьте, что функциональность по вращению объекта у нас сохранилась во всех упражнениях.

Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.