|
Рендерринг в текстуру.
Содержание статьи:
Введение.
Реализация.
Создание буферов.
Пример.
Вступление.
к началу статьи
Сегодняшний туториал будет о рендерринге в текстуру. С помощью этой технологии некоторые вопросы (свечение а ля неон, отрисовка стволов в шутерах) решаются легко и просто. Суть технологии заключается в следующем: при каждом конкретном рендерринге программист имеет дело с 2 буферами – буфером кадра (хранит то что мы потом увидим на мониторе) и буфером глубины (отвечает за корректную отрисовку объектов частично или полностью перекрывающих друг друга). Фишка заключается в том, что бы перенаправить вывод геометрии в наш собственный, а не в стандартный буфер кадра (для него почти наверняка понадобится и свой буфер глубины, но это уже мелочи). После того как рендерринг будет завершен, мы вольны делать с буфером все, что нам заблагорассудится. Вот собственно и все. Можно начинать реализовывать?
Реализация.
к началу статьи
По доброй традиции начнем с класса:
|
DWORD RenderTargetCleanColor = 0x000000;
class cRenderTarget{
LPDIRECT3DSURFACE8 OuterColorBuffer;
LPDIRECT3DSURFACE8 OuterDepthBuffer;
LPDIRECT3DSURFACE8 LocalColorBuffer;
LPDIRECT3DSURFACE8 LocalDepthBuffer;
public:
cRenderTarget( void ){
OuterColorBuffer = NULL;
OuterDepthBuffer = NULL;
LocalColorBuffer = NULL;
LocalDepthBuffer = NULL;
}
void SetTextureAsRenderTarget( LPDIRECT3DDEVICE8 , LPDIRECT3DSURFACE8 );
void ResetRenderTarget( LPDIRECT3DDEVICE8 );
void GetRenderTarget( LPDIRECT3DDEVICE8 );
void CreateRenderTarget( LPDIRECT3DDEVICE8 , LPDIRECT3DSURFACE8 );
};
|
Здесь в RenderTargetCleanColor хранится цвет, которым будет очищаться внеэкранный буфер кадра, LocalColorBuffer и LocalDepthBuffer – наши буферы (буфер кадра и глубины соответственно), которыми мы будем подменять основные, OuterColorBuffer и OuterDepthBuffer – системные буферы, которые мы временно выключаем из рендерринга.
Для начала нам надо научиться специальным образом создавать текстуру. Для этого дополним уже известный и не раз встречавшийся нам класс cTexture:
|
HRESULT cTexture::CreateRenderTarget( LPDIRECT3DDEVICE8 Device , int w , int h ){
return( Device->CreateTexture( w , h ,
1 , D3DUSAGE_RENDERTARGET ,
D3DFMT_X8R8G8B8 , D3DPOOL_DEFAULT , &Texture));
}
HRESULT cTexture::GetSurfaceLevel( UINT Level, LPDIRECT3DSURFACE8 *ppSurfaceLevel ){
return( Texture->GetSurfaceLevel( Level , ppSurfaceLevel ) );
}
|
Первая функция создает текстуру, а вторая берет указатель на LPDIRECT3DSURFACE8 (в данном случае Level должен быть равен 0 – это будет поверхность исходной текстуры).
Вся особенность заключается в том, что текстура создается заданных размеров (вообще говоря, размеры текстуры – w и h - могут не являться степенями двойки) без какого-либо соотнесения с файлом с растровым изображением, а просто как область видео памяти.
Создание буферов.
к началу статьи
Теперь озаботимся созданием внеэкранных буферов. Буфер кадра создается так:
|
cTexture RTTexture;
LPDIRECT3DSURFACE8 TextureSurface
RTTexture.CreateRenderTarget( D3DDevice , 256 , 256 );
RTTexture.GetSurfaceLevel( 0 , TextureSurface );
RenderTarget.CreateRenderTarget ( D3DDevice , TextureSurface );
|
Функцию CreateRenderTarget я написал следующим образом:
|
void cRenderTarget::CreateRenderTarget( LPDIRECT3DDEVICE8 D3DDevice ,
LPDIRECT3DSURFACE8 TextureSurface ){
D3DSURFACE_DESC Desc;
memset( &Desc , 0 , sizeof( Desc ) );
if( TextureSurface->GetDesc( &Desc ) == D3D_OK ){
if( D3DDevice->CreateDepthStencilSurface( Desc.Width ,
Desc.Height , D3DFMT_D24S8 , D3DMULTISAMPLE_NONE ,
&LocalDepthBuffer ) != D3D_OK )
{
MessageBox( NULL , "Буффер глубины не был создан." , "Ошибка" , MB_OK );
exit(0);
}
}
LocalColorBuffer = TextureSurface;
}
|
В этой функции мы сначала узнаем параметры текстуры ( вызов GetDesc( &Desc ) ), нас интересуют только размеры, а затем создаем буфер глубины с размерами совпадающими с размерами текстуры.
Собственно вся подготовка завершена, и мы можем приступать к использованию:
|
void cRenderTarget::SetTextureAsRenderTarget( LPDIRECT3DDEVICE8 D3DDevice ){
//сохраняем указатель на текущий
//буфер цвета
if( !OuterColorBuffer ){
D3DDevice->GetRenderTarget( &OuterColorBuffer );
}
//сохраняем указатель на текущий
//буфер глубины
if( !OuterDepthBuffer ){
D3DDevice->GetDepthStencilSurface( &OuterDepthBuffer );
}
D3DDevice->SetRenderTarget( LocalColorBuffer , LocalDepthBuffer );
D3DDevice->Clear( 0 , NULL , D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER ,
RenderTargetCleanColor , 1.0f , 0 );
}
void cRenderTarget::ResetRenderTarget( LPDIRECT3DDEVICE8 D3DDevice ){
if( OuterColorBuffer && OuterDepthBuffer ){
D3DDevice->SetRenderTarget( OuterColorBuffer , OuterDepthBuffer );
OuterColorBuffer = NULL;
OuterDepthBuffer = NULL;
}
else{
MessageBox( 0 , "Попытка установить неинициализированные поверхности." ,
"Ошибка" , 0 );
exit( 0 );
}
}
|
В первой функции сначала сохраняем указатели на текущие буферы, а затем заменяем их нашими. Во второй функции все наоборот.
Пример.
к началу статьи
Теперь можно рассмотреть ещё один пример (инициализировать буферы мы уже научились, поэтому рассмотрим только пример рендерринга):
|
RenderTarget.SetTextureAsRenderTarget( D3DDevice );
VertexBuffer.Render( D3DDevice , FVF_D3DVERTEX );
RenderTarget.ResetRenderTarget( D3DDevice );
RTTexture.SetTexture( D3DDevice , 0 );
VertexBuffer.Render( D3DDevice , FVF_D3DVERTEX );
RTTexture.ResetTexture( D3DDevice , 0 );
|
В этом примере мы просто рендеррим содержимое VertexBuffer’а (там находится белый квадрат) в текстуру, а затем рендеррим его еще раз, натянув на него полученную текстуру.
Вот, пожалуй, на сегодня и все. Менее тривиальные примеры использования этого класса мы рассмотрим позже, а пока до свиданья!
к началу статьи
ЗЫ: способ связи прежний dodonov_a_a@inbox.ru
Смежные вопросы:
Урок 1.
Инициализация.
Вершинные буфферы и камера.
Урок
2. Проигрывание медиа-файлов.
Урок
3. Автоматизация загрузки и управления аудио и видео ресурсами.
Урок
4. Текстурирование. Текстурный менеджер.
Урок
5. Шрифты в Direct3D.
Урок
6. Элементы управления.
Урок
7. Вершинные шейдеры версии 1.0.
Урок
8. Система частиц.
Исходные коды. |
|
|