提示:
阅读本文需要一定的3D图形学和DirectX9基础,如果你发现阅读困难,请参阅D3D中的材质和光照处理
。
本文用到的坐标系统变换函数请参阅DirectX
9的坐标系统变换。
渲染管道流水线通常需要将来自顶点的颜色,纹理像素的颜色,光照颜色以及物体表面材质反射光颜色进行混合,生成计算机屏幕的像素颜色。将多种颜色混合在一起,必须考虑各种颜色的成分比例,这个比例由Alpha因子决定。对于游戏开发来说,利用Alpha颜色混合可产生背景透明的渲染效果。
颜色混合原理
一般的,屏幕像素的当前颜色值SrcColor可与目标像素颜色值DestColor进行如下运算,然后将获得的颜色值Color作为该像素的新颜色,以实现像素的目标颜色与源颜色的混合。
Color = SrcColor * SrcBlend + DestColor * DestBlend
这里,SrcBlend和DestBlend为源混合因子和目标混合因子,分别乘以源颜色和目标颜色。SrcColor ,SrcBlend , DestColor
,DestBlend都是一个4维向量,而乘法运算 * 则是一个一个向量点积运算。
假设4维向量SrcColor=(Rs, Gs, Bs, As),SrcBlend=(S1, S2, S3, S4), DestColor=(Rd, Gd,
Bd, Ad),DestBlend(D1, D2, D3, D4),则混合颜色Color可用4维向量表示为:
Color = (Rs * S1 + Rd * D1, Gs * S2 + Gd * D2, Bs * S3 + Bd * D3, As * S4 + Ad *
D4)
利用Direct3D设备接口提供的SetRenderState函数可将所要使用的混合因子设置给渲染管道流水线。此时,函数的第一个参数必须指定为D3DRS_SRCBLEND或D3DRS_DESTBLEND,分别表示设置源混合因子和目标混合因子,如下所示:
// IDirect3DDevice9* _d3d_device;
// set alpha blend for source color
_d3d_device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
// set alpha blend for dest
color
_d3d_device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
D3DBLEND_SRCALPHA和D3DBLEND_INVSRCALPHA均为DirectX预定义的混合因子宏,来看看具体定义:
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_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_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
由于渲染管道流水线的默认Alpha颜色混合功能是禁用的,因此必须调用SetRenderState函数设置D3DRS_ALPHABLENDENABLE为true.
// enable alpha-blended transparency
_d3d_device->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
来看一个具体的例子:
需要在工程中设置链接d3dx9.lib d3d9.lib。
由于文件中用到了GE_APP这个类,它的具体使用说明请参阅
主窗口和DirectInput的封装。
若发现代码中存在错误,敬请指出。
源码下载
来看看AlphaBlend.h的定义:
/*************************************************************************************
[Include File]
PURPOSE:
Define for alpha blend.
*************************************************************************************/
#ifndef ALPHA_BLEND_H
#define ALPHA_BLEND_H
struct CUSTOM_VERTEX
{
float x, y, z;
float nx, ny, nz;
};
#define CUSTOM_VERTEX_FVF (D3DFVF_XYZ | D3DFVF_NORMAL)
class ALPHA_BLEND
{
private:
IDirect3D9* _d3d;
IDirect3DDevice9* _d3d_device;
IDirect3DVertexBuffer9* _vertex_buffer1;
IDirect3DVertexBuffer9* _vertex_buffer2;
public:
ALPHA_BLEND();
~ALPHA_BLEND();
bool Create_D3D_Device(HWND hwnd, bool full_screen = true);
bool Init_Vertex_Buffer1();
bool Init_Vertex_Buffer2();
void Compute_Triangle_Normal(D3DXVECTOR3& v1, D3DXVECTOR3& v2, D3DXVECTOR3& v3, D3DVECTOR& normal);
void Set_Camera();
void Set_Point_Light();
void Set_Object_Material(D3DCOLORVALUE& dif, D3DCOLORVALUE& amb, D3DCOLORVALUE& spe,
D3DCOLORVALUE& emi, float power);
void Render();
void Release_COM_Object();
};
#endif
以上的头文件定义了两个三棱锥的顶点格式和顶点结构体,函数
Init_Vertex_Buffer1个Init_Vertex_Buffer2分别用来装入这两个三棱锥的顶点数据,Render函数则设置了渲染管道流水线的
Alpha颜色混合状态值。
再来看看AlphaBlend.cpp的定义:
/*************************************************************************************
[Implement File]
PURPOSE:
Define for alpha blend.
*************************************************************************************/
#include "GE_COMMON.h"
#include "AlphaBlend.h"
//------------------------------------------------------------------------------------
// Constructor, initialize all pointer with NULL.
//------------------------------------------------------------------------------------
ALPHA_BLEND::ALPHA_BLEND()
{
_d3d = NULL;
_d3d_device = NULL;
_vertex_buffer1 = NULL;
_vertex_buffer2 = NULL;
}
//------------------------------------------------------------------------------------
// Destructor, release all COM object.
//------------------------------------------------------------------------------------
ALPHA_BLEND::~ALPHA_BLEND()
{
Release_COM_Object();
}
//------------------------------------------------------------------------------------
// Create direct3D interface and direct3D device.
//------------------------------------------------------------------------------------
bool ALPHA_BLEND::Create_D3D_Device(HWND hwnd, bool full_screen)
{
// Create a IDirect3D9 object and returns an interace to it.
_d3d = Direct3DCreate9(D3D_SDK_VERSION);
if(_d3d == NULL)
return false;
// retrieve adapter capability
D3DCAPS9 d3d_caps;
_d3d->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &d3d_caps);
bool hardware_process_enable = (d3d_caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT ? true : false);
// Retrieves the current display mode of the adapter.
D3DDISPLAYMODE display_mode;
if(FAILED(_d3d->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &display_mode)))
return false;
// set present parameter for direct3D device
D3DPRESENT_PARAMETERS present_param;
ZeroMemory(&present_param, sizeof(present_param));
present_param.BackBufferWidth = WINDOW_WIDTH;
present_param.BackBufferHeight = WINDOW_HEIGHT;
present_param.BackBufferFormat = display_mode.Format;
present_param.BackBufferCount = 1;
present_param.hDeviceWindow = hwnd;
present_param.Windowed = !full_screen;
present_param.SwapEffect = D3DSWAPEFFECT_FLIP;
present_param.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
// Creates a device to represent the display adapter.
DWORD behavior_flags;
behavior_flags = hardware_process_enable ?
D3DCREATE_HARDWARE_VERTEXPROCESSING : D3DCREATE_SOFTWARE_VERTEXPROCESSING;
if(FAILED(_d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd, behavior_flags,
&present_param, &_d3d_device)))
{
return false;
}
// create successfully
return true;
}
//------------------------------------------------------------------------------------
// Initialize vertex buffer for cone.
//------------------------------------------------------------------------------------
bool ALPHA_BLEND::Init_Vertex_Buffer1()
{
CUSTOM_VERTEX custom_vertex[12];
D3DXVECTOR3 v[] =
{
D3DXVECTOR3(5.0f, 6.0f, 5.0f), // left triangle
D3DXVECTOR3(6.0f, 0.0f, 3.0f),
D3DXVECTOR3(1.0f, 0.0f, 7.0f),
D3DXVECTOR3(5.0f, 6.0f, 5.0f), // right triangle
D3DXVECTOR3(10.0f, 0.0f, 8.0f),
D3DXVECTOR3(6.0f, 0.0f, 3.0f),
D3DXVECTOR3(5.0f, 6.0f, 5.0f), // back triangle
D3DXVECTOR3(1.0f, 0.0f, 7.0f),
D3DXVECTOR3(10.0f, 0.0f, 8.0f),
D3DXVECTOR3(1.0f, 0.0f, 7.0f), // bottom triangle
D3DXVECTOR3(6.0f, 0.0f, 3.0f),
D3DXVECTOR3(10.0f, 0.0f, 8.0f)
};
D3DVECTOR normal;
// compute all triangle normal
for(int i = 0; i < 12; i += 3)
{
// compute current triangle's normal
Compute_Triangle_Normal(v[i], v[i+1], v[i+2], normal);
// assign current vertex coordinate and current triangle normal to custom vertex array
for(int j = 0; j < 3; j++)
{
int k = i + j;
custom_vertex[k].x = v[k].x;
custom_vertex[k].y = v[k].y;
custom_vertex[k].z = v[k].z;
custom_vertex[k].nx = normal.x;
custom_vertex[k].ny = normal.y;
custom_vertex[k].nz = normal.z;
}
}
BYTE* vertex_data;
// create vertex buffer
if(FAILED(_d3d_device->CreateVertexBuffer(12 * sizeof(CUSTOM_VERTEX), 0, CUSTOM_VERTEX_FVF,
D3DPOOL_DEFAULT, &_vertex_buffer1, NULL)))
{
return false;
}
// get data pointer to vertex buffer
if(FAILED(_vertex_buffer1->Lock(0, 0, (void **) &vertex_data, 0)))
return false;
// copy custom vertex data into vertex buffer
memcpy(vertex_data, custom_vertex, sizeof(custom_vertex));
// unlock vertex buffer
_vertex_buffer1->Unlock();
return true;
}
//------------------------------------------------------------------------------------
// Initialize vertex buffer for cone.
//------------------------------------------------------------------------------------
bool ALPHA_BLEND::Init_Vertex_Buffer2()
{
CUSTOM_VERTEX custom_vertex[12];
float add = 1.3f;
D3DXVECTOR3 v[] =
{
D3DXVECTOR3(5.0f + add, 6.0f + add, 5.0f + add), // left triangle
D3DXVECTOR3(6.0f + add, 0.0f + add, 3.0f + add),
D3DXVECTOR3(1.0f + add, 0.0f + add, 7.0f + add),
D3DXVECTOR3(5.0f + add, 6.0f + add, 5.0f + add), // right triangle
D3DXVECTOR3(10.0f + add, 0.0f + add, 8.0f + add),
D3DXVECTOR3(6.0f + add, 0.0f + add, 3.0f + add),
D3DXVECTOR3(5.0f + add, 6.0f + add, 5.0f + add), // back triangle
D3DXVECTOR3(1.0f + add, 0.0f + add, 7.0f + add),
D3DXVECTOR3(10.0f + add, 0.0f + add, 8.0f + add),
D3DXVECTOR3(1.0f + add, 0.0f + add, 7.0f + add), // bottom triangle
D3DXVECTOR3(6.0f + add, 0.0f + add, 3.0f + add),
D3DXVECTOR3(10.0f + add, 0.0f + add, 8.0f + add)
};
D3DVECTOR normal;
// compute all triangle normal
for(int i = 0; i < 12; i += 3)
{
// compute current triangle's normal
Compute_Triangle_Normal(v[i], v[i+1], v[i+2], normal);
// assign current vertex coordinate and current triangle normal to custom vertex array
for(int j = 0; j < 3; j++)
{
int k = i + j;
custom_vertex[k].x = v[k].x;
custom_vertex[k].y = v[k].y;
custom_vertex[k].z = v[k].z;
custom_vertex[k].nx = normal.x;
custom_vertex[k].ny = normal.y;
custom_vertex[k].nz = normal.z;
}
}
BYTE* vertex_data;
// create vertex buffer
if(FAILED(_d3d_device->CreateVertexBuffer(12 * sizeof(CUSTOM_VERTEX), 0, CUSTOM_VERTEX_FVF,
D3DPOOL_DEFAULT, &_vertex_buffer2, NULL)))
{
return false;
}
// get data pointer to vertex buffer
if(FAILED(_vertex_buffer2->Lock(0, 0, (void **) &vertex_data, 0)))
return false;
// copy custom vertex data into vertex buffer
memcpy(vertex_data, custom_vertex, sizeof(custom_vertex));
// unlock vertex buffer
_vertex_buffer2->Unlock();
return true;
}
//------------------------------------------------------------------------------------
// Set camera position.
//------------------------------------------------------------------------------------
void ALPHA_BLEND::Set_Camera()
{
D3DXVECTOR3 eye(-6.0, 1.5, 10.0);
D3DXVECTOR3 at(6.0, 2.0, 3.0);
D3DXVECTOR3 up(0.0, 1.0, 0.0);
D3DXMATRIX view_matrix;
// Builds a left-handed, look-at matrix.
D3DXMatrixLookAtLH(&view_matrix, &eye, &at, &up);
// Sets d3d device view transformation state.
_d3d_device->SetTransform(D3DTS_VIEW, &view_matrix);
D3DXMATRIX proj_matrix;
// Builds a left-handed perspective projection matrix based on a field of view.
D3DXMatrixPerspectiveFovLH(&proj_matrix, D3DX_PI/2, WINDOW_WIDTH / WINDOW_HEIGHT, 1.0, 1000.0);
// Sets d3d device projection transformation state.
_d3d_device->SetTransform(D3DTS_PROJECTION, &proj_matrix);
// enable automatic normalization of vertex normals
_d3d_device->SetRenderState(D3DRS_NORMALIZENORMALS, true);
}
//------------------------------------------------------------------------------------
// Set point light.
//------------------------------------------------------------------------------------
void ALPHA_BLEND::Set_Point_Light()
{
D3DLIGHT9 light;
// clear memory with 0
ZeroMemory(&light, sizeof(D3DLIGHT9));
light.Type = D3DLIGHT_POINT;
light.Diffuse.r = 1.0;
light.Diffuse.g = 0.0;
light.Diffuse.b = 0.0;
light.Ambient.r = 0.0;
light.Ambient.g = 1.0;
light.Ambient.b = 0.0;
light.Specular.r = 0.0;
light.Specular.g = 0.0;
light.Specular.b = 0.0;
light.Position.x = 5.0;
light.Position.y = 6.0;
light.Position.z = -20.0;
light.Attenuation0 = 1.0;
light.Attenuation1 = 0.0;
light.Attenuation2 = 0.0;
light.Range = 1000.0;
// Assigns point lighting properties for this device
_d3d_device->SetLight(0, &light);
// enable point light
_d3d_device->LightEnable(0, TRUE);
// enable light
_d3d_device->SetRenderState(D3DRS_LIGHTING, TRUE);
// add ambient light
_d3d_device->SetRenderState(D3DRS_AMBIENT, D3DCOLOR_XRGB(50, 50, 50));
}
//------------------------------------------------------------------------------------
// Sets the material properties for the device.
//------------------------------------------------------------------------------------
void ALPHA_BLEND::Set_Object_Material(D3DCOLORVALUE& dif, D3DCOLORVALUE& amb, D3DCOLORVALUE& spe,
D3DCOLORVALUE& emi, float power)
{
D3DMATERIAL9 material;
material.Diffuse = dif;
material.Ambient = amb;
material.Specular = spe;
material.Emissive = emi;
material.Power = power;
// Sets the material properties for the device.
_d3d_device->SetMaterial(&material);
}
//------------------------------------------------------------------------------------
// Compute triangle normal.
//------------------------------------------------------------------------------------
void ALPHA_BLEND::Compute_Triangle_Normal(D3DXVECTOR3& v1, D3DXVECTOR3& v2, D3DXVECTOR3& v3, D3DVECTOR& normal)
{
D3DXVECTOR3 vec1 = v1 - v2;
D3DXVECTOR3 vec2 = v1 - v3;
D3DXVECTOR3 normal_vec;
D3DXVec3Cross(&normal_vec, &vec1, &vec2);
D3DXVec3Normalize(&normal_vec, &normal_vec);
normal = (D3DVECTOR) normal_vec;
}
//------------------------------------------------------------------------------------
// Draw cones.
//------------------------------------------------------------------------------------
void ALPHA_BLEND::Render()
{
if(_d3d_device == NULL)
return;
// clear surface with black
_d3d_device->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0, 0);
// begin scene
_d3d_device->BeginScene();
// 1) draw cone 1
// Binds a vertex buffer to a device data stream.
_d3d_device->SetStreamSource(0, _vertex_buffer1, 0, sizeof(CUSTOM_VERTEX));
// Sets the current vertex stream declaration.
_d3d_device->SetFVF(CUSTOM_VERTEX_FVF);
// Renders a sequence of nonindexed, geometric primitives of the specified type from the current
// set of data input streams.
_d3d_device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 4);
// enable alpha-blended transparency
_d3d_device->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
// set alpha blend for source cone
_d3d_device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
// set alpha blend for dest cone
_d3d_device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
// 2) draw cone 2
// Binds a vertex buffer to a device data stream.
_d3d_device->SetStreamSource(0, _vertex_buffer2, 0, sizeof(CUSTOM_VERTEX));
// Sets the current vertex stream declaration.
_d3d_device->SetFVF(CUSTOM_VERTEX_FVF);
// draw square
_d3d_device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 4);
// disable alpha blend for d3d device
_d3d_device->SetRenderState(D3DRS_ALPHABLENDENABLE, false);
// end scene
_d3d_device->EndScene();
// Presents the contents of the next buffer in the sequence of back buffers owned by the device.
_d3d_device->Present(NULL, NULL, NULL, NULL);
}
//------------------------------------------------------------------------------------
// Release all COM object.
//------------------------------------------------------------------------------------
void ALPHA_BLEND::Release_COM_Object()
{
Safe_Release(_vertex_buffer1);
Safe_Release(_vertex_buffer2);
Safe_Release(_d3d_device);
Safe_Release(_d3d);
}
main.cpp的实现很简单,它首先调用类ALPHA_BLEND的函数创建两个三棱锥的顶点缓冲区,然后进行取景并设置材质光源,最后调用Render函数进行混色渲染。
/*************************************************************************************
[Implement File]
PURPOSE:
Test for alpha blending.
*************************************************************************************/
#define DIRECTINPUT_VERSION 0x0800
#include "GE_COMMON.h"
#include "GE_APP.h"
#include "AlphaBlend.h"
#pragma warning(disable : 4305 4996)
int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR cmd_line, int cmd_show)
{
GE_APP ge_app;
ALPHA_BLEND alpha_blend;
MSG msg = {0};
// create window
if(! ge_app.Create_Window("Alpha blending test", instance, cmd_show))
return false;
HWND hwnd = ge_app.Get_Window_Handle();
SetWindowPos(hwnd, 0, 0,0,0,0, SWP_NOSIZE);
SetCursorPos(0, 0);
// Create direct3D interface and direct3D device.
if(! alpha_blend.Create_D3D_Device(hwnd, false))
return false;
// Initialize cone 1 vertex buffer with curstom vertex structure.
if(! alpha_blend.Init_Vertex_Buffer1())
return false;
// Initialize cone 2 vertex buffer with curstom vertex structure.
if(! alpha_blend.Init_Vertex_Buffer2())
return false;
// Set camera position.
alpha_blend.Set_Camera();
D3DCOLORVALUE dif = {1.0f, 1.0f, 1.0f, 0.6f};
D3DCOLORVALUE amb = {1.0f, 1.0f, 1.0f, 0.0f};
D3DCOLORVALUE spe = {0.0f, 0.0f, 0.0f, 0.0f};
D3DCOLORVALUE emi = {0.0f, 0.0f, 0.0f, 0.0f};
// Sets the material properties for the device.
alpha_blend.Set_Object_Material(dif, amb, spe, emi, 0);
// Set point light.
alpha_blend.Set_Point_Light();
// Draw all cones
alpha_blend.Render();
while(msg.message != WM_QUIT)
{
if(PeekMessage(&msg, NULL, 0,0 , PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
UnregisterClass(WINDOW_CLASS_NAME, instance);
return true;
}
运行效果:
阅读下篇:
D3D中的Alpha颜色混合(2)