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

RedBook

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

glMatrixMode(GL_PROJECTION); glLoadIdentity();

gluPerspective(45.0,(GLfloat)w/(GLfloat)h,3.0,7.0); glMatrixMode(GL_MODELVIEW);

glLoadIdentity(); glTranslatef(0.0,0.0,-5.0);

}

int main(int argc, char **argv)

{

glutInit(&argc,argv);

glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB|GLUT_DEPTH|GLUT_STENCIL); glutInitWindowSize(400,400);

glutCreateWindow("Using the Stencil Test"); init();

glutDisplayFunc(display);

glutReshapeFunc(reshape);

glutMainLoop(); return 0;

}

Далее приводятся другие примеры использования теста трафарета.

Предположим, что вы рисуете закрытый выпуклый объект (или несколько подобных объектов, при условии, что они не пересекают и не включают друг друга), созданный из нескольких полигонов, и у вас есть отсекающая плоскость, которая может отсекать или не отсекать часть этого объекта. Предположим, что в случае, когда плоскость пересекает объект, вы хотите закрыть его поверхностью постоянного цвета, а не рассматривать его внутренности. Чтобы это сделать, установите буфер трафарета в нули и начинайте рисование, активизировав тест трафарета и установив функцию сравнения в GL_ALWAYS. Инвертируйте значения в битовых плоскостях буфера трафарета каждый раз, когда фрагмент принимается. После того, как все объекты нарисованы, областям, не требующим закрытия, в буфере трафарета будут соответствовать 0, а областям, где оно требуется – 1. Установите трафаретную функцию таким образом, чтобы рисование совершалось только так, где индекс трафарета не равен 0 и нарисуйте большой полигон закрывающего цвета размером с экран.

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

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

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

10.2.4 Тест глубины

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

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

void glDepthFunc (GLenum func);

Устанавливает функцию сравнения для теста глубины. Возможными значениями для func являются GL_NEVER, GL_ALWAYS, GL_LESS, GL_LEQUAL, GL_EQUAL, GL_GEQUAL, GL_GREATER и GL_NOTEQUAL. Входящий фрагмент проходит тест глубины в том случае, если его z координата относится к значению, хранимому в буфере глубины, указанным образом. Функция по умолчанию это GL_LESS, что означает прохождение фрагментом теста в том случае, если его глубина меньше хранимой в буфере. В этом случае величина z представляет собой дистанцию от объекта до точки наблюдения, и меньшие величины означают, что соответствующий объект находится ближе к наблюдателю.

10.2.5 Цветовое наложение, цветовое микширование и логические операции

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

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

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

10.2.5.1 Цветовое наложение

Цветовое наложение комбинирует R, G, B и A величины фрагмента с компонентами пикселя, уже сохраненного в соответствующем месте цветового буфера. Могут применяться различные операции наложения, а результат зависит от альфа входящего фрагмента и альфа уже сохраненного пикселя.

10.2.5.2 Цветовое микширование

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

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

Операция микширования полностью зависит от аппаратуры; все, что позволяет вам сделать OpenGL– это активизировать или деактивировать эту операцию. На некоторых системах, однако, это может вообще ни к чему не приводить, что имеет смысл, если цветовых плоскостей и так достаточно. Чтобы активизировать и деактивировать микширование передайте аргумент GL_DITHER командtglEnable() или glDisable() соответственно. Микширование по умолчанию активизировано.

Микширование работает и в RGBA, и в индексном режиме. Цвета или цветовые индексы изменяются каким-либо аппаратно-зависимым методом между двумя соседними значениями. Например, если микширование включено, и индекс, который нужно нарисовать, равен 4.4, то 60% пикселей могут быть нарисованы с индексом 4, а 40% -- с индексом 5. (Может существовать множество алгоритмов микширования, но результат

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

10.2.5.3 Логические операции

Последняя операция, которая применяется к фрагменту это логическая операция, такая как OR, XOR или INVERT, которая применяется к значениям входящего фрагмента (источнику) и/или тем значениям, которые уже хранятся в цветовом буфере (приемнике). Такие логические операции особенно полезны в машинах, на которых

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

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

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

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

Вы включаете и выключаете логические операции с помощью аргументов

GL_INDEX_LOGIC и GL_COLOR_LOGIC команд glEnable() и glDisable() для индексного и RGBA режимов соответственно. Вы также должны выбрать одну из поддерживаемых логических операций командой glLogicOp(). В противном случае вы получите результат по умолчанию – GL_COPY. (Для обратной совместимости с OpenGL версии 1.0, glEnable(GL_LOGIC_OP) также включает логические операции в индексном режиме.)

void glLogicOp (GLenum opcode);

Выбирает производимую логическую операцию, выполняемую над входящим фрагментом (источником) и пикселем, находящимся в цветовом буфере (приемником). Все возможные значения для opcode и их значения перечислены в таблице 10-4. sозначает источник (source), а dприемник (destination). Значение по умолчанию

GL_COPY.

Таблица 10-4. Логические операции

Параметр

Операция

Параметр

Операция

GL_CLEAR

0

GL_AND

 

GL_COPY

s

GL_OR

 

GL_NOOP

d

GL_NAND

 

GL_SET

1

GL_NOR

 

GL_COPY_INVERTED

 

GL_XOR

 

GL_INVERT

 

GL_EQUIV

 

GL_AND_REVERSE

 

GL_AND_INVERTED

 

GL_OR_REVERSE

 

GL_OR_INVERTED

 

 

 

 

 

10.3Аккумуляторный буфер

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

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

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

void glAccum (GLenum op, GLfloat value);

Управляет буфером аккумуляции. Аргумент opвыбирает операцию, а valueчисло, которое будет использоваться в этой операции. Возможными операциями являются

GL_ACCUM, GL_LOAD, GL_RETURN, GL_ADD и GL_MULT.

GL_ACCUM считывает каждый пиксель из буфера выбранного для чтения командой glReadBuffer(), умножает R, G, B и A величины на число value и добавляет результаты в буфер аккумуляции.

GL_LOAD работает так же как GL_ACCUM за тем исключением, что результирующие значения заменяют уже хранимые в буфере, а не добавляются к ним.

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

GL_ADD и GL_MULT просто добавляют или умножают значения каждого пикселя в буфере аккумуляции к или на величину value. Для GL_MULT value усекается до диапазона [-1.0, 1.0]. Для GL_ADD усечение не производится.

10.3.1 Сглаживание всей сцены

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

glAccum(GL_ACCUM,1.0/n);

а затем в конце вызовите

glAccum(GL_RETURN,1.0);

Заметьте, что этот метод работает несколько быстрее, если при первом проходе вместо GL_ACCUM применить GL_LOAD, а не делать предварительную очистку аккумулятора. С помощью этого кода изображение нужно нарисовать nраз, а затем нарисовать результирующее изображение. Если вы не хотите, чтобы наблюдатель видел промежуточные изображения, считывайте из невидимого цветового буфера, а результат записывайте в видимый (или в задний буфер, впоследствии переключив его с передним).

Вы также можете создать пользовательский интерфейс, который показывает, как

рассматриваемое изображение улучшается с каждым шагом и позволяет пользователю остановить процесс, когда ему кажется, что изображение достигло нужного качества. Чтобы сделать это, в цикле, рисующем последовательные изображения, вызывайте glAccum() с первым аргументом GL_RETURN, а вторым равным 16.0/1.0, 16.0/2.0, 16.0/3.0, ... В данной технике после одного прохода показывается 1/16 часть финального изображения, после двух проходов – 2/16 и так далее. После GL_RETURN программа должна проверять, не остановил ли пользователь процесс. Такой метод будет работать несколько медленнее, поскольку после каждого шага изображение должно быть скопировано в цветовой буфер.

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

Рисунок 10-2. Сглаживание сцены с помощью буфера аккумулятора

Пример 10-2. Функции для сдвига объема видимости

#include <math.h>

#define PI_20 3.14159265358979323846

void accFrustum(GLdouble left,GLdouble right, GLdouble bottom,GLdouble top, GLdouble zNear,GLdouble zFar, GLdouble pixdx,GLdouble pixdy, GLdouble eyedx, GLdouble eyedy, GLdouble focus)

{

GLdouble xwsize, ywsize; GLdouble dx, dy;

GLint viewport[4];

glGetIntegerv(GL_VIEWPORT,viewport);

xwsize=right-left; ywsize=top-bottom;

dx=-(pixdx*xwsize/(GLdouble)viewport[2] + eyedx*zNear/focus); dy=-(pixdy*ywsize/(GLdouble)viewport[3] + eyedy*zNear/focus);

glMatrixMode(GL_PROJECTION); glLoadIdentity();

glFrustum(left+dx,right+dx,bottom+dy,top+dy,zNear,zFar); glMatrixMode(GL_MODELVIEW); glTranslatef(-eyedx,-eyedy,0.0);

}

void accPerspective(GLdouble fovy,GLdouble aspect, GLdouble zNear,GLdouble zFar, GLdouble pixdx,GLdouble pixdy, GLdouble eyedx,GLdouble eyedy, GLdouble focus)

{

GLdouble fov2,left,right,bottom,top; fov2=((fovy*PI_20)/180.0)/2.0;

top=zNear/(cos(fov2)/sin(fov2)); bottom=-top;

right=top*aspect; left=-right;

accFrustum(left,right,bottom,top,zNear,zFar,

pixdx,pixdy,eyedx,eyedy,focus);

}

Пример 10-3 использует две функции из примера 10-2 для выполнения сглаживания сцены.

Пример 10-3. Сглаживание сцены: файл accpersp.cpp

#include <glut.h> #include "jitter.h"

#define ACSIZE 16 #define SPHERE_FACES 32 #define JIT j16

void init()

{

GLfloat mat_ambient[]={1.0,1.0,1.0,1.0}; GLfloat mat_specular[]={1.0,1.0,1.0,1.0}; GLfloat light_position[]={0.0,0.0,10.0,1.0}; GLfloat lm_ambient[]={0.2,0.2,0.2,1.0};

glMaterialfv(GL_FRONT,GL_AMBIENT,mat_ambient); glMaterialfv(GL_FRONT,GL_SPECULAR,mat_specular); glMaterialf(GL_FRONT,GL_SHININESS,50.0); glLightfv(GL_LIGHT0,GL_POSITION,light_position); glLightModelfv(GL_LIGHT_MODEL_AMBIENT,lm_ambient);

glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_DEPTH_TEST); glShadeModel(GL_FLAT);

glClearColor(0.0,0.0,0.0,0.0);

glClearAccum(0.0,0.0,0.0,0.0);

}

void displayObjects()

{

GLfloat torus_diffuse[]={0.7,0.7,0.0,1.0}; GLfloat cube_diffuse[]={0.0,0.7,0.7,1.0}; GLfloat sphere_diffuse[]={0.7,0.0,0.7,1.0}; GLfloat octa_diffuse[]={0.7,0.4,0.4,1.0};

glPushMatrix(); glTranslatef(0.0,0.0,-5.0); glRotatef(30.0,1.0,0.0,0.0);

glPushMatrix(); glTranslatef(-0.80,0.35,0.0); glRotatef(100.0,1.0,0.0,0.0);

glMaterialfv(GL_FRONT,GL_DIFFUSE,torus_diffuse); glutSolidTorus(0.275,0.85,SPHERE_FACES,SPHERE_FACES); glPopMatrix();

glPushMatrix(); glTranslatef(-0.75,-0.50,0.0); glRotatef(45.0,0.0,0.0,1.0); glRotatef(45.0,1.0,0.0,0.0);

glMaterialfv(GL_FRONT,GL_DIFFUSE,cube_diffuse); glutSolidCube(1.5);

glPopMatrix();

glPushMatrix();

glTranslatef(0.75,0.60,0.0);

glRotatef(30.0,1.0,0.0,0.0); glMaterialfv(GL_FRONT,GL_DIFFUSE,sphere_diffuse); glutSolidSphere(1.0,SPHERE_FACES,SPHERE_FACES); glPopMatrix();

glPushMatrix(); glTranslatef(0.70,-0.90,0.25);

glMaterialfv(GL_FRONT,GL_DIFFUSE,octa_diffuse); glutSolidCube(1.5);

glPopMatrix();

glPopMatrix();

}

void display()

{

GLint viewport[4]; int jitter;

glGetIntegerv(GL_VIEWPORT,viewport);

glClear(GL_ACCUM_BUFFER_BIT);

for (jitter=0;jitter<ACSIZE;jitter++)

{

glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

accPerspective(50.0,(GLdouble)viewport[2]/(GLdouble)viewport[3],

1.0,15.0,JIT[jitter].x,JIT[jitter].y,

0.0,0.0,1.0);

displayObjects(); glAccum(GL_ACCUM,1.0/ACSIZE);

}

glAccum(GL_RETURN,1.0); glFlush();

}

void reshape(int w,int h)

{

glViewport(0,0,(GLsizei)w,(GLsizei)h);

}

int main (int argc, char **argv)

{

glutInit(&argc,argv);

glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB|GLUT_DEPTH|GLUT_ACCUM); glutInitWindowSize(310,310); glutInitWindowPosition(100,100);

glutCreateWindow("Scene Antialiasing"); init();

glutDisplayFunc(display);

glutReshapeFunc(reshape);

glutMainLoop(); return 0;

}

Для того, чтобы производить сглаживание сцены, вы не обязательно должны использовать перспективную проекцию. Если вы работаете с ортографической проекцией, для сдвига сцены вам достаточно использовать glTranslate*(). Имейте в виду, что glTranslate*() работает в мировых координатах, а вам нужно сдвигать сцену менее чем на 1 пиксель в оконных координатах. Таким образом, вам нужно обратить процесс преобразования мировых координат, вычислив величины сдвигающего переноса с использованием его ширины и высоты в мировых координатах, деленных на размер порта просмотра. Затем умножьте получившуюся величину в мировых координатах на количество сдвига, вычислив, таким образом, на сколько сцена должна быть перемещена в мировых координатах, чтобы получить предсказуемый сдвиг менее чем в 1 пиксель. Пример 10-4 показывает, как должны выглядеть функции display() и reshape(), если ширина и высота в мировых координатах равны 4.5.

Пример 10-4. Сдвиг в ортографической проекции: файл accanti.cpp

void display()

{

GLint viewport[4]; int jitter;

glGetIntegerv(GL_VIEWPORT,viewport);

glClear(GL_ACCUM_BUFFER_BIT);

for (jitter=0;jitter<ACSIZE;jitter++)

{

glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); glPushMatrix();

//Обратите внимание, что 4.5 - это дистанция в мировых

координатах

//между левой и правой, а также нижней и верхней

границами.

//Формула преобразует сдвиг на часть пикселя в мировые

координаты.

glTranslatef(JIT[jitter].x*4.5/viewport[2],

JIT[jitter].y*4.5/viewport[3],0.0);

displayObjects();

glPopMatrix(); glAccum(GL_ACCUM,1.0/ACSIZE);

}

glAccum(GL_RETURN,1.0); glFlush();

}

void reshape(int w,int h)

{

glViewport(0,0,(GLsizei)w,(GLsizei)h); glMatrixMode(GL_PROJECTION);

if(w<=h) glOrtho(-2.25,2.25,-2.25*h/w,2.25*h/w,-10.0,10.0);

else

glOrtho(-2.25*w/h,2.25*w/h,-2.25,2.25,-10.0,10.0); glMatrixMode(GL_MODELVIEW);

glLoadIdentity();

}

10.3.2 Размытое движение

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

вызывать

glAccum(GL_MULT, decayFactor);

где decayFactor это число от 0.0 до 1.0. Меньшие значения для decayFactor как бы заставляют объект двигаться быстрее. Вы можете перенести результирующее

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

glAccum(GL_RETURN,1.0);

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

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

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

glAccum(GL_LOAD, 0.9); glAccum(GL_RETURN, 1.0);

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

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