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

RedBook

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

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

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

Геометрические операции

Геометрические данные, появляются ли они из списка отображения, из вычислителя, из вершинного массива или в качестве вершин прямоугольника, состоят из вершин и типа примитива, который они описывают (точка, линия, полигон). Вершинные данные включают не только координаты (x, y, z, w), но и вектор нормали, координаты текстуры, цвет RGBA, цветовой индекс, свойства материала и флаг ребра. За

исключением координат вершины все эти элементы могут быть заданы в любом порядке, кроме того, существуют значения по умолчанию. Как только вызывается команда glVertex*(), компоненты расширяются до 4 измерений, если в том есть необходимость (с использованием z=0 и w=1), и текущие значения элементов ассоциируются с вершиной. Полный комплект вершинных данных уходит на обработку. (Если используются вершинные массивы, вершины могут обрабатываться группами, а обработанные группы могут использоваться многократно.)

Повершинные операции

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

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

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

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

Сборка примитивов

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

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

результаты отсекаются по стандартными видовыми плоскостями

.

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

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

Операции над пикселями

Сначала пиксели из памяти хоста распаковываются в нужное количество компонент. Часть OpenGL, ответственная за распаковку, обрабатывает большое количество разных форматов. Далее данные масштабируются, скашиваются и обрабатываются с использованием пиксельной карты (pixel map). Результаты усекаются до нужного

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

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

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

Текстурная память

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

Операции над фрагментами

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

Далее производится тест отреза, потом альфа тест (только в RGBA режиме), тест трафарета и тест глубины. Если работа ведется в режиме RGBA, производится цветовое наложение. За цветовым наложением производятся цветовое микширование и логические операции. Все описанные операции могут быть деактивированы.

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

Матричные операции

Матричные операции работают на текущем матричном стеке, коим может быть видовой, проекционный или текстурный. Команды glMultMatrix*(), glLoadMatrix*() и glLoadIdentity() применяются к верхней матрице стека, а glTranslate*(), glRotate*(), glScale*(), glOrtho() и glFrustum() используются для создания матрицы, на которую затем умножается верхняя матрица стека. Когда изменяется

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

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

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

Приложение E. Однородные координаты и матрицы преобразований

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

Однородные координаты

Команды OpenGL обычно работают с двумерными и трехмерными вершинами, но на самом деле все вершины интерпретируются как трехмерные, состоящие из четырех

координат. Любой вектор столбец представляет собой однородную вершину, если хотя бы один из его элементов не равен 0. Если вещественное число a

не равно 0, то и представляют одну и ту же однородную вершину. Трехмерная точка Евклидового пространства переходит в однородную вершину с координатами , а двумерная точка Евклидового пространства -- в однородную вершину .

До тех пор, пока w не равно 0, однородная вершины соответствует

трехмерной точке . Если w=0.0, вершина не соответствует ни одной точке в Евклидовом пространстве, а представляет собой некоторую идеализированную «точку в бесконечности». Чтобы понять, что такое «точка в бесконечности», рассмотрим вершину с координатами (1, 2, 0, 0) и заметим что последовательность точек (1, 2, 0, 1), (1, 2, 0, 0.01) и (1, 2, 0, 0.0001), соответствует Евклидовым точкам (1, 2), (100, 200) и (10,000, 20,000). Эта последовательность представляет точки, быстро смещающиеся в бесконечность вдоль прямой 2x=y. Таким образом, вы можете думать о точке (1, 2, 0, 0) как о точке в бесконечности, лежащей на этой прямой.

Замечание: OpenGL может неверно обрабатывать однородные усеченные координаты с w<0. Чтобы быть уверенными, что ваш код является переносимым на все реализации OpenGL, используйте только неотрицательные значения.

Преобразование вершин

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

однородную вершину, а М трансформационная матрица 4x4, то Mv является отображением v под воздействием M. (В приложениях компьютерной графики применяемые преобразования, как правило, не вырожденные иными словами, матрица является обратимой. Это не является обязательным условием, но позволяет избежать ряда проблем с вырожденными преобразованиями.)

После преобразования все трансформированные вершины усекаются, чтобы x, y и z лежали в диапазоне [-w, w] (в предположении, что w>0). Заметьте, что этот диапазон соответствует Евклидовому [-1.0, 1.0].

Преобразование нормалей

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

Однородная плоскость определяется вектор строкой ((a, b, c, d), где как минимум один из компонентов не равен 0. Если q вещественное число не равное 0, то (a, b, c,

d) и (qa, qb, qc, qd) представляют одну и ту же плоскость. Точка лежит на поверхности ((a, b, c, d), если ax+by+cz+dw=0. (Если w=1, то это стандартное описание Евклидовой плоскости.) Чтобы (a, b, c, d) представляло Евклидову плоскость, как минимум один из компонентов a, b или c не должен быть равен 0. Если все они равны 0, то (0, 0, 0, d) представляет «плоскость в бесконечности», содержащую все «точки в бесконечности».

Если pp однородная плоскость, а v однородная вершина, то утверждение «v лежит на p» математически записывается как pv=0, где pv обычное произведение матриц. Если M невырожденное вершинное преобразование (то есть матрица 4x4 имеющая

обратную матрицу ), то pv=0 эквивалентно , то есть Mv лежит в

плоскости . Следовательно, является отображением плоскости под воздействием вершинного преобразования M.

Если вам хочется думать о векторах нормалей именно как о векторах, а не как о плоскостях, перпендикулярным к ним, пусть v и n такие векторы, что v

перпендикулярно n. Тогда . Следовательно, для произвольного

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

преобразованный вектор нормали -- . Иными словами вектор нормали преобразуется инвертированной транспозицией матрицы преобразования, преобразующей точки.

Матрицы преобразования

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

Перенос

Вызов команды glTranslate*(x, y, z) генерирует T, где

и .

Масштабирование

Вызов команды glScale*(x, y, z) генерирует S, где

и .

Заметьте, что определена только если x, y и z одновременно не равны 0.

Поворот

Вызов команды glRotate*(a, x, y, z) генерирует R следующим образом:

Пусть , и .

Также пусть

и .

Затем

,

где m представляет элементы матрицы из M, которая является матрицей 3x3, определенной ранее. Матрица R определена всегда. Если x=y=z=0, R представляет

собой единичную матрицу. Вы можете получить инверсию R, , заменив a на –a, или транспонировав матрицу.

Команда glRotate*() генерирует матрицу для поворота вокруг произвольной оси. Часто вы выполняете поворот вокруг одной из координатных осей, этим поворотам соответствуют следующие матрицы:

.

Как и раньше обратные матрицы получаются с помощью транспозиции.

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

Обращение к glFrustum(l,r,b,t,n,f) генерирует R, где

и .

R определена до тех пор, пока и .

Ортографическая проекция

Обращение к glOrtho(l,r,b,t,n,f) генерирует R, где

и .

R определена до тех пор, пока и .

Приложение F. Советы

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

Во избежание сбоев

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

Не полагайтесь на поведение реализации OpenGL в отношении ошибок оно может измениться в следующих версиях. Например, OpenGL версии 1.1 игнорирует матричные операции, производящиеся между вызовами команд glBegin()/glEnd(), но в будущих версиях это может быть и не так.

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

Не вносите слишком интенсивные изменения в одну и ту же матрицу. Например, не следует создавать анимацию вращения, много раз вызывая glRotate*() с одним и тем же углом. Вместо этого используйте glLoadIdentity() для инициализации нужной матрицы каждый кадр, а затем вызывайте glRotate*() один раз с полным углом вращения для этого кадра.

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

Не рассчитывайте на то, что OpenGL будет рапортовать об ошибках. Команды внутри списка генерируют ошибки только тогда, когда список выполняется.

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

Вызывайте glFlush() для форсирования всех ранее вызванных команд OpenGL к исполнению. Не рассчитывайте на то, что glGet*() или glIs*() выполнят формирование потока визуализации. Команды опроса вызывают исполнение только части потока, необходимой для возвращения корректных данных, но не гарантируют выполнения всех команд визуализации.

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

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

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

gluOrtho2D(0, width,0,height);

где width и height это размеры порта просмотра. При такой проекционной матрице

координаты примитивов и пиксельных изображений должны быть представлены целыми числами, чтобы растеризация была предсказуемой. Например, glRecti(0,0,1,1) точно закрасит нижний левый угол порта просмотра, а glRasterPosi(0,0) определенно позиционирует неизмененное изображение в нижний левый угол порта просмотра. Однако вершины точек, вершины линий и позиции битовых карт должны помещаться посередине между двумя целыми числами. Например, линия, нарисованная из (x1, 0.5) в (x2, 0.5) гарантированно будет отображена в нижнем ряду пикселей порта просмотра, а точка, нарисованная с координатами (0.5, 0.5), заполнит тот же пиксель, что и glRecti(0,0,1,1).

Оптимальный компромисс, позволяющий задавать все примитивы в целых локациях, получая предсказуемую визуализацию, заключается в том, чтобы перенести x и y на 0.375, как показано в следующем примере. Такой перенос

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

glViewport(0,0,width,height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0,width,0,height); gllMatrixMode(GL_MODELVIEW); glLadIdentity(); glTranslatef(0.375,0.375,0.0);

/* визуализируйте все примитивы в целых локациях */

Избегайте использования отрицательного w в координатах вершин и отрицательного q в координатах текстуры. OpenGL может не усекать такие вершины корректно, и может совершать ошибки интерполяции при закраске примитивов с такими вершинами.

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

Для увеличения скорости работы OpenGL

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

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

Используйте специфические команды, такие как glRotate*(), glTranslate*() и glScale*() вместо того, чтобы составлять свои собственные матрицы вращений, переносов и масштабирования.

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

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

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

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

Для инкапсуляции текстурных данных используйте текстурные объекты. Поместите все вызовы команды glTexImage*() (включая мипмапы), необходимые для полного определения текстуры, а также ассоциированные вызовы glTexParameter*() в текстурный объект. Свяжите текстурный объект для выбора текстуры.

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

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

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

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

Если плавная закраска не требуется, установите режим GL_FLAT командой glShadeModel().

Если возможно используйте только один вызов glClear() на каждый кадр. Не используйте glClear() для очистки небольших областей буферов, используйте ее только для полной или близкой к полной очистке.

Для рисования нескольких треугольников используйте один вызов glBegin(GL_TRIANGLES) (то же касается примитивов GL_QUADS и GL_LINES), а не множество таких вызовов или вызовов glBegin(GL_POLYGON). Даже если должен быть нарисован только один треугольник, используйте GL_TRIANGLES, а

не GL_POLYGON.

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

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

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

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

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

Советы по работе с GLX

Используйте glXWaitGL() вместо glFinish(), чтобы команды визуализации X следовали за командами визуализации GL.

Точно так же используйте glXWaitX() вместо glFinish(), чтобы команды визуализации GL следовали за командами визуализации X.

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

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

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

Приложение G. Инвариантность OpenGL

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

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

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

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

Рисование линии и стирание ее цветом фона это простой и полезный алгоритм, рассчитанный на инвариантное исполнение. Он работает, только если в случае обоих линий будут сгенерированы фрагменты на одних и тех же (x, y). OpenGL требует, чтобы

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

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

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