本文介绍Direct3D里的融合技术来实现透明效果 在上篇文章中,介绍到深度缓存和深度测试,其效果将会时近处物体遮挡远处物体,
但是如果近处物体是玻璃等透明的物体,则紧紧使用深度测试是行不通的,
这时就需要使用A融合技术来实现透明效果。
融合: 【Direct3D学习手记七(Alpha融合技术)】融合技术就是将当前像素(源像素)的颜色值与先前的像素(目标像素)的颜色值通过某种方式合成。
其中合成方式如下:
最终Color=(源像素的RGB值 X 源融合因子Ksrc) OP (目标像素的RGB值 X 目标融合因子Kdest)
OP为融合的计算方式,有以下几种:
D3DBLENDOP_ADD:源计算结果与目标计算结果相加,默认值
D3DBLENDOP_SUBTRACT:源计算结果减去目标计算结果
D3DBLENDOP_REVSUBTRACT:目标计算结果减去源计算结果
D3DBLENDOP_MIN:取源计算结果和目标计算结果的最小值
D3DBLENDOP_MAX:取源计算结果和目标计算结果的最大值
物体像素的颜色分量有A、R、G、B,其中A为物体的透明度,为0时全透明,为255时为不透明,融合也称为Alpha融合。
Ksrc和Kdest为融合因子,即合成比例,很多时候,融合因子即为像素的Alpha分量的值,
可以为融合因子设置不同的取值方式,为D3DBLEND的枚举成员:
typedef enum D3DBLEND
{
D3DBLEND_ZERO = 1,
D3DBLEND_ONE = 2,
D3DBLEND_SRCCOLOR = 3,
D3DBLEND_INVSRCCOLOR = 4,
D3DBLEND_SRCALPHA = 5,
D3DBLEND_INVSRCALPHA = 6,
D3DBLEND_DESTALPHA = 7,
D3DBLEND_INVDESTALPHA = 8,
D3DBLEND_DESTCOLOR = 9,
D3DBLEND_INVDESTCOLOR = 10,
D3DBLEND_SRCALPHASAT = 11,
D3DBLEND_BOTHSRCALPHA = 12,
D3DBLEND_BOTHINVSRCALPHA = 13,
D3DBLEND_BLENDFACTOR = 14,
D3DBLEND_INVBLENDFACTOR = 15,
D3DBLEND_SRCCOLOR2 = 16,
D3DBLEND_INVSRCCOLOR2 = 17,
D3DBLEND_FORCE_DWORD = 0x7fffffff,
} D3DBLEND, *LPD3DBLEND;
对融合因子进行设置:
//设置融合因子
g_pd3dDevice->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_SRCALPHA);
//设置源融合因子,为源Alpha
g_pd3dDevice->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA);
//设置目标融合因子,为 1.0-源Alpha
Alpha的来源: 1.顶点的Alpha分量
在定义顶点的时候可以为其设置Alpha值,并在程序中设置Alpha的来源为顶点颜色
g_pd3dDevice->SetTextureStageState(0,D3DTSS_ALPHAARG1,D3DTA_DIFFUSE);
g_pd3dDevice->SetTextureStageState(0,D3DTSS_ALPHAOP,D3DTOP_SELECTARG1);
2.材质的Alpha分量
顶点的Alpha是在没有光照和材质的情况下使用,当设置了光照和材质时,顶点的Alpha值取决于材质的漫反射的Alpha分量和光照的Alpha分量
此时Alpha的来源也是根据顶点的Alpha分量,但顶点的Alpha分量是通过材质和光照计算得来
如下方式定义一个不透明的材质:
g_Materials[0].Ambient=D3DXCOLOR(1.0F,0.0F,0.0F,1.0F);
//Alpha为1.0不透明
g_Materials[0].Diffuse=D3DXCOLOR(1.0F,0.0F,0.0F,1.0F);
g_Materials[0].Specular=D3DXCOLOR(0.1F,0.1F,0.1F,1.0F);
g_Materials[0].Emissive=D3DXCOLOR(0.0F,0.0F,0.0F,1.0F);
g_Materials[0].Power=5.0F;
如下方式定义一个半透明的材质:
g_Materials[1].Ambient=D3DXCOLOR(0.0F,1.0F,0.0F,0.5F);
//Alpha为0.5半透明
g_Materials[1].Diffuse=D3DXCOLOR(0.0F,1.0F,0.0F,0.5F);
g_Materials[1].Specular=D3DXCOLOR(0.1F,0.1F,0.1F,0.5F);
g_Materials[1].Emissive=D3DXCOLOR(0.0F,0.0F,0.0F,0.5F);
g_Materials[1].Power=5.0F;
3.纹理的Alpha分量
来源于纹理贴图的Alpha通道
g_pd3dDevice->SetTextureStageState(0,D3DTSS_ALPHAARG1,D3DTA_TEXTURE);
g_pd3dDevice->SetTextureStageState(0,D3DTSS_ALPHAOP,D3DTOP_SELECTARG1);
启用Alpha融合:
//开启融合,默认为关闭状态
g_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE,true);
//开启融合
程序源代码: 函数的声明在 Common.h 头文件中,这里只贴函数的实现
/***************************************************************
* Demo_07 : 融合技术——>透明效果
***************************************************************
* FileName : main.cpp
* Author: Anonymous
* Time: 2013/12/26
***************************************************************/
#include "Common.h"
#include "resource.h"//释放COM接口对象的宏
#defineSAFE_RELEASE(p) \
do\
{\
if(p)\
{\
(p)->Release();
\
(p)=NULL;
\
}\
}while(0);
//全局变量
HWNDg_hWnd=NULL;
//窗口句柄
LPTSTRg_pszClassName=TEXT("Demo_07");
//类名
LPTSTRg_pszWindowName=TEXT("Demo_07 : 融合技术——>透明效果");
//窗口标题
const intg_nWidth=800;
//窗口宽度
const intg_nHeight=600;
//窗口高度LPDIRECT3DDEVICE9g_pd3dDevice=NULL;
//IDirect3DDevice9接口对象,用它绘制场景及其他操作
LPD3DXMESHg_pWallMesh=NULL;
//墙壁网格
LPD3DXMESHg_pTeapotMesh=NULL;
//茶壶网格
LPD3DXMESHg_pSphereMesh=NULL;
//球体网格
D3DMATERIAL9g_Materials[3]={0};
//材质信息//窗口过程
LRESULT CALLBACK WndProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
switch(msg)
{
case WM_PAINT:
Render();
ValidateRect(hWnd,NULL);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_KEYDOWN:
if(VK_ESCAPE==wParam)
DestroyWindow(hWnd);
break;
}
return DefWindowProc(hWnd,msg,wParam,lParam);
}
//程序主函数,入口点
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPreInstance,LPSTR pszCmdLine,int nCmdShow)
{
//Step 1 : 注册窗口类,创建窗口,创建设备
if(!InitDirect3D(hInstance,g_nWidth,g_nHeight,TRUE))
return 0;
//Step 2 : 创建与初始化资源、缓存、变换等
if(!Setup(g_hWnd))
return 0;
//Step 3 : 游戏循环
GameLoop();
//Step 4 : 游戏退出,清理
Cleanup();
UnregisterClass(g_pszClassName,hInstance);
return 0;
}/****************************************************************
*函数名 : InitDirect3D
*功能: 注册窗口类,创建程序窗口,创建IDirect3DDevice9接口对象
*输入: hInstance:程序实例句柄,
*nWidth:窗口宽度,nHeight:窗口高度,
*bWindowed:窗口模式还是全屏模式
*输出: 无
*返回值 : 成功:TRUE失败:FALSE
****************************************************************/
BOOLInitDirect3D(HINSTANCE hInstance,int nWidth,int nHeight,BOOL bWindowed)
{
HINSTANCE hInst=hInstance;
if(NULL==hInst)
hInst=GetModuleHandle(NULL);
//如果hInstance为NULL,则得到当前程序的实例句柄 /**********Step 1 : 注册窗口类 **************/
WNDCLASS wndcls;
//窗口类
wndcls.cbClsExtra=0;
wndcls.cbWndExtra=0;
wndcls.hbrBackground=(HBRUSH)GetStockObject(GRAY_BRUSH);
//使用系统自带的灰色画刷
wndcls.hCursor=LoadCursor(hInst,MAKEINTRESOURCE(IDC_MAIN_CURSOR));
//设置光标
wndcls.hIcon=LoadIcon(hInst,MAKEINTRESOURCE(IDI_MAIN_ICON));
//设置图标
wndcls.hInstance=hInst;
wndcls.lpfnWndProc=WndProc;
//设置窗口过程,用于处理各种消息
wndcls.lpszClassName=g_pszClassName;
//窗口类名
wndcls.lpszMenuName=NULL;
wndcls.style=CS_VREDRAW|CS_HREDRAW;
//注册窗口类,失败返回FALSE
if(!RegisterClass(&wndcls))
return FALSE;
/**********Step 2 : 创建窗口 **************/
g_hWnd=CreateWindow(g_pszClassName,//类名
g_pszWindowName,//窗口标题
WS_OVERLAPPEDWINDOW,//风格
300,//左上角点的横坐标
80,//左上角点的纵坐标
nWidth,//窗口宽度
nHeight,//窗口高度
NULL,//父窗口实例句柄
NULL,//菜单句柄
hInst,//实例句柄
NULL);
//判断窗口是否创建成功
if(NULL==g_hWnd)
{
UnregisterClass(g_pszClassName,hInst);
return FALSE;
} /**********Step 3 : 创建IDirect3DDevice9接口对象**************/
//Step 3.1 : 创建IDirect3D9 接口对象
LPDIRECT3D9 pD3D=NULL;
pD3D=Direct3DCreate9(D3D_SDK_VERSION);
if(NULL==pD3D)//创建失败
{
UnregisterClass(g_pszClassName,hInst);
return FALSE;
} //Step 3.2 : 获取设备性能信息,设置顶点处理方式:软件or硬件
D3DCAPS9 caps;
int vp=0;
//顶点处理方式
if(FAILED(pD3D->GetDeviceCaps(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,&caps)))
{
SAFE_RELEASE(pD3D);
return FALSE;
}
if(caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT)//判断设备是否支持硬件顶点处理(转换和光照)
vp=D3DCREATE_HARDWARE_VERTEXPROCESSING;
//硬件处理
else
vp=D3DCREATE_SOFTWARE_VERTEXPROCESSING;
//软件处理 //Step 3.3 : 初始化创建设备的参数
D3DPRESENT_PARAMETERS d3dpp;
d3dpp.AutoDepthStencilFormat=D3DFMT_D24S8;
//24位深度缓存,8位模板缓存
d3dpp.BackBufferCount=1;
//后台缓存数
d3dpp.BackBufferFormat=D3DFMT_A8R8G8B8;
//后台缓存像素点格式
d3dpp.BackBufferHeight=nHeight;
//后台缓存高度(像素)
d3dpp.BackBufferWidth=nWidth;
//宽度
d3dpp.EnableAutoDepthStencil=true;
//自动管理深度缓存和模板缓存
d3dpp.Flags=0;
//其他标识
d3dpp.FullScreen_RefreshRateInHz=D3DPRESENT_RATE_DEFAULT;
//刷新频率
d3dpp.hDeviceWindow=g_hWnd;
//要绘制的窗口句柄
d3dpp.MultiSampleQuality=0;
//多重采样质量
d3dpp.MultiSampleType=D3DMULTISAMPLE_NONE;
//多重采样设为无
d3dpp.PresentationInterval=D3DPRESENT_INTERVAL_IMMEDIATE;
//立即提交/交换
d3dpp.SwapEffect=D3DSWAPEFFECT_DISCARD;
//交换时销毁缓存
d3dpp.Windowed=bWindowed;
//窗口模式还是全屏模式 //Step 3.4 : 创建设备
if(FAILED(pD3D->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,g_hWnd,vp,&d3dpp,&g_pd3dDevice)))
{
SAFE_RELEASE(pD3D);
return FALSE;
} SAFE_RELEASE(pD3D);
return TRUE;
}/****************************************************************
*函数名 : Setup
*功能: 创建与初始化资源、缓存、变换等
*输入: hWnd:窗口句柄
*输出: 无
*返回值 : 成功:TRUE失败:FALSE
****************************************************************/
BOOLSetup(HWND hWnd)
{
if(NULL==hWnd)
return FALSE;
//创建几何体及材质
//创建长方体作为墙体
D3DXCreateBox(g_pd3dDevice,10.0F,8.0F,1.0F,&g_pWallMesh,NULL);
g_Materials[0].Ambient=D3DXCOLOR(1.0F,0.0F,0.0F,1.0F);
//Alpha为1.0不透明
g_Materials[0].Diffuse=D3DXCOLOR(1.0F,0.0F,0.0F,1.0F);
g_Materials[0].Specular=D3DXCOLOR(0.1F,0.1F,0.1F,1.0F);
g_Materials[0].Emissive=D3DXCOLOR(0.0F,0.0F,0.0F,1.0F);
g_Materials[0].Power=5.0F;
//茶壶
D3DXCreateTeapot(g_pd3dDevice,&g_pTeapotMesh,NULL);
g_Materials[1].Ambient=D3DXCOLOR(0.0F,1.0F,0.0F,0.5F);
//Alpha为0.5半透明
g_Materials[1].Diffuse=D3DXCOLOR(0.0F,1.0F,0.0F,0.5F);
g_Materials[1].Specular=D3DXCOLOR(0.1F,0.1F,0.1F,0.5F);
g_Materials[1].Emissive=D3DXCOLOR(0.0F,0.0F,0.0F,0.5F);
g_Materials[1].Power=5.0F;
//球体
D3DXCreateSphere(g_pd3dDevice,1.0F,20,20,&g_pSphereMesh,NULL);
g_Materials[2].Ambient=D3DXCOLOR(0.0F,1.0F,1.0F,0.5F);
//Alpha为0.5半透明
g_Materials[2].Diffuse=D3DXCOLOR(0.0F,1.0F,1.0F,0.5F);
g_Materials[2].Specular=D3DXCOLOR(0.1F,0.1F,0.1F,0.5F);
g_Materials[2].Emissive=D3DXCOLOR(0.0F,0.0F,0.0F,0.5F);
g_Materials[2].Power=5.0F;
//设置取景变换矩阵
D3DXMATRIX matView;
D3DXVECTOR3 vEye(0.0f,0.0f,-15.0f);
//摄像机位置
D3DXVECTOR3 vAt(0.0f,0.0f,0.0f);
//观察点位置
D3DXVECTOR3 vUp(0.0f,1.0f,0.0f);
//摄像机向上分量
D3DXMatrixLookAtLH(&matView,&vEye,&vAt,&vUp);
g_pd3dDevice->SetTransform(D3DTS_VIEW,&matView);
//设置取景变换矩阵 //设置投影变换矩阵
D3DXMATRIX matProjection;
D3DXMatrixPerspectiveFovLH(&matProjection,D3DX_PI/4.0f,(FLOAT)g_nWidth/(FLOAT)g_nHeight,1.0f,1000.0f);
g_pd3dDevice->SetTransform(D3DTS_PROJECTION,&matProjection);
//设置投影变换矩阵 //设置光照为平行光
SetLight(2,g_pd3dDevice);
//开启融合,默认为关闭状态
g_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE,true);
//开启融合 //设置Alpha的来源,为顶点颜色,如果顶点没有颜色则通过材质与光照计算出来
g_pd3dDevice->SetTextureStageState(0,D3DTSS_ALPHAARG1,D3DTA_DIFFUSE);
g_pd3dDevice->SetTextureStageState(0,D3DTSS_ALPHAOP,D3DTOP_SELECTARG1);
//设置融合因子
g_pd3dDevice->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_SRCALPHA);
//设置源融合因子,为源Alpha
g_pd3dDevice->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA);
//设置目标融合因子,为 1.0-源Alpha //显示窗口
ShowWindow(hWnd,SW_SHOW);
UpdateWindow(hWnd);
return TRUE;
}/****************************************************************
*函数名 : GameLoop
*功能: 游戏循环(消息循环)
*输入: 无
*输出: 无
*返回值 : 无
****************************************************************/
voidGameLoop()
{
MSG msg;
ZeroMemory(&msg,sizeof(MSG));
//消息循环,收到WM_QUIT时退出程序
while(WM_QUIT != msg.message)
{
if(PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
//有消息产生,转发消息
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
//没有消息,利用空余时间绘制场景
Render();
}
}
}/****************************************************************
*函数名 : Render
*功能: 场景绘制
*输入: 无
*输出: 无
*返回值 : 无
****************************************************************/
voidRender()
{
//清屏,颜色设为灰色,深度缓存置为1.0,模板缓存置为0
g_pd3dDevice->Clear(0,NULL,D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER,D3DCOLOR_XRGB(150,150,150),1.0,0);
if(SUCCEEDED(g_pd3dDevice->BeginScene()))//开始绘制
{
/*在此绘制其他*/if(GetAsyncKeyState(0x31) & 0x8000)
g_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE,true);
//按1开启融合
if(GetAsyncKeyState(0x32) & 0x8000)
g_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE,false);
//按2关闭融合
if(GetAsyncKeyState(VK_UP) & 0x8000)//按上方向减,Alpha值增大,透明度减小
{
g_Materials[1].Diffuse.a+=0.01F;
g_Materials[2].Diffuse.a+=0.01F;
}
if(GetAsyncKeyState(VK_DOWN) & 0x8000)//按下方向减,Alpha值减小,透明度增加
{
g_Materials[1].Diffuse.a-=0.01F;
g_Materials[2].Diffuse.a-=0.01F;
}
if(g_Materials[1].Diffuse.a>1.0F)g_Materials[1].Diffuse.a=1.0F;
if(g_Materials[2].Diffuse.a>1.0F)g_Materials[2].Diffuse.a=1.0F;
if(g_Materials[1].Diffuse.a<0.1F)g_Materials[1].Diffuse.a=0.1F;
if(g_Materials[2].Diffuse.a<0.1F)g_Materials[1].Diffuse.a=0.1F;
//设置世界变换矩阵
D3DXMATRIX matWorld,Ry;
D3DXMatrixRotationY(&Ry,timeGetTime()/1000.0f);
//绘制墙体
D3DXMatrixRotationY(&matWorld,-D3DX_PI/4.0F);
g_pd3dDevice->SetTransform(D3DTS_WORLD,&matWorld);
g_pd3dDevice->SetMaterial(&g_Materials[0]);
g_pWallMesh->DrawSubset(0);
//绘制茶壶
D3DXMatrixTranslation(&matWorld,2.0F,1.0F,-3.0F);
D3DXMatrixMultiply(&matWorld,&matWorld,&Ry);
g_pd3dDevice->SetTransform(D3DTS_WORLD,&matWorld);
g_pd3dDevice->SetMaterial(&g_Materials[1]);
g_pTeapotMesh->DrawSubset(0);
//绘制球体
D3DXMatrixTranslation(&matWorld,-2.0F,0.0F,-3.0F);
D3DXMatrixMultiply(&matWorld,&matWorld,&Ry);
g_pd3dDevice->SetTransform(D3DTS_WORLD,&matWorld);
g_pd3dDevice->SetMaterial(&g_Materials[2]);
g_pSphereMesh->DrawSubset(0);
g_pd3dDevice->EndScene();
//结束绘制
}
g_pd3dDevice->Present(NULL,NULL,NULL,NULL);
//翻转,显示
}/****************************************************************
*函数名 : Cleanup
*功能: 程序退出时释放申请的资源
*输入: 无
*输出: 无
*返回值 : 无
****************************************************************/
voidCleanup()
{
SAFE_RELEASE(g_pWallMesh);
SAFE_RELEASE(g_pTeapotMesh);
SAFE_RELEASE(g_pSphereMesh);
SAFE_RELEASE(g_pd3dDevice);
}/****************************************************************
*函数名 : SetLight
*功能: 设置光照
*输入: nType:光源类型,1为点光源,2为平行光,3为聚光灯
*pd3dDevice:设备指针
*输出: 无
*返回值 : 成功:TRUE失败:FALSE
****************************************************************/
BOOLSetLight(int nType,LPDIRECT3DDEVICE9 pd3dDevice)
{
D3DLIGHT9 light;
ZeroMemory(&light,sizeof(light));
switch(nType)
{
case 1://点光源
light.Type=D3DLIGHT_POINT;
//光类型
light.Range=100.0f;
//光照范围
light.Position=D3DXVECTOR3(-20.0f,0.0f,0.0f);
//点光源位置
light.Attenuation0=1.0f;
//衰减系数
light.Ambient=D3DXCOLOR(0.8f,0.8f,0.8f,1.0f);
//环境光
light.Diffuse=D3DXCOLOR(1.0f,1.0f,1.0f,1.0f);
//漫反射光
light.Specular=D3DXCOLOR(0.2f,0.2f,0.2f,1.0f);
//镜面反射光
break;
case 2://平行光
light.Type=D3DLIGHT_DIRECTIONAL;
light.Direction=D3DXVECTOR3(0.0f,0.0f,1.0f);
//平行光方向,设为水平指向Z轴正方向
light.Ambient=D3DXCOLOR(0.6f,0.6f,0.6f,1.0f);
//环境光
light.Diffuse=D3DXCOLOR(1.0f,1.0f,1.0f,1.0f);
//漫反射光
light.Specular=D3DXCOLOR(0.2f,0.2f,0.2f,1.0f);
//镜面反射光
break;
case 3://聚光灯
light.Type=D3DLIGHT_SPOT;
light.Range=100.0f;
//光照范围
light.Position=D3DXVECTOR3(0.0f,20.0f,0.0f);
//聚光灯位置
light.Direction=D3DXVECTOR3(0.0f,-1.0f,0.0f);
//光照方向
light.Falloff=1.0f;
//从内到外的衰减系数,垂直光线方向
light.Attenuation0=1.0f;
//衰减系数,沿着光线方向
light.Theta=D3DX_PI/6.0F;
//内椎体角度,30度
light.Phi=D3DX_PI/3.0f;
//外锥体角度,60度
light.Ambient=D3DXCOLOR(0.4f,0.4f,0.4f,1.0f);
//环境光
light.Diffuse=D3DXCOLOR(1.0f,1.0f,1.0f,1.0f);
//漫反射光
light.Specular=D3DXCOLOR(0.2f,0.2f,0.2f,1.0f);
//镜面反射光
break;
default:
return FALSE;
}
pd3dDevice->SetRenderState(D3DRS_LIGHTING,true);
//开启光照
pd3dDevice->SetLight(nType-1,&light);
//注册光源
pd3dDevice->LightEnable(nType-1,true);
//开启注册的光
pd3dDevice->SetRenderState(D3DRS_SPECULARENABLE,true);
//开启镜面发射
pd3dDevice->SetRenderState(D3DRS_NORMALIZENORMALS,true);
//规格化顶点法向量
return TRUE;
}
程序运行结果:
开启Alpha融合:
文章图片
关闭Alpha融合:
文章图片
源代码及工程文件下载地址: 百度网盘