Использование Р-буферов для внеэкранного рендеринга. Расширения WGL_ARB_pbuffer,
WGL_ARB_pixel_format и WGL_ARB_render_texture.
В ряде случаев возникает необходимость произвести отдельный рендеринг сцены (или какой-либо ее части),
с тем, чтобы результаты такого рендеринга можно было в дальнейшем использовать (например, в качестве
текстуры).
Простейшим примером ситуации, когда это может понадобится, является рендеринг поверхности воды с
рябью. Если мы при этом хотим, чтобы поверхность действительно отражала текущую сцену, то использование
EMBM с заранее рассчитанной кубической картой отражений уже не подходит.
В то же время, если бы отражение в плоской воде (без ряби) можно было бы занести в текстуру, то
тогда для имитации ряби можно или использовать шейдер NV_OFFSET_TEXTURE_2D или выводить поверхность
воды как сетку, вершины которой подвергаются искажениям.
Использование функции glReadPixels позволяет прочитать изображение из фреймбуфера в память CPU, потом
его можно будет загрузить в текстуру. Однако такой подход требует сначала копирования из видеопамяти
в память центрального процессора, а затем копирования из памяти центрального процессора в видеопамять.
Подобные операции копирования являются довольно дорогостоящими, поэтому этот путь малоприемлим.
Гораздо более удобно при помощи функции glCopyTexSubImage2D переписать прямоугольный фрагмент
изображения из фреймбуфера сразу в текстуру, т.е. копирование происходит из видеопамяти в видеопамять
минуя центральный процессор.
Несмотря на то, что этот подход гораздо боле эффективен, чем предыдущий, он все равно требует явного
копирования памяти, а также использование того же самого фреймбуфера, что и для построения главного
изображения.
Было бы гораздо удобнее создать себе "виртуальный" (или пиксельный) буфер в который можно
было бы осуществлять рендеринг, однако со своим размером и другими атрибутами и никак не связанный
с основным фреймбуфером.
Также было бы очень удобно, если бы этот буфер можно было бы непосредственно использовать в качестве
данных для текстуры, вообще избегая тем самым копирования памяти.
Все эти возможности и предоставляет использование так называемых р-буферов (p-buffer). При
этом на современных графических ускорителях р-буфера хранятся непосредственно в видеопамяти и
процесс рендеринга в них аппаратно ускорен.
Для создания и работы с р-буферами необходима поддержка сразу двух расширений WGL_ARB_pixel_format
и WGL_ARB_pbuffer. Для использования р-буфера в качестве источника данных для текстуры необходима
также поддержка расширения WGL_ARB_render_texture.
Обратите внимание, что все эти расширения начинаются с префикса WGL и поэтому предназначены для
операционной системы Windows.
Процесс работы с р-буфером можно разделить на следующие шаги:
- Инициализация необходимых расширений.
- Непосредственное создание р-буфера.
- Выбор р-буфера как текущей цели для рендеринга.
- Уничтожение р-буфера.
Инициализация необходимых расширений.
Поскольку необходимые нам расширения специфичны для Windows, то для проверки их поддержки уже нельзя
пользоваться введенной ранее функцией isExtensionSupported, поскольку используемая там строка не
содержит расширений, специфичный для платформы M$ Windows.
Поэтому нужно получить строку со всеми поддерживаемыми расширениями, специфичными для Windows. Для
этого сначала необходимо стандартным образом проверить поддержку расширения WGL_ARB_extensions и в
случае его поддержки получить указатель на функцию wglGetExtensionsStringARB.
if ( isExtensionSupported ( "WGL_ARB_extensions" ) )
wglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)
getProcAddress ( "wglGetExtensionsStringARB" );
В результате мы получаем указатель на функцию, возвращающую список всех WGL-расширений для заданного
контекста устройства (device context):
const char * exts = wglGetExtensionsStringARB ( wglGetCurrentDC () );
Тогда для проверки какого-либо из WGL-расширений достаточно проверить встречается ли название
расширения в полученной строке. Если да, то можно используя функцию wglGetProcAddress получить адреса
всех вводимых расширением функций.
Версию библиотеки libExt с поддержкой всех используемых в данной статье расширений можно скачать
здесь.
Непосредственное создание р-буфера.
В первую очередь следует получить валидный контекст устройства:
HDC hDc = wglGetCurrentDC ();
Далее необходимо получить подходящий формат пикселов для создания р-буфера.
Для этого служит вводимая расширением WGL_ARB_pixel_format функция wglChoosePixelFormat.
BOOL wglChoosePixelFormatARB ( HDC hDc, const int * intAttrs, const float * floatAttrs,
UINT maxFormats, int * formats, UINT * numFormats );
В параметрах intAttrs и floatAttrs передаются списки целочисленных и вещественных
атрибутов. Каждый такой атрибут задается парой значений (attr, value), где сначала идет
идентификатор атрибута, а затем - его значение. Нулевой идентификатор атрибута обозначает конец
списка.
Параметр maxFormats сообщает какое количество целочисленных идентификаторов формата можно
записать в массив formats.
Последний параметр numFormats является указателем на целочисленную переменную в которой будет
возвращено количество подходящий форматов, это значение может быть меньше чем maxFormats.
Таблица 1. Основные типы атрибутов для функции wglChoosePixelFormatARB
Атрибут | Комментарий |
WGL_COLOR_BITS_ARB | Минимальное общее количество бит под цвет |
WGL_STENCIL_BITS_ARB | Минимальное количество бит по значение в буфере трафарета |
WGL_AUX_BUFFERS_ARB | Минимальное количество дополнительных буферов |
WGL_SUPPORT_OPENGL_ARB | Должен ли формат поддерживать вывод средствами OpenGL |
WGL_DRAW_TO_PBUFFER_ARB | Ненулевое значение говорит о том, что вывод будет направляться в р-буфер |
WGL_ACCUM_BITS_ARB | Общее количество бит под значение в буфере накопления |
WGL_DOUBLE_BUFFER_ARB | Ненулевое значение говорит о необходимости создания второго буфера |
WGL_ALPHA_BITS_ARB | Минимальное количество бит под значение в альфа-канале |
WGL_DEPTH_BITS_ARB | Минимальное количество бит под значение в буфере глубины |
WGL_PIXEL_TYPE_ARB | Тип значений пикселов, принимает одно из двух значений - WGL_TYPE_RGBA_ARB или WGL_TYPE_COLORINDEX_ARB |
WGL_RED_BITS_ARB | Минимальное количество бит под значение в красном канале |
WGL_GREEN_BITS_ARB | Минимальное количество бит под значение в зеленом канале |
WGL_BLUE_BITS_ARB | Минимальное количество бит под значение в синем канале |
Функция возвращает нулевое значение в случае ошибки и ненулевое, если вызов прошел успешно.
В случае, если получен хотя бы один идентификатор формата, то его можно использовать для создания
р-буфера.
Для этого служит следующая функция:
HPBUFFER wglCreatePbufferARB ( HDC hDc, int format, int width, int height, const int * attrs );
Параметр hDc - это контекст устройства, полученный на шаге 1, параметр format - это
идентификатор формата пикселов для создаваемого р-буфера.
Параметры width и height задают желаемый размер р-буфера в пикселах, а в параметре
attrs передаются дополнительные целочисленные параметры, аналогично функции
wglChoosePixelFormatARB.
Данная функция возвращает handle созданного р-буфера или NULL в случае ошибки.
Размер созданного р-буфера может отличаться от запрашиваемого, поэтому после создания р-буфера
следует узнать его истинные размеры при помощи функции wglQueryPbufferARB:
int width;
int height;
wglQueryPbufferARB ( hBuffer, WGL_PBUFFER_WIDTH_ARB, &width );
wglQueryPbufferARB ( hBuffer, WGL_PBUFFER_HEIGHT_ARB, &height );
После непосредственного создания р-буфера, необходимо получить контекст устройства для него при
помощи функции:
HDC wglGetPbufferDCARB ( HPBUFFER hBuffer );
Заключительным шагом в создании р-буфера является создание контекста рендеринга для него. Это можно
сделать при помощи функции wglCreateContext.
HGLRC wglCreateContext ( HDC hBufDc );
Выбор р-буфера как текущей цели для рендеринга.
Для того, чтобы направить вывод в заданный р-буфер служит команда
BOOL wglMakeCurrent ( HDC hBufDc, HGLRC hBufRc );
Параметры hBufDc и hBufRc - это контекст устройства и контекст рендеринга для данного
р-буфера.
Для того, чтобы снова направить вывод в окно, следует вызвать функцию wglMakeCurrent с параметрами,
соответствующеми окну, в которое надо перенаправить вывод. При использовании библиотеки GLUT можно
для этой цели использовать функцию glutSetWindow, передав ей целочисленный идентификатор окна.
Уничтожение р-буфера.
Для того, чтобы уничтожить р-буфер (и, тем самым, освободить ресурсы, связанные с ним) необходимо
выполнить следующие три шага -
- уничтожить контекст рендеринга для р-буфера
- освободить контекст устройства р-буфера
- уничтожить сам р-буфер
Все эти действия выполняет следующий фрагмент кода:
wglDeleteContext ( hGlRc );
wglReleasePbufferDCARB ( hBuffer, hDc );
wglDestroyPbufferARB ( hBuffer );
Обработка переключение видеорежима
В случае переключения видеорежима может произойти нарушение целостности р-буфера, т.е. он фактически
перестает годиться для дальнейшего использования.
Для того, чтобы определить является ли данный р-буфер по-прежнему валидным или нет можно использовать
следующий фрагмент кода:
int flag;
wglQueryPbufferARB ( hBuffer, WGL_PBUFFER_LOST_ARB, &flag );
Если после выполнения этого фрагмента кода значение переменной flag окажется отличным от нуля,
то это значит, что р-буфер был поврежден и не является больше валидным.
В этом случае его следует уничтожить и создать заново.
Копирование данных из р-буфера в текстуру
После того, как р-буфер был успешно создан и в него был произведен вывод, можно использовать функцию
glCopyTexSubImage2D для копирование содержимого р-буфера (или только его части) в текстуру.
Однако обратите внимание, что в платформе Windows различные контексты рендеринга имеют свои наборы
атрибутов, в том числе и свои наборы дисплейных списков, текстур и т.п. Поэтому, если вы хотите
использовать для рендеринга в р-буфер какую-либо текстуру, то ее следует явно загрузить в этом
контексте.
Впрочем вместо этого можно воспользоваться функцией wglShareLists, позволяющей перенести дисплейные
списки и текстуры из одного контекста рендеринга в другой.
Связывание р-буфера с текстурой
В случае поддержки расширения WGL_ARB_render_texture можно непосредственно использовать р-буфер как
источник данных для текстуры не прибегая к копированию.
При этом сам р-буфер привязывается (bind) к соответствующей текстуре и во время этой привязки
текстура содержит содержимое цветового буфера данного р-буфера, а сам р-буфер не доступен для
рендеринга. Когда в р-буфер необходимо произвести вывод, то он сперва должен быть "освобожден" от
данной текстуры.
Можно также связать р-буфер с кубической картой, тогда при рендеринге р-буферу сообщается в какую
грань куба идет вывод.
Можно также использовать пирамидальное фильтрование, однако для этого надо либо явно задавать все
промежуточные уровни, либо воспользоваться расширением GL_SGIS_generate_mipmap, которое обеспечивает
автоматическое построение всех промежуточных уровней для заданной текстуры.
Если р-буфер привязан к текстуре и в это время из-за переключения видеорежима сам р-буфер перестал
быть валидным, то несмотря на это содержимое текстуры по прежнему остается пригодным для
использования и не разрушается.
При освобождении р-буфера от текстуры содержимое его цветового буфера может стать неопределенным.
Для того, чтобы данный р-буфер мог быть использован для привязки к текстуре, необходимо задание
дополнительных атрибутов в функциях wglChoosePixelFormatARB и wglCreatePbufferARB.
Дополнительными атрибутами для функции wglChoosePixelFormatARB являются WGL_BIND_TO_TEXTURE_RGB_ARB
или WGL_BIND_TO_TEXTURE_RGBA_ARB. Посредством задание для одного из этих атрибутов ненулевого значения
сообщается, что данный р-буфер должен поддерживать привязку в RGB (или RGBA) текстуре.
Для функции wglCreatePbufferARB вводятся следующие дополнительные атрибуты - WGL_TEXTURE_TARGET_ARB,
принимающий одно из значений - WGL_TEXTURE_1D_ARB, WGL_TEXTURE_2D_ARB или WGL_TEXTURE_CUBE_MAP_ARB,
и атрибут WGL_MIPMAP_TEXTURE_ARB, ненулевое значение которого обеспечивает выделение памяти под все
промежуточные уровни пирамидального фильтрования.
Для того, чтобы привязать р-буфер к текущей текстуре служит следующая функция.
BOOL wglBindTexImageARB ( HPBUFFER hBuffer, int buf );
Параметр buf задает какой именно из буферов данного р-буфера следует связать с текущей
текстурой. Он принимает одно из следующих значений - WGL_FRONT_LEFT_ARB, WGL_FRON_RIGHT_ARB,
WGL_BACK_LEFT_ARB, WGL_BACK_RIGHT_ARB, WGL_AUX0_ARB, WGL_AUX1_ARB, ...
После выполнения этой команды данный р-буфер привязывается к текущей текстуре и в него нельзя
производить рендеринг.
Для того, чтобы "отвязать" р-буфер от текстуры служит функция:
BOOL wglReleaseTexImageARB ( HPBUFFER hBuffer, int buf );
При привязывании р-буфера к кубической карте необходимо при помощи функции wglSetPbufferAttribARB
устанавливать в какую именно грань куба идет вывод. В качестве атрибута выступает
WGL_CUBE_MAP_FACE_ARB, который принимает одно из значений WGL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,
WGL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB, WGL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,
WGL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB, WGL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB и
WGL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB.
Обратите внимание, что перед выбором очередной грани кубической карты следует выполнить команду
glFlush ().
Реализация
Для удобства работы можно р-буфер завернуть в класс, инкапсулирующий все операции с ним, включая его
создание и уничтожение.
Ниже приводится описание такого класса.
class PBuffer
{
protected:
HDC hdc;
HGLRC hGlRc;
HPBUFFERARB hBuffer;
int width;
int height;
int mode;
bool shareObjects;
HDC saveHdc; // save values from from makeCurrent
HGLRC saveHglrc;
public:
PBuffer ( int theWidth, int theHeight, int theMode = modeAlpha | modeDepth | modeStencil,
bool theShareObjects = true );
~PBuffer ();
int getWidth () const
{
return width;
}
int getHeight () const
{
return height;
}
bool getShareObjects () const
{
return shareObjects;
}
int getMode () const
{
return mode;
}
void clear ();
bool create ();
// set attribute (like WGL_TEXTURE_CUBE_MAP*_ARB)
bool setAttr ( int attr, int value );
bool makeCurrent (); // direct rendering to this pbuffer
bool restoreCurrent (); // direct rendering to prev. target
bool isLost () const; // whether p-buffer is lost due to mode switch
// bind pbuffer to previously bound texture
bool bindToTexture ( int buf = WGL_FRONT_LEFT_ARB );
// unbind from texture (release)
bool unbindFromTexture ( int buf = WGL_FRONT_LEFT_ARB );
// set specific cubemap side, call glFlush
// after side is done
bool setCubemapSide ( int side );
void printLastError();
enum Mode
{
modeAlpha = 1,
modeDepth = 2,
modeStencil = 4,
modeAccum = 8,
modeDouble = 16,
modeTexture1D = 32,
modeTexture2D = 64,
modeTextureCubeMap = 128,
modeTextureMipmap = 256
};
};
Конструктор этого класса принимает размеры буфера, битовую маску атрибутов и признак того, следует
ли копировать объекты из текущего контекста в контекст р-буфера.
Маска атрибутов строится из следующих значений:
Константа | Комментарий |
modeAlpha | Необходим альфа-канал |
modeDepth | Необходим буфер глубины |
modeStencil | Необходим буфер трафарета |
modeAccum | Необходим буфер накопления |
modeDouble | Необходим двойной буфер |
modeTexture1D | Будет привязываться к одномерной текстуре |
modeTexture2D | Будет привязываться к двухмерной текстуре |
modeTextureCubeMap | Будет привязываться к кубической карте |
modeTextureMipmap | Использовать пирамидальное фильтрование |
Методы create и clear служат для создание р-буфера и его уничтожения.
При этом для создания р-буфера используются параметры, переданные в конструктор.
Метод makeCurrent делает данный р-буфер текущей целью вывода, при этом запоминается какая
цель была текущей на момент этого вызова и метод restoreCurrent восстанавливает цель вывода,
которая была на момент вызова makeCurrent.
Метод isLost возвращает значение true, если буфер был разрушен. В этом случае для его
восстановления нужно вызвать сперва clear, а затем create.
Методы bindToTexture unbindFromTexture служат для привязывания и освобождения данного
р-буфера от текущей текстуры.
Метод setCubemapSide задает в какую именно грань кубической карты идет рендеринг. Он
автоматически вызывает glFlush.
Метод printLastError печатает на stderr расшифровку последней возникшей ошибки, что может быть
полезно при отладке приложения.
При реализации данного класса удобно создать параметризованный класс AttrList, для инкапсуляции
работы с атрибутами различных типов.
#define MAX_ATTRIBS 20
#define MAX_PFORMATS 20
template
class AttrList
{
T attrs [2*MAX_ATTRIBS];
int numAttrs;
public:
AttrList ()
{
numAttrs = 0;
clear ();
}
void clear ()
{
for ( int i = 0; i < 2*MAX_ATTRIBS; i++ )
attrs [i] = 0;
}
void add ( int attr, T value )
{
attrs [2*numAttrs ] = attr;
attrs [2*numAttrs + 1] = value;
numAttrs++;
}
int getNumAttrs () const
{
return numAttrs;
}
const T * getAttrs () const
{
return attrs;
}
};
Ниже приводится реализация метода PBuffer :: create ().
bool PBuffer :: create ()
{
HDC hCurDc = wglGetCurrentDC ();
HGLRC hCurRc = wglGetCurrentContext ();
int format;
// check for extension support & initialization
if ( wglChoosePixelFormatARB == NULL || wglCreatePbufferARB == NULL ||
wglGetPbufferDCARB == NULL || wglQueryPbufferARB == NULL ||
wglReleasePbufferDCARB == NULL || wglDestroyPbufferARB == NULL )
return false;
if ( (mode & modeTexture1D) || (mode & modeTexture2D) || (mode & modeTextureCubeMap) )
if ( wglBindTexImageARB == NULL || wglReleaseTexImageARB == NULL ||
wglSetPbufferAttribARB == NULL )
return false;
// query for pixel format
AttrList intAttrs;
AttrList floatAttrs;
intAttrs.add ( WGL_SUPPORT_OPENGL_ARB, GL_TRUE );
intAttrs.add ( WGL_DRAW_TO_PBUFFER_ARB, GL_TRUE );
intAttrs.add ( WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB );
intAttrs.add ( WGL_RED_BITS_ARB, 8 );
intAttrs.add ( WGL_GREEN_BITS_ARB, 8 );
intAttrs.add ( WGL_BLUE_BITS_ARB, 8 );
intAttrs.add ( WGL_DOUBLE_BUFFER_ARB, (mode & modeDouble ? GL_TRUE : GL_FALSE ) );
if ( mode & modeAlpha )
intAttrs.add ( WGL_ALPHA_BITS_ARB, 8 );
if ( mode & modeDepth )
intAttrs.add ( WGL_DEPTH_BITS_ARB, 24 );
if ( mode & modeStencil )
intAttrs.add ( WGL_STENCIL_BITS_ARB, 8 );
if ( mode & modeAccum )
intAttrs.add ( WGL_ACCUM_BITS_ARB, 32 );
if ( (mode & modeTexture1D) || (mode & modeTexture2D) || (mode & modeTextureCubeMap) )
if ( mode & modeAlpha )
intAttrs.add ( WGL_BIND_TO_TEXTURE_RGBA_ARB, GL_TRUE );
else
intAttrs.add ( WGL_BIND_TO_TEXTURE_RGB_ARB, GL_TRUE );
int pixelFormats [MAX_PFORMATS];
unsigned numFormats = 0;
if ( !wglChoosePixelFormatARB ( hCurDc, intAttrs.getAttrs (), floatAttrs.getAttrs (),
MAX_PFORMATS, pixelFormats, &numFormats ) )
return false;
if ( numFormats < 1 )
return false;
format = pixelFormats [0];
AttrList props;
if ( (mode & modeTexture1D) || (mode & modeTexture2D) )
if ( mode & modeAlpha )
props.add ( WGL_TEXTURE_FORMAT_ARB, WGL_TEXTURE_RGBA_ARB );
else
props.add ( WGL_TEXTURE_FORMAT_ARB, WGL_TEXTURE_RGB_ARB );
if ( mode & modeTexture1D )
props.add ( WGL_TEXTURE_TARGET_ARB, WGL_TEXTURE_1D_ARB );
else
if ( mode & modeTexture2D )
props.add ( WGL_TEXTURE_TARGET_ARB, WGL_TEXTURE_2D_ARB );
else
if ( mode & modeTextureCubeMap )
props.add ( WGL_TEXTURE_TARGET_ARB, WGL_TEXTURE_CUBE_MAP_ARB );
if ( mode & modeTextureMipmap )
props.add ( WGL_MIPMAP_TEXTURE_ARB, GL_TRUE );
hBuffer = wglCreatePbufferARB ( hCurDc, format, width, height, props.getAttrs () );
if ( hBuffer == NULL )
return false;
hdc = wglGetPbufferDCARB ( hBuffer );
if ( hdc == NULL )
return false;
hGlRc = wglCreateContext ( hdc );
if ( shareObjects )
wglShareLists ( hCurRc, hGlRc );
wglQueryPbufferARB ( hBuffer, WGL_PBUFFER_WIDTH_ARB, &width );
wglQueryPbufferARB ( hBuffer, WGL_PBUFFER_HEIGHT_ARB, &height );
return true;
}
Примеры использования
Рассмотрим простейший пример использования р-буфера - строится изображение вращающегося куба, на
каждую грань которого накладывается текстура, представляющая собой изображение вращающегося
чайника (выбор именно чайника объясняется тем, что это единственный из стандартных объектов glut для
всех вершин которого заданы текстурные координаты).
Рис 1. Вращающийся куб с текстурой вращающегося чайника.
Ниже приводится фрагмент кода.
void renderToBuffer ()
{
if ( pbuffer.isLost () )
printf ( "pbuffer lost\n" );
if ( !pbuffer.makeCurrent () )
printf ( "makeCurrent failed\n" );
glClearColor ( 0, 0.5, 0, 1 );
glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glBindTexture ( GL_TEXTURE_2D, mainTex );
glPushMatrix ();
glRotatef ( angle, 0, 1, 0 );
glRotatef ( angle/2, 0, 0, 1 );
glutSolidTeapot ( 2 );
glPopMatrix ();
glBindTexture ( GL_TEXTURE_2D, pbufferTex );
pbuffer.restoreCurrent ();
}
void display ()
{
initPBuffer ();
renderToBuffer ();
glClearColor ( 0.0, 0.0, 1.0, 1.0 );
glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glEnable ( GL_TEXTURE_2D );
glBindTexture ( GL_TEXTURE_2D, pbufferTex );
if ( !pbuffer.bindToTexture () )
pbuffer.printLastError ();
glPushMatrix ();
glRotatef ( angle, 1, 0, 0 );
glRotatef ( angle/2, 0, 1, 0 );
drawBox ( -1.5, 1.5, -1.5, 1.5, -1.5, 1.5, pbufferTex );
glPopMatrix ();
glBindTexture ( GL_TEXTURE_2D, pbufferTex );
if ( !pbuffer.unbindFromTexture () )
pbuffer.printLastError ();
glutSwapBuffers ();
}
Рассмотрим еще один пример (его идея взята из работы по курсу компьютерной графики на факультете ВМиК
МГУ в 2004 году) - строится изображение объекта в р-буфер, затем полученная текстура используется
для вывода прямоугольной сетки вершин. Сами вершины при этом подвергаются небольшим смещениям для
создания эффекта "горячего воздуха на огнем".
Рис 2. Эффект горячего воздуха.
Ниже приводится реализация
void distortVertex ( Vector3D& v, float time )
{
float sine = 0.005 * sin ( 11*v.y + 16*v.z + time / 5 );
float cosine = 0.005 * cos ( 7*v.y - 14*v.z + time / 5 );
v.y += sine;
v.z += cosine;
}
void display ()
{
initPBuffer ();
renderToBuffer ();
glClearColor ( 0.0, 0.0, 1.0, 1.0 );
glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glEnable ( GL_TEXTURE_2D );
glBindTexture ( GL_TEXTURE_2D, pbufferTex );
if ( !pbuffer.bindToTexture () )
pbuffer.printLastError ();
float time = 0.08 * glutGet ( GLUT_ELAPSED_TIME );
glBegin ( GL_TRIANGLES );
for ( int i = 0; i < 29; i++ )
for ( int j = 0; j < 29; j++ )
{
Vector3D v00 ( vertexGrid [i][j ] );
Vector3D v01 ( vertexGrid [i][j+1] );
Vector3D v10 ( vertexGrid [i+1][j] );
Vector3D v11 ( vertexGrid [i+1][j+1] );
distortVertex ( v00, time );
distortVertex ( v01, time );
distortVertex ( v10, time );
distortVertex ( v11, time );
glTexCoord2fv ( textureGrid [i][j] );
glVertex3fv ( v00 );
glTexCoord2fv ( textureGrid [i][j+1] );
glVertex3fv ( v01 );
glTexCoord2fv ( textureGrid [i+1][j+1] );
glVertex3fv ( v11 );
glTexCoord2fv ( textureGrid [i][j] );
glVertex3fv ( v00 );
glTexCoord2fv ( textureGrid [i+1][j] );
glVertex3fv ( v10 );
glTexCoord2fv ( textureGrid [i+1][j+1] );
glVertex3fv ( v11 );
}
glEnd ();
glBindTexture ( GL_TEXTURE_2D, pbufferTex );
if ( !pbuffer.unbindFromTexture () )
pbuffer.printLastError ();
glutSwapBuffers ();
}
Весь исходный код к этой статье, включая реализацию класса PBuffer и оба приведенных примера можно
скачать здесь.
Более полную информацию о работе с р-буферами (в том числе и для платформы Linux), Вы можете найти
в книге "Расширения OpenGL",
недавно вышедшей в издательстве БХВ-Петербург.
|