Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

RedBook

.pdf
Скачиваний:
20
Добавлен:
11.06.2015
Размер:
7.43 Mб
Скачать

for(j=0;j<n;j++)

{

glBegin(GL_QUAD_STRIP); for(i=0;i<=m;i++)

{

glVertex2i(i,j);

glVertex2i(i,j+1); set_color(i,j);

}

glEnd();

}

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

Далее приводится несколько более обобщенная версия, которая искажает изображение с использованием функций x(i,j) и y(i,j):

glShadeModel(GL_FLAT); glScale(3.2,3.2,1.0); for(j=0;j<n;j++)

{

glBegin(GL_QUAD_STRIP); for(i=0;i<=m;i++)

{

glVertex2i(x(i,j), y(i,j)); glVertex2i(x(i,j+1), y(i,j+1)); set_color(i,j);

}

glEnd();

}

И даже еще лучшее искаженное изображение может быть нарисовано с помощью следующего кода:

glShadeModel(GL_SMOOTH); glScale(3.2,3.2,1.0); for(j=0;j<n-1;j++)

{

glBegin(GL_QUAD_STRIP); for(i=0;i<m;i++)

{

set_color(i,j); glVertex2i(x(i,j), y(i,j)); set_color(i,j+1); glVertex2i(x(i,j+1), y(i,j+1));

}

glEnd();

}

Этот код плавно интерполирует цвет по всему четырехугольнику. Заметьте, что последняя версия производи на один четырехугольник меньше в каждом измерении, поскольку цвета изображения используются в вершинах полигонов. Кроме того, вы

можете сглаживать изображение с помощью антиалиасинга и соответствующих факторов наложения (GL_SRC_ALPHA, GL_ONE), чтобы еще больше улучшить качество изображения.

14.8 Отображение слоев

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

Вкачестве простого примера, предположим, что у вас есть три различные субстанции, которые можно отобразить в виде слоев. В любой точке может оказаться одна из восьми комбинаций слоев, как показано в таблице 14-2.

Таблица 14-2. Восемь комбинаций слоев

 

Слой 1

Слой 2

Слой 3

Цвет

0

отсутствует

отсутствует

отсутствует

черный

1

присутствует

отсутствует

отсутствует

красный

2

отсутствует

присутствует

отсутствует

зеленый

3

присутствует

присутствует

отсутствует

синий

4

отсутствует

отсутствует

присутствует

розовый

5

присутствует

отсутствует

присутствует

желтый

6

отсутствует

присутствует

присутствует

белый

7

присутствует

присутствует

присутствует

серый

 

 

 

 

 

Вы хотите, чтобы ваша программа отображала 8 разных цветов в зависимости от того, какие слои присутствуют в определенной точке. Одна из возможных цветовых схем показана в последней колонке таблицы 14-2. Чтобы применить этот метод, используйте индексный цветовой режим и загрузите вхождения в палитру следующим образом: 0 – черным цветом, 1 – красным, 2 – зеленым и так далее. Обратите внимание, что если числа от 0 до 7 перевести в двоичную систему, то бит 4 будет установлен везде, где присутствует слой 3, бит 2 – везде, где присутствует слой 2, а бит 1 – везде, где присутствует слой 1.

Для очистки окна, установите маску записи в 7 (все три слоя), а очищающий цвет в 0. Чтобы нарисовать изображение, установите цвет в 7, а маску записи в n, где n – это слой, в котором вы хотите рисовать. В других типах приложений может появиться необходимость удалить один из слоев. В этом случае вам нужно использовать маски записи так, как было оговорено ранее, но цвет устанавливать не в 7, а в 0.

14.9 Сглаженные символы

Использование стандартной техники отображения символов с помощью команды glBitmap() рисует каждый пиксель символа по принципу «все или ничего» -- либо пиксель рисуется, либо нет. Например, если вы рисуете черные символы на белом фоне, результирующие символы либо черные, либо белые, без намека на оттенок серого. Плавные изображения высокого качества могут быть получены при использовании промежуточных цветов (в данном примере оттенков серого.)

В предположении, что вы рисуете черные символы на белом фоне, представьте себе сильно увеличенное изображение пикселей, на которое наложено изображение символа с высоким разрешением, как показано на рисунке 14-1 слева.

Рисунок 14-1. Сглаженные символы

Заметьте, что некоторые пиксели полностью содержатся внутри границы символа и должны быть нарисованы черным, некоторые полностью находятся вне границ символа и должны быть нарисованы белым, но многие пиксели было бы идеально нарисовать одним из оттенков серого, чтобы степень темноты оттенка соответствовала количеству черного на пикселе. Если использовать эту технику, изображение на экране будет иметь лучшее качество.

Если скорость и количество памяти не дают поводов для беспокойства, каждый символ можно нарисовать в виде небольшого изображения, а не битовой карты. Однако если вы работаете в режиме RGBA, этот метод потребует до 32 бит на каждый пиксель символа вместо 1 для стандартного символа. Альтернативно вы можете использовать для каждого пикселя 8-битовый индекс и конвертировать его в RGBA с помощью цветовой таблицы во время переноса пикселей. Во многих случаях возможен компромисс, позволяющий вам рисовать символы с помощью нескольких оттенков серого цвета, и результирующее изображение при этом потребует всего 2 или 3 бита на пиксель.

Числа в правой части рисунка 14-1 показывают примерный проценты покрытия пикселя: 0 означает практическое отсутствие такового, 1 означает одну треть покрытия, 2 – две трети и 3 – полное покрытие пикселя символом. Если пиксели, отмеченные 0 рисовать белым, а пиксели, отмеченные 3 рисовать черным, а пиксели с номерами 1 и 2 можно рисовать оттенками 33% и 66% серого, соответственно, символы будут выглядеть достаточно хорошо. Для сохранения чисел 0, 1, 2 и 3 требуется только 2 бита, так что 2 бита позволяют сохранять 4 оттенка серого цвета.

Существует два основных метода реализации сглаженных символов, применимость которых зависит от того, в каком цветовом режиме вы работаете в RGBA или в индексном.

В RGBA режиме создайте три различных битовых карты символа, соответствующих пикселям с номерами 1, 2 и 3. Установите цвет в белый и очистите экран. Установите цвет в 33% оттенок серого (RGB=(0.666, 0.666, 0.666)) и нарисуйте все пиксели, отмеченные 1 (с помощью одной из трех ранее подготовленных битовых карт). Затем установите RGB=(0.333, 0.333, 0.333) и нарисуйте вторую карту. Наконец, установите цвет в черный и нарисуйте третью карту. В принципе вы создали три разных шрифта и перерисовали строку символов три раза, причем каждый проход заполнил биты определенной цветовой плотности.

В индексном режиме вы можете сделать то же самое, но если вы решите использовать верно настроенную цветовую карту и маску записи, вы можете обойтись всего двумя битовыми картами и двумя проходами на строку. Вспомнив предыдущий пример, создайте первую битовую карту, содержащую все пиксели, отмеченные 1 или 3. Создайте вторую битовую карту из пикселей, отмеченных 2 или 3. Загрузите цветовую карту так, чтобы в 0 содержался белый, в 1 светло-серый, в 2 – темно-серый, а в 3 – черный. Установите цвет в 3, а маску записи в 1 и нарисуйте первую карту. Затем измените маску на 2 и нарисуйте вторую. В тех местах, где на рисунке 14-1 стоят 0, в буфере кадра ничего нарисовано не будет. Там, где стоят 1, 2 и 3, в буфере окажутся

1, 2 и 3.

Для данного примера с 4-мя оттенками серого экономия достаточно мала два прохода вместо трех. Если бы использовалось 8 оттенков вместо 4, метод RGBA потребовал бы 7 проходов, а индексный только три. С 16 оттенками серого соотношение было бы 15 проходов к 4.

14.10 Рисование круглых точек

Чтобы нарисовать почти круглые несглаженные точки, нужно активизировать сглаживание точек, выключить цветовое наложение и использовать альфа функцию (в альфа тесте), которая пропускает только фрагменты со значением альфа большим

0.5.

14.11 Интерполяция изображений

Предположим, что у вас имеется два изображения (загруженных из файла или сгенерированных с помощью геометрии обычным путем), и вы хотите, чтобы одно изображение плавно перешло в другое. Это можно легко сделать при помощи альфа компонента и соответствующих операций наложения. Скажем, вы хотите достигнуть полного перехода за 10 шагов, при этом изображение A будет полностью показано в кадре 0, а изображение B будет полностью показано в кадре 9. Очевидный подход заключается в том, чтобы в i-ом кадре рисовать изображение A с альфа равным (9-i)/9, а изображение B с альфа равным i/9.

Проблема этого метода заключается в том, что оба изображения должны рисовать в каждом кадре. Было бы быстрее нарисовать изображение A в кадре 0. Затем для получения кадра 1 можно совместить 1/9 изображения B и 8/9 того изображение, которое уже нарисовано на экране. В кадре 2 нужно совместить 1/8 изображения B и 7/8 имеющегося изображения. Для кадра 3 нужно совместить 1/7 изображения B с 6/7 имеющегося изображения и так далее. На последнем шаге вам нужно нарисовать 1/1 изображения B и 0/1 имеющегося изображения, то есть вам нужно вывести только изображение B.

Чтобы понять, как это работает, предположим, что ваше изображение на кадре i

представляет собой

и вы смешиваете B/(9-i) с (8-i)/(9-i) этого изображения. Тогда вы получите следующее:

.

14.12 Создание ярлыков

Предположим, что вы рисуете сложную трехмерную картинку с использованием буфера глубины для удаления невидимых поверхностей. Кроме того, предположим, что одна из частей вашей картинки состоит из двух объектов, лежащих на одной плоскости – A и B. При этом B является чем-то вроде ярлыка, который всегда должен появляться поверх

A.

Первым, приходящим на ум подходом, является рисовать B после рисования A, установив функцию теста глубины таким образом, чтобы тест проходили фрагменты с глубиной большей или равной тому, что записано в буфере глубины (GL_GEQUAL). Однако благодаря ограниченной точности дробного представления вершин, ошибки округления могут приводить к тому, что объект B иногда будет появляться перед объектом A, а иногда за ним. Далее приводится одно из решений проблемы:

1.Запретить запись в буфер глубины и вывести объект A.

2.Разрешить запись в буфер глубины и вывести объект B.

3.Запретить запись в цветовой буфер и вывести объект A.

4.Разрешить запись в цветовой буфер.

Обратите внимание на то, что тест глубины активизирован и выполняется в течение всего процесса. На шаге 1 A отображается там, где должно, но никакие величины не записываются в буфер глубины; следовательно, на шаге 2 гарантируется появление объекта B. Шаг 3 просто позволяет убедиться в том, что все значения глубины под A установлены в правильные значения, но, поскольку цветовой буфер закрыт для записи, пиксели на экране не изменяются. Наконец, шаг 4 возвращает системы к состоянию по умолчанию (оба буфера цветовой и глубины открыты для записи).

Если имеется буфер трафарета, можно использовать более простую технику.

1.Настройте буфер трафарета, чтобы он записывал 1 в случае прохождения фрагментом теста глубины и 0 в противном случае. Нарисуйте A.

2.Настройте буфер трафарета, запретив изменение его величин и разрешив визуализацию только там, где значение в буфере трафарета равно 1. Заблокируйте тест буфера глубины и изменение величин в нем. Нарисуйте B.

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

14.13 Рисование закрашенных вогнутых полигонов с помощью буфера трафарета

Возьмем вогнутый полигон 1234567, показанный на рисунке 14-2. Представьте, что он рисуется в виде серии треугольников: 123, 134, 145, 156 и 167. Толстая линия показывает границу оригинального полигона. Рисование всех этих треугольников разделяет буфер на 9 областей A, B, C, ..., I, причем I находится вне всех треугольников.

Рисунок 14-2. Выпуклый полигон

В тексте к рисунку справа от имени каждой области приводится список треугольников, которые его накрывают. Области A, D и F составляют оригинальный полигон; обратите внимание на то, что эти области накрыты нечетным количеством треугольников. Все остальные области накрыты четным (возможно нулевым) числом треугольников. Таким образом, чтобы отобразить вогнутый полигон, вам нужно вывести только области, ограниченные нечетным числом треугольников. Это может быть сделано с помощью буфера трафарета и двухпроходного алгоритма.

Сначала очистите буфер трафарета и запретите запись в цветовой буфер. Далее, по

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

Замечание: Существует обобщение приведенной техники, согласно которому вы не обязаны начинать с вершины полигона. Пусть в примере 1234567 точка P – любая точка, принадлежащая или не принадлежащая полигону. Нарисуйте треугольники P12, P23, P34, P45, P56, P67 и P71. Области, накрытые нечетным числом треугольников, находятся внутри полигона, остальные области находятся снаружи полигона. Если P находится на одном из ребер полигона, один из треугольников будет пустым.

Эта техника может использоваться и для заполнения непростых полигонов (полигонов, чьи ребра пересекаются) и для заполнения полигонов с дырами. Следующий пример иллюстрирует обработку сложного полигона с двумя областями одной четырехсторонней и одной пятисторонней. Кроме того, предположим, что в полигоне есть треугольная дыра и четырехугольная дыра (неважно в каких именно областях находятся дыры). Назовем две области abcd и efghi, а дыры jkl и mnop. Пусть z любая точка на плоскости. Нарисуем следующие треугольники:

zab zbc zcd zda zef zfg zgh zhi zie zjk zkl zlj zmn zno zop zpm

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

14.14 Нахождение областей пересечения

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

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

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

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

Нарисуйте каждый из проверяемых объектов и отсеките их плоскостью. Запомните, какие пиксели плоскости находятся внутри объекта, используя подсчет четное нечетное в буфере трафарета, как объяснено в разделе «Рисование закрашенных вогнутых полигонов с помощью буфера трафарета». (Для правильно сформированных объектов точка находится внутри, если луч из этой точки в сторону наблюдателя пересекает нечетное число поверхностей объекта.) Чтобы найти пересечения, вам нужно найти пиксели в буфере кадра, где отсекающая плоскость находится внутри двух или более областей одновременно; другими словами на пересечении внутренних областей любой пары объектов.

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

этой области с объединением внутренних областей уже протестированных объектов и учитывайте точки пересечения. Затем добавьте внутренние точки нового объекта к объединению внутренних областей других объектов.

Вы можете выполнить операции, описанные в предыдущем параграфе, используя разные биты буфера трафарета, совместно с разными операциями маскирования. На каждый пиксель требуется три бита буфера трафарета один для переключения, определяющего внутреннюю область объекта; один для объединения всех внутренних областей, обнаруженных на данный момент; и один для областей, в которых на данный момент обнаружено пересечение. Чтобы сделать дискуссию более конкретной, предположим, что 1-ый бит буфера трафарета используется для переключения между внутренними и внешними точками, 2-ой для объединения и 4-ый для пересечений. Для каждого выводимого объекта, очистите 1-ый бит (используя маску трафарета 1 и очистив в 0), затем переключайте 1-ый бит, оставляя маску в состоянии1 и, используя операцию трафарета GL_INVERT.

Вы можете найти пересечения и объединения битов в буфере трафарета, используя операции с трафаретом. Например, чтобы поместить в буфер 2 объединение битов в буферах 1 и 2, маскируйте трафарет на эти 2 бита и нарисуйте что-либо поверх всего объекта, заставив функцию трафарета пропускать только фрагменты с ненулевым значением трафарета в буфере. Это произойдет, если установлен бит в буфере 1, бит в буфере 2 или биты в обоих буферах. Если сравнение удается, записывайте 1 в буфер 2. Кроме того, убедитесь в том, что запись в цветовой буфер заблокирована.

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

14.15 Тени

Любую возможную проекцию трехмерного пространства на трехмерно пространство можно выполнить с помощью подходящей инвертируемой матрицы 4x4 и однородных координат. Если матрица не является инвертируемой, но имеет ранг 3, она проецирует трехмерное пространство на двумерную плоскость. Любую проекцию такого типа можно выполнить с помощью подходящей матрицы 4x4, имеющей ранг 3. Чтобы найти тень произвольного объекта от произвольного источника света (возможно, лежащего в бесконечности) на произвольную плоскость, вам нужно найти матрицу, представляющую такую проекцию, умножить на нее матричный стек и нарисовать объект цветом тени. Имейте в виду, что вы должны спроецировать объект на каждую плоскость, которую вы называете «землей».

В качестве простой иллюстрации, предположим, что источник света находится в начале координат, а плоскость «земли» представлена уравнением ax+by+cz+d=0. Возьмем вершину S=(sx, sy, sz, 1). Луч из источника света, проходящий через точку S,

содержит все точки , где -- произвольное вещественное число. точка, где луч пересекает плоскость, появляется тогда, когда:

то есть

.

Помещая последнюю формулу в уравнение линии, получаем:

или

где I – точка пересечения описанного луча с плоскостью «земли», то есть одна из точек, составляющих тень.

Матрица, которая отображает точку S на точку I для любого S, равна:

.

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

Если свет исходит от источника, лежащего в бесконечности, все, что у вас есть это точка S и направление D=(dx, dy, dz). Точки вдоль луча получаются как .

Действуя, как и прежде, пересечение этого луча с плоскостью получается из уравнения

.

Разрешая это уравнение относительно , помещая получившуюся формулу в уравнение луча и, далее, определяем проекционную матрицу

.

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

14.16 Удаление невидимых линий

Если вы хотите нарисовать каркасный объект с удалением невидимых линий, один из подходов заключается в том, чтобы сначала нарисовать границы в виде линий, а затем закрасить внутренние области полигонов, приводя поверхности полигонов в соответствие с цветом фона. С включенным буфером глубины эта закраска внутренних областей накроет любые границы, которые должны быть загорожены гранями, находящимися ближе к наблюдателю. Этот метод работает до тех пор, пока внутренние области объекта полностью заключены в границы полигонов; на самом деле они могут накладываться друг на друга в различных местах (в результате будут получаться «прошитые полигоны»).

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

14.16.1 Удаление невидимых линий с помощью полигонального смещения

Чтобы использовать полигональное смещение для удаления невидимых линий, объект рисуется дважды. Сначала выбранным цветом рисуются ребра полигонов с использованием полигонального режима GL_LINE. Затем те же полигоны рисуются цветом фона в режиме GL_FILL и с полигональным смещением, достаточным для того, чтобы сдвинуть их дальше от наблюдателя. Полигональное смещение позволяет избежать неприятных визуальных артефактов.

glEnable(GL_DEPTH_TEST); glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);

установить_цвет(передний_план);

нарисовать_объект_в_виде_закрашенных_полигонов();

glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); glEnable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(1.0,1.0);

установить_цвет(фон); нарисовать_объект_в_виде_закрашенных_полигонов(); glDisable(GL_POLYGON_OFFSET_FILL);

Вам может понадобиться изменить настройки смещения (для более толстых линий, например).

14.16.2 Удаление невидимых линий с помощью буфера трафарета

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

этом случае полную очистку буфера трафарета нужно будет произвести только один раз.

Например, следующий код представляет собой внутренний цикл, который вы можете использовать для удаления невидимых линий. Граница каждого полигона рисуется цветом переднего плана, закрашивается цветом фона, и затем граница рисуется еще раз. Буфер трафарета используется для предотвращения закрашивания границы цветом фона. Для оптимизации быстродействия параметры трафарета и цвета

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

glEnable(GL_STENCIL_TEST); glEnable(GL_DEPTH_TEST); glClear(GL_STENCIL_BUFFER_BIT); glStencilFunc(GL_ALWAYS,0,1); glStencilOp(GL_INVERT,GL_INVERT,GL_INVERT);

установить_цвет(передний_план); for(i=0;i<max;i++)

{

нарисовать_границу_полигона(i); установить_цвет(фон); glStencilFunc(GL_EQUAL,0,1); glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);

нарисовать_закрашенный_полигон(i); установить_цвет(передний_план); glStencilFunc(GL_ALWAYS,0,1); glStencilOp(GL_INVERT,GL_INVERT,GL_INVERT);

нарисовать_границу_полигона(i);

}

14.17 Варианты применения текстурирования

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

Соседние файлы в предмете Компьютерная Графика