Главная -
Статьи -
Проекты -
Ссылки -
Скачать -
Из гельминтов -
Юмор, приколы -
Почитать -
Обо мне -
Мысли -
Гостевая -

Использование Р-буферов для внеэкранного рендеринга. Расширения 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.

Процесс работы с р-буфером можно разделить на следующие шаги:

  1. Инициализация необходимых расширений.
  2. Непосредственное создание р-буфера.
  3. Выбор р-буфера как текущей цели для рендеринга.
  4. Уничтожение р-буфера.

Инициализация необходимых расширений.

Поскольку необходимые нам расширения специфичны для 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", недавно вышедшей в издательстве БХВ-Петербург.


Copyright © 2004 Алексей В. Боресков

Используются технологии uCoz