Книги

OpenGL / FPC - Глава 16

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

А теперь, в качестве приза за то, что дочитали до этого места, леденец на палочке. Скомпилируйте и наслаждайтесь ;)

Актуальные версии
FPC3.2.2release
Lazarus3.2release
MSE5.10.0release
fpGUI1.4.1release
links