- •Введение.
- •Цели, задачи и структура курса.
- •Предмет компьютерной графики.
- •Сферы применения компьютерной графики.
- •Отображение информации.
- •Проектирование.
- •Моделирование.
- •Интерфейс пользователя.
- •Графическая система.
- •Объект и наблюдатель.
- •Визуализация изображений.
- •Растровая визуализация.
- •Векторная визуализация.
- •Цвет
- •Свет и изображение.
- •Глаз человека.
- •Характеристики цвета.
- •Цветовые модели.
- •Глубина цвета
- •Палитра
- •Координаты.
- •Прямоугольная система координат на плоскости.
- •Прямоугольная система координат в 3-x мерном пространстве.
- •Прямоугольная система координат в n-мерном пространстве.
- •Полярная система координат.
- •Графическое представление.
- •Трёхмерное расширение.
- •Цилиндрическая система координат.
- •Сферическая система координат.
- •Координаты в матричном виде.
- •Произведение матриц.
- •Преобразование координат.
- •Аффинные преобразования координат.
- •Аффинные преобразования координат на плоскости.
- •Однородные координаты.
- •Аффинные преобразования координат в трехмерном пространстве.
- •Преобразования объектов.
- •Аффинные преобразования объектов на плоскости.
- •Трехмерные аффинные преобразования объектов.
- •Композиция преобразований.
- •Эффективность преобразований.
- •Базовые растровые алгоритмы.
- •Связность.
- •Алгоритмы вывода прямой линии
- •Прямое вычисление координат.
- •Алгоритм Брезенхэма.
- •Алгоритм вывода окружности.
- •Алгоритм Брезенхэма построения окружности.
- •Растеризация многоугольника.
- •Алгоритм со списком реберных пересечений.
- •Алгоритм заполнения со списком активных ребер.
- •Заливка с затравкой.
- •Алгоритмы отсечения.
- •Отсечение отрезков.
- •Алгоритм Коэна-Сазерленда.
- •FC-алгоритм.
- •Алгоритм Лианга-Барски.
- •Двумерный алгоритм Кируса — Бека
- •Проверка выпуклости многоугольника и определение нормалей
- •Алгоритм с использованием векторных произведений
- •Разбиение не выпуклых многоугольников
- •Отсечение многоугольника
- •Алгоритм Сазерленда-Ходгмана
- •Простой алгоритм отсечения многоугольника
- •Алгоритм отсечения многоугольника Вейлера-Азертона
- •Удаление невидимых линий и поверхностей.
- •Алгоритм удаления поверхностей с Z-буфером.
- •Алгоритм разбиения области Варнока.
- •Алгоритм трассировки лучей.
Компьютерная графика.
точек, удовлетворяющих определенному уравнению, или задана другим образом. Реальный экран это всегда конечное количество точек. Изображение представляет из себя прямоугольную сетку, узлы которой имеет целочисленные координаты. Появляется законный вопрос: как определить связность линии на экране?
Традиционно вводятся два понятия связности.
4-связность (рис. 17 б): пикселы p1(x1,y1) и p2(x2,y2) называются соседними, если либо разность их координат по оси x, либо разность их координат по оси y равна 1.
|x2 – x1| + |y2 – y1| <= 1
8-связность (рис. 17 а): пикселы p1(x1,y1) и p2(x2,y2) называются соседними, если разность их координат по оси x и разность их координат по оси y не больше 1.
|x2 – x1| <= 1, |y2 – y1| <= 1
Рисунок 17: Связность: а) 8-связность, б) 4-связность
Алгоритмы вывода прямой линии
Линией на растровой сетке будем считать последовательность пикселов {P1, …, Pn}, таких, что любые два пиксела Pi, Pi+1 являются соседними в смысле заданной связности. Отметим, что любая четырехсвязная линия одновременно является восьмисвязной, но не наоборот. Таким образом 4-связность является более сильным понятием.
Рассмотрим растровые алгоритмы для отрезков прямой линии. Предположим, что заданы координаты (х1,у1 - х2,у2) концов отрезка прямой. Для вывода линии необходимо закрасить в определенный цвет все пиксели вдоль линии. Для того чтобы закрасить каждый пиксель, необходимо знать его координаты.
Наиболее просто нарисовать отрезок горизонтальной линии:
int x = x1; while(x <= x2)
{
SetPixel(x, y1); x = x + 1;
}
Вычисление текущих координат пикселя здесь выполняется как приращение по x (необходимо, чтобы х1<=х2), а вывод пикселя обеспечивается функцией SetPixel().
-31-
Компьютерная графика.
Аналогично рисуется отрезок вертикали:
int y = y1; while(y <= y2)
{
SetPixel(х1, y); y = y + 1;
}
Как видим, в цикле выполняются простейшие операции над целыми числами — приращение на единицу и проверка на "<=". Поэтому операция рисования отрезка выполняется быстро и просто.
Можно поставить такой вопрос: какая линия рисуется быстрее — горизонталь или вертикаль? На первый взгляд — одинаково быстро. Если учитывать только математические аспекты, то скорость должна быть одной и той же при одинаковой длине линий, поскольку в обоих случаях выполняется равное количество идентичных операций. Однако если кроме расчета координат анализировать также вывод пикселей, то могут быть отличия. В растровых системах рисование пикселя обычно означает запись одного или нескольких бит в память, где сохраняется растр. И здесь уже не все равно — по строкам или по столбцам заполняется растр. Необходимо учитывать логическую организацию памяти компьютера, в которой хранятся биты или байты растра. Даже для компьютеров одного типа для различных поколений процессоров и памяти скорость записи по соседним адресам может существенно отличаться от скорости записи по не соседним адресам.
Горизонтали и вертикали представляют собой частный случай линий. Рассмотрим линию общего вида. Для нее также необходимо вычислять координаты каждого пикселя. Известно несколько методов расчетов координат точек линии.
Прямое вычисление координат.
Пусть заданы координаты конечных точек отрезка прямой. Найдем координаты точки внутри отрезка (рис 18).
Рисунок 18: Отрезок прямой.
Запишем отношения катетов для подобных треугольников:
-32-
Компьютерная графика.
x −x1 = x2−x1 . |
|
y − y1 |
y2− y1 |
Тогда
x= x1 +( y− y1) x2−x1 , y2− y1
то есть x= f ( y).
А также
y= y1 +( x−x1) y2− y1 , x2−x1
то есть y=F (x).
В зависимости от угла наклона прямой выполняется цикл по оси x или по у.
Пример записи этого алгоритма на языке С, C++, где выполняется цикл по оси х, причем x1 < х2 :
int x = x1; int y = y1;
while(x <= x2)
{
SetPixel(х, y); x = x + 1;
y = y1 + ((x – x1) * (y2 – y1)) / (x2 – x1);
}
Рисунок 19: Растеризация прямой.
Здесь все операции выполняются над целыми числами. Недостатки такой
-33-
Компьютерная графика.
программы — в цикле выполняется много лишних операций, присутствуют операции деления и умножения. Это обуславливает малую скорость работы. Можно вынести вычисление dy / dx из цикла, поскольку это значение не изменяется. Однако для этого уже необходимо использовать операции с плавающей точкой:
int x = x1;
float y = y1 + 0.5;
float slope = (float)(y2 – y1) / (float)(x2 – x1);
while(x <= x2)
{
SetPixel(х, y); x = x + 1;
y = y + slope;
}
Простота реализации — это одно положительное качество данного алгоритма. Из недостатков метода можно отметить:
•Использование операций с плавающей точкой или целочисленных операций умножения и деления обуславливает малую скорость. Однако это зависит от процессора и для различных типов компьютеров может быть по-разному. В современных компьютерах время выполнения целочисленных операций уже не намного меньше. Для старых компьютеров разница могла быть в десятки раз, поэтому и старались разрабатывать алгоритмы только на основе целочисленных операций.
•Накапливание погрешности при вычислениях. Значение у вычисляется прибавлением приращения slope на каждом шаге, и на последнем шаге цикла (когда х=х2) должно стать у=у2. Исходя из чисто математических соображений здесь все корректно, однако необходимо учесть, что в компьютере дробные числа представляются в формате с плавающей точкой не точно. Кроме погрешности представления чисел существует ошибка выполнения арифметических операций с плавающей точкой. С каждым шагом цикла ошибки накапливаются, и может так произойти, что у не равно у2 на последнем шаге. Это необходимо учитывать при использовании алгоритма.
Алгоритм Брезенхэма.
Брезенхэм предложил подход, позволяющий разрабатывать так называемые инкрементные алгоритмы растеризации. Основной целью для разработки таких алгоритмов было построение циклов вычисления координат на основе только целочисленных операций сложения/вычитания без использования умножения и деления.
Инкрементные алгоритмы выполняются как последовательное вычисление координат соседних пикселей путем добавления приращений координат. Приращения рассчитываются на основе анализа функции погрешности. В цикле выполняются только целочисленные операции сравнения и сложения/вычитания. Достигается повышение быстродействия для вычислений каждого пикселя по сравнению с прямым способом.
Из подобия треугольников следует равенство:
( x−x1) = ( y− y1)
(x2− x1) ( y2− y1 )
-34-
Компьютерная графика.
раскрываем скобки:
(x− x1) ( y2− y1)−( y− y1 ) (x2−x1)=0,
получаем неявное уравнение прямой вида ax + by + c = 0 :
x dy− y dx−(x1 dy− y1 dx)=0,
где: dx=x2− x1 и dy= y2− y1 ,
f (x , y)=x dy− y dx−( x1 dy− y1 dx ),
если в функцию f(x, y) подставить точку пространства, то пространство разобьется на три подпространства:
{f (x , y)>0 точка( x , y)ниже прямой
f (x , y )=0 точка( x , y)находится на прямой f (x , y)<0 точка( x , y)выше прямой
Теперь рассмотрим пример (рис. 20). На текущей итерации закрашен пиксель P. Далее необходимо понять какой пиксель закрасить следующим E или NE. Для этого устанавливаем точку M с координатами (x + 1, y + ½), если точка лежит ниже прямой (f(M) > 0) значит закрашиваем пиксель NE, иначе закрашиваем пиксель E.
Рисунок 20: Алгоритм Брезенхэма генерации отрезка.
Пример реализации алгоритма на языке C:
int x = x1; int y = y1;
while(x <= x2)
{
SetPixel(x, y);
if( f(x + 1, y + 0.5) ) > 0)
{
y = y + 1;
}
x = x + 1;
-35-
Компьютерная графика.
}
/* f(x, y) = dy * x - dx * y - (x1 * dy - y1 * dx) * dx = x2 – x1; dy = y2 – y1;
*/
Данный алгоритм не лишен арифметики с числами с плавающей точкой, необходима модификация алгоритма.
Рисунок 21: Алгоритм Брезенхэма генерации отрезка.
На каждой итерации цикла координата x увеличивается на 1, а координата y увеличивается только в некоторых случаях. Необходимо заменить функцию вычисления по точке M на каждой итерации на приращение по y. Если на i-той итерации направление выбиралось относительно точки M, то на итерации i+1 направление выбирается относительно точки ME (если закрасили точку E) или относительно точки MNE (если закрасили точку NE). Теперь посмотрим как меняется значение функции f(x, y) при переходе к следующим точкам.
f (M ) |
= |
f (M E ) |
= |
f (M NE ) =
f (x+1, y+1 /2) =
f( x+2, y +1 /2) =
f(x+2, y +3 /2) =
dy (x+1)−dx (y +1 /2)−C = dy x+dy−dx y−dx /2−C
dy (x+2)−dx ( y+1 /2)−C = dy x+2 dy−dx y−dx/2−C = f ( M )+dy
dy (x+2)−dx ( y+3/2 )−C = dy x+2 dy−dx y−dx 3/2−C = f (M )+dy−dx
Теперь известны приращения на каждой итерации и осталось узнать только значение в первой точке M0.
f (M 0) = f (x1 +1, y1 +1/2) = dy (x1 +1)−dx ( y1+1/2)−( x1 dy− y1 dx) = dy−dx/2
Опишем новый алгоритм.
int x = x1; int y = y1;
int dx = x2 - x1; int dy = y2 - y1;
float f = dy - dx / 2.0;
while(x <= x2)
{
SetPixel(x, y);
-36-
Компьютерная графика.
if(f > 0)
{
y = y + 1;
f = f + (dy - dx);
}
else
{
f = f + dy;
}
x = x + 1;
}
Вещественные числа сохранились. Для избавления от чисел с плавающей точкой необходимо dx и dy домножить на 2, а также можно избавиться от лишних операций в цикле посчитав приращения заранее. Получаемый программный код с изменениями:
int x = x1; int y = y1;
int dx = x2 - x1; int dy = y2 - y1; int f = 2 * dy - dx;
while(x <= x2)
{
SetPixel(x, y);
if(f > 0)
{
y = y + 1;
f = f + (dy — dx) * 2;
}
else
{
f = f + dy * 2;
}
x = x + 1;
}
-37-
