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。
源程序:
#include
<
d3dx9.h
>
#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
;
}