您的位置:首页 > 其它

D3D中的Alpha颜色混合(1)

2016-06-20 00:00 337 查看
提示:

阅读本文需要一定的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 (R
s, G
s, B
s, A
s).

D3DBLEND_INVSRCCOLOR

Blend factor is (1 - R
s, 1 - G
s, 1 - B
s, 1 - A
s).

D3DBLEND_SRCALPHA

Blend factor is (A
s, A
s, A
s, A
s).

D3DBLEND_INVSRCALPHA

Blend factor is ( 1 - A
s, 1 - A
s, 1 - A
s, 1 - A
s).

D3DBLEND_DESTALPHA

Blend factor is (A
d A
d A
d A
d).

D3DBLEND_INVDESTALPHA

Blend factor is (1 - A
d 1 - A
d 1 - A
d 1 - A
d).

D3DBLEND_DESTCOLOR

Blend factor is (R
d, G
d, B
d, A
d).

D3DBLEND_INVDESTCOLOR

Blend factor is (1 - R
d, 1 - G
d, 1 - B
d, 1 - A
d).

D3DBLEND_SRCALPHASAT

Blend factor is (f, f, f, 1); where f = min(A
s, 1 - A
d).

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 - A
s, 1 - A
s, 1 - A
s, 1 - A
s), and destination blend factor is (A
s, A
s, A
s, A
s); 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
;
}

运行效果:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: