Вы находитесь на странице: 1из 88

Министерство науки и высшего образования Российской Федерации

Сибирский федеральный университет

С. Н. Баранов, С. Г. Толкач

Основы компьютерной графики

Учебное пособие

Красноярск
СФУ
2018
УДК 004.92(07)
ББК 32.973я73
Б241

Р е ц е н з е н т ы:
Пак Н. И., доктор педагогических наук, профессор, заведующий
кафедрой информатики и информационных технологий в образовании
Красноярского государственного педагогического университета
им. В.П. Астафьева;
Исаев С. В., кандидат технических наук, доцент, заместитель дирек-
тора по научной работе ИВМ СО РАН – ОП ФИЦ КНЦ СО РАН

Баранов, С. Н.
Б241 Основы компьютерной графики : учеб. пособие / С. Н. Баранов,
С. Г. Толкач. – Красноярск : Сиб. федер. ун-т, 2018. – 88 с.
ISBN 978-5-7638-3968-5

Представлены основные примитивы графической библиотеки OpenGL,


приведены примеры использования простейших геометрических преобразо-
ваний, примеры отображения графиков функций и геометрических фракта-
лов, механизмы установки камеры, проекций отображения, алгоритмы на-
ложения текстур и управления освещением сцены.
Предназначено для студентов, обучающихся по программе бакалавриата
по направлениям подготовки 01.03.01 «Математика», 01.03.02 «Прикладная
математика и информатика» и 02.03.01 «Математика и компьютерные нау-
ки».

Электронный вариант издания см.: УДК 004.92(07)


http://catalog.sfu-kras.ru ББК 32.973я73

ISBN 978-5-7638-3968-5 © Сибирский федеральный университет, 2018


Оглавление
Введение...................................................................................................... 4
Глава 1. Введение в компьютерную графику ..................................... 6
1.1. Графическая библиотека OpenGL ..................................................... 6
1.2. Установка цвета ................................................................................... 9
1.3. Рисование геометрических примитивов ......................................... 11
Глава 2. Преобразования на плоскости и в пространстве ............. 19
2.1. Использование базовых преобразований........................................ 19
2.2. Установка перспективы и камеры ................................................... 22
Глава 3. Основы OpenGL...................................................................... 29
3.1. Вывод графиков функций ................................................................. 29
3.2. Установка освещения ........................................................................ 37
3.3. Наложение текстур ............................................................................ 40
3.4. Использование списков отображения ............................................. 44
Глава 4. Моделирование двух- и трехмерных сцен ........................ 49
4.1. Использование 3D-объектов библиотеки GLAUX ........................ 49
4.2. Создание своих 3D-объектов ........................................................... 53
4.3. Построение фракталов ...................................................................... 70
Варианты практических работ ............................................................ 80
Практическая работа № 1. Графические примитивы ........................... 80
Практическая работа № 2. Анимация графических примитивов ....... 82
Практическая работа № 3. Вывод графика функции ............................ 82
Практическая работа № 4. Наложение текстур ..................................... 83
Практическая работа № 5. Построение 3D-сцены ................................ 83
Практическая работа № 6. Построение фракталов ............................... 83
Библиографический список ................................................................. 85

3
Компьютерная графика ‒ область деятельности, в которой изо-
бражения создаются с помощью компьютерных технологий. Компью-
терная графика используется в научной деятельности для иллюстри-
рования результатов экспериментов, построения графиков, диаграмм
и чертежей. С развитием компьютерных технологий расширяются и
сферы применения компьютерной графики.
Настоящее учебное пособие предназначено для ознакомления
студентов с основами компьютерной графики. В пособии рассмотре-
ны основные функции графической библиотеки OpenGL, подробно
описаны примеры создания приложений, использования графических
примитивов, установки различных параметров и т. д. Пособие содер-
жит иллюстрации результатов работы программ, которые демонстри-
руют различные функции графической библиотеки OpenGL.
Все примеры выполнены в среде разработки Visual Studio 2015
на языке программирования C++.
В первой главе идет речь о библиотеке OpenGL. Приводятся
примеры создания приложений с помощью библиотеки «glaux.h» или
библиотеки «glut.h». Далее обозначаются основные функции уста-
новки цвета, приведено описание графических примитивов.
Во второй главе даны простейшие геометрические преобразова-
ния и демонстрируются примеры установки перспективных проекций,
работы с камерой, обработки нажатия клавиш клавиатуры и измене-
ния положения камеры.
В третьей главе представлены алгоритмы построения графиков
алгебраических функций и их производных, алгоритмы построения
функций, заданных в параметрическом виде. Рассмотрены примеры
вывода графиков функций от двух переменных в трехмерном про-
странстве. Также приведены функции установки освещения, алгорит-
мы наложения текстур на графические примитивы и алгоритмы при-
менения списков отображения для оптимизации работ приложения.

4
В четвертой главе приведены примеры использования трехмер-
ных объектов из библиотеки «glaux.h», а также примеры построения
3D-сцены из своих 3D-объектов. Далее следуют иллюстрации основ-
ных геометрических фракталов и рассмотрены алгоритмы построения
фракталов с помощью графических примитивов.
В завершение представлены примеры практических работ, кото-
рые должны быть выполнены для закрепления пройденного материала.
Настоящее пособие поможет успешно освоить основные алго-
ритмы и понятия компьютерной графики и позволит применять
функции библиотеки OpenGL для создания графических приложений
с использованием простейших примитивов, текстур и геометрических
преобразований.

5
OpenGL (Open Graphics Library ‒ открытая графическая библио-
тека, графическое API) ‒ спецификация, определяющая независимый
от языка программирования платформонезависимый программный
интерфейс для написания приложений, использующих двухмерную и
трехмерную компьютерную графику.
Библиотека OpenGL была разработана как независимый от аппа-
ратного обеспечения интерфейс, поэтому не содержит функций для
создания окон или для обработки пользовательского ввода. Для этих
операций должны применяться средства той операционной системы, в
которой создается приложение. По тем же причинам в OpenGL нет
высокоуровневых функций для описания моделей трехмерных объек-
тов. При обращении к библиотеке OpenGL необходимо пользоваться
набором геометрических примитивов – точек, линий и многоугольни-
ков – и с их помощью создавать более сложные объекты.
Основные графические операции, которые выполняет OpenGL
для вывода изображения на экран:
• конструирует фигуры из геометрических примитивов (прими-
тивами в OpenGL являются точки, линии, полигоны, битовые
карты и изображения);
• позиционирует объекты в трехмерном пространстве и выбира-
ет точку наблюдения для осмотра полученной композиции;
• вычисляет цвета для всех объектов; цвета могут быть опреде-
лены приложением, получены из расчета условий освещенно-
сти, вычислены при помощи текстур, наложенных на объекты
или из любой комбинации этих факторов;
• преобразует математическое описание объектов и ассоцииро-
ванной с ними цветовой информации в пиксели на экране.
6
Этот процесс называется растеризацией (или растровой раз-
верткой).
Рассмотрим пример приложения, созданного в Visual Studio
2015, с использованием библиотеки «glaux.h». Необходимо создать
пустой проект, имеющий тип Консольное приложение Win32, далее
нужно скопировать в директорию с исходниками файлы GLAUX.H и
GLAUX.LIB, после чего добавить в проект файл исходного кода и
внести в него текст из примера 1.
Пример 1. Первое приложение с использованием библиоте-
ки «glaux.h»
#include "glaux.h"
using namespace std;
// подключение предкомпилированных библиотек
#pragma comment (lib, "opengl32.lib")
#pragma comment (lib, "glu32.lib")
#pragma comment (lib, "glaux.lib")
// подключение библиотеки legacy_stdio_definitions
// для Visual Studio 2015 и выше
#pragma comment(lib, "legacy_stdio_definitions.lib")
// функция вызывается при изменении размеров окна
void CALLBACK resize(int width, int height)
{
// установка области вывода
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION); //выбор матрицы проекции
glLoadIdentity(); // сброс текущей матрицы преобразований
// установка перспективы
gluPerspective(45.0, (GLfloat)width / height, 1.0, 100.0);
// установка камеры
gluLookAt(0, 0, 5, 0, 0, 0, 0, 1, 0);
glMatrixMode(GL_MODELVIEW); // выбор матрицы отображения
glEnable(GL_DEPTH_TEST); // установка теста глубины
}
// функция вызывается при рисовании
void CALLBACK display(void)
{ // очистка цвета и глубины
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity(); // сброс текущей матрицы преобразований
// начало рисования примитива "четырехугольник"
glBegin(GL_QUADS);
glVertex3d(–1, –1, 0); // задание вершины примитива
glVertex3d(–1, 1, 0); // задание вершины примитива
glVertex3d(1, 1, 0); // задание вершины примитива

7
glVertex3d(1, –1, 0); // задание вершины примитива
glEnd(); // окончание рисования примитива
// обмен переднего и заднего буферов, вывод на экран
auxSwapBuffers();
}
// основная функция программы
void main()
{ // задание позиции и размеров окна
auxInitPosition(0, 0, 800, 600);
// установка режима отображения
auxInitDisplayMode(AUX_RGB | AUX_DEPTH | AUX_DOUBLE);
auxInitWindow(L"Glaux Template"); // инициализация окна
auxIdleFunc(display); // задание функции рисования
// задание функции изменения размеров окна
auxReshapeFunc(resize);
glClearColor(0, 0, 0, 0.5);// задание цвета очистки экрана
// задание параметра очистки буфера глубины
glClearDepth(1.0);
auxMainLoop(display); // вызов основного цикла приложения
}

После запуска приложения создастся окно, на котором будет


изображен белый квадрат, как показано на рис. 1.

Рис. 1. Первое приложение

Рассмотрим пример приложения, созданного в Visual Studio


2015 с использованием библиотеки «glut.h». Нужно создать пустой

8
проект, имеющий тип Консольное приложение Win32, далее добавить
в проект файл исходного кода и внести в него текст из примера 2.
Пример 2. Приложение с использованием библиотеки «glut.h»
#include <GL/glut.h>
void display(void)
{ // Очистить экран
glClear(GL_COLOR_BUFFER_BIT);
// Нарисовать белый полигон (квадрат) с углами
// в (0.25, 0.25, 0.0) и (0.75, 0.75, 0.0)
glColor3f(1.0, 1.0, 1.0);
glBegin(GL_POLYGON);
glVertex3f(0.25, 0.25, 0.0);
glVertex3f(0.75, 0.25, 0.0);
glVertex3f(0.75, 0.75, 0.0);
glVertex3f(0.25, 0.75, 0.0);
glEnd();

glFlush();
}
// Установить начальные характеристики окна, открыть окно с
заголовком //«hello». Зарегистрировать дисплейную функцию об-
ратного вызова
// Войти в главный цикл
int main(int argc, char **argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(250, 250);
glutInitWindowPosition(100, 100);
glutCreateWindow(“hello”);
init();
glutDisplayFunc(display);
glutMainLoop();
return 0;
}

Цветовая модель ‒ модель описания цвета в виде набора чисел


(обычно из трех или четырех значений, называемых цветовыми ком-
понентами или цветовыми координатами). Цветовые модели под-
разделяются на аддитивные и субтрактивные.

9
В аддитивной цветовой модели задаются три основных цвета, а
любой другой цвет представляется как взвешенная сумма трех основ-
ных цветов. Широко известная цветовая модель RGB является адди-
тивной цветовой моделью (сокращенно от Red, Green, Blue ‒ красный,
зеленый, синий).
Другая модель смешения цветов ‒ субтрактивная цветовая мо-
дель, или модель CMYK (CMY), использующая в качестве первичных
составляющих цвета Cyan, Magenta, Yellow (голубой, пурпурный,
желтый), которые являются дополнительными к Red, Green, Blue.
Связь между значениями (R, G, B) и (C, M, Y) для одного и того же
цвета выражается формулой:
 R   C  1
     
 G    M   1
 B   Y  1
      .
На рис. 2 демонстрируются примеры получения некоторых цве-
тов в указанных цветовых моделях.

Рис. 2. Пример сложения цветов

Функция glColor3f (GLfloat red, GLfloat green,


GLfloat blue) принимает три параметра, каждый из которых явля-
ется вещественным числом в диапазоне между 0.0 и 1.0. Эти парамет-

10
ры задают красный, зеленый и синий компоненты цвета соответст-
венно.
Пример 3. Задание разных цветов
glColor3f(0.0,0.0,0.0); черный
glColor3f(1.0,0.0,0.0); красный
glColor3f(0.0,1.0,0.0); зеленый
glColor3f(1.0,1.0,0.0); желтый
glColor3f(0.0,0.0,1.0); синий
glColor3f(1.0,0.0,1.0); фиолетовый
glColor3f(0.0,1.0,1.0); голубой
glColor3f(1.0,1.0,1.0); белый

На рис. 3 приведена схема получения цветов для обеих цветовых


моделей.

Рис. 3. Схема смешивания цветов для моделей

Любой геометрический объект однозначно описывается в виде


упорядоченного набора вершин. Для установки одной вершины ис-
пользуется функция glVertex*():

void glVertex{234}{sifd}(TYPE coords);


void glVertex{234}{sifd}v(const TYPE *coords);

11
Для каждой вершины можно указывать от двух до четырех ко-
ординат и задавать тип, используемый для описания значения коор-
динаты (short, int, float, double).
Существует несколько разных версий этой функции:

glVertex2s // вершина из двух координат, имеющих тип short


glVertex2i // вершина из двух координат, имеющих тип int
glVertex3f // вершина из трех координат, имеющих тип float
glVertex4d // вершина из четырех координат, имеющих тип double
glVertex3dv // вершина из трех координат, имеющих тип double,
координаты задаются массивом из трех элементов типа double.

Приведем примеры задания вершины.


Пример 4. Первое задание вершин с разными параметрами
glVertex2s(1, 5);
glVertex3d(0.7, 5.224, 3.1415926535898);
glVertex4f(2.1, 1.3, –2.0, 2.0);
GLdouble dvect[3] = { 5.0,9.0,1992.0 };
glVertex3dv(dvect);

Далее рассмотрим задание примитивов с помощью оператора


установки вершин. Для задания примитива необходимо вызвать
функции glBegin() и glEnd().
Функция glBegin(GLenum mode) устанавливает начало описания
примитива. Функция glEnd() задает окончание описания примитива.
Константы, используемые в функции glBegin():
• GL_POINTS – индивидуальные точки;
• GL_LINES – вершины попарно интерпретируются как само-
стоятельные отрезки;
• GL_LINE_STRIP – серия соединенных отрезков (ломаная);
• GL_LINE_LOOP – аналогично предыдущему, но, кроме того,
автоматически добавляется отрезок, соединяющий первую и
последнюю вершины (замкнутая ломаная);
• GL_TRIANGLES – каждая тройка вершин интерпретируется как
треугольник;
• GL_TRIANGLE_STRIP – цепочка соединенных треугольников;
• GL_TRIANGLE_FAN – веер из соединенных треугольников;
• GL_QUADS – каждая четверка вершин интерпретируется как
четырехугольный полигон;
• GL_QUAD_STRIP – цепочка соединенных четырехугольников;

12
• GL_POLYGON – граница простого выпуклого полигона.
На рис. 4 и 5 представлены все виды примитивов, которые мож-
но нарисовать с помощью функции glBegin().

Рис. 4. Геометрические примитивы – точки и линии

Рис. 5. Геометрические примитивы – треугольники, четырехугольники


и полигоны

13
Приведем перечень команд, которые допускается использовать
между glBegin() и glEnd():
glVertex*() – установка координат вершины;
glColor*() – установка текущего цвета;
glIndex*() – установка текущего цветового индекса;
glNormal*() – установка координат вектора нормали;
glTexCoord*() – установка координат текстуры;
glMultiTexCoord*ARB() – установка координат текстуры при
мультитекстурировании;
glEdgeFlag*() – контроль рисования ребер;
glMaterial*() – установка свойств материала;
glArrayElement() – выделение данных из массива вершин;
glEvalCoords*(), glEvalPoint*() – генерация координат;
glCallList(), glCallLists() – выполнение списка ото-
бражения.
В примере 5 рассмотрен вывод примитива «Точки» с заданием
размеров точек, сглаживанием и установкой цвета. На рис. 6 пред-
ставлен результат выполнения программы.
Пример 5. Задание рисования примитива «Точки»
glPointSize(20); // установка размера точки
glEnable(GL_POINT_SMOOTH); // включение сглаживания точки
glEnable(GL_BLEND); // включение наложения цветов
// задание функции наложения
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBegin(GL_POINTS); // начало примитива «точки»
glColor3d(1, 1, 1); // установка цвета
glVertex2f(0.0, 0.0); // установка вершины
glColor3d(1, 0, 0); // установка цвета
glVertex2f(0.0, 3.0); // установка вершины
glColor3d(0, 1, 0); // установка цвета
glVertex2f(4.0, 3.0); // установка вершины
glColor3d(0, 0, 1); // установка цвета
glVertex2f(6.0, 1.5); // установка вершины
glVertex2f(4.0, 0.0); // установка вершины
glEnd(); // окончание примитива «точки»

В примере 6 рассмотрено рисование примитива «Полигон» с за-


данием координат вершин и установкой цвета вершин, как и в приме-
ре 5. Заполнение полигона цветом происходит градиентным образом,
т. е библиотека OpenGL сама рассчитывает цвет каждого пиксела
внутри полигона (см. рис. 7).
14
Рис. 6. Пример рисования примитива «Точки»

Пример 6. Задание рисования примитива «Полигон»


glBegin(GL_POLYGON);
glColor3d(1, 1, 1); // установка цвета
glVertex2f(0.0, 0.0); // установка вершины
glColor3d(1, 0, 0); // установка цвета
glVertex2f(0.0, 3.0); // установка вершины
glColor3d(0, 1, 0); // установка цвета
glVertex2f(4.0, 3.0); // установка вершины
glColor3d(0, 0, 1); // установка цвета
glVertex2f(6.0, 1.5); // установка вершины
glVertex2f(4.0, 0.0); // установка вершины
glEnd();

Рис. 7. Пример рисования примитива «Полигон»

Для удобства рисования примитивов можно создать в программе


вспомогательные функции. В примере 7 реализована подобная функ-
ция рисования линии.
15
Пример 7. Функция рисования примитива «Линии»
struct vertex3 { GLdouble x, y, z; }; // структура вершины
struct color3 { GLdouble r, g, b; }; // структура цвета
void DrawLine(vertex3 v1, vertex3 v2, color3 c)
{// передаются координаты двух точек в трехмерном пространстве
и цвет линии
glBegin(GL_LINES);
glColor3d(c.r, c.g, c.b);
glVertex3d(v1.x, v1.y, v1.z);
glVertex3d(v2.x, v2.y, v2.z);
glEnd();
}
// вызов функции рисования линии
DrawLine(vertex3{0,0,0}, vertex3{1,2,0}, color3{1,1,0});

Для изменения толщины линии используется функция


glLineWidth(GLfloat width), в качестве параметра передается значение тол-
щины линии в пикселах:
void glLineWidth(GLfloat width);

В OpenGL предусмотрена возможность рисовать отрезки разно-


го вида, например пунктирные или штрихованные отрезки. Для этого
необходимо применить функцию glLineStipple(Glint factor,
GLushort pattern) и затем включить механизм использования
шаблонов с помощью функции glEnable(GL_LINE_STIPPLE).
В функции glLineStipple() первый параметр представляет собой мно-
житель, а второй – шестнадцатибитную серию из нулей и единиц, ко-
торая задает вид шаблона (см. рис. 8).

Рис. 8. Примеры шаблонов отрезков

16
В примере 8 реализована функция рисования окружности с ис-
пользованием примитива «Линии». Для использования тригономет-
рических функций подключается библиотека математических функ-
ций <math.h>.
Пример 8. Функция рисования окружности
#include <math.h>
void DrawCircle(GLdouble x, GLdouble y, GLdouble z, GLdouble
radius, GLdouble r, GLdouble g, GLdouble b)
// передаются координаты центра, радиус и цвет окружности
{
glColor3d(r, g, b);
int n = 60;
glBegin(GL_LINE_LOOP);
for (int i = 0; i<n; i++)
glVertex3d(x + radius*sin(i * 2 * 3.14 / n),
y + radius*cos(i * 2 * 3.14 / n), z);
glEnd();
}

Если в примере 8 заменить тип примитива с «Линий» на «Тре-


угольники», то нарисуется залитый заданным цветом круг. Это пока-
зано в примере 9.
Пример 9. Функция рисования закрашенного круга
#include <math.h>
void DrawRound(GLdouble x, GLdouble y, GLdouble z, GLdouble ra-
dius, GLdouble r, GLdouble g, GLdouble b)
// передаются координаты центра, радиус и цвет круга
{
glColor3d(r, g, b);
int n = 60;
glBegin(GL_TRIANGLES);
for (int i = 0; i<n; i++)
glVertex3d(x + radius*sin(i * 2 * 3.14 / n),
y + radius*cos(i * 2 * 3.14 / n), z);
glEnd();
}

17
Результаты работы программ из примеров 8 и 9 приведены на
рис. 9.

Рис. 9. Окружность и круг

18
На практике довольно часто возникают задачи, требующие реа-
лизации передвижений или преобразований изображений. Рассмот-
рим типы базовых преобразований, позволяющих осуществлять такие
действия.
Преобразование плоскости называется аффинным, если оно вза-
имно-однозначно и образом любой прямой линии является прямая
линия.
Преобразование называется взаимно-однозначным, если разные
точки переходят в разные точки и в каждую точку переходит какая-
либо точка. На рис. 10 и 11 проиллюстрированы примеры аффинных
преобразований.

Рис. 10. Примеры движений

Рис. 11. Примеры аффинных преобразований

19
В OpenGL применяются следующие виды преобразований:
• сжатие/растяжение,
• поворот,
• параллельный перенос,
• отражение.
Каждое из приведенных преобразований имеет простой и нагляд-
ный геометрический смысл. Любое преобразование всегда можно пред-
ставить как последовательное исполнение простейших преобразований.
Функция glTranslate() вносит дополнительное смещение в
текущую матрицу. Эта функция имеет три параметра: x, y, z – коор-
динаты вектора сдвига. Она принимает следующие формы:
glTranslated(GLdouble x, GLdouble y, GLdouble z);
glTranslatef(GLfloat x, GLfloat y, GLfloat z);

Функция glRotate() поворачивает текущую матрицу на задан-


ный угол вокруг заданного вектора. Эта функция имеет четыре пара-
метра: angle, x, y, z – угол поворота и координаты вектора, задающего
ось поворота. Она принимает следующие формы:
glRotated(GLdouble angle, GLdouble x, GLdouble y, GLdouble z);
glRotatef(GLfloat angle, GLfloat x, GLdouble y, GLfloat z);

Функция glScale() масштабирует текущую матрицу преобра-


зований. Эта функция имеет три параметра: x, y, z ‒ коэффициенты
масштабирования по соответствующим осям. Виды функции
glScale():
glScaled(GLdouble x, GLdouble y, GLdouble z);
glScalef(GLfloat x, GLfloat y, GLfloat z);

Каждое преобразование действует следующим образом: текущая


матрица умножается на матрицу преобразования, а затем результат
этого произведения записывается в текущую матрицу. Таким обра-
зом, если M ‒ текущая матрица, а R ‒ матрица преобразования, то
матрица M будет заменена на M*R. После этого на все примитивы,
которые будут рисоваться после вызова функции преобразования,
применится текущая матрица преобразований, т. е. все примитивы,
нарисованные после вызова методов glScale(), glRotate(),
glTranslated(), соответствующим образом изменят масштаб, по-
вернутся и сдвинутся.
Для управления текущей матрицей преобразования применяется
еще несколько функций. Функция glLoadIdentity() заменяет те-

20
кущую матрицу преобразований на единичную матрицу. Обычно она
используется в начале формирования кадра.
Функции glPushMatrix() и glPopMatrix() предназначены
для помещения матриц в стек и извлечения из него.
Пример 10. Рисование куба, состоящего из сетки
// функция рисования сетки из линий заданного цвета
void DrawGrid(GLdouble r, GLdouble g, GLdouble b)
{
GLdouble dx = 1;
for (int i = -10; i <= 10; i++)
DrawLine(vertex3{ i*dx, -10, 0 },
vertex3{ i*dx, 10, 0 },
color3{ r, g, b });
}

// функция рисования куба с использованием преобразований и


функции рисования сетки
void DrawKube()
{
glTranslated(0, 0, -30);
glRotated(GetTickCount() / 10.f, 0.1, 0.2, 0.3);
glTranslated(0, 0, -10); DrawGrid(1, 1, 1);
glRotated(90, 0, 0, 1); DrawGrid(1, 1, 1);
glPushMatrix(); glTranslated(0, 0, -20);
DrawGrid(1, 0, 0); glRotated(90, 0, 0, 1);
DrawGrid(1, 0, 0);
glPopMatrix(); glTranslated(-10, 0, -10);
glRotated(90, 0, 1, 0); DrawGrid(0, 1, 0);
glRotated(90, 0, 0, 1); DrawGrid(0, 1, 0);
glTranslated(0, 0, 20); DrawGrid(0, 0, 1);
glRotated(90, 0, 0, 1); DrawGrid(0, 0, 1);
}

В примере 10 используются преобразования сдвига


glTranslated() и поворота glRotate(). Кроме того, здесь исполь-
зуются функция помещения текущей матрицы преобразований в стек
glPushMatrix() и функция взятия текущей матрицы преобразова-
ний из стека glPopMatrix(). Результат выполнения программы
проиллюстрирован на рис.12.

21
Рис. 12. Сеточный куб

В ряде задач компьютерной графики требуется симулировать


режим переключения камеры и задавать перспективу.
Рассмотрим функцию установки перспективы:
void gluPerspective (GLdouble fovy, GLdouble aspect, GLdouble
zNear, GLdouble zFar);

Здесь параметр fovy указывает угол поля зрения в градусах в на-


правлении у, параметр aspect определяет соотношение, определяющее
поле зрения в направлении х. Соотношение сторон представляет со-
бой отношение ширины к высоте. Параметр zNear указывает расстоя-
ние от зрителя до ближайшей плоскости отсечения (всегда положи-
тельной). Параметр zFar указывает расстояние от зрителя до дальней
плоскости отсечения (см. рис. 13).
Рассмотрим еще один вариант функции установки перспективы:
void glFrustum(GLdouble left, GLdouble right, GLdouble bottom,
GLdouble top, GLdouble zNear, GLdouble zFar);

Здесь параметры left, right представляют собой координаты для


левой и правой вертикальных плоскостей отсечения. Параметры bot-

22
tom, top ‒ координаты для нижней и верхней горизонтальных плоско-
стей отсечения. Параметры zNear, zFar ‒ расстояния до ближней и
дальней плоскостей отсечения в глубину. Оба расстояния должны
быть положительными (см. рис. 14).

Рис. 13. Описание параметров функции gluPerspective()

Рис. 14. Описание параметров функции glFrustum()

23
Рассмотрим функцию установки камеры:
void gluLookAt(GLdouble eyex, GLdouble eyey, GLdouble eyez,
GLdouble atx, GLdouble aty, GLdouble atz, GLdouble upx,
GLdouble upy, GLdouble upz);

Точка (eyex, eyey, eyez) определяет точку наблюдения, точка (atx,


aty, atz) задает центр сцены, который будет проектироваться в центр
области вывода. Вектор (upx, upy, upz) задает положительное направ-
ление оси у, определяя поворот камеры (см. рис. 15). Если камеру не
нужно поворачивать, то задается значение (0, 1, 0). Значение (0, –1, 0)
переворачивает сцену.

Рис. 15. Описание параметров функции gluLookAt()

В примерах 11 и 12 демонстрируются возможности вышеопи-


санных функций для реализации разных способов задания движения
камеры.
Пример 11. Простой пример работы с камерой
// описание глобальных переменных
float anglez = 0, anglex = 0, rz = 10;
float ex = 0, ey = 0, ez = 0;
float ax = 0, ay = 0, az = 0;
// добавление в функцию display вызова функции установки камеры
void CALLBACK display(void)
{
// очистка цвета и глубины

24
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity(); // сброс текущей матрицы преобразований

ex = rz*sin(anglez);
ez = rz*cos(anglez);
gluLookAt(ex, ey, ez, ax, ay, az, 0, 1, 0);

// рисование кадра
auxSwapBuffers(); // обмен буфера, вывод на экран
}
// описание функций нажатия клавиш клавиатуры
void CALLBACK PressKeyA(void) // шаг по кругу
{
anglez += 0.1;
}
void CALLBACK PressKeyD(void) // шаг по кругу
{
anglez –= 0.1;
}
void CALLBACK PressKeyW(void) // шаг вперед
{
rz –= 1;
}
void CALLBACK PressKeyS(void) // шаг назад
{
rz += 1;
}
void CALLBACK PressKeyQ(void) // шаг вверх
{
ey += 1;
}
void CALLBACK PressKeyE(void) // шаг вниз
{
ey –= 1;
}
// основная функция программы
void main()
{
// инициализация OpenGL
auxKeyFunc(AUX_w, PressKeyW);
auxKeyFunc(AUX_s, PressKeyS);
auxKeyFunc(AUX_a, PressKeyA);
auxKeyFunc(AUX_d, PressKeyD);
auxKeyFunc(AUX_q, PressKeyQ);
auxKeyFunc(AUX_e, PressKeyE);

25
auxMainLoop(display); // вызов основного цикла приложения
}

На рис. 16 проиллюстрирована работа с камерой.

Рис. 16. Пример движения камеры

Пример 12. Более сложный пример работы с камерой


// описание глобальных переменных
double eX = 0, eY = 1, eZ = 10;
double aX = 0, aY = 0, aZ = 0;
float moveK = 0.01;
// добавление в функцию display вызова функции установки камеры
void CALLBACK display(void)
{ // очистка цвета и глубины
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity(); // сброс текущей матрицы преобразований

gluLookAt(eX, eY, eZ, aX, aY, aZ, 0, 1, 0);

// рисование кадра
auxSwapBuffers(); // обмен буфера, вывод на экран
}
// описание функций нажатия клавиш клавиатуры
void CALLBACK PressKeyD(void) // шаг вправо
{
float dx, dz, a = 3.14 / 2;
float cx_ = (aX – eX)*cos(a) – (aZ – eZ)*sin(a);

26
float cz_ = (aX – eX)*sin(a) + (aZ – eZ)*cos(a);
dx = cx_*moveK; dz = cz_*moveK;
eX = eX + dx; aX = aX + dx;
eZ = eZ + dz; aZ = aZ + dz;
}
void CALLBACK PressKeyA(void) // шаг влево
{
float dx, dz;
float a = –3.14 / 2;
float cx_ = (aX – eX)*cos(a) – (aZ – eZ)*sin(a);
float cz_ = (aX – eX)*sin(a) + (aZ – eZ)*cos(a);
dx = cx_*moveK;
dz = cz_*moveK;
eX = eX + dx; aX = aX + dx;
eZ = eZ + dz; aZ = aZ + dz;
}
void CALLBACK PressKeyW(void) // шаг вперед
{
float dx, dz;
dx = (aX – eX)*moveK;
dz = (aZ – eZ)*moveK;
eX = eX + dx;
aX = aX + dx;
eZ = eZ + dz;
aZ = aZ + dz;
}
void CALLBACK PressKeyS(void) // шаг назад
{
float dx, dz;
dx = –(aX – eX)*moveK;
dz = –(aZ – eZ)*moveK;
eX = eX + dx;
aX = aX + dx;
eZ = eZ + dz;
aZ = aZ + dz;
}
void CALLBACK PressKeyE(void) // поворот направо
{
float a = 3.14 / 180 * 2;
float cx_ = (aX – eX)*cos(a) – (aZ – eZ)*sin(a);
float cz_ = (aX – eX)*sin(a) + (aZ – eZ)*cos(a);
aX = cx_ + eX; aZ = cz_ + eZ;
}
void CALLBACK PressKeyQ(void)// поворот налево
{

27
float a = –3.14 / 180 * 2;
float cx_ = (aX – eX)*cos(a) – (aZ – eZ)*sin(a);
float cz_ = (aX – eX)*sin(a) + (aZ – eZ)*cos(a);
aX = cx_ + eX; aZ = cz_ + eZ;
}
void CALLBACK PressKeyZ(void)// шаг вверх
{
eY += 1; aY += 1;
}
void CALLBACK PressKeyX(void) // шаг вниз
{
eY –= 1; aY –= 1;
}
// основная функция программы
void main()
{ // инициализация OpenGL
auxKeyFunc(AUX_w, PressKeyW); auxKeyFunc(AUX_s, PressKeyS);
auxKeyFunc(AUX_a, PressKeyA); auxKeyFunc(AUX_d, PressKeyD);
auxKeyFunc(AUX_q, PressKeyQ); auxKeyFunc(AUX_e, PressKeyE);
auxKeyFunc(AUX_z, PressKeyZ); auxKeyFunc(AUX_x, PressKeyX);
auxMainLoop(display); // вызов основного цикла приложения
}

Работа данного примера проиллюстрирована на рис. 17.

Рис. 17. Пример движения камеры

28
В данном параграфе рассмотрим пример вывода графиков функ-
ций с помощью простейших геометрических примитивов.
Пример 13. Рисование графика функций
void DrawLine(float x1, float y1, float x2, float y2)
{
glLineWidth(2);
glColor3d(1, 1, 1);
glBegin(GL_LINES);
glVertex2d(x1, y1);
glVertex2d(x2, y2);
glEnd();
}
float f(float x)
{
return (sin(x));
}
void DrawOs()
{
glPushMatrix();
double dx = 2;
DrawLine(–10 * dx, 0, 10 * dx, 0);
DrawLine(0, –10 * dx, 0, 10 * dx);
for (int i = –10; i <= 10; i++)
{
DrawLine(i*dx, 0.1*dx, i*dx, –0.1*dx);
DrawLine(0.1*dx, i*dx, –0.1*dx, i*dx);
}
glPopMatrix();
}
void DrawFunc()
{

29
float x, x1 = –10, x2 = 10, dx = (x2 – x1) / n;
int n = 100;
DrawOs();
glPointSize(3);
glBegin(GL_POINTS); // рисование точек
// glBegin(GL_LINE_STRIP); // рисование линий
glColor3f(1, 0, 0);
for (x = x1; x <= x2; x += dx)
glVertex3f(x, f(x), 0);
glEnd();
}

В примере 13 функция DrawLine() добавлена для удобства ис-


пользования. Она принимает четыре параметра и рисует белую линию
из точки (x1, y1) в точку (x2, y2). Функция f() задает математическую
функцию, график которой мы хотим построить. Функция DrawOs()
рисует ось координат на плоскости. Функция DrawFunc() вызывает
функцию рисования осей координат, устанавливает размер точек и в
операторе цикла добавляет в примитив «Точки» значения математи-
ческой функции в точках x, т. е добавляются точки (x, f(x))
(см. рис. 18).

Рис. 18. Вывод графика функций точками и линиями

В примерах 14–16 приводятся программы, рисующие более


сложные графики функций. Результаты их работы представлены на
рис. 20–25.
Пример 14. Пример рисования сложного графика функций
// Объявление переменных и типов
bool createMas = false;
float **masF, **masFF;
int masSize;
// Функция создания массива
30
void GenerateMasByFunc(float **mas, float x1, float x2,
float dx)
{
for (int i = 0; i<masSize; i++)
{
mas[0][i] = x1 + dx * i;
mas[1][i] = f(x1 + dx * i);
}
}
// Функция рисования массива
void DrawFuncMas(float **mas, float xScale, float yScale, float
r, float g, float b)
{
glBegin(GL_LINE_STRIP);
glColor3f(r, g, b);
for (int i = 0; i<masSize; i++)
glVertex3f(mas[0][i] * xScale,
mas[1][i] * yScale, 0);
glEnd();
}
// Рисование графика функций
void DrawFuncMasExample()
{
float xScale = 1, yScale = 1, x1 = –10, x2 = 10, dx = 0.1;
masSize = int((x2 – x1) / dx);
if (!createMas)
{
masF = new float *[2];
masF[0] = new float[masSize];
masF[1] = new float[masSize];
GenerateMasByFunc(masF, x1, x2, dx);
createMas = true;
}
DrawOs();
DrawFuncMas(masF, xScale, yScale, 1, 0, 0);
}
// Нахождение максимума функции
float MaxFuncMas(float **mas, int n)
{
float res = mas[1][0];
for (int i = 1; i<n; i++)
if (mas[1][i]>res)
res = mas[1][i];
return res;
}

31
// Вычисление производной функции
void CalcDFuncMas(float **mas, float **dmas, float dx)
{
for (int i = 0; i<masSize; i++)
{
dmas[0][i] = mas[0][i];
if (i<masSize – 1)
dmas[1][i] = (mas[1][i + 1] – mas[1][i]) / dx;
else
dmas[1][i] = (mas[1][i] – mas[1][i – 1]) / dx;
}
}
// Вычисление интеграла функции
float CalcFFuncMas(float **mas, int n, float dx)
{
float res = 0;
for (int i = 0; i<n – 1; i++)
res += (mas[1][i + 1] + mas[1][i]) / 2 * dx;
return res;
}

Алгоритм вычисления производной проиллюстрирован


на рис. 19.

Рис. 19. Производная функции

32
Пример 15. Вывод функции, заданной в параметрическом
виде
void DrawFuncParam(){
float t1 = 0, t2 = 4 * 3.14, dt = 0.01;
float t = t1, x, y; float xScale = 3, yScale = 3;
DrawOs();
glBegin(GL_LINE_STRIP);
glColor3f(1, 1, 1);
while (t<t2){
x = sin(2 * t); y = cos(3 * t);
glVertex3f(x * xScale, y * yScale, 0);
t += dt;
} glEnd();
}

Рис. 20. График функции x=sin(2*t), y=cos(3*t), t=0..4*Pi

Рис. 21. График функции x=sin(3*t), y=cos(5*t), t=0..4*Pi

33
Рис. 22. График функции x = 16*sin(t)*sin(t)*sin(t); y = 13*cos(t)–5*cos(2*t)–
2*cos(3*t)–cos(4*t)

Пример 16. Пример рисования графика функции в трехмер-


ном пространстве
// функция в трехмерном пространстве
float f2(float x, float y)
{
return (sin(x) + sin(y));
}
// функция рисования четырехугольника, передаются 4 координаты
и цвет
void DrawQuad(vertex3 v1, vertex3 v2, vertex3 v3, vertex3 v4,
color3 c)
{
glBegin(GL_QUADS);
glColor3d(c.r, c.g, c.b);
glVertex3d(v1.x, v1.y, v1.z);
glVertex3d(v2.x, v2.y, v2.z);
glVertex3d(v3.x, v3.y, v3.z);
glVertex3d(v4.x, v4.y, v4.z);
glEnd();
}
// функция рисования графика функции в трехмерном пространстве
void DrawFunc3D()
{
float x, x1 = –10, x2 = 10;
float y, y1 = –10, y2 = 10;
int n = 100;
float dx = (x2 – x1) / n;
float dy = (y2 – y1) / n;

34
DrawOs();
glRotated(90, 0, 1, 0);
DrawOs();
glPointSize(2);
glBegin(GL_POINTS); // рисование точек
glColor3f(1, 0, 0);
for (x = x1; x <= x2; x += dx)
for (y = y1; y <= y2; y += dy)
{
glVertex3f(x, f2(x, y), y);
}
glEnd();
for (x = x1; x <= x2; x += dx)
{
glBegin(GL_LINE_STRIP); // рисование линий
for (y = y1; y <= y2; y += dy)
glVertex3f(x, f2(x, y), y);
glEnd();
}
for (y = y1; y <= y2; y += dy)
{
glBegin(GL_LINE_STRIP); // рисование линий
for (x = x1; x <= x2; x += dx)
glVertex3f(x, f2(x, y), y);
glEnd();
}
for (x = x1; x <= x2; x += dx)
for (y = y1; y <= y2; y += dy)
{
// рисование полигонов
DrawQuad(vertex3{ x, f2(x, y), y },
vertex3{ x + dx, f2(x + dx, y), y },
vertex3{ x + dx, f2(x + dx, y + dy),
y + dx },
vertex3{ x, f2(x, y + dx), y + dy },
color3{ 0, 0, 1 });
}
}

35
Рис. 23. Рисование графика функций точками

Рис. 24. Рисование графика функций линиями

Рис. 25. Рисование графика функций линиями и полигонами

36
3.2. Установка освещения

Одной из интересных и востребованных задач компьютерной


графики является задание освещения и тени на рисунках (см. рис 26).

Рис. 26. Освещенная и неосвещенная сферы

Рассмотрим виды источников света.


Фоновое излучение – это свет, который настолько распределен
средой (предметами, стенами), что его направление определить не-
возможно, – кажется, что он исходит отовсюду.
Диффузный компонент – это свет, идущий в заданном направ-
лении, таким образом, что он выглядит ярче, если падает на поверх-
ность под прямым углом, и выглядит тусклым, если касается ее всего
лишь вскользь. Вероятно, любой свет, исходящий из определенной
точки, имеет диффузный компонент.
Зеркальный свет исходит из определенной точки и отражается от
поверхности в определенном направлении. При отражении хорошо
сфокусированного лазерного луча от качественного зеркала происхо-
дит почти 100-процентное зеркальное отражение. Блестящий метал
или пластик имеют высокий зеркальный компонент.
Исходящий свет – это свет, исходящий от самого объекта. В мо-
дели освещения OpenGL исходящий свет добавляет поверхности объ-
екта интенсивность цвета, но на него не влияют никакие источники
света, и он не производит дополнительного света для сцены в целом.
В следующих примерах демонстрируются функции управления осве-
щением.
Пример 17. Пример установки источника света и включе-
ния освещения
// Значение фонового света
GLfloat light1[] = { 1.0,1.0,1.0,1.0 };
// Позиция света

37
GLfloat light_position1[] = { –9.0,–9.0,5.0,0.0 };
glLightfv(GL_LIGHT0, GL_POSITION, light_position1);
glLightfv(GL_LIGHT0, GL_DIFFUSE, light1);
GL_LIGHT0, GL_LIGHT1, …, GL_LIGHT7 – источники света
// включение источника света
glEnable(GL_LIGHT0);
// включение освещения
glEnable(GL_LIGHTING);
// выключение освещения
glDisable(GL_LIGHTING);

Чтобы включить на сцене освещение, требуется выполнить не-


сколько шагов:
1. Определить вектор нормали для каждой вершины каждого
объекта. Эти нормали задают ориентацию объекта по отношению к
источникам света.
2. Создать, выбрать и позиционировать один или более источни-
ков света.
3. Создать и выбрать модель освещения, которая определяет
уровень глобального фонового света и эффективное положение точки
наблюдения (для вычислений, связанных с освещением).
4. Задать свойства материала для объектов сцены.
Пример 18. Пример установки источника света и включе-
ния освещения
GLfloat mat_specular[] = { 1.0,1.0,1.0,1.0 };
GLfloat mat_shininess[] = { 50.0 };
GLfloat light_position[] = { 2.0,2.0,0.0,0.0 };
GLfloat white_light[] = { 1.0,1.0,1.0,1.0 };
glClearColor(1.0, 1.0, 1.0, 0.0);
glShadeModel(GL_SMOOTH);
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
glLightfv(GL_LIGHT0, GL_DIFFUSE, white_light);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);

Результаты работы данной программы приведены на рис. 27.


В примере 18 для нулевого источника света (GL_LIGHT0) зада-
ется красный цвет для диффузного отражения. Кроме того, можно до-
бавить к сцене как минимум 8 источников света различных цветов,
используя константы GL_LIGHT0.. GL_LIGHT7.
38
Рис. 27. Сфера, освещенная красным источником света

Каждому добавленному источнику света можно задать следую-


щие параметры:
• GL_AMBIENT − интенсивность фонового света;
• GL_DIFFUSE − интенсивность диффузного света;
• GL_SPECULAR − интенсивность зеркального света;
• GL_POSITION − положение источника света;
• GL_SPOT_DIRECTION − направление света прожектора;
• GL_SPOT_EXPONENT − концентрация светового луча;
• GL_SPOT_CUTOFF − угловая ширина светового луча;
• GL_CONSTANT_ATTENUATION − постоянный фактор ос-
лабления;
• GL_LINEAR_ATTENUATION − линейный фактор ослабле-
ния;
• GL_QUADRATIC_ATTENUATION − квадратичный фактор
ослабления.
Пример 19. Пример установки второго источника света
GLfloat light_position2[] = { -3.0,2.0,0.0,0.0 };
GLfloat light2[] = { 0.0,0.0,1.0,1.0 };
glLightfv(GL_LIGHT1, GL_POSITION, light_position2);
glLightfv(GL_LIGHT1, GL_DIFFUSE, light2);
glEnable(GL_LIGHT1);

Рис. 28. Сфера, освещенная красным и синим источниками света

39
Как видно из примера, добавленный источник синего света бу-
дет направлен на левую сторону сферы. Результаты работы данной
программы продемонстрированы на рис. 28.

Главной целью любого моделирования объектов является созда-


ние точной и максимально реалистичной модели. Насколько реали-
стичной получится создаваемая модель, зависит от выбора материа-
лов, из которых будет создана текстура объекта.
Текстура – растровое изображение, воспроизводящее визуаль-
ные свойства каких-либо поверхностей или объектов.
Текстурирование (или наложение текстур) – способ придания
модели объекта и ее поверхности нужных свойств путем наложения
текстуры (изображения) на данную поверхность. Текстурирование
является одним из важнейших этапов работы при построении любой
модели. На данном этапе поверхностям модели объекта придаются
нужные свойства, для того чтобы сделать ее более реалистичной.
Текстурирование позволяет наложить изображение на полигон
(многоугольник) и вывести его с наложенной на него текстурой, соот-
ветствующим образом преобразованной. Пример наложения текстуры
проиллюстрирован на рис. 29.

Рис. 29. Наложение текстур

40
OpenGL поддерживает одно- и двумерные текстуры и различные
способы наложения текстуры. Функция glTexImage2D() определяет
двумерную текстуру. Рассмотрим эту функцию подробнее.
glTexImage2D(GL_TEXTURE_2D, 0, 3, texture1–>sizeX,
texture1–>sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, texture1–>data);

Перечислим значения параметров функции glTexImage2D():


1) текстура будет двумерной (GL_TEXTURE_2D);
2) задает уровень детализации, это обычно ноль;
3) число компонент цветовых данных, так как изображение сде-
лано из трех цветовых компонент (красный, зеленый, синий);
4) texture1– >sizeX – ширина текстуры;
5) texture1–>sizeY – высота текстуры;
6) это бордюр; он обычно принимает нулевое значение;
7) GL_RGB сообщает OpenGL, что данные изображения пред-
ставлены в порядке следования красных, зеленых и синих компонент
цвета;
8) GL_UNSIGNED_BYTE означает, что данные, из которых со-
стоит изображение, имеют размер байта и все числа без знака;
9) texture1–>data сообщает OpenGL, откуда брать данные. В этом
случае указатель на данные содержится в записи texture1.
Пример 20. Пример загрузки и хранения текстуры
// Подключение модуля glaux.h:
#include “glaux.h”
// Загрузка библиотеки glaux.lib :
#pragma comment (lib, "glaux.lib")
// Объявление массива для хранения индексов текстур
GLuint texture[2];
// Функция загрузки текстуры
GLvoid LoadTextures()
{
// задает указатель на структуру для хранения картинки
AUX_RGBImageRec *texture1;
// загрузка текстуры из файла
texture1 = auxDIBImageLoad("dom2.bmp");
// генерация имени текстуры
glGenTextures(1, &texture[0]);
// выбор текущей текстуры
glBindTexture(GL_TEXTURE_2D, texture[0]);
// установка линейной фильтрации
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
GL_LINEAR);

41
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
GL_LINEAR);
// создание текстуры
glTexImage2D(GL_TEXTURE_2D, 0, 3, texture1–>sizeX,
texture1–>sizeY, 0,GL_RGB, GL_UNSIGNED_BYTE, texture1–>data);
}

После загрузки текстуры можно применять наложение текстуры


на полигоны. Для этого используются функции установки текущих
координат:
void glTexCoord[1 2 3 4][s i f d](type coord)
void glTexCoord[1 2 3 4][s i f d]v(type *coord)

Функция glTexCoord*() устанавливает текущие координаты


текстуры. Эти координаты ассоциируются со всеми последующими
вызовами glVertex*(), пока glTexCoord*() не будет вызвана сно-
ва. Для того чтобы включить или выключить текстурирование, необ-
ходимо вызвать функцию glEnable(GL_TEXTURE_2D) или
glDisable(GL_TEXTURE_2D) соответственно. Чтобы указать, какая
именно текстура будет накладываться на примитив, используется
функция glBindTexture().
Пример 21. Наложение текстур
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texture[0]);
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f);
glVertex3f(-1, -1, 0);
glTexCoord2f(1.0f, 0.0f);
glVertex3f(-1, 1, 0);
glTexCoord2f(1.0f, 1.0f);
glVertex3f(1, 1, 0);
glTexCoord2f(0.0f, 1.0f);
glVertex3f(1, -1, 0);
glEnd();
glDisable(GL_TEXTURE_2D);

В примере 21 рисуется текстурированный четырехугольник. Пе-


ред заданием каждой вершины примитива указывается текстурная ко-
ордината, которая будет ассоциироваться с этой вершиной. Примитив
из примера 21 показан на рис. 30. Вся текстура накладывается на весь

42
четырехугольник. На рис. 31 проиллюстрирован пример наложения
текстуры с произвольными текстурными координатами.

Рис. 30. Пример наложения текстуры

Рис. 31. Наложение текстуры с произвольными


текстурными координатами

Пример 22. Загрузка текстур из bmp–файла


void LoadBMP(GLuint *texture, char *filename){
AUX_RGBImageRec *txt;
txt = auxDIBImageLoad(filename);
glGenTextures(1, texture);
glBindTexture(GL_TEXTURE_2D, *texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
GL_LINEAR);

43
glTexImage2D(GL_TEXTURE_2D, 0, 3, txt–>sizeX, txt–>sizeY, 0,
GL_RGB, GL_UNSIGNED_BYTE, txt–>data);
delete txt;
}

Важные замечания:
• для правильной загрузки текстуры из файла необходимо, что-
бы размеры картинки, находящейся в файле, в пикселах были
кратны степени 2. Например, картинка может быть размером
256 на 256 пикселов;
• текстурные координаты изменяются в пределах от 0 до 1, и в
левом нижнем углу находится начало координат внутри тек-
стуры (рис. 30, 31).

Список отображения – это группа команд OpenGL, сохраненная


для дальнейшего исполнения. Когда выполняется список отображе-
ния, команды, включенные в него, исполняются в том порядке, в ко-
тором они были заданы. Применяя список отображения, можно соз-
дать объект лишь один раз, в дальнейшем он может быть использован
любое количество раз, потом этот объект должен быть удален.
Перечислим основные преимущества использования списков
отображения.
• Так как объект уже создан в списке отображения, не нужно
указывать OpenGL, как создать его. Он уже создан в памяти.
Это снижает нагрузку на процессор и позволяет программам
выполняться быстрее.
• Списки отображения могут увеличить быстродействие, по-
скольку вы можете сохранять в них команды OpenGL для
дальнейшего исполнения.
• Хорошей идеей является кэширование команд в списке ото-
бражения, если вы планируете рисовать какую-либо геомет-
рию несколько раз или несколько раз устанавливаете одно и
то же состояние настроек.
• Используя списки отображения, вы можете определить гео-
метрические данные или изменения в состоянии один раз и за-
тем исполнять их столько раз, сколько вам нужно.

44
Функция glGenLists(n) создает место для n списков и воз-
вращает указатель на первый из них.
Функция glNewList(GLuint list, GLenum mode) марки-
рует начало списка отображения. Первый параметр – место хране-
ния списка, на которое указывает переменная list. Второй пара-
метр – режим создания списка, он может принимать одно из двух
значений GL_COMPILE или GL_COMPILE_AND_EXECUTE. Ис-
пользуя значение GL_COMPILE, OpenGL создаст список в памяти,
но команды при этом не выполнятся. Используя значение
GL_COMPILE_AND_EXECUTE, OpenGL создаст список в памяти
и выполнит его команды первый раз на этапе создания. Функция
glEndList() маркирует конец списка отображения.
Функция glCallList(GLuint list) исполняет список ото-
бражения, заданный аргументом list. Команды в списке исполняются
в том порядке, в котором они сохранялись, таким же образом, как ес-
ли бы они выполнялись вне списка.
Пример 23. Создание списка отображения
GLvoid BuildLists()
{
GLuint quad;
box = glGenLists(2); // создаем два списка
quad = box + 1;
// Новый откомпилированный список
glNewList(quad, GL_COMPILE);
glBegin(GL_QUADS);
glTexCoord2f(0, 0);
glVertex3f(0, 0, 0);
glTexCoord2f(0, 1);
glVertex3f(0, 1, 0);
glTexCoord2f(1, 1);
glVertex3f(1, 1, 0);
glTexCoord2f(1, 0);
glVertex3f(1, 0, 0);
glEnd(); // Закончили рисование четырехугольников
glEndList(); // Закончили создание списка

// Новый откомпилированный список box


glNewList(box, GL_COMPILE);
glBindTexture(GL_TEXTURE_2D, texture[0]);
glEnable(GL_TEXTURE_2D);
glPushMatrix();
glTranslated(–0.5, –0.5, –0.5);

45
glCallList(quad);
glTranslated(0, 0, 1);
glCallList(quad);
glRotated(90, 0, 1, 0);
glCallList(quad);
glTranslated(0, 0, 1);
glCallList(quad);
glTranslated(0, 1, –1);
glRotated(90, 1, 0, 0);
glBindTexture(GL_TEXTURE_2D, texture[1]);
glCallList(quad);
glTranslated(0, 0, 1);
glCallList(quad);
glPopMatrix();
glDisable(GL_TEXTURE_2D);
glEndList(); // Закончили создание списка box
}

Между функциями glNewList() и glEndList() можно зада-


вать цвета, менять текстуры, выполнять преобразования и т. д. Един-
ственный тип кода, который нельзя использовать, – это код, способ-
ный изменить список отображения «на лету». Однажды создав список
отображения, его нельзя изменить. На рис. 32 демонстрируется работа
программы из примера 23, использующая список отображения.

Рис. 32.Вызов списка отображения glCallList(box)

46
Пример 24. Вызов списка отображения
GLvoid DrawPyramide()
{
glPushMatrix();
int n = 5; int m = 5;
int mas[6][3] = { { 1,0,0 },{ 0,1,0 },{ 0,0,1 },
{ 1,1,0 },{ 0,1,1 },{ 1,0,1 } };
for (int j = 0; j <= n; j++)
{
for (int i = –m; i <= m; i++)
for (int k = –m; k <= m; k++)
{
glPushMatrix();
glTranslated(i, j, k);
glColor3d(mas[m][0], mas[m][1], mas[m][2]);
glCallList(box);
glPopMatrix();
}
m––;
}
glPopMatrix();
}

В результате вызова функции из примера 24 получится фигура,


изображенная на рис. 33.

Рис. 33. Пример работы функции DrawPyramide()

47
Функция glDeleteLists (GLuint list, GLsizei range)
удаляет последовательные (по индексам) списки отображения, начи-
ная с индекса list. Количество удаляемых списков задается парамет-
ром range. После вызова функции glDeleteLists() индексы удаленных
списков становятся доступны для дальнейшего использования.

48
В модуле «glaux.h» реализовано множество трехмерных геомет-
рических фигур. На рис. 34 приведены некоторые из них.

Рис. 34. Трехмерные фигуры в модуле «glaux.h»

Рассмотрим примеры использования некоторых стандартных


трехмерных геометрических фигур модуля «glaux.h».
Для рисования сферы используются функции auxSolid
Sphere(radius) и auxWireSphere(radius). Первая рисует за-
крашенную сферу, вторая – сеточную сферу. Обе функции рисуют
сферы единичного радиуса текущего цвета в начале координат. Для
более удобного использования этих функций реализуем дополнитель-
ную функцию DrawSphere(), в которую будут передаваться допол-
49
нительные параметры – координаты, размер, цвет и флаг: закрашен-
ная сфера или нет. В примере 25 демонстрируется работа функции
рисования сферы. На рис. 35 можно посмотреть результат работы
программы из примера 25.
Пример 25. Функция рисования сферы
void DrawSphere(vertex3 v, float radius, color3 color,
bool solid)
{
glPushMatrix();
glTranslated(v.x, v.y, v.z);
glColor3d(color.r, color.g, color.b);
if (solid) auxSolidSphere(radius);
else auxWireSphere(radius);
glPopMatrix();
}

Пример 26. Вызовы функции рисования сферы с разными


параметрами
DrawSphere(vertex3{ 0, 0, 0 }, 5, color3{ 0, 1, 0 }, 0);
DrawSphere(vertex3{ –7, 0, 2 }, 3, color3{ 1, 0, 0 }, 1);
DrawSphere(vertex3{ 6, 2, 0 }, 2.5, color3{ 0, 0, 1 }, 1);

Рис. 35. Пример рисования трех сфер

Аналогично функции DrawSphere() реализуем функцию рисо-


вания куба DrawKub(), которая использует функции auxSolidKub()
и auxWireKub() из модуля «glaux.h». Дополнительным парамет-
ром является угол поворота angle относительно каждой из осей ко-
ординат.
50
Пример 27. Функция рисования куба
void DrawKub(vertex3 v, float w, color3 color, vertex3 angle,
bool solid)
{
glPushMatrix();
glTranslated(v.x, v.y, v.z);
glRotated(angle.x, 1, 0, 0);
glRotated(angle.y, 0, 1, 0);
glRotated(angle.z, 0, 0, 1);
glColor3d(color.r, color.g, color.b);
if (solid) auxSolidCube(w);
else auxWireCube(w);
glPopMatrix();
}

Пример 28. Вызовы функции рисования куба с разными па-


раметрами
DrawKub(vertex3{ 0, 0, 0 }, 4, color3{ 1,0,0 },
vertex3{ 45, 45, 20 }, 0);
DrawKub(vertex3{ –5, 0, 0 }, 3, color3{ 1,1,0 },
vertex3{ 30, 60, 10 }, 1);
DrawKub(vertex3{ 7, 3, 0 }, 4.5, color3{ 0,1,1 },
vertex3{–45, 80, 50}, 0);

Рис. 36. Пример рисования трех кубов

На рис. 36 демонстрируется результат выполнения примера 28.


Другие функции рисования геометрических фигур перечислены
в табл. 1.

51
Таблица 1
Функции рисования геометрических фигур «glaux.h»
Функция Описание
auxSolidBox(width, height, depth) параллелепипед со сторонами width, height,
auxWireBox(width, height, depth) depth
auxSolidTorus(r,R) тор, образованный с помощью вращения
auxWireTorus(r,R) окружностей; задаются малый и большой
радиусы r, R
auxSolidCylinder(r,height) цилиндр с радиусом основания r и высотой
auxWireCylinder(r,height) height
auxSolidCone(r,height) конус с радиусом основания r и высотой
auxWireCone(r,height) height
auxSolidIcosahedron(width) правильные многогранники; задается диа-
auxWireIcosahedron(width) метр описанной окружности width
auxSolidOctahedron(width)
auxSolidOctahedron(width)
auxSolidTetrahedron(width)
auxSolidTetrahedron(width)
auxSolidDodecahedron(width)
auxSolidDodecahedron(width)
auxSolidTeapot(width) чайник; задается размер объекта
auxWireTeapot(width)

Рис. 37. Пример рисования геометрических объектов

На рис. 37 демонстрируется результат работы функций рисова-


ния 10 различных геометрических фигур, представленных в модуле
«glaux.h».
52
Одной из наиболее сложных и актуальных задач современной
компьютерной графики является 3D-моделирование (создание 3D-
моделей и алгоритмов их визуализации, преобразования и т. д.). В на-
стоящее время это направление широко востребовано и активно при-
меняется в различных сферах человеческой деятельности:
• реклама и маркетинг (3D-визуализация и анимация различных
объектов);
• компьютерные игры и фильмы (3D-модели персонажей филь-
мов и игр, а также ландшафта, строений и т. д.);
• строительство и дизайн интерьеров (3D-модели зданий, ком-
нат и т. д.);
• производство (3D-модели выпускаемых изделий);
• наука;
• образование и многое другое.
В данном разделе рассмотрим пример создания сцены из раз-
личных 3D-объектов. Сначала представим структуры и вспомогатель-
ные функции.
Пример 29. Описание структур
// структура для вершины
struct vertex3 { GLdouble x, y, z; };
// структура для цвета
struct color3 { GLdouble r, g, b; };
// структура для текстурной координаты
struct tvertex2 { GLfloat x, y; };
// структура для линии
struct line2 { unsigned int p1, p2; };
// структура для треугольной грани
struct triangle3 { unsigned int p1, p2, p3; };
// структура для четырехугольной грани
struct quad4 { unsigned int p1, p2, p3, p4; };

// создание структуры вектора


vertex3 vertex(GLdouble x, GLdouble y, GLdouble z) {
vertex3 result = { x, y, z };
return result;
}
// создание структуры цвета
color3 color(GLdouble r, GLdouble g, GLdouble b) {
color3 result = { r, g, b };
return result;

53
}
// создание структуры текстурной координаты
tvertex2 tvertex(float x, float y)
{
tvertex2 tv = { x, y };
return tv;
}
// создание структуры линии
line2 line(int p1, int p2)
{
line2 l;
l.p1 = p1; l.p2 = p2;
return l;
}
// создание структуры треугольника
triangle3 triangle(int p1, int p2, int p3)
{
triangle3 t;
t.p1 = p1; t.p2 = p2; t.p3 = p3;
return t;
}
// создание структуры четырехугольника
quad4 quad(int p1, int p2, int p3, int p4)
{
quad4 q;
q.p1 = p1; q.p2 = p2; q.p3 = p3; q.p4 = p4;
return q;
}

Далее опишем базовый класс. Этот класс содержит поля: пози-


ция, угол поворота, цвет и размер объекта. Также класс содержит
конструктор, метод рисования, метод инициализации и методы уста-
новки значений основных полей.
Пример 30. Описание и реализация базового класса
class Item { // базовый класс
protected:
vertex3 pos;
vertex3 angle;
color3 color;
float size;
public:
Item(); // конструктор
virtual void Draw(); // рисование
virtual void Init(); // инициализация
54
void SetSize(float size); // установка размера
void SetPos(vertex3 pos); // установка позиции
void SetAngle(vertex3 angle); // установка угла
void SetColor(color3 color); // установка цвета
void Rotate(); // применение поворота
};
Item::Item() // конструктор
{
pos.x = 0; pos.y = 0; pos.z = 0;
angle.x = 0; angle.y = 0; angle.z = 0;
color.r = 1; color.g = 1; color.b = 1;
size = 1;
};
void Item::Draw() {} // рисование
void Item::Init() {} // инициализация
void Item::SetSize(float size) // установка размера
{
this–>size = size;
}
void Item::SetPos(vertex3 pos) // установка позиции
{
this–>pos = pos;
}
void Item::SetAngle(vertex3 angle) // установка угла
{
this–>angle = angle;
}
void Item::SetColor(color3 color) // установка цвета
{
this–>color = color;
}
void Item::Rotate() // применение поворота
{
glTranslated(pos.x, pos.y, pos.z);
glRotated(angle.x, 1, 0, 0);
glRotated(angle.y, 0, 1, 0);
glRotated(angle.z, 0, 0, 1);
glTranslated(–pos.x, –pos.y, –pos.z);
}

Класс Point (точка) является наследником класса Item. Point со-


держит свой конструктор и метод рисования.

55
Пример 31. Описание и реализация класса Point, наслед-
ника базового класса Item
class Point : public Item
{
public:
Point(vertex3 pos, color3 color, float size);
virtual void Draw();
};
Point::Point(vertex3 pos, color3 color, float size) :Item()
{
SetPos(pos); SetColor(color); SetSize(size);
}
void Point::Draw()
{
glPointSize(size);
glColor3d(color.r, color.g, color.b);
glPushMatrix();
glTranslated(pos.x, pos.y, pos.z);
glBegin(GL_POINTS);
glVertex3d(0, 0, 0);
glEnd();
glPopMatrix();
}

Класс Line (линия) является наследником класса Point. Класс


Line содержит дополнительные поля: координаты и цвет второй точ-
ки. Также класс содержит свои конструкторы и метод рисования.
Пример 32. Описание и реализация класса Line, наслед-
ника класса Point
class Line : public Point {
protected:
vertex3 pos2;
color3 color2;
public:
// конструктор с координатами, цветом и размером
Line(vertex3 pos1, vertex3 pos2, color3 color, float size);
// конструктор с координатами, двумя цветами и размером
Line(vertex3 pos1, vertex3 pos2, color3 color, color3 color2,
float size);
virtual void Draw();
};
Line::Line(vertex3 pos1, vertex3 pos2, color3 color, float
size)

56
:Point(pos1, color, size)
{
this–>pos2 = pos2;
color2 = color;
}
Line::Line(vertex3 pos1, vertex3 pos2, color3 color, color3
color2, float size) : Point(pos1, color, size)
{
this–>pos2 = pos2;
this–>color2 = color2;
}
void Line::Draw()
{
glPushMatrix();
Rotate();
glLineWidth(size);
glBegin(GL_LINES);
glColor3d(color.r, color.g, color.b);
glVertex3d(pos.x, pos.y, pos.z);
glColor3d(color2.r, color2.g, color2.b);
glVertex3d(pos2.x, pos2.y, pos2.z);
glEnd();
glPopMatrix();
glPopMatrix();
}

Класс Triangle (треугольник) является наследником класса Line.


Класс Triangle содержит дополнительные поля: координаты и цвет
третьей точки. Кроме того, этот класс содержит свои конструкторы и
метод рисования.
Пример 33. Описание и реализация класса Triangle, на-
следника класса Line
class Triangle : public Line
{
protected:
vertex3 pos3;
color3 color_3;
public:
// конструктор с координатами, цветом и размером
Triangle(vertex3 pos1, vertex3 pos2, vertex3 pos3, color3
color);
// конструктор с координатами, тремя цветами и размером

57
Triangle(vertex3 pos1, vertex3 pos2, vertex3 pos3, color3
color, color3 color2, color3 color_3);
virtual void Draw();
};
Triangle::Triangle(vertex3 pos1, vertex3 pos2, vertex3 pos3,
color3 color) :Line(pos1, pos2, color, size)
{
this–>pos3 = pos3;
color_3 = color;
}
Triangle::Triangle(vertex3 pos1, vertex3 pos2, vertex3 pos3,
color3 color, color3 color2, color3 color_3) : Line(pos1, pos2,
color, color2, size)
{
this–>pos3 = pos3; this–>color_3 = color_3;
}
void Triangle::Draw()
{
glPushMatrix();
Rotate();
glBegin(GL_TRIANGLES);
glColor3d(color.r, color.g, color.b);
glVertex3d(pos.x, pos.y, pos.z);
glColor3d(color2.r, color2.g, color2.b);
glVertex3d(pos2.x, pos2.y, pos2.z);
glColor3d(color_3.r, color_3.g, color_3.b);
glVertex3d(pos3.x, pos3.y, pos3.z);
glEnd();
glPopMatrix();
glPopMatrix();
}

Создадим еще один класс − класс Quad (четырехугольник). Он


является наследником класса Triangle. Класс Quad содержит допол-
нительные поля: координаты и цвет четвертой точки, а также свои
конструкторы и метод рисования.
Пример 34. Описание и реализация класса Quad, наслед-
ника класса Triangle
class Quad : public Triangle
{
protected:
vertex3 pos4; color3 color4;
public:

58
// конструктор с координатами, цветом и размером
Quad(vertex3 pos1, vertex3 pos2, vertex3 pos3, vertex3 pos4,
color3 color);
// конструктор с координатами, четырьмя цветами и размером
Quad(vertex3 pos1, vertex3 pos2, vertex3 pos3, vertex3 pos4,
color3 color, color3 color2, color3 color_3, color3 color4);
virtual void Draw();
};
Quad::Quad(vertex3 pos1, vertex3 pos2, vertex3 pos3, vertex3
pos4, color3 color) :Triangle(pos1, pos2, pos3, color)
{
this–>pos4 = pos4;
color4 = color;
}
Quad::Quad(vertex3 pos1, vertex3 pos2, vertex3 pos3, vertex3
pos4, color3 color, color3 color2, color3 color_3, color3 col-
or4) : Triangle(pos1, pos2, pos3, color, color2, color_3)
{
this–>pos4 = pos4;
this–>color4 = color4;
}
void Quad::Draw()
{
glPushMatrix(); Rotate();
glBegin(GL_QUADS);
glColor3d(color.r, color.g, color.b);
glVertex3d(pos.x, pos.y, pos.z);
glColor3d(color2.r, color2.g, color2.b);
glVertex3d(pos2.x, pos2.y, pos2.z);
glColor3d(color_3.r, color_3.g, color_3.b);
glVertex3d(pos3.x, pos3.y, pos3.z);
glColor3d(color4.r, color4.g, color4.b);
glVertex3d(pos4.x, pos4.y, pos4.z);
glEnd();
glPopMatrix();
glPopMatrix();
}

Добавим класс QuadT (текстурированный четырехугольник), ко-


торый является наследником класса Quad. Класс QuadT содержит до-
полнительные поля: текстурные координаты для четырех точек и на-
звание файла с текстурой, а также свои конструкторы и метод рисо-
вания.

59
Пример 35. Описание и реализация класса QuadT, наслед-
ника класса Quad
class QuadT : public Quad
{
protected:
tvertex2 tv1, tv2, tv3, tv4;
string name_texture;
public:
QuadT(vertex3 pos1, vertex3 pos2, vertex3 pos3, vertex3 pos4,
color3 color, string name_texture);
QuadT(vertex3 pos1, vertex3 pos2, vertex3 pos3, vertex3 pos4,
color3 color, string name_texture, tvertex2 tv1, tvertex2 tv2,
tvertex2 tv3, tvertex2 tv4);
virtual void Draw();
};
QuadT::QuadT(vertex3 pos1, vertex3 pos2, vertex3 pos3, vertex3
pos4, color3 color, string name_texture) :Quad(pos1, pos2,
pos3, pos4, color)
{
this–>name_texture = name_texture;
this–>tv1 = tvertex(0, 0); this–>tv2 = tvertex(0, 1);
this–>tv3 = tvertex(1, 1); this–>tv4 = tvertex(1, 0);
}
QuadT::QuadT(vertex3 pos1, vertex3 pos2, vertex3 pos3, vertex3
pos4, color3 color, string name_texture, tvertex2 tv1, tvertex2
tv2, tvertex2 tv3, tvertex2 tv4) :Quad(pos1, pos2, pos3, pos4,
color)
{
this–>name_texture = name_texture;
this–>tv1 = tv1; this–>tv2 = tv2;
this–>tv3 = tv3; this–>tv4 = tv4;
}
void QuadT::Draw()
{
glPushMatrix();
Rotate();
int tindex = SCENE::getTextureIndex(name_texture);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, tindex);
glBegin(GL_QUADS);
glTexCoord2f(tv1.x, tv1.y);
glColor3d(color.r, color.g, color.b);
glVertex3d(pos.x, pos.y, pos.z);

glTexCoord2f(tv2.x, tv2.y);

60
glColor3d(color2.r, color2.g, color2.b);
glVertex3d(pos2.x, pos2.y, pos2.z);
glTexCoord2f(tv3.x, tv3.y);
glColor3d(color_3.r, color_3.g, color_3.b);
glVertex3d(pos3.x, pos3.y, pos3.z);
glTexCoord2f(tv4.x, tv4.y);
glColor3d(color4.r, color4.g, color4.b);
glVertex3d(pos4.x, pos4.y, pos4.z);
glEnd();
glPopMatrix();
glDisable(GL_TEXTURE_2D);
}

В завершение опишем класс Item3D, который является наслед-


ником класса Item. Класс Item3D – это сложный 3D-объект, состоя-
щий из точек, линий, треугольников и четырехугольников. Этот класс
содержит дополнительные поля: вектор точек, вектор линий, вектор
треугольников и вектор четырехугольников. Кроме того, класс
Item3D содержит свои конструктор, деструктор и метод рисования.
Пример 36. Описание и реализация класса Item3D, на-
следника базового класса Item
class Item3D : public Item
{
protected:
int tindex;
int drawtype;
vector <vertex3> points;
vector <line2> lines;
vector <triangle3> triangles;
vector <quad4> quads;
public:
Item3D();
~Item3D();
virtual void Draw();
};
Item3D::Item3D() :Item()
{
tindex = –1;
drawtype = 0;
};
Item3D::~Item3D()
{
points.clear();

61
lines.clear();
quads.clear();
triangles.clear();
}
void Item3D::Draw()
{
glPushMatrix();
Rotate();
if ((drawtype == 0)) // рисование только точками
for (unsigned i = 0; i<points.size(); i++)
DrawPoint(points[i], 10, color);

if ((drawtype == 1)) // рисование только линиями


for (unsigned i = 0; i<lines.size(); i++)
DrawLine(points[lines[i].p1], points[lines[i].p2], color, 2);

if ((drawtype == 2)) // рисование треугольниками и


четырехугольниками
{
for (unsigned i = 0; i<triangles.size(); i++)
DrawTriangle(points[triangles[i].p1],
points[triangles[i].p2],
points[triangles[i].p3], color);
for (unsigned i = 0; i<quads.size(); i++)
DrawQuad(points[quads[i].p1], points[quads[i].p2],
points[quads[i].p3], points[quads[i].p4], color);
}
if ((drawtype == 3)) // рисование текстурированными
треугольниками и четырехугольниками
{
for (unsigned i = 0; i<triangles.size(); i++)
DrawTriangleT(points[triangles[i].p1], points[triangles[i].p2],
points[triangles[i].p3], clWhite,tvertex(0, 0), tvertex(0.5,
1), tvertex(1, 0), tindex);
for (unsigned i = 0; i<quads.size(); i++)
DrawQuadT(points[quads[i].p1], points[quads[i].p2],
points[quads[i].p3], points[quads[i].p4], clWhite, tindex);
}
glPopMatrix();
}

На рис. 38 представлена иерархия классов.


Приведем пример создания класса, описывающего куб по вось-
ми точкам (см. рис. 39).

62
Рис. 38. Диаграмма классов

Рис. 39. Создание куба по восьми точкам

Пример 37. Описание и реализация класса Kub, наследни-


ка класса Item3D
class Kub : public Item3D
{
public:
Kub(vertex3 pos, float size, int drawtype, int tindex);

63
Kub(vertex3 pos, float size, color3 color, int drawtype, int
tindex);
Kub(vertex3 pos, float size, color3 color, vertex3 angle, int
drawtype, int tindex);
void Init();
};
Kub::Kub(vertex3 pos, float size, int drawtype, int tindex)
:Item3D()
{
SetPos(pos);
SetSize(size);
this–>tindex = tindex;
this–>drawtype = drawtype;
Init();
}
Kub::Kub(vertex3 pos, float size, color3 color, int drawtype,
int tindex) :Item3D()
{
SetPos(pos); SetSize(size); SetColor(color);
this–>tindex = tindex;
this–>drawtype = drawtype;
Init();
}
Kub::Kub(vertex3 pos, float size, color3 color, vertex3 angle,
int drawtype, int tindex) :Item3D()
{
SetPos(pos);
SetSize(size);
SetColor(color);
SetAngle(angle);
this–>tindex = tindex;
this–>drawtype = drawtype;
Init();
}
void Kub::Init() // создание вершин, ребер и граней куба
{
GLdouble x0 = pos.x – size / 2,
y0 = pos.y – size / 2, z0 = pos.z – size / 2;
GLdouble dx = size, dy = size, dz = size;
points.push_back(vertex(x0, y0, z0));
points.push_back(vertex(x0 + dx, y0, z0));
points.push_back(vertex(x0 + dx, y0 + dy, z0));
points.push_back(vertex(x0, y0 + dy, z0));
points.push_back(vertex(x0, y0, z0 + dz));
points.push_back(vertex(x0 + dx, y0, z0 + dz));

64
points.push_back(vertex(x0 + dx, y0 + dy, z0 + dz));
points.push_back(vertex(x0, y0 + dy, z0 + dz));
lines.push_back(line(0, 1)); lines.push_back(line(1, 2));
lines.push_back(line(2, 3)); lines.push_back(line(0, 3));
lines.push_back(line(4, 5)); lines.push_back(line(5, 6));
lines.push_back(line(6, 7)); lines.push_back(line(4, 7));
lines.push_back(line(0, 4)); lines.push_back(line(1, 5));
lines.push_back(line(2, 6)); lines.push_back(line(3, 7));
quads.push_back(quad(0, 3, 2, 1));
quads.push_back(quad(1, 2, 6, 5));
quads.push_back(quad(5, 6, 7, 4));
quads.push_back(quad(4, 7, 3, 0));
quads.push_back(quad(2, 3, 7, 6));
quads.push_back(quad(1, 0, 4, 5));
}

Пример 38. Описание и реализация класса Shere, наслед-


ника класса Item3D
class Shere : public Item3D
{
public:
Shere(vertex3 pos, float size, int drawtype, int tindex);
Shere(vertex3 pos, float size, color3 color, int drawtype, int
tindex);
Shere(vertex3 pos, float size, color3 color, vertex3 angle, int
drawtype, int tindex);
void Init();
};
Shere::Shere(vertex3 pos, float size, int drawtype, int tindex)
:Item3D()
{
SetPos(pos);
SetSize(size);
this–>tindex = tindex;
this–>drawtype = drawtype;
Init();
}
Shere::Shere(vertex3 pos, float size, color3 color, int
drawtype, int tindex) :Item3D()
{
Item3D();
SetPos(pos);
SetSize(size);
SetColor(color);
this–>tindex = tindex;

65
this–>drawtype = drawtype;
Init();
}
Shere::Shere(vertex3 pos, float size, color3 color, vertex3 an-
gle, int drawtype, int tindex) :Item3D()
{
Item3D();
SetPos(pos);
SetSize(size);
SetColor(color);
SetAngle(angle);
this–>tindex = tindex;
this–>drawtype = drawtype;
Init();
}
void Shere::Init()// создание вершин, ребер и граней сферы
{
int i = 0, j, l, k = 0, q = 0; float a, b;
float R = size / 2; int N = 30;
float da = 2 * 3.1415f / N; float dx = 1; float x, y, z;
for (l = 0; l < N; l++) // формирование точек
for (j = 0; j < N; j++)
{
a = j*da; b = l*da / 2;
x = R * sin(a)*sin(b);
y = R * cos(b);
z = R * cos(a)*sin(b);
points.push_back(vertex(x + pos.x, y + pos.y, z + pos.z));
}
// формирование горизонтальных линий
for (l = 0; l < N; l++)
{
for (j = 0; j < N – 1; j++)
lines.push_back(line(j + l*N, j + 1 + l*N));
lines.push_back(line(l*N, N – 1 + l*N));
}
// формирование вертикальных линий
for (l = 0; l < N – 1; l++)
for (j = 0; j < N; j++)
lines.push_back(line(j + l*N, j + N + l*N));
// формирование полигонов
for (j = 0; j < N – 1; j++)
{
for (l = 0; l < N – 1; l++)
quads.push_back(quad(l + j*N, l + 1 + j*N,

66
l + N + 1 + j*N, l + N + j*N));
quads.push_back(quad(0 + j*N, 0 + N + j*N,
0 + j*N + N + N – 1, 0 + j*N + N – 1));
}

Рис. 40. Рисование сфер в разных режимах

На рис. 40 демонстрируется работа программы из примера 38


при различных параметрах рисования.
Опишем класс TexturesList для загрузки и хранения текстур.
Этот класс содержит поля: вектор имен текстур и вектор индексов
текстур, а также метод загрузки текстуры и метод поиска текстуры по
имени.
Пример 39. Описание и реализация класса TexturesList
для хранения имен загруженных текстур
class TexturesList
{
vector<string> textures_names;
vector<GLuint> textures_index;
public:
void LoadTexture(string name, string type = "bmp");
GLuint getTextureIndex(string name);
};

// загрузка текстуры из файла


void TexturesList::LoadTexture(string name, string type)
{
int i = textures_index.size();
GLuint value;
if (strcmp(type.c_str(), "bmp") == 0)
LoadBMP(&value, (char *)name.c_str());
if (strcmp(type.c_str(), "tga") == 0)
LoadTGA(&value, (char *)name.c_str());

67
textures_index.push_back(value);
textures_names.push_back(name);
}
// поиск текстуры по имени
GLuint TexturesList::getTextureIndex(string name)
{
if (textures_names.size()>0)
for (unsigned i = 0; i<textures_names.size(); i++)
if (textures_names[i].compare(name) == 0)
return textures_index[i];
return –1;
}

Теперь опишем класс-контейнер SCENE для создания, хранения


и рисования объектов сцены. Этот класс содержит поле: вектор эле-
ментов типа Item. Так как все классы находятся в иерархии и являют-
ся наследниками базового класса, то в этот вектор мы можем поме-
щать экземпляры всех этих классов.
Пример 40. Класс-контейнер для хранения и рисования
элементов сцены
static TexturesList * textures;
using namespace std;
class SCENE
{
std::vector<Item *> items;
public:
SCENE();
~SCENE();
void Init();
void Draw();
static GLuint getTextureIndex(string name);
void InitLight();
void SetLight();
};
SCENE::SCENE()
{
Init();
}
SCENE::~SCENE()
{
for (unsigned i = 0; i<items.size(); i++)
delete items[i];
items.clear();

68
}
void SCENE::Init()
{
textures = new TexturesList();
textures–>LoadTexture("wall1.bmp");
textures–>LoadTexture("wall2.bmp");
textures–>LoadTexture("Grass32.bmp");
items.push_back(new Line(vertex(–10, 0, 0), vertex(10, 0, 0),
color(1, 1, 1), 1));
items.push_back(new Line(vertex(0, –10, 0), vertex(0, 10, 0),
color(1, 1, 1), 1));
items.push_back(new Line(vertex(0, 0, –10), vertex(0, 0, 10),
color(1, 1, 1), 1));
// points
items.push_back(new Point(vertex(0, 0, 0), color(1, 1, 1),
10));
items.push_back(new Point(vertex(0, 1, 0), color(1, 0, 0), 5));
items.push_back(new Point(vertex(0, –1, 0), color(1, 0, 0),
5));
items.push_back(new Point(vertex(1, 0, 0), color(0, 1, 0), 5));
items.push_back(new Point(vertex(–1, 0, 0), color(0, 0, 1),
5));
// triangles
items.push_back(new Triangle(vertex(–0.5, 0.2, 0), vertex(0,
0.5, 0), vertex(0.5, 0.2, 0), color(0, 1, 0)));
items.push_back(new Triangle(vertex(–0.5, –0.2, 0), vertex(0, –
0.5, 0), vertex(0.5, –0.2, 0), color(1, 0, 0), color(0, 1, 0),
color(0, 0, 1)));
// quads
Quad * quad = new Quad(vertex(–2, –2, 0), vertex(–2, –1, 0),
vertex(–1, –1, 0), vertex(–1, –2, 0), color(1, 0, 0));
quad–>SetAngle(vertex(0, 0, 45));
items.push_back(quad);
items.push_back(new Quad(vertex(–2, 2, 0), vertex(–2, 1, 0),
vertex(–1, 1, 0), vertex(–1, 2, 0), color(1, 0, 0), color(0, 1,
0), color(0, 0, 1), color(0, 1, 1)));
// quadst
items.push_back(new QuadT(vertex(1, 1, 0), vertex(1, 2, 0),
vertex(2, 2, 0), vertex(2, 1, 0), color(1, 1, 1), "wall1.bmp",
tvertex(0, 0), tvertex(0, 1), tvertex(1, 1), tvertex(1, 0)));
items.push_back(new QuadT(vertex(1, 2, 0), vertex(1, 3, 0),
vertex(2, 3, 0), vertex(2, 2, 0), color(1, 0, 0), "wall1.bmp",
tvertex(0, 0), tvertex(0, 0.5), tvertex(0.5, 0.5), tvertex(0.5,
0)));

69
items.push_back(new Kub(vertex(1, 0, 0), 1, color(1, 1, 1), 1,
0));
items.push_back(new Shere(vertex(3, 0, 0), 2, color(1, 1, 1),
1, 0));
}
GLuint SCENE::getTextureIndex(string name)
{
return textures–>getTextureIndex(name);
}
void SCENE::Draw()
{
for (unsigned i = 0; i<items.size(); i++)
items[i]–>Draw();
}

На рис. 41 демонстрируется результат работы программы из


примера 40, т. е. реализация работы с классом-контейнером для хра-
нения и рисования элементов сцены.

Рис. 41. Рисование сцены с различными объектами

Фракталы известны уже почти век и хорошо изучены. Многие


объекты в природе обладают свойствами фрактала, например: побе-
режья, облака, кроны деревьев, снежинки, система кровообращения,
кораллы и многие другие.

70
Фракталом называют геометрическую фигуру, которая удовле-
творяет одному или нескольким из следующих свойств:
• обладает сложной структурой при любом увеличении;
• является (приближенно) самоподобной;
• обладает дробной хаусдорфовой (фрактальной) размерностью,
которая больше топологической;
• может быть построена рекурсивными процедурами.
В 1904 году швед Хельге фон Кох придумал непрерывную кри-
вую, которая нигде не имеет касательной, причем ее довольно просто
нарисовать. Оказалось, что она обладает свойствами фрактала. Один
из вариантов этой кривой носит название «снежинка Коха» (см.
рис. 42).

Рис. 42. Построение кривой Коха

Пример 41. Построение кривой Коха заданного уровня n


из точки (x1, y1) в точку (x2, y2)
void DrawKox(int n, float x1, float y1, float x2, float y2)
{
if (n <= 0) return;
if (n == 1) DrawLine(x1, y1, 0, x2, y2, 0, 1, 1, 1);
else {
float x3, y3, x4, y4, x5, y5;
x3 = x1 + (x2 – x1) / 3;
y3 = y1 + (y2 – y1) / 3;
DrawKox(n – 1, x1, y1, x3, y3);
x4 = x1 + (x2 – x1) / 2 – (y2 – y1) / 4;
y4 = (y1 + y2) / 2 + (x2 – x1) / 4;
x5 = x1 + 2 * (x2 – x1) / 3;
y5 = y1 + 2 * (y2 – y1) / 3;
DrawKox(n – 1, x3, y3, x4, y4);

71
DrawKox(n – 1, x4, y4, x5, y5);
DrawKox(n – 1, x5, y5, x2, y2);
}
}

Рис. 43. Пирамида Коха

На рис. 43–49 приведены примеры наиболее известных геомет-


рических фракталов. В основе построения этих фракталов лежит
очень простой принцип: вначале задается очень простая фигура, затем
задается простое преобразование, и, последовательно применяя это
преобразование, можно построить фрактал любого порядка.

Рис. 44. С-кривая Леви

72
Рис. 45. H-фрактал

Рис. 45. Кривая Пеано

Рис. 47. Кривая Гильберта

73
Рис. 48. Ковер Серпинского

Рис. 49. Дерево Пифагора

В математике под фракталами понимают множества точек


в евклидовом пространстве, имеющие дробную метрическую размер-
ность. Первые примеры самоподобных множеств с необычными
свойствами появились в XIX веке в результате изучения непрерывных
недифференцируемых функций (см. рис. 50, 51).

74
Рис. 50. Множества Жюлиа

Рис. 51. Множество Мандельброта

Пример 42. Построение множества Мандельброта


void DrawMondelbrot()
{
glBegin(GL_POINTS);
int n = 200;
for (int y = –n; y<n; y++)
for (int x = –n; x<n; x++)

75
{
float re = 0, im = 0, i = 0;
bool flag = false;
while (i<100)
{
float re2 = re*re – im*im + (x) / 140.0;
float im2 = 2 * re*im + (y) / 140.0;
re = re2; im = im2;
if (sqrt(re*re + im*im) >= 2)
{
flag = true;
break;
}
i++;
}
if (!flag)
{
glColor3f(1, 1, 1);
glVertex2f(x / 20.0 – 10, y / 20.0 – 10);
}
}
glEnd();
}

Рассмотрим самый распространенный алгоритм построения


фракталов – L-системы. L-системой называют набор, состоящий из
алфавита, аксиомы и множества правил. Алфавитом называется ко-
нечное множество, а его элементы ‒ символами. Аксиома ‒ это неко-
торая строка над алфавитом. Каждое правило ‒ это пара, состоящая из
предшественника и последователя. Предшественник ‒ это символ ал-
фавита, а последователь ‒ строка над алфавитом. Вот пример пары:
A→FBFA+HFA+FB–FA.
Рассмотрим последовательное построение L-системы «Водо-
росль», ее развитие моделирует рост одного из видов водорослей. За-
дадим аксиому ‒ «A», а правила ‒ «A→B», «B→AB». Получившиеся
состояния для последовательных поколений приведены в табл. 2.
Если проинтерпретировать состояние системы следующим обра-
зом: «A» означает поворот на 60 градусов и шаг вперед; «B» ‒ пово-
рот на –60 градусов и шаг вперед, то получим поколения водорослей
на плоскости, изображенные на рис. 52.
Рассмотрим пример L-системы для построения «снежинки Коха».
Зададим аксиому «F++F++F» и правило «F→F–F++F–F». Символом «F»

76
обозначим рисование единичного отрезка. Символами «+» и «–» обозна-
чены повороты на 60 градусов против и по часовой стрелке соответст-
венно. Получим следующую иллюстрацию фрактала (рис. 53).

Таблица 2
Поколения L-системы «Водоросль»
Поколение Состояние
0 A
1 B
2 AB
3 BAB
4 ABBAB
5 BABABBAB
6 ABBABBABABBAB
7 BABABBABABBABBABABBAB
8 ABBABBABABBABBABABBABABBABBABABBAB

Рис. 52. L-система «Водоросль»

Рис. 53. Пример L-системы «снежинка Коха»

77
Рис. 54. Пример L-системы «дракон Хартера – Хейтвея»

Рис. 55. L-системы и деревья

На рис. 54, 55 приведены примеры различных L-систем.


Пример 43. Реализация L-системы
string s = "F––F––F+++F++F++F"; // аксиома
string s1 = "F"; // левое выражение правила
string r = "F–F++F–F"; // правое выражение правила
int n = 5;
for (int i = 1; i <= n; i++)
{
s = UseRule(s, s1, r); // применение правила

78
size = size / 3;
}
DrawLSystem(s, size); // вывод L-системы
string UseRule(string s, string s1, string r1, string s2 = "",
string r2 = "", string s3 = "", string r3 = "")
{ int n = s.length() – s1.length() + 1; string s0 = "";
string s00;
for (int i = 0; i<n; i++)
{ s00 = s.substr(i, s1.length());
if (s1.compare(s00) == 0) s0 = s0 + r1;
else if ((s2 != "") && (s2.compare(s00) == 0))
s0 = s0 + r2;
else if ((s3 != "") && (s3.compare(s00) == 0))
s0 = s0 + r3;
else s0 = s0 + s[i];
}
return s0;
}
void DrawLSystem(string s, float w)
{ int n = s.length();
for (int i = 0; i<n; i++) {
switch (s[i]) {
case 'F': DrawLine(0, 0, 0, w, 0, 0, 1, 1, 1);
glTranslated(w, 0, 0); break;
case '+': glRotated(60, 0, 0, 1); break;
case '–': glRotated(60, 0, 0, –1); break;
}
}
}

На рис. 56 представлен результат работы программы из примера 43.

Рис. 56. «Снежинки Коха» 1-го и 5-го порядка

79
Необходимо реализовать программу с использованием библио-
теки OpenGL, которая выводит на экран изображение, созданное из
нескольких графических примитивов (точки, отрезки, ломаные, четы-
рехугольники, треугольники, полигоны, квадраты, прямоугольники,
круги, эллипсы, окружности, дуги и т. д.). В программе должны быть
использованы операции геометрических преобразований: glTranslate,
glRotate, glScale.
Варианты работ:
1. Используя базовые примитивы, создайте один из трех рисунков:

2. Используя базовые и собственные графические примитивы,


создайте графический примитив «Цветок». Добавьте к цветку стебель
и листья.

3. Используя базовые и собственные графические примитивы,


создайте графический примитив «Колесо». Дополните рисунок, чтобы
получилась «телега» или «машина».

80
4. Используя базовые и собственные графические примитивы,
создайте графические примитивы «Солнышко с лучами» и «Облако».

5. Используя базовые и собственные графические примитивы,


создайте графический примитив «Аналоговые часы с маятником».
6. Используя базовые и собственные графические примитивы,
создайте графический примитив «Корабль».
7. Используя базовые и собственные графические примитивы,
создайте графический примитив «Домик». На крыше домика нари-
суйте трубу, из которой выходит дым.
8. Используя базовые и собственные графические примитивы,
создайте картину, состоящую из графических примитивов «Радуга»,
«Солнце», «Дождь».
9. Используя базовые и собственные графические примитивы,
создайте графический примитив «Ползающее насекомое», например
паука.
10. Используя базовые и собственные графические примитивы,
создайте графический примитив «Летающее насекомое», например
пчелу.
11. Используя базовые и собственные графические примитивы,
создайте любое узнаваемое изображение по собственному желанию
(дерево, снеговик, медведь, собака, кошка, цыпленок, смешарик и
т. д.). Детализация приветствуется. Проявите фантазию.

81
В работе необходимо анимировать примитивы (задать траекто-
рии движения, реализовать вращение).
Варианты работ:
1. Несколько точек двигаются в прямоугольной области и оттал-
киваются от стенок прямоугольника («броуновское движение»).
2. Несколько полигонов вращаются вокруг своей оси и двигают-
ся справа налево и сверху вниз под разными углами («астероидный
поток»).
3. Несколько объектов вращаются относительно центра коорди-
нат, а относительно них вращаются спутники («солнечная система»).
4. Несколько объектов вращаются относительно своей оси и
двигаются по синусоиде слева направо («синусоида»).
5. Придумайте свои траектории движения, алгоритм изменения
размеров и углов поворота.

Необходимо реализовать программу с использованием библио-


теки OpenGL для вывода графиков функции. Программа должна вы-
полнить следующие действия:
• вывести график заданной функции на заданном отрезке;
• вывести график производной заданной функции;
• отметить максимум и минимум функции на заданном отрезке;
• раскрасить график функции: области, на которых график убы-
вает, – одним цветом, области, на которых график возраста-
ет, – другим цветом;
• выделить цветом локальные минимумы и максимумы.
Варианты работ:
1. y = sin(x)
2. y = cos(x)*sin(2*x)
3. y = cos(x)*x*x
4. y = log(x)+sin(x)
5. y = exp(x)+sin(x)
6. y = sqrt(x)*tan(x)*sin(2*x)
7. y = myFunction(x), любая произвольная функция.

82
Необходимо реализовать наложение текстур на примитивы и
сделать освещение сцены.
Варианты работ:
1. Несколько точек двигаются в прямоугольной области и оттал-
киваются от стенок прямоугольника («броуновское движение»).
2. Несколько полигонов вращаются вокруг своей оси и двигают-
ся справа налево и сверху вниз под разными углами («астероидный
поток»).
3. Несколько объектов вращаются относительно центра коорди-
нат, а относительно них вращаются спутники («солнечная система»).
4. Несколько объектов вращаются относительно своей оси и
двигаются по синусоиде слева направо («синусоида»).
5. Придумайте свои траектории движения, алгоритм изменения
размеров и углов поворота.

Необходимо реализовать программу с использованием библио-


теки OpenGL, которая отображает трехмерную сцену из множества
трехмерных текстурированных объектов. Объекты можно создать са-
мому, можно использовать объекты, описанные в модулях glaux
(auxSolidSphere, auxSolidBox, auxSolidCylinder, auxSolidIcosahedron,
auxSolidTeapot и др.) или glu (gluSphere, gluDisk, gluCylinder).
Варианты работ:
1. Улица из домов разных цветов, размеров и типов.
2. Лес с деревьями разных цветов, размеров и типов.
3. Внутренний вид комнаты с предметами мебели.
4. Внутренний зал музея с экспонатами разных размеров и форм.
5. Придумайте сами сцену и объекты, расположенные на ней.

Необходимо реализовать программу с использованием библио-


теки OpenGL, которая рисует фрактал. Для рисования фрактала мож-
но использовать алгоритм рекурсии или алгоритм L-систем.

83
Варианты работ:
1. Нарисовать фрактал «Кривая Пеано».
2. Нарисовать фрактал «Кривая Гильберта».
3. Нарисовать фрактал «Ковер Серпинского».
4. Нарисовать фрактал «Дерево Пифагора».
5. Нарисовать фрактал «Пирамида Коха».
6. Придумайте свой фрактал или скомбинируйте уже известные
фракталы.

84
1. Большаков В. П., Тозик В. Т., Чагина А. В. Инженерная и
компьютерная графика: учеб. пособие. − СПб.: БХВ-Петербург,
2013. − 288 c.
2. Френсис Х. OpenGL. Программирование компьютерной гра-
фики. − СПб.: Питер, 2002.
3. Гинсбург Д., Пурномо Б. OpenGL ES 3.0. Руководство разра-
ботчика. − М.: ДМК Пресс, 2015. − 448 c.
4. Боресков А.В. Расширения OpenGL. − М.: БХВ-Петербург,
2005. − 688 c.
5. Электронный ресурс. – URL: http://www.glprogramming.com/red/
6. Электронный ресурс. – URL: https://e.sfu-kras.ru/course/view.php?
id=9144

85
Научное издание

Баранов Сергей Николаевич


Толкач Светлана Геннадьевна

Основы компьютерной графики

Учебное пособие

Редактор Л. А. Киселева
Компьютерная верстка И. В. Гревцовой

86
Подписано в печать 24.10.2018. Печать плоская
Формат 60×84/16. Бумага офсетная. Усл. печ. л. 5,5
Тираж 100 экз. Заказ № 5776

Библиотечно-издательский комплекс
Сибирского федерального университета
660041, Красноярск, пр. Свободный, 82а
Тел. (391) 206-26-67; http://bik.sfu-kras.ru
E-mail: publishing_house@sfu-kras.ru

87
88