alpha混合原理
在前面介绍的示例程序中,绘制图形的颜色总是替换当前颜色缓冲区中存在的颜色,这样后面的物体总是覆盖在原有的物体上。但是当想要绘制类似于玻璃、水等具有透明效果的物体时,这种方法显然满足不了要求。通过定义一个表示物体半透明度的alpha值和一个半透明计算公式,可以将要绘制的物体颜色与颜色缓冲区中存在的颜色相混合,从而绘制出具有半透明效果的物体。Direct3D计算alpha颜色混合的方法如下:
color = (RGBsrc * Ksrc) OP (RGBdst * Kdst)
其中color表示alpha混合后的颜色值,RGBsrc表示源颜色值,即将要绘制的图元的颜色值;Ksrc表示源混合系数,通常赋值为表示半透明程度的alpha值,也可以是属于枚举类型D3DBLEND的任意值,用来和RGBsrc相乘。RGBdst表示目标颜色值,即当前颜色缓冲区中的颜色值,Kdst表示目标混合系数,可以是属于枚举D3DBLEND的任意值,用来和RGBdst相乘。OP表示源计算结果与颜色缓冲区计算结果的混合方法,默认状态下OP为D3DBLEND_ADD,即源计算结果与颜色缓冲区计算结果相加。
图形显示中,对alpha混合最普遍的用法是:把Ksrc赋值为D3DBLEND_SRCALPHA,即当前绘制像素的alpha值;把Kdst赋值为D3DBLEND_INVSRCALPHA,即1减去当前绘制像素的alpha值;把OP赋值为D3DBLEND_ADD,使源计算结果和颜色缓冲区计算结果相加,这样一来,alpha混合颜色的公式变为:
color = (RGBsrc * Ksrc) + (RGBdst * Kdst)
上面的设置可以较好地模拟大多数半透明物体的效果。
启用alpha混合
想要绘制半透明物体,首先需要激活Direct3D的alpha混合运算,调用Direct3D渲染状态设置函数IDirect3DDevice9:::SetRenderState(),将第一个参数设置为D3DRS_ALPHABLENDENABLE,第二个参数设置为TRUE,可以激活alpha混合,代码如下:
g_device->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
设置alpha混合系数
在上面介绍的alpha混合原理中提到的源混合系数和目标混合系数,也是通过Direct3D渲染状态设置函数IDirect3DDevice9::SetRenderState()设置的。若将第一个参数设置为D3DRS_SRCBLEND,则用于设置源混合系数,若将第一个参数设置为D3DRS_DESTBLEND,则用于设置目标混合系数,第二个参数可以设置为D3DBLEND枚举常量,各具体枚举常量的含义如下:
Defines the supported blend mode.
typedef enum D3DBLEND
{
D3DBLEND_ZERO = 1,
D3DBLEND_ONE = 2,
D3DBLEND_SRCCOLOR = 3,
D3DBLEND_INVSRCCOLOR = 4,
D3DBLEND_SRCALPHA = 5,
D3DBLEND_INVSRCALPHA = 6,
D3DBLEND_DESTALPHA = 7,
D3DBLEND_INVDESTALPHA = 8,
D3DBLEND_DESTCOLOR = 9,
D3DBLEND_INVDESTCOLOR = 10,
D3DBLEND_SRCALPHASAT = 11,
D3DBLEND_BOTHSRCALPHA = 12,
D3DBLEND_BOTHINVSRCALPHA = 13,
D3DBLEND_BLENDFACTOR = 14,
D3DBLEND_INVBLENDFACTOR = 15,
D3DBLEND_SRCCOLOR2 = 16,
D3DBLEND_INVSRCCOLOR2 = 17,
D3DBLEND_FORCE_DWORD = 0x7fffffff,
} D3DBLEND, *LPD3DBLEND;
Constants
-
D3DBLEND_ZERO
- Blend factor is (0, 0, 0, 0). D3DBLEND_ONE
- Blend factor is (1, 1, 1, 1). D3DBLEND_SRCCOLOR
- Blend factor is (Rs, Gs, Bs, As). D3DBLEND_INVSRCCOLOR
- Blend factor is (1 - Rs, 1 - Gs, 1 - Bs, 1 - As). D3DBLEND_SRCALPHA
- Blend factor is (As, As, As, As). D3DBLEND_INVSRCALPHA
- Blend factor is ( 1 - As, 1 - As, 1 - As, 1 - As). D3DBLEND_DESTALPHA
- Blend factor is (Ad Ad Ad Ad). D3DBLEND_INVDESTALPHA
- Blend factor is (1 - Ad 1 - Ad 1 - Ad 1 - Ad). D3DBLEND_DESTCOLOR
- Blend factor is (Rd, Gd, Bd, Ad). D3DBLEND_INVDESTCOLOR
- Blend factor is (1 - Rd, 1 - Gd, 1 - Bd, 1 - Ad). D3DBLEND_SRCALPHASAT
- Blend factor is (f, f, f, 1); where f = min(As, 1 - Ad). D3DBLEND_BOTHSRCALPHA
- Obsolete. Starting with DirectX 6, you can achieve the same effect by setting the source and destination blend factors to D3DBLEND_SRCALPHA and D3DBLEND_INVSRCALPHA in separate calls. D3DBLEND_BOTHINVSRCALPHA
- Source blend factor is (1 - As, 1 - As, 1 - As, 1 - As), and destination blend factor is (As, As, As, As); the destination blend selection is overridden. This blend mode is supported only for the D3DRS_SRCBLEND render state. D3DBLEND_BLENDFACTOR
- Constant color blending factor used by the frame-buffer blender. This blend mode is supported only if D3DPBLENDCAPS_BLENDFACTOR is set in the SrcBlendCaps or DestBlendCaps members of D3DCAPS9. D3DBLEND_INVBLENDFACTOR
- Inverted constant color-blending factor used by the frame-buffer blender. This blend mode is supported only if the D3DPBLENDCAPS_BLENDFACTOR bit is set in the SrcBlendCaps or DestBlendCapsmembers of D3DCAPS9. D3DBLEND_SRCCOLOR2
-
Blend factor is (PSOutColor[1]r, PSOutColor[1]g, PSOutColor[1]b, not used). See Render Target Blending.
Differences between Direct3D 9 and Direct3D 9Ex: This flag is available in Direct3D 9Ex only.
D3DBLEND_INVSRCCOLOR2
-
Blend factor is (1 - PSOutColor[1]r, 1 - PSOutColor[1]g, 1 - PSOutColor[1]b, not used)). See Render Target Blending.
Differences between Direct3D 9 and Direct3D 9Ex: This flag is available in Direct3D 9Ex only.
D3DBLEND_FORCE_DWORD
- Forces this enumeration to compile to 32 bits in size. Without this value, some compilers would allow this enumeration to compile to a size other than 32 bits. This value is not used.
Remarks
In the preceding member descriptions, the RGBA values of the source and destination are indicated by the s andd subscripts.
The values in this enumerated type are used by the following render states:
- D3DRS_DESTBLEND
- D3DRS_SRCBLEND
- D3DRS_DESTBLENDALPHA
- D3DRS_SRCBLENDALPHA
See D3DRENDERSTATETYPE
Render Target Blending
Direct3D 9Ex has improved text rendering capabilities. Rendering clear-type fonts would normally require two passes. To eliminate the second pass, a pixel shader can be used to output two colors, which we can call PSOutColor[0] and PSOutColor[1]. The first color would contain the standard 3 color components (RGB). The second color would contain 3 alpha components (one for each component of the first color).
These new blending modes are only used for text rendering on the first render target.
设置alpha混合系数的代码示例如下:
g_device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
g_device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
设置alpha混合方法
alpha混合方法指定源颜色和目标颜色的混合方法,通过Direct3D渲染状态设置函数IDirect3DDevice9::SetRenderState()设置,其中第一个参数设置为 D3DRS_BLENDOP,第二个参数设置为D3DBLENDOP枚举常量,各常量的含义如下:
Defines the supported blend operations. See Remarks for definitions of terms.
typedef enum D3DBLENDOP
{
D3DBLENDOP_ADD = 1,
D3DBLENDOP_SUBTRACT = 2,
D3DBLENDOP_REVSUBTRACT = 3,
D3DBLENDOP_MIN = 4,
D3DBLENDOP_MAX = 5,
D3DBLENDOP_FORCE_DWORD = 0x7fffffff,
} D3DBLENDOP, *LPD3DBLENDOP;
Constants
-
D3DBLENDOP_ADD
- The result is the destination added to the source. Result = Source + Destination D3DBLENDOP_SUBTRACT
- The result is the destination subtracted from to the source. Result = Source - Destination D3DBLENDOP_REVSUBTRACT
- The result is the source subtracted from the destination. Result = Destination - Source D3DBLENDOP_MIN
- The result is the minimum of the source and destination. Result = MIN(Source, Destination) D3DBLENDOP_MAX
- The result is the maximum of the source and destination. Result = MAX(Source, Destination) D3DBLENDOP_FORCE_DWORD
- Forces this enumeration to compile to 32 bits in size. Without this value, some compilers would allow this enumeration to compile to a size other than 32 bits. This value is not used.
Remarks
Source, Destination, and Result are defined as:
Term | Type | Description |
---|---|---|
Source | Input | Color of the source pixel before the operation. |
Destination | Input | Color of the pixel in the destination buffer before the operation. |
Result | Output | Returned value that is the blended color resulting from the operation. |
This enumerated type defines values used by the following render states:
- D3DRS_BLENDOP
- D3DRS_BLENDOPALPHA
示例程序:
该示例程序模拟了直升飞机螺旋桨的半透明效果。在程序的初始化阶段,载入Heli.x文件,它是一个包含直升飞机的三维模型文件,其中螺旋桨的材质漫反射属性为(R, G, B, A) = (0.183700; 0.183700; 0.183700; 0.500000; ),可以用文本方式打开Heli.x查看它的材质属性,Heli.x是一个文本格式的.x文件。
示例程序中没有设置alpha混合方法,所以应用程序将采用默认的alpha混合方法D3DBLEND_ADD,即将源计算结果与颜色缓冲区计算结果相加。由于直升机螺旋桨的材质的alpha值为0.5f,因此它的最终颜色就是50%的玻璃颜色加上50%的背景颜色。
按下数字键"1",激活alpha混合。
按下数字键"0",禁用alpha混合。
设置alpha混合方法为D3DBLENDOP_SUBTRACT的效果图,启用alpha混合时。
g_device->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_SUBTRACT);
设置以下代码时的效果图,可以看到直升机变亮了,启用alpha混合:
g_device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
g_device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_DESTCOLOR);
设置以下代码时的效果图,可以看到直升机变暗了,启用alpha混合:
g_device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
g_device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVDESTCOLOR);
可以根据需要自己修改设置,以实现不同的颜色混合效果。
由于alpha混合是当前绘制的像素颜色与颜色缓冲区中存在的颜色的混合运算,因此,在绘制半透明物体前,必须保证位于半透明物体后的物体先于半透明物体绘制,也就是说,先绘制不透明物体,再绘制半透明物体。
有些计算机硬件由于功能的限制,可能不支持某些混合方法,这时Direct3D会自动使用alpha混合方法D3DBLENDOP_ADD。
源程序:
#pragma warning(disable : 4127 )
#define CLASS_NAME "GameApp"
#define release_com(p) do { if(p) { (p)->Release(); (p) = NULL; } } while(0)
IDirect3D9 * g_d3d;
IDirect3DDevice9 * g_device;
ID3DXMesh * g_mesh;
D3DMATERIAL9 * g_mesh_materials;
IDirect3DTexture9 ** g_mesh_textures;
DWORD g_num_materials;
void setup_world_matrix()
{
D3DXMATRIX mat_world;
D3DXMatrixRotationY( & mat_world, timeGetTime() / 1000.0f );
g_device -> SetTransform(D3DTS_WORLD, & mat_world);
}
void setup_view_proj_matrices()
{
// setup view matrix
D3DXVECTOR3 eye( 0.0f , 15.0f , - 20.0f );
D3DXVECTOR3 at( 0.0f , 0.0f , 0.0f );
D3DXVECTOR3 up( 0.0f , 1.0f , 0.0f );
D3DXMATRIX mat_view;
D3DXMatrixLookAtLH( & mat_view, & eye, & at, & up);
g_device -> SetTransform(D3DTS_VIEW, & mat_view);
// setup projection matrix
D3DXMATRIX mat_proj;
D3DXMatrixPerspectiveFovLH( & mat_proj, D3DX_PI / 4 , 1.0f , 1.0f , 500.0f );
g_device -> SetTransform(D3DTS_PROJECTION, & mat_proj);
}
bool init_geometry()
{
ID3DXBuffer * material_buffer;
if (FAILED(D3DXLoadMeshFromX( " heli.x " , D3DXMESH_SYSTEMMEM, g_device, NULL, & material_buffer, NULL,
& g_num_materials, & g_mesh)))
{
MessageBox(NULL, " Could not find heli.x " , " ERROR " , MB_OK);
return false ;
}
D3DXMATERIAL * xmaterials = (D3DXMATERIAL * ) material_buffer -> GetBufferPointer();
g_mesh_materials = new D3DMATERIAL9[g_num_materials];
g_mesh_textures = new IDirect3DTexture9 * [g_num_materials];
for (DWORD i = 0 ; i < g_num_materials; i ++ )
{
g_mesh_materials[i] = xmaterials[i].MatD3D;
// set ambient reflected coefficient, because .x file do not set it.
g_mesh_materials[i].Ambient = g_mesh_materials[i].Diffuse;
g_mesh_textures[i] = NULL;
if (xmaterials[i].pTextureFilename != NULL && strlen(xmaterials[i].pTextureFilename) > 0 )
D3DXCreateTextureFromFile(g_device, xmaterials[i].pTextureFilename, & g_mesh_textures[i]);
}
material_buffer -> Release();
return true ;
}
bool init_d3d(HWND hwnd)
{
g_d3d = Direct3DCreate9(D3D_SDK_VERSION);
if (g_d3d == NULL)
return false ;
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory( & d3dpp, sizeof (d3dpp));
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
d3dpp.EnableAutoDepthStencil = TRUE;
d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
if (FAILED(g_d3d -> CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING,
& d3dpp, & g_device)))
{
return false ;
}
if ( ! init_geometry())
return false ;
setup_view_proj_matrices();
g_device -> SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
g_device -> SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
g_device -> SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
// g_device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
// g_device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVDESTCOLOR);
// g_device->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_SUBTRACT);
g_device -> SetRenderState(D3DRS_ZENABLE, TRUE);
g_device -> SetRenderState(D3DRS_ZFUNC, D3DCMP_LESS);
g_device -> SetRenderState(D3DRS_ZWRITEENABLE, TRUE);
g_device -> SetRenderState(D3DRS_AMBIENT, 0xFFFFBB55 );
return true ;
}
void cleanup()
{
delete[] g_mesh_materials;
if (g_mesh_textures)
{
for (DWORD i = 0 ; i < g_num_materials; i ++ )
release_com(g_mesh_textures[i]);
delete[] g_mesh_textures;
}
release_com(g_mesh);
release_com(g_device);
release_com(g_d3d);
}
void render()
{
g_device -> Clear( 0 , NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB( 5 , 5 , 5 ), 1.0f , 0 );
g_device -> BeginScene();
setup_world_matrix();
// render opaque object first
for (DWORD i = 0 ; i < g_num_materials; i ++ )
{
if (g_mesh_materials[i].Diffuse.a == 1.0f )
{
g_device -> SetMaterial( & g_mesh_materials[i]);
g_device -> SetTexture( 0 , g_mesh_textures[i]);
g_mesh -> DrawSubset(i);
}
}
// render transparent object second
for (DWORD i = 0 ; i < g_num_materials; i ++ )
{
if (g_mesh_materials[i].Diffuse.a != 1.0f )
{
g_device -> SetMaterial( & g_mesh_materials[i]);
g_device -> SetTexture( 0 , g_mesh_textures[i]);
g_mesh -> DrawSubset(i);
}
}
g_device -> EndScene();
g_device -> Present(NULL, NULL, NULL, NULL);
}
LRESULT WINAPI WinProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_KEYDOWN:
switch (wParam)
{
case 48 : // press key "0", disable alpha blend.
g_device -> SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
break ;
case 49 : // press key "1", enable alpha blend.
g_device -> SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
break ;
case VK_ESCAPE:
DestroyWindow(hwnd);
break ;
}
break ;
case WM_DESTROY:
PostQuitMessage( 0 );
return 0 ;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE inst, HINSTANCE, LPSTR, INT)
{
WNDCLASSEX wc;
wc.cbSize = sizeof (WNDCLASSEX);
wc.style = CS_CLASSDC;
wc.lpfnWndProc = WinProc;
wc.cbClsExtra = 0 ;
wc.cbWndExtra = 0 ;
wc.hInstance = inst;
wc.hIcon = NULL;
wc.hCursor = NULL;
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpszClassName = CLASS_NAME;
wc.hIconSm = NULL;
if ( ! RegisterClassEx( & wc))
return - 1 ;
HWND hwnd = CreateWindow(CLASS_NAME, " Direct3D App " , WS_OVERLAPPEDWINDOW, 200 , 100 , 640 , 480 ,
NULL, NULL, wc.hInstance, NULL);
if (hwnd == NULL)
return - 1 ;
if (init_d3d(hwnd))
{
ShowWindow(hwnd, SW_SHOWDEFAULT);
UpdateWindow(hwnd);
MSG msg;
ZeroMemory( & msg, sizeof (msg));
while (msg.message != WM_QUIT)
{
if (PeekMessage( & msg, NULL, 0 , 0 , PM_REMOVE))
{
TranslateMessage( & msg);
DispatchMessage( & msg);
}
render();
}
}
cleanup();
UnregisterClass(CLASS_NAME, wc.hInstance);
return 0 ;
}