Direct3D融合技术
程序员文章站
2022-07-13 10:10:21
...
一、概要
融合(blending)技术能使我们将当前要进行光栅化的像素的颜色与先前已光栅化并处于同一位置的像素进行合成。即将正在处理的图元颜色与存储在后台缓存中的像素颜色值进行合成(混合)。利用该技术,我们可以获得各种各样的效果,尤其是透明效果。
二、融合方程
需要明确的一个要点是,当前要进行光栅化的三角形单元是与已写入后台缓存的像素进行融合。所以,在融合运算时,必须遵循以下原则:
首先绘制那些不需要进行融合的物体。然后将需要进行融合的物体按照相对于摄像机的深度值进行排序;如果物体已处于观察坐标系中,该运算的效率会相当高,因为此时只需要对 Z 分量进行排序。最后,按照自后往前的顺序逐个绘制将要进行融合运算的物体。
对两个像素颜色值进行融合处理的公式如下:
OutputPixel = SourcePixel ╳ SourceBlendFactor + DestPixel ╳ DestBlendFactor
公式中的每个变量都是一个4D颜色向量(r, g, b, a),符号 “╳” 表示分量逐个相乘。
●OutputPixel 融合后的颜色值
●SourcePixel 当前计算得到的、用于与后台缓存中对应像素进行融合的像素颜色值
●SourceBlendFactor 源融合因子,指定了源像素的颜色值在融合中所占的比例,该值的取值区间为[0, 1]
●DestPixel 当前处于后台缓存中的像素颜色值
●DestBlendFactor 目标融合因子,指定了目标像素的颜色值在融合中所占的billion,该值取值区间为[0, 1]
默认状态下是禁止融合运算的。
融合的计算开销并不低,所以应该仅在必需的场合中使用。当绘制完需要进行融合的几何体之后,应禁止Alpha融合。在对三角形单元组进行融合时,最好进行批处理,之后应立即绘制出来。这样就可以避免在每帧图像中都启用和禁止融合运算。
三、融合因子
通过设定融合方程中的融合因子,可以创建一系列不同的融合效果。通过设置绘制状态D3DRS_SRCBLEND和D3DRS_DESTBLEND的值来对源融合因子和目标融合因子分别进行设定。
Device->SetRenderState(D3DRS_SRCBLEND, Source);
Device->SetRenderState(D3DRS_DESTBLEND, Destination);
其中,Source 和 Destination 可取下列融合因子:
D3DBLEND_ZERO,
D3DBLEND_ONE,
D3DBLEND_SRCCOLOR ,
D3DBLEND_INVSRCCOLOR,
D3DBLEND_SRCALPHA,
D3DBLEND_INVSRCALPHA,
D3DBLEND_DESTALPHA ,
D3DBLEND_INVDESTALPHA,
D3DBLEND_DESTCOLOR ,
D3DBLEND_INVDESTCOLOR,
D3DBLEND_SRCALPHASAT,
D3DBLEND_BOTHSRCALPHA,
D3DBLEND_BOTHINVSRCALPHA,
D3DBLEND_BLENDFACTOR,
D3DBLEND_INVBLENDFACTOR
D3DBLEND_SRCALPHA,
D3DBLEND_INVSRCALPHA,
D3DBLEND_DESTALPHA ,
D3DBLEND_INVDESTALPHA,
D3DBLEND_DESTCOLOR ,
D3DBLEND_INVDESTCOLOR,
D3DBLEND_SRCALPHASAT,
D3DBLEND_BOTHSRCALPHA,
D3DBLEND_BOTHINVSRCALPHA,
D3DBLEND_BLENDFACTOR,
D3DBLEND_INVBLENDFACTOR
四、透明度
在融合中需要重点考虑顶点颜色中的Alpha分量和材质,每个顶点颜色中的Alpha分量与颜色值类似,都是沿着三角形单元表面渐变的,但它并非用于确定某像素的颜色值,而是用于确定像素的Alpha分量。
Alpha 分量主要用于指定像素的透明度。假定我们为每个像素的 Alpha 分量保留了8位,则该 Alpha 分量的合法区间是[0, 255],对应于透明度[0, 100]。当像素的 Alpha 值为 0 时,该像素就是完全透明的,当 Alpha 值为 255 时,该像素是完全不透明的。
为了能够用 Alpha 分量来描述像素的透明度,我们必须将源融合因子和目标融合因子分别设置为D3DBLEND_SRCALPHA和D3DBLEND_INVSRCALPHA。这些值恰好也是融合因子的默认值。
1、Alpha通道
我们并不能直接使用计算得到的 Alpha 分量,而往往是从纹理的 Alpha 通道中获取 Alpha 信息。Alpha 通道是保留给存储了 Alpha 分量的纹理元的一个额外的位集合(bits set)。当纹理映射到某个图元中时,Alpha 通道中的 Alpha 分量也进行了映射,并成为该图元中像素的 Alpha 分量。
2、指定 Alpha 来源
默认状态下,如果当前设置的纹理拥有一个 Alpha 通道,Alpha 值就取自该 Alpha 通道。如果没有 Alpha 通道,Alpha 值就取自顶点的颜色。也可以用下列绘制状态来指定 Alpha 值的来源:
//compute Alpha from diffuse colors during shading
Device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE);
Device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
//take Alpha from Alpha channel
Device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
Device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
五、示例代码
#include <Windows.h>
#include <mmsystem.h>
#include <d3dx9.h>
#pragma warning( disable : 4996 ) // disable deprecated warning
#include <strsafe.h>
#pragma warning( default : 4996 )
#include <d3dx9math.h>
LPDIRECT3D9 g_pD3D = NULL;
LPDIRECT3DDEVICE9 g_pd3dDevice = NULL;
LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL;
LPDIRECT3DINDEXBUFFER9 g_pIB = NULL;
LPDIRECT3DTEXTURE9 g_pTexture = NULL;
struct CUSTOMVERTEX
{
FLOAT x, y, z;
FLOAT tu,tv;
};
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_TEX1)
HRESULT InitD3D(HWND hWnd)
{
// Create the D3D object.
if (NULL == (g_pD3D = Direct3DCreate9(D3D_SDK_VERSION)))
return E_FAIL;
// Set up the structure used to create the D3DDevice
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
d3dpp.EnableAutoDepthStencil = TRUE;
d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
// Create the D3DDevice
if (FAILED(g_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp, &g_pd3dDevice)))
{
return E_FAIL;
}
return S_OK;
}
HRESULT InitGeometry()
{
if(FAILED(D3DXCreateTextureFromFile(g_pd3dDevice,L"cratealpha.dds",&g_pTexture)))
{
return E_FAIL;
}
CUSTOMVERTEX vertices[] =
{
{ -10.0f, -10.0f, 0.0f, 0,1},
{ -10.0f, 10.0f, 0.0f, 0,0 },
{ 10.0f, 10.0f, 0.0f, 1,0},
{ 10.0f, -10.0f, 0.0f,1,1 },
};
// Create the vertex buffer.
if (FAILED(g_pd3dDevice->CreateVertexBuffer(sizeof(vertices)/sizeof(vertices[0]) * sizeof(CUSTOMVERTEX),
0, D3DFVF_CUSTOMVERTEX,
D3DPOOL_DEFAULT, &g_pVB, NULL)))
{
return E_FAIL;
}
// Fill the vertex buffer.
VOID* pVertices;
if (FAILED(g_pVB->Lock(0, sizeof(vertices), (void**)&pVertices, 0)))
return E_FAIL;
memcpy(pVertices, (void*)vertices, sizeof(vertices));
g_pVB->Unlock();
WORD indices[] = {0,1,2,0,2,3};
if( FAILED( g_pd3dDevice->CreateIndexBuffer( sizeof(indices)/sizeof(indices[0]) * sizeof( WORD ),
0, D3DFMT_INDEX16,
D3DPOOL_DEFAULT, &g_pIB, NULL ) ) )
{
return E_FAIL;
}
VOID* pIndices = NULL;
if(FAILED(g_pIB->Lock(0, sizeof(indices), (void**)&pIndices,0)))
return E_FAIL;
memcpy(pIndices,(VOID*)indices, sizeof(indices));
g_pIB->Unlock();
return S_OK;
}
VOID SetupMatrices()
{
D3DXVECTOR3 vEyePt(0.0f, 0.0f, -30);
D3DXVECTOR3 vLookatPt(0.0f, 0.0f, 0.0f);
D3DXVECTOR3 vUpVec(0.0f, 1.0f, 0.0f);
D3DXMATRIXA16 matView;
D3DXMatrixLookAtLH(&matView, &vEyePt, &vLookatPt, &vUpVec);
g_pd3dDevice->SetTransform(D3DTS_VIEW, &matView);
//D3DXMatrixPerspectiveFovLH()函数中的最远、最近距离为相对于视点的距离(即vEyePt中的距离)
D3DXMATRIXA16 matProj;
D3DXMatrixPerspectiveFovLH(&matProj, D3DX_PI / 4, 1.0f, 1.0f, 200.0f);
g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &matProj);
}
VOID Cleanup()
{
if( g_pTexture != NULL )
g_pTexture->Release();
if (g_pVB != NULL)
g_pVB->Release();
if (g_pIB != NULL)
g_pIB->Release();
if (g_pd3dDevice != NULL)
g_pd3dDevice->Release();
if (g_pD3D != NULL)
g_pD3D->Release();
}
VOID Render()
{
g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(255, 255, 255), 1.0f, 0);
// Begin the scene
if (SUCCEEDED(g_pd3dDevice->BeginScene()))
{
SetupMatrices();
g_pd3dDevice->SetTexture( 0, g_pTexture );
g_pd3dDevice->SetStreamSource(0, g_pVB, 0, sizeof(CUSTOMVERTEX));
g_pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX);
g_pd3dDevice->SetIndices(g_pIB);
g_pd3dDevice->SetRenderState( D3DRS_LIGHTING, FALSE );
g_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
g_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
g_pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
g_pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
g_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
g_pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,0,0,4,0,2);
g_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, false);
g_pd3dDevice->EndScene();
}
// Present the backbuffer contents to the display
g_pd3dDevice->Present(NULL, NULL, NULL, NULL);
}
//-----------------------------------------------------------------------------
// Name: MsgProc()
// Desc: The window's message handler
//-----------------------------------------------------------------------------
LRESULT WINAPI MsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_DESTROY:
Cleanup();
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
//-----------------------------------------------------------------------------
// Name: WinMain()
// Desc: The application's entry point
//-----------------------------------------------------------------------------
INT WINAPI wWinMain(HINSTANCE hInst, HINSTANCE, LPWSTR, INT)
{
UNREFERENCED_PARAMETER(hInst);
// Register the window class
WNDCLASSEX wc =
{
sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L,
GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
L"D3D Tutorial", NULL
};
RegisterClassEx(&wc);
// Create the application's window
HWND hWnd = CreateWindow(L"D3D Tutorial", L"D3D Tutorial 03: CreateAlpha",
WS_OVERLAPPEDWINDOW, 100, 100, 700, 700,
NULL, NULL, wc.hInstance, NULL);
// Initialize Direct3D
if (SUCCEEDED(InitD3D(hWnd)))
{
// Create the scene geometry
if (SUCCEEDED(InitGeometry()))
{
// Show the window
ShowWindow(hWnd, SW_SHOWDEFAULT);
UpdateWindow(hWnd);
// Enter the message loop
MSG msg;
ZeroMemory(&msg, sizeof(msg));
while (msg.message != WM_QUIT)
{
if (PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
Render();
}
}
}
UnregisterClass(L"D3D Tutorial", wc.hInstance);
return 0;
}
运行效果如下: