Лекции по Компьютерной графике
.pdf
|
X |
|
Сдвиг системы |
Поворот |
Сдвиг системы x |
|
|
|
|
|
|
Y = координат на на уголα координат на y =
|
1 |
|
|
− x0 ,− y0 |
|
|
|
|
|
|
|
x0 , y0 |
|
1 |
|
||||
|
|
|
|
|
|
|
|
|
|
|
|||||||||
|
1 |
0 |
x |
cosα |
− sin α |
0 |
1 |
0 |
− x |
x |
|
|
|
||||||
|
|
|
|
0 |
|
|
|
|
|
|
|
|
0 |
|
|
|
|
||
= 0 |
1 |
y0 |
sin α |
|
cosα |
0 |
0 |
1 |
− y0 |
y = |
|
|
|||||||
|
|
0 |
0 |
1 |
|
0 |
|
0 |
1 |
|
|
|
1 |
|
|
|
|
||
|
|
|
|
0 0 |
|
1 |
|
|
|
||||||||||
|
cosα |
− sin α |
− x cosα + y |
|
sin α + x |
x |
|
|
|
||||||||||
|
|
|
|
|
|
|
|
0 |
|
|
0 |
|
|
0 |
|
|
|
|
|
= sin α |
cosα |
|
− x0 sin α − y0 cosα + y0 |
y . |
|
|
|
||||||||||||
|
|
|
0 |
|
0 |
|
|
|
1 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
|
|
|
|||
Рассмотрим второй пример. Задачей будет вывод формул параметрического описания поверхности тора. Изобразим тор следующим образом (рис. 8).
Для произвольной точки P , лежащей на поверхности тора, требуется выразить координаты ( x, y, z ) через константы, описывающие размеры фигуры, а также через некоторые параметры. Для поверхности в трехмерном пространстве необходимо использовать два параметра. В качестве которых выберем угловые величины: ϕ (широта) и ω (долгота).
Рис. 8 - Тор
Непосредственное определение координат точки P представляется сложным, поэтому искомые координаты будем искать несколькими шагами преобразований. Рассмотрим окружность, лежащую в плоскости z0 y , центр этой окружности совпадает с центром координат. Координаты точки P′′ с
широтой ϕ составляют:
x′′ = 0,
y′′ = r cosϕ , z′′ = r sin ϕ ,
где r — малый радиус тора.
Теперь перенесем окружность на расстояние R (большой радиус тора)
по оси y в той же плоскости z0 y . Получим точку P′ . Ее координаты:
x′ = x′′ = 0,
y′ = R + y′′ = R + r cosϕ , z′ = z′′ = r sin ϕ.
Окружность, которой принадлежит точка P′ , является геометрическим местом точек тора с нулевой долготой с0 . Если точку P′ повернуть на угол ω ,
то получим искомую точку P поверхности тора с координатами:
x = x′ cosω + y′sin ω, y = − x′sin ω + y′ cosω,
z = z′.
Подставляя значения ( x′, y′, z′ ) получим искомые формулы:
x = (R + r cosϕ ) sin ω,
y = (R + r cosϕ ) cosω,
z = r sin ϕ.
Эту задачу можно решить, используя преобразование координат.
Однако, как представляется, более ясным здесь выглядит использование операций перемещения точки (из положения P′′ в P′ , а затем в P ).
Мировые и экранные координаты
При отображении пространственных объектов на экране или на листе бумаги с помощью принтера необходимо знать координаты объектов. Мы рассмотрим две системы координат. Первая — мировые координаты,
которые описывают истинное положение объектов в пространстве с заданной точностью. Другая — система координат устройства изображения, в котором осуществляется вывод изображения объектов в заданной проекции. Пусть мировые координаты будут трехмерными декартовыми координатами. Где должен размещаться центр координат и какими будут единицы измерения
вдоль каждой оси, пока для нас не очень важно. Важно то, что для отображения мы будем знать какие-то числовые значения координат отображаемых объектов.
Для получения изображения в определенной проекции необходимо рассчитать координаты проекции. Из них можно получить координаты для графического устройства — назовем их экранными координатами. Для синтеза изображения на плоскости достаточно двумерной системы координат. Однако в некоторых алгоритмах визуализации используются трехмерные экранные координаты, например, в алгоритме Z -буфера.
Представим цепочку преобразований координат от мировых к экранным координатам следующим образом (рис. 9):
Рис. 9 – Этапы преобразований координат
Базовые растровые алгоритмы
Сформировать растровое изображение можно по-разному. Для того чтобы создать изображение на растровом дисплее, можно просто скопировать готовый растр в видеопамять. Этот растр может быть получен,
например, с помощью сканера или цифрового фотоаппарата. А можно создавать изображение объекта путем последовательного рисования отдельных простых элементов.
Простые элементы, из которых складываются сложные объекты, будем называть графическими примитивами. Их можно встретить повсюду.
Например, для построения изображения используется некоторый набор примитивов, которые поддерживаются определенными графическими устройствами вывода. Графические примитивы также можно применять для описания пространственных объектов в базе данных компьютерной системы
— модели объектов. Могут использоваться различные множества примитивов для модели и для алгоритмов отображения. Удобно, когда эти множества совпадают, тогда упрощается процесс отображения.
Простейшим и, вместе с тем, наиболее универсальным растровым графическим примитивом является пиксель. Любое растровое изображение можно нарисовать по пикселам, но это сложно и долго. Необходимы большие сложные элементы, для которых рисуются сразу несколько пикселов.
Рассмотрим графические примитивы, которые используются наиболее часто в современных графических системах — это линии и фигуры.
Алгоритмы вывода прямой линии
Рассмотрим растровые алгоритмы для отрезков прямой линии.
Предположим, что заданы координаты ( x1 y1 − x2 y2 ) концов отрезка прямой.
Для вывода линии необходимо закрасить в определенный цвет все пикселы вдоль линии. Для того чтобы закрасить каждый пиксел, необходимо знать его координаты.
Наиболее просто нарисовать отрезок горизонтальной линии:
for (x = x1; x ≤ x2 ; x + +)
//Пиксел( x, y1 );
Вычисление текущих координат пиксела здесь выполняется как приращение поля (необходимо, чтобы x1 ≤ x2 ), а вывод пиксела обеспечивается функцией Пиксел(). Поскольку в языке С, C++ для названия функции нельзя использовать кириллицу, то будем дальше использовать ее как комментарий.
Аналогично рисуется отрезок вертикали:
for ( y = y1; y ≤ y2 ; y + +)
//Пиксел( x1 , y );
Как видим, в цикле выполняются простейшие операции над целыми числами — приращение на единицу и проверка на " ≤ ". Поэтому операция рисования отрезка выполняется быстро и просто. Ее используют как базовую операцию для других операций, например, в алгоритмах заполнения плоскости ПОЛИГОНОВ.
Можно поставить такой вопрос: какая линия рисуется быстрее— горизонталь или вертикаль? На первый взгляд — одинаково быстро. Если учитывать только математические аспекты, то скорость должна быть одной и той же при одинаковой длине линий, поскольку в обоих случаях выполняется равное количество идентичных операций. Однако если кроме расчета координат анализировать также вывод пикселов, то могут быть отличия. В
растровых системах рисование пиксела обычно означает запись одного или нескольких бит в память, где сохраняется растр. И здесь уже не все равно — по строкам или по столбцам заполняется растр. Необходимо учитывать логическую организацию памяти компьютера, в которой хранятся биты или байты растра. Даже для компьютеров одного типа (например, персональных компьютеров) для различных поколений процессоров и памяти скорость записи по соседним адресам может существенно отличаться от скорости записи по не соседним адресам. В особенности это заметно, когда для растра используется виртуальная память с сохранением отдельных страниц на диске и (или) в оперативной памяти (RAM). При работе графических программ в среде операционной системы Windows часто случается так, что горизонтали рисуются быстрее вертикалей, поскольку в страницах памяти хранятся соседние байты. А может быть, что RAM достаточно, но скорости рисования все же различны. Например, если используется черно-белый растр в формате один бит на пиксел, то для вертикали битовая маска одинакова для всех пикселов линии, а для горизонтали маску нужно изменять на каждом шаге.
Здесь необходимо заметить, что рисование черно-белых горизонталей можно
существенно ускорить, если записывать сразу восемь соседних пикселов — байт в памяти.
Горизонтали и вертикали представляют собой частный случай линий.
Рассмотрим линию общего вида. Для нее также необходимо вычислять координаты каждого пиксела. Известно несколько методов расчетов координат точек линии.
Прямое вычисление координат
Пусть заданы координаты конечных точек отрезка прямой. Найдем координаты точки внутри отрезка (рис. 10).
Рис. 10 – Отрезок прямой линии Запишем отношения катетов для подобных треугольников:
|
x − x1 |
= |
x2 − x1 |
. |
|
|
||
|
|
|
|
|
|
|||
|
y − y1 |
|
y2 − y1 |
|
|
|||
Тогда |
|
|
|
|
|
|
|
|
x = x + ( y − y ) |
x2 − x1 |
, |
||||||
|
||||||||
1 |
1 |
|
y2 − y1 |
|
||||
|
|
|
|
|
|
|||
то есть x = f ( y). |
|
|
|
|
|
|
|
|
А также |
|
|
|
|
|
|
|
|
y = y + ( x − x ) |
y2 − y1 |
, |
|
|||||
|
|
|||||||
1 |
1 |
|
|
|
|
|
||
x2 − x1
то есть y = F (x).
В зависимости от угла наклона прямой выполняется цикл по оси x или по оси y (рис. 11).
Рис. 11 – Алгоритм вывода отрезка прямой линии Приведем пример записи этого алгоритма на языке программирования
С, C++. Для сокращения текста рассмотрим фрагмент программы, где выполняется цикл по оси x , причем x1 < x2 :
for (x = x1; x ≤ x2 ; x + +)
{
y = y1 + (( x − x1 ) ( y2 − y1 )) /( x2 − x1 ); // Пиксел( x, y);
}
Здесь все операции выполняются над целыми числами. Двойные скобки необходимы для того, чтобы деление выполнялось после умножения.
Недостатки такой программы — в цикле выполняется много лишних операций, присутствуют операции деления и умножения. Это обуславливает малую скорость работы. Относительно лишних операций в цикле: можно вынести вычисление ( y2 − y1 ) /( x2 − x1 ) из цикла, поскольку это значение не изменяется. Однако для этого уже необходимо использовать операции с плавающей точкой:
float k;
k = ( float )( y2 − y1 ) /( float )(x2 − x1 );
for (x = x1; x ≤ x2 )
{
y = y1 + ( float)( x − x1 ) k; // Пиксел( x, y);
}
Поскольку используются операции с плавающей точкой, то попробуем еще уменьшить количество операций в цикле. Если раскрыть скобки в
выражении y = y1 + (x − x1 )k ; то получим y = y1 + xk − x1k . Здесь значение
( y1 − x1k ) является константой — эти операции также вынесем из тела цикла.
float Yy, k;
k = ( float )( y2 − y1 ) /( float )(x2 − x1 ); Yy = ( float) y1 − ( float ) x1k;
for (x = x1; x ≤ x2 ; x + +)
{
y = y + ( float ) x k;
// Пиксел( x, y);
}
В цикле выполняются только две арифметические операции и
преобразование x из целого в форму с плавающей точкой.
Если рассматривать цикл вычисления y , по соответствующим
значениям xi = ( x1, x2 ,...) как итеративный процесс, то можно поставить такой вопрос: чему равна разность
Она |
равна yi +1 − yi = x1 + ( xi +1 − x1 )k − x1 − (xi − x1 )k = ( xi +1 − xi )k = k , поскольку |
xi +1 − xi = 1. |
Разность yi +1 − yi - константа, равная k . Исходя из этого, можно |
построить цикл таким образом:
float k;
k = ( float )( y2 − y1 ) /( float )(x2 − x1 ); y = y1;
for (x = x1; x ≤ x2 ; x + +)
{
// Пиксел( x, y); y+ = k;
}
{
xerr = xerr + dx; yerr = yerr + dy;
Если xerr > d , то xerr = xerr − d x = x + incX
Если yerr > d , то yerr = yerr − d y = y + incY
Пиксел( x, y)
}
Если сравнивать последний вариант с предыдущим, то последний лучше по быстродействию. Также существенно отличаются способы
вычисления координаты |
y . В последнем варианте значение |
y вычисляется |
|
прибавлением приращения k |
на каждом шаге, и на последнем шаге цикла |
||
(когда x = x2 ) должно |
стать |
y − y2 . Исходя из чисто |
математических |
соображений здесь все корректно, однако необходимо учесть, что в компьютере дробные числа представляются в формате с плавающей точкой не точно. Кроме погрешности представления чисел существует ошибка выполнения арифметических операций с плавающей точкой. Ошибка зависит от разрядности мантисс, и самая малая — для long double, но все равно не нулевая. С каждым шагом цикла ошибки накапливаются, и может так произойти, что y не равно y2 на последнем шаге. Это необходимо учитывать при использовании алгоритма.
Положительные черты прямого вычисления координат:
1)Простота, ясность построения алгоритма.
2)Возможность работы с нецелыми значениями координат отрезка.
Недостатки:
1) Использование операций с плавающей точкой или целочисленных операций умножения и деления обуславливает малую скорость. Однако это зависит от процессора и для различных типов компьютеров может быть по-
разному. В современных компьютерах, в которых процессоры используют эффективные способы ускорения (например, конвейер арифметических операций с плавающей точкой), время выполнения целочисленных операций уже не намного меньше. Для старых компьютеров разница могла быть в десятки раз, поэтому и старались разрабатывать алгоритмы только на основе целочисленных операций.
2) При вычислении координат путем добавления приращений может накапливаться ошибка вычислений координат.
Последнюю разновидность прямого вычисления координат,
рассмотренную здесь, можно было бы назвать "инкрементной". Но такое название получили алгоритмы другого типа.
Инкрементные алгоритмы
Брезенхэм предложил подход, позволяющий разрабатывать так называемые инкрементные алгоритмы растеризации. Основной целью для разработки таких алгоритмов было построение циклов вычисления координат на основе только целочисленных операций сложения/вычитания без использования умножения и деления. Уже известны инкрементные алгоритмы не только для отрезков прямых, но и для кривых линий.
Инкрементные алгоритмы выполняются как последовательное вычисление координат соседних пикселов путем добавления приращений координат.
Приращения рассчитываются на основе анализа функции погрешности. В
цикле выполняются только целочисленные операции сравнения и сложения/вычитания. Достигается повышение быстродействия для вычислений каждого пиксела по сравнению с прямым способом. Один из вариантов алгоритма Брезенхэма:
