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

RedBook

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

GL_POLYGON_OFFSET_POINT. Вы также должны вызвать glPolygonMode(), чтобы выбрать текущий режим растеризации полигонов.

void glPolygonOffset (GLfloat factor, GLfloat units);

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

,

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

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

OpenGL вычисляет разброс глубины вершин полигона самостоятельно, но важно, чтобы вы понимали, что есть этот разброс, поскольку это позволит вам выбирать осмысленные значения для аргумента factor. Разброс глубины это изменение в величине глубины, деленное на изменение в xили y – координатах, пересекающих полигон. Величины глубины при этом измеряются в оконных координатах, усеченных до диапазона [0, 1]. Чтобы вычислить разброс глубин полигона, используйте формулу

.

Рисунок 6-9. Полигоны и их разброс глубин

Для полигонов, которые параллельны ближней или дальней отсекающим плоскостям, разброс глубин равен 0. Для полигонов в вашей сцене, разброс глубин которых близок к нулю, требуется лишь небольшая величина смещения. Чтобы создать небольшое, постоянное смещение, вы можете передать в команду glPolygonOffset() factor=0.0 и units=1.0.

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

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

Пример 6-7 демонстрирует часть кода с применением списка отображения (рисующим залитый объект). Сначала объект отображается с освещением, режимом полигонов GL_FILL и полигональном смещением с величинами factor=1.0 и units=1.0. Данные величины гарантируют, что смещение достаточно для всех полигонов в вашей сцене, независимо от разброса глубины. (Эти величины могут быть даже больше минимально необходимых, однако чуть-чуть большее, чем нужно полигональное смещение заметно меньше, чем чуть-чуть меньшее.) Затем, для выделения ребер первого объекта, объект отображается еще раз в виде неосвещенного каркаса с выключенным смещением.

Пример 6-7. Использование полигонального смещения для удаления визуальных

артефактов

glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(1.0,1.0);

glCallList(list); glDisable(GL_POLYGON_OFFSET_FILL); glDisable(GL_LIGHTING); glDisable(GL_LIGHT0); glColor3f(1.0,1.0,1.0); glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); glCallList(list); glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);

В небольшом числе ситуаций простейшие значения для величин factor и units (1.0, и 1.0) не являются решениями проблемы. Например, если длина линий, выделяющих ребра больше 1, может быть необходимо увеличение значения factor. Также, поскольку

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

Глава 7. Списки отображения

Список отображения (дисплейные списки) это группа команд OpenGL, сохраненная для дальнейшего исполнения. Когда исполняется список отображения, команды, включенные в него, исполняются в том порядке, в котором они были заданы. Большинство команд OpenGL могут быть как сохранены в списке отображения, так и выполняться в непосредственном режиме, в котором они выполняются немедленно. В

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

7.1 Зачем использовать списки отображения

Списки отображения могут увеличить быстродействие, поскольку вы можете сохранять в них команды OpenGL для дальнейшего исполнения. Хорошей идеей является кэширование команд в списке отображения, если вы планируете рисовать какую-либо

геометрию несколько раз или если несколько раз устанавливаете одно и то же состояние настроек. Используя списки отображения, вы можете определить

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

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

исполнением списка для вычисления правильного размера и положения каждого колеса.

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

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

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

7.2 Пример использования списка отображения

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

Пример 7-1. Создание списка отображения: файл torus.cpp

#include <glut.h> #include <math.h>

GLuint theTorus;

//Отрисовка торуса

void torus(int numc,int numt)

{

int i,j,k;

double s,t,x,y,z,twopi; twopi=2*(double) 3.1415; for(i=0;i<numc;i++)

{

glBegin(GL_QUAD_STRIP); for(j=0;j<=numt;j++)

{

for(k=1;k>=0;k--)

{

s=(i+k)%numc+0.5;

t=j%numt;

x=(1+.1*cos(s*twopi/numc))*cos(t*twopi/numt);

y=(1+.1*cos(s*twopi/numc))*sin(t*twopi/numt);

z=.1*sin(s*twopi/numc);

glVertex3f(x,y,z);

}

}

glEnd();

}

}

void init(void)

{

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

torus(8,25);

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

}

void display()

{

glClear(GL_COLOR_BUFFER_BIT); glColor3f(1.0,1.0,1.0); glCallList(theTorus); glFlush();

}

void reshape(int w,int h)

{

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

gluPerspective(30,(GLfloat)w/(GLfloat)h,1.0,100.0); glMatrixMode(GL_MODELVIEW);

glLoadIdentity();

gluLookAt(0,0,10,0,0,0,0,1,0);

}

//"x" -- повернуть вокруг x -оси;"y" -- повернуть вокруг y -оси;"i"--вернуться в начальное состояние

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

{

switch(key)

{

case 'X': case 'x':

glRotatef(30.,1.0,0.0,0.0);

glutPostRedisplay();

break; case 'Y': case 'y':

glRotatef(30.,0.0,1.0,0.0);

glutPostRedisplay();

break; case 'I': case 'i':

glLoadIdentity();

gluLookAt(0,0,10,0,0,0,0,1,0);

glutPostRedisplay();

break; case 27:

exit(0);

}

}

int main(int argc, char** argv)

{

glutInit(&argc,argv); glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB); glutInitWindowSize(200,200); glutCreateWindow("Creating a Display List"); init();

glutDisplayFunc(display);

glutReshapeFunc(reshape);

glutKeyboardFunc(keyboard);

glutMainLoop(); return 0;

}

Начнем с рассмотрения функции init(). Она создает список отображения для торуса и инициализирует состояние OpenGL. Заметьте, что вызов функции рисования торуса (torus()) заключена в командные скобки glNewList() и glEndList(), которые определяют список отображения. Аргумент listName команды glNewList()

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

Пользователь может поворачивать торус вокруг осей xи y, нажимая клавиши ‘x’ и ‘y’, соответственно. Каждый раз, когда это случается, вызывается функция обратного вызова keyboard(), которая комбинирует текущую видовую матрицу с матрицей поворота вокруг соответствующей оси. Затем вызывается функция glutPostRedisplay(), которая заставляет glutMainLoop() вызвать функцию display()

и вывести торус после того, как будут обработаны все остальные события. Когда нажимается клавиша ‘i’, функция keyboard() восстанавливает начальную видовую матрицу и возвращает торус к начальному положению.

Сама функция display() крайне проста. Она очищает окно и затем вызывает команду glCallList() для исполнения команд в списке отображения. Если бы мы не использовали списки отображения, в функции display() следовало бы вызывать все команды для отображения торуса.

Список отображения содержит только команды OpenGL. В примере 7-1 в списке отображения сохраняются только команды glBegin(), glEnd() и glVertex().

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

Замечание: Списки отображения также отлично работают с командами из библиотеки утилит GLU, поскольку эти команды могут быть разбиты на низкоуровневые команды OpenGL и сохранены в списках отображения.

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

7.3 Философия разработки списков отображения

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

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

Способ, которым оптимизируются команды в списке отображения, может меняться от реализации к реализации. За счет базовых команд вроде glRotate() можно получить достаточно большой выигрыш по производительности, поместив их в списки отображения, поскольку вычисления, необходимые для построения матрицы поворота нетривиальны (и могут включать вычисление квадратных корней и тригонометрические функции). Однако в списке отображения можно сохранить только результирующую матрицу поворота, поэтому команда поворота в списке отображения может быть выполнена настолько быстро, насколько быстро аппаратура может выполнить glMultMatrix*(). Наиболее продвинутые реализации OpenGL могут даже комбинировать смежные команды преобразований в одну финальную матрицу.

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

Матричные операции. Большинство матричных операций OpenGL вычисления обратных матриц. Обе матрицы расчетная и обратная к ней могут быть сохранены в списке отображения реализацией OpenGL.

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

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

Текстуры. Вы можете максимизировать эффективность при определении текстур скомпилировав их в список отображения, поскольку список отображения может кэшировать изображение текстуры в выделенной текстурной памяти. После этого не требуется копировать изображение текстуры каждый раз, когда оно понадобилось. Кроме того, аппаратный формат текстуры может отличаться от формата OpenGL, и конверсия может быть выполнена во время компиляции списка отображения, а не во время отображения сцены. В OpenGL версии 1.0 списки отображения были главным методом управления текстурами. Однако если вы работаете с OpenGL версии 1.1 или выше, вам следует сохранять текстуры в объекте текстуры. (Некоторые реализации версии 1.0 имеют специфичные для производителя расширения для поддержки объектов текстуры. Если ваша реализация поддерживает объекты текстуры, вам следует использовать их.)

Рисунки шаблонов полигона.

Некоторые из команд, используемых для задания свойств, перечисленных здесь, являются контекстно-чувствительными, и это следует принимать в расчет для получения оптимального быстродействия. Например, когда включен режим цвета материала (GL_COLOR_MATERIAL), некоторые из параметров материала будут отслеживать текущий цвет. Любые вызовы glMaterial*() для установки тех же параметров будут игнорироваться.

Сохранение установок переменных состояния вместе с геометрическими данными может увеличить быстродействие. Например, предположим, что вы хотите трансформировать некоторые геометрические объекты и затем вывести результат. Код для этого может быть таким:

glNewList(1, GL_COMPILE);

нарисовать_некоторые_объекты(); glEndList();

glLoadMatrix(M);

glCallList(1);

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

glNewList(1, GL_COMPILE); glLoadMatrix(M);

нарисовать_некоторые_объекты(); glEndList();

glCallList(1);

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

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

7.4 Создание и исполнение списка отображения

Как вы уже видели, для начала и конца описания списка применяются команды glNewList() и glEndList(). Далее список может быть исполнен передачей его индекса в команду glCallList(). В примере 7-2 список создается в функции init(). Этот список отображения содержит команды OpenGL для рисования красного треугольника. Затем в функции display() список исполняется 10 раз. Кроме того, там же рисуется линия в непосредственном режиме. Имейте в виду, что для списка отображения выделяется память, в которой хранятся его команды и все необходимые переменные.

Пример 7-2. Использование списка отображения: файл list.cpp

#include <glut.h>

GLuint listName;

void init(void)

{

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

glColor3f(1.0,0.0,0.0); glBegin(GL_TRIANGLES);

glVertex2f(0.0,0.0);

glVertex2f(1.0,0.0);

glVertex2f(0.0,1.0);

glEnd();

glTranslatef(1.5,0.0,0.0);

glEndList(); glShadeModel(GL_FLAT);

}

void drawLine()

{

glBegin(GL_LINES); glVertex2f(0.0,0.5); glVertex2f(15.0,0.5);

glEnd();

}

void display(void)

{

GLuint i; glClear(GL_COLOR_BUFFER_BIT); glColor3f(0.0,1.0,0.0); for(i=0;i<=10;i++)

glCallList(listName);

//Эта линия НЕ будет зеленой, так как текущий цвет был изменен внутри ЛИСТА drawLine();

glFlush();

}

void reshape(int w, int h)

{

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

if (w<=h) gluOrtho2D(0.0,2.0,-0.5*(GLfloat)h/(GLfloat)w,1.5*(GLfloat)h/(GLfloat)w);

else gluOrtho2D(0.0,2.0*(GLfloat)w/(GLfloat)h,-0.5,1.5);

glMatrixMode(GL_MODELVIEW); glLoadIdentity();

}

int main(int argc, char** argv)

{

glutInit(&argc,argv); glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB); glutInitWindowSize(650,50); glutCreateWindow("Using a Display List"); init();

glutDisplayFunc(display);

glutReshapeFunc(reshape);

glutMainLoop(); return 0;

}

Команда glTranslatef() в списке отображения сдвигает позицию следующего рисуемого объекта. Без нее два вызова списка к исполнению рисовали бы треугольники в одном и том же месте один поверх другого. Функция drawLine(), вызываемая в непосредственном режиме затронута влиянием 10-го вызова glTranslate(). Таким образом, если вы вызываете команды преобразований внутри списка отображения, не забудьте учесть эффект от влияния этих команд на более поздние вызовы в вашей программе.

В каждый момент времени создается только один список отображения. Другими словами, за командой glNewList() должна следовать команда glEndList(), завершающая создание списка, и только после этого можно начинать создавать новый список. Как вы можете подозревать, вызов glEndList() без предшествующего вызова glNewList() сгенерирует ошибку GL_INVALID_OPERATION.

7.4.1 Именование и создание списка отображения

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

используйте команду glGenLists() для генерирования одного или более незадействованных индексов.

GLuint glGenLists (GLsizei range);

Выделяет непрерывный диапазон незадействованных индексов, количеством range. Возвращаемое значение это первый индекс в этом блоке пустых индексов. Возвращаемые индексы помечаются как пустые и используемые, таким образом последующие вызовы glGenLists() не возвратят именно эти номера до тех пор, пока они не будут стерты. Если range равен 0 или блок свободных индексов нужного размера отсутствует, glGenLists() вернет 0.

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

listIndex=glGenLists(1); if (listIndex!=0)

{

glNewList(listIndex, GL_COMPILE);

...

glEndList();

}

Замечание: 0 не является допустимым индексом.

void glNewList (GLuint list, GLenum mode);

Маркирует начало списка отображения. Вызываемые далее (до вызова glEndList()) команды OpenGL (кроме нескольких, запрещенных к использованию в списках) сохраняются в списке отображения. (Эти запрещенные команды, если они встречаются в описании списка, исполняются немедленно в непосредственном режиме.) Аргумент list это ненулевое положительное целое число, уникально идентифицирующее список отображения. Возможными значениями для mode являются GL_COMPILE и GL_COMPILE_AND_EXECUTE. Используйте GL_COMPILE, если вы не хотите, чтобы команды OpenGL исполнялись немедленно при помещении в список. Если вы хотите, чтобы при создании списка команды, помещаемые в него, выполнились еще и в непосредственном режиме используйте GL_COMPILE_AND_EXECUTE.

void glEndList (void);

Маркирует конец списка отображения.

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

7.4.2 Что сохраняется в списке отображения

Когда вы строите список, в нем сохраняются только значения выражений. Если впоследствии элементы массива изменятся величины в списке останутся нетронутыми. В следующем фрагменте кода список отображения содержит команды для установки текущего RGBA цвета в черный (0.0, 0.0, 0.0). последующее изменение элементов массива color_vector на красный (1.0, 0.0, 0.0) не затрагивает список отображения, поскольку в нем сохранились величины, действовавшие на момент его создания.

GLfloat color_vector[3]={0.0, 0.0, 0.0};

glNewList(1, GL_COMPILE); glColor3f(color_vector);

glEndList(); color_vector[0]=1.0;

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

Далее перечислены команды OpenGL, которые не сохраняются в списках отображения. (Заметьте также, что команда glNewList() сгенерирует ошибку, если будет вызвана во время создания списка отображения.)

glColorPointer()

glFlush()

glNormalPointer()

glDeleteLists()

glGenLists()

glPixelStore()

glDisableClientState()

glGet*()

glReadPixels()

glEdgeFlagPointer()

glIndexPointer()

glRenderMode()

glEnableClientState()

glInterleavedArrays()

glSelectBuffer()

glFeedbackBuffer()

glIsEnabled()

glTexCoordPointer()

glFinish()

glIsList()

glVertexPointer()

 

 

 

Чтобы более ясно понять, почему эти команды не сохраняются в списках, помните, что при использовании OpenGL по сети, клиент и сервер могут находиться на разных машинах. После создания списка отображения, он находится на сервере, и сервер не может полагаться на клиента в вопросах касающихся любой информации, связанной со списком. Если бы команды опроса, такие как glGet*() или glIs*() были допустимы в списках отображения, вызывающая программа была бы крайне удивлена данным, поступающим по сети в случайные моменты времени. Без разбора отосланного списка, программа вообще не будет знать, что делать с этими данными. Таким образом, любые команды, возвращающие значения, не могут быть сохранены в списке отображения. Кроме того, в списках не сохраняются команды, изменяющие состояние клиента, такие как glPixelStore() или glSelectBuffer(), и команды, определяющие вершинные массивы.

Действие некоторых команд OpenGL зависит от состояния клиента. Например, команды описания вершинных массивов (такие как glVertexPointer(), glColorPointer() и glInterleavedArrays()) устанавливают указатели в пространстве клиента и не могут сохраняться в списках отображения. Команды glArrayElement(), glDrawArrays() и glDrawElements() посылают данные состоянию серверу для построения примитивов из вершинных массивов, так что эти операции могут быть сохранены в списках отображения. Данные вершинных массивов, сохраненные в списке отображения извлекаются путем разрешения указателей, а не сохранением самого указателя. Таким образом, последующие изменения в данных вершинных массивов не повлияют на описание примитива в списке отображения.

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

7.4.3 Исполнение списка отображения

После того, как вы создали список отображения, вы можете исполнять его, вызвав команду glCallList(). Естественно, вы можете исполнять один и тот же список много раз, и, как вы уже видели, вы можете смешивать в программе код для исполнения списков отображения и команды непосредственного режима.

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