Direct3D学习手记七(Alpha融合技术)

本文介绍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融合:
Direct3D学习手记七(Alpha融合技术)
文章图片




关闭Alpha融合:
Direct3D学习手记七(Alpha融合技术)
文章图片




源代码及工程文件下载地址: 百度网盘

    推荐阅读