Direct-X学习笔记--Alpha颜色混合

Alpha混合技术是灰常有用的东东。待我好好学习一下。

一.简介

首先看一下Alpha通道,Alpha通道是计算机中存储图片透明度信息的通道,它是一个8位灰度的通道,用256级灰度记录图像中的透明信息,定义透明,不透明,半透明等,其中黑色表示完全透明,白色表示不透明,灰色为半透明。
如果不用Alpha混合,我们绘制图形的颜色总是替换当前颜色缓冲区中存在的颜色,这样后面的物体总是覆盖在原有的物体上。但是当想要绘制类似于玻璃、水等具有透明效果的物体时,这种方法显然满足不了要求。通过定义一个表示物体半透明度的Alpha值和一个半透明计算公式,可以将要绘制的物体颜色与颜色缓冲区中存在的颜色相混合,从而绘制出具有半透明效果的物体,即传说中的Alpha Blend。

二.Direct-X中的Alpha融合公式

DirectX中的Alpha融合公式如下:

OutPutColor = (RGBsrc * Ksrc) OP (RGBdst * Kdst)

OutPutColor表示alpha混合后的颜色值.
RGBsrc表示源颜色值,即将要绘制的图元的颜色值
Ksrc表示源混合系数,通常赋值为表示半透明程度的alpha值,也可以是属于枚举类型D3DBLEND的任意值,用来和RGBsrc相乘。
RGBdst表示目标颜色值,即当前颜色缓冲区中的颜色值
Kdst表示目标混合系数,可以是属于枚举D3DBLEND的任意值,用来和RGBdst相乘。
OP表示源计算结果与颜色缓冲区计算结果的混合方法,默认状态下OP为D3DBLEND_ADD,即源计算结果与颜色缓冲区计算结果相加。


图形显示中,对alpha混合最普遍的用法是:把OP赋值为D3DBLEND_ADD,使源计算结果和颜色缓冲区计算结果相加,这样一来,alpha混合颜色的公式变为:

color = (RGBsrc * Ksrc) + (RGBdst * Kdst)


注意,这个公式中的*,其实指的是向量的点积运算,因为所谓的RGBsrc,和RGBdest都是一个颜色和Alpha组合而成的四元组(R,G,B,A)即红,绿,蓝,Alpha值,各对应融合的参数。所以更加详细的来看,上面的公式应该写成:

color = (Rsrc * Ksrc1 + Rdst * Kdes1, Gsrc * Ksrc2 + Gdst * Kdst2, Bsrc * Ksrc3 + Bdst * Kdst3, Asrc * Ksrc4  + Adst * Kdst4);

而其实这个公式可以更加简化,即我们要透明的物体使用一个系数K,而要透明到的地方只需要设置成(1- K)即可,在DX中已经为我们设置好了这个宏:即把Ksrc赋值为D3DBLEND_SRCALPHA,即当前绘制像素的alpha值;把Kdst赋值为D3DBLEND_INVSRCALPHA,即1减去当前绘制像素的alpha值。

其实上面的设置已经可以较好地模拟大多数半透明物体的效果。当然我们也可以自己再设置其他的融合方式。所以我们还是有必要记一下其他的融合方式以及融合系数。

三. 融合的方式和融合系数整理

1.融合方式及操作符:
D3DBLENDOP_ADD 源像素计算结果与目标像素的计算结果相加,即【最终结果】=【源】+【目标】
D3DBLENDOP_SUBTRACT 源像素计算结果与目标像素的计算结果相减,即【最终结果】=【源】-【目标】
D3DBLENDOP_REVSUBTRACT 目标像素的计算结果减去源像素计算结果,即【最终结果】=【目标】-【源】
D3DBLENDOP_MIN 在源像素计算结果和目标像素计算结果之间取小者。即【最终结果】= MIN(【目标】,【源】)
D3DBLENDOP_MAX 在源像素计算结果和目标像素计算结果之间取大者。即【最终结果】= MAX(【目标】,【源】)

2.融合因子:
D3DBLEND_ZERO 融合因子=(0,0,0,0)
D3DBLEND_ONE 融合因子=(1,1,1,1)
D3DBLEND_SRCCOLOR 融合因子=(R_src,G_src,B_src,A_src)
D3DBLEND_INVSRCCOLOR 融合因子=(1-R_src,1-G_src,1-B_src,1-A_src)
D3DBLEND_SRCALPHA 融合因子=(1-A_src,A_src,A_src,A_src)
D3DBLEND_INVSRCALPHA 融合因子=(1-A_src,1-A_src,1-A_src,1-A_src)
D3DBLEND_DESTALPHA 融合因子=(A_dst , A_dst, A_dst  , A_dst)
D3DBLEND_INVDESTALPHA 融合因子= (1-A_dst, 1-A_dst, 1-A_dst , 1-A_dst ).
D3DBLEND_DESTCOLOR 融合因子=(R_dst , G_dst, B_dst  , A_dst).
D3DBLEND_INVDESTCOLOR 融合因子= (1 - R_dst, 1 - G_dst, 1 - B_dst, 1 - A_dst).
D3DBLEND_SRCALPHASAT 融合因子= (f, f, f, 1),其中f = min(A_src,1 - A_dst)

其中R_src  , G_src , B_src  , A_src分别表示源(即source)像素的红、绿、蓝、透明四个分量值,而R_dst  , G_dst, B_dst  , A_dst表示目标(即destination)像素的红、绿、蓝、透明四个分量值。



四.Alpha值的设定

使用Alpha融合时,需要明确Alpha值的来源。我们在设置一个对象的颜色属性时,有三种方式:
1.顶点颜色:这个是最古老的方法,也是最麻烦的。最早使用顶点缓冲区或者索引缓冲区绘图的时候设置过定点属性,其中有颜色属性,可以设置Alpha值。
2.光照和材质:材质中各种光的反射系数是一个四元组,其中就包含了Alpha值。
3.纹理:最容易的就是设置纹理来确定一个模型的颜色,所以这个也是最常用的。

既然有三种设置对象的颜色Alpha值的方式,而且常用程度是纹理>光照材质>顶点颜色,所以Alpha值的来源顺序也就很明了了,如果有纹理,那就从纹理获取,如果没有纹理,那就从光照材质中获取,如果光照材质也没有,那就从顶点属性中获取。

五.使用Alpha Blend

个人感觉Blend用于R,G,B,A都可以,可能是因为透明的情况用得比较多,所以才常用Alpha blend吧(纯属个人猜测)。

这里我们使用了第二种情况,设置光照材质,改变模型材质的Alpha值,来达到观察不同Alpha值进行融合的效果。

感觉Alpha Blend就是我们在绘制某个透明物体时,开启的一种渲染状态。

使用Alpha Blend有下面几部:

1.设定绘制对象的Alpha值,默认的值是1.0f,即不透明,所以我们如果需要设置某个对象为透明的,就改变他的Alpha值,就可以让其透明。
这里面,我们直接使用的是对象的材质信息,即默认的1.0f,并且增加了一个可以改动材质Alpha值的函数,让Alpha值改变。

2.开启Alpha测试
DX默认是关闭Alpha测试的,所以我们需要通过DX的万能函数...SetRenderState来开启Alpha测试。
	//开启Alpha融合
	m_pDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
当然,关闭的话,第二个参数为false即可。我们绘制完透明对象之后,不需要Alpha blend时,要将其关闭。

3.设置融合因子
这里面就是用上面我们说过的,DX为我们准备的最常用的那两个融合因子就可以啦!
	//设置融合因子
	m_pDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);		//设置源融合因子
	m_pDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);	//设置目标融合因子

4.设置融合运算方式
这一步其实可以省略的,DX默认的Alpha融合方式就是ADD方式,但是我们还是写一下,万一哪天不用这个了呢,还得会设置别的呀!
	//设置融合运算方式(可以省略,DX默认即为D3DBLENDOP_ADD的融合运算方式)
	m_pDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);

六.完整的Demo

这里面,我们使用了一个龙的模型(好吧,还是从浅墨大大那里偷得),读取之后,不使用纹理信息,使用的默认的材质信息,并且添加一个可以改变材质Alpha值的函数,用来改变对象的透明度。
先来一张没去掉材质的截图:

Direct-X学习笔记--Alpha颜色混合_第1张图片

去掉纹理信息,开启Alpha融合,默认Alpha值为1.0f,即完全不透明
Direct-X学习笔记--Alpha颜色混合_第2张图片

减少材质中Diffuse中的Alpha值,半透明:
Direct-X学习笔记--Alpha颜色混合_第3张图片

再减少,再减少,咦?龙呢?
答:被我吃了....
Direct-X学习笔记--Alpha颜色混合_第4张图片

代码(将Alpha混合部分和材质信息部分的代码放在了之前缩写的CMesh类中,所以这里仅贴出Mesh.cpp相关内容):
#include "stdafx.h"
#include "Mesh.h"


CMesh::CMesh(LPDIRECT3DDEVICE9 pDevice) : m_pDevice(pDevice)
{

}


CMesh::~CMesh(void)
{
	//释放相关资源
	SAFE_DELETE_ARRAY(m_pMaterials);

	if (m_pTextures)
	{
		for (int i = 0; i < m_dwNumMtrls; i++)
		{
			SAFE_RELEASE(m_pTextures[i]);
		}
		SAFE_DELETE_ARRAY(m_pTextures);
	}

	SAFE_RELEASE(m_pMesh);
}

void CMesh::CreateMesh(LPSTR filename)
{
	LPD3DXBUFFER pAdjBuffer = NULL;
	LPD3DXBUFFER pMtrlBuffer = NULL;

	D3DXLoadMeshFromX(filename, D3DXMESH_MANAGED, m_pDevice, &pAdjBuffer, &pMtrlBuffer, NULL, &m_dwNumMtrls, &m_pMesh);

	//读取材质和纹理数据
	D3DXMATERIAL *pMtrl = (D3DXMATERIAL*)pMtrlBuffer->GetBufferPointer();

	m_pMaterials = new D3DMATERIAL9[m_dwNumMtrls];
	m_pTextures = new LPDIRECT3DTEXTURE9[m_dwNumMtrls];

	for (int i = 0; i < m_dwNumMtrls; i++)
	{
		//材质信息
		m_pMaterials[i] = pMtrl[i].MatD3D;

		m_pTextures[i] = NULL;
		//不使用Mesh模型的纹理信息
		//D3DXCreateTextureFromFileA(m_pDevice, pMtrl[i].pTextureFilename, &m_pTextures[i]);

	}

	//优化网格模型
	m_pMesh->OptimizeInplace(D3DXMESHOPT_COMPACT | D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_STRIPREORDER, (DWORD*)pAdjBuffer->GetBufferPointer(), NULL, NULL, NULL);

	SAFE_RELEASE(pAdjBuffer);
	SAFE_RELEASE(pMtrlBuffer);
	
}

void CMesh::DrawMesh(const D3DXMATRIXA16& matWorld)
{
	m_pDevice->SetTransform(D3DTS_WORLD, &matWorld);
	///Alpha Blend相关

	//开启Alpha融合
	m_pDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
	//设置融合因子
	m_pDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);		//设置源融合因子
	m_pDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);	//设置目标融合因子
	//设置融合运算方式(可以省略,DX默认即为D3DBLENDOP_ADD的融合运算方式)
	m_pDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
	
	//绘制Mesh
	for (int i = 0; i < m_dwNumMtrls; i++)
	{
		m_pDevice->SetMaterial(&m_pMaterials[i]);
		m_pDevice->SetTexture(0, m_pTextures[i]);
		m_pMesh->DrawSubset(i);
	}
}

//增加Alpha值的函数
void CMesh::AddAlphaValue()
{
	for (int i = 0; i < m_dwNumMtrls; i++)
	{
		m_pMaterials[i].Diffuse.a += 0.1f;
		if (m_pMaterials[i].Diffuse.a > 1.0f)
			m_pMaterials[i].Diffuse.a = 1.0f;
	}
}

//减少Alpha值的函数
void CMesh::ReduceAlphaValue()
{
	for (int i = 0; i < m_dwNumMtrls; i++)
	{
		m_pMaterials[i].Diffuse.a -= 0.1f;
		if (m_pMaterials[i].Diffuse.a < 0.0f)
			m_pMaterials[i].Diffuse.a = 0.0f;
	}
}





你可能感兴趣的:(Direct-X)