Я работаю над игрой, использующей DirectX 9. Вот что я пытаюсь сделать:
После рендеринга сцены, поверх нее я хочу визуализировать несколько спрайтов: черная обложка на всю сцену и несколько спрайтов, которые являются масками, показывающими, где на обложке должны быть отверстия. До сих пор я пробовал возиться с режимом наложения, но безуспешно. Мой код настройки выглядит так:
D3DD->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
D3DD->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
D3DD->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
Я думаю, что лучшим способом было бы умножить каждый спрайт альфа, но согласно http://msdn.microsoft.com/en-us/library/windows/desktop/bb172508%28v=vs.85%29.aspx такой режим не поддерживается. Есть ли другой способ сделать это?
edit
После ответа Нико Шертлера вот код, который я придумал:
LPDIRECT3DTEXTURE9 pRenderTexture;
LPDIRECT3DSURFACE9 pRenderSurface,
pBackBuffer;
// create texture
D3DD->CreateTexture(1024,
1024,
1,
D3DUSAGE_RENDERTARGET,
D3DFMT_R5G6B5,
D3DPOOL_DEFAULT,
&pRenderTexture,
NULL);
pRenderTexture->GetSurfaceLevel(0,&pRenderSurface);
// store old render target - back buffer
D3DD->GetRenderTarget(0,&pBackBuffer);
// set new render target - texture
D3DD->SetRenderTarget(0,pRenderSurface);
//clear texture to opaque black
D3DD->Clear(0,
NULL,
D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,
D3DCOLOR_XRGB(0,0,0),
32.0f,
0);
// set blending
D3DD->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO);
D3DD->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);
D3DD->SetRenderState(D3DRS_SRCBLENDALPHA, D3DBLEND_ZERO);
D3DD->SetRenderState(D3DRS_DESTBLENDALPHA, D3DBLEND_SRCALPHA);
D3DD->SetRenderState(D3DRS_SEPARATEALPHABLENDENABLE, TRUE);
//// now I render hole sprites the usual way
// restore back buffe as render target
D3DD->SetRenderTarget(0,pBackBuffer);
// restore blending
D3DD->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
D3DD->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
D3DD->SetRenderState(D3DRS_SRCBLENDALPHA, D3DBLEND_SRCALPHA);
D3DD->SetRenderState(D3DRS_DESTBLENDALPHA, D3DBLEND_INVSRCALPHA);
D3DD->SetRenderState(D3DRS_SEPARATEALPHABLENDENABLE, FALSE);
ulong color = ulong(-1);
Vertex2D v[4];
v[0] = Vertex2D(0, 0, 0);
v[1] = Vertex2D(1023, 0, 0);
v[3] = Vertex2D(1023, 1023, 0);
v[2] = Vertex2D(0, 1023, 0);
D3DD->SetTexture(0, pRenderTexture);
D3DD->SetFVF(D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1);
D3DD->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, v, sizeof(Vertex2D));
D3DD->SetTexture(0, NULL);
// release used resources
pRenderTexture->Release();
pRenderSurface->Release();
pBackBuffer->Release();
К сожалению, приложение аварийно завершает работу при восстановлении старой цели рендеринга. Любой совет?
Во-первых, вы должны сначала создать маску в отдельной текстуре. Затем вы можете добавить отверстия по мере необходимости. Наконец, нарисуйте маску на экране:
Initialize the texture
Clear it to opaque black
Using the following blend states:
D3DRS_SRCBLEND -> D3DBLEND_ZERO (hole's color does not matter)
D3DRS_DESTBLEND -> D3DBLEND_ONE (preserve the black color)
D3DRS_SRCBLENDALPHA -> D3DBLEND_ZERO
D3DRS_DESTBLENDALPHA -> D3DBLEND_SRCALPHA
D3DRS_SEPARATEALPHABLENDENABLE -> TRUE
Draw each hole sprite
Restore default blending (src_alpha / inv_src_alpha)
Render the texture as a sprite to the back buffer
Вышеупомянутое состояние смешивания предполагает, что отверстия непрозрачны там, где должно быть отверстие. Затем цвет рассчитывается по формуле:
blended color = 0 * hole sprite color + 1 * background color
который всегда должен быть черным.
И альфа-канал рассчитывается по:
blended alpha = 0 * hole sprite alpha + (1 - hole sprite alpha) * background alpha
Поэтому, когда спрайт дыры непрозрачен, смешанная альфа становится равной 0. Там, где она прозрачна, смешанная альфа является предыдущим значением. Значения между ними смешиваются.
Других решений пока нет …