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测试。

<span style="white-space:pre">	</span>//开启Alpha融合
	m_pDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, true);

当然,关闭的话,第二个参数为false即可。我们绘制完透明对象之后,不需要Alpha blend时,要将其关闭。

3.设置融合因子

这里面就是用上面我们说过的,DX为我们准备的最常用的那两个融合因子就可以啦!

<span style="white-space:pre">	</span>//设置融合因子
	m_pDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);		//设置源融合因子
	m_pDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);	//设置目标融合因子

4.设置融合运算方式

这一步其实可以省略的,DX默认的Alpha融合方式就是ADD方式,但是我们还是写一下,万一哪天不用这个了呢,还得会设置别的呀!

<span style="white-space:pre">	</span>//设置融合运算方式(可以省略,DX默认即为D3DBLENDOP_ADD的融合运算方式)
	m_pDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);

六.完整的Demo

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

先来一张没去掉材质的截图:

去掉纹理信息,开启Alpha融合,默认Alpha值为1.0f,即完全不透明

减少材质中Diffuse中的Alpha值,半透明:

再减少,再减少,咦?龙呢?

答:被我吃了....

代码(将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;
	}
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-10 02:09:07

Direct-X学习笔记--Alpha颜色混合的相关文章

【Unity Shaders】学习笔记——SurfaceShader(六)混合纹理

[Unity Shaders]学习笔记——SurfaceShader(六)混合纹理 转载请注明出处:http://www.cnblogs.com/-867259206/p/5619810.html 写作本系列文章时使用的是Unity5.3. 写代码之前: 当然啦,如果Unity都没安装的话肯定不会来学Unity Shaders吧? 阅读本系列文章之前你需要有一些编程的概念. 在VS里面,Unity Shaders是没有语法高亮显示和智能提示的,VS党可以参考一下这篇文章使代码高亮显示,也可以下载

OpenCV之Python学习笔记

OpenCV之Python学习笔记 直都在用Python+OpenCV做一些算法的原型.本来想留下发布一些文章的,可是整理一下就有点无奈了,都是写零散不成系统的小片段.现在看 到一本国外的新书<OpenCV Computer Vision with Python>,于是就看一遍,顺便把自己掌握的东西整合一下,写成学习笔记了.更需要的朋友参考. 阅读须知: 本文不是纯粹的译文,只是比较贴近原文的笔记:         请设法购买到出版社出版的书,支持正版. 从书名就能看出来本书是介绍在Pytho

linux学习笔记(一)-文件目录相关的命令&&文件通配符

一.几个命令概述 1.查看目录以及目录底下的文件:ls(-a显示隐藏文件:-d显示目录本身:-l显示详细内容:-R递归显示,即把子目录的文件也显示出来:-h以更加被人类理解的格式显示,比如显示文件大小的时候用M为单位显示:-i显示文件索引ID) 2.查看文件内容:cat,head,tail,less,more,tac cat:将文件连接起来,输出在屏幕上,可接多个文件(-E:显示隐藏的换行符:-n:显示出行号) head:默认查看文件的前10行(-n#:查看文件的前#行.空白行也是一行) tai

OpenCV 2.4.9 学习笔记(1)—— 基本功能结构

一些关于OpenCV(2.4.9版本)的学习笔记,作为记录,以免自己忘了. 安装与配置 OpenCV的下载.安装以及在各个平台(Windows/Linux等)配置网上有很多的资料,自己就不用存了.需要或者遇到问题的时候再说. 基本模块结构 OpenCV(Open Source Computer Vision Library),一个遵循BSD协议的计算机视觉技术开源库,包含了几百个计算机视觉算法.目前最新版本应该是OpenCV3.0,alpha版和beta版都有了,我目前是用的2.4.9,3.0版

ufldl学习笔记与编程作业:Convolutional Neural Network(卷积神经网络)

ufldl出了新教程,感觉比之前的好,从基础讲起,系统清晰,又有编程实践. 在deep learning高质量群里面听一些前辈说,不必深究其他机器学习的算法,可以直接来学dl. 于是最近就开始搞这个了,教程加上matlab编程,就是完美啊. 新教程的地址是:http://ufldl.stanford.edu/tutorial/ 本节学习地址:http://ufldl.stanford.edu/tutorial/supervised/ConvolutionalNeuralNetwork/ 一直没更

IOS学习笔记 -- Modal和Quartz2D

一. Modal1.Modal的默认效果:新控制器从屏幕的最底部往上钻,直到盖住之前的控制器为止;Modal只是改变了View的现实,没有改变rootViewController 2.常用方法1>.以Modal的形式展示控制器- (void)presentViewController:(UIViewController *)viewControllerToPresent animated: (BOOL)flag completion:(void (^)(void))completion2>.关

第十七篇:实例分析(4)--初探WDDM驱动学习笔记(十一)

感觉有必要把 KMDDOD_INITIALIZATION_DATA 中的这些函数指针的意思解释一下, 以便进一步的深入代码. DxgkDdiAddDevice 前面已经说过, 这个函数的主要内容是,将BASIC_DISPLAY_DRIVER实例指针存在context中, 以便后期使用, 支持多实例. DxgkDdiStartDevice 取得设备信息, 往注册表中加入内容, 从POST设备中获取FRAME BUFFER以及相关信息(DxgkCbAcquirePostDisplayOwnershi

&lt;老友记&gt;学习笔记

这是六个人的故事,从不服输而又有强烈控制欲的monica,未经世事的千金大小姐rachel,正直又专情的ross,幽默风趣的chandle,古怪迷人的phoebe,花心天真的joey——六个好友之间的情路坎坷,事业成败和生活中的喜怒哀乐,无时无刻不牵动着彼此的心,而正是正平凡的点点滴滴,却成为最令人感动与留恋的东西. 人物:1.瑞秋•格林(RACHEL GREENE)由珍妮佛•安妮斯顿(Jennifer Aniston)扮演 瑞秋是莫妮卡的高中同学,在与牙医未婚夫的婚礼上脱逃至莫妮卡处. 2.罗

arcgis for flex 学习笔记(一)

初步认识 地图由图层.要素.样式等组成.地图上有N个图层,图层上有N个要素,每个要素可以存放点.线.面等,每个要素可以设置样式,如果显示图片.或文字均可以先创建一个mxml组件,然后设置到要素上. 面和线都是由点组成的. 添加点 1.首先初始化图层,GraphicsLayer. 2.获取坐标点,MapPoint. 3.创建要素,Graphic. 4.设置样式 Symbol. 5.添加要素至Layer,即是第一步创建的图层. 添加线 在添加点的基础上多了一个步骤. 获取到多个点,可以实例化一个线对