2.3. Связь преобразований объектов

с преобразованиями координат

 

Когда пользователь графической системы видит на экране перемещающийся

объект, то, как вы считаете, что на самом деле происходит — перемещаются

объекты или система координат в обратном направлении? Например, если в  кино вы видите объекты, вращающиеся на экране по часовой стрелке, то может в ействительности это камера поворачивается против часовой стрелки?

           Преобразование объектов и преобразование систем координат тесно связаны между собой. Движение объектов можно рассматривать как движение в обратном направлении соответствующей системы координат.

 

Такая относительность для объектов отображения и систем координат дает разработчикам компьютерных систем дополнительные возможности для мо­делирования и визуализации пространственных объектов. С каждым объек­том можно связывать как собственную локальную систему координат, так и единую для нескольких объектов. Это можно использовать, например, для моделирования подвижных объектов.

Обычно, того же самого эффекта можно добиться, если использовать различ­ные подходы. Однако в одних случаях удобнее использовать преобразование координат, а в других — преобразование объектов. Не последнюю роль иг­рает сложность обоснования какого-то способа, его понятность.

Рассмотрим пример комбинированного подхода. Пусть нам нужно получить i функцию расчета координат (X, Y) для поворота вокруг центра с координата­ми 00) (рис. 2.13).

 

 

   Выше мы рассмотрели поворот относительно центра координат (0, 0). Для I решения нашей задачи введем новую систему координат (х',0',уг) с центром в точке 0, уо):

 

 

 

            Рассмотрим второй пример. Нашей задачей будет вывод формул параметри­ческого описания поверхности тора. Изобразим тор следующим образом (рис. 2.14).

           Для произвольной точки Р, лежащей на поверхности тора, требуется выра­зить координаты (х, у, z) через константы, описывающие размеры фигуры, а также через некоторые параметры. Для поверхности в трехмерном простран­стве необходимо использовать два параметра. В качестве таковых выберем угловые величины: φ (широта) и ω (долгота).

 

 

 

            Непосредственное определение координат точки Р представляется сложным, поэтому искомые координаты будем искать несколькими шагами преобразо­ваний. Рассмотрим окружность, лежащую в плоскости z0y, центр этой ок­ружности совпадает с центром координат. Координаты точки Р" с широтой φ составляют

 

 

где r— малый радиус тора.

 

           Теперь перенесем окружность на расстояние R (большой радиус тора) по оси у в той же плоскости z0y. Получим точку Р Ее координаты:

 

 

 

Окружность, которой принадлежит точка Р', является геометрическим ме­стом точек тора с нулевой долготой и). Если точку Р' повернуть на угол а), то получим искомую точку Р поверхности тора с координатами

 

 

Эту задачу можно было бы решить, используя преобразование координат. Подобный случай мы рассмотрим ниже (пример studex8 в разделе програм­мирования). Однако, как представляется, более ясным здесь выглядит ис­пользование операций перемещения точки (из положения Р" в Р', а за­тем в Р).

 

2.4. Проекции

 

В настоящее время наиболее распространены устройства отображения, кото­рые синтезируют изображения на плоскости— экране дисплея или бумаге. Устройства, которые создают истинно объемные изображения, пока доста­точно редки. Но все чаще появляются сведения о таких разработках, напри­мер, об объемных дисплеях [37] или даже о трехмерных принтерах [45].

При использовании любых графических устройств обычно используют про­екции. Проекция задает способ отображения объектов на графическом уст­ройстве. Мы будем рассматривать только проекции на плоскость.

 

Мировые и экранные координаты

 

При отображении пространственных объектов на экране или на листе бумаги с помощью принтера необходимо знать координаты объектов. Мы рассмот­рим две системы координат. Первая — мировые координаты, которые опи­сывают истинное положение объектов в пространстве с заданной точностью. Другая — система координат устройства изображения, в котором осуществ­ляется вывод изображения объектов в заданной проекции.

       Пусть мировые координаты будут трехмерными декартовыми координатами. Где должен размещаться центр координат и какими будут единицы измерения вдоль каждой оси, пока для нас не очень важно. Важно то, что для ото­бражения мы будем знать какие-то числовые значения координат отображае­мых объектов.

Для получения изображения в определенной проекции необходимо рассчи­тать координаты проекции. Из них можно получить координаты для графи­ческого устройства— назовем их экранными координатами. Для синтеза изображения на плоскости достаточно двумерной системы координат. Одна­ко в некоторых алгоритмах визуализации используются трехмерные экран­ные координаты, например, в алгоритме Z-буфера.

 

Основные типы проекций

 

В компьютерной графике наиболее распространены параллельная и цент­ральная проекции (рис. 2.15).

 

 

Для центральной проекции (также называемой перспективной) лучи проеци­рования исходят из одной точки, размещенной на конечном расстоянии от объектов и плоскости проецирования. Для параллельной проекции лучи про­ецирования параллельны.

 

Аксонометрическая проекция

 

          Аксонометрическая проекция — разновидность параллельной проекции. Для нее все лучи проецирования располагаются под прямым углом к плоскости проецирования (рис. 2.16).

Зададим положения плоскости проецирования с помощью двух углов — а и β Расположим камеру так, чтобы проекция оси z на плоскости проецирова­ния  X0Y была бы вертикальной линией (параллельной оси 0Y).

 

 

Для того чтобы найти соотношения между координатами (х, у, z) и (X, Y, Z) для любой точки в трехмерном пространстве, рассмотрим преобразования системы координат (х, у, z) в систему (X, Y, Z). Зададим такое преобразование двумя шагами.

 

1-й шаг. Поворот системы координат относительно оси z на угол а. Такой поворот осей описывается матрицей

 

 

 

2-й шаг. Поворачиваем систему координат (V, у', z') относительно оси х' на угол β— получаем координаты (X, Y, Z). Матрица поворота

 

 

 

 

 

 

           

 

 

Как вы считаете, будет ли получена та же проекция, если описывать преобра­зования координат теми же двумя шагами, но в другой последовательности — сначала поворот системы координат относительно оси х на угол Д а потом поворот системы координат относительно оси z' на угол а? И будут ли вер­тикальные линии в системе координат (x,y,z) рисоваться также вертикалями в системе координат (X, Y, Z)? Иначе говоря, выполняется ли А x В = ВxА?

        Обратное преобразование координат аксонометрической проекции. Для того, чтобы координаты проекции (X, Y, Z) преобразовать в мировые коорди­наты (х, у, z), нужно проделать обратную последовательность поворотов. Вначале выполнить поворот на угол -Д а затем — поворот на угол -а. Запи­шем обратное преобразование в матричном виде

 

 

 

 

 

 

 

 

 

 

Перспективная проекция

 

Перспективную проекцию (рис. 2.17) сначала рассмотрим при вертикальном расположении камеры, когда а = β = 0. Такую проекцию можно себе пред­ставить как изображение на стекле, через которое смотрит наблюдатель, рас­положенный сверху в точке {х, у, z) = (0, 0, zk). Здесь плоскость проецирова­ния параллельна плоскости 0 у).

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

 

 

 

 

 

 

 

 

 

Обратите внимание на то, что здесь коэффициенты матрицы зависят от коор­динаты z (в знаменателе дробей). Это означает, что преобразование коор­динат является нелинейным (а точнее, дробно-линейным), оно относится к классу проективных преобразований.

     Теперь рассмотрим общий случай — для произвольных углов наклона каме­ры (а и β) так же, как и для параллельной аксонометрической проекции. Пусть (х', у', z’) — координаты для системы координат, повернутой относи­тельно начальной системы (х, у, z) на углы а и β.

 

 

Преобразование в целом нелинейное. Его нельзя описать одной матрицей коэффициентов-констант для всех объектов сцены (хотя для преобразования координат можно использовать и матричную форму).

Для такой перспективной проекции плоскость проецирования перпендику­лярна лучу, исходящему из центра (х, у, z) = (0, 0, 0) и наклоненному под углом а, р. Если камеру отдалять от центра координат, то центральная проек­ция видоизменяется. Когда камера в бесконечности, центральная проекция вырождается в параллельную проекцию.

Укажем основные свойства перспективного преобразования. В центральной проекции:

□ не сохраняется отношение длин и площадей;

□ прямые линии изображаются прямыми линиями;

 □ параллельные прямые изображаются сходящимися в одной точке.

Последнее свойство широко используется в начертательной геометрии для ручного рисования на бумаге. Проиллюстрируем это на примере каркаса домика (рис. 2.18).

Существуют и другие перспективные проекции, которые различаются поло­жением плоскости проецирования и местом точки схождения лучей проеци­рования. Кроме того, проецирование может осуществляться не на плоскость, а, например, на сферическую или цилиндрическую поверхность.

            Рассмотрим косоугольную проекцию, для которой лучи проецирования не перпендикулярны плоскости проецирования. Основная идея такой проекции — камера поднята на высоту h с сохранением вертикального положения плоскости проектирования (рис. 2.19).

 

 

 

 

Получить такую проекцию можно следующим способом:

1.  Выполняем поворот вокруг оси z на угол а.

2.  Заменяем z'на-у', а у'на z'.

3.  Выполняем сдвиг системы координат вверх на высоту камеры h.

            4.   В плоскости 1, у', 0) строим перспективную проекцию уже рассмотрен­ным выше способом (точка схода лучей на оси z).-

 

Преобразование координат может быть описано таким образом. Сначала оп­ределяются (х', у', z1).

 

 

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

Примеры изображений в различных проекциях. Приведем примеры изо­бражений одинаковых объектов в различных проекциях. В качестве объектов будут кубы одинакового размера. Положение камеры определим углами наклонна  a = 27°,β = 70°.     Пример аксонометрической проекции приведен на рис. 2.20.

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

В оптических системах известно понятие фокусного расстояния. Чем больше фокусное расстояние объектива, тем меньше восприятие перспективы (рис. 2.21), и наоборот, для короткофокусных объективов перспектива наибольшая (рис. 2.22). Данный эффект вы, наверное, уже замечали, если занимались съемками видеокамерой или фотоаппаратом. В наших примерах можно на­блюдать некоторое соответствие величины расстояния от камеры до плоско­сти проецирования (zK zпл) и фокусного расстояния объектива. Это соответ­ствие, однако, условно, аналогия с оптическими системами здесь неполная.

          Для приведенных ниже примеров (рис. 2.21, 2.22) zпл = 700. Углы наклона камеры а = 27°, β= 70°.

 

 

            В случае короткофокусной камеры (zK = 1200) восприятие перспективы наи­более заметно для кубов, которые расположены ближе всего к камере. Вер­тикальные линии объектов не являются вертикалями на проекции (объекты "разваливаются").

               Рассмотрим примеры косоугольной проекции (рис. 2.23, 2.24). Для нее вер­тикальные линии объектов сохраняют вертикальное расположение на проек­ции. Положение камеры (точки схождения лучей проецирования) описывает­ся углом поворота а = 27° и высотой подъема h = 500. Плоскость проециро­вания параллельна плоскости (х'Оу') и располагается на расстоянии zпл = 700.

 

 

 

 

 

Рассмотрим еще один пример изображения в центральной проекции — текст в стиле фильма "Звездные войны":

Отображение в окне

 

Как мы уже рассмотрели выше, отображение на плоскость проецирования соответствует некоторому преобразованию координат. Это преобразование координат различно для разных типов проекции, но, так или иначе, осущест­вляется переход к новой системе координат — координатам проецирования. Координаты проецирования могут быть использованы для формирования изображения с помощью устройства графического вывода. Однако при этом могут понадобиться дополнительные преобразования, поскольку система ко­ординат в плоскости проецирования может не совпадать с системой коорди­нат устройства отображения. Например, должны отображаться объекты, из­меряемые в километрах, а в растровом дисплее единицей измерения является пиксел. Как выразить километры в пикселах?

Кроме того, вы, наверное, видели, что на экране компьютера можно показы­вать увеличенное, уменьшенное изображение объектов, а также их переме­щать. Как это делается?

Введем обозначения. Пусть (Хэ, YЭ, Zэ) — это экранные координаты объектов в графическом устройстве отображения. Заметим, что не следует восприни­мать слово "экранные" так, будто речь идет только о дисплеях — все ниже­следующее можно отнести и к любым другим устройствам, использующим декартову систему координат. Координаты проецирования обозначим здесь как {X, Y, Z).

                               

            Назовем окном прямоугольную область вывода с экранными координатамиОбычно  приходится отображать в окне или всю сцену, или отдельную ее часть (рис. 2.25).

 

 

Преобразование координат проекции в экранные координаты можно задать как растяжение/сжатие и сдвиг:

 

Такое преобразование сохраняет пропорции объектов благодаря одинаково­му коэффициенту растяжения/сжатия (К) для всех координат. Заметим, что для плоского отображения можно отбросить координату Z.

Рассмотрим, как можно вычислить К, dx и dy. Например, необходимо впи­сать все изображение сцены в окно заданных размеров. Условие вписывания можно определить так:

 

 

 

 

 

 

            Если значение Кх или значение Ку равно бесконечности, то его необходимо отбросить. Если оба — то значение Kmin можно задать равным единице. Для того чтобы изображение в окне имело наибольший размер, выберем К = Kmin. Теперь можно найти dx. Из неравенства (1):

 

При таких значениях dx и dy центр сцены будет в центре окна.

          В других случаях, когда в окне необходимо показывать с соответствующим масштабом лишь часть сцены, можно прямо задавать числовые значения масштаба (К) и координаты сдвига (dx, dy). При проектировании интерфейса графической системы желательно ограничить выбор К, dx, dy диапазоном допустимых значений.

            В графических системах используются разнообразные способы задания мас­штаба отображения и определения границ сцены для показа в окне просмот­ра. Например, для сдвига часто используют ползунки скроллинга. Также можно указывать курсором точку на сцене, и затем эта точка становится цен­тральной точкой окна. Или можно очертить прямоугольник, выделяя грани­цы фрагмента сцены, — тогда этот фрагмент затем будет вписан в окно.

 И так далее. Все эти способы отображения основываются на растяже­нии/сжатии (масштабировании), а также сдвиге, и описываются аффинным преобразованием координат.

 

2.5. Выводы

 

Представим цепочку преобразований координат от мировых к экранным сле­дующим образом (рис. 2.26):

 

 

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

 

 

            Важно то, что для аксонометрической проекции коэффициенты матрицы — это константы, одинаковые для всех точек трехмерного пространства. Это дает возможность свести к минимуму вычисления координат в цикле графи­ческого вывода. В этом плане можно отметить крайний случай, когда миро­вые координаты совпадают с экранными — вообще нет никаких преобразо­ваний координат. Например, координаты объектов задаются в пикселах экра­на. Такое часто встречается, например, в двухмерной графике.    Для перспективной (центральной) проекции коэффициенты матрицы проеци­рования на плоскость не являются константами — они зависят от Z. Это де­лает нецелесообразным запись цепочки преобразований в виде одной матри­цы и, как следствие этого, усложняется расчет координат в сравнении с па­раллельной проекцией.

 

 

           

                 Для центральной проекции иногда используют матричную форму с примене­нием обобщенных однородных нелинейных координат [28, 32]. В качестве мировых и экранных координат нами была использована трех­мерная ортогональная система. В компьютерных графических системах так­же используются другие системы координат и иные проекции. В особенности это касается систем, которые моделируют объекты, располагающиеся на по­верхности Земли. С этими вопросами можно ознакомиться в многочисленной литературе по геодезии и картографии, а также в работах, посвященных гео­информационным системам.

 

ГЛАВА 3

 

Базовые

растровые алгоритмы

 

            Сформировать растровое изображение можно по-разному. Для того чтобы  создать изображение на растровом дисплее, можно просто скопировать гото­вый растр в видеопамять. Этот растр может быть получен, например, с помощью сканера или цифрового фотоаппарата. А можно создавать изображение объекта путем последовательного рисования отдельных простых элементов.

Простые элементы, из которых складываются сложные объекты, будем назы­вать графическими примитивами. Их можно встретить повсюду. Например, для построения изображения используется некоторый набор примитивов, ко­торые поддерживаются определенными графическими устройствами вывода. Графические примитивы также можно применять для описания пространст­венных объектов в базе данных компьютерной системы— модели объектов. Могут использоваться различные множества примитивов для модели и для Алгоритмов отображения. Удобно, когда эти множества совпадают, тогда уп­рощается процесс отображения.

Простейшим и, вместе с тем, наиболее универсальным растровым графиче­ским примитивом является пиксел. Любое растровое изображение можно на­рисовать по пикселем, но это сложно и долго. Необходимы больше сложные элементы, для которых рисуются сразу несколько пикселов.

Рассмотрим графические примитивы, которые используются наиболее часто : в современных графических системах, — это линии и фигуры.

 

3.1. Алгоритмы вывода прямой линии

 

Рассмотрим растровые алгоритмы для отрезков прямой линии. Предполо­жим, что заданы координаты (x 1 ,y 1 - х2,у2) концов отрезка прямой. Для вывода линии необходимо закрасить в определенный цвет все пикселы вдоль линии. Для того чтобы закрасить каждый пиксел, необходимо знать его ко­ординаты.

Наиболее просто нарисовать отрезок горизонтальной линии:

 

Вычисление текущих координат пиксела здесь выполняется как приращение по х (необходимо, чтобы х1 <-х2), а вывод пиксела обеспечивается функцией Пиксел(). Поскольку в языке С, C++ для названия функции нельзя использо­вать кириллицу, то будем дальше использовать ее как комментарий.

Аналогично рисуется отрезок вертикали:

 

Как видим, в цикле выполняются простейшие операции над целыми числа­ми — приращение на единицу и проверка на "< =". Поэтому операция рисо­вания отрезка выполняется быстро и просто. Ее используют как базовую операцию для других операций, например, в алгоритмах заполнения плоско­сти полигонов.

           Можно поставить такой вопрос: какая линия рисуется быстрее — горизон­таль или вертикаль? На первый взгляд — одинаково быстро. Если учитывать только математические аспекты, то скорость должна быть одной и той же при одинаковой длине линий, поскольку в обоих случаях выполняется равное количество идентичных операций. Однако если кроме расчета координат анализировать также вывод пикселов, то могут быть отличия. В растровых системах рисование пиксела обычно означает запись одного или нескольких бит в память, где сохраняется растр. И здесь уже не все равно — по строкам или по столбцам заполняется растр. Необходимо учитывать логическую ор­ганизацию памяти компьютера, в которой хранятся, биты или байты растра.

        Даже для компьютеров одного типа (например, персональных компьютеров) для различных поколений процессоров и памяти скорость записи по сосед­ним адресам может существенно отличаться от скорости записи по не сосед­ним адресам [40]. В особенности это заметно, когда для растра используется виртуальная память с сохранением отдельных страниц на диске и (или) в оперативной памяти (RAM). При работе графических программ в среде опе­рационной системы Windows часто случается так, что горизонтали рисуются быстрее вертикалей, поскольку в страницах памяти хранятся соседние байты. А может быть, что RAM достаточно, но скорости рисования все же различны.

Например, если используется черно-белый растр в формате один бит на пиксел, то для вертикали битовая маска одинакова для всех пикселов линии, а для горизонтали маску нужно изменять на каждом шаге. Здесь необходимо  заметить, что рисование черно-белых горизонталей можно существенно ускорить, если записывать сразу восемь соседних пикселов — байт в памяти.

            Горизонтали и вертикали представляют собой частный случай линий. Рассмотрим линию общего вида. Для нее также необходимо вычислять координат каждого пиксела. Известно несколько методов расчетов координат точек линии.

 

Прямое вычисление координат

 

 Пусть заданы координаты конечных точек отрезка прямой. Найдем координаты точки внутри отрезка (рис. 3.1).

 

 

 

 

В зависимости от угла наклона прямой выполняется цикл по оси х или по у

(рис. 3.2).

 

            Приведем пример записи этого алгоритма на компьютерном языке програм­мирования С, C++. Для сокращения текста рассмотрим фрагмент программы, где выполняется цикл по оси х, причем х1 <х2:

Здесь все операции выполняются над целыми числами. Двойные скобки не­обходимы для того, чтобы деление выполнялось после умножения. Недос­татки такой программы — в цикле выполняется много лишних операций, присутствуют операции деления и умножения. Это обуславливает малую скорость работы. Относительно лишних операций в цикле. Можно вынести вычисление (у2 - у 1)/(х2 – х1) из цикла, поскольку это значение не изменя­ется. Однако для этого уже необходимо использовать операции с плавающей точкой:

      Поскольку мы решили использовать операции с плавающей точкой, то по­пробуем еще уменьшить количество операций в цикле. Если раскрыть скобки в выражении у = у1 + (х – х1)-к; то получим у = у1 + х.к- х1 .к. Здесь значе­ние (yl х1.к) является константой— эти операции также вынесем из тела цикла.

                        

 

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

Если рассматривать цикл вычисления у, по соответствующим значениям хi = х1, х1 +1, ... , х2 как итеративный процесс, то можно поставить такой вопрос: чему равна разность (уi+1 — уi)1 Она равна уi+1 —уi = xl + (хi+1xl)k-xl- (xi – х1) к = (хi+1xi) к = к; поскольку xi+1xi =1. Разность (yi+1уi) — константа, равная к. Исходя из этого, можно построить цикл таким образом:

В теле цикла есть только одна операция для вычисления координаты у (если  не учитывать <=   и ++).

           Если сравнивать последний вариант с предыдущим, то последний лучше по быстродействию. Также существенно отличаются способы вычисления коор­динаты у. В последнем варианте значение у вычисляется прибавлением при­ращения к на каждом шаге, и на последнем шаге цикла (когда х=х2) долж­но стать у=у2. Исходя из чисто математических соображений здесь все кор­ректно, однако необходимо учесть, что в компьютере дробные числа представляются в формате с плавающей точкой не точно. Кроме погрешно­сти представления чисел существует ошибка выполнения арифметических операций с плавающей точкой. Ошибка зависит от разрядности мантисс, и самая малая — для long double, но все равно не нулевая. С каждым шагом цикла ошибки накапливаются, и может так произойти, что у не равно у2 на последнем шаге. Это необходимо учитывать при использовании алгоритма.

          Положительные черты прямого вычисления координат:

1.  Простота, ясность построения алгоритма.

2.   Возможность работы с нецелыми значениями координат отрезка. (Как вы считаете, какой вариант из четырех корректно вычисляет координаты пикселов, если х1, у1, х2 иу>2 — дробные?)

Недостатки:

1.  Использование операций с плавающей точкой или целочисленных опера­ций умножения и деления обуславливает малую скорость. Однако это за­висит от процессора и для различных типов компьютеров может быть по-разному. В современных компьютерах, в которых процессоры используют эффективные способы ускорения (например, конвейер арифметических операций с плавающей точкой), время выполнения целочисленных опера­ций уже не намного меньше. Для старых компьютеров разница могла быть в десятки раз, поэтому и старались разрабатывать алгоритмы только на основе целочисленных операций.

2.   При вычислении координат путем добавления приращений может накап­ливаться ошибка вычислений координат.

Последнюю разновидность прямого вычисления координат, рассмотренную здесь, можно было бы назвать "инкрементной". Но такое название получили алгоритмы другого типа.

 

Инкрементные алгоритмы

 

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

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

 

                            

 

 

 

Рассмотрим пример работы приведенного выше алгоритма Брезенхэма для отрезка (х1у1 - х2у2) = (2,3 - 8,6). Этот алгоритм восьмисвязный, то есть при вычислении приращений координат для перехода к соседнему пикселу возможны восемь случаев (рис. 3.3).

 

  Известны также четырехсвязные алгоритмы (рис. 3.4).

  Четырехсвязные алгоритмы проще, но они генерируют менее качественное

   изображение линий за большее количество тактов роботы. Для приведенного

  примера четырехсвязный алгоритм работает 10 тактов, а восьмисвязный —

  только 7.

 

3.2. Алгоритм вывода окружности

 

Для вывода контура круга можно использовать соотношение между коорди­натами X и Y для точек круга X2 + Y2 = R2 и построить алгоритм прямого вычисления координат. Однако тогда необходимо вычислять квадратный ко­рень, а это в цифровом компьютере выполняется медленно.

          Дадим запись инкрементного алгоритма Брезенхэма на языке C++:

 

 

 

 

 

 

 

 

 

             В этом алгоритме использована симметрия круга — в основном цикле вычисляются координаты точек круга только для одного октанта и сразу рису­ются восемь симметрично расположенных пикселов [33].

 

3.3. Алгоритм вывода эллипса

 

          Инкрементный алгоритм для эллипса подобен алгоритму для круга, но более сложный.Этот алгоритм приведен в [33].

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

В этом алгоритме использована симметрия эллипса по квадрантам (рис. 3.5).

Алгоритм состоит из двух циклов. Сначала от х=0 до x=dxt, где

 

 

 

3.4. Кривая Безье

 

 Разработана математиком Пьером Безъе. Кривые и поверхности Безье были ; использованы в 60-х годах компанией "Рено" для компьютерного проектирования формы кузовов автомобилей. В настоящее время они широко используются в компьютерной графике.

 Кривые Безье описываются в параметрической форме:

 

Значение t  выступает как параметр, которому отвечают координаты отдель­ной точки линии. Параметрическая форма описания может быть более удоб­ной для некоторых кривых, чем задание в виде функции y=f(x). Это потому, что функцияƒ(х) может быть намного сложнее, чем Px{f) и Py(t), кроме того, f(x) может быть неоднозначной.

             Многочлены Безье для Р х и Ру имеют такой вид:

 

 

 

 

 

 

     m = 3 (по четырем точкам, кубическая, рис. 3.8). Используется довольно час­то, в особенности в сплайновых кривых.

 

 

 

 

 

 

Геометрический алгоритм для кривой Безье

 

Этот алгоритм позволяет вычислить координаты (х, у) точки кривой Безье по значению параметра t. Алгоритм описан в [19].

1.  Каждая сторона контура многоугольника, проходящего по точкам-ориен­тирам, делится пропорционально значению t.

2.  Точки деления соединяются отрезками прямых и образуют новый много­угольник. Количество узлов нового контура на единицу меньше, чем  ко­личество узлов предыдущего контура.

3.  Стороны нового контура снова делятся пропорционально значению t. И так далее. Это продолжается до тех пор, пока не будет получена единст­венная "речка деления. Эта точка и будет точкой кривой Безье (рис. 3.9).

 

 

Результат работы алгоритма— координаты одной точки кривой Безье — записываются в R[0].

 

3.5. Алгоритмы вывода фигур

 

Фигурой здесь будем считать плоский геометрический объект, который со­стоит из линий контура и точек, которые содержатся внутри контура.

В общем случае линий контура может быть несколько — когда объект имеет внутри пустоты. В этом случае для описания таких фигур необходимы два и более контуров (рис. 3.10).

 

            В некоторых графических системах одним объектом может считаться и более сложная многоконтурная фигура — совокупность островов с пустотами.

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

Для вывода точек заполнения известны методы, разделяющиеся в зависимо­сти от использования контура на два типа— алгоритмы закрашивания от внутренней точки к границам произвольного контура и алгоритмы, которые используют математическое описание контура.

Алгоритмы закрашивания

 

Рассмотрим алгоритмы закрашивания произвольного контура, который уже нарисован в растре. Сначала находится пиксел внутри контура фигуры. Цвет этого пиксела изменяем наружный цвет заполнения. Потом проводится ана­лиз цветов всех соседних пикселов. Если цвет некоторого соседнего пиксела не равен цвету границы контура или цвету заполнения, то цвет этого пиксела изменяется на цвет заполнения. Потом анализируется цвет пикселов, сосед­них с предыдущими. И так далее, до тех пор, пока внутри контура все пиксе­лы не перекрасятся в цвет заполнения.

Пикселы контура — это граница, за которую нельзя выходить в ходе после­довательного перебора всех соседних пикселов. Соседними могут считаться только четыре пиксела (справа, слева, сверху и снизу — четырехсвязность), или все восемь пикселов (восьмисвязность). Не всякий контур может слу­жить границей закрашивания (рис. 3.11).                                                         

                   Простейший алгоритм закрашивания. Для всех алгоритмов закрашивания нужно задавать начальную точку внутри контура с координатами  xo, yo. Про­стейший алгоритм можно описать так [32]:

            Такое определение  функции  является  рекурсивным.  Рекурсия  позволяет „ упростить запись некоторых алгоритмов. Но для этого алгоритма рекурсия порождает   существенные   проблемы —   рекурсивные   вызовы    функции закрашивание () делаются для каждого пиксела, что обычно приводит к переполнению стека в ходе выполнения компьютерной программы. Практика по­казывает, что этот алгоритм неприемлем для фигур площадью в тысячу и больше пикселов.

Можно построить подобный алгоритм и без рекурсии, если вместо стека компьютера использовать отдельные массивы. Тогда стек не переполняется.

        Волновой алгоритм закрашивания. Алгоритм был разработан авторами работы [38] и предназначался для расчета центра тяжести объектов по соот­ветствующим изображениям. Идея была навеяна волновым алгоритмом по­иска трассы на графе, известным в САПР электронных схем. Суть подобных алгоритмов состоит в том, что для начальной точки (вершины на графе) на­ходятся соседние точки (другие вершины), которые отвечают двум условиям: во-первых — эти вершины связаны с начальной; во-вторых — эти вершины еще не отмечены, то есть они рассматриваются впервые. Соседние вершины  текущей итерации отмечаются в массиве описания вершин, и каждая из них становится текущей точкой для поиска новых соседних вершин в следующей итерации. Если в специальном массиве отмечать каждую вершину номером итерации, то когда будет достигнута конечная точка, можно совершить об­ратный цикл — от конечной точки к начальной по убыванию номеров итера­ций. В ходе обратного цикла и находятся все кратчайшие пути (если их не­сколько) между двумя заданными точками на графе. Подобный алгоритм можно также использовать, например, для поиска всех нужных файлов на диске. Относительно закрашивания растровых фигур, то здесь вершинами графа являются пикселы, а отметка пройденных пикселов делается прямо в растре цветом закрашивания. Как видим, это почти полностью повторяет идею предыдущего простейшего алгоритма, однако здесь мы не будем ис­пользовать рекурсию. Это обуславливает совсем другую последовательность обработки пикселов при закрашивании.

Запишем волновой алгоритм закрашивания на языке C++ с использованием графических функций API Windows.

 

 

            Здесь цвет закрашивания и цвет контура — черный цвет (код 0). Пример ра­боты алгоритма приведен на рис. 3.12.

От начальной точки распространяется волна пикселов закрашивания в виде ромба. В одном цикле onestep закрашиваются пикселы вдоль линии пери­метра ромба (или нескольких ромбов в зависимости от сложности фигуры).

 В качестве рабочих массивов для текущего сохранения координат пикселов

 фронтов волн использованы динамические массивы емкостью по 10 000 эле­ментов. Максимальная емкость массивов обуславливается размерами контура и рассчитывается эмпирически.

 Достаточно просто модифицировать приведенный алгоритм для случая отли­чающихся цветов контура и заполнения.

             Необходимо заметить, что этот алгоритм не является самым быстрым из из­вестных алгоритмов закрашивания, особенно если для его реализации использовать медленную функцию setPixel для рисования отдельных пикселов в программах для Windows. Большую скорость закрашивания обеспечивают алгоритмы, которые обрабатывают не отдельные пикселы, а сразу большие блоки из многих пикселов, которые образовывают прямоугольники или линии.

 

 

Алгоритм закрашивания линиями. Данный алгоритм получил широкое распространение в компьютерной графике. От приведенного ранее простей­шего рекурсивного алгоритма он отличается тем, что на каждом шаге закра­шивания рисуется горизонтальная линия, которая размещается между пиксе­лами контура. Алгоритм рекурсивный, но поскольку вызов функции осуще­ствляется для линии, а не для каждого отдельного пиксела, то количество вложенных вызовов уменьшается пропорционально длине линии. Это уменьшает нагрузку на стековую память компьютера.

         Приведем запись алгоритма на языке C++. Этот пример взят из [32], незна­чительно усовершенствован и переработан для использования в среде Windows.

 

 

 

 

 

                                                                                                     

 

Так же, но немного быстрее, работает функция FioodFiii API Windows. Раз­ница в быстродействии обусловлена тем, что для LineFiii мы использовали стандартные функции для интерфейса прикладных программ, a FioodFiii оптимизирована на системном уровне

 

 

Алгоритмы заполнения, которые используют

математическое описание контура

 

Математическим описанием контура фигуры может служить уравнение у = f(x) для окружности, эллипса или другой кривой. Для многоугольника (полигона) контур задается множеством координат вершин 1 у1). Возможны и другие формы описания контура. В любом случае алгоритмы данного клас­са не предусматривают обязательное предварительное создание пикселов контура растра — контур может совсем не выводиться в растр. Рассмотрим некоторые из подобных алгоритмов заполнения.

Заполнение прямоугольников. Среди всех фигур прямоугольник заполнять наиболее просто. Если прямоугольник задан координатами противополож­ных углов, например, левого верхнего 1 у1) и правого нижнего 2 у2), то­гда алгоритм может заключаться в последовательном рисовании горизон­тальных линий заданного цвета (рис. 3.14).

Заполнение круга. Для заполнения круга можно использовать алгоритм вы­вода контура, который мы уже рассмотрели в разд. 3.2. В процессе выполне­ния этого алгоритма последовательно вычисляются координаты пикселов контура в границах одного октанта. Для заполнения надлежит выводить го­ризонтали, которые соединяют пары точек на контуре, расположенные сим­метрично относительно оси у (рис. 3.15).

              Так же может быть построен и алгоритм заполнения эллипса.

            Заполнение полигонов. Контур полигона определяется вершинами, которые соединены отрезками прямых (рис. 3.16). Это— векторная форма задания фигуры.

 

           Рассмотрим один из наиболее популярных алгоритмов заполнения полигона. Его основная идея — закрашивание фигуры отрезками прямых линий. Наи­более удобно использовать горизонтали. Алгоритм представляет собою цикл вдоль оси у, в ходе этого цикла выполняется поиск точек сечения линии кон­тура с соответствующими горизонталями. Этот алгоритм получил назва­ние XY [33]:

 

            В этом алгоритме использовано свойство топологии контура фигуры. Оно состоит в том, что любая прямая линия пересекает любой замкнутый контур четное количество раз (рис. 3.17). Для выпуклых фигур точек пересечения с любой прямой всегда две. Таким образом, на шаге 3 этого алгоритма в мас­сив { xj} всегда должно записываться парное число точек сечения.

 

При нахождении точек пересечения горизонтали с контуром необходимо принимать во внимание особые точки. Если горизонталь имеет координату (у), совпадающую с уi вершины Рi тогда надлежит анализировать то, как горизонталь проходит через вершину. Если горизонталь при этом пересекает контур, как, например, в вершинах Рo или Р4 то в массив записывается одна точка сечения. Если горизонталь касается вершины контура (в этом случае вершина соответствует локальному минимуму или максимуму как, например, в вершинах Р1 Р2, Рз или Р5), тогда координата точки касания или не записы­вается, или записывается в массив два раза. Это условие четности количества точек пересечения, хранящихся в массиве {xj}.

           Процедура определения точек пересечения контура с горизонтальной раз­верткой, учитывая анализ на локальный максимум, может быть достаточно сложной. Это замедляет работу. Радикальным решением для упрощения по­иска точек сечения может быть смещение координат вершин контура или горизонталей заполнения таким образом, чтобы ни одна горизонталь не попала в вершину. Смещение можно выполнять различными способами, на­пример, ввести в растр дробные координаты для горизонталей: утiп+ 0.5, ymin  + 1.5, ..., ymax-0,5.

Но такое упрощение процедуры нахождения точек пересечения приводит к искажению формы полигона.

Для определения координат (х) точек пересечения для каждой горизонтали необходимо перебирать все п отрезков контура. Координата пересечения от­резка piрk с горизонталью у равна

где к— коэффициент пропорциональности, п — число вершин полигона.

Возможны модификации приведенного алгоритма для ускорения его работы. Например, можно принять во внимание то, что каждая горизонталь в боль­шинстве случаев пересекает небольшое количество ребер контура. Поэтому если при поиске точек сечения делать предварительный отбор ребер, которые находятся вокруг каждой горизонтали, то можно добиться уменьшения количества тактов работы с Nгop = к. п до к .пр, где пр — число отобранных ребер. Например, разделим диапазон уmin - ymax пополам. Если для диапазона от ymin до уср составить список отрезков (или вершин), которые попадают в этот диапазон, то в этот список будет включено  приблизительно вдвое меньшее р количество, чем для всего диапазона от ymin до утах. Почему приблизительно— ибо это зависит от формы полигона. Таким образом, при работе алго­ритма для каждой горизонтали в диапазоне ymin  до уср уже нужно не к.п тактов, а  ~ к .п/2.

         Аналогично для диапазона уср ymax  также можно составить   список ребер, (который также будет почти вдвое меньшим. Если принять, что подсчеты для каждой горизонтали теперь выполняются за вдвое меньшее количество тактов, а именно за (Nгop/2), то общее число тактов:

 

Такой способ повышения быстродействия эффективен для большого количе­ства вершин. Контур можно делить не пополам, а на более мелкие части — соответственно повышается скорость.

Приведенные выше алгоритмы заполнения могут быть использованы не только для рисования фигур. На основе алгоритмов заполнения могут быть разработаны алгоритмы для других целей. Например, для определения пло­щади фигуры, если считать плрщадь пропорциональной количеству пикселов заполнения. Или, например, алгоритм для поиска объектов по внутренней точке — эта операция часто используется в векторных графических редак­торах.

 

3.6. Стиль линии. Перо

 

Что общее и что разное у объектов, изображенных на рис. 3.18?

 

 

                  Общее — линия оси, описывающая каждый из восьми объектов. Разное — элементы вдоль линии оси. Для описания различных по виду изображений на основе линий используют термин стиль линий или перо. Термин перо ино­гда делает более понятной суть алгоритма вывода линий для некоторых сти­лей — в особенности для толстых линий. Например, если для тонкой непре­рывной линии перо соответствует одному пикселу, то для толстых линий пе­ро можно представить себе как фигуру или отрезок линии, который скользит вдоль оси линии, оставляя за собой след (табл. 3.1).

 

 

 

Алгоритмы вывода толстой линии

 

 Взяв за основу любой алгоритм вывода обычных тонких линий (например, алгоритм Брезенхэма), запишем его в следующем обобщенном виде:

 

Можно представить себе такой алгоритм, как цикл, в котором определяются координаты (х,у) каждого пиксела. Этот алгоритм можно модифицировать для вывода толстой линии следующим образом:

 

 

Вместо вывода отдельного пиксела стоит вывод фигуры или линии, соответ­ствующей перу — прямоугольник, круг, отрезок прямой.

 Такой подход к разработке алгоритмов толстых линий имеет преимущества и недостатки. Преимущество— можно прямо использовать эффективные ал­горитмы для вычисления координат точек линии оси, например, алгоритмы ррезенхэма. Недостаток — неэффективность для некоторых форм пера. Для перьев, которые соответствуют фигурам с заполнением, количество тактов работы алгоритма пропорционально квадрату толщины линии. При этом большинство пикселов многократно закрашивается в одних и тех же точках (рис. 3.19).

Такие алгоритмы более эффективны для перьев в виде отрезков линий. В этом случае каждый пиксел рисуется только один раз. Но здесь важным

является наклон изображаемой линии. Ширина пера зависит от наклона (рис. 3.20).

            Очевидно, горизонтальное перо не может рисовать толстую горизонтальную линию.

            Для вывода толстых линий с помощью пера в качестве отрезка линии чаще всего используются отрезки горизонтальной или вертикальной линий, ре­же — диагональные отрезки под углом 45 градусов. Целесообразность ис­пользования такого способа определяется большой скоростью вывода гори­зонтальных и вертикальных отрезков прямой. Для того чтобы достигнуть минимального количества тактов вывода, толстые линии, которые по накло­ну ближе к вертикальным, рисуют горизонтальным пером, а пологие ли­нии — вертикальным пером.

При выводе толстых линий с использованием пера-отрезка линии заметны разрывы в углах полилиний и плохие концы (рис. 3.21).

 

          Для решения таких проблем иногда используют другие алгоритмы вывода толстых линий. Например, вывод толстой полигинии можно выполнить как рисование полигона с заполнением (рис. 3.22).

 

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

Третья разновидность алгоритмов вывода толстых линий — рисование тол­стой линии последовательным наложением нескольких тонких линий, сме­щенных одна относительно второй.

 

 

Алгоритмы вывода пунктирной линии

 

Алгоритм для рисования тонкой пунктирной линии можно получить из алго­ритма вывода тонкой непрерывной линии

 

 

 

 

 

 

В, таком алгоритме используется новая переменная (С) — счетчик пикселов линии. Если значение С удовлетворяет некоторому логическому условию, то рисуется пиксел заданного цвета с текущими координатами (х, у). Логическое условие будет определять стиль линии. Например, если условием будет чет­ность значения С, то получим линию из одиночных точек. Для рисования пунктирной линии можно анализировать остаток от деления С на S. Напри­мер, если рисовать пикселы линии только тогда, когда С mod S < S/2, то по­лучим пунктирную линию с длиной штрихов S/2 и с шагом S.

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

 

                        Алгоритм вывода толстой пунктирной линии

 

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

 

3.7. Стиль заполнения. Кисть. Текстура

 

При выводе фигур могут использоваться различные стили заполнения. Для обозначения стилей заполнения, отличных от сплошного стиля, используют такие понятия, как кисть и текстура. Их можно считать синонимами, одна­ко понятие текстуры обычно используется применительно к трехмерным объектам, а кисть — для изображения двумерных объектов. Текстура — это стиль заполнения, закрашивание, которое имитирует сложную рельефную объемную поверхность, выполненную из какого-то материала. Для описания алгоритмов заполнения фигур с определенным стилем исполь­зуем тот же способ, что и для описания алгоритмов рисования линий. Мы уже ранее рассмотрели некоторые алгоритмы заполнения, и вы, наверное, согласитесь, что описание всех разновидностей подобных алгоритмов можно дать с помощью такой обобщенной схемы:        

                                      

          Например, в алгоритме вывода полигонов пикселы заполнения рисуются в теле цикла горизонталей, а все другие операции предназначены для подсчета координат (х, у) этих пикселов. Сплошное заполнение означает, что цвет (С) всех пикселов одинаков, то есть C=const. Нам нужно как-то изменять цвет пикселов заполнения, чтобы получить определенный узор. Преобразуем ал­горитм заполнения следующим образом:

 

 

Функция f(x,y) будет определять стиль заполнения. Аргументами функции цвета являются координаты текущего пиксела заполнения. Однако в отдель­ных случаях эти аргументы не нужны. Например, если цвет С вычислять как случайное значение в определенных границах: С = random (), то можно создать иллюзию шершавой матовой поверхности (рис. 3.23).

 

Другой стиль заполнения — штриховой (рис. 3.24). Для него функцию цвета также можно записать в аналитической форме:

 

 

 

Если не рисовать пикселы фона, то можно создать иллюзию полупрозрачной фигуры. Подобную функцию можно записать и для других типов штриховки. Аналитическая форма описания стиля заполнение позволяет достаточно про­сто изменять размеры штрихов при изменениях масштаба показа, например, для обеспечения режима WYSIWYG.

           Зачастую при использовании кистей и текстур используется копирование не­больших растровых изображений. Такой алгоритм заполнения можно описать вышеупомянутой общей схемой, если строку С =f(x, у) заменить двумя г другими строками:                                                              

 

                                      

 

Преобразование координат пиксела заполнения (х, у) в координаты внутри образца кисти можно выполнить таким образом:

где т, п — размеры растра кисти соответственно по горизонтали и вертикали. При этом координаты Т, уТ) будут в диапазоне хТ= 0...m- 1, уТ =0...п - 1 при любых значениях х и у. Таким образом, обеспечивается циклическое ко­пирование фрагментов кисти внутри области заполнения фигуры (рис. 3.25).

 

 

Удобно, когда размеры кисти равны степени двойки. В этом случае вместо операций взятия остатка (mod) можно использовать более быстродействую­щие для цифровых компьютеров поразрядные двоичные операции. Приведем пример вычисления остатка от деления на 16.

 

Здесь можно использовать поразрядную операцию & (И). Еще один пример. Если необходимо вычислить X mod 256, а значение X за­писано в регистре АХ микропроцессора (совместимого с 80x86), то в качест­ве результата достаточно взять содержимое младшей байтовой части этого регистра — AL.

          Для пикселов текстур часто употребляется название тексел. Растровые текстуры и кисти широко используются в современной компью­терной графике, в том числе и в ЗD-графике. Для отображения трехмерных  объектов часто используются полигональные поверхности, каждая грань отображается с наложенной текстурой. Поскольку объекты обычно показы­ваются с разных ракурсов — повороты, изменения размеров и тому подоб­ное, то необходимо соответственно трансформировать и каждую грань с тек­стурой. Для этого используются проективные текстуры.

Общая схема алгоритма заполнения контуров полигонов для проективных текстур такая же, как и приведенная выше. Однако растровый образец здесь представляет всю грань, а преобразование координат из (х, у) в г, уТ) более сложное, например, аффинное:

 

где коэффициенты А, В, ... , F —- константы при пересчете координат всех пикселов для отдельной текстурированной грани. Такое преобразование ко­ординат можно использовать, если привязать текстуру к грани по трем опор­ным точкам. Пример наложения проективной текстуры приведен на рис. 3.26.

 

 

                  где i = 1, 2, 3. По, известным координатам (jt7y, Утд и (*/, Уд можно найти ко­эффициенты А, В, ... , F, если решить систему линейных уравнений. Эта сис­тема распадается на две независимые системы третьего порядка. Для упро­щения записи заменим xTi наXi ayTi на У,:

                                        

 

 

 

 

Для решения систем линейных уравнений известно множество способов. Ис­пользуем способ, основанный на вычислении определителей. Решение пер­вой системы для коэффициентов А, В и С можно записать в виде

 

 

      Если главный определитель равен нулю, то это означает, что решение систе­мы невозможно. Это может быть, например, тогда, когда все три точки (х1 y1), (х2, у2) и (x3 x3) располагаются вдоль прямой линии (грань видна с торца). Однако в этом случае рисовать текстуру и не нужно. Вычислить оп­ределитель третьей степени можно, например, по "правилу Саррюса" [3]. Для этого справа нужно дописать первые два столбца, а затем сложить (вычесть) произведения по диагоналям:

 

 

 

 

                                                                                                                      

Аналогично вычисляются определители detA и detB. Определитель detC яв­ляется самым сложным из всех. Но его вычислять не обязательно. Запишем  решение системы в следующем виде:

 

Заметьте, что здесь главный определитель det совпадает с определителем первой системы уравнений — для А, В я С.

Наложение текстур в перспективной проекции сложнее, чем для аксонометрической проекции. Рассмотрим рис. 3.27, на котором изображен текстурированный прямоугольник.

 

 

            Прямоугольник в аксонометрической (параллельной) проекции всегда вы­глядит как параллелограмм, поскольку для этой проекции сохраняется па­раллельность прямых и отношение длин. В перспективной (центральной) проекции это уже не параллелограмм и не трапеция (в косоугольной — тра­пеция), поскольку параллельность и отношение длин здесь не сохраняются. А что сохраняется? Как изображать плоские грани?

В этой книге мы рассматриваем проекции на плоскость. Для таких проекций прямые линии остаются прямыми линиями, поэтому грани можно выводить как полигоны.                       

Здесь уместно вспомнить, как формируется изображение в некоторой проек­ции средствами компьютерной графики. Как уже нами было рассмотрено в главе 2, последовательность преобразований координат выглядит так:

 

 

 

Если считать, что точки текстуры должны соответствовать точкам на объек­те, то координаты текстуры должны связываться с мировыми координатами. Однако поскольку для аксонометрической проекции в цепочке от мировых координат до экранных все преобразования линейны, то вполне допустимо связать координаты текстуры с экранными координатами одним аффинным преобразованием.

          Для перспективной проекции так делать нельзя. Преобразование координат из видовых координат в координаты плоскости проецирования не линейно. Поэтому экранные координаты вначале следует преобразовать в такие, кото­рые линейно связаны с мировыми — это могут быть, скажем, видовые. А за­тем видовые координаты (или непосредственно мировые) связать с коорди­натами текстуры аффинным преобразованием, используя, например, метод трех точек.

       Рассмотрим, как можно выводить в перспективной проекции полигон с текстурой. Будем использовать алгоритм заполнения полигона горизонтальными линиями, уже рассмотренный нами. На рис. 3.28 изображена одна из горизонталей (АВ). Вершины полигона (1-2-3-4) здесь заданы экранными двумер­ными координатами. Для краткости изложения по­ложим, что экранные координаты совпадают с ко­ординатами в плоскости проецирования. В ходе вывода полигона для связи с текстурой будем вычислять видовые координаты  произвольной точки (Р) этого полигона. Для этого будем использовать в качестве базовой такую операцию: по известным видовым координатам концов  отрезка находим видовые координаты точки отрезка, заданной координатами  в плоскости проецирования.

 Для определения видовых координат X, Y, Z точки А должны быть известны  видовые координаты концов отрезка (1-2). Тогда справедливы соотношения:

 

       после чего вычисляется X, например, по формуле Х= а + Zb. Для определе­ния координаты Y достаточно заменить везде X на У. Здесь можно отметить, что вычисление видовых координат (X, Y, Z) соответствует обратному про­ективному преобразованию.-

Найдя видовые координаты точки А, мы можем точно так же вычислить ви­довые координаты и для точки В, лежащей на отрезке (3-4). Аналогично вы­числяются видовые координаты точки Р.

Следует отметить, что, несмотря на то, что для преобразования координат необходимо вычислять дробно-линейные выражения, цикл вычислений мож­но сделать достаточно простым подобно инкрементным алгоритмам. Сделай­те это самостоятельно в виде упражнения.

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

Одна из проблем наложения текстур заключается в том, что преобразование растровых образцов (повороты, изменение размеров и тому подобное) приво­дят к ухудшению качества растров. Повороты растра добавляют ступенча­тость (aliasing); увеличение размеров укрупняет пикселы, а уменьшение раз­меров растра приводит к потере многих пикселов образца текстуры, появля­ется муар. Для улучшения текстурованных изображений используют методы фильтрации (интерполяции) растров текстур [41, 47]. Также используются несколько образцов текстур для различных ракурсов показа (mipmaps) — компьютерная система во время отображения находит в памяти наиболее пригодный растровый образец.

Для использования текстур необходим достаточный объем памяти компью­тера — количество растровых образцов может достигать десятков, сотен и более в зависимости от количества типов объектов и многообразия простран­ственных сцен. Чтобы как можно быстрее создавать изображение, необходи­мо сохранять текстуры в оперативной памяти.

Для экономии памяти, выделяемой для текстур, можно использовать блочное текстурирование. Текстура здесь уже не представляет всю грань целиком, а лишь отдельный фрагмент, который циклически повторяется в грани. Это напоминает процесс размножения рисунка кисти при закрашивании полиго­нов, рассмотренный нами в этом разделе.

Разумеется, далеко не для всех объектов можно использовать такой способ отображения, однако, например, для образцов современной массовой "коро­бочной" архитектуры в этом плане имеются практически неограниченные возможности (рис. 3.29).

Современные видеоадаптеры оснащены графическими процессорами, кото­рые аппаратно поддерживают операции с текстурами.

 

 

 

3.8. Фракталы

 

Фрактал можно определить как объект довольно сложной формы, получаю­щийся в результате выполнения простого итерационного цикла. Итерационность, рекурсивность обуславливают такие свойства фракталов, как самопо­добие— отдельные части похожи по форме на весь фрактал в целом. Латин­ское   fractus   означает   "составленный   из   фрагментов".   В    1975   году французский   математик   Бенуа   Мандельброт   издал   книгу   "The   fractal Geometry of Nature". С того времени слово "фрактал" стало модным. Фракталом Мандельброта названа фигура, которая порождается очень про­стым циклом. Для создания этого фрактала необходимо для каждой точки изображения выполнить цикл итераций согласно формуле:

                                          .

 

где  к = 0, 1, ..., п. Величины zk — это комплексные числа, Zk - xк + i уk  причем стартовые значения  x0  и у0 — это координаты точки изображения. Для Каждой точки изображения итерации выполняются ограниченное количество раз (п) или до тех пор, пока модуль числа Zk  не превышает 2. Модуль комплексного числа равен корню квадратному из х22. Для вычисления квадрата величины zk можно воспользоваться формулой z = (х + iy) (x + iy) = (x22) + i (2у), поскольку i2 = -1. Цикл итераций для фрактала Мандельброта можно выполнять в диапазоне: х = (от -2.2 до 1), у = (от -1.2 до 1.2). Для то­го чтобы получить изображение в растре, не­обходимо пересчитывать координаты этого диапазона в пиксельные. В главе 6 мы рас­смотрим соответствующий пример програм­мы, генерирующей изображения этого фрак­тала.

Фрактал Джулия внешне совсем не похож на фрактал Мандельброта, однако он определя­ется итерационным циклом, почти полностью тождественным с циклом генерации Ман­дельброта. Формула итераций для фрактала Джулия такая:

 

 

 

где с — комплексная константа. Условием завершения итераций является | z k | > 2 — гак же, как для фрактала Мандельброта.

На рис. 3.30 приведено несколько изображе­ний фрактала Джулия для с = 0.36 + i 0.36, n=256. На верхнем образце картинка построена в границах х = (от -1 до 1), у = (от -1.2 до 1.2). На втором и третьем изображе­ниях рис. 3.30 показаны увеличенные фрагменты фрактала. Как видим, фрактал самоподобный — при любом увеличении отдельные части напоминают формы це­лого. Самоподобие считается важным свойством фракталов. Это отличает их от других типов объектов сложной формы.

Рассмотрим следующий пример фрактала — фрактал Ньютон. Для него ите­рационная формула имеет такой вид:

где z — также комплексные числа, причем z0 = x + iy соответствует координа­там точки изображения.

         Условием прекращения цикла итераций для фрактала Ньютон есть прибли­жение значений |z4- 1| к нулю. Например, изображение на рис. 3.31 было по­лучено для |z4- 1|2> 0.001, границы расчетах = (от-1 до 1),у = (от-1 до 1).

 

 

            Рассмотрим еще одну разновидность фракталов. Такие фракталы названы геометрическими, поскольку их форма может быть описана как последова­тельность простых геометрических операций. Например, кривая Кох стано­вится фракталом в результате бесконечного количества итераций, в ходе ко­торых выполняется деление каждого отрезка прямой на три части. На рис. 3.32 показаны три итерации— постепенно линия становится похожей на снежинку.

Следующую группу составляют фракталы, которые генерируются согласно методу "систем итеративных функций" — IFS (Iterated Functions Systems). Этот метод может быть описан, как последовательный итеративный расчет координат новых точек в пространстве:

 

где Fx и Fy— функции преобразования координат, например, аффинного преобразования. Эти функции и определяют форму фрактала. В случае аф­финного преобразования необходимо найти соответствующие числовые зна­чения коэффициентов.

Давайте попробуем создать фрактал, который бы выглядел как растение. Во­образим себе ствол, на котором много веточек. На каждой веточке много меньших веточек и так далее. Самые малые ветки можно считать листвой или колючками. Все элементы будем рисовать отрезками прямой. Каждый отрезок будет задаваться двумя конечными точками.

           Для начала итераций необходимо задать стартовые координаты линии. Это будут точки 1, 2. На каждом шаге итераций будем рассчитывать координаты других точек.

 

 

Теперь зададим длину и угол наклона ветвей, которые выходят из точки 4. Сначала найдем координаты точки 5. Введем еще один   параметр— к1, который будет определять соотношение длин отрезков 4-5 и 4-3, причем так же I 0<А:1<1. Координаты точки 5 равны:          

 

                                                

Кроме расчета опорных точек на каждом шаге будем рисовать один отрезок  1-4. В зависимости от номера итераций можно изменять цвет отрезка. Также  можно устанавливать его толщину, например, пропорционально длине.

          Таким образом, фрактал мы задали как последовательность аффинных преобразований координат точек. Величины а,β к, к1 — это параметры, кото­рые описывают вид фрактала в целом. Они представляют собой константы на протяжении всего итеративного процесса. Это дает возможность в итерациях использовать только операции сложения, вычитания и умножения, если вычислить значения sin(), cos(), (I - к) и (1 – к1) только один раз перед началом итераций как коэффициенты-константы.

Дадим запись алгоритма в виде рекурсивной процедуры шаг ()

Для того чтобы нарисовать фрактал, необходимо вызывать процедуру шаг, установив соответствующие значения ее аргументов: шаг( х1 yl, х2, у2,   о) . Обратите внимание на один из аргументов этой процедуры — num, который в начале работы равен 0. В теле процедуры есть три рекурсивных  вызова с различными значениями этого аргумента:

 

 

          Значение num показывает степень детализации расчета дерева. Один цикл итераций содержит много шагов, соответствующих одному значению величины num. Числовое значение num можно использовать для прекращения ите­ративного процесса, а также для определения текущего цвета элементов "растения".

Завершение циклов итераций в нашем алгоритме происходит тогда, когда длина ветви становится меньше некоторой величины lmm, например, l min=1. Этот фрактал при а = 2°, β = 86°, к = 0.14, к1 = 0.3 похож на папоротник (рис. 3.34).

 

                  Метод IFS применяется не только для создания изображений. Его использовали Барнсли и Слоан для эффективного сжатия графических изображений  при записи в файлы. Основная идея такая: поскольку фракталы могут представлять очень сложные изображения с помощью простых итераций, то описание этих итераций требует значительно меньшего объема информации, чем соответствующие растровые изображения. Для кодирования изображений  необходимо решать обратную задачу — для изображения (или его фрагмента) подобрать соответствующие коэффициенты аффинного преобразования.  Этот метод используется для записи цветных фотографий в файлы со сжатием в десятки и сотни раз без заметного ухудшения изображения. Формат таких графических файлов назван FIF (Fractal Image Format) и запатентован фирмой Iterated Systems [36].

 

ГЛАВА 4

 

Методы и алгоритмы трехмерной графики

 

Понятие ""трехмерная графика" в настоящее время можно считать наиболее Fраспространенным для обозначения темы, которую мы рассмотрим (в лите­ратуре название часто сокращается до "ЗD-графики"). Однако необходимо отметить, что такое название не совсем точно, ибо речь пойдет о создании  изображения на плоскости, а не в объеме. Истинно трехмерные способы ото­бражения пока что не достаточно широко распространены.

 

4.1. Модели описания поверхностей

 

Рассмотрим, как можно представлять форму трехмерных объектов в систе­мах КГ. Для описания формы поверхностей могут использоваться разнооб­разные методы. Сделаем обзор некоторых из них.

 

Аналитическая модель

 

Аналитической моделью будем называть описание поверхности математиче­скими формулами. В КГ можно использовать много разновидностей такого описания. Например, в виде функции двух аргументов z =f(x, у). Можно использовать уравнение F (х, у, z) = .0.

Зачастую используется параметрическая форма описания поверхности. За­пишем формулы для трехмерной декартовой системы координат (х, у, z):

 

где s и t— параметры, которые изменяются в определенном диапазоне, а функции Fx, Fy и убудут определять форму поверхности.

Преимущества параметрического описания — легко описывать поверхности, которые отвечают неоднозначным функциям, замкнутые поверхности. Опи­сание можно сделать таким образом, что формула не будет существенно из­меняться при поворотах поверхности, масштабировании.                               

В качестве примера рассмотрим аналитическое описание поверхности шара. " Сначала как функцию двух1 аргументов:

 

 

Для описания сложных поверхностей часто используют сплайны. Сплайн — это специальная функция, более всего пригодная для аппроксимации отдель­ных фрагментов поверхности. Несколько сплайнов образовывают модель сложной поверхности. Другими словами, сплайн — эта тоже поверхность, но такая, для которой можно достаточно просто вычислять координаты ее точек. Обычно используют кубические сплайны. Почему именно кубические? По­тому, что третья степень — наименьшая из степеней, позволяющих описы­вать любую форму, и при стыковке сплайнов можно обеспечить непрерыв­ную первую производную — такая поверхность будет без изломов в местах стыка. Сплайны часто задают параметрически. Запишем формулу для компо­ненты x(s,t) кубического сплайна в виде многочлена третьей степени пара­метров s Ht:

 

      В математической литературе можно ознакомиться со способами определения коэффициентов ау для сплайнов, которые имеют заданные свойства. Приме­ры анализа и синтеза сплайнов в матричной форме приведены в [19, 28].

 

 

 

            Характеризуя аналитическую модель в целом, можно сказать, что эта модель наиболее пригодна для многих операций анализа поверхностей. С позиций КГ' можно указать такие положительные черты модели: легкая процедура расчета координат каждой точки поверхности, нормали; небольшой объем информации для описания достаточно сложных форм.

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

 

Векторная полигональная модель

 

Для описания пространственных объектов здесь используются такие элемен­ты: вершины; отрезки прямых {векторы); полилинии, полигоны; полигональ­ные поверхности (рис. 4.2).          Элемент "вершина" (vertex) — главный элемент описания, все другие явля­ются производными. При использовании трехмерной декартовой системы координаты вершин определяются как (хi уi zi,). Каждый объект однозначно определяется координатами собственных вершин.

 

                               

Вершина может моделировать отдельный точечный объект, размер которого не имеет значения, а также может использоваться в качестве конечных точек для линейных объектов и полигонов. Двумя вершинами задается вектор. Не­сколько векторов составляют полилинию. Полилиния может моделировать отдельный линейный объект, толщина которого не учитывается, а также мо­жет представлять контур полигона. Полигон моделирует площадный объект. Один полигон может описывать плоскую грань объемного объекта. Несколь­ко граней составляют объемный объект в виде полигональной поверхно­сти— многогранник или незамкнутую поверхность (в литературе часто употребляется название "полигональная сетка").

Векторную полигональную модель можно считать наиболее распространен­ной в современных системах трехмерной КГ. Ее используют в системах ав­томатизированного проектирования, в компьютерных играх и тренажерах, в САПР, геоинформационных системах и тому подобное.

Обсудим структуры данных, которые используются в векторной полигональ­ной модели. В качестве примера объекта будет куб. Рассмотрим, как можно организовать описание такого объекта в структурах данных.о-

 

 

 

 

       В компьютерной программе такой способ описания объекта можно реализовать разнообразно. Например, для каждой грани открыть в памяти отдельный массив. Можно все грани записывать в один массив-вектор. А можно использовать классы (языком C++) как для описания отдельных граней, так и объектов в целом. Можно создавать структуры, которые объединяют тройки  (х, у, z), или сохранять координаты отдельно. В значительной мере это относится уже к компетенции программиста, зависит от его вкуса.  Принципиаль­но это мало что изменяет— так или иначе в памяти необходимо сохранять координаты вершин граней плюс некоторую информацию в качестве накладных затрат.

Рассчитаем объем памяти, необходимый для описания куба следующим об­разом:

где Рв — разрядность чисел, необходимая для представления координат.

Шесть граней здесь описываются 24 вершинами. Такое представление избы­точно — каждая вершина записана трижды. Не учитывается то, что у граней есть общие вершины.