Additive Blending Tutorial Setting up Additive Blending is simple. To show it off, we need a few more things. First our 'sprite' interface. It's pretty lame, but it does what we need. It's a simple structure that holds the current position of each of our 3 sprites as well as their x & y velocities. These values are all in screen coordinates. ```#define SPRITE_SIZE (400.0f) struct track_struct{ float x_vel,y_vel; float left,top,right,bottom; } g_tracker[3]={ {0.15f,0.12f, 100.0f,50.0f,100.0f+SPRITE_SIZE,50.0f+SPRITE_SIZE}, {0.13f,0.15f, 100.0f,50.0f,100.0f+SPRITE_SIZE,50.0f+SPRITE_SIZE}, {-0.12f,-0.14f, 100.0f,50.0f,100.0f+SPRITE_SIZE,50.0f+SPRITE_SIZE} }; ``` Here we have our vertex definitions, one set for each sprite. ```//The diffuse colour will be used to modulate the texture. Since our texture is //black & white, the black parts will be unaffected and the white parts will be //replaced by the vertex colour. //This way we get a Red, Green, and Blue sprite with only a single texture. //The x & y values will be changed every frame and rebuilt into a vertex buffer, all //the other values will stay the same. my_vertex g_square_vertices[] ={ { 100.0f, 350.0f, 0.5f, 1.0f,0xFFFF0000, 0.0f, 1.0f}, // x, y, z, rhw, colour, tu, tv { 100.0f, 50.0f, 0.5f, 1.0f,0xFFFF0000, 0.0f, 0.0f}, { 400.0f, 350.0f, 0.5f, 1.0f,0xFFFF0000, 1.0f, 1.0f}, { 400.0f, 350.0f, 0.5f, 1.0f,0xFFFF0000, 1.0f, 1.0f}, { 100.0f, 50.0f, 0.5f, 1.0f,0xFFFF0000, 0.0f, 0.0f}, { 400.0f, 50.0f, 0.5f, 1.0f,0xFFFF0000, 1.0f, 0.0f}, { 100.0f, 350.0f, 0.5f, 1.0f,0xFF0000FF, 0.0f, 1.0f}, // x, y, z, rhw, colour, tu, tv { 100.0f, 50.0f, 0.5f, 1.0f,0xFF0000FF, 0.0f, 0.0f}, { 400.0f, 350.0f, 0.5f, 1.0f,0xFF0000FF, 1.0f, 1.0f}, { 400.0f, 350.0f, 0.5f, 1.0f,0xFF0000FF, 1.0f, 1.0f}, { 100.0f, 50.0f, 0.5f, 1.0f,0xFF0000FF, 0.0f, 0.0f}, { 400.0f, 50.0f, 0.5f, 1.0f,0xFF0000FF, 1.0f, 0.0f}, { 100.0f, 350.0f, 0.5f, 1.0f,0xFF00FF00, 0.0f, 1.0f}, // x, y, z, rhw, colour, tu, tv { 100.0f, 50.0f, 0.5f, 1.0f,0xFF00FF00, 0.0f, 0.0f}, { 400.0f, 350.0f, 0.5f, 1.0f,0xFF00FF00, 1.0f, 1.0f}, { 400.0f, 350.0f, 0.5f, 1.0f,0xFF00FF00, 1.0f, 1.0f}, { 100.0f, 50.0f, 0.5f, 1.0f,0xFF00FF00, 0.0f, 0.0f}, { 400.0f, 50.0f, 0.5f, 1.0f,0xFF00FF00, 1.0f, 0.0f} }; ``` We set our COLOR Texture Stages to allow the vertex colour to modulate the texture. We do not set the ALPHA Texture Stages, because we have no alpha channel in the texture or vertices. ``` //Here we're setting the texture colour to be modulated by the //vertex colours. This allows us to tint our textures. In this //case we're using the same texture but using the vertices to //completely change the colours for each sprite. g_d3d_device->SetTextureStageState(0,D3DTSS_COLOROP, D3DTOP_MODULATE); g_d3d_device->SetTextureStageState(0,D3DTSS_COLORARG1, D3DTA_TEXTURE); g_d3d_device->SetTextureStageState(0,D3DTSS_COLORARG2, D3DTA_DIFFUSE ); ``` We do need to set our Blend States and enable Alpha Blending. ``` //This is the magic bit here. There is no alpha channel in our textures //and no alpha in our vertices. By setting our blend states to D3DBLEND_ONE //we're setting an Additive drawing mode. Each new object has it's pixel values //added to the background (and what's been drawn before). //The black background in our textures is automatically ignored since it just adds 0 //whereas the coloured areas add their colour. g_d3d_device->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_ONE); g_d3d_device->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_ONE); //Even though no alpha channels are being used, we have to set this to TRUE //or each sprite will opaquely over-write the background as well as previously //drawn objects. g_d3d_device->SetRenderState(D3DRS_ALPHABLENDENABLE,TRUE); ``` In the render() procedure we update the positions of each sprite and write it back over our vertex buffer. ```int counter; my_vertex *runner; //This will point to the vertex we are currently modifying //We use it to 'run' through the vertex buffer rebuilding our scene //Lock our VB so we can rebuild our 'sprites' each frame g_vb->Lock(0, //Offset, we want to start at the beginning 0, //SizeToLock, 0 means lock the whole thing (unsigned char**)&runner, //If successful, this will point to the data in the VB 0); //Flags, nothing special //We have 3 'sprites', we update each of them based on their velocities for(counter=0;counter<3;counter++){ //Apply x velocity and clamp to the edges. g_tracker[counter].left+=g_tracker[counter].x_vel; g_tracker[counter].right+=g_tracker[counter].x_vel; if(g_tracker[counter].left < 0.0f){ g_tracker[counter].left=0.0f; g_tracker[counter].right=SPRITE_SIZE; //Constant, bad programmer g_tracker[counter].x_vel*=-1; }else if(g_tracker[counter].right > (float)g_width){ g_tracker[counter].right=(float)g_width; g_tracker[counter].left=g_width-SPRITE_SIZE; g_tracker[counter].x_vel*=-1; } //Apply y velocity and clamp to the edges. g_tracker[counter].top+=g_tracker[counter].y_vel; g_tracker[counter].bottom+=g_tracker[counter].y_vel; if(g_tracker[counter].top < 0.0f){ g_tracker[counter].top=0.0f; g_tracker[counter].bottom=SPRITE_SIZE; //Constant, bad programmer g_tracker[counter].y_vel*=-1; }else if(g_tracker[counter].bottom > (float)g_height){ g_tracker[counter].bottom=(float)g_height; g_tracker[counter].top=g_height-SPRITE_SIZE; g_tracker[counter].y_vel*=-1; } //Now that we've updated the sprite structures, we cram the data into our //Vertex Buffer. runner->x=g_tracker[counter].left; runner->y=g_tracker[counter].bottom; runner++; runner->x=g_tracker[counter].left; runner->y=g_tracker[counter].top; runner++; runner->x=g_tracker[counter].right; runner->y=g_tracker[counter].bottom; runner++; runner->x=g_tracker[counter].right; runner->y=g_tracker[counter].bottom; runner++; runner->x=g_tracker[counter].left; runner->y=g_tracker[counter].top; runner++; runner->x=g_tracker[counter].right; runner->y=g_tracker[counter].top; runner++; } //We've updated all of the positions, time to unlock our VB g_vb->Unlock(); ``` And that's all there is to it.