Программирование игр, создание игрового движка, OpenGL, DirectX, физика, форум
GameDev.ru / Программирование / Статьи / Работа с расширениями OpenGL с использованием NVIDIA OpenGL SDK 5.1. (Часть 3)

Работа с расширениями OpenGL с использованием NVIDIA OpenGL SDK 5.1. (Часть 3)

Автор:

Во время подготовки примеров для этой части статьи я столкнулся со следующей проблемой. В FAQ по GLUT написано, что файл glut.h уже содержит необходимые определения из windows.h, которые необходимы для нормальной компиляции gl.h. Поэтому файл windows.h можно подключать как до, так и после подключения файла glut.h (а лучше, не подключать вообще). Но оказалось, что если следовать этим указаниям, то на некоторых компиляторах компиляция будет проходить успешно, а на некоторых - вылетать с ошибкой во время компиляции gl.h. Поэтому все примеры предыдущих частей, размещённые на сайте,  были обновлены (в них была добавлена директива #include < windows.h>).

PS. Если вы откроете файл glut.h, то увидите почти в самом начале файла следующие строки:

# if 0
#  define  WIN32_LEAN_AND_MEAN
#  include <windows.h>
# else

Потому, если заменить "# if 0" на "# if 1", то проблема должна исчезнуть. Но вам придется распространять этот файл вместе с вашей программой.

Расширения WGL_ARB_pbuffer и WGL_ARB_pixel_format
Использование класса PBuffer
  Восстановление буфера пикселей в случае потери
Создание эффекта зеркального отражения с использованием pbuffer
Заключение
  Достоинства
  Недостатки

Расширения WGL_ARB_pbuffer и WGL_ARB_pixel_format

При написании программ, использующих многопроходные алгоритмы, часто возникает потребность осуществить промежуточное отображение на виртуальный экран, невидимый пользователю. Информация с виртуального экрана может использоваться для создания динамических текстур, зеркальных поверхностей, кубических текстур и т.д. При использовании виртуального экрана можно избежать мелькания служебной информации на экране, а также значительно повысить быстродействие (виртуальный экран хранится в видеопамяти, которая превосходит обычную оперативную память по быстродействию почти на порядок). DirectX почти с самого начала поддерживал такие виртуальные экраны, называемые внеэкранными поверхностями (off-screen surfaces).

OpenGL не содержит стандартных средств для работы с внеэкранными поверхностями, не считая использования второго кадрового буфера в режиме двойной буферизации (и то, эту функцию выполняет WGL). Но при использовании кадрового буфера мы сталкиваемся с ограничением - размер образа, создаваемого во втором кадровом буфере, не может превышать разрешение экрана (т.е. если пользователь работает в режиме 800*600, не удастся использовать динамические текстуры с разрешением 1024*1024).

В начале 1999 года появилось WGL-расширение WGL_ARB_pbuffer, предназначенное для работы с буфером пикселей (pixel buffer или сокращенно pbuffer). Что такое буфер пикселей? Это область видеопамяти, в которой размещается виртуальный экран. С точки зрения программы, виртуальный экран не отличается от настоящего - он имеет свой контекст OpenGL, поддерживает практически все команды OpenGL и т.д.

Ниже приводится пример простейшей программы, использующей буфер пикселей (см. каталог Ex01 в архиве, доступном на сайте журнала). Программа создает буфер пикселей размером 256*256, рисует в нем вращающийся чайник и копирует его на экран (см. рис. 1). Пример написан "по мотивам" примеров из NVIDIA OpenGL SDK.

Изображение
Рисунок 1.

Но сначала сделаем небольшое отступление: рассмотрим расширение WGL_ARB_pixel_format. Это расширение содержит команду wglChoosePixelFormatARB, которая используется при создании буфера пикселей. Эта команда является расширенной версией команды wglChoosePixelFormat. Главное ее отличие от оригинала - отсутствие фиксированной структуры PIXELFORMATDESCRIPTOR. Иначе дальнейшее увеличение числа полей структуры привело бы к огромному количеству разных версий однотипных структур, что и произошло в DirectX. Вместо этого используются два массива (второй и третий параметры) iattributes и fattributes. Первый содержит значения целочисленных параметров, второй - вещественных. Для указания количества элементов в этих массивах используется четвертый параметр. Остальные параметры функции остались без изменения. Функция возвращает список поддерживаемых форматов пикселей, удовлетворяющих указанным условиям (пятый параметр). Шестой параметр - адрес переменной, в которой будет занесено количество элементов в массиве.

Массивы параметров имеют структуру, показанную в таблице 1.

Индекс Значение
Название поля
Значение поля
Название поля
Значение поля
...  ...

Таблица 1.

Четные элементы содержат константу, которая определяет название поля, а нечетные - значения этого поля. Если в название поля равно 0, то оно и последующие поля игнорируются.

Теперь вернемся к нашей программе. После инициализации обоих расширений получаем значения дескриптора текущего окна и его дескриптор контекста OpenGL.

HDC hdc=wglGetCurrentDC();
HGLRC hglrc=wglGetCurrentContext();

Затем создаем и заполняем массивы параметров iattributes и fattributes.

const MAX_ATTRIBS=32;
//--------------------------
int iattributes[2*MAX_ATTRIBS]; // Массивы атрибутов
float fattributes[2*MAX_ATTRIBS];
int niattribs=0; // Текуший целый атрибут
int nfattribs=0; // Текуший вещественный атрибут

// Очистка массива атрибутов
for (int i=0; i<2*MAX_ATTRIBS; i++)
{
  iattributes[i]=0;
  fattributes[i]=0;
}

// Требуется формат пикселей, совместимый с PBuffer
iattributes[2*niattribs]=WGL_DRAW_TO_PBUFFER_ARB;
iattributes[2*niattribs+1]=true;
niattribs++;

// 24-битный буфер глубины
iattributes[2*niattribs]=WGL_DEPTH_BITS_ARB;
iattributes[2*niattribs+1]=true;
niattribs++;

// 32-битный буфер цвета
iattributes[2*niattribs]=WGL_RED_BITS_ARB;
iattributes[2*niattribs+1]=true;
niattribs++;

iattributes[2*niattribs]=WGL_GREEN_BITS_ARB;
iattributes[2*niattribs+1]=true;
niattribs++;

iattributes[2*niattribs]=WGL_BLUE_BITS_ARB;
iattributes[2*niattribs+1]=true;
niattribs++;

iattributes[2*niattribs]=WGL_ALPHA_BITS_ARB;
iattributes[2*niattribs+1]=true;
niattribs++;

Обратите внимание, что при заполнении массива используется переменная niattribs, которая содержит номер текущего поля. Этот прием позволяет избежать исправления индексов при вставке/удалении параметров. Теперь получим список формата пикселей, которые можно использовать в буфере пикселей:

if (!wglChoosePixelFormatARB(hdc, iattributes, fattributes, MAX_PFORMATS, pformat, &nformats))
{
  MessageBox(NULL, "PBuffer creating error: request pixel format is unsupported", 
             "Unsupported pixel format", MB_OK | MB_ICONSTOP);
  exit(-1);
}

Теперь у нас есть вся необходимая информация для создания буфера пикселей:

HPBUFFERARB hbuffer1;
//----------------------
iattributes[0]=0;
hbuffer1=wglCreatePbufferARB(hdc, pformat[0], 256, 256, iattributes);

Для создания буфера пикселей используется команда wglCreatePbufferARB. В качестве параметров она принимает дескриптор окна, номер формата пикселей, разрешение буфера и список параметров. В случае удачного завершения она возвращает указатель на созданный pbuffer.

Функция wglChoosePixelFormatARB возвращает массив форматов пикселей, который удовлетворяет нашим критериям. Следовательно, можно использовать любой элемент массива, например, нулевой. Список параметров iattributes у нас пустой. Для того чтобы указать это, мы устанавливаем первый (нулевой) элемент массива равным нулю, в результате чего функция игнорирует остальные элементы массива.

Осталось только получить контексты окна и OpenGL для созданного буфера пикселей:

HDC hpbufdc1;
HGLRC pbufglctx1;
//---------------------
hpbufdc1=wglGetPbufferDCARB(hbuffer1);
pbufglctx1=wglCreateContext(hpbufdc1);

Контекст окна возвращает функция wglGetPbufferDCARB, принимающая в качестве параметра указатель на pbuffer. Контекст OpenGL получается традиционным для WGL способом.

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

wglQueryPbufferARB(hbuffer1, WGL_PBUFFER_WIDTH_ARB, &width);
wglQueryPbufferARB(hbuffer1, WGL_PBUFFER_WIDTH_ARB, &height);
char str[80];
sprintf(str, "PBuffer's width=%d, height=%d", width, height);
MessageBox(NULL, str, "Info", MB_OK | MB_ICONINFORMATION);

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

Для получения доступа к pbuffer его нужно сделать активным, используя функцию wglMakeCurrent, принимающей в качестве параметров контексты окна и OpenGL.

if (!wglMakeCurrent(hpbufdc1, pbufglctx1))
{
  MessageBox(NULL, "Cannot activate Pbuffer", "Error", MB_OK | MB_ICONSTOP);
  exit(-1);
}
Далее производим инициализацию OpenGL для данного pbuffer (параметры освещения, матрицы модели и проекции и т.д.)
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glClearColor(0, 0, 0, 0);

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(30, 1, 1, 10);
glMatrixMode(GL_MODELVIEW);
glTranslatef(0,0,-4);

glutSetWindow(WinID);

Последний оператор вновь делает активным текущее окно программы, используя оператор glutSetWindow библиотеки. На этом часть функции Init, отвечающая за инициализация pbuffer, оканчивается. Теперь рассмотрим собственно процедуру рисования:

void Display()
{
  if (!wglMakeCurrent(hpbufdc1, pbufglctx1))
  {
    MessageBox(NULL, "Cannot activate Pbuffer", "Error", MB_OK | MB_ICONSTOP);
    exit(-1);
  }

  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  glPushMatrix();
  glColor3f(0.5, 0.5, 0.5);
    glRotatef(GLfloat((GetTickCount()-Time))10, 0, 1, 0);
    glutSolidTeapot(0.5);

    glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, imgdata);
    glPopMatrix();

    glutSetWindow(WinID);

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glRasterPos2i(0,0);

    glDrawPixels(width, height, GL_RGBA, GL_UNSIGNED_BYTE, imgdata);

    glutSwapBuffers();
}

Сначала мы делаем pbuffer активным, рисуем в нем вращающийся чайник и копируем получившееся изображение в буфер с помощью команды glReadPixels. Затем активируем основное окно программы и командой glDrawPixels выводим в него изображение из буфера.

После окончания работы с буфером его нужно обязательно удалить (если этого не сделать, буфер останется в видеопамяти). Это можно сделать следующим образом:

WglDeleteContext(pbufglctx1);
WglReleasePbufferDCARB(hbuffer1, hpbufdc1);
WglDestroyPbufferARB(hbuffer1);

Сначала уничтожаем контекст OpenGL, затем освобождаем контекст pbuffer, и наконец, уничтожаем pbuffer.

Страницы: 1 2 3 4 Следующая »

24 февраля 2002

#расширения OpenGL, #NVIDIA, #OpenGL, #PBuffer, #SDK


Обновление: 17 сентября 2009

2001—2018 © GameDev.ru — Разработка игр