DirectX — отложенное затенение с объемами трафаретов

Я создаю графическое приложение, используя DirectX9, используя DXUT. В моем приложении я хотел бы реализовать отложенное затенение. Этот метод освещения требует, чтобы я отображал объем трафарета (маску) для каждого источника света в сцене. Чтобы избежать визуализации томов по отдельности, я бы хотел использовать возможности DirectX для создания экземпляров. Используя вершинный шейдер 3.0 и пиксельный шейдер 3.0, я успешно рисовал несколько экземпляров любой данной модели, однако, кажется, что когда я пытаюсь нарисовать тома трафарета, данные не записываются на поверхность трафарета глубины в буферном буфере.

Моя реализация отложенного затенения выглядит следующим образом в псевдокоде:

Нарисуйте всю геометрию сцены (проход геометрии) в Backbuffer:

  • Включить Z (D3DRS_ZENABLE, TRUE)
  • Включить ZWrites (D3DRS_ZWRITEENABLE, TRUE)
  • Отключить трафарет (D3DRS_STENCILENABLE, FALSE)
  • Установить CullMode (D3DRS_CULLMODE, D3DCULL_CCW)
  • Отключить ColorWrites (D3DRS_COLORWRITEENABLE, 0)
  • Отключить Alphablending (D3DRS_ALPHABLENDENABLE, FALSE)
  • RenderScene

Получить указатель на буфер

  • IDirect3DDevice9 :: GetRenderTarget (0, pBackbufferSurface)

Установите 3 цели визуализации (ЦВЕТ (0), НОРМАЛЬНЫЙ (1), ПОЗИЦИЯ (2)):

  • IDirect3DDevice9 :: SetRenderTarget (0 -> 2, ЦВЕТ -> ПОЛОЖЕНИЕ)

Нарисуйте всю геометрию сцены для 3 целей рендера:

  • Включить ColorWrites (D3DRS_COLORWRITEENABLE, CW_RED, CW_GREEN,
    CW_BLUE, CW_ALPHA)
  • Render Scene

Сбросить буфер для устройства RenderTarget 0:

  • IDirect3DDevice9 :: SetRenderTarget (0, pBackbufferSurface)

Установите текстуру эффекта (SrcColor) на цель рендеринга COLOR.

  • ID3DXEffect :: SetTexture («g_TextureSrcColor», pRTColor)

Визуализация полноэкранного четырехугольника и сэмпла из цели рендеринга COLOR (рисует сцену)


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

// set the instance buffer vertex declaration
hr = pd3dDevice->SetVertexDeclaration( CContentManager::GetInstanceBufferVertexDeclaration() );
if( FAILED( hr ) )
{
DebugStringDX( ClassName, "Failed to IDirect3DDevice9::SetVertexDeclaration( instances )at RenderScene()", __LINE__, hr );
return hr;
}

// render instances of each light( sphere and cone )
for( UINT i = 0; i < 2; ++i )
{
// point to base model
CXModel *pXBase = ( CXModel* )pCM->GetAppObject( i );
if( !pXBase )
{
DebugStringDX( ClassName,
"Failed to retrieve the base light model at index( " + ToString( i ) + " ) at Render()",
__LINE__,
E_POINTER );
continue;
}

// if the model has no instances
//      there is no reason to try and draw
UINT ucInstances = pXBase->GetNumInstances();
if( ucInstances < 1 )
continue;

// Set up the geometry data stream
hr = pd3dDevice->SetStreamSourceFreq( 0,
( D3DSTREAMSOURCE_INDEXEDDATA | ucInstances ) );
if( FAILED( hr ) )
{
DebugStringDX( ClassName, "Failed to IDirect3DDevice9::SetStreamSourceFreq( source )( index - " + ToString( i ) + " ) at RenderScene()", __LINE__, hr );
continue;
}

// Set up the instance data stream
IDirect3DVertexBuffer9 *pVBInstances = pXBase->GetInstanceBuffer();
hr = pd3dDevice->SetStreamSource( 1, pVBInstances, 0,
sizeof( CXModel::sInstanceEntry ) );
if( FAILED( hr ) )
{
DebugStringDX( ClassName, "Failed to IDirect3DDevice9::SetStreamSource( instances )( index - " + ToString( i ) + " ) at RenderScene()", __LINE__, hr );
continue;
}

hr = pd3dDevice->SetStreamSourceFreq( 1,
( D3DSTREAMSOURCE_INSTANCEDATA | 1ul ) );
if( FAILED( hr ) )
{
DebugStringDX( ClassName, "Failed to IDirect3DDevice9::SetStreamSourceFreq( instances )( index - " + ToString( i ) + " ) at Render()", __LINE__, hr );
continue;
}

// get light model frame root
pFrame = pXBase->GetFrameRoot();
if( !pFrame )
{
DebugStringDX( ClassName, "Failed to CXModel::GetFrameRoot( Light Model ) at Render()", __LINE__, hr );
continue;
}

//------------------------------------------------------------------------------------------------------------
// Stencil Volume Mask Pass
//------------------------------------------------------------------------------------------------------------
dev.SetRenderState( D3DRS_ZENABLE, TRUE );
dev.SetRenderState( D3DRS_ZWRITEENABLE, FALSE );
dev.SetRenderState( D3DRS_STENCILENABLE, TRUE );
dev.SetRenderState( D3DRS_STENCILFUNC, D3DCMP_ALWAYS );
dev.SetRenderState( D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP );
dev.SetRenderState( D3DRS_STENCILZFAIL, D3DSTENCILOP_INCR );
dev.SetRenderState( D3DRS_STENCILPASS, D3DSTENCILOP_KEEP );
dev.SetRenderState( D3DRS_STENCILWRITEMASK, 0xffffffff );
dev.SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );
dev.SetRenderState( D3DRS_COLORWRITEENABLE, 0 );

hr = BeginPass( CFXDeferred::STENCIL );
if( FAILED( hr ) )
{
DebugStringDX( ClassName, "Failed to BeginPass( StencilVolumeMask ) at Render()", __LINE__, hr );
continue;
}

// draw the light model
hr = DrawStaticModel( pd3dDevice, pFrame, &mWorld, pmView, pmProjection, true, bRenderStrips );
if( FAILED( hr ) )
{
DebugStringDX( ClassName, "Failed to DrawStaticModel( Light Model ) at Render()", __LINE__, hr );
continue;
}

hr = pE->EndPass();
if( FAILED( hr ) )
{
DebugStringDX( ClassName, "Failed to ID3DXEffect::EndPass() at Render()", __LINE__, hr );
continue;
}

//------------------------------------------------------------------------------------------------------------
// Diffuse Light Pass
//------------------------------------------------------------------------------------------------------------
dev.SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
dev.SetRenderState( D3DRS_SRCBLEND, D3DBLEND_ONE );
dev.SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ONE );
dev.SetRenderState( D3DRS_ZENABLE, FALSE );
dev.SetRenderState( D3DRS_CULLMODE, D3DCULL_CW );
dev.SetRenderState( D3DRS_COLORWRITEENABLE,
D3DCOLORWRITEENABLE_RED | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_ALPHA );
dev.SetRenderState( D3DRS_STENCILENABLE, TRUE );
dev.SetRenderState( D3DRS_STENCILFUNC, D3DCMP_EQUAL );
dev.SetRenderState( D3DRS_STENCILMASK, 0x1 );
dev.SetRenderState( D3DRS_STENCILPASS, D3DSTENCILOP_ZERO );
dev.SetRenderState( D3DRS_STENCILREF, 0x1 );
dev.SetRenderState( D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP );
dev.SetRenderState( D3DRS_STENCILFAIL, D3DSTENCILOP_ZERO );

hr = BeginPass( CFXDeferred::DIFFUSELIGHTSTENCIL );
if( FAILED( hr ) )
{
DebugStringDX( ClassName, "Failed to BeginPass( DiffuseSpecularLightStencilPass ) at Render()", __LINE__, hr );
continue;
}

// draw light model
hr = DrawStaticModel( pd3dDevice, pFrame, &mWorld, pmView, pmProjection, true, bRenderStrips );
if( FAILED( hr ) )
{
DebugStringDX( ClassName, "Failed to DrawStaticModel( Light Model ) at Render()", __LINE__, hr );
continue;
}

hr = pE->EndPass();
if( FAILED( hr ) )
{
DebugStringDX( ClassName, "Failed to ID3DXEffect::EndPass() at Render()", __LINE__, hr );
continue;
}
}

Вот объявление вершины буфера экземпляра:

// create instance vertex declaration
D3DVERTEXELEMENT9 pElements[] = {
{ 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
{ 0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0 },
{ 0, 24, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 },
{ 1, 0, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 1 },
{ 1, 16, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 2 },
{ 1, 32, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 3 },
{ 1, 48, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 4 },
{ 1, 64, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 5 },
D3DDECL_END()
};

Этот метод отлично работает без инстансинга. Когда я использую PIX для отладки, это означает, что «Пиксель не прошел проверку трафарета». Вот вызов для очистки буфера перед рендерингом:

pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL, D3DCOLOR_ARGB( 0, 0, 0, 0 ), 1.0f, 0 ) );

Во время создания устройства я изменяю настройки для использования AutoDepthStencilFormat D3DFMT_D24S8, например, так:

// check for D3DFMT_D24S8 AutoDepthStencilFormat
// if present set the AutoDepthStencilFormat to D3DFMT_D24S8
if( DXUTGetD3D9Enumeration()->GetPossibleDepthStencilFormatList()->Contains( D3DFMT_D24S8 ) )
{
pDeviceSettings->d3d9.pp.AutoDepthStencilFormat = D3DFMT_D24S8;
}

Если я изменяю параметр трафарета (последний параметр) IDirect3DDevice9 :: Clear () на 1. Объем трафарета проходит тест трафарета … это потому, что я установил операцию трафарета, чтобы увеличить значение трафарета на 1, если пиксель маска тома пересекается с пикселем в буферном буфере, и в этом случае допускается цвет пикселя источника света. Это указывает на то, что существует одна из двух проблем:

  1. При рисовании сцены (объектов) во время «Прохождения Instanced Geometry» … буферный буфер не сохраняет глубину объектов при их отрисовке (поэтому у теста трафарета для сравнения есть значение «0»)
    или же
  2. При рисовании томов (источников света) трафарета буфер не хранит глубину томов при их рисовании …

Это из-за моего метода инстансинга? Это из-за версий шейдеров (vs3, ps3) или версии DX?

0

Решение

Проблема была в том, что я скомпилировал вершинный шейдер (для маски объема трафарета) с vs_3_0 … Я изменил его на vs_2_0, и теперь он работает нормально … Я не понимаю, потому что я думал, что было необходимо использовать модель вершинного шейдера 3.0 с экземплярами …

0

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]