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

RedBook

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

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

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

glMatrixMode(GL_MODELVIEW); glLoadIdentity();

}

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

{

switch(key)

{

case 'r': case 'R':

rotAngle+=10;

if (rotAngle>=360) rotAngle=0;

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("Antialiased Lines"); init();

glutDisplayFunc(display);

glutReshapeFunc(reshape);

glutKeyboardFunc(keyboard);

glutMainLoop(); return 0;

}

6.2.1.2Антиалиасинг в индексном режиме

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

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

Пример 6-4. Сглаживание в индексном режиме: файл aaindex.cpp

#include <GL/glut.h>

#define RAMPSIZE 16 #define RAMP1START 32 #define RAMP2START 48

float rotAngle=0;

void init(void)

{

int i;

for (i=0;i<RAMPSIZE;i++)

{

GLfloat shade; shade=(GLfloat)i/(GLfloat)RAMPSIZE;

glutSetColor(RAMP1START+(GLint)i,0.,shade,0.);

glutSetColor(RAMP2START+(GLint)i,0.,0.,shade);

}

glEnable(GL_LINE_SMOOTH); glHint(GL_LINE_SMOOTH_HINT,GL_NICEST); glLineWidth(1.5); glClearIndex((GLfloat)RAMP1START);

}

void display(void)

{

glClear(GL_COLOR_BUFFER_BIT); glIndexi(RAMP1START); glPushMatrix();

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

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

glEnd();

glPopMatrix();

glIndexi(RAMP2START);

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

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

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

glMatrixMode(GL_MODELVIEW); glLoadIdentity();

}

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

{

switch(key)

{

case 'r': case 'R':

rotAngle+=10;

if (rotAngle>=360) rotAngle=0; glutPostRedisplay();

break; case 27:

exit(0);

break;

}

}

int main(int argc, char** argv)

{

glutInit(&argc,argv); glutInitDisplayMode(GLUT_SINGLE|GLUT_INDEX); glutInitWindowSize(200,200); glutCreateWindow("Antialiasing in Color-Index mode"); init();

glutDisplayFunc(display);

glutReshapeFunc(reshape);

glutKeyboardFunc(keyboard);

glutMainLoop(); return 0;

}

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

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

Трюк, описанный в разделе «Трехмерное цветовое наложение» может быть также

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

6.2.2 Сглаживание полигонов

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

Замечание: Если вы рисуете ваши полигоны в виде точек в вершинах или границы

– то есть, передавая аргумент GL_POINT или GL_LINE команде glPolygonMode() – применяется техника сглаживания точек или линий, описанная ранее (если сглаживание включено). Оставшаяся часть данного раздела посвящена сглаживанию полигонов в режиме их отображения GL_FILL.

В теории, вы можете сглаживать полигоны и в RGBA, и в индексном цветовых режимах.

Однако пересечение объектов оказывает на сглаживание полигонов значительно большее воздействие, чем оно влияет на сглаживание точек и линий, и, следовательно, порядок отображения и точность наложения играют критическую роль. На сомом деле они грают настолько критическую роль, что если вы сглаживаете более чем один полигон, вам нужно упорядочивать полигоны от передних к задним и использовать команду glBlendFunc() с параметрами GL_SRC_ALPHA_SATURATE (для источника) и GL_ONE (для приемника). Следовательно, сглаживание полигонов в индексном режиме не является практически применимым.

Чтобы сглаживать полигоны в RGBAрежиме, вы используете альфа величины для представления величины покрытия ребер полигона. Вам следует включить сглаживание полигонов передачей аргумента GL_POLYGON_SMOOTH команде glEnable(). Это приведет к тому, что пикселям на ребрах полигона будут присвоены частичные альфа величины в зависимости от величины покрытия, так же как при сглаживании точек и линий. Также, если вы захотите, вы можете задать величину для GL_POLYGON_SMOOTH_HINT. Теперь вам следует должным образом наложить друг на друга перекрывающиеся ребра. Во-первых, выключите буфер глубины, чтобы иметь контроль над тем, как накладываются друг на друга перекрывающиеся пиксели. Затем установите факторы наложения в GL_SRC_ALPHA_SATURATE (для источника) и GL_ONE

(для приемника). С заданной таким образом функцией наложения результирующий

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

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

6.3 Туман

Иногда компьютерные изображения могут казаться нереально четкими и резкими. Антиалиасинг делает объекты более реалистичными за счет сглаживания их ребер. Кроме того, вы можете увеличить реалистичность всего изображения, добавив туман, который постепенно размывает объекты в зависимости от дистанции до наблюдателя. «Туман» -- это довольно общий термин, описывающий несколько похожих форм атмосферных явлений (дым от горения, дымка, туман над болотом, смог и так далее). Туман просто необходим для различных программ визуальной симуляции, которым требуется имитация ограниченной видимости. Он часто применяется в симуляторах полетов.

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

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

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

6.3.1 Использование тумана

Использовать туман очень просто. Вы включаете его, передавая аргумент GL_FOG, команде glEnable(). Также вы выбираете цвет тумана и уравнение, управляющее его плотностью, с помощью команды glFog*(). Кроме того, вы можете установить качество расчета тумана с помощью аргумента GL_FOG_HINTкоманды glHint(). В примере 6-5 отображаются 5 красных сфер, все на разных дистанциях от наблюдателя. Клавиша ‘f’ выбирает уравнение плотности тумана. Сами уравнения объясняются далее.

Рисунок 6-6. Пять затуманенных сфер

Пример 6-5. Пять затуманенных сфер в RGBA режиме: файл fog.cpp

#include <windows.h> #include <GL/glut.h>

GLint fogMode;

void init(void)

{

GLfloat position[]={0.5,0.5,3.0,0.0}; glEnable(GL_DEPTH_TEST); glLightfv(GL_LIGHT0,GL_POSITION,position); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0);

{

GLfloat mat[3]={0.1745,0.01175,0.01175}; glMaterialfv(GL_FRONT,GL_AMBIENT,mat); mat[0]=0.61424;mat[1]=0.04135;mat[2]=0.04136; glMaterialfv(GL_FRONT,GL_DIFFUSE,mat); mat[0]=0.727811;mat[1]=0.626959;mat[2]=0.626959; glMaterialfv(GL_FRONT,GL_SPECULAR,mat); glMaterialf(GL_FRONT,GL_SHININESS,0.6*128.0);

}

glEnable(GL_FOG);

{

GLfloat fogColor[4]={0.5,0.5,0.5,1.0}; fogMode=GL_EXP; glFogi(GL_FOG_MODE,fogMode); glFogfv(GL_FOG_COLOR,fogColor); glFogf(GL_FOG_DENSITY,0.35); glHint(GL_FOG_HINT,GL_NICEST); glFogf(GL_FOG_START,1.0); glFogf(GL_FOG_END,5.0);

}

glClearColor(0.5,0.5,0.5,1.0);

}

void renderSphere(GLfloat x ,GLfloat y, GLfloat z)

{

glPushMatrix();

glTranslatef(x,y,z);

glutSolidSphere(0.4,16,16);

glPopMatrix();

}

void display(void)

{

glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

renderSphere(-2.,-0.5,-1.0); renderSphere(-1.,-0.5,-2.0); renderSphere(0.,-0.5,-3.0); renderSphere(1.,-0.5,-4.0); renderSphere(2.,-0.5,-5.0); glFlush();

}

void reshape(int w, int h)

{

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

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

10.0,10.0); else

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

glMatrixMode(GL_MODELVIEW); glLoadIdentity();

}

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

{

switch (key)

{

case 'f': case 'F':

if(fogMode==GL_EXP)

{

fogMode=GL_EXP2;

MessageBox(NULL,"Fog mode is GL_EXP2","Five Fogged Spheres in RGBA mode",MB_OK);

}

else if(fogMode==GL_EXP2)

{

fogMode=GL_LINEAR;

MessageBox(NULL,"Fog mode is GL_LINEAR","Five Fogged Spheres in RGBA mode",MB_OK);

}

else if(fogMode==GL_LINEAR)

{

fogMode=GL_EXP;

MessageBox(NULL,"Fog mode is GL_EXP","Five Fogged Spheres in

RGBA mode",MB_OK);

}

glFogi(GL_FOG_MODE,fogMode); glutPostRedisplay();

break; case 27:

exit(0);

break;

}

}

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)

{

char* argv=""; int argc=0;

glutInit(&argc,&argv); glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB|GLUT_DEPTH); glutInitWindowSize(500,500);

glutCreateWindow("Five Fogged Spheres in RGBA mode"); init();

glutDisplayFunc(display);

glutReshapeFunc(reshape);

glutKeyboardFunc(keyboard);

glutMainLoop(); return 0;

}

6.3.2 Уравнения расчета плотности тумана

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

В этих трех уравнениях zдистанция в видовых координатах от точки наблюдения до центра фрагмента. Величины density, start и end задаются командой glFog*(). Фактор fиспользуется по-разному в зависимости от того, работаете ли вы в RGBA или в индексном цветовом режиме.

void glFog{if} (GLenum pname, TYPE param); void glFog{if}v (GLenum pname, TYPE *params);

Задает параметры и функцию для вычисления тумана. Если pname имеет значение GL_FOG_MODE, то param может принимать значения GL_EXP (значение по умолчанию), GL_EXP2 или GL_LINEAR и задает метод вычисления фактора тумана. Если pname равен

GL_FOG_DENSITY, GL_FOG_STARTили GL_FOG_END, то param содержит (или для векторной версии команды указывает на) величины density, start или end для использования в уравнениях. (Значения по умолчанию – 1, 0 и 1 соответственно.) В RGBA режиме pname может также содержать значение GL_FOG_COLOR, в этом случае params указывает на четверку величин, задающую RGBA цвет тумана.

Соответствующее значение pname для индексного режима – GL_FOG_INDEX, в этом случае param должен содержать единственную величину, задающую цветовой индекс тумана.

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

Рисунок 6-7. Уравнения плотности тумана

6.3.2.1Туман в RGBA режиме

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

,

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

6.3.2.2Туман в индексном режиме

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

,

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

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

Возможно, вы захотите использовать glClearIndex(), чтобы инициализировать цвет заднего фона индексом последнего цвета в карте; таким образом, полностью затуманенные объекты будут совмещены с задним фоном. Похожим образом, до того, как объекты нарисованы, вам следует вызвать команду glIndex*() и передать ей индекс первого цвета в карте (цвета незатуманенного объекта). Кроме того, если вы хотите нарисовать несколько затуманенных объектов разного цвета, вам следует

загрузить соответствующее количество цветовых карт и вызывать glIndex*() перед

рисованием каждого объекта. Пример 6-6 иллюстрирует инициализацию всех нужных аспектов для использования тумана в индексном режиме.

Пример 6-6. Туман в индексном режиме: файл fogindex.cpp

#include <windows.h> #include <GL/glut.h>

#define NUM_COLORS 32 #define RAMPSTART 16

void init(void)

{

int i;

glEnable(GL_DEPTH_TEST); for(i=0;i<NUM_COLORS;i++)

{

GLfloat shade; shade=(GLfloat)(NUM_COLORS-i)/(GLfloat)NUM_COLORS; glutSetColor(RAMPSTART+i,shade,shade,shade);

}

glEnable(GL_FOG); glFogi(GL_FOG_MODE,GL_LINEAR); glFogi(GL_FOG_INDEX,NUM_COLORS); glFogf(GL_FOG_DENSITY,0.35); glFogf(GL_FOG_START,1.0); glFogf(GL_FOG_END,6.0); glHint(GL_FOG_HINT,GL_NICEST);

glClearIndex((GLfloat)(NUM_COLORS+RAMPSTART-1));

}

void renderSphere(GLfloat x ,GLfloat y, GLfloat z)

{

glPushMatrix();

glTranslatef(x,y,z);

glutWireSphere(0.4,16,16);

glPopMatrix();

}

void display(void)

{

glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); glIndexi(RAMPSTART);

renderSphere(-2.,-0.5,-1.0); renderSphere(-1.,-0.5,-2.0); renderSphere(0.,-0.5,-3.0); renderSphere(1.,-0.5,-4.0); renderSphere(2.,-0.5,-5.0); glFlush();

}

void reshape(int w, int h)

{

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

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

10.0,10.0); else

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

glMatrixMode(GL_MODELVIEW); glLoadIdentity();

}

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)

{

char* argv=""; int argc=0;

glutInit(&argc,&argv);

glutInitDisplayMode(GLUT_SINGLE|GLUT_INDEX|GLUT_DEPTH); glutInitWindowSize(500,500);

glutCreateWindow("Fog in Color-Index Mode"); init();

glutDisplayFunc(display);

glutReshapeFunc(reshape);

glutMainLoop(); return 0;

}

6.4 Смещение полигонов

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

который в результате выглядит недостаточно качественно и часто называется

«простроченным» («stiching»).

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

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

Рисунок 6-8. Залитые и проволочные торусы с полигональным смещением (справа) и без него (слева)

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

GL_POLYGON_OFFSET_FILL, GL_POLYGON_OFFSET_LINE или

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