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

RedBook

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

Колонка «Принадлежность» показывает, к чему может быть применена константа: к источнику или к приемнику.

Таблица 6-1. Факторы наложения источника и приемника

Константа

Принадлежность

Вычисляемый фактор

GL_ZERO

источник или приемник

(0,0,0,0)

GL_ONE

источник или приемник

(1,1,1,1)

GL_DST_COLOR

источник

 

GL_SRC_COLOR

приемник

 

GL_ONE_MINUS_DST_COLOR

источник

 

GL_ONE_MINUS_SRC_COLOR

приемник

 

GL_SRC_ALPHA

источник или приемник

 

GL_ONE_MINUS_SRC_ALPHA

источник или приемник

 

GL_DST_ALPHA

источник или приемник

 

GL_ONE_MINUS_DSR_ALPHA

источник или приемник

 

GL_SRC_ALPHA_SATURATE

источник

 

GL_CONSTANT_COLOR

источник или приемник

 

GL_ONE_MINUS_CONSTANT_COLOR

источник или приемник

 

GL_CONSTANT_ALPHA

источник или приемник

 

GL_ONE_MINUS_CONSTANT_ALPHA

источник или приемник

 

 

 

 

Замечание: GL_CONSTANT_COLOR, GL_ONE_MINUS_CONSTANT_COLOR, GL_CONSTANT_ALPHA

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

6.1.2 Примеры использования цветового наложения

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

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

Одним из способов нарисовать картинку, состоящую из половины одного изображения и половины второго, равномерно смешанных, является установка фактора источника в GL_ONE, а фактора приемника в GL_ZERO и отображение первого изображения. Затем следует установить фактор источника в GL_SRC_ALPHA, фактор приемника в GL_MINUS_SRC_ALPHA и нарисовать второе изображение с альфа равным 0.5. Данная пара факторов, вероятно, представляет собой наиболее часто используемую операцию наложения. Если картинка должна быть смешана из 75% первого изображения и 25% второго, нарисуйте первое изображение как и раньше, затем нарисуйте второе с альфа равным 0.25.

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

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

Предположим, что вы пишете программу рисования, и вам требуется создать кисть, постепенно добавляющую цвет таким образом, чтобы каждый росчерк кисти добавлял немного больше своего цвета к тому, что в данный момент находится на экране (например, каждый росчерк состоит из 10% цвета и 90% исходного изображения). Чтобы это сделать, нарисуйте изображение росчерка кисти с 10-ти процентным альфа, используя GL_SRC_ALPHA для источника и GL_ONE_MINUS_SRC_ALPHA для приемника. Заметьте, что вы можете изменять альфа для разных частей росчерка кисти (например, чтобы цвет в центре росчерка был более ярким, чем по его границе). Похожим образом, ластик может быть реализован путем установки его цвета в цвет фона.

Функции наложения, использующие цвета источника или приемника

GL_DST_COLOR или GL_ONE_MINUS_DST_COLOR для источника и GL_SRC_COLOR или GL_ONE_MINUS_SRC_COLOR для приемника позволяют вам эффективно выделять отдельные цветовые компоненты. Данная операция эквивалентна наложению простого фильтра, например, умножение красного компонента на 0.80, зеленого на 0.40, а синего на 0.72 имитирует просмотр сцены через фотографический фильтр, блокирующий 20% красного света, 60% зеленого и 28% синего.

Предположим, что вам требуется отобразить три полупрозрачные поверхности на непрозрачном фоне. Части одних поверхностей закрывают другие. Предположим, что самая дальняя поверхность пропускает 80% цвета, средняя – 40% цвета, а ближняя – 90%. Чтобы составить картинку, вначале нарисуйте задний фон с факторами источника и приемника по умолчанию, затем измените факторы на GL_SRC_ALPHA (для источника) и GL_ONE_MINUS_SRC_ALPHA (для приемника). Далее, нарисуйте самую дальнюю поверхность с альфа равным 0.2, среднюю с альфа равным 0.6, а самую ближнюю с альфа равным 0.1.

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

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

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

вы можете нарисовать полигон в форме дерева и наложить на него текстуру с изображением кроны; наблюдатель сможет видеть через части прямоугольной текстуры, не являющиеся частями дерева, если вы назначите им альфа равные 0. Этот метод, иногда называемый billboarding (billboard – большая подсвечиваемая вывеска на здании), намного быстрее, чем создание дерева из трехмерных полигонов. Например, на рисунке 6-1 дерево создано из двух одинаковых полигонов повернутых под углом 90 градусов друг к другу. Буфер глубины выключен, при рисовании обоих полигонов использовались методы вычисления факторов GL_SRC_ALPHA (для источника) и GL_ONE_MINUS_SRC_ALPHA (для приемника).

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

Рисунок 6-1. Создание трехмерного дерева с помощью техники billboarding: файл billboarding.cpp

6.1.3 Пример цветового наложения

В примере 6-1 рисуются два перекрывающихся цветных треугольника, каждый из которых имеет альфа, равное 0.75. Механизм цветового наложения включен, а факторы источника и приемника установлены в GL_SRC_ALPHAи GL_ONE_MINUS_SRC_ALPHA соответственно.

Когда программа стартует, слева отображается желтый треугольник, а справа голубой. Таким образом, в центре окна, где треугольники пересекаются, голубой накладывается на желтый (рисунок 6-2). Вы можете изменить порядок, в котором отображаются треугольники с помощью клавиши ‘t’.

Рисунок 6-2. Цветовое наложение двух треугольников

Пример 6-1. Пример цветового наложения: файл alpha.cpp

#include <GL/glut.h>

int leftFirst=GL_TRUE;

//Инициализация void init(void)

{

glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); glShadeModel(GL_FLAT); glClearColor(0.0,0.0,0.0,0.0);

}

void drawLeftTriangle(void)

{

//Нарисовать желтый треугольник на левой половине экрана glBegin(GL_TRIANGLES);

glColor4f(1.0,1.0,0.0,0.75);

glVertex3f(0.1,0.9,0.0);

glVertex3f(0.1,0.1,0.0);

glVertex3f(0.7,0.5,0.0);

glEnd();

}

void drawRightTriangle(void)

{

//Нарисовать голубой треугольник на правой половине экрана glBegin(GL_TRIANGLES);

glColor4f(0.0,1.0,1.0,0.75);

glVertex3f(0.9,0.9,0.0);

glVertex3f(0.3,0.5,0.0);

glVertex3f(0.9,0.1,0.0);

glEnd();

}

void display(void)

{

glClear(GL_COLOR_BUFFER_BIT); if(leftFirst)

{

drawLeftTriangle();

drawRightTriangle();

}

else

{

drawRightTriangle();

drawLeftTriangle();

}

glFlush();

}

void reshape(int w, int h)

{

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

if (w<=h) gluOrtho2D(0.0,1.0,0.0,1.0*(GLfloat)h/(GLfloat)w);

else gluOrtho2D(0.0,1.0*(GLfloat)w/(GLfloat)h,0.0,1.0);

}

void keyboard(unsigned char key,int x,int y)

{

switch(key)

{

case 't': case 'T':

leftFirst=!leftFirst;

glutPostRedisplay();

break; case 27:

exit(0);

break;

}

}

int main(int argc, char** argv)

{

glutInit(&argc,argv); glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB); glutInitWindowSize(200,200); glutCreateWindow("Blending Example"); init();

glutDisplayFunc(display);

glutReshapeFunc(reshape);

glutKeyboardFunc(keyboard);

glutMainLoop(); return 0;

}

Порядок, в котором рисуются треугольники, влияет на цвет региона их перекрытия. Когда первым рисуется левый треугольник, голубые фрагменты (источник) смешиваются с желтыми фрагментами, которые уже находятся в буфере кадра (приемник). Когда первым рисуется правый треугольник, желтые фрагменты накладываются на голубые. Поскольку все альфа величины равны 0.75, факторами наложения становятся значения 0.75 для источника и 1-0.75=0.25 для приемника. Другими словами фрагменты источника в некотором смысле прозрачны, но они оказывают больший эффект на результирующий цвет, чем фрагменты приемника.

6.1.4 Трехмерное цветовое наложение и буфер глубины

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

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

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

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

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

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

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

Пример 6-2 демонстрирует использование данного метода для рисования непрозрачных и прозрачных объектов в трехмерном пространстве. Внутри программы, клавиша ‘a’ запускает анимационную последовательность, в процессе которой полупрозрачный куб проходит через непрозрачную сферу (рисунок 6-3). Нажатие клавиши ‘r’ приводит объекты в начальное состояние. Когда полупрозрачные объекты пересекаются, для получения наилучших результатов рисуйте объекты от дальних к ближним.

Рисунок 6-3. Полупрозрачный куб и непрозрачная сфера

Пример 6-2. Трехмерное цветовое наложение: файл alpha3D.cpp

#include <GL/glut.h>

#define MAXZ 8.0 #define MINZ -8.0 #define ZINC 0.02

float solidZ=MAXZ;

float transparentZ=MINZ; GLuint sphereList,cubeList;

void init(void)

{

GLfloat mat_specular[]={1.0,1.0,1.0,0.15}; GLfloat mat_shininess[]={100.0};

GLfloat position[]={0.5,0.5,1.0,0.0};

glMaterialfv(GL_FRONT,GL_SPECULAR,mat_specular); glMaterialfv(GL_FRONT,GL_SHININESS,mat_shininess); glLightfv(GL_LIGHT0,GL_POSITION,position); glEnable(GL_LIGHTING);

glEnable(GL_LIGHT0); glEnable(GL_DEPTH_TEST); sphereList=glGenLists(1); glNewList(sphereList,GL_COMPILE);

glutSolidSphere(0.4,16,16);

glEndList();

cubeList=glGenLists(1); glNewList(cubeList,GL_COMPILE);

glutSolidCube(0.6);

glEndList();

}

void display(void)

{

GLfloat mat_solid[]={0.75,0.75,0.0,1.0}; GLfloat mat_zero[]={0.0,0.0,0.0,1.0}; GLfloat mat_transparent[]={0.0,0.8,0.8,0.6}; GLfloat mat_emission[]={0.0,0.3,0.3,0.6};

glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); glPushMatrix();

glTranslatef(-0.15,-0.15,solidZ); glMaterialfv(GL_FRONT,GL_EMISSION,mat_zero); glMaterialfv(GL_FRONT,GL_DIFFUSE,mat_solid); glCallList(sphereList);

glPopMatrix();

glPushMatrix();

glTranslatef(0.15,0.15,transparentZ);

glRotatef(15.0,1.0,1.0,0.0);

glRotatef(30.0,0.0,1.0,0.0); glMaterialfv(GL_FRONT,GL_EMISSION,mat_emission); glMaterialfv(GL_FRONT,GL_DIFFUSE,mat_transparent); glEnable(GL_BLEND);

glDepthMask(GL_FALSE); glBlendFunc(GL_SRC_ALPHA,GL_ONE); glCallList(cubeList); glDepthMask(GL_TRUE); glDisable(GL_BLEND);

glPopMatrix();

glutSwapBuffers();

}

void reshape(int w, int h)

{

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

if (w<=h) glOrtho(-1.5,1.5,-0.5*(GLfloat)h/(GLfloat)w,1.5*(GLfloat)h/(GLfloat)w,-

10.0,10.0); else

glOrtho(-1.5*(GLfloat)w/(GLfloat)h,1.5*(GLfloat)w/(GLfloat)h,-1.5,1.5,- 10.0,10.0);

glMatrixMode(GL_MODELVIEW); glLoadIdentity();

}

void animate()

{

if(solidZ<=MINZ || transparentZ >=MAXZ) glutIdleFunc(NULL);

else

{

solidZ-=ZINC; transparentZ+=ZINC; glutPostRedisplay();

}

}

void keyboard(unsigned char key,int x, int y)

{

switch (key)

{

case 'a': case 'A':

solidZ=MAXZ;

transparentZ=MINZ;

glutIdleFunc(animate);

break; case 'r': case 'R':

solidZ=MAXZ;

transparentZ=MINZ;

glutPostRedisplay();

break;

case 27: exit(0);

}

}

int main(int argc, char** argv)

{

glutInit(&argc,argv); glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGB|GLUT_DEPTH); glutInitWindowSize(500,500); glutCreateWindow("Three-Dimensional Blending"); init();

glutDisplayFunc(display);

glutReshapeFunc(reshape);

glutKeyboardFunc(keyboard);

glutMainLoop(); return 0;

}

6.2 Антиалиасинг

На некоторых изображениях, созданных с помощью OpenGL вы могли заметить, что линии выглядят «ступенчатыми», особенно если они близки к горизонтальным или вертикальным. Эти ступеньки появляются потому, что идеальная линия аппроксимируется сериями пикселей из пиксельной решетки экрана. «Ступенчатость» называется алиасинг (aliasing), и в данном разделе описывается техника антиалиасинга, позволяющая ее устранить. На рисунке 6-4 показаны две пары пересекающихся линий: слева пара несглаженных, а справа пара сглаженных путем антиалиасинга. Для демонстрации эффекта изображение было увеличено.

Рисунок 6-4. Сглаженные и несглаженные линии

На рисунке 6-5 показано, что диагональная линия в 1 пиксель толщиной покрывает площадь некоторых пикселей в большей степени, чем других. Вообще говоря, когда производится антиалиасинг, OpenGL рассчитывает величину покрытия для каждого фрагмента, базируясь на доле покрываемой им площади пикселя на экране. Рисунок 6- 5 демонстрирует эти величины для линии. В RGBA режиме OpenGL умножает эту величину на альфа величину фрагмента. Затем вы можете использовать получившееся альфа для цветового наложения фрагмента на соответствующий пиксель, уже находящийся в буфере кадра. В индексном цветовом режиме, OpenGL устанавливает 4

младших бита цветового индекса в зависимости от величины покрытия фрагмента (0000 – если фрагмент не попадает на пиксель и 1111 – при полном покрытии фрагментом пикселя). Загрузка цветовой таблицы и ее верное применение для получения выгоды от этих величин покрытия лежит на вас.

Рисунок 6-5. Определение величин покрытия

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

void glHint (GLenum target, GLenum hint);

Управляет определенными аспектами поведения OpenGL. Параметр target задает, какой именно аспект поведения контролируется, его возможные значения показаны в таблице 6-2. Значениями для параметра hint может быть GL_FASTEST (для самого эффективного по скорости метода действий), GL_NICEST(для самого высококачественного метода) или GL_DONT_CARE(для метода по выбору библиотеки). Интерпретация этих установок зависит от реализации, конкретная реализация OpenGL может вообще игнорировать эти установки.

Целевая установка GL_PERSPECTIVE_CORRECTION_HINT относится к тому, как цветовые величины и координаты текстуры интерполируются внутри примитива: линейно в экранном пространстве (относительно простой метод вычислений) или путем перспективной коррекции (которая требует больших вычислений). Часто системы производят линейную цветовую интерполяцию, поскольку результаты хотя и не являются технически верными, но визуально приемлемы; однако текстуры в большинстве случаев требуют интерполяции с коррекцией перспективы для того, чтобы быть визуально приемлемыми. Следовательно, реализация OpenGL может интерпретировать этот параметр для управления тем, какой метод интерполяции использовать.

Таблица 6-2. Значения, используемые в команде glHint()

Параметр

GL_POINT_SMOOTH_HINT GL_LINE_SMOOTH_HINT GL_POLYGON_SMOOTH_HINT

GL_FOG_SMOOTH_HINT

Смысл

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

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

(GL_NICEST) или для каждой вершины (GL_FASTEST)

GL_PERSPECTIVE_CORRECTION_HINT задает желаемое качество интерполяции цветов и координат текстуры

6.2.1 Сглаживание точек и линий

Для сглаживания точек или линий вам нужно включить антиалиасинг командой glEnable(), передав ей в качестве аргумента GL_POINT_SMOOTHили GL_LINE_SMOOTH

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

6.2.1.1Антиалиасинг в RGBA режиме

В режиме RGBA вам нужно включить механизм цветового наложения. Факторы, которые вы, скорее всего, будете при этом использовать – GL_SRC_ALPHA (для источника) и GL_ONE_MINUS_SRC_ALPHA (для приемника). в качестве альтернативы вы можете задать фактор GL_ONE(для приемника), что сделает линии несколько более яркими в точках пересечения. Теперь вы можете рисовать любые точки или линии они будут сглажены. Эффект от сглаживания наиболее заметен в случае использования достаточно высоких значений альфа. Помните, что поскольку вы используете цветовое наложение, вам, возможно, понадобится следить за порядком выбора фигур. Однако, в большинстве случаем, порядок вывода может быть проигнорирован без значительных визуальных последствий. В примере 6-3 инициализируются все необходимые режимы, и затем отображаются две пересекающиеся диагональные линии. Нажатие на клавишу ‘r’ во время работы программы поворачивает линии, чтобы можно было рассмотреть эффект от сглаживания линии, нарисованных под разными углами. Заметьте, что в данном примере выключен буфер глубины.

Пример 6-3. Сглаженные линии: файл aargb.cpp

#include <GL/glut.h>

float rotAngle=0;

void init(void)

{

glEnable(GL_LINE_SMOOTH); glEnable(GL_BLEND);

glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); glHint(GL_LINE_SMOOTH_HINT,GL_NICEST); glLineWidth(1.5);

glClearColor(0.0,0.0,0.0,0.0);

}

void display(void)

{

glClear(GL_COLOR_BUFFER_BIT); glColor3f(0.0,1.0,0.0); glPushMatrix();

glRotatef(-rotAngle,0.0,0.0,0.1); glBegin(GL_LINES);

glVertex2f(-0.5,0.5); glVertex2f(0.5,-0.5);

glEnd();

glPopMatrix();

glColor3f(0.0,0.0,1.0);

glPushMatrix();

glRotatef(rotAngle,0.0,0.0,0.1); glBegin(GL_LINES);

glVertex2f(0.5,0.5); glVertex2f(-0.5,-0.5);

glEnd();

glPopMatrix();

glFlush();

}

void reshape(int w, int h)

{

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

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