Геометрические примитивы OpenGL

advertisement
Федеральное государственное
Бюджетное образовательное
Учреждение высшего профессионального
Образования
«Казанский национальный технический университет им. А.Н.Туполева – КАИ»
(КНИТУ_КАИ)
Компьютерная графика
Методические указания для работы студентов 2 курса
Программирование изображений в API
OPENGL
Пикулева Н.И.
Казань 2013
1
Программирование в OPENGL
Знакомство с OPENGL
В OpenGL геометрический объект создается в мировой системы
координат (МСК), в которой программист привык работать при создании
геометрического объекта. В МСК координатные оси могут носить любой
физический смысл, а единица по координатной оси может принимать любое
значение – мм, см, м, км, мсек, сек, час, годы и т.д. В МСК программист
выделяет прямоугольное окно – мировое окно, содержимое которого и
будет отображаться в порт просмотра (viewport) на экране
видеомонитора (ВМ). При этом мировое окно в МСК (прямоугольная
область (r–l)(t–b)) отображается с сохранением подобия в порт просмотра
(viewport) размерами wh в экранном окне (window). В отличие от мирового
окна размеры порта просмотра w и h всегда задаются как целое число
пикселей. На рис. 1 показана схема отображения мирового окна в порт
просмотра.
МСК
Мировое
окно
t
r
l
b
Порт просмотра
(viewport)
a
Экран ВМ (screen)
b
Экранное окно window
h
e
i
g
h
t
h
x
y
w
width
Рис. 1 Схема отображения мирового окна в порт просмотра (viewport):
l – левая граница мирового окна в МСК; r – правая граница мирового окна в МСК; t – верхняя
граница мирового окна в МСК; b – нижняя граница мирового окна в МСК; w, h – размеры порта
просмотра viewport; width, height – размеры экранного окна window; x, y – положение viewport
в системе координат window; a, b – положение window в системе координат screen
Следующие функции реализуют схему, показанную на рис. 1:
2
void gluOrtho2D(GLdouble l, GLdouble r, GLdouble b, GLdouble t) –
установка мирового окна;
void glutInitWindowSize(int width, int height) – установка размеров
window;
void glutInitWindowPosition(int a, int b) – установка положения window;
void glViewport(GLint x, GLint y, GLsizei w, GLsizei h) – установка
viewport.
В окне просмотра мы видим то, что изображено в мировом окне, или не
видим, если неправильно указали форматное соотношение между размерами
мирового окна и портом просмотра.
Лабораторная работа №1
Первая программа
1.
Создайте папку Projects на диске, указанном преподавателем. В
этой папке вы будете создавать свои проекты.
2.
Запустите Microsoft Visual Studio 2005, пользуясь кнопками Пуск
– Программы.
Варианты создания проекта
Первый вариант (из окна в центре).
1. Закройте окно Start Page.
2. Последовательно выполните команды:
FILE – NEW – PROJECT. В открывшемся окне слева синим цветом должно
быть выделено Win32, ниже в поле NAME введите имя создаваемого проекта
латинскими буквами без пробелов, проект должен создаваться в папке
PROJECT.
3. Откроется окно мастера, выполните команды NEXT– FINISH, ничего
не меняя в настройках окна.
4. Перед вами откроется окно ввода программы, удалите имеющиеся в
окне команды, за исключением комментария.
Второй вариант
3
1.
Выполните команды Create – Project – Win32 (в окне слева) –
Win32 Console Application (в окне справа). Задайте имя проекта в поле Name
(в созданной вами папке Project).
2.
Откроется окно Мастера, пройдите по диалогу NEXT – Finish.
Открытие созданной ранее программы
В среде Microsoft Visual Studio открываются файлы с расширением .sln
командой FILE – OPEN – Project/Solution – имя проекта – файл с
расширением .sln.
Если при этом исходный текст программы не будет выведен на экран
следует выполнить команды:
View – Solution Explorer – имя-файла.cpp
Вывод окна программы
Наберите приведенный ниже текст программы, которая создает и
закрашивает определенным цветом окно программы, необходимая для
понимания программы теория приведена в начале и ниже приводятся
пояснения некоторых команд (эти команды необходимо выучить).
glutInit(&argc, argv) – инициализация библиотеки GLUT;
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB) – устанавливается
режим дисплея: использовать один буфер для кадра; цвета представлять как
смесь RGB;
glutInitWindowSize(640, 480) – устанавливается размер экранного окна
window;
glutInitWindowPosition(50, 50) – устанавливается положение (позиция)
экранного окна window.
glutCreateWindow("Заголовое окна") – инициализируется открытие
экранного окна window;
glutDisplayFunc(draw) – функция draw( ) регистрируется как функция
обратного вызова для события открытия или обновления экранного окна;
4
glutMainLoop( ) – переводит программу в бесконечный цикл ожидания
события;
glClearColor(1.0, 1.0, 0.0, 0.0) – назначает то значение буфера цвета
экранного окна, которое будет установлено при его сбросе (очистке)
функцией glClear(GL_COLOR_BUFFER_BIT). По существу это назначение
цвета фона;
glMatrixMode(GL_PROJECTION) – в качестве текущей устанавливается
матрица проецирования;
glLoadIdentity( ) – текущая матрица устанавливается в единицу;
gluOrtho2D(0.0, 2.0, 0.0, 1.0) – устанавливается мировое окно;
glColor3f(1.0, 0.0, 0.0) – устанавливается цвет рисования;
glViewport(10, 10, 600, 400) – устанавливается положение и размеры
порта просмотра;
glFlush() – принудительное выполнение накопленных команд OpenGL.
Различные реализации OpenGL буферизируют команды в нескольких
различных местах. Команда glFlush() освобождает все эти буферы, заставляя
все вызванные команды выполниться.
Выполните компиляцию программы командой Build – Build Solution.
Если
компиляция
прошла
успешно,
запустите
программу
на
выполнение с помощью команды Debug.
#include "stdafx.h"
1. #include <gl/glut.h>
2. void init(void);
3. void draw(void);
4. void main(int argc, char **argv)
5. {
6.
glutInit(&argc, argv);
7.
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
8.
glutInitWindowSize(640, 480);
9.
glutInitWindowPosition(50, 50);
10.
glutCreateWindow("Программа вывода окна");
11.
init();
12.
glutDisplayFunc(draw);
13.
glutMainLoop() ;
14.
}
5
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
void init(void)
{
glClearColor(1.0, 1.0, 0.0, 0.0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, 2.0, 0.0, 1.0);
}
void draw(void)
{
glClear(GL_COLOR_BUFFER_BIT);
glViewport(10, 10, 600, 400);
glFlush();
}
(okno.sln – файл с таким расширением открывается в VC 2005)
Задания
1.
Выведите поочередно окно красного, зеленого, синего, розового
(1,0,1), коричневого (0.7, 0.4, 0), оранжевого (1, 0.5, 0), серого (0.7, 0.7, 0.7)
цветов. Подберите и приведите в отчете коды еще некоторых цветов для
закраски окна.
2.
Измените размеры окна.
3.
Нарисуйте в центре окна точку красного цвета, затем еще
несколько точек разных цветов. (Видимые элементы должны укладываться в
границы мировых координат).
Команда для рисования точки:
glVertex2f(2, 3); – точка с координатами x = 2, y = 3, z = 0, w = 1
Размер точки задается командой glPointSize();
4.
Составьте отчет о выполнении лабораторной работы, приведите в
нем выполненные программы и объясните значение каждой команды.
Вопросы
5.
Какая команда изменяет размеры окна Window?
6.
Какая команда устанавливает размеры окна просмотра?
7.
В какой системе координат создается изображение?
8.
Какая команда устанавливает окно мировой системы координат?
9.
Где (в каком окне) на экране можно увидеть созданное изображение?
10.
Почему созданное изображение может не показываться на экране?
6
11.
Для чего используется команда glFlush();
Лабораторная работа №2
Создание 2D примитивов
Геометрические примитивы OpenGL
Точки в OpenGL используются для задания отрезка, ломаной линии или
многоугольника. Поэтому точки часто будем называть вершинами.
Пример задания вершины glVertex2i(10, 20). Это один пример из
большого количества модификаций задания вершины. Здесь вершина
задается двумя параметрами (координатами) целого типа. В общем случае
вершина задается функцией:
void glVertex{234}{sifd}[v](TYPE cords)
количество координат
от 2 до 4. Если явным
образом
не
определены z и w, то
по умолчанию z = 0,
w= 1.
тип аргумента (табл.1)
говорит о том, что в
качестве
аргументов
(параметров
функции)
используется указатель
на массив из 2, 3 или 4
элементов.
Z – координата по оси z, величина w используется в однородных координатах.
Таблица 1
Типы и спецификаторы для их задания в командах OpenGL
Суффикс, задающий
тип аргумента
b
s
i
f
d
ub
us
ui
Описание типа
8 – бит, целое
16 – бит, целое
32 – бит, целое
32 – бит, вещественное
64 – бит, вещественное
8 – бит, целое, без знака
16 – бит, целое, без знака
32 – бит, целое, без знака
Имя типа в OpenGL
GLbyte
GLshort
GLint, GLsizei
GLfloat, GLclampf
GLdouble, GLclampd
GLubyte, GLboolean
GLushort
GLuint, GLenum, GLbitfield
Примеры задания точек:
7
glVertex2s(2, 3) – точка с координатами x = 2, y = 3, z = 0, w = 1;
glVertex3d(0.0, 0.0, 3.14) – точка с координатами x = 0.0, y = 0.0, z = 3.14,
w = 1.0;
glVertex4f(2.3, 1.0, –2.2, 2.0) – точка с координатами x = 2.3, y = 1.0,
z = –2.2, w = 2.0;
GLdouble dvect[3] = {5.0, 9.0, 1992.0};
glVertex3dv(dvect) – точка с координатами x = 5.0, y = 9.0, z = 1992.0, w =
1.0.
Последовательность вершин, имеющих самостоятельный смысл,
назовем примитивом. Примитивы или группы подобных примитивов
задаются между функциями glBegin и glEnd. Пример для 2D (в этом примере
* означает, что в это место должен быть подставлен какой-либо из
спецификаторов типа):
glBegin(mode);
glVertex2*(x1, y1);
glVertex2*(x2, y2);
................
glVertex2*(xN, yN);
glEnd();
Прототипы функций glBegin и glEnd:
void glBegin(GLenum mode);
void glEnd(void).
Параметр mode может принимать одно из значений, приведенных в
табл. 2. В ней же описано, как при этом обрабатываются последовательность
вершин, определенных между glBegin и glEnd.
Таблица 2
Значения и функциональный смысл параметра mode
Значение
GL_POINTS
Смысл
3
1
Обрабатывает каждую вершину как отдельную точку.
Вершина n определяет точку n.
2
4
GL_LINES
Обрабатывает каждую пару вершин как независимый
линейный сегмент. Вершины 2n-1 и 2n определяют прямую n.
Рисуется N/2 прямых.
8
3
2
1
4
Продолжение табл. 2
Значение
GL_LINE_STRIP
Смысл
3
2
1
4
Рисует связанную группу линейных сегментов от первой
вершины до последней. Вершины n и n+1 определяют
прямую n. Рисуется N–1 прямых.
GL_LINE_LOOP
3
2
1
4
GL_TRIANGLES
1
4
6
3
2
Рисует связанную группу линейных сегментов от первой
вершины до последней, а затем назад к первой. Вершины n и
n+1 определяют прямую n. Последняя прямая определена
вершинами 1 и N. Рисуется N прямых.
Обрабатывает тройку вершин как независимый треугольник.
Вершины 3n–2, 3n–1и 3n определяют треугольник n. Рисуется
N/3 треугольников.
5
GL_TRIANGLE_STRIP
3
1
5
6
4
2
Рисует связанную группу треугольников. Для нечетного
значения n вершины n, n+1 и n+2 определяют треугольник n.
Для четного значения n вершины n+1, n и n+2 определяют
треугольник n. Рисуется N–2 треугольников.
GL_TRIANGLE_FAN
5
Рисует связанную группу треугольников. Вершины 1, n+1 и
n+2 определяют треугольник n. Рисуется N–2 треугольников.
4
1
3
2
4
1
GL_QUADS
3 8
2 5
7
6
Обрабатывает каждую группу из четырех вершин в качестве
независимого четырехугольника. Вершины 4n–3, 4n–2, 4n–1 и
4n определяют четырехугольник n. Рисуется N/4
четырехугольников.
9
GL_QUAD_STRIP
2
4
8
6
1
3
5
Рисует связанную группу четырехугольников. Вершины 2n–1,
2n, 2n+2 и 2n+1 определяют четырехугольник n. Рисуется
N/2–1 четырехугольников.
7
Окончание табл. 2
Значение
GL_POLYGON
8
7
5
6
1
2
3
Смысл
Рисует отдельный выпуклый многоугольник. Вершины от 1
до N определяют этот многоугольник.
4
1. Размер точки задается командой glPointSize();
2. Изменение атрибутов линии.
Для линий можно
изменять атрибуты: ширину, цвет, размер,
сглаживание. Если задать разные цвета для начала и конца линии, то ее цвет
будет
переливающемся
(градиент).
OpenGL
по
умолчанию
делает
интерполяцию. Так же можно рисовать прерывистые линии (менять форму
линии), делается это путем наложения маски при помощи следующей
функции:
void glLineStipple(GLint factor, GLushort pattern);
Второй параметр задает саму маску. Например, если его значение
равно 255(0x00FF), то в двоичном виде это число выглядит так:
0000000011111111, т.е. всего 16 бит. Старшие восемь установлены в ноль,
значит тут линии не будет. Младшие установлены в единицу, тут будет
рисоваться линия. Первый параметр определяет, сколько раз повторяется
каждый бит. Скажем, если его установить равным 2, то накладываемая маска
будет выглядеть так:
00000000000000001111111111111111
10
3. Многие переменные относятся к возможностям OpenGL, которые
можно включать или выключать командами glEnable() или glDisable().
Чтобы изменить атрибуты линии (смену цвета от точки к точке и форму
линии), необходимо включить соответствующие возможности.
glEnable(GL_LINE_SMOOTH); //разрешить сглаживание при заливке.
glEnable(GL_LINE_STIPPLE); // разрешаем рисовать прерывистую линию
glLineStipple(2,58360);
// устанавливаем маску
Шаблонированные отрезки
4. Толщину
линий
задают
командой
glLineWidth();
по умолчанию толщина равна 1 пикселю. Эту и приведенные в данном
разделе команды необходимо написать до операторных скобок glBegin();
5. Команды выключения glDisable() необходимо указывать после
операторных скобок.
6. Полигоны, нарисованные командой GL_LINE_LOOP нельзя закрасить.
11
7. С помощью нужных параметров функции glPolygonMode() можно
рисовать закрашенный или проволочный примитив.
Эта функция устанавливает опции для отрисовки многоугольника.
Первый параметр в этой функции может принимать значения
GL_FRONT, GL_BACK
или
GL_FRONT_AND_BACK.
Второй параметр указывает, как будет рисоваться многоугольник. Он
принимает значения
GL_POINT (рисуются только точки),
GL_LINE
(рисуем линии)
GL_FILL (рисуем заполненный многоугольник).
Первый параметр указывает: к лицевой, тыльной или же к обеим
сторонам применяется опция, заданная вторым параметром.
Треугольники можно рисовать, передав GL_TRIANGLE_STRIP или
GL_TRIANGLE_FAN в glBegin. В первом случае первая, вторая и третья
вершины задают первый треугольник. Вторая, третья и четвертая вершина второй треугольник. Третья, четвертая и пятая вершина - третий треугольник
и т.д. Вершины n, n+1 и n+2 определят n-ый треугольник.
Во втором случае первая, вторая и третья вершина задают первый
треугольник. Первая, третья и четвертая вершины задают второй треугольник
и т.д. Вершины 1, n+1, n+2 определяют n-ый треугольник.
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); //рисуем
// залитые фигуры
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); //рисуем
// проволочные фигуры
Задания
1.
Нарисуйте контур прямоугольника по размеру окна вывода, состоящий
2.
из четырех отдельных линий. Линии должны быть разной толщины,
цвета, (в том числе и градиентные) и разной формы.
12
3. Нарисуйте внутри образовавшегося прямоугольника все примитивы из
таблицы 1.
В верхней части окна разместите контурные примитивы, в
нижней – площадные.
4. Составьте отчет о выполнении лабораторной работы, приведите в нем
выполненные программы фрагментарно и объясните значение каждой новой,
изученной на этой лабораторной работе команде.
Основой
для
наполнения
содержимым
является
приведенная
ниже
программа.
#include "stdafx.h"
#include <GL/glut.h>
void init();
void draw();
void main(int argc, char **argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(?, ?);
glutInitWindowPosition(?, ?);
glutCreateWindow("Основа для наполнения");
init();
glutDisplayFunc(draw);
glutMainLoop();
}
void init()
{
}
void draw()
{
}
Вопросы
1.
Какие типы данных можно использовать в OPENGL, программируя на
языке C++?
2.
Как указать массив значений для координат точки?
3.
Как задать точки с координатами в виде массива?
4.
Какой командой изменить толщину линии?
5.
Для чего используются команды glEnable() или glDisable()?
6.
Внутри каких функций задаются примитивы или группы примитивов?
7.
Какими командами изменяют форму линий (шаблонируют отрезки)?
13
8.
Какая команда используется для рисования контурных и площадных
примитивов?
Лабораторная работа №3
Соотношение мирового окна и порта просмотра
Автоматическое сохранение форматных соотношений мирового окна
в порту просмотра
Форматное соотношение – это результат деления длины мирового окна
на его высоту.
Для того, чтобы изображение, созданное в мировом окне, отображалось
в порту просмотра без искажений, необходимы определенные форматные
отношения между ними, (а именно соотношение R=W/H). Мировое окно и
порт просмотра не обязаны иметь одно и то же форматное соотношение, но
наличие разных форматных соотношений приводит к искажению. Если мы
хотим нарисовать фигуру максимального размера, которая без искажений
поместится в экранном окне, то необходимо задать порт просмотра с тем же
форматным соотношением, которое имеет мировое окно. Допустим
форматное соотношение мирового окна известно и равно R, а экранное окно
имеет ширину W и высоту H. Необходимо рассмотреть два случая, когда
R>W/H и R<=W/H.
1.
Если R>W/H, то мировое окно короче и толще чем экранное
окно. Порт просмотра с таким же форматным соотношением R целиком
поместится по высоте экранного окна, но сверху или снизу останется
неиспользованное пространство, поэтому наибольший порт просмотра будет
иметь ширину W, а высоту W/R и его можно задать (0,W,0, W/R), но порт
просмотра должен иметь действительно форматное соотношение R.
2.
Если R<=W/H, то мировое окно выше и уже, чем экранное окно,
это значит, что порт просмотра с таким же форматным соотношением R
14
расположится сверху донизу экранного окна, но справа или слева останется
неиспользованное пространство. Поэтому наибольший порт просмотра будет
иметь высоту H, а длину H*R. его можно задать величинами (0,H*R,0,H).
Внимание порядок параметров в скобках разный
Пример Высокое окно
Пусть форматное соотношение для мирового окна составляет R=1,6.
Экранное окно имеет высоту H=200 и ширину W=360. W/H=1,8, значит
имеем случай 2. Порту просмотра назначается высота 200 пикселей, а
ширина 320 пикселей.
Пример Короткое окно
Пусть форматное соотношение для мирового окна составляет R=2, а
экранное окно такое же, как в предыдущем примере. Имеем случай 1 и порту
просмотра назначается высота 180, а ширина 320.
float R = 5; // – форматное соотношение для мирового окна
void myInit()
{…………………
}
void myReshape(int W, int H)
{if (R>W/H) glViewport(0, 0, W, W/R);
else glViewport(0, 0, H*R, H);
}
void myDisplay()
{…………………
gluOrtho2D(–5.0, 5.0, –0.5, 1.5); // – здесь устанавливаем параметры
мирового окна с форматным соотношением R=5, т.е. ширина от -5 до 5 равна
10, а высота мирового окна от -0.5 до 1.5 равна 2. Значит 10/2=5.
}
void main(int argc, char* argv[])
{…………………
glutDisplayFunc(myDisplay);
15
glutReshapeFunc(myReshape);
…………………
}
Window
Viewport
480
128
640
а
Window
Viewport
480
96
R
480
W
H
б
Window
100
R
W
H
Viewport
500
в
Рис. 2. Window при:
а – W=640, H=480;
б – W=480, H=480; в – W=640, H=100
Рис. 2 показывает, что при всех изменениях размера экранного окна
window размеры порта viewport сохраняют форматное соотношение R = 5
такое же, как это установлено для мирового окна. Пропорции фигуры,
рисуемой в мировом окне при отображении в viewport, при этом всегда
16
сохраняются. Фигура без искажений должна отображаться во всех портах
просмотра.
Пример программы, суть в том, чтобы ни в каком порту просмотра
пропорции фигуры не менялись.
#include "stdafx.h"
#include <gl/glut.h>
float R = 5, W=480, H=480; // – форматное соотношение для мирового окна,
меняем, 640 на 100, 640 на 480, фигура выглядит одинаково
{
void myInit(void)
glClearColor(?, ?, ?, ?);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// gluOrtho2D(l, r, b, t);
gluOrtho2D(?, ?, ?, ?);
}
void myReshape(int W, int H)
{
if (R>W/H) glViewport(0, 0, W, W/R);
else glViewport(0, 0, H*R, H);
}
void myDisplay()
{
glClear(GL_COLOR_BUFFER_BIT);
glColor3f((?, ?, ?);
glPointSize(?);
glBegin(GL_LINE_LOOP);
GLfloat i;
GLfloat x,y;
for(любой цикл для нахождения меняющихся координат точек фигуры)
{
x=?;
y=?;
glVertex2f(x, y);
}
glEnd( );
glFlush();
}
void main(int argc, char* argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(?, ?);
glutInitWindowPosition(?, ?);
glutCreateWindow("Программа форматного соотношения экранного окна и
порта вывода");
myInit();
glutReshapeFunc(myReshape);
glutDisplayFunc(myDisplay);
glutMainLoop();
}
Задания
17
1.
Напишите программу создания нескольких портов просмотра
Размер окна window 600х400.
Размер портов просмотра 60х60.
Фигура из мирового окна отображается в цикле в каждый из портов
просмотра. В результате на экране мозаика. Напишите программу, за основу
примите приведенный ниже фрагмент.
Фрагмент возможной программы:
void draw()
{
GLfloat l = ?, r = ?, b = ?, t = ?;
GLint x = 0, y = 0, w = ?, h = ?;
glClear(GL_COLOR_BUFFER_BIT);
gluOrtho2D(l, r, b, t);
for(x = 0; x <= ?; x += w)
for(y = 0; y <= ?; y += h)
{
glViewport(x, y, w, h);
glColor3f(?,?,?);
glBegin(GL_LINES);
<Рисуется любая сложная составная фигура>
glEnd();
}
glFlush();
}
2.
Создайте программу
рисования трех любых закрашенных фигур.
Последовательно применяйте форматные соотношения между мировым
портом и окном просмотра R=2; R=3; R=1,6. Каким при этом должен быть
результат?
3.
Составьте отчет о проделанной работе, приведите в нем тексты
программ и объясните каждый оператор.
18
ВНИМАНИЕ! В дальнейших работах ВСЕГДА учитывайте форматные
соотношения!
Вопросы
1.
Что такое форматное отношение?
2.
Какое условие должно быть соблюдено, чтобы нарисовать фигуру
максимального размера, которая без искажений поместится в экранном окне?
3.
Как вычисляются размеры наибольшего порта просмотра при условии
R>W/H?
4.
Как вычисляются размеры наибольшего порта просмотра при условии
R<=W/H?
5.
Как, нарисовав только одно изображение, получить мозаику в порту
просмотра?
Лабораторная работа №4
Графики функций
С помощью OPENGL можно рисовать графики функций. Ниже приведены
формулы для получения графиков по вариантам и пример программы.
#include <vector> // эти три оператора необходимы для вычислений
#include <complex>
#include <iostream>
1) y(x) = sin(x) /x, где  – const;
2) y(t) = sin(1t)cos(2t), где 1, 2 – const;
3) y(t) = e-atSin(t), где e, a,  – const;
4) x(t) = sin(t); y(t) = cos(t), где  – const;
5) x(t) = Asin(t); y(t) = Bcos(t), где A, B,  – const;
6) y(x) = Ax3 + Bx2 +Cx +D, где A, B, C, D – const.
Значения констант и диапазон изменения переменных подберите так,
чтобы на экране хорошо просматривались особенности графика функции.
Пример: синусоида
#include "stdafx.h"
19
#include
#include
#include
#include
<GL/glut.h>
<vector> // эти три команды необходимы для вычисления синуса
<complex>
<iostream>
//#include <math>
void init(void);
void draw(void);
void main(int argc, char **argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(?, ?);
glutInitWindowPosition(?, ?);
glutCreateWindow("График функции");
init();
glutDisplayFunc(draw);
glutMainLoop();
}
void init(void)
{
glClearColor(?,?,?,?);
glMatrixMode(GL_PROJECTION);
glLoadIdentity( );
gluOrtho2D(?,?,?,?);
}
void draw(void)
{
glClear(GL_COLOR_BUFFER_BIT);
glViewport(?,?,?,?);
glColor3f(?,?,?);
glBegin(GL_LINE_STRIP);
GLfloat i;
GLfloat x,y;
for(i=0;i<=100;i=i+0.005)
{
x=?;
y=(sin(?))/13*x;
glVertex2f(x, y);
}
glEnd( );
glFlush();
}
Задание
1.
Получите вариант задания, создайте программу, рисующую кривую.
2.
Составьте отчет о проделанной работе, приведите в нем тексты
программ и объясните каждый оператор.
Вопросы
1.
Какой вклад в график внесли подобранные вами значения констант и
диапазон изменения переменных?
20
2.
Как изменить толщину кривой вашего графика?
3.
Как создать кривую с градиентным цветом?
Лабораторная работа № 5
Окружности, закраска и шаблонирование
Теория
1. Рисование окружностей
В OPENGL окружность получается из отрезков прямых, т.е. нужно
нарисовать многоугольник. Если число сторон многоугольника велико, то он
стремится к окружности, например, если n=40.
Вершины n-угольников
лежат на порождающей окружности (parent circle). Нарисуем 6-угольник,
его вершины нарисованы на одинаковом расстоянии через каждые 60о по
окружности. Центр порождающей окружности радиуса R находится в начале
координат, а первая вершина P0 лежит на положительной части оси X.
Далее для 6-угольника, который в грубом приближении может
рассматриваться как окружность, вершины располагаются следующим
образом:
Pi=(R*cos(i*a),R*sin(i*a) где i=1…6, a=π/3 радиан
Вершины n-угольника располагаются следующим образом:
Pi=(R*cos(2 πi/n),R*sin(2 πi/n) где i=0…n-1
Пример
#include "stdafx.h"
#include <GL/glut.h>
#include <vector>
#include <complex>
#include <iostream>
void init();
void draw();
21
void main(int argc, char **argv) //Главная функция
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(?,?);
glutInitWindowPosition(?,?);
glutCreateWindow("ОВАЛ - ДВА СПОСОБА ");
init( );
glutDisplayFunc(draw);
glutMainLoop( );
}
void init() //Функция инициализации
{
glClearColor(?,?,?,?);
glMatrixMode(GL_PROJECTION);
glLoadIdentity( );
gluOrtho2D(?,?,?,?);
}
void draw() //Функция рисования
{
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(?,?,?);
glViewport(?,?,?,?);
glColor3f(?,?,?);
glBegin(GL_POLYGON);
// КООРДИНАТЫ ВЫБИРАЕМ В ПРЕДЕЛАХ МИРОВОГО ОКНА
GLint i,n;
GLfloat x,y;
n=40;
for(i=1;i<=n;i++)
{
x=cos(2*i*3.14/n);
y=sin(2*i*3.14/n);
glVertex2f(0.15*x+1.0,0.15*y+0.5); //что будет, если не
делать сдвиг по координатам от начала координат?
}
glEnd();
glFlush( );
}
22
Цвет устанавливается четырьмя параметрами: красный, синий, зеленый
и прозрачность. Эти параметры варьируют в диапазоне от нуля до единицы.
Четвертый параметр нужен не всегда, поэтому пока будет использоваться
glColor с тремя параметрами. В этом случае, значение четвертого параметра,
прозрачности, по умолчанию считается равным единице, т.е. абсолютно
непрозрачным, ноль - будет абсолютно прозрачным.
void glShadeModel (GLenum mode);
Этим оператором устанавливается модель заливки. Параметр mode
может принимать значения GL_SMOOTH (плавная заливка – режим по
умолчанию) или GL_FLAT (плоская заливка). При использовании плоской
заливки
цвет
одной
отдельной
вершины
независимого
примитива
дублируется для всех остальных вершин при визуализации этого примитива.
При
плавной
заливке
цвет
каждой
вершины
считается
индивидуальным. Для линии цвет на протяжении отрезка интерполируется
на основании цветов на его концах. Для полигона цвета его внутренней
области интерполируются между цветами его вершин. В примере рисуется
плавно залитый треугольник, показанный на рисунке 3.
Рисунок 3. Плавно залитый треугольник
23
При использовании плавной заливки соседние пиксели имеют немного
различающиеся цвета. В RGBA режиме соседние пиксели с немного
различающимся цветом выглядят одинаково, таким образом, цвет плавно
меняется по всей плоскости полигона.
Шаблонирование полигонов
По умолчанию, если полигон рисуется в виде закрашенной области, он
закрашивается одинаково по всей своей площади, без просветов. Эту
ситуацию можно изменить, определив шаблон заливки полигона в виде
матрицы 32 x 32 бита и установив его командой
glPolygonStipple().
void glPolygonStipple (const GLubyte *mask);
Определяет текущий рисунок шаблона заливки полигона. Параметр
mask – это указатель на битовую карту размером 32 x 32 бита,
интерпретируемую в качестве маски, накладываемой на полигон при
рисовании (и при необходимости повторяемой). Там, где стоит 1 –
соответствующий пиксель полигона будет нарисован, а там, где появляется 0
– пиксель нарисован не будет. Рисунок 4 показывает, как рисунок шаблона
конструируется из символов в параметре mask. Шаблонирование полигонов
включается и выключается с помощью аргумента GL_POLYGON_STIPPLE
функций
glEnable()
и glDisable(). Интерпретация mask
зависит от
GL_UNPACK* параметров команды glPixelStore*().
Кроме того, вы должны включить шаблонирование полигонов:
glEnable(GL_POLYGON_STIPPLE);
Используйте glDisable() с аналогичным аргументом для выключения
шаблонирования.
На рисунке 4 показаны результаты рисования трех полигонов: одного
нешаблонированного и двух шаблонированных, каждый со своим рисунком.
Инверсия готового полигона (рисунок 4) относительно шаблона
происходит потому, что программа рисует полигоны белым цветом на
24
черном фоне (шаблон не влияет на цвет рисуемых точек, он просто
определяет, какие точки полигона будут нарисованы, а какие нет).
Рисунок 4. Шаблонированные полигоны
Пример
#include "stdafx.h"
#include <GL/glut.h>
void init(void)
{
glClearColor(0.0,0.0,0.0,0.0);
glShadeModel(GL_FLAT);
}
void display(void)
{
GLubyte fly[]= {
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
// первая маска
0x03,0x80,0x01,0xC0,0x06,0xC0,0x03,0x60,
0x04,0x60,0x06,0x20,0x04,0x30,0x0C,0x20,
0x04,0x18,0x18,0x20,0x04,0x0C,0x30,0x20,
0x04,0x06,0x60,0x20,0x44,0x03,0xC0,0x22,
0x44,0x01,0x80,0x22,0x44,0x01,0x80,0x22,
0x44,0x01,0x80,0x22,0x44,0x01,0x80,0x22,
0x44,0x01,0x80,0x22,0x44,0x01,0x80,0x22,
0x66,0x01,0x80,0x66,0x33,0x01,0x80,0xCC,
0x19,0x81,0x81,0x98,0x0C,0xC1,0x83,0x30,
0x07,0xE1,0x87,0xE0,0x03,0x3F,0xFC,0xC0,
0x03,0x31,0x8C,0xC0,0x03,0x33,0xCC,0xC0,
0x06,0x64,0x26,0x60,0x0C,0xCC,0x33,0x30,
0x18,0xCC,0x33,0x18,0x10,0xC4,0x23,0x08,
0x10,0x63,0xC6,0x08,0x10,0x30,0x0C,0x08,
0x10,0x18,0x18,0x08,0x10,0x00,0x00,0x08
};
GLubyte halftone[] = {
0xAA,0xAA,0xAA,0xAA,0x55,0x55,0x55,0x55, // вторая маска
0xAA,0xAA,0xAA,0xAA,0x55,0x55,0x55,0x55,
0xAA,0xAA,0xAA,0xAA,0x55,0x55,0x55,0x55,
0xAA,0xAA,0xAA,0xAA,0x55,0x55,0x55,0x55,
0xAA,0xAA,0xAA,0xAA,0x55,0x55,0x55,0x55,
0xAA,0xAA,0xAA,0xAA,0x55,0x55,0x55,0x55,
0xAA,0xAA,0xAA,0xAA,0x55,0x55,0x55,0x55,
25
0xAA,0xAA,0xAA,0xAA,0x55,0x55,0x55,0x55,
0xAA,0xAA,0xAA,0xAA,0x55,0x55,0x55,0x55,
0xAA,0xAA,0xAA,0xAA,0x55,0x55,0x55,0x55,
0xAA,0xAA,0xAA,0xAA,0x55,0x55,0x55,0x55,
0xAA,0xAA,0xAA,0xAA,0x55,0x55,0x55,0x55,
0xAA,0xAA,0xAA,0xAA,0x55,0x55,0x55,0x55,
0xAA,0xAA,0xAA,0xAA,0x55,0x55,0x55,0x55,
0xAA,0xAA,0xAA,0xAA,0x55,0x55,0x55,0x55,
0xAA,0xAA,0xAA,0xAA,0x55,0x55,0x55,0x55
};
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1.0,1.0,1.0);
//Рисуем один нешаблонированный прямоугольник
//и два шаблоннированных
glRectf(25.0,25.0,125.0,125.0); // рисует четырехугольник по координатам
glEnable(GL_POLYGON_STIPPLE);
glPolygonStipple(fly);
glRectf(125.0,25.0,225.0,125.0);
glPolygonStipple(halftone);
glRectf(225.0,25.0,325.0,125.0);
glDisable(GL_POLYGON_STIPPLE);
glFlush();
}
void reshape(int w, int h)
{
glViewport(0,0,(GLsizei) w, (GLsizei) h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0,350.0,0.0,150.0);
}
int main(int argc, char **argv)
{
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB);
glutInitWindowSize(350,150);
glutCreateWindow("Шаблонирование полигонов");
init();
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutMainLoop();
return 0;
}
Задание
1. Напишите программу создания окружностей двумя способами в одном
окне.
2. Напишите программу создания любой более сложной замкнутой
фигуры из простых объектов.
26
3. Закрасьте фигуры различными цветами, используя разные цвета
вершин, заливка должна быть меняющейся по поверхности фигуры.
4. Напишите свою программу шаблонированных полигонов (допустимо
преобразовать приведенные в шаблоне маски).
5. Составьте отчет о проделанной работе, приведите в нем новые тексты
программ и объясните каждый новый оператор.
Вопросы
1.
Какой командой устанавливается цвет объекта?
2.
Какой командой устанавливается модель заливки объекта?
3.
Какие значения в команде, устанавливающей модель заливки,
принимает параметр mode?
4.
Для чего используется шаблонирование полигонов?
5.
Какая команда устанавливает шаблон заливки?
6.
Какими командами включается/выключается возможность
шаблонирования полигонов?
7.
Почему в OPENGL многие возможности по умолчанию выключены?
Лабораторная работа № 6
Создание 3D объектов
Рисование трехмерных объектов
Часть1
Теория
Библиотека GLUT включает в себя несколько подпрограмм для
рисования перечисленных ниже трехмерных объектов:
Конус
Икосаэдр
Чайник
27
Куб
Октаэдр
Тетраэдр
Додекаэдр
Сфера
Тор
Однако если при создании 3D объектов не использовать освещение и
материалы, выглядеть такие объекты будут как 2D объекты.
Библиотека GLUT включает в себя несколько подпрограмм для
рисования перечисленных ниже трехмерных объектов:
Можно нарисовать эти объекты в виде каркасных моделей или в виде
сплошных
закрашенных
объектов
с
определенными
нормалями
к
поверхностям. Если используется освещение, следует выбирать сплошную
версию объекта. Все объекты рисуются с учетом текущих параметров,
например, цвета и характеристик материала.
Команды для создания 3D объектов
void glutWireSphere (GLdouble radius, GLint slices, GLint stacks);
void glutSolidSphere (GLdouble radius, GLint slices, GLint stacks);
Рисуют проволочную или сплошную сферу с радиусом radius,
количеством частей (полигонов из которых состоит сфера) slices – вокруг оси
z и stacks – вдоль оси z. Для того, чтобы понять, что означает вокруг оси z и
вдоль нее, представьте себе, что вы смотрите в длинную трубу. В данном
случае направление вашего обзора совпадает с осью z трубы. Она может
быть мысленно разделена как вдоль (на длинные фрагменты), так и поперек
(на кольца). После таких разбиений труба фактически состоит из множества
мелких кусочков. В случае сферы количество разбиений поперек задается
параметром stacks, а количество разбиений вдоль – параметром slices. Из
этого следует, что чем больше разбиений, тем более гладкой выглядит сфера
на экране, но тем больше вычислений требуется для ее рисования.
void glutWireCube (GLdouble size);
void glutSolidCube (GLdouble size);
28
Рисуют проволочный или сплошной куб с длиной ребра size.
void glutWireTorus (GLdouble innerRadius, GLdouble outerRadius, GLint
nsides, GLint rings);
void glutSolidTorus (GLdouble innerRadius, GLdouble outerRadius, GLint
nsides, GLint rings);
Рисуют проволочный или сплошной торус (бублик) с внешним
радиусом outerRadius и внутренним радиусом innerRadius. Параметр rings
задает желаемое число колец из которых будет состоять торус, параметр
nsides – из скольких частей будет состоять каждое кольцо.
void glutWireCone (GLdouble radius, GLdouble height, GLint slices, GLint
stacks);
void glutSolidCone (GLdouble radius, GLdouble height, GLint slices, GLint
stacks);
Рисуют проволочный или сплошной конус радиусом radius, высотой
height. Значение параметров slices и stacks аналогично таким же параметрам
для сферы.
void glutWireIcosahedron (void);
void glutSolidIcosahedron (void);
void glutWireOctahedron (void);
void glutSolidOctahedron (void);
void glutWireTetrahedron (void);
void glutSolidTetrahedron (void);
void glutWireDodecahedron (void);
void glutSolidDodecahedron (void);
Рисуют проволочные или сплошные икосаэдр, октаэдр, тетраэдр и
додекаэдр соответственно.
void glutWireTeapot (GLdouble size);
void glutSolidTeapot (GLdouble size);
Рисуют проволочный или сплошной чайник размера size.
29
Все эти модели рисуются центрированными относительно начала мировой
системы координат.
В OPENGL все преобразования над объектами производятся с
помощью операций с матрицами.
Для сохранения и восстановления текущей матрицы используются
функции
glPushMatrix()
и
glPopMatrix().
Они
записывают
и
восстанавливают текущую матрицу из стека, причем для каждого типа
матриц стек свой. Для модельно-видовых матриц его глубина равна как
минимум 32, для остальных – как минимум 2.
Функции
переменные
glPushMatrix()
состояния
и
и
glPopMatrix()
используются,
восстанавливают
например,
при
аффинных
преобразованиях. К аффинным преобразованиям относятся команды сдвиг,
вращение, масштабирование. Без использования матриц можно получить
неожиданный результат.
Сама матрица может быть создана с помощью следующих команд:
void glTranslate[f d] (GLtype x, GLtype y, GLtype z)
void glRotate[f d] (GLtype angle, GLtype x, GLtype y, GLtype z)
void glScale[f d] (GLtype x, GLtype y, GLtype z)
glTranlsate*() производит перенос объекта, прибавляя к координатам
его вершин значения своих параметров.
glRotate*() производит поворот объекта против часовой стрелки на
угол angle (измеряется в градусах) вокруг вектора (x,y,z).
glScale*()
производит
масштабирование
объекта
(сжатие
или
растяжение) вдоль вектора (x,y,z), умножая соответствующие координаты
его вершин на значения своих параметров.
Все эти преобразования изменяют текущую матрицу, а поэтому
применяются к примитивам, которые определяются позже. В случае, если
30
надо, например, повернуть один объект сцены, а другой оставить
неподвижным, удобно сначала сохранить текущую видовую матрицу в стеке
командой glPushMatrix(), затем вызвать glRotate() с нужными параметрами,
описать примитивы, из которых состоит этот объект, а затем восстановить
текущую матрицу командой glPopMatrix().
Любой 3D объект создается в координатах x=0 y=0 z=0.
Пример
#include "stdafx.h"
#include <GL/glut.h>
#include <stdlib.h>
void init (void)
{
glClearColor(A, B, C, 0.0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(a, b, c, d);
}
/* Функция вызывается при необходимости
* перерисовки изображения. В ней осуществляется
* весь вывод геометрии.
*/
void display (void)
{
/* очищаем буфер кадра и буфер глубины */
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glViewport(p1, p2, p3, p4);
glColor3f(k,l,m);
glPushMatrix ();
glTranslatef по х, по у, по z);
glRotatef (угол, по х, по у, по z);
//рисуем сплошной объект
glPopMatrix ();
glLineWidth(?);
glPushMatrix ();
glColor3f(k1,l1,m1);
glTranslatef (s, p, u); //эта команда сдвигает объект
glRotatef (угол, по х, по у, по z);
glScalef(по х, по у, по z);
//рисуем проволочный объект
glPopMatrix ();
glFlush ();
}
/* Главный цикл приложения.
* Создается окно, устанавливается режим
* экрана с буфером глубины
*/
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB
| GLUT_DEPTH);
31
glutInitWindowSize (W, H);
glutCreateWindow ("");
init ();
glutDisplayFunc (display);
glutMainLoop();
return 0;
}
Задание
1. Создайте 9 самостоятельных программ, которые рисуют проволочные
и сплошные 3D-объекты. Если объекты не будут выглядеть трехмерными,
объясните почему. 3D-объекты по умолчанию создаются в начале системы
координат. Обеспечьте сдвиг объектов относительно друг друга, с помощью
аффинных преобразований.
2. Составьте отчет о проделанной работе.
Вопросы
1.
Для чего используются функции glPushMatrix() и glPopMatrix()?
2.
Назовите оператор сдвига и приведите пример использования
параметров в операторе.
3.
Назовите оператор вращения и приведите пример использования
параметров в операторе.
4.
Назовите
оператор
масштабирования
и
приведите
пример
использования параметров в операторе.
5.
Опишите значения параметров в команде создания сферы.
6.
Опишите значения параметров в команде создания куба.
7.
Опишите значения параметров в команде создания конуса.
8.
Опишите значения параметров в команде создания тора.
Лабораторная работа № 7
Анимация 3D объектов
32
Для анимации в OPENGL необходимо запрограммировать следующую
строку:
Движение = Перерисовка изображения + Перестановка буферов
В программе следует использовать двойную буферизацию.
Команда использования двойной буферизации:
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
Команда смены буфера кадра : glutSwapBuffers();
В этом случае можно обойтись без команды glFlush();
Порядок действий для анимации объектов::
открыть_окно_в_режиме_двойной_буфериэации();
for (i = 0; i <1000000; i++) {
очистить_окно();
нарисовать_кадр(i);
поменять_буферы_местами() ;
}
Для явного указания на то, что окно надо обновить в функции draw()
иногда нужно использовать функцию glutPostRedisplay();
Задания
1.
Откройте
созданную
программу
вывода
3D
объектов.
Закомментируйте вывод одного объекта, добавьте команды для анимации
второго объекта.
2.
После получения результата уберите комментарии и добейтесь с
помощью команд того, чтобы один объект вращался, а второй оставался
неподвижным.
3.
Решите эту задачу для всех 9 программ вывода 3D объектов.
Основа для программы анимации
#include "stdafx.h"
33
#include <gl/glut.h>
void init(void);
void draw(void);
float i,angle=0;
void main(int argc, char **argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
glutInitWindowSize(,);
glutInitWindowPosition(,);
glutCreateWindow("Программа анимации ");
init();
glutDisplayFunc(draw);
glutMainLoop() ;
}
void init(void)
{
glClearColor();
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D();
}
void draw(void)
{
glViewport();
glColor3f( );
{
glClear(GL_COLOR_BUFFER_BIT);
angle+=0.05;
glClearColor( );
glPushMatrix();
аффинные операции и геометрия объекта (в конкретных случаях
можно обойтись без циклов приращения координат, т.к. приращение
происходит за счет смены буферов)
glPopMatrix();
glPushMatrix();
glColor3f( );
аффинные операции и геометрия объекта
glPopMatrix();
glutPostRedisplay();
}}
34
Вопросы
1.
Перечислите порядок действий, необходимых для создания анимации.
2.
Почему дисплей устанавливается в режиме двойной буферизации?
3.
Почему при анимации не используется команда glFlush()?
4.
Для чего используется команда glutPostRedisplay()?
5.
Для чего используется команда glutSwapBuffers()?
6.
Почему для создания анимации не обязателен цикл изменения
движения?
Лабораторная работа № 8
Освещение
Чтобы объект выглядел реалистично, он должен быть отображен с
учетом освещения, текстурирования, удаления невидимых поверхностей,
тумана, других механизмов (states), влияющих на его внешний вид, и каждый
из этих механизмов имеет множество, ассоциированных с ним, переменных
состояния.
Помним
о
том,
что
переменные
состояния
нужно
включать/выключать.
По умолчанию, большинство из этих механизмов OpenGL неактивно,
так как они могут быть весьма дороги в смысле производительности.
Например, включение текстурирования практически наверняка замедлит
процесс визуализации примитива. Однако изображение будет лучше по
качеству, и будет выглядеть более реалистично. Для включения или
выключения многих механизмов используются 2 уже известные нам
команды:
void glEnable (GLenum cap);
void glDisable (GLenum cap);
35
glEnable() включает механизм, glDisable() выключает его. Существует более
40 величин, которые могут передаваться в качестве параметров glEnable()
или glDisable(). Вот некоторые примеры:
GL_BLEND (контролирует наложение RGBA – величин),
GL_DEPTH_TEST (контролирует сравнение по глубине и обновление
буфера глубины),
GL_LINE_STIPPLE (применение шаблонов для линий)
GL_LIGHTING (освещение).
В модели OpenGL эффект от источника света присутствует только,
если есть поверхности поглощающие или отражающие свет. Считается, что
каждая поверхность состоит из материала с несколькими свойствами.
Материал может излучать свой собственный свет (например, фара
автомобиля), он может распределять некоторое количество входящего света
во всех направлениях, также он может отражать часть света в определенном
направлении (например, зеркало или другая блестящая поверхность).
RGB – величины для света и материалов
Цветовые компоненты, задаваемые для источников света, означают
совсем не то же самое, что для материалов. Для источника света число
представляет собой процент от полной интенсивности каждого цвета. Если R,
G и B – величины цвета источника света все равны 1.0, свет будет
максимально белым. Если величины будут равны 0.5, свет все равно
останется белым, но лишь с половиной интенсивности (он будет казаться
серым). Если R=G=1 и B=0 (полный красный, полный зеленый, отсутствие
синего), свет будет желтым.
Для материалов числа соответствуют отраженным пропорциям этих
цветов. Так что, если для материала R=1, G=0.5 и B=0, этот материал
отражает весь красный свет, половину зеленого и совсем не отражает синего.
Другими словами, если обозначить компоненты источника света как (LR, LG,
LB), а компоненты материала как (MR, MG, MB) и проигнорировать все
36
остальные взаимодействия, то свет, который поступит в глаз можно
определить как (LR–MR, LG–MG, LB–MB).
Похожим образом, если два источника света с характеристиками (R1,
G1, B1) и (R2, G2, B2) направлены в глаз, OpenGL сложит компоненты:
(R1+R2, G1+G2, B1+B2). Если какая-либо из сумм будет больше 1
(соответствуя цвету, который нельзя отобразить), компонент будет урезан до
1.
В модели освещения OpenGL предполагается, что освещение может
быть разделено на 4 компонента:
фоновое (ambient)
диффузное (diffuse)
зеркальное (specular)
исходящее (эмиссионное – emissive).
Все 4 компонента рассчитываются независимо и только затем
суммируются.
Фоновый, диффузный, зеркальный и исходящий свет
Фоновое излучение – это свет, который настолько распределен средой
(предметами, стенами и так далее), что его направление определить
невозможно – кажется, что он исходит отовсюду. Лампа дневного света
имеет большой фоновый компонент, поскольку большая часть света,
достигающего глаза, сначала отражается от множества поверхностей.
Уличный фонарь имеет маленький фоновый компонент: большая часть его
света идет в одном направлении, кроме того, поскольку он находится на
улице, очень небольшая часть света попадает в глаз после того, как отразится
от других объектов. Когда фоновый свет падает на поверхность, он
одинаково распределяется во всех направлениях.
Диффузный компонент – это свет, идущий из одного направления,
таким образом, он выглядит ярче, если падает на поверхность под прямым
37
углом, и выглядит тусклым, если касается ее всего лишь вскользь. Однако,
когда он падает на поверхность, он распределяется одинаково во всех
направлениях, то есть его яркость одинакова вне зависимости от того, с
какой стороны вы смотрите на поверхность. Вероятно, любой свет,
исходящий из определенного направления или позиции, имеет диффузный
компонент.
Зеркальный свет исходит из определенного направления и отражается
от поверхности в определенном направлении. При отражении хорошо
сфокусированного лазерного луча от качественного зеркала происходит
почти 100 процентное зеркальное отражение. Блестящий металл или пластик
имеет высокий зеркальный компонент, а кусок ковра или плюшевая игрушка
– нет. Можно думать о зеркальности как о том, насколько блестящим
выглядит материал.
Помимо фонового, диффузного и зеркального цветов, материалы могут
также иметь исходящий цвет, имитирующий свет, исходящий от самого
объекта. В модели освещения OpenGL исходящий свет поверхности
добавляет объекту интенсивности, но на него не влияют никакие источники
света, и он не производит дополнительного света для сцены в целом.
Хотя источник света излучает единое распределение частот, фоновый,
диффузный и зеркальный компоненты могут быть различны. Например, если
в комнате красные стены и белый свет, то этот свет, отражаясь от стен будет
скорее красным, чем белым (несмотря на то, что падающий на стену свет белый). OpenGL позволяет устанавливать значения красного, зеленого и
синего независимо для каждого компонента света.
Чтобы добавить на сцену освещение, требуется выполнить
несколько шагов:
1. Определить вектор нормали для каждой вершины каждого объекта.
Эти нормали задают ориентацию объекта по отношению к источникам света.
38
2. Создать, выбрать и позиционировать один или более источников
света.
3. Создать и выбрать модель освещения, которая определяет уровень
глобального фонового света и эффективное положение точки наблюдения
(для вычислений, связанных с освещением).
4. Задать свойства материала для объектов сцены.
Вызовы команд, связанных с освещением помещают в функцию
init().
GLfloat light_ambient[] = { 0.0, 0.0, 0.0, 1.0 };
GLfloat light_diffuse[] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat light_specular[] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 };
/* устанавливаем параметры источника света */
glLightfv (GL_LIGHT0, GL_AMBIENT, light_ambient);
glLightfv (GL_LIGHT0, GL_DIFFUSE, light_diffuse);
glLightfv (GL_LIGHT0, GL_SPECULAR, light_specular);
glLightfv (GL_LIGHT0, GL_POSITION, light_position);
/* включаем освещение и источник света */
glEnable (GL_LIGHTING);
glEnable (GL_LIGHT0);
/* включаем z-буфер */
glEnable(GL_DEPTH_TEST);
Определение вектора нормали для каждой вершины каждого
объекта
Нормали объекта задают его ориентацию относительно источников
света. OpenGL использует нормаль вершины для определения того, как
много света эта вершина получает от каждого источника. В приведенном
39
примере процесс определения нормалей происходит внутри функции
glutSolidSphere().
Создание, позиционирование и включение одного или более
источников света
Создание источников света
Источники света имеют несколько параметров, таких как цвет, позиция
и направление. Следующие разделы объясняют, как контролировать эти
свойства, на что будет похож результирующий источник света. Команда,
используемая для указания всех параметров света – это glLight*(). Она
принимает три аргумента: идентификатор источника света, имя свойства и
желаемое для него значение.
void glLight{if} (GLenum light, GLenum pname, TYPE param);
void glLight{if}v (GLenum light, GLenum pname, TYPE *param);
Создает источник, задаваемый параметром light (который может
принимать значения GL_LIGHT0, GL_LIGHT1, ..., GL_LIGHT7). Задаваемая
характеристика света определяется аргументом pname в виде константы
(таблица 3). В параметре param задается значение или значения, в которые
следует установить характеристику pname.
Если используется векторная версия команды, param представляет
собой вектор величин, а если невекторная, то param – одно единственное
значение. Невекторная версия команды может использоваться только для
указания параметров, чье значение выражается одним числом.
Таблица 3 Значения по умолчанию для свойств источника света
40
Замечание:
Значения по умолчанию для GL_DIFFUSEи GL_SPECULAR в таблице
различаются для GL_LIGHT0 и других источников света (GL_LIGHT1,
GL_LIGHT2,...). Для параметров GL_DIFFUSEи GL_SPECULAR источника
света GL_LIGHT0 значение по умолчанию – (1.0, 1.0, 1.0, 1.0). Для других
источников света значение тех же параметров по умолчанию – (0.0, 0.0, 0.0,
1.0).
В примере используется всего один источник белого света. Его
местоположение задается вызовом команды glLightfv(). В этом примере для
нулевого источника света (GL_LIGHT0) задается белый цвет для фонового,
диффузного и зеркального отражения. Кроме того, можно добавить к сцене
как минимум 8 источников света различных цветов. (Конкретная реализация
OpenGL может позволять и больше 8-ми источников.) По умолчанию цвет
всех источников света кроме GL_LIGHT0 – черный. Можно располагать
источники света везде, где только захотите, например, близко к сцене (как
настольную лампу) или бесконечно далеко за ней (симулирую солнечный
41
свет). Кроме того, вы можете управлять тем, испускает ли источник
сфокусированный, узкий луч или более широкий. Помните, что каждый
источник света требует дополнительных (и немалых) расчетов для
отображения сцены, так что быстродействие вашего приложение зависит от
количества источников.
После
настройки
параметров
источника
света
необходимо
активизировать его командой glEnable(). Кроме того, необходимо вызвать
команду glEnable() с аргументом GL_LIGHTING, чтобы подготовить
OpenGL к выполнению расчетов, связанных с освещением.
Замечание: Помните, что каждый источник света нужно включить командой
glEnable().
Выбор модели освещения
Параметры
glLightModel*().
модели
освещения
описываются
командой
Модель освещения также позволяет указывать, где
находится предполагаемое местоположение наблюдателя: бесконечно далеко
или локально по отношению к сцене, и должны ли вычисления
производиться по-разному для лицевых и обратных поверхностей объектов.
Возможно использовать значения по умолчанию, например для двух
аспектов модели – наблюдатель находится бесконечно далеко (режим
«бесконечно
удаленного
наблюдателя») и
одностороннее освещение.
Использование режима «локального наблюдателя» заставляет производить
намного больший объем сложных расчетов, так как OpenGL должна
вычислить угол между точкой наблюдения и каждым объектом. В режиме
«бесконечно удаленного наблюдателя», однако, этот угол игнорируется, и
результат может быть менее реалистичным. Поскольку в примере обратная
поверхность сферы никогда не видна (она находится внутри сферы),
достаточно одностороннего освещения.
glLightModel*() – это команда, используемая для задания всех
параметров модели освещения, glLightModel*() принимает два аргумента:
42
имя параметра модели освещения в виде константы и значение для этого
параметра.
void glLightModel{if} (GLenum pname, TYPE param);
void glLightModel{if}v (GLenum pname, TYPE *param);
Устанавливает
свойства
модели
освещения.
Устанавливаемая
характеристика модели освещения определяется аргументом pname (таблица
4). param задает величину, в которую устанавливается pname; если
используется векторная версия команды, то это указатель на группу величин,
если применяется невекторная версия – в param содержится сама величина.
Невекторная версия команды может использоваться только для установки
параметров, определяемых одной величиной (и не может применяться
для GL_LIGHT_MODEL_AMBIENT).
Таблица 4. Значения по умолчанию для параметра pname модели освещения
Определение свойств материала для объектов сцены
Свойства материала объектов определяют, как он отражает свет и,
таким образом, из какого реального материала он сделан (в зрительном
восприятии). Поскольку взаимодействие между поверхностью материала и
43
входящим светом достаточно сложное, довольно трудно задать такие
параметры материала, чтобы объект имел определенный, желаемый вид.
Можно задавать фоновый, диффузный и зеркальный цвета материала и то,
насколько блестящим он будет выглядеть.
Замечания
Когда вы пишете свою собственную программу с освещением, всегда
помните, что для некоторых его параметров вы можете использовать
значения по умолчанию, тогда как другие должны быть заданы явно. Кроме
того, не забудьте включить все описанные вами источники света, а также сам
механизм расчетов, связанных с освещением.
Указание свойств материала
Большинство свойств материала похожи на те, которые использовались
при создании источников света. Механизм из указания также аналогичен
установке параметров источника света за исключением того, что здесь
используется команда glMaterial*().
void glMaterial{if}(GLenum face, GLenum pname, TYPE param);
void glMaterial{if}v(GLenum face, GLenum pname, TYPE *param);
Задает
свойство
материала
для
использования
при
расчете
освещенности. Аргумент face может принимать значения GL_FRONT,
GL_BACK или GL_FRONT_AND_BACK, указывая для каких граней объекта
задается
свойство
материала.
Устанавливаемое
свойство
материала
определяется значением аргумента pname, а его значение содержится в param
(в виде указателя на вектор величин в случае векторной версии команды или
в виде самой величины при использовании невекторного варианта).
Невекторная
версия
команды
работает
только
для
параметра
GL_SHININESS.
Возможные значения для аргумента pname перечислены в таблице 4.
Заметьте,
что
константа
GL_AMBIENT_AND_DIFFUSE
позволяет
44
одновременно установить фоновый и диффузный цвета материала в одно и
то же RGBAзначение.
Таблица 5. Значения по умолчанию для параметра материала pname
Можно задать расчет освещенности для лицевых и обратных полигонов
объекта. Если в приложении обратные грани могут быть видимыми, можно
по-разному задать параметры материала для лицевых и обратных граней
объекта, используя аргумент face команды glMaterial*().
Пример
Данный пример создает самую непритязательную сферу для понимания
способа ее создания.
#include "stdafx.h"
#include <GL/glut.h>
#include <stdlib.h>
/* параметры материала шара */
float mat3_dif[]={0.9f,0.2f,0.0f};
float mat3_amb[]= {0.2f,0.2f,0.2f};
float mat3_spec[]={0.6f,0.6f,0.6f};
float mat3_shininess=0.1f*128;
/* Инициализируем параметры материалов и
* источника света
*/
void init (void)
{ glClearColor(1.0, 1.0, 0.0, 0.0);
GLfloat light_ambient[] = { 0.0, 0.0, 0.0, 1.0 };
GLfloat light_diffuse[] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat light_specular[] = { 1.0, 1.0, 1.0, 1.0 };
45
GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 };
/* устанавливаем параметры источника света */
glLightfv (GL_LIGHT0, GL_AMBIENT, light_ambient);
glLightfv (GL_LIGHT0, GL_DIFFUSE, light_diffuse);
glLightfv (GL_LIGHT0, GL_SPECULAR, light_specular);
glLightfv (GL_LIGHT0, GL_POSITION, light_position);
/* включаем освещение и источник света */
glEnable (GL_LIGHTING);
glEnable (GL_LIGHT0);
/* включаем z-буфер */
glEnable(GL_DEPTH_TEST);
}
/* Функция вызывается при необходимости
* перерисовки изображения. В ней осуществляется
* весь вывод геометрии.
*/
void display (void)
{
/* очищаем буфер кадра и буфер глубины */
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMaterialfv (GL_FRONT,GL_AMBIENT,mat3_amb);
glMaterialfv (GL_FRONT,GL_DIFFUSE,mat3_dif);
glMaterialfv (GL_FRONT,GL_SPECULAR,mat3_spec);
glMaterialf (GL_FRONT,GL_SHININESS,mat3_shininess);
glColor3f(1.0,0.0,1.0);
glutSolidSphere (0.3, 15, 15);
/* выводим сцену на экран */
glFlush ();
}
/* Главный цикл приложения.
* Создается окно, устанавливается режим
* экрана с буфером глубины
*/
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB
| GLUT_DEPTH);
glutInitWindowSize (500, 500);
glutCreateWindow (argv[0]);
init ();
46
glutDisplayFunc (display);
glutMainLoop();
return 0;
}
Задание
1. Добавьте в каждую из созданных ранее 9 программ освещение,
в
программах используйте разные параметры, так, чтобы эффект от освещения
был разным.
1. Составьте отчет о проделанной работе, приведите в нем тексты
программ, объясните каждый оператор.
2. Приготовьтесь к сдаче теоретического материала по данной теме.
Основа для программы
/* параметры материала объекта */
float mat3_dif[]={0.9f,0.2f,0.0f};
float mat3_amb[]= {0.2f,0.2f,0.2f}; //поэкспериментировать
Float mat3_spec[]={0.6f,0.6f,0.6f};
float mat3_shininess=0.1f*128;
/* Инициализируем параметры материалов и источника света
void init(void);
void draw(void);
void main(int argc, char **argv)
{
glutInit(&argc, argv);
glutInitDisplayMode( GLUT_RGB| GLUT_DOUBLE);
glutInitWindowSize();
glutInitWindowPosition();
glutCreateWindow(“Программа анимации и освещения “);
init();
glutDisplayFunc(draw);
glutMainLoop() ;
}
void init(void)
{
glClearColor();
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D();
Glfloat light_ambient[] = { 0.0, 0.0, 0.0, 1.0 };
Glfloat light_diffuse[] = { 1.0, 1.0, 1.0, 1.0 };
Glfloat light_specular[] = { 1.0, 1.0, 1.0, 1.0 };
Glfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 };
/* устанавливаем параметры источника света */
glLightfv (GL_LIGHT0, GL_AMBIENT, light_ambient);
glLightfv (GL_LIGHT0, GL_DIFFUSE, light_diffuse);
glLightfv (GL_LIGHT0, GL_SPECULAR, light_specular);
glLightfv (GL_LIGHT0, GL_POSITION, light_position);
/* включаем освещение и источник света */
glEnable (GL_LIGHTING);
glEnable (GL_LIGHT0);
/* включаем z-буфер */
glEnable(GL_DEPTH_TEST);
с параметрами!
47
}
void draw(void)
{
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glViewport();
glMaterialfv (GL_FRONT,GL_AMBIENT,mat3_amb);
glMaterialfv (GL_FRONT,GL_DIFFUSE,mat3_dif);
glMaterialfv (GL_FRONT,GL_SPECULAR,mat3_spec);
glMaterialf (GL_FRONT,GL_SHININESS,mat3_shininess);
glColor3f();
glPushMatrix();
glClearColor();
ВСЯ ГЕОМЕТРИЯ ВЫВОДА ОБЪЕКТОВ
glPopMatrix();
glutSwapBuffers();
glFlush();
}
}
Вопросы
1.
Что означают цветовые компоненты для источников света?
2.
Что означают цветовые компоненты для материалов?
3.
В каком диапазоне находятся значения цветовых компонент?
4.
Из каких компонент состоит освещение?
5.
Сколько источников света можно использовать в программе?
6.
В какой команде указываются значения источников света?
7.
Что означает каждый параметр в команде glLight*()?
8.
Какие цвета имеют источники света по умолчанию?
9.
Какая команда описывает параметры модели освещения?
10.
Какая команда устанавливает свойства материалов?
Лабораторная работа № 9
Теория
Как известно, при выводе изображения на экран используется
проецирование объектов на экран. Наиболее распространенными проекциями
являются параллельная и центральная (перспективная) проекции.
48
Использование параллельной проекции
void init(void)
{ glMatrixMode(GL_PROJECTION);
glLoadIdentity();
//эти 2 команды выбирают текущей матрицей матрицу проекций и
устанавливают ее в единичную матрицу.
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
//далее устанавливают в единичную матрицу аффинных преобразований модельно-видовую матрицу, которая преобразует объект в мировых
координатах (сдвиг, поворот, масштабирование)
Далее программист определяет, какая именно будет использоваться
проекция.
Ортографическая проекция
gluOrtho2D(); используется эта команда
}
void glOrtho (GLdouble left, GLdouble right, GLdouble bottom, GLdouble top,
GLdouble near, GLdouble far)
void gluOrtho2D (GLdouble left, GLdouble right, GLdouble bottom, GLdouble
top)
Система координат используется левосторонняя.
Центральная проекция
void init(void)
{glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60, 1, 1, 10); // Перспективная проекция
}
49
Перспективная проекция
задает усеченный конус видимости в
левосторонней системе координат. Параметр angley определяет угол
видимости в градусах по оси у и должен находиться в диапазоне от 0 до 180.
Угол видимости вдоль оси x задается параметром aspect, который обычно
задается как отношение сторон области вывода (как правило, размеров окна)
Параметры zfar и znear задают расстояние от наблюдателя до плоскостей
отсечения по глубине и должны быть положительными. Чем больше
отношение zfar/znear, тем хуже в буфере глубины будут различаться
расположенные рядом поверхности, так как по умолчанию в него будет
записываться ‘сжатая’ глубина в диапазоне от 0 до 1.
Положение наблюдателя можно задать с помощью команды
void gluLookAt (GLdouble eyex, GLdouble eyey, GLdouble eyez,
GLdouble centerx, GLdouble centery, GLdouble centerz,
GLdouble upx, GLdouble upy, GLdouble upz)
точка (eyex,eyey,eyez) определяет точку наблюдения,
(centerx,
centery,
centerz)
задает
центр
сцены,
который
будет
проектироваться в центр области вывода
вектор (upx,upy,upz) задает положительное направление оси у, определяя
поворот камеры. Если, например, камеру не надо поворачивать, то задается
значение (0,1,0), а со значением (0,-1,0) сцена будет перевернута.
Вызывать
команду gluLookAt() имеет смысл перед определением
преобразований
объектов,
когда
модельно-видовая
матрица
равна
единичной.
При изменении положения источника света следует учитывать
следующий факт: в OpenGL источники света являются объектами, такими
же, как многоугольники и точки. На них распространяется основное правило
обработки координат в OpenGL – параметры, описывающее положение в
пространстве, преобразуются текущей модельно-видовой матрицей в момент
50
формирования объекта, т.е. в момент вызова соответствующих команд
OpenGL. Таким образом, формируя источник света одновременно с объектом
сцены или камерой, его можно привязать к этому объекту. Или, наоборот,
сформировать стационарный источник света, который будет оставаться на
месте, пока другие объекты перемещаются.
Общее правило такое:
Если положение источника света задается командой glLight*() перед
определением
положения
виртуальной
камеры
(например,
командой
glLookAt()), то будет считаться, что координаты (0,0,0) источника находится
в точке наблюдения и, следовательно, положение источника света
определяется относительно положения наблюдателя.
Если положение устанавливается между определением положения
камеры и преобразованиями модельно-видовой матрицы объекта, то оно
фиксируется, т.е. в этом случае положение источника света задается в
мировых координатах.
ЗАДАНИЕ
1.
Добавить освещение к четырем любым созданным ранее
программам, при этом камера в одних случаях должна быть привязана к
объектам, а в других - нет.
Творческая работа
1.
Создать программу с составным объектом в середине экрана и
несколькими объектами, расположенными вокруг центрального, обеспечив
единую композицию. Объект в середине экрана должен быть составным.
2.
Обеспечить анимацию вращения и перемещения объектов,
добавить освещение, обеспечив разный цвет объектов и, возможно
перспективу, т.е. удаляясь, объекты уменьшаются.
51
Download