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.
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() }
|
|