MULTIMEDIA

Programming with DirectX : Additional Texture Mapping - Alpha Mapping

1/29/2011 6:56:44 PM
Alpha mapping is a technique in which you use another image or the alpha channel of the decal image to specify areas of the image that are to be visible, transparent, and semitransparent. Therefore, an alpha map is used to specify the pixel-level transparency of a surface, while the color map specifies the pixel-level color values of a surface.

Creating Alpha Maps

There are two ways to specify per-pixel alpha values. The first is to use the alpha channel of a 32-bit RGBA image. This can be done in any image editor, and the alpha values typically range between 0 for transparent and 255 for visible. Anything between 0 and 255 represents a level of transparency between the two extremes. In fact, you can consider 0 to be 0% and 255 to be 100% visible. An example of an alpha channel from a texture created in Adobe Photoshop is shown in Figure 1. If this alpha map was used, there would be many semi-transparent areas (any gray area in the alpha map), only a few fully visible areas, and even fewer fully transparent areas.

Figure 1. The alpha map channel in Adobe Photoshop.

Another way to create an alpha map is to create a separate texture image. If you use an RGB image, these images are typically grayscale images (black and white). More specifically, it means the red, green, and blue components have equal values (e.g., R:200 G:200 B:200, R:123 G:123 B:123, etc.). When you use a grayscale image, it does not matter which color channel you use for the alpha value since it is the same value across the channels. If the image is not in black and white, it would be difficult to determine how the alpha map would affect the rendered object unless you were looking solely at one component at a time.

Creating an alpha map using a 24-bit RGB image is wasteful memory-wise if you are only using one component because the alpha channel is one byte; having RGBA images just to use that one channel means that the other channels (the RGB channels) are not used, which results in three wasted bytes per pixel. Therefore, it is better to use either a one-component image or the alpha channel of an RGBA image. That way each pixel is 1 byte instead of 3. If semitransparent pixels are not needed, you can replace that 1 byte per pixel with 1 bit, where 0 represents invisible and 1 represents visible (i.e., true or false).

Alpha Mapping Demo

The demo places the alpha values in the alpha channel of the image to avoid having to load a separate image, and the alpha map used is the one from Figure 1. The image in Figure 1 was copied into the alpha channel of the decal image in Photoshop and saved as an RGBA DXT5 texture.

Alpha mapping is done by using alpha blending. In Direct3D 10 this can be set on the application side or through HLSL. Throughout this article we will set states in HLSL, but if you were to do it on the application side, you would create a D3D10_BLEND_DESC object, set each of its member states (variables), and send it to Direct3D by calling OMSetBlendState().

In HLSL a blend state can be created by creating a BlendState descriptor and naming it whatever you like. In the Alpha Mapping demo for this article, the BlendState descriptor is called AlphaBlending. Inside the descriptor we can set a host of related states that include the following.

  • AlphaToCoverageEnable

  • BlendEnable

  • SrcBlend

  • DstBlend

  • BlendOp

  • SrcBlendAlpha

  • DstBlendAlpha

  • BlendOpAlpha

  • RenderTargetWriteMask[n]

Alpha to coverage is a term in computer graphics that deals with multisampling and refers to the way alpha-mapped surfaces are rendered in scenes that have many overlapping polygons. Alpha to coverage can be used even if the application is not using multisampling, in which case it is used to draw overlapping polygons that have transparency without the need for you to render them in a specific order to get the correct results.

The keyword BlendEnable enables or disables the blending feature, while SrcBlend, SrcBlendAlpha, DstBlend, and DstBlendAlpha set the blend options of the source 1 (SRC) and source 2 (DST) colors and alphas. The blend options can be one of the following. Note that the HLSL keywords are the same minus the D3D10_BLEND_ part of each option.

  • D3D10_BLEND_ZERO: The data source is black.

  • D3D10_BLEND_ONE: The data source is white.

  • D3D10_BLEND_SRC_COLOR: The data source is the color from the pixel shader.

  • D3D10_BLEND_INV_SRC_COLOR: The data source is 1 minus the color from the pixel shader.

  • D3D10_BLEND_SRC_ALPHA: The data source is the alpha value from the pixel shader.

  • D3D10_BLEND_INV_SRC_ALPHA: The data source is 1 minus the alpha value from the pixel shader.

  • D3D10_BLEND_DEST_ALPHA: The data source is the alpha value from the rendering target.

  • D3D10_BLEND_INV_DEST_ALPHA: The data source is 1 minus the alpha value from the rendering target.

  • D3D10_BLEND_DEST_COLOR: The data source is the color value that is already stored in the rendering target.

  • D3D10_BLEND_INV_DEST_COLOR: The data source is 1 minus the render target’s color value.

  • D3D10_BLEND_SRC_ALPHA_SAT: The data source is the clamped alpha value from the pixel shader.

  • D3D10_BLEND_BLEND_FACTOR: The data source is the blend factor that was set by the Direct3D function OMSetBlendState() in the second parameter (the first parameter is the blend descriptor, and the last is the blend mask).

  • D3D10_BLEND_INV_BLEND_FACTOR: The data source is 1 minus the blend factor set by the Direct3D function OMSetBlendState().

  • D3D10_BLEND_SRC1_COLOR: The data source is both color values outputted by the pixel shader (used in dual-source color blending).

  • D3D10_BLEND_INV_SRC1_COLOR: The data source is 1 minus the color values from the pixel shader.

  • D3D10_BLEND_SRC1_ALPHA: The same as D3D10_BLEND_SRC1_COLOR but using the alpha values from the pixel shader.

  • D3D10_BLEND_INV_SRC1_ALPHA: One minus D3D10_BLEND_SRC1_ALPHA.

BlendOp and BlendOpAlpha from the blend state are used to set the blend operation. This means the two sources can be added using D3D10_BLEND_OP_ADD, subtracted using D3D10_BLEND_OP_SUBTRACT (or D3D10_BLEND_OP_REV_SUB-TRACT, which subtracts source 2 from source 1), set to the minimum of the two sources using D3D10_BLEND_OP_MIN, or set to the maximum of the two sources using D3D10_BLEND_OP_MAX.

The last member of the blend description, RenderTargetWriteMask[n], is used to set the per-pixel write mask that is used to determine which components of the rendering target can be written to during rendering. A value of 0×0F can be used to specify that all components are to be written to. The value 0×0F is essentially the logical OR result of D3D10_COLOR_WRITE_ENABLE_RED | D3D10_COLOR_WRITE_ENABLE_GREEN | D3D10_COLOR_WRITE_ENABLE_BLUE | D3D10_COLOR_WRITE_ENABLE_ALPHA.

The shader from the Alpha Mapping demo is shown in Listing 1.  This object is set by calling the HLSL function SetBlendState(), which is the same as calling the Direct3D function OMSetBlendState() on the application side. The function takes the blend state description, a blend factor, and a blend mask. The mask is set to 0xFFFFFF to allow for all components. Note that you can set the blend state either in HLSL, which is what we are doing in this demo, or on the application side using OMSetBlendState(). You don’t have to do both.

Listing 1. The Alpha Mapping Demo’s Shader
   /*
Alpha Mapping Demo's HLSL Shader
Ultimate Game Programming with DirectX 2nd Edition
Created by Allen Sherrod
*/


Texture2D decal;

SamplerState DecalSampler
{
Filter = MIN_MAG_MIP_LINEAR;
AddressU = Wrap;
AddressV = Wrap;
};


BlendState AlphaBlending
{
AlphaToCoverageEnable = FALSE;
BlendEnable[0] = TRUE;
SrcBlend = SRC_ALPHA;
DestBlend = INV_SRC_ALPHA;
BlendOp = ADD;
SrcBlendAlpha = ZERO;
DestBlendAlpha = ZERO;
BlendOpAlpha = ADD;
RenderTargetWriteMask[0] = 0×0F;
};

cbuffer cbChangesEveryFrame
{
matrix World;
matrix View;
};

cbuffer cbChangeOnResize
{
matrix Projection;
};

struct VS_INPUT
{
float4 Pos : POSITION;
float2 Tex : TEXCOORD;
};

struct PS_INPUT
{
float4 Pos : SV_POSITION;
float2 Tex : TEXCOORD0;
};


PS_INPUT VS(VS_INPUT input)
{
PS_INPUT output = (PS_INPUT)0;

output.Pos = mul(input.Pos, World);
output.Pos = mul(output.Pos, View);
output.Pos = mul(output.Pos, Projection);

output.Tex = input.Tex;

return output;
}
float4 PS(PS_INPUT input) : SV_Target
{
return decal.Sample(DecalSampler, input.Tex);
}

technique10 AlphaMapping
{
pass P0
{
SetBlendState(AlphaBlending, float4(0.0f, 0.0f, 0.0f, 0.0f),
0xFFFFFFFF);

SetVertexShader(CompileShader(vs_4_0, VS()));
SetGeometryShader(NULL);
SetPixelShader(CompileShader(ps_4_0, PS()));
}
}


In the Alpha Mapping demo the only difference is in the HLSL effect shader. The main source file from the demo is the same except for a few object name changes. The relevant code form the Alpha Mapping demo’s main source file is shown in Listing 2. Figure 2 shows a screenshot from the demo.

Listing 2. The Relevant Code in the Alpha Mapping Demo’s Source File
/*
Alpha Mapping
Ultimate Game Programming with DirectX 2nd Edition
Created by Allen Sherrod
*/


#include<d3d10.h>
#include<d3dx10.h>
#pragma comment(lib, "d3d10.lib")
#pragma comment(lib, "d3dx10.lib")

#define WINDOW_NAME "Alpha Mapping"
#define WINDOW_CLASS "UPGCLASS"
#define WINDOW_WIDTH 800
#define WINDOW_HEIGHT 600

// Global window handles.
HINSTANCE g_hInst = NULL;
HWND g_hwnd = NULL;

// Direct3D 10 objects.
ID3D10Device *g_d3dDevice = NULL;
IDXGISwapChain *g_swapChain = NULL;
ID3D10RenderTargetView *g_renderTargetView = NULL;

struct DX10Vertex
{
D3DXVECTOR3 pos;
D3DXVECTOR2 tex0;
};

ID3D10InputLayout *g_layout = NULL;
ID3D10Buffer *g_squareVB = NULL;
ID3D10ShaderResourceView *g_squareDecal = NULL;

ID3D10Effect *g_shader = NULL;
ID3D10EffectTechnique *g_alphaMapTech = NULL;
ID3D10EffectShaderResourceVariable *g_decalEffectVar = NULL;
ID3D10EffectMatrixVariable *g_worldEffectVar = NULL;
ID3D10EffectMatrixVariable *g_viewEffectVar = NULL;
ID3D10EffectMatrixVariable *g_projEffectVar = NULL;
D3DXMATRIX g_worldMat, g_viewMat, g_projMat;


bool InitializDemo()
{
// Load the shader.

DWORD shaderFlags = D3D10_SHADER_ENABLE_STRICTNESS;

#if defined( DEBUG ) || defined( _DEBUG )
shaderFlags |= D3D10_SHADER_DEBUG;
#endif

HRESULT hr = D3DX10CreateEffectFromFile(
"AlphaMapDemoEffects.fx", NULL, NULL, "fx_4_0", shaderFlags,
0, g_d3dDevice, NULL, NULL, &g_shader, NULL, NULL);

if(FAILED(hr))
return false;

g_alphaMapTech = g_shader->GetTechniqueByName("AlphaMapping");

g_worldEffectVar = g_shader->GetVariableByName(
"World")->AsMatrix();

g_viewEffectVar = g_shader->GetVariableByName(
"View")->AsMatrix();

g_projEffectVar = g_shader->GetVariableByName(
"Projection")->AsMatrix();

g_decalEffectVar = g_shader->GetVariableByName(
"decal")->AsShaderResource();

// Load the texture.
hr = D3DX10CreateShaderResourceViewFromFile(g_d3dDevice,
"AlphaBrick.dds", NULL, NULL, &g_squareDecal, NULL);

if(FAILED(hr))
return false;


// Create the geometry.

D3D10_INPUT_ELEMENT_DESC layout[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0,
D3D10_INPUT_PER_VERTEX_DATA, 0 },
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12,
D3D10_INPUT_PER_VERTEX_DATA, 0 },
};

unsigned int numElements = sizeof(layout) / sizeof(layout[0]);
D3D10_PASS_DESC passDesc;

g_alphaMapTech->GetPassByIndex(0)->GetDesc(&passDesc);

hr = g_d3dDevice->CreateInputLayout(layout, numElements,
passDesc.pIAInputSignature, passDesc.IAInputSignatureSize,
&g_layout);

if(FAILED(hr))
return false;

DX10Vertex vertices[] =
{
{ D3DXVECTOR3( 0.5f, 0.5f, 1.5f), D3DXVECTOR2(1.0f, 0.0f) },
{ D3DXVECTOR3( 0.5f, -0.5f, 1.5f), D3DXVECTOR2(1.0f, 1.0f) },
{ D3DXVECTOR3(-0.5f, -0.5f, 1.5f), D3DXVECTOR2(0.0f, 1.0f) },
{ D3DXVECTOR3(-0.5f, -0.5f, 1.5f), D3DXVECTOR2(0.0f, 1.0f) },
{ D3DXVECTOR3(-0.5f, 0.5f, 1.5f), D3DXVECTOR2(0.0f, 0.0f) },
{ D3DXVECTOR3( 0.5f, 0.5f, 1.5f), D3DXVECTOR2(1.0f, 0.0f) }
};

// Create the vertex buffer.

D3D10_BUFFER_DESC buffDesc;
buffDesc.Usage = D3D10_USAGE_DEFAULT;
buffDesc.ByteWidth = sizeof(DX10Vertex) * 6;
buffDesc.BindFlags = D3D10_BIND_VERTEX_BUFFER;
buffDesc.CPUAccessFlags = 0;
buffDesc.MiscFlags = 0;

D3D10_SUBRESOURCE_DATA resData;
resData.pSysMem = vertices;

hr = g_d3dDevice->CreateBuffer(&buffDesc, &resData,
&g_squareVB);

if(FAILED(hr))
return false;

// Set the shader matrix variables that won't change once here.
D3DXMatrixIdentity(&g_worldMat);
D3DXMatrixIdentity(&g_viewMat);

g_viewEffectVar->SetMatrix((float*)&g_viewMat);
g_projEffectVar->SetMatrix((float*)&g_projMat);

return true;
}
void RenderScene()
{
float col[4] = { 0, 0, 0, 1 };

g_d3dDevice->ClearRenderTargetView(g_renderTargetView, col);

g_worldEffectVar->SetMatrix((float*)&g_worldMat);
g_decalEffectVar->SetResource(g_squareDecal);

unsigned int stride = sizeof(DX10Vertex);
unsigned int offset = 0;

g_d3dDevice->IASetInputLayout(g_layout);

g_d3dDevice->IASetVertexBuffers(0, 1, &g_squareVB, &stride,
&offset);

g_d3dDevice->IASetPrimitiveTopology(
D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

D3D10_TECHNIQUE_DESC techDesc;
g_alphaMapTech->GetDesc(&techDesc);

for(unsigned int i = 0; i < techDesc.Passes; i++)
{
g_alphaMapTech->GetPassByIndex(i)->Apply(0);
g_d3dDevice->Draw(6, 0);
}

g_swapChain->Present(0, 0);
}

void Shutdown()
{
if(g_d3dDevice) g_d3dDevice->ClearState();
if(g_swapChain) g_swapChain->Release();
if(g_renderTargetView) g_renderTargetView->Release();

if(g_shader) g_shader->Release();
if(g_layout) g_layout->Release();
if(g_squareVB) g_squareVB->Release();

if(g_squareDecal)
{
ID3D10Resource *pRes;
g_squareDecal->GetResource(&pRes);

pRes->Release()
g_squareDecal->Release()
}

if(g_d3dDevice) g_d3dDevice->Release()
}


Figure 2. Screenshot from the Alpha Mapping demo.

Other  
  •  Microsoft XNA Game Studio 3.0 : Writing Your First Program (part 2) - Running the Same XNA Game on Different Devices
  •  Microsoft XNA Game Studio 3.0 : Writing Your First Program (part 1)
  •  Programming with DirectX : Shading and Surfaces - Additional Texturing Topics
  •  iPhone 3D Programming : Adding Textures to ModelViewer (part 4) - Enabling Textures with ES2::RenderingEngine
  •  iPhone 3D Programming : Adding Textures to ModelViewer (part 3) - Enabling Textures with ES1::RenderingEngine
  •  iPhone 3D Programming : Adding Textures to ModelViewer (part 2) - Generating Texture Coordinates
  •  iPhone 3D Programming : Adding Textures to ModelViewer (part 1) - Enhancing IResourceManager
  •  Programming with DirectX : Shading and Surfaces - Implementing Texture Mapping (part 2) - Multi Texture Demo
  •  Programming with DirectX : Shading and Surfaces - Implementing Texture Mapping (part 1) - 2D Texture Mapping Demo
  •  Building Out Of Browser Silverlight Applications - Using COM Interoperability and File System Access
  •  Building Out Of Browser Silverlight Applications - Controlling the Application Window
  •  iPhone 3D Programming : Adding Depth and Realism - Loading Geometry from OBJ Files
  •  iPhone 3D Programming : Adding Depth and Realism - Better Wireframes Using Polygon Offset
  •  Programming with DirectX : Textures in Direct3D 10 (part 2)
  •  Programming with DirectX : Textures in Direct3D 10 (part 1) - Textures Coordinates
  •  Programming with DirectX : Shading and Surfaces - Types of Textures
  •  iPhone 3D Programming : Adding Shaders to ModelViewer (part 2)
  •  iPhone 3D Programming : Adding Shaders to ModelViewer (part 1) - New Rendering Engine
  •  iPhone 3D Programming : Adding Depth and Realism - Shaders Demystified
  •  Programming with DirectX : Transformation Demo
  •  
    Most View
    Installing HP-UX : Loading Patches
    LG LM9600 47” Smart TV - A New Direction
    HP Photosmart 7520 e-All-in-One
    Acer CloudMobile - Ambitious Android Phone (Part 2)
    What To Do With An Old Mac (Part 1)
    Deploying to an iPhone, Debugging, and Testing : Distributing Your Application
    How To Choose A Printer (Part 2)
    Nvidia GeForce GTX Titan 6 GB Graphics Card Review (Part 1)
    Appetizing Art - The Complete Picture (Part 2)
    How To Deal With Windows And Network-Related Problems (Part 2)
    Top 10
    The NZXT Kraken X40 Compact Liquid Cooler Review (Part 3)
    The NZXT Kraken X40 Compact Liquid Cooler Review (Part 2)
    T-Mobile’s Samsung Galaxy Note II Review (Part 6)
    T-Mobile’s Samsung Galaxy Note II Review (Part 5)
    T-Mobile’s Samsung Galaxy Note II Review (Part 4)
    T-Mobile’s Samsung Galaxy Note II Review (Part 3)
    T-Mobile’s Samsung Galaxy Note II Review (Part 2)
    T-Mobile’s Samsung Galaxy Note II Review (Part 1)
    Sony Cybershot DSC-TF1 - Affordable Water-Resistant Camera
    Buffalo MiniStation Slim 500GB External Hard Drive