
RedBook
.pdf
glutCreateWindow(?Using Evaluators for Textures?); init(); glutDisplayFunc(display); glutReshapeFunc(reshape); glutMainLoop(); return 0; }< pre>
12.2 Интерфейс GLU NURBS
Хотя единственными непосредственно доступными в OpenGL примитивами для рисования кривых и поверхностей являются вычислители, и даже, несмотря на то, что они могут быть весьма эффективно реализованы на аппаратном уровне, приложения часто обращаются к ним через высокоуровневые библиотеки. Библиотека утилит GLU предоставляет интерфейс NURBS, построенный поверх команд OpenGL для работы с вычислителями.
12.2.1Простой пример NURBS
Если вы разбираетесь в NURBS, написать код для манипулирования NURBS в OpenGL относительно просто, даже если вы хотите использовать освещение или текстурирование. Для того, чтобы нарисовать кривую или поверхность NURBS выполните следующие шаги.
1.Если вы намереваетесь использовать освещение на поверхности NURBS, вызовите glEnable() с аргументом GL_AUTO_NORMAL для активизации режима автоматического вычисления нормалей (вы также можете вычислить свои собственные).
2.Используйте функцию gluNewNurbsRenderer() для создания нового объекта NURBS и получения указателя на него. На этот объект вы будете ссылаться при построении кривой или поверхности NURBS.
3.Если хотите, вызовите gluNurbsProperty() для установки значений различных свойств, например, максимального размера линий или полигонов, используемых для визуализации кривой или поверхности. gluNurbsProperty() также позволяет активизировать режим, в котором тесселированные геометрические данные могут быть получены через интерфейс возвратно – вызываемых функций.
4.Если вы хотите принимать уведомления об ошибках, вызовите gluNurbsCallback(). (Проверка ошибок может снизить быстродействие программы, но, тем не менее, ее наличие настойчиво рекомендуется.) gluNurbsCallback() также позволяет задать возвратные функции, которые будут вызываться для извлечения тесселированных геометрических данных.
5.Начните описание вашей кривой или поверхности, вызвав gluBeginCurve() или gluBeginSurface().
6.Сгенерируйте и визуализируйте вашу кривую или поверхность. Вы должны хотя бы один раз вызвать функцию gluNurbsCurve() или gluNurbsSurface(),
передавая им контрольные точки (рациональные или нерациональные),
узловые последовательности и порядок полиномиальной базисной функции для вашего объекта NURBS. Для указания нормалей и/или координат текстуры вам могут понадобиться дополнительные вызовы этих функций.
7.Вызовите gluEndCurve() или gluEndSurface(), завершая описание кривой или поверхности.
Пример 12-5 визуализирует поверхность NURBS в форме симметричного холма с контрольными точками, изменяющимися от -3.0 до 3.0. Базисная функция представляет собой кубический B – сплайн, но узловая последовательность является неравномерной, заставляя функцию вести себя в каждом направлении как кривая Безье. Поверхность освещена, имеет темно-серое диффузное отражение и белый зеркальный блик. Поверхность в каркасном и закрашенном виде можно увидеть на рисунке 12-5.
Рисунок 12-5. Поверхность NURBS

Пример 12-5. Поверхность NURBS: файл surface.cpp
#include
#include
#ifndef CALLBACK #define CALLBACK #endif
GLfloat ctrlpoints[4][4][3]; int showPoints=0; GLUnurbsObj *theNurb;
void init_surface()
{
int u,v;
for (u=0;u<4;u++)
{
for(v=0;v<4;v++)
{
ctrlpoints[u][v][0]=2.0*((GLfloat)u-1.5); ctrlpoints[u][v][1]=2.0*((GLfloat)v-1.5);
if((u==1||u==2)&&(v==1||v==2))
ctrlpoints[u][v][2]=3.0;
else
ctrlpoints[u][v][2]=-3.0;
}
}
}
void CALLBACK nurbsError(GLenum errorCode)
{
char message[100];
sprintf(message,"NURBS error: %s\n",gluErrorString(errorCode)); MessageBox(NULL,message,"NURBS surface",MB_OK);
exit(0);
}
void display()
{
GLfloat knots[8]={0.0,0.0,0.0,0.0,1.0,1.0,1.0,1.0}; int i,j;
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

glPushMatrix();
glRotatef(330.0,1.0,0.0,0.0);
glScalef(0.5,0.5,0.5);
gluBeginSurface(theNurb);
gluNurbsSurface(theNurb,8,knots,8,knots,4*3,3, &ctrlpoints[0][0][0],4,4,GL_MAP2_VERTEX_3);
gluEndSurface(theNurb);
if(showPoints)
{
glPointSize(5.0); glDisable(GL_LIGHTING); glColor3f(1.0,1.0,0.0); glBegin(GL_POINTS);
for(i=0;i<4;i++)
for(j=0;j<4;j++)
glVertex3fv(&ctrlpoints[i][j][0]);
glEnd(); glEnable(GL_LIGHTING);
}
glPopMatrix();
glFlush();
}
void init()
{
GLfloat mat_diffuse[]={0.7,0.7,0.7,1.0}; GLfloat mat_specular[]={1.0,1.0,1.0,1.0}; GLfloat mat_shininess[]={100.0};
glClearColor(0.0,0.0,0.0,0.0); glMaterialfv(GL_FRONT,GL_DIFFUSE,mat_diffuse); glMaterialfv(GL_FRONT,GL_SPECULAR,mat_specular); glMaterialfv(GL_FRONT,GL_SHININESS,mat_shininess);
glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_DEPTH_TEST); glEnable(GL_AUTO_NORMAL); glEnable(GL_NORMALIZE);
init_surface();
theNurb=gluNewNurbsRenderer(); gluNurbsProperty(theNurb,GLU_SAMPLING_TOLERANCE,2.0); gluNurbsProperty(theNurb,GLU_DISPLAY_MODE,GLU_FILL); gluNurbsCallback(theNurb,GLU_ERROR,(void(__stdcall
*)(void))nurbsError);
}
void reshape(int w, int h)
{
glViewport(0,0,(GLsizei) w, (GLsizei) h); glMatrixMode(GL_PROJECTION); glLoadIdentity();
gluPerspective(45.0,(GLdouble)w/(GLdouble)h,3.0,8.0); glMatrixMode(GL_MODELVIEW);
glLoadIdentity(); glTranslatef(0.0,0.0,-5.0);
}
void keyboard(unsigned char key,int x,int y)

{
switch(key)
{
case 'c': case 'C':
showPoints=!showPoints;
glutPostRedisplay();
break;
}
}
int main(int argc, char** argv)
{
glutInit(&argc,argv); glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB|GLUT_DEPTH); glutInitWindowSize(500,500); glutInitWindowPosition(100,100); glutCreateWindow("NURBS surface");
init();
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutKeyboardFunc(keyboard);
glutMainLoop(); return 0;
}
12.2.2 Управление объектом NURBS
Как показано в примере 12-5, функция gluNewNurbsRenderer() возвращает новый объект NURBS, чей тип определен в виде указателя на структуру GLUnurbsObj. Вы должны создать этот объект до вызова любой функции, связанной с NURBS. Когда вы завершите работу с объектом NURBS, вы можете использовать функцию gluDeleteNurbsRenderer() для освобождения всей используемой памяти.
GLUnurbsObj* gluNewNurbsRenderer (void);
Создает новый объект NURBS – nobj и возвращает указатель на него или ноль, если OpenGL не может выделить память для нового объекта NURBS.
void gluDeleteNurbsRenderer (GLUnurbsObj* nobj);
Уничтожает объект NURBS nobj.
12.2.2.1 Управление свойствами визуализации NURBS
Набор свойств, ассоциированных с объектом NURBS, влияет на то, как визуализируется объект. Эти свойства задают, как визуализируется поверхность (например, закрашенной или каркасной), должны ли тесселированные вершины отображаться или возвращаться в программу, а также точность тесселяции.
void gluNurbsProperty (GLUnurbsObj* nobj, GLenum property, GLfloat value);
Управляет атрибутами NURBS объекта nobj. Аргумент property задает конфигурируемое свойство и может принимать значения GLU_DISPLAY_MODE, GLU_NURBS_MODE, GLU_CULLING, GLU_SAMPLING_METHOD, GLU_SAMPLING_TOLERANCE, GLU_PARAMETRIC_TOLERANCE, GLU_U_STEP, GLU_V_STEP или
GLU_AUTO_LOAD_MATRIX. Аргумент value задает величину, в которую должно быть установлено свойство.
Для свойства GLU_DISPLAY_MODE, значение по умолчанию – GLU_FILL, в результате чего поверхность визуализируется с помощью полигонов. Если для свойства используется значение GLU_OUTLINE_POLYGON, тесселяцией создаются и отображаются только полигоны, окантовывающие поверхность. Если же используется GLU_OUTLINE_PATCH, визуализируется только полигон, обозначающий участок, занимаемый поверхностью и декорирующие кривые.
Свойство GLU_NURBS_MODE управляет тем, должны ли тесселированные вершины выводиться на экран (значение по умолчанию, GLU_NURBS_RENDERER) или
тесселированные вершины должны быть возвращены в программу через интерфейс возвратно – вызываемых функций (если значение свойства –
GLU_NURBS_TESSELATOR).
Параметр GLU_CULLING может значительно повысить быстродействие, если задать ему значение GL_TRUE (значение по умолчанию – GL_FALSE). В этом случае, расчет поверхности не будет производиться, если она целиком выпадает из объема видимости.
Поскольку объект NURBS визуализируется в виде примитивов, он строится по различным величинам своих параметров (uи v) и разбивается на небольшие сегменты линий или полигоны. Если аргумент property равен GLU_SAMPLING_METHOD, value может принимать значения GLU_PATH_LENGTH (значение по умолчанию), GLU_PARAMETRIC_ERROR, GL_DOMAIN_DISTANCE, GLU_OBJECT_PATH_LENGTH или
GLU_OBJECT_PARAMETRIC_ERROR, что определяет способ тесселяции поверхности. Когда value установлено в значение GLU_PATH_LENGTH, поверхность визуализируется таким образом, что максимальная длина ребер полигонов в пикселях не больше, чем значение, заданное для свойства GLU_SAMPLING_TOLERANCE. Когда value установлено в GLU_PARAMETRIC_ERROR, величина, заданная для свойства GLU_PARAMETRIC_TOLERANCE представляет максимально допустимое расстояние в пикселях между аппроксимирующими полигонами и реальной поверхностью. Значение value равное GLU_OBJECT_PATH_LENGTH похоже на GLU_PATH_LENGTH за тем исключением, что величина, заданная для GLU_SAMPLING_TOLERANCE считается измеряемой в объектном пространстве, а не в пикселях. Точно так же
GLU_OBJECT_PARAMETRIC_ERROR отличается от GLU_PARAMETRIC_ERROR тем, что значение GLU_SAMPLING_TOLERANCE измеряется в объектном пространстве, а не в пикселях.
Когда value установлено в значение GLU_DOMAIN_DISTANCE, приложение задает в параметрических координатах, сколько точек должно быть взято на единицу длины в направлениях uи vс помощью величин GLU_U_STEP и GLU_V_STEP.
Если property установлено в GLU_SAMPLING_TOLERANCE и режим построения равен
GLU_PATH_LENGTH или GLU_OBJECT_PATH_LENGTH, value управляет максимальной длиной в пикселях или объектных координатах, соответственно, которая используется для результирующих полигонов. Например, значение по умолчанию 50.0 ограничивает максимальную длину сегмента линии или ребра полигона 50.0 пикселями или 50.0 единицами в объектном пространстве. Если property равно GLU_PARAMETRIC_ERROR и режим построения установлен в GLU_PARAMETRIC_ERROR или GLU_OBJECT_PARAMETRIC_ERROR, value управляет максимальной дистанцией в
пикселях или объектных координатах между результирующими полигонами и исходной поверхностью. Для обоих режимов построения значение GLU_PARAMETRIC_ERROR по умолчанию равно 0.5. Для GLU_PARAMETRIC_ERROR эта величина означает, что результирующие полигоны должны иметь такой размер и положение, чтобы отстоять от исходной аналитической поверхности не более, чем на 0.5 пикселя. (Смысл этого значения для GLU_OBJECT_PARAMETRIC_ERROR вовсе не очевиден.)

Если режим построения установлен в GLU_DOMAIN_DISTANCE, а property равно GLU_U_STEP или GLU_V_STEP, value является числом точек поверхности, которые нужно вычислить в направлениях uили v на единицу длины в параметрических координатах. Значениями по умолчанию для этих параметров является 100.
Свойство GLU_AUTO_LOAD_MATRIX определяет, должны ли видовая и проекционная матрица, а также размеры порта просмотра извлекаться из сервера OpenGL (в случае значения GL_TRUE – значение по умолчанию) или приложение должно загрузить эти матрицы с помощью функции gluLoadSamplingMatrices() (GL_FALSE).
Замечание: Некоторые свойства NURBS, такие как GLU_NURBS_MODE и его значение GLU_NURBS_TESSELATOR, а также режимы построения на основе объектных координат
GLU_OBJECT_PATH_LENGTH и GLU_OBJECT_PARAMETRIC_ERROR появились только в GLU
версии 1.3. До версии 1.3 эти режимы и свойства существовали только в качестве расширений отдельных производителей (если вообще существовали). Перед попыткой использовать эти свойства обязательно проверьте свою версию
GLU.
void gluLoadSamplingMatrices (GLUnurbsObj* nobj, const GLfloat modelMatrix[16], const GLfloat projMatrix[16], const GLint viewport[4]);
Если режим GLU_AUTO_LOAD_MATRIX выключен, порт просмотра, видовая и проекционная матрицы, заданные в gluLoadSamplingMatrices() используется для создания расчетной и отсекающей матриц для каждой кривой или поверхности NURBS.
Если вам нужно выяснить текущую величину одного из свойств NURBS, используйте функцию gluGetNurbsProperty().
void gluGetNurbsProperty (GLUnurbsObj* nobj, GLenum property, GLfloat* value);
Возвращает текущее значение свойства property объекта nobj в переменной value.
12.2.2.2 Обработка ошибок NURBS
Поскольку насчитывается 37 ошибок, специфических для работы с функциями NURBS,
неплохой идеей является регистрация функции обратного вызова для обработки ошибок. Эта функция будет вызвана в случае, если вы наткнетесь на одну из специфических ошибок. В примере 12-5 функция обратного вызова регистрируется с
помощью строки
gluNurbsCallback(theNurb, GLU_ERROR, (GLvoid (*)()) nurbsError);
void gluNurbsCallback (GLUnurbsObj* nobj, GLenum which, void (* fn)(GLenum errorCode));
which задает тип возвратно вызываемой функции, для функции проверки ошибок which должно иметь значение GLU_ERROR. Когда механизм работы с NURBS сталкивается с одной из ошибок, он вызывает функцию fn, передавая ей код ошибки в качестве единственного аргумента. errorCode представляет собой одно из 37 значений кода ошибки, имеющих символические имена от GLU_NURBS_ERROR1 до GLU_NURBS_ERROR37. Для получения строки с описание ошибки используйте функцию gluErrorString().
В примере 12-5 в качестве возвратной функции обработки ошибок была зарегистрирована функция nurbsErrorr():
void CALLBACK nurbsError(GLenum errorCode)

{
char message[100];
sprintf(message,"NURBS error: %s\n",gluErrorString(errorCode)); MessageBox(NULL,message,"**NURBS surface",MB_OK);
exit(0);
}
В GLU версии 1.3 были добавлены дополнительные функции обратного вызова, которые позволяют программе получать результирующие данные обратно, а не визуализировать их.
12.2.3 Создание кривой или поверхности NURBS
Чтобы визуализировать поверхность NURBS, функции gluNurbsSurface() обрамляются вызовами gluBeginSurface() и gluEndSurface(). Обрамляющие функции сохраняют и восстанавливают состояние вычислителя.
void gluBeginSurface (GLUnurbsObj* nobj); void gluEndSurface (GLUnurbsObj* nobj);
После вызова gluBeginSurface() один или более вызовов функции gluNurbsSurface() задает атрибуты поверхности. Для генерирования вершин поверхности типа
GL_MAP2_VERTEX_3 или GL_MAP2_VERTEX_4 требуется один и только один такой вызов. Функция gluEndSurface() используется в качестве маркера конца определения поверхности. Декорирование NURBS также происходит в обрамлении указанных функций.
void gluNurbsSurface (GLUnurbsObj* nobj, GLint uknot_count, GLfloat* uknot, GLint vknot_count, GLfloat* vknot,
GLint u_stride, GLint v_stride, GLfloat* ctlarray, GLint uorder, GLint vorder, GLenum type);
Описывает вершины (или нормали, или координаты текстуры) поверхности NURBS nobj. Часть величин должна быть задана для обоих параметрических направлений uи v, например, узловые последовательности (uknot и vknot), число узлов (uknot_count и vknot_count) и порядок полинома (uorder и vorder). Заметьте, что число контрольных точек не задается. Вместо этого число точек вдоль каждого параметрического направления вычисляется как количество узлов минус порядок полинома. Далее общее число контрольных точек получается как произведение двух найденных величин. Аргумент ctlarray указывает на массив контрольных точек.
Последний параметр type должен быть равен одной из констант, обозначающих тип двумерного вычислителя. Чаще всего используются GL_MAP2_VERTEX_3 (для нерациональных вершин) и GL_MAP2_VERTEX_4 (для рациональных). Вы также можете использовать другие типы, такие как GL_MAP2_TEXTURE_COORD_* или GL_MAP2_NORMAL для вычисления и ассоциирования координат текстуры и вектора нормали. Например, чтобы создать освещенную (с вычисленными векторами нормали) и текстурированную поверхность NURBS, вам может понадобиться выполнить следующую последовательность команд:
gluBeginSurface(nobj);
gluNurbsSurface(nobj, ..., GL_MAP2_TEXTURE_COORD_2); gluNurbsSurface(nobj, ..., GL_MAP2_NORMAL); gluNurbsSurface(nobj, ..., GL_MAP2_VERTEX_3);
glEndSurface();

Аргументы u_stride и v_stride представляют количество дробных величин между контрольными точками в каждом параметрическом направлении. На значение аргументов u_stride и v_stride влияют тип вычислителя и его порядок. Так в примере 12-5 u_stride равен 12 (4*3), поскольку задаются 3 координаты для каждой вершины (GL_MAP2_VERTEX_3) и четыре контрольные точки в параметрическом направлении v, v_stride равен 3, поскольку каждая вершина имеет 3 координаты и точки v расположены в памяти плотно одна за одной.
Рисование кривой NURBS похоже на рисование поверхности, за тем исключением, что вычисления производятся по одному параметру – u, а не по двум. Кроме того, для кривых используются обрамляющие функции gluBeginCurve() и gluEndCurve().
void gluBeginCurve (GLUnurbsObj* nobj); void gluEndCurve (GLUnurbsObj* nobj);
После вызова gluBeginCurve(), один или несколько вызовов gluNurbsCurve() определяют атрибуты кривой. Для генерирования вершин кривой типа
GL_MAP1_VERTEX_3 или GL_MAP1_VERTEX_4 требуется один и только один такой вызов. Для завершения определения кривой, используйте функцию gluEndCurve().
void gluNurbsCurve (GLUnurbsObj* nobj, GLint uknot_count, GLfloat* uknot, GLint u_stride, GLfloat* ctlarray, GLint uorder, GLenum type);
Определяет кривую NURBS для объекта nobj. Аргументы имеют тот же смысл, что и для gluNurbsSurface(). Заметьте, что эта функция требует только одной узловой последовательности и одного объявления порядка для объекта NURBS. Если эта кривая определяется внутри блока gluBeginCurve() / gluEndCurve(), аргумент typeможет иметь любое значение, допустимое для типа одномерного вычислителя (например, GL_MAP1_VERTEX_3 или GL_MAP1_VERTEX_4).
12.2.3.1 Получение примитивов из тесселятора NURBS обратно
По умолчанию тесселятор NURBS разбивает объект NURBS на геометрические линии и полигоны, и визуализирует их. В GLU версии 1.3 были добавлены дополнительные возвратные функции, так что вместо визуализации пост – тесселяционных величин, они могут быть возвращены для использования программе.
Чтобы это сделать, первым шагом является вызов функции gluNurbsProperty() для установки свойства GLU_NURBS_MODE в режим GLU_NURBS_TESSELATOR. Следующие
шагом является выполнение нескольких вызовов функции gluNurbsCallback() для регистрации функций обратного вызова, которые будут вызываться тесселятором
NURBS.
void gluNurbsCallback (GLUnurbsObj* nobj, GLenum which, void (*fn)());
nobj представляет собой тесселируемый объект NURBS. which – это перечислимое значение, идентифицирующее тип возвратной функции. Если свойство
GLU_NURBS_MODE установлено в режим GLU_NURBS_TESSELATOR, то помимо функции
GLU_ERROR можно зарегистрировать еще 12 функций. (В противном случае, активна только функция GLU_ERROR.) 12 возвратных функций идентифицируются следующими константами и имеют следующие прототипы:
GLU_NURBS_BEGINtd> |
void begin (GLenum type); |
|
|
||
GLU_NURBS_BEGIN_DATA |
void begin (GLenum type, void* userData); |
|
|
||
GLU_NURBS_TEXTURE_COORD |
void texCoord (GLfloat* tCrd); |
|
|
||
GLU_NURBS_TEXTURE_COORD_DATA |
void texCoord (GLfloat* tCrd, void* userData); |
|
|
||
|
|
|

