Blending: Vertex Alpha Introduction In Direct3D there are a remarkable number of ways to blend a source texture with a destination surface. Often that surface will be the backbuffer. True alphablending requires a transparency value which is the alpha. The alpha can come from a variety of sources, each with their own benefits. In this tutorial the alpha will be set in the vertices of the quad. Since each vertex can have a different value this allows a fair bit of control over the transparency of the rendered object. The source from the Textured Quad lesson will be used as the base for this lesson. Vertex Definition The Textured Quad tutorial used a vertex definition with a screen-space position and 1 set of texture coordinates. To control transparency on a per-vertex level another member will need to be added, this is the diffuse. The diffuse colour is a 32-bit value with 1 byte being assigned to each of the Red, Green, Blue and Alpha channels. In previous lessons polygons were coloured by setting the RGB values, but the Alpha channel was ignored. In this lesson the colour channels will be ignored and only the alpha will be used. The order of the members of a vertex structure is important. The diffuse goes between the position and the texture coordinates. The FVF definition needs to be updated to reflect this addition. ```struct textured_vertex{ float x, y, z, rhw; // The transformed(screen space) position for the vertex. DWORD diffuse; // Our diffuse colour, which we'll only use for alpha float tu,tv; // Texture coordinates }; //Transformed vertex with 1 set of texture coordinates and a diffuse colour const DWORD tri_fvf=D3DFVF_XYZRHW|D3DFVF_TEX1|D3DFVF_DIFFUSE; ``` Vertex Data To better illustrate the effects of alphablending, 4 textured quads will be shown with different alpha values. The top-left quad will be fully opaque (alpha == 0xFF or 255). The second quad will be 50% transparent (0x7f alpha) and the third will be completely transparent (0x0) at the top fading into complete opacity at the bottom. The final quad will have all vertices set to 0x0F for an almost invisible quad. Please note that while the RGB values of the diffuse colour are set to 0xFFFFFF, those values are never used in this tutorial and could be set to any value without having any impact. ```textured_vertex data[]={ //Opaque Triangle {50,220,1,1,0xFFFFFFFF,0,1},{50,20,1,1,0xFFFFFFFF,0,0},{250,20,1,1,0xFFFFFFFF,1,0}, {50,220,1,1,0xFFFFFFFF,0,1},{250,20,1,1,0xFFFFFFFF,1,0},{250,220,1,1,0xFFFFFFFF,1,1}, //Half visible {400,220,1,1,0x7FFFFFFF,0,1},{400,20,1,1,0x7FFFFFFF,0,0},{600,20,1,1,0x7FFFFFFF,1,0}, {400,220,1,1,0x7FFFFFFF,0,1},{600,20,1,1,0x7FFFFFFF,1,0},{600,220,1,1,0x7FFFFFFF,1,1}, //Top-Down fade {50,450,1,1,0xFFFFFFFF,0,1},{50,250,1,1,0x00FFFFFF,0,0},{250,250,1,1,0x00FFFFFF,1,0}, {50,450,1,1,0xFFFFFFFF,0,1},{250,250,1,1,0x00FFFFFF,1,0},{250,450,1,1,0xFFFFFFFF,1,1}, //Barely visible {400,450,1,1,0x0FFFFFFF,0,1},{400,250,1,1,0x0FFFFFFF,0,0},{600,250,1,1,0x0FFFFFFF,1,0}, {400,450,1,1,0x0FFFFFFF,0,1},{600,250,1,1,0x0FFFFFFF,1,0},{600,450,1,1,0x0FFFFFFF,1,1}, }; int vert_count=sizeof(data)/sizeof(textured_vertex); ``` The last line of code that computes the vertex count is exactly the same as it was in the previous lesson. It's repeated here to underscore how useful it is to have your code do all of the hard work for you. It would have been simple to set a constant variable to the vertex count or even to pass it directly to the rendering function, but then it would have to be maintained. That maintenance may not seem like much effort, but it's very easy to forget and the resulting errors can be very frustrating. By using code like this 3 additional quads were added and no changes were required to take that into account. Enabling Blending With those changes the code will compile and run. 4 quads will be visible but they will be completely opaque. To have them blend properly alphablending must be enabled. Additionally a number of other states must be set to determine how the blending should take place. A single call to SetRenderState will enable blending. The D3DRS_ALPHABLENDENABLE only has 2 valid values: true and false. Setting it to true enables alphablending and false disables it. ``` //Enable alpha blending g_d3d_device->SetRenderState(D3DRS_ALPHABLENDENABLE,true); ``` When using an alpha value to control blending the source of the alpha needs to be configured. Previous lessons used SetTextureStageState to control where colour values come from. Setting the source for alpha is almost the same. ``` //Set the colour to come completely from the texture g_d3d_device->SetTextureStageState(0,D3DTSS_COLOROP,D3DTOP_SELECTARG1); g_d3d_device->SetTextureStageState(0,D3DTSS_COLORARG1,D3DTA_TEXTURE); g_d3d_device->SetTextureStageState(0,D3DTSS_COLORARG2,D3DTA_DIFFUSE); //Ignored //Set the alpha to come completely from the diffuse g_d3d_device->SetTextureStageState(0,D3DTSS_ALPHAOP,D3DTOP_SELECTARG1); g_d3d_device->SetTextureStageState(0,D3DTSS_ALPHAARG1,D3DTA_DIFFUSE); g_d3d_device->SetTextureStageState(0,D3DTSS_ALPHAARG2,D3DTA_TEXTURE); //Ignored ``` The Alpha Operation (D3DTSS_ALPHAOP) is set to select argument #1 as the sole source of alpha and argument #1 is set to the diffuse colour. Notice that we set argument #2 even though it is not used. Not all video drivers are created equal and it is always best to set things to safe values even when they are not being used. Blending Equations At this point Alphablending is enabled and the source of the alpha has been set. Exactly how the blending should occur has not been determined. This is done with a blending equation. Though they can get quite involved, blending equations are generally pretty simple. The end result that we want is for a certain percentage of the source texture (the texture on the quad) to be blended with the existing image on the backbuffer (the destination). The percentage is controlled by the alpha value. We also want an inverse relationship, as the quad becomes more opaque we want less of the backbuffer added in. This could be expressed as: ``` A = source alpha Source = Textured Quad Dest = Contents of Backbuffer final_result = ( Source * A) + (Dest * (1-A)) ``` Seeing the multiplication may be confusing since the alpha values were given as values from 0..255. Internally those values are scaled to a floating point value between 0 and 1. In this lesson setting the alpha to 0xFF (255) causes the texture to be completely opaque. The value 0xFF gets scaled to 1.0 so the resulting equation for this case would be: ``` final_result = (Source * 1.0) + (Dest * 0) ``` As you can see this results in the final result be 100% of the textured quad and 0% of the backbuffer. The opaque quad completely overwrites the backbuffer. Now that the desired blending equation has been determined it must be conveyed to the Direct3D device. ``` //Determine how they will be blended g_d3d_device->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_SRCALPHA); g_d3d_device->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA); g_d3d_device->SetRenderState(D3DRS_BLENDOP,D3DBLENDOP_ADD); ``` The translation to code is fairly straightforward. D3DRS_SRCBLEND sets the source image to be multiplied by the source's alpha (D3DBLEND_SRCALPHA). D3DRS_DESTBLEND is set so the destination surface is multiplied by the inverse of the source's alpha (D3DBLEND_INVSRCALPHA). Finally those 2 results should be added together(D3DRS_BLENDOP=D3DBLENDOP_ADD). ``` FinalColor = (SourceColor * SourceBlend) + (DestinationColor * DestBlend) ``` Earlier in the Introduction it was noted that there are many ways to do alphablending. Even in the case where a simple single source (like the vertex alpha used here) there are a dizzying number of options. D3DRS_SRCBLEND can be set to any of 15 different values some of which ignore the alpha value completely. D3DRS_DESTBLEND can be set to any of those 15 values as well. Adding to that, D3DRS_BLENDOP has 5 different possible values for a grand total of (15 * 15 * 5) 1125 different blending equations! Many of those combinations will give similar results and many more may not yield anything terribly useful. Still more won't be supported by most graphics hardware. The blending setup used in this lesson is probably the most common. Other common ones will be covered in later tutorials. Changing the Background Clear Colour To make it easier to see how a texture will blend against different coloured backgrounds, controls have been added to cycle the colour used in the backbuffer. The Left and Right Arrows are used to cycle between black, red, green, blue, and white. Other colours could be trivially added to the array of colours. The code to do this isn't anything new and isn't related to the lesson so it won't be reviewed here. Lesson Downloads Source and Executable for C++ (215K) Source for C++ (10K) To compile this lesson you will also need the Drunken Hyena Common Code Back