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

RedBook

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

Нахождение нормалей для аналитических поверхностей

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

V(s, t) = [X(s, t) Y(s, t) Z(s, t)],

где s и t определены в некотором пространстве, а X, Y и Z дифференцируемые функции 2-ух переменных. Чтобы вычислить нормаль, найдите

,

являющиеся векторами касательными к поверхности в направлениях s и t. Их

векторное произведение

перпендикулярно им обоим и, как следствие, перпендикулярно поверхности. Следующая формула отражает процесс вычисления векторного произведения 2-ух векторов. (Следите за случаями, когда векторное произведение имеет нулевую длину.)

Вероятно, вам потребуется нормализовать результирующий вектор. Для нормализации вектора [x y z], вычислите его длину

и разделите на нее каждый из компонентов вектора.

В качестве примера для этих расчетов, возьмем следующую аналитическую

поверхность

Отсюда имеем

Таким образом, если в данном примере s=1 и t=2, соответствующей точкой поверхности является точка с координатами (1, 8, 1), а вектор (-24, 2, 24) является перпендикуляром к поверхности в этой точке. Длина этого вектора равна 34, следовательно, вектор нормали единичной длины равен

(-24/34, 2/34, 24/34) = (-0.70588, 0.058823, 0.70588).

В случае аналитических поверхностей, заданных в неявной форме

F(x, y, z)=0

найти решение значительно сложнее. В некоторых случаях вы можете разрешить это уравнение относительно одной из переменных, скажем z=G(x, y) и записать его в уже

рассмотренной форме

V(s, t) = [s t G(s, t)].

В этом случае можно продолжать работу уже описанным способом.

Если вы не можете привести уравнение поверхности к явной форме, вам, возможно, поможет тот факт, что вектор нормали можно получить как градиент

,

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

примера неявно определенной аналитической функции рассмотрим уравнение сферы радиусом в 1 с центром в начале координат:

Это означает, что

.

Это уравнение может быть разрешено относительно z следующим образом:

.

Таким образом, нормали можно рассчитать с помощью явного вида

как было описано ранее.

Если бы не могли разрешить уравнение относительно z, вы могли бы воспользоваться

градиентом

,

конечно, если бы смогли найти точку на поверхности. В данном случае это совсем не сложно например, (2/3, 1/3, 2/3) лежит на поверхности. Вычисленная с помощью градиента нормаль в данной точке будет равна (4/3, 2/3, 4/3). Тогда нормаль единичной длины – (2/3, 1/3, 2/3), что, как и ожидалось, совпадает с точкой на поверхности.

Нахождение нормалей по полигональным данным

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

Рисунок B-2. Усреднение векторов нормали

Чтобы найти вектор нормали для плоского полигона, возьмите три любые вершины полигона v1, v2 и v3, не лежащие на одной прямой. Векторное произведение

и будет перпендикулярно полигону. (Обычно результирующий вектор нужно нормализовать.) Затем вам нужно усреднить нормали соседствующих полигонов, чтобы не давать больше веса одному из них. Например, если в случае, показанном на рисунке B-2, n1, n2, n4 и n5 нормали полигонов, соединяющихся в точке P, вычислите n1+n2+n4+n5, а затем нормализуйте получившийся вектор. (Вы можете получить лучшее усреднение, если взвесите нормали величинами углов в общем пересечении.) Результирующий вектор может использоваться в качестве нормали в точке P.

Иногда вы должны модифицировать этот метод под конкретную ситуацию. Например, на границе поверхности (такой как точка Q на рисунке B-2) вы можете выбрать лучшую нормаль, основываясь на вашем знании о том, как поверхность должна выглядеть. Иногда лучшее, что вы можете сделать, это усреднить и нормали полигонов на границе. Кроме того, у некоторых моделей есть плавные части и острые углы (точка R на рисунке B-2 находится на таком ребре). В этом случае не нужно усреднять нормали

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

Приложение C. Основы GLUT

При использовании GLUT ваше приложение структурируется благодаря тому, что для обработки событий используются функции обратного вызова. (Этот метод похож на использование Xt Toolkit, также известного как X Intristics.) Например, сначала вы открываете окно и регистрируете функции обратного вызова для нужных событий. Затем вы создаете главный цикл, из которого нет выхода. Если в этом цикле происходит событие, вызывается ассоциированная с ним функция обратного вызова. По завершении этой функции поток управления возвращается в главный цикл.

Управление окном

Для инициализации окна существует минимальный набор из пяти функций.

void glutInit (int argc, char **argv);

glutInit() должна быть вызвана до любых других GLUT – функций, так как она инициализирует саму библиотеку GLUT. glutInit() также обрабатывает параметры командной строки, но сами параметры зависят от конкретной оконной системы. Для системы X Window, примерами могут быть –iconic, -geometry и –display. (Параметры,

передаваемые glutInit(), должны быть теми же самыми, что и параметры, передаваемые в функцию main()).

void glutInitDisplayMode (unsigned int mode);

Указывает режим отображения (например, RGBA или индексный, одинарной или двойной буферизации) для окон, создаваемых вызовами glutCreateWindow(). Вы также можете указывать, имеет ли окно ассоциированные с ним буфер или буферы глубины, трафарета и аккумуляции. Аргумент mask это битовая комбинация, полученная при помощи операции OR и следующих констант: GLUT_RGBA или

GLUT_INDEX (для указания цветового режима), GLUT_SINGLE или GLUT_DOUBLE (для указания режима буферизации), а также константы для включения различных буферов

GLUT_DEPTH, GLUT_STENCIL, GLUT_ACCUN. Например, для окна с двойной буферизаций, RGBA – цветовым режимом и ассоциированными буферами глубины и трафарета, используйте GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL.

Значение по умолчанию – GLUT_RGBA | GLUT_ SINGLE (окно с однократной буферизацией в RGBA - режиме).

void glutInitWindowSize (int width, int height);

void glutInitWindowPosition (int x, int y);

Запрашивают окно определенного размера и в определенном месте экрана соответственно. Аргументы (x, y) определяют, где будет находиться угол окна относительно всего экрана. width и height определяют размер окна (в пикселях).

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

int glutCreateWindow (char *name);

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

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

Этот идентификатор может быть использован для управления несколькими окнами в одном приложении (каждое со своим контекстом OpenGL) и рисования в них.

Функции управления событиями

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

void glutDisplayFunc (void (*func)(void));

Позволяет указать функцию (аргументом func), которая будет вызываться каждый раз, когда содержимое окна требует перерисовки. Это может случиться, когда окно открывается, разворачивается, освобождается его часть, ранее перекрытая другим окном, или вызывается функция glutPostRedisplay().

void glutReshapeFunc (void (*func)(int width, int height));

Позволяет указать функцию, которая вызывается каждый раз при изменении размера окна или его позиции на экране. Аргумент func это указатель на функцию, которая принимает два параметра: width новая ширина окна и height новая высота окна.

Обычно func вызывает glViewport() для отсечения графического вывода по новым размерам окна, а также настраивает проекционную матрицу для сохранения пропорций спроецированного изображения в соответствии с новыми размерами порта просмотра. Если glutReshapeFunc() не вызывается или ей передается NULL (для отмены регистрации функции обратного вызова), вызывается функция изменения метрик по умолчанию, которая вызывает glViewport (0,0,width,height).

void glutKeyboardFunc (void (*func)(unsigned int key, int x, int y));

Задает функцию func, которая вызывается, когда нажимается клавиша, имеющая ASCII-код. Этот код передается функции обратного вызова в параметре key. В параметрах x и y передается позиция курсора мыши (относительно окна) в момент нажатия клавиши.

void glutMouseFunc (void (*func)(int button, int state, int width, int height));

Указывает функцию, которая вызывается при нажатии или отпускании кнопки мыши.

Параметр button может иметь значения GLUT_LEFT_BUTTON, GLUT_MIDDLE_BUTTON или GLUT_RIGHT_BUTTON. Параметр state может иметь значения GLUT_UP или GLUT_DOWN

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

void glutMotionFunc (void (*func)(int x, int y));

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

void glutPostRedisplay (void);

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

Загрузка палитры

Если вы работаете в индексном режиме, то можете к своему удивлению обнаружить, что в OpenGL нет команд для загрузки цвета в цветовую таблицу. Дело в том, что процесс загрузки палитры целиком зависит от оконной системы. В GLUT существует обобщенная функция для загрузки одного цветового индекса с соответствующим RGB значением.

void glutSetColor (Glint index, GLfloat red, GLfloat green, GLfloat blue);

Загружает в палитру по индексу index, RGB-значение, определенное параметрами red, green и blue. Последние три параметра нормализуются до диапазона [0.0, 1.0].

Рисование трехмерных объектов

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

void glutWireSphere (GLdouble radius, GLint slices, GLint stacks);

void glutSolidSphere (GLdouble radius, GLint slices, GLint stacks);

Рисуют проволочную или сплошную сферу с радиусом radius, количеством частей (полигонов из которых состоит сфера) slices вокруг оси z и stacks вдоль оси z. Для того, чтобы понять, что означает вокруг оси z и вдоль нее, представьте себе, что вы смотрите в длинную трубу. В данном случае направление вашего обзора совпадает с осью z трубы. Она может быть мысленно разделена как вдоль (на длинные фрагменты), так и поперек (на кольца). После таких разбиений труба фактически состоит из множества мелких кусочков. В случае сферы количество разбиений поперек задается параметром stacks, а количество разбиений вдоль параметром slices. Из этого следует, что чем больше разбиений, тем более гладкой выглядит сфера на экране, но тем больше вычислений требуется для ее рисования.

void glutWireCube (GLdouble size);

void glutSolidCube (GLdouble size);

Рисуют проволочный или сплошной куб с длиной ребра size.

void glutWireTorus (GLdouble innerRadius, GLdouble outerRadius, GLint nsides, GLint rings);

void glutSolidTorus (GLdouble innerRadius, GLdouble outerRadius, GLint nsides, GLint rings);

Рисуют проволочный или сплошной торус (бублик) с внешним радиусом outerRadius и внутренним радиусом innerRadius. Параметр rings задает желаемое число колец из которых будет состоять торус, параметр nsides из скольких частей будет состоять каждое кольцо.

void glutWireCone (GLdouble radius, GLdouble height, GLint slices, GLint stacks);

void glutSolidCone (GLdouble radius, GLdouble height, GLint slices, GLint stacks);

Рисуют проволочный или сплошной конус радиусом radius, высотой height. Значение параметров slices и stacks аналогично таким же параметрам для сферы.

void glutWireIcosahedron (void);

void glutSolidIcosahedron (void);

void glutWireOctahedron (void);

void glutSolidOctahedron (void);

void glutWireTetrahedron (void);

void glutSolidTetrahedron (void);

void glutWireDodecahedron (GLdouble radius);

void glutSolidDodecahedron (GLdouble radius);

Рисуют проволочные или сплошные икосаэдр, октаэдр, тетраэдр и додекаэдр соответственно (единственный параметр последний пары функций задает радиус додекаэдра).

void glutWireTeapot (GLdouble size);

void glutSolidTeapot (GLdouble size);

Рисуют проволочный или сплошной чайник размера size.

Управление фоновым процессом

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

void glutIdleFunc (void (*func)(void));

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

Запуск программы

После того, как все настройки выполнены, программы GLUT входят в цикл обработки сообщений функцией glutMainLoop().

void glutMainLoop (void);

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

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

Приложение D. Порядок операций

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

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

В этом разделе описан возможный порядок. Любая реализация OpenGL должна выдавать эквивалентный результат.

Введение

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

Рисунок D-1. Порядок операций

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

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

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