Здесь планировалось рассказать о точечных источниках света, но я думаю, что вы честно заработали небольшую перемену. Эта глава рассказывает о списках отображения (display lists). Как вы увидите, они не только сокращают размер OpenGL кода, но и повышают производительность, экономят место на диске, готовят кофе и бутербродами и проч. Что такое списки отображения? Говоря по-простому: это список вызовов OpenGL, хранящийся в одной переменной.
Чтобы показать пользу списков отображения, давайте рассмотрим простой пример. Создадим колонну с четырьмя сторонами, состоящую из низа (4 четырехугольника), средней части (4 четырехугольника), и верха (4 треугольника). Итого 12 поверхностей или 44 вершины.
glBegin(GL_QUADS);
glColor3f( 0.0, 0.0, 0.5 );
glNormal3f(0.0,0.0,1.0);
glVertex3f( 10.0,-10.0, 0.0);
glVertex3f( 10.0, 10.0, 0.0);
glVertex3f(-10.0, 10.0, 0.0);
glVertex3f(-10.0,-10.0, 0.0);
glEnd();
glTranslatef( 0.0, 0.0, 1.0);
glColor3f( 0.9, 0.0, 0.0 );
glBegin(GL_QUADS);
glNormal3f(1.0, 0.0, 0.0);
glVertex3f( 1.0, 1.0,-1.0);
glVertex3f( 1.0, 1.0, 1.0); // rechts unten
glVertex3f( 1.0,-1.0, 1.0);
glVertex3f( 1.0,-1.0,-1.0);
glNormal3f(-1.0, 0.0, 0.0);
glVertex3f(-1.0, 1.0, 1.0);
glVertex3f(-1.0, 1.0,-1.0); // links unten
glVertex3f(-1.0,-1.0,-1.0);
glVertex3f(-1.0,-1.0, 1.0);
glNormal3f(0.0,1.0,0.0);
glVertex3f( 1.0, 1.0, 1.0);
glVertex3f( 1.0, 1.0,-1.0); // hinten unten
glVertex3f(-1.0, 1.0,-1.0);
glVertex3f(-1.0, 1.0, 1.0);
glNormal3f(0.0, -1.0, 0.0);
glVertex3f( 1.0,-1.0,-1.0);
glVertex3f( 1.0,-1.0, 1.0); // vorne unten
glVertex3f(-1.0,-1.0, 1.0);
glVertex3f(-1.0,-1.0,-1.0);
FindNormal(-0.5,0.5,6.0,0.5,0.5,6.0,1.0,1.0,1.0);
glNormal3f(CNormal[1], CNormal[2], CNormal[3]);
glVertex3f(-0.5, 0.5, 6.0);
glVertex3f( 0.5, 0.5, 6.0); // vorne oben
glVertex3f( 1.0, 1.0, 1.0);
glVertex3f(-1.0, 1.0, 1.0);
FindNormal(0.5,-0.5,6.0,-0.5,-0.5,6.0,-1.0,-1.0,1.0);
glNormal3f(CNormal[1], CNormal[2], CNormal[3]);
glVertex3f( 0.5,-0.5, 6.0);
glVertex3f(-0.5,-0.5, 6.0); // hinten oben
glVertex3f(-1.0,-1.0, 1.0);
glVertex3f( 1.0,-1.0, 1.0);
FindNormal(1.0,1.0,1.0,0.5,0.5,6.0,0.5,-0.5,6.0);
glNormal3f(CNormal[1], CNormal[2], CNormal[3]);
glVertex3f( 1.0, 1.0, 1.0);
glVertex3f( 0.5, 0.5, 6.0); // rechts oben
glVertex3f( 0.5,-0.5, 6.0);
glVertex3f( 1.0,-1.0, 1.0);
FindNormal(-0.5,0.5,6.0,-1.0,1.0,1.0,-1.0,-1.0,1.0);
glNormal3f(CNormal[1], CNormal[2], CNormal[3]);
glVertex3f(-0.5, 0.5, 6.0);
glVertex3f(-1.0, 1.0, 1.0); // links oben
glVertex3f(-1.0,-1.0, 1.0);
glVertex3f(-0.5,-0.5, 6.0);
glEnd();
glBegin(GL_TRIANGLES);
FindNormal(-0.5,0.5,6.0,0.0,0.0,7.0,0.5,0.5,6.0);
glNormal3f(CNormal[1], CNormal[2], CNormal[3]);
glVertex3f(-0.5, 0.5, 6.0);
glVertex3f( 0.0, 0.0, 7.0);
glVertex3f( 0.5, 0.5, 6.0); // vorne oben
FindNormal(0.5,-0.5,6.0,0.0,0.0,7.0,-0.5,-0.5,6.0);
glNormal3f(CNormal[1], CNormal[2], CNormal[3]);
glVertex3f( 0.5,-0.5, 6.0);
glVertex3f( 0.0, 0.0, 7.0);
glVertex3f(-0.5,-0.5, 6.0); // hinten oben
FindNormal(0.5,0.5,6.0,0.0,0.0,7.0,0.5,-0.5,6.0);
glNormal3f(CNormal[1], CNormal[2], CNormal[3]);
glVertex3f( 0.5, 0.5, 6.0); // rechts oben
glVertex3f( 0.0, 0.0, 7.0);
glVertex3f( 0.5,-0.5, 6.0);
FindNormal(-0.5,-0.5,6.0,0.0,0.0,7.0,-0.5,0.5,6.0);
glNormal3f(CNormal[1], CNormal[2], CNormal[3]);
glVertex3f(-0.5,-0.5, 6.0);
glVertex3f( 0.0, 0.0, 7.0);
glVertex3f(-0.5, 0.5, 6.0); // links oben
glEnd;
Это все. Скачайте пример отсюда и посмотрите на него.
Все в порядке? Увидели? А теперь проделайте вот что: нарисуйте 4 колонны, стоящие на одной плоскости друг за дружкой. Вперед — и с песней!
Вы, наверно, подумали: "Это ведь так и умотаться можно!". Конечно, придется рассчитать координаты еще 220 вершин. Веселого здесь мало. Однако, с помощью списков отображения, можно использовать заново объект много раз.
Список отображения хранит такие вызовы OpenGL, как "установить вершину" или "осветить" и тому подобное. Сейчас сохраним в списке нашу колонну. Для этого нам потребуется целочисленная переменная и ID для нашего списка. И после присваивания Variable := glGenLists() наша ID будет указывать на список.
После создания списка нам следует его заполнить. Во-первых, нужно указать OpenGL, какой список хотим заполнить. Это делается при помощи glNewList(). Первый аргумент — имя списка, второй — то, как следует обращаться со списком. Для нашего случая выберем GL_COMPILE, что означает, что мы просто его заполним, а запускать будем потом.
Теперь последуют обычные вызовы OpenGL. Делайте, что хотите. Можно даже вызывать другие процедуры (например, вычисляющие нормали, и прочее). Если список заполнен, его следует закрыть посредством glEndList();
После создания списка, его можно запускать с помощью glCallList(). Каждый раз, когда мы его запускаем, список исполняется, рисуя ту хрень, что в него была занесена. В нашем примере мы нарисуем колонну, сделаем трансляцию, и нарисуем следующую.
glColor3f( 0.0, 0.0, 0.5 ); glTranslatef( 0.0, 0.0, 1.0); glCallList(Ground); glColor3f( 0.9, 0.0, 0.0 ); glTranslatef( 5.0,5.0,0.0); glCallList(Pillar); glColor3f( 0.0, 0.9, 0.0 ); glTranslatef( 0.0,-10.0,0.0); glCallList(Pillar); glColor3f( 0.9, 0.9, 0.0 ); glTranslatef(-10.0,0.0,0.0); glCallList(Pillar); glColor3f( 0.0, 0.9, 0.9 ); glTranslatef(0.0,10.0,0.0); glCallList(Pillar);
То есть рисование четырех колонн занимает меньше места, чем рисование одной непосредственным способом. Наша процедура OpenGL стала намного более читабельной. Теперь берите пример и пробуйте свои силы.
Другим преимуществом является то, что теперь нам не надо каждый раз вычислять нормали. Они рассчитываются один раз в начале, а затем просто вызываются из списка. Кроме того, большинство 3d карт могут выполнять списки отображения быстрее, чем обычные вызовы OpenGL, поскольку они производят кэширование.
А теперь, в качестве приза за то, что дочитали до этого места, леденец на палочке. Скомпилируйте и наслаждайтесь ;)
| FPC | 3.2.2 | release |
| Lazarus | 3.2 | release |
| MSE | 5.10.0 | release |
| fpGUI | 1.4.1 | release |