本文由哈利_蜘蛛侠原创,转载请注明出处!有问题请联系[email protected]
这一次我们继续来讲述Jim Adams老哥的RPG编程书籍第二版第二章的第6节:Alpha Blending,也就是alpha混合。这一节的内容不多,所以就一次性讲完吧!
我们先将这一节的各小节的标题列在下面,以供大家参考:
1、 Enabling Alpha Blending (开启alpha混合)
2、 Drawing with Alpha Blending (用alpha混合进行绘图)
3、 Transparent Blitting with Alpha Testing (用alpha测试进行透明的blitting)
4、 Loading Textures with Color Keying (载入带颜色关键字的纹理)
5、 Enabling Alpha Testing (开启alpha测试)
6、 A Transparent Blitting Example (一个透明的blitting示例)
注意:原书这一节还包括几个小节关于光照的内容,不过我觉得还是把它们作为第7节内容讲述比较好。当然,这也就意味后面的章节号都有相应的延迟。
原文翻译:
===============================================================================
想象着站在世界上最高的建筑之一上面,走向一个窗口,然后凝视着下面的广阔的城市。窗户玻璃的浅蓝色色调给了所有东西一种类似于早晨的天空的平静的着色。
想象着同样的场景,但是这次是用3-D图形的语言。整个世界都是由多边形构造的,这些多边形对于所有的实际应用来说都是实心的物体。你不能够看穿它们。那么如果你想在你的游戏中看透一扇窗户呢?刚才的那个窗户给予所有物体一种漂亮的色调的效果呢?
你想要的酷炫的效果不仅仅是刚才提到过的, 还有一些叫做透明blits(transparent blits)(也就是,你想要绘制一个一部分是完全透明的多边形)的东西。
将绘制一个部分透明(部分透明,是指图片的一部分是完全透明的,而不是指整个图片是半透明的)的物体用这样的术语描述,就是绘制一个中间有洞的墙。这个洞是完全透明的,尽管这墙是实心的;你穿过洞的视线不会受到阻碍。
这些效果通过使用一种叫做alpha混合(alpha blending)的技术是可能实现的。使用alpha混合,你可以改变一个多边形的透明度,使得你可以将它看穿。如果多边形是上了色的,那么其颜色将会与该多边形后面的所有东西进行混合。更奇妙的是,你还可以在这个多边形上运用纹理来创建一些很牛的效果!
一个物体的透明度叫做alpha值(alpha value)。你也许已经注意到了,Direct3D有好几种方式来使用alpha值。例如,使用纹理时,你可以设定一个使用alpha值的格式。alpha值被储存在一个叫做alpha通道(alpha channel)的地方。
注意
===============================================================================
alpha通道是一个很像颜色成分(红、绿、蓝)的值。它设定要运用的透明度,表面上的每一个像素都有自己的一个alpha通道。
这个alpha通道可以在1位和8位之间变动。如果你有一个8位的alpha通道,你可以设定256个alpha值(从0变动到255)。一个4位的alpha通道使用16个alpha值(0-15)。
===============================================================================
启用Direct3D 的alpha混合函数很简单,只要使用IDirect3DDevice9::SetRenderState 函数并设定适当的渲染状态(render states)就OK了。第一个渲染状态是D3DRS_ALPHABLENDENABLE,它实际上启用了alpha混合:
// g_pD3DDevice =pre-initialized 3-D device object // To enablealpha-blending, use: g_pD3DDevice->SetRenderState(D3DRS_ALPHABLENDENBALE,TRUE); // Set the type ofalpha blending g_pD3DDevice->SetRenderState(D3DRS_SRCBLEND, \ D3DBLEND_SRCALPHA); g_pD3DDevice->SetRenderState(D3DRS_DESTBLEND, \ D3DBLEND_INVSRCALPHA); // To disablealpha-blending, use: g_pD3DDevice->SetRenderState(D3DRS_ALPHABLENDENBALE,FALSE);
注意到在前面的代码中还有另外两个渲染状态(D3DRS_SRCBLEND和D3DRS_DESTBLEND)。这两个状态告诉Direct3D你想要在渲染的时候想要使用alpha的值。有时候,你会看到D3DRS_DESTBLEND值被设为D3DBLEND_ONE而不是D3DBLEND_INVSRCALPHA。我会在它们出现的时候向你指出来的。
为了使用alpha混合,你唯一需要的额外的信息是如何将alpha值添加到你的自定义顶点信息之中。你通过增加漫反射颜色成分到自定义顶点结构体和描述器中而实现这一点。当你定义漫反射颜色的时候,你必须设定alpha值。
下面的例子建立了一个简单的顶点结构,它储存了3-D坐标和漫反射颜色成分(它现在包含了一个alpha值):
// The custom vertexstructure and descriptor typedef struct { FLOAT x, y, z; D3DCOLOR diffuse; } sVertex; #define VertexFVF(D3DFVF_XYZ | D3DFVF_DIFFUSE) // Define 3 verticesin a local array sVertex Verts = { { 0.0f, 100.0f, 0.0f, D3DCOLOR_RGBA(255, 0, 0, 64) }, { 100.0f, -100.0f, 0.0f, D3DCOLOR_RGBA(0, 255, 0, 128) }, { -100.0f, - 100.0f, 0.0f, D3DCOLOR_RGBA(0, 0, 255, 255) }, };
第一个顶点设成红色,并且是1.4半透明的(颜色的1.4会被混合)。第二个顶点是绿色的,使用1.2的半透明度度(颜色的1.2会被混合)。第三个顶点是蓝色的,并且是完全不透明的,表示不会有颜色被混合进来。(其实这段话很抽象。反正alpha值位0则表示完全透明,为255则表示完全不透明啦!)
如果你增加纹理映射坐标,并设置有效的纹理,你可以将漫反射颜色设为满值(红、绿、蓝都设为255),然后设置alpha值来混合纹理。
alpha测试(alpha testing)是这样一种技术,它在像素被绘制到显示屏之前测试其alpha值。
具有那些不落在某个特定范围内的alpha值的像素会被拒绝,因此就不会到达渲染阶段。跟前一节中获得半透明效果的方式类似,你可以使用alpha测试来渲染包含完全透明的部分的多边形。
使用前一节中的“墙中之洞”的例子,想象墙是一个多边形,然后你想要在其中心画一个洞。你想要这个多边形是完全不透明的(实心的),除了这个洞以外。你想要这个洞是完全透明的。为了实现这个效果,你使用一种称为透明的blit的技术(transparent blit),它使得你可以将一个纹理的某些部分排除掉,这样允许你可以从这些空洞中窥见背面的图形。
透明的blitting的秘密是建立你的纹理,并将单独的一个颜色设为颜色关键字。颜色关键字(color key)是在多边形被渲染时不会被绘制的颜色。
比如,如果你有一个纹理,它在中间有一个圆,被黑色所包围(如图2.17所示),你可以将颜色关键字设为黑色。当这个纹理被运用到一个多边形、该多边形被绘制的时候,Direct3D不会挥着这些黑色的像素,这样使得只有中间的圆会被渲染。
在实际应用中,并不是颜色关键字标记了透明的像素,而是像素的alpha值。为了让一个像素完全透明,它的alpha值必须被设为0。对于要被绘制的像素,alpha值必须被设为最高,也就是255。
正如你可能已经猜到的那样,与颜色关键字匹配的像素具有为0的alpha值;其他的所有像素具有更高的alpha值。
当使用这种方式使用alpha测试的时候,你不需要在你的自定义顶点结构体或者描述器中设定漫反射颜色成分。alpha值直接储存在了纹理的像素数据中了。为了设置纹理像素数据中的alpha值,你使用扩展版本的D3DXCreateTextureFromFile函数载入纹理,如下所示:
HRESULT D3DXCreateTextureFromFileEx( LPDIRECT3DDEVICE9 pDevice, // device object to create with LPCSTR pSrcFile, // filename of texture to load UINT Width, // D3DX_DEFAULT UINT Height,// D3DX_DEFAULT UINT MipLevels, // D3DX_DEFAULT DWORD Usage,// 0 D3DFORMAT Format,// color format to use D3DPOOL Pool,// D3DPOOL_MANAGED DWORD Filter, // D3DX_FILTER_TRIANGLE DWORD MipFilter,// D3DX_FILTER_TRIANGLE D3DCOLOR ColorKey, // Color key to use! D3DXIMAGE_INFO *pSrcInfo, // NULL PALETTEENTRY *pPalette, // NULL LPDIRECT3DTEXTURE9 *ppTexture); // texture object to create
大多数参数使用上面显示的默认参数。你唯一需要提供的东西是要载入的位图的文件名、在创建纹理时要使用哪个3-D设备对象、在载入纹理时使用的颜色格式(以D3DFMT_开头的类型,它必须使用一个alpha值,例如D3DFMT_A8R8B8G8)以及颜色关键字(以D3DCOLOR的格式)。
在设定颜色关键字的值时,使用D3DCOLOR_RGBA 或D3DCOLOR_COLORVALUE 宏来设定你想要的颜色。例如,如果你想要避免黑色被绘制,那么使用一个颜色关键字来载入纹理:
D3DCOLOR_RGBA(0, 0,0, 255);
注意alpha 的值为255。这很重要!在载入位图文件(.BMP)的时候,你还必须将alpha的值设定为255。如果你在处理已经包含了alpha通道值的非位图文件(例如.TGA),你必须将设定的alpha值与储存在图像文件中的alpha值相匹配。
在此书中,我只使用位图文件,所以只需要记住使用等于255的alpha值。在将纹理的每个像素的alpha值都设定好后,使用alpha测试来基于像素的alpha值对其拒绝就是小意思了。
一旦载入了纹理(并且设置了颜色关键字和alpha值),你可以在你初始化时或者渲染循环中添加下列代码来启用alpha测试:
// g_pD3DDevice = pre-initialized deviceobject g_pD3DDevice->SetRenderState(D3DRS_ALPHATESTENABLE,TRUE); g_pD3DDevice->SetRenderState(D3DRS_ALPHAREF,0x08); g_pD3DDevice->SetRenderState(D3DRS_ALPHAFUNC, \ D3DCMP_GREATEREQUAL);
D3DRS_ALPHAREF 状态很神奇,因为它告诉Direct3D允许哪些alpha值(从0到255之间变动)。在刚才展示的三种函数被调用后,所有的alpha值低于8的像素将会被拒绝。如果你正确地设定了颜色关键字,这三个函数会迫使所有具有零值的alpha的纹理从渲染阶段中排除掉,这样就使得它们透明了!
已经说够了;是时候给出一些代码了!下面是一个载入一个按钮图像并将之显示到屏幕上的小小例子。纹理的黑色像素被拒绝了,这样就允许背景颜色透出来:
// g_pD3DDevice = pre-initialized deviceobject // Custom vertex structure and descriptor typedef struct { FLOATx, y, z, rhw; // Screen coordinates FLOAT u, v; // Texture coordinates } sVertex; #define VertexFVF (D3DFVF_XYZRHW |D3DFVF_TEX1) // Vertex buffer and texture IDirect3DVertexBuffer9 *g_pVB = NULL; IDirect3DTexture9 *g_pTexture = NULL;
// Set up the vertex buffer and texture // assuming a 400x400 window BYTE *Ptr; sVertex Verts[4] = { { 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f }, {399.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f }, { 0.0f, 399.0f, 0.0f, 1.0f, 0.0f, 1.0f }, {399.0f, 399.0f, 0.0f, 1.0f, 1.0f, 1.0f } }; // Create vertex buffer and stuff in data g_pD3DDevice->CreateVertexBuffer(sizeof(sVertex)*4,0, \ VertexFVF,D3DPOOL_MANAGED, &g_pVB, NULL))) { g_pVB->Lock(0,0, (void**)&Ptr, 0))) memcpy(Ptr, Verts, sizeof(Verts)); g_pVB->Unlock(); // Get texture D3DXCreateTextureFromFileEx(g_pD3DDevice, “button.bmp”, \ D3DX_DEFAULT,D3DX_DEFAULT, D3DX_DEFAULT, 0, \ D3DFMT_A8R8G8B8,D3DPOOL_MANAGED, D3DX_FILTER_TRIANGLE, \ D3DX_FILTER_TRIANGLE,D3DCOLOR_RGBA(0,0,0,255), NULL, \ NULL,&g_pTexture); // Set alpha testing g_pD3DDevice->SetRenderState(D3DRS_ALPHATESTENABLE,TRUE); g_pD3DDevice->SetRenderState(D3DRS_ALPHAREF,0x01); g_pD3DDevice->SetRenderState(D3DRS_ALPHAFUNC, \ D3DCMP_GREATEREQUAL); // Clear device backbuffer g_pD3DDevice->Clear(0, NULL,D3DCLEAR_TARGET, \ D3DCOLOR_RGBA(0,128,128,255),1.0f, 0); if(SUCCEEDED(g_pD3DDevice->BeginScene())){ //Set stream source to particle vertex buffer g_pD3DDevice->SetStreamSource(0,g_pVB, 0, sizeof(sVertex)); //Set vertex shader to particle type g_pD3DDevice->SetFVF(VertexFVF); //Set texture g_pD3DDevice->SetTexture(0,g_pTexture); //Draw vertex buffer g_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP,0, 2); g_pD3DDevice->EndScene(); //Clear texture g_pD3DDevice->SetTexture(0,NULL); //Turn off alpha testing g_pD3DDevice->SetRenderState(D3DRS_ALPHATESTENABLE,FALSE); } // Flip surfaces to display work g_pD3DDevice->Present(NULL, NULL, NULL,NULL);
===============================================================================
好啦,翻译完这一节了!下面是时候送上代码了!跟上次一样,我将同时送上原书的代码以及本人更新后的代码。下面是我的更新版代码运行时的截图:
几点说明:
1、 更新版代码在前面的更新版Draw2D基础上更改而得。
2、 用了两幅美女图来让程序不至于太枯燥。
3、 原书是简单的带颜色的顶点组成的两个长方形,顶点颜色自带alpha值;而现在用的两幅图不带alpha值,所以如果按照原来的blend factor来写代码的话,那么效果很不理想(大家可以试试),所以我把它换成了另外一种blend factor,效果……还真不错!
最后给出下载地址:
alpha代码下载地址