3D游戏编程入门(二十二)Alpha混合
恩,我们首先来想一下,我们现实中的物体,有很多是透明的,如玻璃,冰块等,我们可以透过它们看到后面的物体和颜色。而我们单纯的纹理贴图是无法实现这种效果的,这就要求我们使用Alpha混合。
在Alpha混合之前,我们有必要了解一下深度缓存。
深度缓冲
我们可以想象,离我们视角近的东西,可以遮挡到离视角远的东西,就象一个桌子,放在地上,我们从上向下看,桌子当然是遮挡住地面的。
在D3D中,我们有一个专门的内存区来存放物体之间的深度位置关系,这就是深度缓冲区。根据物体离我们视角的远近,我们将其值存放在深度缓冲区,并进行判断,到底是谁遮盖了谁,这就是深度缓冲。
我们依旧拿刚才的例子来说,假如桌子上我们又放了一本书,那么深度关系就是,书遮盖了桌子,而桌子又遮盖了地面。我们D3D设备中可以这么处理,它先获得地面的深度,然后判断地面之前有没有物体,有物体,则丢弃地面的颜色信息,记录桌子的颜色信息,又发现桌子上有本书,则再记录书的颜色信息,丢弃部分被遮盖的桌子颜色信息。投影到我们视角的平面颜色就确定了。
- 进行深度缓冲的判断,我们首先要将深度激活
D3DDevice->SetRenderState( D3DRS_ZENABLE, TRUE );
若第二个参数是FALSE则代表关闭深度缓存,和灯光一样。
- 设置深度测试函数
我们需要说明,该如何进行深度测试
D3DDevice->SteRenderState( D3DRS_ZFUNS,D3DCMP_LESS );
这代表,越离我们视角摄象机近的物体越不会被遮盖,当然,假若将第2个参数设置为MORE,则会越远的物体越不被遮盖,当然不符合现实世界,不过可以测试测试看下效果。
- 开启深度缓存的写入许可
我们需要清楚,我们所观察的物体并非不动的,我们可以想象对面一个敌人,原本是在一棵树后的,此时是树木遮挡敌人,当敌人发现我们,冲了过来,此时就是敌人遮挡树木了,深度缓存一直在进行着变化,所以我们需要开启深度缓存的写入,允许我们时刻进行深度缓存的更新修正。
D3DDevice->SetRenderState( D3DRS_ZWRITEENABLE, TRUE );
和上面一样,若我们将第二个参数设置为FALSE,则代表不开启写入,此时所有的深度缓存将固定不许修改,也就没有了更新可能。
其实深度缓存还有很多的知识,可惜的是某志在这里也没有系统的理解,只能日后想到哪儿说到哪儿了。恩,很多的辅导书上并没有对这个进行系统条理的说明。
Alpha混合
如此章节开篇所说,现在假如一个玻璃和我们的物体的位置确定了,那么我们就需要将玻璃设置为半透明了。
首先需要说一下其中的重要公式:
最后渲染的颜色 = 前面透明物的透明度 * 透明物颜色 + 后方背景物颜色 *(1-前方透明物透明度)
呵呵,是不是有些晕呼呼的?其实很好理解,那就是说,我们看到的颜色,是由透明物和后方背景色共同作用的。而且影响透明度的就影响各自所占的色深。
进行一次Alpha混合需要进行的步骤有:
- 开启Aplha混合
Aplha混合默认模式是被关闭的,我们有必要象灯光啊,深度缓存一样的将其开启。
D3DDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, true );
- 设置混合要素
(一定记得要打开深度缓存!!!) 其实说白些就是设置透明度
D3DDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
D3DDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
这里第一语句中的SRCBLEND是说我们的源体,后面跟着的第二个参数是它的透明度。 第二语句中的DESTBLEND是我们的目标体,第二个参数同样是它的透明度。 在第二个参数透明度的混合要素说明时我们可以设置一些固定的参数,我仅仅对一些常用的来进行说明。
- D3DBLEND_ZERO代表完全透明
- D3DBLEND_ONE代表完全不透明
- D3DBLEND_SRCALPHA代表源混合要素的值
- D3DBLEND_INVSRCALPHA代表用1减去源混合要素得到的值
光说明不去实验的话是没有用的,之后大家可以将上述参数进行简单的更换,就明白作用了。
- 设置Alpha资源
很多时候,我们Alpha通道并非对整个纹理进行设置的,例如我们的镜子,也是有边缘不透明的镜框的,但假若我们将整个镜子做为了一块完整的纹理,在我们进行透明度设置时,当然不能对整个镜子包括镜框都设置为透明吧。此时我们就需要指定专门的Alpha资源来控制,哪部分是透明的,哪部分是不透明的。
Device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
Device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
上述代码段则代表我们的Alpha通道是由一个Alpha通道中进行获取,当然假若我们没有Alpha通道,则我们的Alpha值就需要从顶点信息中获取了,我们在设置顶点格式FVF时就需要加Alpha通道信息了,之后设置Alpha资源时,也需要将参数改变一下
Device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE);
Device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
- 创建Alpha通道
虽然很多教材书里建议我们使用DX纹理工具DxTex.exe去创建,某志个人却更喜欢使用PS来创建,恩,或许是对美工的爱好吧,但值得注意的是,我们的jpg格式是不支持Alpha通道的,我们在保存时记得保存为tga格式或是dds格式。
- 总结
最后再整理一下,我们进行Alpha混合的必要步骤都有:
- 开启Alpha混合
- 开启深度缓存,设置混合要素D3DRS_SRCBLEND和D3DRS_DESTBLEND
- 假若使用指定的纹理资源进行Alpha混合,或顶点Alpha混合,需加说明
- 关闭Alpha通道
最后给出应该补充的代码段。
//
// 建立纹理并设置纹理过滤
//
D3DXCreateTextureFromFile(
Device,
"XXX.jpg",
&BkGndTex);
Device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
Device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
Device->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_POINT);
//
// 设置Alpha通道
//
// 从顶点中获取Alpha混合
Device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE);
Device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
// 设置混合要素
Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);