Здесь планировалось рассказать о точечных источниках света, но я думаю, что вы честно заработали небольшую перемену. Эта глава рассказывает о списках отображения (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, поскольку они производят кэширование.
А теперь, в качестве приза за то, что дочитали до этого места, леденец на палочке. Скомпилируйте и наслаждайтесь ;)