您的位置:首页 > 编程语言

【阅读笔记之九】《DIRECTX.9.0.3D游戏开发编程基础》:Direct3D中的融合技术

2012-11-01 15:01 555 查看
Direct3D中的融合技术

融合(blending)技术,将当前要进行光栅化的像素的颜色和先前已光栅化的并处于同一位置的像素的颜色进行合成,即将正在处理的图元颜色值与存储在后台缓存中的像素颜色值进行合成。利用该技术,可以获得各种各样的效果,尤其是透明(transparency)效果。
这一章主要学习:
1)理解融合原理以及如何运用融合技术;
2)了解Direct3D支持的融合模式;
3)学会如何用Alpha分量控制图元的透明度;

1. 融合方程
举个例子。我们以一个木箱为背景,绘制一个茶壶。不透明的茶壶如图7.1,进行融合之后可以得到有一定透明度的茶壶,如图7.2。





如何实现这样一个想法呢?当我们对处在木箱前方的茶壶的三角形单元进行光栅化运算时,我们需要将计算得到的茶壶的像素颜色与木箱的像素颜色进行合成,以使得木箱能够透过茶壶显示。这种将当前计算得到的像素(源像素)颜色值与先前计算得到的像素(目标像素)颜色值进行合成的做法称为融合。在Direct3D中,默认状态下是禁止融合运算的,将绘制状态D3DRS_ALPHABLENDENABLE设为true即可,如下。另外,融合开销较大,一般只在必要场合使用,并且进行批处理。
Device->SetRenderState(D3DRS_ALPHABLENDENABLE, true);


2. 融合因子

通过设定源融合因子和目标融合因子,可以创建一系列不同的融合效果。可以通过设置绘制状态D3DRS_SRC BLEND和D3DRS_DESTBLEND的值来对源融合因子和目标融合因子分别进行设置。

3. 透明度

顶点颜色中存在Alpha分量。每个顶点颜色中的Alpha分量与颜色值类似都是沿着三角形单元表面渐变的,但它并非用于确定某像素的颜色值,而是用于确定像素的Alpha分量。Alpha分量主要用于指定像素的透明度。假设像素为Alpha分量保留了8位,则透明度0%~100%对应着0~255。为了能够使用Alpha来确定像素的透明度,必须将源融合因子和目标融合因子分别设为D3DBLEND_SRCALPHA和D3DBLEND_INVSRCALPHA,而这也恰是其默认值。

1)Alpha通道

我们并不直接使用Alpha分量,而往往是从纹理的Alpha通道中获取Alpha信息。Alpha通道是保留给存储了Alpha分量的纹理元的一个额外的位集合。当纹理映射到某个图元中时,Alpha通道中的Alpha分量也进行了映射,并成为该图元的Alpha分量。

2)指定Alpha来源

默认状态下,如果当前设置的纹理拥有一个Alpha通道,Alpha值就取自该Alpha通道,否则就取自顶点颜色。但是也可以下列绘制状态来指定Alpha值的来源。如下。

//设置Alpha值的来源
//
//1.通过漫反射计算Alpha值
Device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE);
Device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);

//2.通过Alpha通道计算Alpha值
Device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
Device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);


另外,可以用DirectX Texture Tool创建Alpha通道。因为常见的图片格式不包含有Alpha信息,DirectX有工具来创建一个具有Alpha通道的DDS文件。DDS文件是专为DirectX应用程序和纹理设计的图像格式。

4. 示例

例程以木箱为背景,绘制一个透明的茶壶。允许通过按下A键和S键增大或减小Alpha分量的值。

1) 总结融合处理步骤如下:

a. 设置融合因子D3DRS_SRCBLEND和D3DRS_DESTBLEND;

b. 如果使用Alpha分量,还要指定其来源(材质或者Alpha通道);

c. 启用Alpha融合绘制状态;

2) 全局变量初始化如下:

ID3DXMesh* Teapot = 0;
D3DMATERIAL9 TeapotMtrl;

IDirect3DVertexBuffer9* BkGndQuad = 0;		//
IDirect3DTexture9*      BkGndTex  = 0;		// 创建纹理
D3DMATERIAL9            BkGndMtrl;		//背景材料

3)Setup函数。该函数指定了Alpha值的来源——材质的漫反射分量。但是在Setup函数我们并没有启用Alpha融合,因为Alpha融合需要额外的开销仅在需要的场合使用。在本例中,我们在Display函数中启用Alpha融合。Setup函数如下:

bool Setup()
{
//
// 初始化材料
//

TeapotMtrl = d3d::RED_MTRL;
TeapotMtrl.Diffuse.a = 0.5f; // 设置漫反射Alpha分量为0.5,即绘图时将以50%透明度绘制

BkGndMtrl = d3d::WHITE_MTRL;

//
// 创建茶壶
//

D3DXCreateTeapot(Device, &Teapot, 0);

//
// Create the background quad.
//

Device->CreateVertexBuffer(
6 * sizeof(Vertex),
D3DUSAGE_WRITEONLY,
Vertex::FVF,
D3DPOOL_MANAGED,
&BkGndQuad,
0);

Vertex* v;
BkGndQuad->Lock(0, 0, (void**)&v, 0);

v[0] = Vertex(-10.0f, -10.0f, 5.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f);
v[1] = Vertex(-10.0f,  10.0f, 5.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f);
v[2] = Vertex( 10.0f,  10.0f, 5.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f);

v[3] = Vertex(-10.0f, -10.0f, 5.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f);
v[4] = Vertex( 10.0f,  10.0f, 5.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f);
v[5] = Vertex( 10.0f, -10.0f, 5.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f);

BkGndQuad->Unlock();

//
// Setup a directional light.
//

D3DLIGHT9 dir;
::ZeroMemory(&dir, sizeof(dir));
dir.Type      = D3DLIGHT_DIRECTIONAL;
dir.Diffuse   = d3d::WHITE;
dir.Specular  = d3d::WHITE * 0.2f;
dir.Ambient   = d3d::WHITE * 0.6f;
dir.Direction = D3DXVECTOR3(0.707f, 0.0f, 0.707f);

Device->SetLight(0, &dir);
Device->LightEnable(0, true);

Device->SetRenderState(D3DRS_NORMALIZENORMALS, true);
Device->SetRenderState(D3DRS_SPECULARENABLE, true);

//
// Create texture and set texture filters.
//

D3DXCreateTextureFromFile(
Device,
"crate.jpg",
&BkGndTex);

Device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
Device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
Device->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_POINT);

//
// Set alpha blending states.
//

// use alpha in material's diffuse component for alpha
Device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE);
Device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);

// set blending factors so that alpha component determines transparency
Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);

//
// Set camera.
//

D3DXVECTOR3 pos(0.0f, 0.0f, -3.0f);
D3DXVECTOR3 target(0.0f, 0.0f, 0.0f);
D3DXVECTOR3 up(0.0f, 1.0f, 0.0f);
D3DXMATRIX V;
D3DXMatrixLookAtLH(&V, &pos, &target, &up);

Device->SetTransform(D3DTS_VIEW, &V);

//
// Set projection matrix.
//

D3DXMATRIX proj;
D3DXMatrixPerspectiveFovLH(
&proj,
D3DX_PI * 0.5f, // 90 - degree
(float)Width / (float)Height,
1.0f,
1000.0f);
Device->SetTransform(D3DTS_PROJECTION, &proj);

return true;
}

4)Display函数。在该函数中,我们检查A键和S键是否被按下,以确定是增大还是减小Alpha值,须得保证Alpha在[0,1]之间。然后再对背景的四边形进行绘制。最后启用融合运算,用激活的Alpha融合来绘制茶壶,再将Alpha融合计算禁止。Display函数如下:

bool Display(float timeDelta)
{
if( Device )
{
//
// Update
//

// increase/decrease alpha via keyboard input
if( ::GetAsyncKeyState('A') & 0x8000f )
TeapotMtrl.Diffuse.a += 0.01f;
if( ::GetAsyncKeyState('S') & 0x8000f )
TeapotMtrl.Diffuse.a -= 0.01f;

// force alpha to [0, 1] interval
if(TeapotMtrl.Diffuse.a > 1.0f)
TeapotMtrl.Diffuse.a = 1.0f;
if(TeapotMtrl.Diffuse.a < 0.0f)
TeapotMtrl.Diffuse.a = 0.0f;

//
// Render
//

Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0);
Device->BeginScene();

// Draw the background
D3DXMATRIX W;
D3DXMatrixIdentity(&W);
Device->SetTransform(D3DTS_WORLD, &W);
Device->SetFVF(Vertex::FVF);
Device->SetStreamSource(0, BkGndQuad, 0, sizeof(Vertex));
Device->SetMaterial(&BkGndMtrl);
Device->SetTexture(0, BkGndTex);
Device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 2);

// Draw the teapot
Device->SetRenderState(D3DRS_ALPHABLENDENABLE, true);

D3DXMatrixScaling(&W, 1.5f, 1.5f, 1.5f);
Device->SetTransform(D3DTS_WORLD, &W);
Device->SetMaterial(&TeapotMtrl);
Device->SetTexture(0, 0);
Teapot->DrawSubset(0);

Device->SetRenderState(D3DRS_ALPHABLENDENABLE, false);

Device->EndScene();
Device->Present(0, 0, 0, 0);
}
return true;
}


————Josh 2012年11月01月
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐