Файлы к лабораторной работе Вы можете скачать здесь.
Реализация библиотеки Microsoft (Open
C начинаются с префикса glGLU - библиотеку). Команды этой библиотеки
дополняют OpenGL и позволяют выполнять триангуляцию
многоугольников, создавать и выводить стандартные фигуры
(сферы, цилиндры, ...), строить сплайновые кривые и поверхности,
обрабатывать ошибки. Имена
команд библиотеки утилит в реализации на C начинаются с префикса gluOpenGL ( AUX -
библиотеку). Библиотека содержит функции управления окнами,
3D -объектов
(тор, тетраэдр, ...), управления двойной C начинаются с префикса aux OpenGL с Windows ( WGL - функции).
Эти функции управляют контекстом воспроизведения, списками
команд, шрифтами. Имена WGL - функций начинаются с префикса wglWin32 - функции для управления форматов пикселов и двойной
AUX .На этом этапе мы создадим простую программу с использованием
графической библиотеки . Для этого мы создадим консольное
приложение на C++ и настроим его на вызов функций библиотеки .
Выполните
команду меню оболочки File/New/Project
В появившемся
окне мастера создания заготовки проекта задайте имя проекта Simple, выберите каталог размещения проекта и другие установки,
как показано на рисунке
Оставьте
установки мастера по умолчанию и щелкните на кнопке Finish 
Переименуйте
функцию _tmain() файла Simple.cpp в main(), уберите параметры
в заголовке main() и возвращаемое
значение, чтобы код выглядел так// Simple.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
int main()
{
}
Теперь необходимо сообщить линковщику о необходимости использования
библиотечных файлов (хотя это и не обязательно, поскольку
далее мы используем вспомогательный заголовочный файл glut.h,
где все это будет прописано).
Для этого:
Выполните
команду меню оболочки Project/Simple Properties и
в открывшемся диалоговом окне в поле Linker /Input/Additional
Dependencies введите разделенные пробелами наименования
библиотекopengl32.lib glu32.lib glaux.lib

Необходимые подключемые файлы OpenGL хранятся по адресу: C:\Program Files\Microsoft Visual Studio 8\VC\PlatformSDK\Include\gl |
Необходимые статические библиотеки OpenGL хранятся в файловой системе компьютера по адресу: C:\Program Files\Microsoft Visual Studio 8\VC\PlatformSDK\Lib |
Необходимые динамические библиотеки OpenGL хранятся в системном каталоге по адресу: C:\WINNT\system32 |
Но нам понадобится дополнительно еще одна статическая библиотека glut32.lib и одна динамическая библиотека glut32.dll, а также один заголовочный файл glut.h - все они находятся в каталоге Source, прилагаемом к этой лабораторной работе!!!
Скопируйте
из каталога Source данной лабораторной
работы файл glut32.lib в
каталог статических библиотек, файл glut32.dll в
каталог динамических библиотек, а файл glut.h -
в каталог размещения подключаемых файлов Вашего
компьютера (примерные адреса размещения перечислены
выше)
Через
панель Solution Explorer оболочки откройте файл stdafx.h,
который создал мастер проекта, и добавьте в самое его начало после инструкции #pragma once код подключения стандартных // stdafx.h : include file for standard system include files, // or project specific include files that are used frequently, but // are changed infrequently // #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> // TODO: reference additional headers your program requires here
Заполните
Simple.cpp следующим кодом// Simple.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
// Прототипы функций
void RenderScene(void);
void SetupRC(void);
// Точка входа приложения
void main(void)
{
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA);
glutCreateWindow("Simple");
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-канал
}
Запустите
приложение на выполнениеОболочка выдаст предупреждение, что данная форма проекта является устаревшей.

Установите
флажок Do not show this dialog again и щелкните
на кнопке 19_16Должен получиться такой результат

Наша простая программа содержит вызовы четырех функций библиотеки GLUT (с префиксом glut ) и три вызова "чистых"
функций (с префиксом gl ). Вспомогательная
библиотека GLUT ( - набор инструментов ) написана Марком Килгардом ( содействует обучению и такому написанию программ , чтобы
программист не занимался деталями конкретной среды, а сосредоточился
на написании основного кода графического приложения. Библиотеку GLUT можно бесплатно найти в Internete.
Сама наша программа делает немного. Она создает стандартное
окно графического интерфейса пользователя ( ) с надписью Simple и синим фоном. В файл stdafx.h помещены подключения
заголовков
Строка
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA);
устанавливает режим отображения графического окна при его
создании. Соединение GLUT_SINGLE )
и режим цвета RGB с альфа-каналом ( GLUT_RGBA или GLUT_RGB ). Альтернативным режимом задания цветов является режим
использования индексированных цветов ( GLUT_INDEX ). Одиночная
буферизация означает, что все команды рисования
выполняются прямо в отображаемом окне (на контексте, холсте).
Существует еще двойная буферизация (задается
параметром GLUT_DOUBLE ),
когда команды рисования выполняются в буфере вне экрана, а
затем быстро отображаются в окне.
Строка
glutCreateWindow("Simple");
создает графическое окно с надписью в строке заголовка из параметра вызова.
Строка
glutDisplayFunc(RenderScene);
устанавливает нашу функцию RenderScene как функцию обратного вызова (call back),
в которой можно размещать вызовы функций визуализации .
Строка
SetupRC();
является вызовом нашей функции упаковки кода, в которой разместили
всю необходимую на данном этапе инициализацию , которую
нужно выполнить перед визуализацией окна. Многие состояния устанавливаются только один раз и их не нужно обновлять
при каждой визуализации кадра (экрана, заполненного графикой).
Строка
glutMainLoop();
вводит приложение в цикл сообщений. Она должна быть вызвана
только один раз и работает до завершения приложения. Она обрабатывает
обрабатывает все сообщения, связанные с операционной системой
(
Строка
glClearColor(0.0F, 0.0F, 1.0F, 1.0F);// RGB + alpha-канал
устанавливает цвет модели RGB, используемый для очистки окна.
Получая код цвета, Windows и преобразуют его в ближайшую
точную величину, поддерживаемую конкретной видеоаппаратурой.
Последний аргумент в вызове этой функции определяет величину альфа-канала - уровень прозрачности (1 - нeпрозрачный, 0 -
абсолютно прозрачный).
Строка
glClear(GL_COLOR_BUFFER_BIT);
выполняет собственно очистку. Она может очищать определенный
буфер или комбинацию буферов. Буфер -
это область хранения информации об изображении. В существует
несколько типов буферов: цвета, глубины, трафарета
и накопления. С помощью рассматриваемой команды удаляют из
окна последний отображаемый рисунок.
Обычно решение о подвижке очереди команд принимает сама операционная система. Но строка
glFlush();
дает указание системе выполнить все невыполненные команды немедленно и освободить очередь команд. Если не обращаться
к этой функции, изображение все равно появится на экране, но
с видимой задержкой вывода, особенно при наличии на компьютере
сетевого оборудования. В приведенном выше коде у нас есть только
одна команда для немедленного выполения - glClear().
Во внутреннем
представлении используется конвейер, последовательно
обрабатывающий команды. Команды часто выстраиваются
в очередь, чтобы потом драйвер обработал несколько
команд одновременно. Такая схема увеличивает быстродействие,
поскольку связь с
Усложним нашу программу, добавив возможность рисования прямоугольника в центре окна. Добавьте к проекту новый файл. Для этого:
В панели Solution Explorer щелкните правой кнопкой
мыши на
узле проекта и через контекстное меню
выполните команду Add/New Item
В открывшемся
окне задайте имя файла GLRect, а тип и размещение выберите
в соответствии со снимком
Нажмите
кнопку Add и к проекту добавится новый
пустой файл C++
Исключите
из проекта прежний файл Simple.cpp . Для
этого в панели Solution
Explorer щелкните правой кнопкой мыши на этом файле
и через контекстное меню выполните команду RemoveФайл можно исключить из проекта и клавишей . В этом случае
оболочка выдаст диалоговое окно

Кнопка Delete удалит файл физически, а кнопка Remove только
исключит файл из проекта. Физически на диске он останется,
но в проекте участвовать не будет. В последующем его всегда
можно будет вновь добавить к проекту.
Щелкните
на пиктограмме Show All Files панели Solution
Explorer и
убедитесь, что файл Simple.cpp действительно
существует
Заполните
файл GLRect.cpp следующим кодом#include "stdafx.h"
// Прототипы функций
void RenderScene(void);
void SetupRC(void);
void ChangeSize(int, int);
//**********************************************************
// Точка входа приложения
void main()
{
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(800, 600);
glutCreateWindow("GLRect");
glutDisplayFunc(RenderScene);
glutReshapeFunc(ChangeSize);
SetupRC();
glutMainLoop();
}
//**********************************************************
// Вызывается для рисования сцены
void RenderScene(void)
{
// Очищаем окно текущим цветом очистки
glClear(GL_COLOR_BUFFER_BIT);
// Устанавливаем текущим цветом рисования красный
// в модели R G B
glColor3f(1.0f, 0.0f, 0.0f);
// Рисуем прямоугольник, заполненный текущим цветом
glRectf(-50.0f, 50.0f, 50.0f, -50.0f);
// Выполняет всю очередь текущих команд
glFlush();
}
//**********************************************************
// Устанавливаем состояние визуализации
void SetupRC(void)
{
// Устанавливаем цвет очистки синий непрозрачный
glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
}
//**********************************************************
// Вызывается библиотекой GLUT при изменении размеров окна
void ChangeSize(int width, int height)
{
GLfloat aspectRatio;
// Предотвращаем деление на нуль
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 (-100.0, 100.0, -100 / aspectRatio,
100.0 / aspectRatio,
1.0, -1.0);
else
glOrtho (-100.0 * aspectRatio,
100.0 * aspectRatio,
-100.0, 100.0,
1.0, -1.0);
// Восстановливает матрицу преобразования в исходный режим вида
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
Запустите
приложение и получите результат
При программировании с использованием библиотеки часто
более удобно пользоваться псевдонимами C. Эти типы определены
в
typedef unsigned int GLenum; typedef unsigned char GLboolean; typedef unsigned int GLbitfield; typedef signed char GLbyte; typedef short GLshort; typedef int GLint; typedef int GLsizei; typedef unsigned char GLubyte; typedef unsigned short GLushort; typedef unsigned int GLuint; typedef float GLfloat; typedef float GLclampf; typedef double GLdouble; typedef double GLclampd; typedef void GLvoid;
Вызов
glutInitWindowSize(800, 600);
устанавливает начальные размеры графического окна.
Функции RenderScene() и ChangeSize() являются функциями обратного
вызова. Остановимся на функции ChangeSize(), которая срабатывает
каждый раз при изменении размеров окна.
Практически во всех средах с управлением окнами пользователь может изменить размеры окна. Даже если приложение запускается сразу в полноэкранном режиме, все равно считается, что окно меняет размер один раз при создании и настройке под конкретное разрешение экрана. Иногда нужно просто разместить иллюстрацию по клиентской области окна с соблюдением пропорций рисунка или без них.
void ChangeSize(int width, int height)
вызывается всякий раз при изменении размеров окна и принимает новые параметры ширины и высоты клиентской области окна.
Код
// Предотвращаем деление на нуль
if(height == 0)
height = 1;
предотвращает возникновение системного
Внутри клиентской области функция
void glViewport(GLint x, GLint y, GLsizei width, GLsizei height)
устанавливает поле просмотра для .
Поле просмотра - это та часть клиентской области, куда
отображается графическая информация, генерируемая функциями . Параметры x,
y задают левый
нижний угол поля просмотра внутри окна, а параметры
ширины и высоты определяют эти размеры в пикселах. Обычно x, y равны нулю, но поля просмотра можно использовать для размещения
нескольких рисунков в разных частях окна.Поле просмотра определяет
область внутри окна в реальных экранных координатах. Если задать
поле просмотра меньше окна, визуализация будет соответствующим
образом масштабирована.

В нашем обращении
// Устанавливаем поле просмотра с размерами окна
glViewport(0, 0, width, height);
поле просмотра размещается в пределах всей клиентской области,
поскольку в
void ChangeSize(int width, int height)
передаются текущие значения ширины и высоты именно клиентской области.
Описание трехмерного объекта представляется в трехмерной системе
координат объекта. Наиболее простая - ортогональная система
координат. В базовой принята правая система
координат (большой палец правой руки
направлен по X, указательный по Y, средний по Z ).
Кодом
// Рисуем прямоугольник, заполненный текущим цветом glRectf(-50.0f, 50.0f, 50.0f, -50.0f);
мы нарисовали прямоугольник (заданным ранее красным цветом),
который расположен симметрично относительно начала системы
координат объекта с координатой глубины z = 0.
| Цель: | Нарисовать плоский прямоугольник |
| Варианты: | void glRectd(GLdouble x1, GLdouble y1, GLdouble x2,
GLdouble y2) ;void glRectf(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat
y2) ; |
| Описание: | Приведенная функция предлагает простой метод задания
прямоугольника через координаты противоположных вершин.
Прямоугольник задается в |
| Параметры: |

Если не устанавливать отсекающий объем ( перспективу )
при ортогональном проецировании, то в поле просмотра будут
помещены (предварительно преобразованные в растр) все
точки прямоугольника. При этом размеры прямоугольника,
установленные функцией glRectf() не будут иметь никакого значения
- прямоугольник растянется по всему полю просмотра.
Чтобы проецировать прямоугольник в требуемом аспекте, необходимо вокруг него создать отсекающий объем в виде параллелепипеда, который и будет целиком отображаться в поле просмотра вместе с размещенным в нем прямоугольником.

Отображение в поле просмотра экранного окна будет таким, каким
спроецируется прямоугольник на переднюю стенку параллелепипеда
расположенным сзади параллельным (бесконечно удаленным) источником
света, или каким он будет в рамках отсекающего объема на x0y.
Поле просмотра мы задали на всю
aspectRatio = (GLfloat)width / (GLfloat)height;
if (width <= height)
glOrtho (-100.0, 100.0, -100 / aspectRatio,
100.0 / aspectRatio,
1.0, -1.0);
else
glOrtho (-100.0 * aspectRatio,
100.0 * aspectRatio,
-100.0, 100.0,
1.0, -1.0);
который при каждом изменении размеров окна корректирует координаты
отсекающего параллелепипеда так, чтобы сохранялось соотношение
сторон ширины и высоты, обратное соотношению размеров клиентской
области графического окна приложения. Характеристическое число aspectRatio определяет
соотношение сторон и используется как раз для этих целей.
Приведенный код определения отсекающего объема (перспективы) окаймляется следующими вызовами
// Устанавливает матрицу преобразования в режим проецирования
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
.......................................................
// Восстановливает матрицу преобразования в исходный режим вида
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
В системах компьютерной графики, использующих конвейерный
принцип обработки, желательно, чтобы все операции формирования
изображения выполнялись единообразно - в форме перемножения
матриц. Ряд матриц преобразования являются и сохраняют свои последние значения до тех пор, пока
их принудительно не изменят. Существует много матриц преобразования,
в том числе матрица проецирования и матрица
вида. Доступ к каждой из этих матриц осуществляется
функций glMatrixMode() с
соответствующим glLoadIdentity() применяет
соответствующую матрицу к
Хорошим тоном
считается восстанавливать систему в исходное состояние
для использования в других областях программы, поэтому применяются
два последних вызова приведенного фрагмента кода.
Усложним нашу программу, добавив возможность рисования квадрата так, чтобы он рикошетом отскакивал от сторон окна.
Добавьте к проекту новый файл. Для этого:
В панели Solution Explorer щелкните правой кнопкой
мыши на
узле проекта и через контекстное меню выполните
команду Add/New Item
В
открывшемся окне задайте имя файла Bounce,
а тип и размещение выберите в соответствии со снимком
Нажмите
кнопку Add и к проекту добавится новый
пустой файл C++
Удалите
из проекта прежний файл GLRect.cpp. Для
этого в панели Solution Explorer щелкните
правой кнопкой мыши на этом файле и через контекстное меню
выполните команду Remove
Заполните
файл Bounce .cpp следующим
кодом#include "stdafx.h"
// Прототипы глобальных функций
void RenderScene(void);
void SetupRC(void);
void ChangeSize(int, int);
void TimerFunction(int);
// Глобальные переменные:
// Начальная позиция и размер прямоугольника (будет квадрат)
GLfloat x = 0.0f;
GLfloat y = 0.0f;
GLfloat rsize = 25;
// Величина шага в направлениях x и y (в пикселах)
GLfloat xstep = 3.0f;
GLfloat ystep = 3.0f;
// Текущие ширина и высота клиентской области окна приложения
GLfloat windowWidth;
GLfloat windowHeight;
//**********************************************************
// Точка входа приложения
void main(int argc, char* argv[])
{
glutInit(argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
glutInitWindowSize(600,300);
glutCreateWindow("Bounce");
glutDisplayFunc(RenderScene);
glutReshapeFunc(ChangeSize);
glutTimerFunc(33, TimerFunction, 1);
SetupRC();
glutMainLoop();
}
//**********************************************************
// Рисование сцены
void RenderScene(void)
{
// Очистка окна текущим цветом
glClear(GL_COLOR_BUFFER_BIT);
// Установка цвета рисования в модели RGB (красный)
glColor3f(1.0f, 0.0f, 0.0f);
// Рисование и заливка красным
glRectf(x, y, x + rsize, y - rsize);
// Переключение буфера с очисткой очереди команд
glutSwapBuffers();
}
//**********************************************************
// Вызывается по таймеру библиотекой GLUT в спокойном состоянии,
// когда не меняются размеры окна приложения и нет перемещений
void TimerFunction(int value)
{
// Меняем направление перемещения на противоположное
// при подходе к левому или правому краю окна
if(x > windowWidth - rsize || x < -windowWidth)
xstep = -xstep;
// Меняем направление перемещения на противоположное
// при подходе к верхнему или нижнему краю окна
if(y > windowHeight || y < -windowHeight + rsize)
ystep = -ystep;
// Перемещаем прямоугольник в новую позицию
x += xstep;
y += ystep;
// Проверка на случай, если прямоугольник окажется больше окна
// или за пределами объема отсекающего параллелепипеда
if(x > (windowWidth - rsize + xstep))
x = windowWidth - rsize - 1;
else if(x < -(windowWidth + xstep))
x = -windowWidth - 1;
if(y > (windowHeight + ystep))
y = windowHeight - 1;
else if(y < -(windowHeight - rsize + ystep))
y = -windowHeight + rsize - 1;
// Перерисовываем сцену с новыми координатами
glutPostRedisplay();
// Запускаем новый таймер
glutTimerFunc(33,TimerFunction, 1);
}
//**********************************************************
// Установка состояния рендеринга (визуализации)
void SetupRC(void)
{
// Устанавливаем синий цвет очистки
glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
}
//**********************************************************
// Вызывается библиотекой GLUT при изменении размеров окна
// приложения. Осталась такой же, как в примере GLRect,
// только в вызовы glOrtho() поставили переменные
void ChangeSize(int width, int height)
{
GLfloat aspectRatio;
// Предотвращаем деление на нуль
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)
{
windowWidth = 100;
windowHeight = 100 / aspectRatio;
glOrtho (-100.0, 100.0,
-windowHeight, windowHeight, 1.0, -1.0);
}
else
{
windowWidth = 100 * aspectRatio;
windowHeight = 100;
glOrtho (-windowWidth, windowWidth,
-100.0, 100.0, 1.0, -1.0);
}
// Восстановливает матрицу преобразования в исходный режим вида
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
Запустите
приложение и убедитесь, что результат совпадает с задуманнымМотором для создания движения прямоугольника в данной программе
является вызов функции -
glutTimerFunc(33, TimerFunction, 1);
Первый раз этот вызов осуществляется из функции main(), а
затем повторяется в сервисной TimerFunction().
Синтаксис вызова этой GLUT -библиотечной
функции следующий:
glutTimerFunc(unsigned int msecs, void (*func)(int value), int value);
Первый параметр указывает, сколько миллисекунд должна ждать
эта функция перед однократным вызовом
той сервисной (нашей) функции, имя которой будет подставлено
вторым параметром в качестве фактического значения указателя
на функцию. Наша (сервисная) TimerFunction() ) должна
иметь один аргумент целого
При выводе изображения нужно, чтобы частота обновления кадра
была достаточно высокой и чтобы пользователь не смог заметить,
как экран очищается и как на нем формируется новая сцена. В
театрах для этих целей на период смены декараций закрывают
занавес, за которым и формируют новую сцену. Если быстродействие
рабочего буфера кадра превышает 50 Герц, то плавность
Но все равно пользователь может заметить отдельные этапы формирования кадра, особенно если изображение достаточно сложное и отдельную его фазу не удается построить за один период регенерации. В этом случае пользователь увидит половину кадра с новой фазой движения, а половину с прежней. Изображение перемещающегося объекта будет при этом искажено на экране.
Эту проблему можно решить с помощью двойной
Механизм двойной
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
с флагом GLUT_DOUBLE вместо GLUT_SINGLE,
как это было ранее. В таком режиме функция рисования сцены
void RenderScene(void){...}
будет выполнять всю работу в фоновом буфере и только в самом конце своей работы по команде
// Переключение буфера с очисткой очереди командglutSwapBuffers();
сделает этот буфер рабочим - и это новенькое наконец-то и увидит пользователь.
Заметьте, что теперь функцию glFlush() мы
уже не вызываем. Она уже не нужна, поскольку, выполняя переключение
буферов, мы неявно вызываем функцию очистки очереди команд.
Двойная
В этом можно убедиться на примере нашей программы. Квадрат
в законченный вид превращается мгновенно, поэтому нет разницы,
по какой технологии его рисовать: с двойной
Внесите
в текст программы Bounce следующие изменения//**********************************************************
// Точка входа приложения
void main(int argc, char* argv[])
{
...............................................
// glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
...............................................
}
//**********************************************************
// Рисование сцены
void RenderScene(void)
{
...............................................
// Переключение буфера с очисткой очереди команд
// glutSwapBuffers();
glFlush();}
Остальные пояснения к коду приведены
в комментариях листинга программы.
Вы, наверное, отметили главный
недостаток в данной работе - вместе с графическим окном,
создаваемым библиотекой GLUT, параллельно система создает
консольное окно. Пока этот недостаток не должен нас смущать
на данном этапе изучения самой библиотеки , но
в дальнейшем мы его устраним, построив другой тип оконного
приложения.
Работу преподавателю сдавайте так: поочередно присоединяйте к проекту файлы
или сразу заготовьте три варианта исполнимых файлов, но будьте готовы к выполнению просьбы по внесению определенных изменений.

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