This tutorial is part of a Collection: 01. DirectX 9 - Braynzar Soft Tutorials
2597
views
12. Blending
In this lesson, we'll learn how to use simple blending. Blending can be used for many different effects, like the for glass in windows, or for masking textures and objects. Here we'll take 3 different images for the background, and slowly blend them together to make the background look interesting. We'll also learn how to make and use the alpha channel in a texture for blending.
Blending is used to achieve a variety of effects including windows, masking, cool effects and other things. In this lesson, we're going to learn how to give a texture an alpha channel, then use the alpha channel to determine what parts of the image or more or less transparent. We'll also learn how to us the diffuse alpha in a material to make an image transparent. Blending is the idea of taking the pixel that is going to be computed(source pixel), and combining it with the pixel that has already been computed (destination pixel). Don't forget that for blending to work properly, objects that don't use blending need to be drawn first, and then objects that use blending to be drawn according to their distance from the camera in a back to front order. Blending doesn't take much code, so there will be more explaining this lesson than code to go through Blending is turned off by default, so to enable it you must do this d3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, true); Blending takes more to compute, so when your done drawing everything that needs to be blended, you should turn off blending by setting it to false instead of true The formula for computing the source pixel in blending is: OutputPixel = SourcePixel x SourceBlendFactor + DestinationPixel x DestinationBlendFactor Each component is a 4-D color vector (R, G, B, A) **OutputPixel **- Pixel that is the result of blending **SourcePixel **- Pixel going to be computed/drawn and will be blended with pixel on the Back-Buffer. **SourceBlendFactor **- A value 0 to 1 and is the percent of the source pixel that will be used. **DestPixel **- Pixel that is already on the backbuffer that the source pixel will be blended with. **DestBlendFactor **- A value between 0 and 1 which is the percent of the destination pixel that will be used. The source and destination blend factors can be used to manipulate the pixels that will be used in blending to get different effects. Here are some predefined values you can use: **D3DBLEND_ZERO **— (0, 0, 0, 0) **D3DBLEND_ONE **- (1, 1, 1, 1) **D3DBLEND_SRCCOLOR **- (Rs, Gs, Bs, As) **D3DBLEND_INVSRCCOLOR **— (1 – Rs, 1 – Gs, 1 – Bs, 1 – As) **D3DBLEND_SRCALPHA **— (As, As, As, As) - default **D3DBLEND_INVSRCALPHA **— (1 – As, 1 – As, 1 – As, 1 – As) - default **D3DBLEND_DESTALPHA **— (Ad, Ad, Ad, Ad) **D3DBLEND_INVDESTALPHA **— (1 – Ad, 1 – Ad, 1 – Ad, 1 – Ad) **D3DBLEND_DESTCOLOR **— (Rd, Gd, Bd, Ad) **D3DBLEND_INVDESTCOLOR **— (1 – Rd, 1 – Gd, 1 – Bd, 1 – Ad) There are two ways to get an alpha value out of an object. Through the textures alpha channel, or through the alpha in the diffuse color. The alpha value used is by default taken from the textures alpha channel, but if that is not available, it is taken from the diffuse color in the material. You can choose whether to get the alpha value from the texture or the diffuse color with the following. //alpha channel d3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); d3dDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); //diffuse color d3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE); d3dDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); Ok, now were gonna give an image an alpha channel using the DirectX texture Tool. The DirectX Texture Tool can be found in the DirectX Utilities folder in the start menu. We are going to learn how to create a DDS file using this tool. It can be loaded the same way as a BMP or JPG image using D3DXCreateTextureFromFile. We will use this image as the texture. +[http://www.braynzarsoft.net/image/100139][braynzar] And this will be the image we'll use to create an alpha channel on the texture. (save this image) +[http://www.braynzarsoft.net/image/100140][blend mask] Here is the final result after applying the alpha channel. (save this one too) +[http://www.braynzarsoft.net/image/100141][final result] Ok, now open up the DirectX Texture Tool. Then goto File->Open and open the picture of the brain. then goto Format->Change Surface Format. A box should pop up. Pull down the drop-down box and select the Unsigned 32-bit: A8R8G8B8. This adds another 8 bits to the pixel format to include 8 bits for an alpha channel. Then goto File->Open Onto Alpha Channel Of This Texture.... Now load the alpha image. Goto File->Save and thats it. On to the code... bgblendcount will be the speed we are changing the alpha value each frame. bgblend will hold the change. The same is for the bg1blendcount and bg1blend values. float bgblendcount = 0.002f; float bgblend = 0.0f; float bg1blendcount = 0.001f; float bg1blend = 0.0f; These next two new lines are just pointers to hold the vertex data so we can map out our textures onto quads. IDirect3DVertexBuffer9* brainVertexBuffer = 0; IDirect3DVertexBuffer9* bgVertexBuffer = 0; The next lines are pointers to store the 3 background textures we'll be using. IDirect3DTexture9* bg1 = 0; IDirect3DTexture9* bg2 = 0; IDirect3DTexture9* bg3 = 0; Here is where you can use the source and destination blend factor values i mentioned above. Right now its set at the default values. d3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); d3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); First we increment or decrement the bgblend and bg1blend values by the bgblendcount and bg1blendcount values. Then we check to see if the bgblend or bg1blend values is higher than one or lower than 0 because alpha values are only between 0 and 1. after that we set the diffuse alpha color to the bgblend and bg1blend values. bgblend += bgblendcount; bg1blend += bg1blendcount; if( bgblend >= 1.0f ) bgblendcount = -bgblendcount; if( bgblend <= -0.0f ) bgblendcount = -bgblendcount; bgm.Diffuse.a = bgblend; if( bg1blend >= 0.8f ) bg1blendcount = -bg1blendcount; if( bg1blend <= -0.1f ) bg1blendcount = -bg1blendcount; bgm1.Diffuse.a = bg1blend; First thing we draw in our scene is the first background image which won't be blended at all. d3dDevice->SetMaterial(&solidwhite); d3dDevice->SetTexture(0, bg1); d3dDevice->SetStreamSource(0, bgVertexBuffer, 0, sizeof(Vertex)); d3dDevice->SetFVF(VERTEXFVF); d3dDevice->DrawPrimitive(D3DPT_TRIANGLELIST,0,2); Now enable alpha blending. d3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, true); This is where we set the alpha component to the diffuse color alpha. This way, we can increment/decrement it each frame to make it look like the background is morphing kinda. d3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE); d3dDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); Now we draw our second and third backgrounds which will slowly be blended in and out. d3dDevice->SetMaterial(&bgm); d3dDevice->SetTexture(0, bg2); d3dDevice->SetStreamSource(0, bgVertexBuffer, 0, sizeof(Vertex)); d3dDevice->SetFVF(VERTEXFVF); d3dDevice->DrawPrimitive(D3DPT_TRIANGLELIST,6,2); d3dDevice->SetMaterial(&bgm1); d3dDevice->SetTexture(0, bg3); d3dDevice->SetStreamSource(0, bgVertexBuffer, 0, sizeof(Vertex)); d3dDevice->SetFVF(VERTEXFVF); d3dDevice->DrawPrimitive(D3DPT_TRIANGLELIST,12,2); Now we change the alpha component from diffuse color alpha to the alpha channel in the texture, so we can mask the brain thing. d3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); d3dDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); Lastly, we draw our brain, and disable alpha blending. d3dDevice->SetTexture(0, cubetex); d3dDevice->SetMaterial(&white); d3dDevice->SetStreamSource(0, brainVertexBuffer, 0, sizeof(Vertex)); d3dDevice->SetFVF(VERTEXFVF); d3dDevice->DrawPrimitive(D3DPT_TRIANGLELIST,0,2); d3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, false); Thats all there is to it, i hoped i explained this well enough! Here's the final code: main.cpp ... ... float bgblendcount = 0.002f; float bgblend = 0.0f; float bg1blendcount = 0.001f; float bg1blend = 0.0f; IDirect3DVertexBuffer9* brainVertexBuffer = 0; IDirect3DVertexBuffer9* bgVertexBuffer = 0; IDirect3DTexture9* cubetex = 0; //Pointer to store texture data //Pointer to store background textures IDirect3DTexture9* bg1 = 0; IDirect3DTexture9* bg2 = 0; IDirect3DTexture9* bg3 = 0; D3DMATERIAL9 white; //Pointer to store material D3DMATERIAL9 solidwhite;//Pointer to store first bg material D3DMATERIAL9 bgm; //Pointer to store second bg material D3DMATERIAL9 bgm1; //Pointer to store third bg material D3DLIGHT9 light; //Pointer to store light ... ... bool SetupScene(){ //Set up our Scene d3dDevice->CreateVertexBuffer( 18 * sizeof(Vertex), D3DUSAGE_WRITEONLY, VERTEXFVF, D3DPOOL_MANAGED, &bgVertexBuffer, 0); Vertex bgvertices[] = { //front face { 133.0f,-100.0f,95.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f}, {-133.0f,-100.0f,95.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f}, {-133.0f, 100.0f,95.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}, { 133.0f,-100.0f,95.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f}, {-133.0f, 100.0f,95.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}, { 133.0f, 100.0f,95.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f}, { 133.0f,-100.0f,94.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f}, {-133.0f,-100.0f,94.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f}, {-133.0f, 100.0f,94.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}, { 133.0f,-100.0f,94.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f}, {-133.0f, 100.0f,94.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}, { 133.0f, 100.0f,94.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f}, { 133.0f,-100.0f,93.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f}, {-133.0f,-100.0f,93.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f}, {-133.0f, 100.0f,93.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}, { 133.0f,-100.0f,93.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f}, {-133.0f, 100.0f,93.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}, { 133.0f, 100.0f,93.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f}, }; void* pVoid; bgVertexBuffer->Lock(0, 0, (void**)&pVoid, 0); //copy the bgvertices[] array into the vertex buffer memcpy(pVoid, bgvertices, sizeof(bgvertices)); bgVertexBuffer->Unlock(); d3dDevice->CreateVertexBuffer( 6 * sizeof(Vertex), D3DUSAGE_WRITEONLY, VERTEXFVF, D3DPOOL_MANAGED, &brainVertexBuffer, 0); Vertex squarevertices[] = { //front face { 1.0f,-1.0f,-1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f}, {-1.0f,-1.0f,-1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f}, {-1.0f, 1.0f,-1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}, { 1.0f,-1.0f,-1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f}, {-1.0f, 1.0f,-1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}, { 1.0f, 1.0f,-1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f}, }; brainVertexBuffer->Lock(0, 0, (void**)&pVoid, 0); //copy the squarevertices[] array into the vertex buffer memcpy(pVoid, squarevertices, sizeof(squarevertices)); brainVertexBuffer->Unlock(); D3DXCreateTextureFromFile( // Create texture. d3dDevice, "braynzar.dds", &cubetex); D3DXCreateTextureFromFile( // background 1 texture. d3dDevice, "bg1.jpg", &bg1); D3DXCreateTextureFromFile( // bg 2 texture. d3dDevice, "bg2.jpg", &bg2); D3DXCreateTextureFromFile( // bg 3 texture. d3dDevice, "bg3.jpg", &bg3); //Create the light ZeroMemory(&light, sizeof(light)); light.Type = D3DLIGHT_DIRECTIONAL; light.Diffuse = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f); light.Ambient = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f) * 0.2f; light.Specular = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f) * 0.1f; light.Direction = D3DXVECTOR3(1.0f, 0.0f, 0.7f); //Create a white material ZeroMemory(&white, sizeof(white)); white.Diffuse = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f); white.Ambient = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f); white.Specular = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f); white.Emissive = D3DXCOLOR(0.0f, 0.0f, 0.0f, 1.0f); white.Power = 5.0f; ZeroMemory(&solidwhite, sizeof(solidwhite)); solidwhite.Diffuse = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f); solidwhite.Ambient = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f); solidwhite.Specular = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f); solidwhite.Emissive = D3DXCOLOR(0.0f, 0.0f, 0.0f, 1.0f); solidwhite.Power = 5.0f; ZeroMemory(&bgm, sizeof(bgm)); bgm.Diffuse = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f); bgm.Ambient = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f); bgm.Specular = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f); bgm.Emissive = D3DXCOLOR(0.0f, 0.0f, 0.0f, 1.0f); bgm.Diffuse.a = 0.0f; bgm.Power = 5.0f; ZeroMemory(&bgm1, sizeof(bgm1)); bgm1.Diffuse = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f); bgm1.Ambient = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f); bgm1.Specular = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f); bgm1.Emissive = D3DXCOLOR(0.0f, 0.0f, 0.0f, 1.0f); bgm1.Diffuse.a = 0.0f; bgm1.Power = 5.0f; //Set the Filter d3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); d3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); d3dDevice->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR); D3DXVECTOR3 pos(0.0f, 0.0f, -4.0f); //position of camera in 3d space D3DXVECTOR3 targ(0.0f, 0.0f, 0.0f); //Direction camera is looking D3DXVECTOR3 up(0.0f, 1.0f, 0.0f); //Direction that is up D3DXMATRIX View; D3DXMatrixLookAtLH(&View, &pos, &targ, &up); //Compute view space tranformation matrix d3dDevice->SetTransform(D3DTS_VIEW, &View); //Set the view matrix D3DXMATRIX proj; D3DXMatrixPerspectiveFovLH( //Compute Projection Matrix &proj, //Pointer to returned matrix D3DX_PI * 0.5f, //Vertical Field of view angle in radians (float)Width / (float)Height, //aspect ratio 1.0f, //Near plane distance 1000.0f); //Far plane distance d3dDevice->SetTransform(D3DTS_PROJECTION, &proj); //Set the projection Matrix d3dDevice->SetRenderState(D3DRS_NORMALIZENORMALS, true); //Renormalize Normals d3dDevice->SetRenderState(D3DRS_LIGHTING, true); //Enable Lighting d3dDevice->SetLight(0, &light); //Set the light d3dDevice->LightEnable(0, true); //Enable the light // set blending factors so that alpha // component determines transparency d3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); d3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); return true; } void CleanUpScene(){ //Release memory brainVertexBuffer->Release(); cubetex->Release(); return; } bool RenderScene(float timeDelta) //Renders a single frame { if( d3dDevice ) { //increment/decrement the background alpha for two of the images bgblend += bgblendcount; bg1blend += bg1blendcount; //reverse the counter when if( bgblend >= 1.0f ) bgblendcount = -bgblendcount; if( bgblend <= -0.0f ) bgblendcount = -bgblendcount; bgm.Diffuse.a = bgblend; if( bg1blend >= 0.8f ) bg1blendcount = -bg1blendcount; if( bg1blend <= -0.1f ) bg1blendcount = -bg1blendcount; bgm1.Diffuse.a = bg1blend; //Clear the window to 0x00FFFFFF (white) d3dDevice->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0x00FFFFFF, 1.0f, 0); d3dDevice->BeginScene(); //Start drawing our scene d3dDevice->SetMaterial(&solidwhite); //Set Material d3dDevice->SetTexture(0, bg1); //set Texture d3dDevice->SetStreamSource(0, bgVertexBuffer, 0, sizeof(Vertex)); d3dDevice->SetFVF(VERTEXFVF); d3dDevice->DrawPrimitive(D3DPT_TRIANGLELIST,0,2); //enable blending d3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, true); //use diffuse alpha for alpha component d3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE); d3dDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); d3dDevice->SetMaterial(&bgm); //Set Material d3dDevice->SetTexture(0, bg2); //set Texture d3dDevice->SetStreamSource(0, bgVertexBuffer, 0, sizeof(Vertex)); d3dDevice->SetFVF(VERTEXFVF); d3dDevice->DrawPrimitive(D3DPT_TRIANGLELIST,6,2); d3dDevice->SetMaterial(&bgm1); //Set Material d3dDevice->SetTexture(0, bg3); //set Texture d3dDevice->SetStreamSource(0, bgVertexBuffer, 0, sizeof(Vertex)); d3dDevice->SetFVF(VERTEXFVF); d3dDevice->DrawPrimitive(D3DPT_TRIANGLELIST,12,2); //use alpha in textures alpha channel d3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); d3dDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); d3dDevice->SetTexture(0, cubetex); //set Texture d3dDevice->SetMaterial(&white); //Set Material d3dDevice->SetStreamSource(0, brainVertexBuffer, 0, sizeof(Vertex)); d3dDevice->SetFVF(VERTEXFVF); d3dDevice->DrawPrimitive(D3DPT_TRIANGLELIST,0,2); //disable blending d3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, false); d3dDevice->EndScene(); //Stop drawing our scene d3dDevice->Present(0, 0, 0, 0); //Display our newly created scene } return true; }
Sign in to comment