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

RedBook_OpenGL

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

12.1.1 Одномерные вычислители

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

12.1.1.1 Одномерный пример: простая кривая Безье

Программа, представленная в примере 12-1 рисует кубическую кривую Безье с использованием 4 контрольных точек, как показано на рисунке 12-1.

Рисунок 12-1. Кривая Безье

Пример 12-1. Кривая Безье с 4 контрольными точками: файл bezcurve.cpp

#include

GLfloat ctrlpoints[4][3]={ {-4.0,-4.0,0.0}, {-2.0,4.0,0.0}, {2.0,-4.0,0.0}, {4.0,4.0,0.0}

};

void init()

{

glClearColor(0.0,0.0,0.0,0.0); glShadeModel(GL_FLAT);

glMap1f(GL_MAP1_VERTEX_3,0.0,1.0,3,4,&ctrlpoints[0][0]); glEnable(GL_MAP1_VERTEX_3);

}

void display()

{

int i;

glClear(GL_COLOR_BUFFER_BIT); glColor3f(0.0,1.0,0.0); glBegin(GL_LINE_STRIP);

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

glEvalCoord1f((GLfloat)i/30.0);

glEnd();

//Контрольные точки glPointSize(5.0); glColor3f(1.0,1.0,0.0); glBegin(GL_POINTS);

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

glVertex3fv(&ctrlpoints[i][0]);

glEnd();

glFlush();

}

void reshape(int w, int h)

{

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

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

5.0*(GLfloat)h/(GLfloat)w,-5.0,5.0);

else

glOrtho(-5.0*(GLfloat)w/(GLfloat)h, 5.0*(GLfloat)w/(GLfloat)h,-5.0,5.0,-5.0,5.0);

glMatrixMode(GL_MODELVIEW); glLoadIdentity();

}

int main(int argc, char** argv)

{

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

glutCreateWindow("Bezier Curve with Four Control Points"); init();

glutDisplayFunc(display);

glutReshapeFunc(reshape);

glutMainLoop(); return 0;

}

Кубическая кривая Безье описывается 4 точками, которые фигурируют в примере в виде массива ctrlpoints[][]. Этот массив является одним из аргументов для команды glMap1f(). Полный список аргументов для этой команды следующий:

GL_MAP1_VERTEX3 Предоставлены трехмерные точки и должны быть сгенерированы трехмерные вершины

0.0Нижняя граница для u

1.0Верхняя граница для u

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

4Порядок сплайна, равный его степени плюс 1. В данном случае степень равна 3 (поскольку это кубический сплайн)

&ctrlpoints[][] Указатель на данные первой контрольной точки

Обратите внимание на то, что второй и третий аргументы команды управляют параметризацией кривой в то время как uменяется от 0.0 до 1.0, кривая проходит от своего начала до своего конца. Вызов команды glEnable() активизирует одномерный вычислитель для трехмерных вершин.

Сама кривая рисуется в функции display() между вызовами glBegin() и glEnd(). поскольку вычислитель активизирован, вызов команды glEvalCoord1f() аналогичен выполнению команды glVertex() с координатами вершины на кривой, соответствующими заданному параметру u.

12.1.1.2 Определение и вычисление одномерного вычислителя

Многочлен Бернштейна степени n(или порядка n+1) вычисляется по формуле

Если представляет набор контрольных точек (одно-, двух-, трех- или даже четырехмерных), то уравнение

представляет кривую Безье в процессе изменения uот 0.0 до 1.0. Чтобы представить ту же кривую, позволяя u меняться от до , а не между 0.0 и 1.0, вычислите

.

Команда glMap1() определяет одномерный вычислитель, использующий данные уравнения.

void glMap1{fd} (GLenum target, TYPE u1, TYPE u2, GLint stride, GLint order, const TYPE *points);

Определяет одномерный вычислитель. Аргумент target определяет, что именно задается контрольными точками (смотрите таблицу 12-1) и, как следствие, сколько величин должно быть задано в аргументе points. Точки могут представлять вершины, цветовые данные RGBA, вектора нормалей или координаты текстуры. Например, если задать для target значение GL_MAP1_COLOR_4, вычислитель генерирует цветовые данные в четырехмерном RGBA пространстве вдоль кривой. Величины параметров, перечисленные в таблице 12-1, также используются для активизации конкретного вычислителя до его вызова. Чтобы активизировать или деактивировать нужный вычислитель, передайте соответствующий аргумент командам glEnable() или glDisable() соответственно.

Следующие два параметра glMap1*() u1 и u2 задают диапазон изменения переменной u. Аргумент strideэто количество величин с плавающей точкой одинарной или двойной точности в каждом блоке хранилища, то есть это величина смещения между началом предыдущей контрольной точки и началом следующей.

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

Таблица 12-1. Типы контрольных точек для glMap1*()

Параметр

Смысл параметра

GL_MAP1_VERTEX_3

координаты вершины x, yи z

GL_MAP1_VERTEX_4

координаты вершины x, y, z и w

GL_MAP1_INDEX

цветовой индекс

GL_MAP1_COLOR_4

R, G, B, A

GL_MAP1_NORMAL

координаты нормали

GL_MAP1_TEXTURE_COORD_1

координата текстуры s

GL_MAP1_TEXTURE_COORD_2

координаты текстуры s и t

GL_MAP1_TEXTURE_COORD_3

координаты текстуры s, tи r

GL_MAP1_TEXTURE_COORD_4

координаты текстуры s, t, r и q

 

 

Одновременно может быть включено более одного вычислителя. Если у вас определены, например, два вычислителя GL_MAP1_VERTEX_3 и GL_MAP1_COLOR_4, то вызов команды glEvalCoord1() сгенерирует и позицию и цвет. Одновременно может быть активизирован только один вершинный вычислитель, даже если определены оба. Также одновременно может быть включен только один текстурный вычислитель. В других случаях, однако, вычислители могут использоваться для генерирования любой комбинации вершин, нормалей, цветов и координат текстуры. Если вы определите и активизируете два или более вычислителей одного и того же типа, будет использован тот из них, в котором наибольшее число измерений.

Для вычисления заданной и активизированной одномерной таблицы используйте команду glEvalCoord1*().

void glEvalCoord1{fd} (TYPE u); void glEvalCoord1{fd}v (TYPE* u);

Вызывает к исполнению процесс вычисления одномерных таблиц. Аргумент uзадает одномерную координату на кривой.

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

12.1.1.3 Определение одномерных доменных координат на кривой с равными промежутками

Вы можете использовать glEvalCoord1() с любыми значениями u, но наиболее частой

практикой является использование сетки величин с равными промежутками между ними, как показано ранее в примере 12-1. Чтобы получить такие величины, определите сетку командой glMapGrid1*() и примените ее, используя glEvalMesh1().

void glMapGrid1{fd} (GLint n, TYPE u1, TYPE u2);

Определяет сетку величин изменяющихся от u1 до u2 за n равных шагов.

void glEvalMesh1 (GLenum mode, GLint p1, GLint p2);

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

вызовам glEvalCoord1() для каждого шага от p1 до p2 включительно, где 0<=p1, p2<=n. С точки зрения кода, это эквивалентно следующему фрагменту:

glBegin(GL_POINTS); /* или glBegin(GL_LINES); */ for(i=p1;i<=p2;i++)

glEvalCoord1(u1+i*(u2-u1)/n);

glEnd();

за исключением того, что если i=0 или i=n, glEvalCoord1() вызывается непосредственно с параметрами u1 или u2.

12.1.2 Двумерные вычислители

Двумерный случай практически идентичен одномерному за тем исключением, что все команды должны принимать в расчет 2 параметра u и v. Точки, цвета, нормали и координаты текстуры должны поставляться по поверхности, а не по кривой.

Математически описание поверхности Безье задается в виде

где величины представляют собой набор из m*n контрольных точек, а функции B это те же многочлены Бернштейна, что и в одномерном случае. Как и раньше величины

могут являться вершинами, нормалями, цветами или координатами текстуры.

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

1.Определите вычислитель (или вычислители) с помощью glMap2*().

2.Активизируйте их, передав нужную величину (или величины) команде glEnable().

3.Вызовите их к исполнению либо с помощью команд glEvalCoord2() между glBegin() и glEnd(), либо определив и применив сетку величин с помощью команд glMapGrid2() и glEvalMesh2().

12.1.2.1 Определение и вычисление двумерного вычислителя

Используйте glMap2*() и glEvalCoord2*() для определения и выполнения двумерного вычислителя.

void glMap2{fd} (GLenum target, TYPE u1, TYPE u2, GLint ustride, GLint uorder, TYPE v1, TYPE v2,

GLint vstride, GLint vorder, TYPE* points);

Параметр target может принимать любые значения из таблицы 12-1, но в данном случае MAP1 нужно изменить на MAP2 в именах всех параметров. Как и раньше, эти же

значения используются в командах glEnable() и glDisable() для активизации или деактивации нужных вычислителей. Минимальное и максимальное значения аргументов u и v, задаются в виде аргументов u1, u2, v1 и v2, соответственно. Аргументы ustride и vstride задают количество чисел однократной или двойной точности между независимыми установками величин u и v, позволяя пользователю выбирать подрегион контрольных точек из намного большего по размеру массива. Например,

если данные заданы в форме

GLfloat ctrlpoints[100][100][3];

и вы хотите использовать только подмножество точек размером 4x4, начинающееся с точки ctrlpoints[20][30], установите ustrideв 100*3, а vstride в 3. Аргумент points в этом случае должен быть задан как &ctrlpoints[20][30][0]. Наконец, аргументы, определяющие порядок uorder и vorder, могут иметь разные значения, позволяя создавать, например, поверхности квадратные в одном направлении и кубические в другом.

void glEvalCoord2{fd} (TYPE u, TYPE v); void glEvalCoord2{fd}v (TYPE* u, TYPE* v);

Вызывает к исполнению заданные и активизированные двумерные вычислители. Аргументы uи v являются величинами (или указателями на величины в случае векторной версии команды) доменных координат (координат на поверхности или кривой). Если активизирован один из вершинных вычислителей (GL_MAP2_VERTEX_3 или GL_MAP2_VERTEX_4), то координаты нормалей к поверхности вычисляются автоматически. Если активизирован режим автоматической генерации нормалей (с помощью аргумента GL_AUTO_NORMAL в команде glEnable()), эта нормаль ассоциируется с вычисленной вершиной. Если этот режим выключен, для вычисления нормали используется текущий активизированный вычислитель. Если же таковой отсутствует, используется текущий вектор нормали.

12.1.2.2 Двумерный пример: поверхность Безье

Пример 12-2 отображает каркасную поверхность Безье, показанную на рисунке 12-2, с использованием вычислителя. В этом примере поверхность рисуется в виде 9 изогнутых линий в каждом направлении. Каждая линия состоит из 30 сегментов. Для получения цельной программы, добавьте функции reshape() и main() из примера 12-1.

Рисунок 12-2. Поверхность Безье

Пример 12-2. Поверхность Безье: файл bezsurf.cpp

GLfloat ctrlpoints[4][4][3]={ {{-1.5,-1.5,4.0},{-0.5,-1.5,2.0},{0.5,-1.5,-1.0},{1.5,-

1.5,2.0}}, {{-1.5,-0.5,1.0},{-0.5,-0.5,3.0},{0.5,-0.5,0.0},{1.5,-0.5,-

1.0}}, {{-1.5,0.5,4.0},{-0.5,0.5,0.0},{0.5,0.5,3.0},{1.5,0.5,4.0}},

{{-1.5,1.5,-2.0},{-0.5,1.5,-2.0},{0.5,1.5,0.0},{1.5,1.5,-1.0}}

};

void display()

{

int i,j;

glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); glColor3f(0.0,0.0,0.0);

glLoadIdentity();

glRotatef(85.0,1.0,1.0,1.0);

for(j=0;j<=8;j++)

{

glBegin(GL_LINE_STRIP); for(i=0;i<=30;i++)

glEvalCoord2f((GLfloat)i/30.0,(GLfloat)j/8.0);

glEnd(); glBegin(GL_LINE_STRIP);

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

glEvalCoord2f((GLfloat)j/8.0,(GLfloat)i/30.0);

glEnd();

}

glFlush();

}

void init()

{

glClearColor(1.0,1.0,1.0,0.0);

glLineWidth(2.0);

glMap2f(GL_MAP2_VERTEX_3,0,1,3,4,0,1,12,4,&ctrlpoints[0][0][0]); glEnable(GL_MAP2_VERTEX_3); glMapGrid2f(20,0.0,1.0,20,0.0,1.0); glEnable(GL_DEPTH_TEST);

glShadeModel(GL_FLAT);

}

12.1.2.3 Определение двумерных доменных координат на поверхности с равными промежутками

В двух измерениях команды glMapGrid2*() и glEvalMesh2() используются так же как их одномерные версии, за тем исключением, что должна быть задана информация и о u, и о v.

void glMapGrid2{fd} (GLint nu, TYPE u1, TYPE u2, GLint vn, TYPE v1, TYPE v2); void glEvalMesh2 (GLenum mode, GLint i1, GLint i2, GLint j1, GLint j2);

Определяют двумерную сетку величин, проходящих от u1 до u2 за nu шагов с равными промежутками и от v1 до v2 за nv с равными промежутками (glMapGrid2*()) и затем применяют эту сетку ко всем активизированным вычислителям (glEvalMesh2()). Единственное существенное отличие от одномерного случая заключается в том, что аргумент mode команды glEvalMesh2() помимо GL_POINT и GL_LINE может принимать и значение GL_FILL. GL_FILL генерирует закрашенные полигоны с помощью четырехугольников. Если говорить точно, вызов glEvalMesh2() практически эквивалентен одному из трех следующих блоков кода. («Практически» потому, что, когда i=nu или j=nv, параметры равны u2 или v2, а не u1+nu*(u2-u1)/nu или v1+nv*(v2-v1)/nv эти числа могут различаться из-за ошибок округления.)

glBegin(GL_POINTS);

/* режим GL_POINT */

for(i=nu1;i<=nu2;i++)

 

for(j=nv1;j<=nv2;j++)

glEvalCoord2(u1+i*(u2-u1)/nu, v1+j*(v2-v1)/nv);

glEnd();

или

for(i=nu1;i<=nu2;i++) /* режим GL_LINE */

{

glBegin(GL_LINES); for(j=nv1;j<=nv2;j++)

glEvalCoord2(u1+i*(u2-u1)/nu, v1+j*(v2-v1)/nv);

glEnd();

}

for(j=nv1;j<=nv2;j++)

{

glBegin(GL_LINES); for(i=nu1;i<=nu2;i++)

glEvalCoord2(u1+i*(u2-u1)/nu, v1+j*(v2-v1)/nv);

glEnd();

}

или

for(i=nu1;i<=nu2;i++) /* режим GL_FILL */

{

glBegin(GL_QUAD_STRIP); for(j=nv1;j<=nv2;j++)

glEvalCoord2(u1+i*(u2-u1)/nu, v1+j*(v2-v1)/nv); glEvalCoord2(u1+(i+1)*(u2-u1)/nu, v1+j*(v2-v1)/nv);

glEnd();

}

Пример 12-3 показывает отличия, которые нужно сделать в примере 12-2, чтобы нарисовать ту же поверхность, но с применением glMapGrid2() и glEvalMesh2() для разделения квадрата доменных координат на сетку величин размерностью 8x8. Пример 12-3 также добавляет освещение и закраску, как показано на рисунке 12-3.

Рисунок 12-3. Освещенная и закрашенная поверхность Безье, нарисованная по сетке

доменных координат

Пример 12-3. Освещенная и закрашенная поверхность Безье, нарисованная по сетке доменных координат: файл bezmesh.cpp

void display()

{

glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); glLoadIdentity();

glRotatef(85.0,1.0,1.0,1.0); glEvalMesh2(GL_FILL,0,20,0,20); glFlush();

}

void init()

{

glClearColor(1.0,1.0,1.0,0.0);

glMap2f(GL_MAP2_VERTEX_3,0,1,3,4,0,1,12,4,&ctrlpoints[0][0][0]); glEnable(GL_MAP2_VERTEX_3); glMapGrid2f(20,0.0,1.0,20,0.0,1.0); glEnable(GL_DEPTH_TEST);

glEnable(GL_AUTO_NORMAL);

//Источники света

GLfloat ambient[]={0.2,0.2,0.2,1.0}; GLfloat position[]={0.0,0.0,2.0,1.0}; GLfloat mat_diffuse[]={0.6,0.6,0.6,1.0}; GLfloat mat_specular[]={1.0,1.0,1.0,1.0}; GLfloat mat_shininess[]={50.0};

glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glLightfv(GL_LIGHT0,GL_AMBIENT,ambient); glLightfv(GL_LIGHT0,GL_POSITION,position);

glMaterialfv(GL_FRONT,GL_DIFFUSE,mat_diffuse); glMaterialfv(GL_FRONT,GL_SPECULAR,mat_specular); glMaterialfv(GL_FRONT,GL_SHININESS,mat_shininess);

}

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

Пример 12-3 активизирует одновременно 2 вычислителя: первый генерирует трехмерные точки на той же поверхности Безье, что и пример 12-3, а второй генерирует координаты текстуры. В данном случае в качестве координат текстуры выступают те же значения uи v, которые используются для вычисления вершин, но для того, чтобы применить их к поверхности, требуется определить отдельный вычислитель.

Второй вычислитель определяется на квадрате с углами в точках (0,0), (0, 1), (1,0) и (1,1); он генерирует (0,0) в углу (0,0), (0,1) в углу (0,1) и так далее. Поскольку он имеет порядок 2 (являясь линейным первая степень плюс 1), вычисление этой текстуры в точке (u,v) дает координаты текстуры (s,t). Он активизируется в то же время, что и вершинный вычислитель, таким образом, на рисуемую поверхность воздействуют оба, что можно увидеть на рисунке 12-4. Если вы хотите, чтобы текстура повторилась 3 раза, измените каждую 1.0 в массиве texpts[ ][ ][ ].

Рисунок 12-4. Текстурированная поверхность Безье

Пример 12-4. Использование вычислителей для текстур: файл texturesurf.cpp

#include

#include

GLfloat ctrlpoints[4][4][3]={ {{-1.5,-1.5,4.0},{-0.5,-1.5,2.0},{0.5,-1.5,-1.0},{1.5,-

1.5,2.0}}, {{-1.5,-0.5,1.0},{-0.5,-0.5,3.0},{0.5,-0.5,0.0},{1.5,-0.5,-

1.0}}, {{-1.5,0.5,4.0},{-0.5,0.5,0.0},{0.5,0.5,3.0},{1.5,0.5,4.0}},

{{-1.5,1.5,-2.0},{-0.5,1.5,-2.0},{0.5,1.5,0.0},{1.5,1.5,-1.0}}

};

GLfloat texpts[2][2][2]={{{0.0,0.0},{0.0,1.0}},{{1.0,0.0},{1.0,1.0}}};

void display()

{

glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); glColor3f(1.0,1.0,1.0); glEvalMesh2(GL_FILL,0,20,0,20);

glFlush();

}

#define imageWidth 64 #define imageHeight 64

GLubyte image[3*imageWidth*imageHeight];

void makeImage()

{

int i,j; float ti,tj;

for(i=0;i<="h)" glOrtho(-4.0,4.0,-4.0*(GLfloat)h (GLfloat)w, 4.0*(GLfloat)h (GLfloat)w,-4.0,4.0); else glOrtho(-4.0*(GLfloat)w (GLfloat)h, 4.0*(GLfloat)w (GLfloat)h,-4.0,4.0,-4.0,4.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glRotatef(85.0,1.0,1.0,1.0); } int main(int argc, char** argv) { glutInit(&argc,argv); glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB|GLUT_DEPTH); glutInitWindowSize(500,500); glutInitWindowPosition(100,100);

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