Alpha blending in render targets

rendering UI elements can be very time consuming – especially when multiple layers of stacked objects such as containers and windows are being drawn semi-transparently.

One way of reducing rendering time i<pre lang=”cpp”>s to cache certain UI elements – a window for example usually remains the same until it has changed its size or displays new content. Since that happens extremely rarely when compared to the ~60 times a second it is being rendered, caching seems like a sensible move.

There’s only one problem:

rendering (semi-)transparent elements to a render target doesn’t exactly work the way you’d expect it to. This is due to the fact that during rendering, pixels get blended together using a set of render states and texture stage states. If the color of a pixel gets computed using a certain blend function, its alpha value gets computed the same way. This usually results in oddly looking UI elements with parts that have a higher transparency than intended.

Googling for a while has brought up the idea of premultiplied alpha channels which – in some cases – help. The idea behind this is to (as the name implies) premultiply a pixel’s alpha value with its color components.

This seems to work fine for single-layered elements. As soon as several UI elements overlap (a transparent button inside a window for example), this method fails as well.

Some more research brought up a DirectX9 renderstate that can be activated to enable a different blending operation for alpha values.

D3DRS_SEPARATEALPHABLENDENABLE

What this renderstate – once enabled – does is allow to set a different blend function for alpha values of a pixel.

pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA)
pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
pd3dDevice->SetRenderState(D3DRS_SRCBLENDALPHA, D3DBLEND_ONE);
pd3dDevice->SetRenderState(D3DRS_DESTBLENDALPHA, D3DBLEND_INVSRCALPHA);
pd3dDevice->SetRenderState(D3DRS_SEPARATEALPHABLENDENABLE, TRUE);

This snippet blends the color components of a pixel together using their respective alpha values as one would expect:

res.rgb =p0.rgb * (1 – p1.a) + p1.rgb * p1.a;

But the alpha component of the resulting pixel gets computed differently:

res.a = p0.a * (1 – p1.a)  + p1.a;

I’m still experimenting with this, but it seems to be a step in the right direction.

p.s.

a quick performance check of caching UI elements vs. not caching them turned out to reduce rendering time for my example scene to almost 50%.