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

RedBook_OpenGL

.pdf
Скачиваний:
641
Добавлен:
02.04.2015
Размер:
7 Mб
Скачать

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

Рисунок 10-3. Размытое движение

Пример 10-5. Размытие движения с помощью буфера аккумулятора: файл motiblur.cpp

#include <glut.h>

#define SPHERE_FACES 64

//Горизонтальный размер проекции

#define PROJ_SIZE 8

//Количество шагов сдвига/поворота

#define TIME_JITTER_STEPS 10

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

#define FULL_ROT 0

//Фактор сжатия модели

#define SCALE_FACTOR 1.0

void init()

{

GLfloat mat_ambient[]={0.8,0.8,0.8,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_RESCALE_NORMAL); glEnable(GL_DEPTH_TEST); glShadeModel(GL_SMOOTH);

glClearColor(0.0,0.0,0.0,0.0);

glClearAccum(0.0,0.0,0.0,0.0);

}

void displayObjects(GLfloat xoffset,GLfloat yrot)

{

GLfloat torus1_diffuse[]={1.0,0.0,0.0,1.0}; GLfloat torus2_diffuse[]={1.0,0.7,0.7,1.0}; GLfloat sphere_diffuse[]={1.0,1.0,1.0,1.0};

glLoadIdentity();

glTranslatef(xoffset,0.0,0.0);

glRotatef(yrot,0.0,1.0,0.0); glScalef(SCALE_FACTOR,SCALE_FACTOR,SCALE_FACTOR); glMaterialfv(GL_FRONT,GL_DIFFUSE,torus1_diffuse); glutSolidTorus(0.3,0.55,SPHERE_FACES,SPHERE_FACES); glMaterialfv(GL_FRONT,GL_DIFFUSE,torus2_diffuse); glutSolidTorus(0.35,0.45,SPHERE_FACES,SPHERE_FACES); glMaterialfv(GL_FRONT,GL_DIFFUSE,sphere_diffuse); glutSolidSphere(0.4,SPHERE_FACES,SPHERE_FACES);

}

void display()

{

GLfloat correctedProjSize=PROJ_SIZE-3; int step;

GLfloat oneStepTrans=correctedProjSize/TIME_JITTER_STEPS; GLfloat oneStepRot=FULL_ROT/TIME_JITTER_STEPS;

glClear(GL_COLOR_BUFFER_BIT|GL_ACCUM_BUFFER_BIT); for(step=0;step<=TIME_JITTER_STEPS;step++)

{

glClear(GL_DEPTH_BUFFER_BIT); displayObjects(-correctedProjSize/2+oneStepTrans*step,

oneStepRot*step); if(step!=TIME_JITTER_STEPS)

glAccum(GL_LOAD,0.9);

else

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

}

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(-PROJ_SIZE/2,PROJ_SIZE/2,

-(PROJ_SIZE/2)*h/w,(PROJ_SIZE/2)*h/w,-10.0,10.0);

else

glOrtho(-PROJ_SIZE/2*w/h,(PROJ_SIZE/2)*w/h, -(PROJ_SIZE/2),PROJ_SIZE/2,-10.0,10.0);

glMatrixMode(GL_MODELVIEW); glLoadIdentity();

}

int main (int argc, char **argv)

{

glutInit(&argc,argv);

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

glutCreateWindow("Motion blur with Accumulator"); init();

glutDisplayFunc(display);

glutReshapeFunc(reshape);

glutMainLoop(); return 0;

}

10.3.3 Глубина поля

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

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

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

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

Рисунок 10-4. Перемещение объема видимости для эффекта глубины поля

На рисунке 10-5 изображено 5 чайников, нарисованных с применением эффекта глубины поля. Золотой чайник (второй слева) находится в фокусе, а остальные

размываются в разной степени в зависимости от дистанции до плоскости фокуса (золотого чайника). Код, используемый для рисования этого изображения, приводится в примере 10-6. Сцена рисуется 8 раз, каждый раз со слегка сдвинутым объемом видимости (сдвиг осуществляется с помощью функции accPerspective()). Как вы можете помнить, из метода сглаживании сцены, 5-ый и 6-ой аргументы сдвигают объем видимости в направлениях xи y соответственно. Однако для эффекта глубины поля нам нужно сдвигать объем видимости таким образом, чтобы он оставался на плоскости фокуса. Плоскость фокуса это величина глубины, определяемая 9-ым (последним) аргументом accPerspective() (например z=5.0). Количество размытия определяется умножением величин сдвига по xи y(7-го и 8-го аргументов) на константу. Определение такой константы не представляет сложности экспериментируйте с ней до тех пор, пока изображение не будет иметь тот вид, который вам нужен. (Обратите внимание на то, что в примере 10-6 4-ый и 5-ый аргументы accPerspective() установлены в 0.0. Таким образом, сглаживание сцены не производится.)

Рисунок 10-5. Чайники с эффектом глубины поля

Пример 10-6. Эффект глубины поля: файл dof.cpp

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

#define ACSIZE 8 #define JIT j8

GLint teapotList;

void init()

{

GLfloat ambient[]={0.0,0.0,0.0,1.0}; GLfloat diffuse[]={1.0,1.0,1.0,1.0}; GLfloat specular[]={1.0,1.0,1.0,1.0}; GLfloat position[]={0.0,3.0,3.0,0.0};

GLfloat lmodel_ambient[]={0.2,0.2,0.2,1.0};

GLfloat local_view[]={0.0};

glLightfv(GL_LIGHT0,GL_AMBIENT,ambient); glLightfv(GL_LIGHT0,GL_DIFFUSE,diffuse); glLightfv(GL_LIGHT0,GL_POSITION,position);

glLightModelfv(GL_LIGHT_MODEL_AMBIENT,lmodel_ambient); glLightModelfv(GL_LIGHT_MODEL_LOCAL_VIEWER,local_view);

glFrontFace(GL_CW); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0);

glEnable(GL_AUTO_NORMAL); glEnable(GL_NORMALIZE); glEnable(GL_DEPTH_TEST);

glClearColor(0.0,0.0,0.0,0.0);

glClearAccum(0.0,0.0,0.0,0.0);

teapotList=glGenLists(1); glNewList(teapotList,GL_COMPILE); glutSolidTeapot(0.5); glEndList();

}

void renderTeapot(GLfloat x,GLfloat y,GLfloat z,

GLfloat ambr,GLfloat ambg,GLfloat ambb, GLfloat difr,GLfloat difg,GLfloat difb, GLfloat specr,GLfloat specg,GLfloat specb, GLfloat shine)

{

GLfloat mat[4];

glPushMatrix();

glTranslatef(x,y,z);

mat[0]=ambr;mat[1]=ambg;mat[2]=ambb;mat[3]=1.0; glMaterialfv(GL_FRONT,GL_AMBIENT,mat); mat[0]=difr;mat[1]=difg;mat[2]=difb; glMaterialfv(GL_FRONT,GL_DIFFUSE,mat); mat[0]=specr;mat[1]=specg;mat[2]=specb; glMaterialfv(GL_FRONT,GL_SPECULAR,mat); glMaterialf(GL_FRONT,GL_SHININESS,shine); glCallList(teapotList);

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); glPushMatrix();

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

1.0,15.0,0.0,0.0,

0.33*JIT[jitter].x,0.33*JIT[jitter].y,5.0);

//Рубиновый, золотой, серебряный, изумрудный и голубой

чайники

renderTeapot(-1.1,-0.5,-4.5, 0.1745,0.01175,0.01175, 0.61424,0.04136,0.04136, 0.727811,0.626959,0.626959, 0.6);

renderTeapot(-0.5,-0.5,-5.0, 0.24725,0.1995,0.0745, 0.75164,0.60648,0.22648, 0.628281,0.555802,0.366065,

0.4); renderTeapot(0.2,-0.5,-5.5,

0.19225,0.19225,0.19225,

0.50754,0.50754,0.50754,

0.508273,0.508273,0.508273,

0.4); renderTeapot(1.0,-0.5,-6.0,

0.0215,0.1745,0.0215,

0.07568,0.61424,0.07568,

0.633,0.727811,0.633,

0.6); renderTeapot(1.8,-0.5,-6.5,

0.0,1.0,0.06,

0.0,0.50980392,0.50980392,

0.50196078,0.50196078,0.50196078,

0.25); glAccum(GL_ACCUM,0.125); glPopMatrix();

}

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(620,620); glutInitWindowPosition(100,100); glutCreateWindow("Depth-of-Field Effect");

init();

glutDisplayFunc(display);

glutReshapeFunc(reshape);

glutMainLoop(); return 0;

}

10.3.4 Мягкие тени

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

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

10.3.5 Сдвиг

Если вам нужно снять 9 или 16 кадров для сглаживания изображения, вы возможно решите, что лучшие решение это взять точки, распределенные по пикселю с равными промежутками. Удивительно, но это не обязательно так. По правде говоря, иногда является хорошей идеей взять точки на соседних пикселях. Иногда вам потребуется равномерное распределение точек, а иногда нормальное, уплотняющееся вблизи центра пикселя. Кроме того, в таблице 10-5 приводится несколько наборов разумных величин сдвига, которые могут использоваться в некоторых случаях. Все величины в этой таблице лежат внутри пикселя, а большинство из них еще и равномерно распределены.

Таблица 10-5. Примеры величин сдвига

Число Величины

2[0.25,0.75],[0.75,0.25]

3[0.5033922635,0.8317967229],[0.7806016275,0.2504380877], [0.2261828938,0.4131553612]

4[0.375,0.25],[0.125,0.75],[0.875,0.25],[0.625,0.75]

5[0.5,0.5],[0.3,0.1],[0.7,0.9],[0.9,0.3],[0.1,0.7]

6[0.4646464646,0.4646464646],[0.1313131313,0.7979797979], [0.5353535353,0.8686868686], [0.8686868686,0.5353535353], [0.7979797979,0.1313131313],[0.2020202020,0.2020202020]

8[0.5625,0.4375],[0.0625,0.9375],[0.3125,0.6875],[0.6875,0.8125], [0.8125,0.1875], [0.9375,0.5625],[0.4275,0.0625],[0.1875,0.3125]

[0.5,0.5],[0.1666666666,0.9444444444], [0.5,0.1666666666],[0.5,0.8333333333],

9[0.1666666666,0.2777777777],[0.8333333333,0.3888888888], [0.1666666666,0.6111111111], [0.8333333333,0.7222222222], [0.8333333333,0.0555555555]

[0.4166666666,0.625],[0.9166666666,0.875],[0.25,0.375], [0.4166666666,0.125],[0.75,0.125],

12[0.0833333333,0.125], [0.75,0.625],[0.25,0.875],[0.5833333333,0.375], [0.9166666666,0.375], [0.0833333333,0.625],[0.5833333333,0.875]

[0.375,0.4375],[0.625,0.0625],[0.875,0.1875],[0.125,0.0625], [0.375,0.6875],[0.875,0.4375],

16[0.625,0.5625],[0.375,0.9375], [0.625,0.3125],[0.125,0.5625],[0.125,0.8125],[0.375,0.1875], [0.875,0.9375],[0.875,0.6875],[0.125,0.3125],[0.625,0.8125]

Глава 11. Тесселяция и квадрические поверхности

Библиотека OpenGL (GL) разработана для низкоуровневых операций, одновременно являющихся конвейерными и имеющими доступ к аппаратному ускорению. Библиотека утилит OpenGL (GLU) дополняет OpenGL, предоставляя высокоуровневые операции. Некоторые из операций GLU (такие как мипмаппинг функцией gluBuild*DMipmaps(), масштабирование изображений функцией gluScaleImage(), операции матричных преобразований функциями gluOrtho2D(), gluPerspective(), gluLookAt(), gluProject(), gluUnProject() и gluUnProject4()) рассматриваются в других главах.

Некоторые будут рассмотрены далее.

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

11.1 Тесселяция полигонов

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

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

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

Рисунок 11-1. Контуры, нуждающиеся в тесселяции

Если вы считаете, что полигон нуждается в тесселяции, выполните следующие шаги:

1.Создайте новый объект тесселяции с помощью функции gluNewTess().

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

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

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

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

Замечание: Тесселяция, описанная здесь, появилась в GLU версии 1.2. Если у вас более старая версия GLU, вы должны использовать функции, описанные в разделе «Описание ошибок GLU». Чтобы запросить версию используемой GLU, используйте функцию gluGetString(GLU_VERSION), которая возвращает строку с номером версии GLU. Если в вашей GLU нет функции gluGetString(), значит это GLU версии 1.0 – в этой версии такая функция отсутствует.

11.1.1 Создание объекта тесселяции

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

GLUtesselator* gluNewTess (void);

Создает новый объект тесселяции и возвращает указатель на него. Если создать объект не удается, возвращается нулевой указатель.

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

11.1.2 Возвратно-вызываемые функции тесселяции

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

Любые опущенные возвратно-вызываемые функции просто не вызываются в процессе тесселяции, и любая информация, которую они могут возвращать в вашу программу будет потеряна. Все возвратные функции задаются с помощью gluTessCallback().

void gluTessCallback (GLUtesselator *tessobj, GLenum type, void (*fn)());

Ассоциирует возвратно-вызываемую функцию fn с объектом тесселяции tessobj. Тип возвратной функции определяется аргументом type, который может быть равен

GLU_TESS_BEGIN, GLU_TESS_BEGIN_DATA, GLU_TESS_EDGE_FLAG, GLU_TESS_EDGE_FLAG_DATA, GLU_TESS_VERTEX, GLU_TESS_VERTEX_DATA, GLU_TESS_END, GLU_TESS_END_DATA, GLU_TESS_COMBINE, GLU_TESS_COMBINE_DATA, GLU_TESS_ERROR или GLU_TESS_ERROR_DATA. 12

возможных возвратно-вызываемых функций имеют следующие прототипы:

GLU_TESS_BEGIN

void begin (GLenum type);

GLU_TESS_BEGIN_DATA

void begin (GLenum type, void* user_data);

GLU_TESS_EDGE_FLAG

void edgeFlag (GLboolean flag);

GLU_TESS_EDGE_FLAG_DATA

void edgeFlag (GLboolean flag, void* user_data);

GLU_TESS_VERTEX

void vertex (void* vertex_data);

GLU_TESS_VERTEX_DATA

void vertex (void* vertex_data, void* user_data);

GLU_TESS_END

void end (void);

GLU_TESS_END_DATA

void end (void* user_data);

GLU_TESS_COMBINE

void combine (GLdouble coords[3], void* vertex_data[4], GLfloat

weight[4], void** outData);

 

GLU_TESS_COMBINE_DATA

void combine (GLdouble coords[3], void* vertex_data[4], GLfloat weight[4],

void** outData, void* user_data);

 

GLU_TESS_ERROR

void error (GLenum errno);

GLU_TESS_ERROR_DATA

void error (GLenum errno, void* user_data);

 

 

Чтобы изменить возвратно-вызываемую функцию, просто вызовите gluTessCallback() с адресом новой функции. Чтобы устранить возвратную функцию без замещения ее новой, передайте gluTessCallback() нулевой указатель для соответствующего типа функции.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]