前面的一些例子中,已经遇到了alpha值的概念,如结构体D3DCOLORVALUE中的成员变量a,但它并没有被使用,因为还没有激活alpha混合(alpha
blending),并且alpha总是被赋值为1.0f(默认渲染状态下alpha为1.0f,表示完全不透明)。
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
DestBlendCaps members 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 and d
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;
/*
D3DXLoadMeshFromXA(
LPCSTR pFilename,
DWORD Options,
LPDIRECT3DDEVICE9 pD3DDevice,
LPD3DXBUFFER *ppAdjacency,
LPD3DXBUFFER *ppMaterials,
LPD3DXBUFFER *ppEffectInstances,
DWORD *pNumMaterials,
LPD3DXMESH *ppMesh);
*/
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;
}
下载示例工程