Whenever Direct3D renders a primitive, it computes the color for each pixel, deriving the color from the primitive's texture, material, and lighting information. You can use alpha blending to combine the primitive's color with the color previously stored in that pixel of the frame buffer (from other primitives or the clear color, that is, the color the scene is specified to be cleared to when you use the IDirect3DDevice7::Clear method).
Using this form of alpha blending, you can simulate semitransparent objects, combine two images, and add special effects such as force fields, flames, plasma beams, and light mapping. Alpha information can be stored in the polygon's vertex structure or in each texel of a texture map. Direct3D uses the following formula to compute the final color for each pixel in the rendered primitive:
FinalColor = SourcePixelColor · SourceBlendFactor + DestPixelColor · DestBlendFactor
The variables used in this equation are defined as follows:
Direct3D lets you change the SourceBlendFactor and DestBlendFactor flags to generate the effect you want. For example, the final pixel will just be the color of the polygon if you set SourceBlendFactor to D3DBLEND_ONE (1.0, 1.0, 1.0, 1.0) and DestBlendFactor to D3DBLEND_ZERO (0.0, 0.0, 0.0, 0.0). On the other extreme, if you set SourceBlendFactor to D3DBLEND_ZERO and DestBlendFactor to D3DBLEND_ONE, the polygon will be completely transparent and the final pixel will just be the color that was previously in the frame buffer. If you set up the blend factors to use values between these extremes such that the source factor and end factor add up to 1.0, the polygon will be blended into the frame buffer with varying levels of transparency. For most of the interesting transparency effects (like flames and plasma beams), you need to set SourceBlendFactor and DestBlendFactor to settings that use the alpha values of the texture or vertices rather than to simple constant values. By varying the alpha values from frame to frame, you can create a shimmering effect. Also, if FinalColor has a value over 1.0—for example, if SourceBlendFactor and DestBlendFactor are both (1.0, 1.0, 1.0, 1.0)—the color values will get saturated at 1.0 because 1.0 is the highest acceptable value.
Recall from earlier chapters that when you set a color, you're actually setting the red, green, blue, and alpha values. Thus you can use the alpha value of the texels in each texture with the SourceBlendFactor variable to control the resulting transparency.
Direct3D uses a variety of formulas to combine a source color and a destination color into the final color, based on the blending flags you choose. As an example, let's say the following variables define the source and destination values:
(sr, sg, sb, sa) is the information in the source pixel.
(dr, dg, db, da) is the information in the destination pixel.
Let's also say that the source blend flag and the destination blend flag are both set to D3DBLEND_SRCALPHA, which generates blend factors represented as (sa, sa, sa, sa). Given these definitions, this formula will compute the final blended color (fr, fg, fb, fa):
(fr, fg, fb, fa) = (sr · sa + dr · sa, sg · sa + dg · sa, sb · sa + db · sa, sa · sa + da · sa)
Here is a description of each of the possible blend flags. Except where noted, these flags can be applied to either the D3DRENDERSTATE_SRCBLEND state or the D3DRENDERSTATE_DESTBLEND state:
Once you've verified that a target system supports alpha blending, you can pass the D3DRENDERSTATE_ALPHABLENDENABLE enumerated value as the first parameter to IDirect3DDevice7::SetRenderState and TRUE as the second parameter to enable alpha transparency blending. Then you can control the blending factors by using the D3DRENDERSTATE_SRCBLEND and D3DRENDERSTATE_DESTBLEND enumerated values, as follows:
// // Set up alpha blending. // m_pd3dDevice->SetRenderState( D3DRENDERSTATE_ALPHABLENDENABLE, TRUE ); m_pd3dDevice->SetRenderState( D3DRENDERSTATE_SRCBLEND, D3DBLEND flag ); m_pd3dDevice->SetRenderState( D3DRENDERSTATE_DESTBLEND, D3DBLEND flag ); |
To maximize performance, you should set ALPHABLENDENABLE to FALSE for the primitives you're rendering that don't require alpha blending rather than provide constant alpha values, which produce the same effect—that is, just writing the source pixel into the frame buffer without blending it. Alpha blending requires a fair bit of extra math and memory access, so turning it on and off is worth the effort. You should minimize the need to change render states by grouping the objects that require alpha blending. One way to reduce the number of necessary render state changes is to render all opaque polygons first with alpha blending turned off and then to enable alpha blending and render the alpha polygons second.