Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
12260.pdf
Скачиваний:
144
Добавлен:
29.03.2016
Размер:
1.6 Mб
Скачать

Компьютерная графика.

точек, удовлетворяющих определенному уравнению, или задана другим образом. Реальный экран это всегда конечное количество точек. Изображение представляет из себя прямоугольную сетку, узлы которой имеет целочисленные координаты. Появляется законный вопрос: как определить связность линии на экране?

Традиционно вводятся два понятия связности.

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 = x2x1 .

y y1

y2y1

Тогда

x= x1 +( yy1) x2x1 , y2y1

то есть x= f ( y).

А также

y= y1 +( xx1) y2y1 , x2x1

то есть 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 на последнем шаге. Это необходимо учитывать при использовании алгоритма.

Алгоритм Брезенхэма.

Брезенхэм предложил подход, позволяющий разрабатывать так называемые инкрементные алгоритмы растеризации. Основной целью для разработки таких алгоритмов было построение циклов вычисления координат на основе только целочисленных операций сложения/вычитания без использования умножения и деления.

Инкрементные алгоритмы выполняются как последовательное вычисление координат соседних пикселей путем добавления приращений координат. Приращения рассчитываются на основе анализа функции погрешности. В цикле выполняются только целочисленные операции сравнения и сложения/вычитания. Достигается повышение быстродействия для вычислений каждого пикселя по сравнению с прямым способом.

Из подобия треугольников следует равенство:

( xx1) = ( yy1)

(x2x1) ( y2y1 )

-34-

Компьютерная графика.

раскрываем скобки:

(xx1) ( y2y1)−( yy1 ) (x2x1)=0,

получаем неявное уравнение прямой вида ax + by + c = 0 :

x dyy dx−(x1 dyy1 dx)=0,

где: dx=x2x1 и dy= y2y1 ,

f (x , y)=x dyy dx−( x1 dyy1 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+dydx ydx /2C

dy (x+2)−dx ( y+1 /2)−C = dy x+2 dydx ydx/2C = f ( M )+dy

dy (x+2)−dx ( y+3/2 )−C = dy x+2 dydx ydx 3/2C = f (M )+dydx

Теперь известны приращения на каждой итерации и осталось узнать только значение в первой точке M0.

f (M 0) = f (x1 +1, y1 +1/2) = dy (x1 +1)−dx ( y1+1/2)−( x1 dyy1 dx) = dydx/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-

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]