一、概要
融合(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
四、透明度
在融合中需要重点考虑顶点颜色中的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
#include
#include
#pragma warning( disable : 4996 ) // disable deprecated warning
#include
#pragma warning( default : 4996 )
#include
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;
}
运行效果如下:
alpha通道与混合技术
Alpha其作用是要实现一种半透明效果。
假设一种不透明的东西颜色是A,另一种透明的东西颜色是B,那么透过B去看A,看上去的颜色C就是B与A的混合颜色。设置B的透明度为alpha(取值为0-1,0为完全透明,1为不透明)
R(C)=alpha*R(B)+(1-alpha)*R(A)
G(C)=alpha*G(B)+(1-alpha)*G(A)
B(C)=alpha*B(B)+(1-alpha)*B(A)
融合因子
首先,Direct3D中依然是用Alpha通道来实现多个像素颜色值的融合。每个像素都包含四个分量:Alpha分量、红色分量、绿色分量和蓝色分量(即ARGB四分量)。其中,Alpha分量用于指定像素的透明度,在0~255之间取值,0表示完全透明,255表示完全不透明。另外,根据使用的不同的颜色宏的区别,还可能是在0.0~1.0之间取值。
在Direct3D中,融合这一领域有一个权威,那便是Alpha融合公式。Alpha融合公式如下:
其中RGB_src 和RGB_dst分别表示源像素和目标像素的颜色值,为包含四个颜色分量的颜色值。
K_src 和K_dst分别表示源融合因子和目标融合因子。他们指定了源像素和目标像素的颜色值在融合过程中所占的比例,在[0,1]之间取值。通过原融合因子和目标融合因子,我们能够以多种方式来修改源像素和目标像素的颜色值,从而获得我们满意的最终的融合后的颜色值。稍后会讲解融合因子的具体取法,这里我们先把这个融合公式解析完。
在融合公式中,OP表示源和目标的融合运算方式,由D3DBLENDOP枚举体来指定,需要注意的是它的默认值是源计算结果和目标计算结果相加。
上面我们刚提到过,融合运算方式由D3DBLENDOP枚举体来指定。
我们指的是SetRenderState中的第二个参数在D3DBLENDOP枚举体中取值,而第一个参数,取D3DRS_BLENDOP。
这一节就来看一下这个D3DBLENDOP枚举体的定义:
typedef enum D3DBLENDOP {
D3DBLENDOP_ADD = 1,
D3DBLENDOP_SUBTRACT = 2,
D3DBLENDOP_REVSUBTRACT = 3,
D3DBLENDOP_MIN = 4,
D3DBLENDOP_MAX = 5, D3DBLENDOP_FORCE_DWORD = 0x7fffffff } D3DBLENDOP, *LPD3DBLENDOP;
我们用一个列表来进行讲解吧:
D3DBLENDOP操作符 |
精析 |
D3DBLENDOP_ADD |
源像素计算结果与目标像素的计算结果相加,即【最终结果】=【源】+【目标】 |
D3DBLENDOP_SUBTRACT |
源像素计算结果与目标像素的计算结果相减,即【最终结果】=【源】-【目标】 |
D3DBLENDOP_REVSUBTRACT |
目标像素的计算结果减去源像素计算结果,即【最终结果】=【目标】-【源】 |
D3DBLENDOP_MIN |
在源像素计算结果和目标像素计算结果之间取小者。即【最终结果】= MIN(【目标】,【源】) |
D3DBLENDOP_MAX |
在源像素计算结果和目标像素计算结果之间取大者。即【最终结果】= MAX(【目标】,【源】)
|
源融合因子和目标融合因子可以在SetRenderState方法中第一个参数取D3DRS_SRCBLEND和D3DRS_DESTBLEND分别进行设置,而第二个参数都是在一个D3DBLEND枚举体中进行的取值,我们在MSDN中查到它的原型如下:
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;
依旧是通过一个表格来对其中常用的参数进行讲解,:
D3DBLEND融合类型 |
精析 |
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分量来自何方。像素的Alpha值一般有三处来源,分别是顶点颜色的Alpha值、材质的Alpha值、纹理的Alpha值。我们通常在这三处来源中取一处就可以了。它们的优先级是这样的,纹理>材质>顶点颜色。
1.顶点Alpha分量
首先我们要知道,顶点Alpha分量只是备胎的备胎而已,它在没有使用光照和材质的情况下才有上场的机会。
如果在程序中直接指定每个顶点的颜色,那么可以直接给出每个顶点颜色的Alpha值,并且这些顶点的Alpha值是可以在程序运行过程中动态修改的。
我们可以通过IDirect3DDevice9::SetTextureStageState方法指定Alpha值的来源,把第三个参数指定为D3DTA_DIFFUSE,来指定Alpha值来自顶点颜色。
在MSDN中查到这个函数原型如下:
HRESULT SetTextureStageState(
[in] DWORD Stage,
[in]D3DTEXTURESTAGESTATETYPE Type,
[in] DWORD Value
);
■第一个参数,DWORD类型的Stage,指定当前设置的纹理层为第几层(有效值0~7)
■第二个参数,D3DTEXTURESTAGESTATETYPE类型的Type,填将要设置的纹理渲染状态,在枚举类型D3DTEXTURESTAGESTATETYPE中任意取值。
■第三个参数,DWORD类型的Value,表示所设置的状态值,它是根据第二个参数来决定具体取什么值的。
对于顶点Alpha分量,我们就这样来两句:
2.材质的Alpha分量
之前部分我们讲的材质的Alpha分量,充其量也只是个备胎而已,在场景内的物体没有指定纹理的时候,才有用武之地。在这种情况下,顶点的Alpha值取决于材质属性中漫反射颜色的Alpha系数以及灯光颜色中的Alpha系数,通过材质和光照中的Alpha系数相互作用,计算得到。我们知道,顶点的光照计算过程是分别针对红、绿、蓝、Alpha这四个颜色分量分开独立计算的。而我们关注的顶点的Alpha值就决定于光照计算结果的Alpha分量,和其他的红、绿、蓝三个分量毫无瓜葛。
比如我们可以这样来设定某材质的Alpha分量值,这句代码中我们把这种材质的漫反射颜色值的Alpha分量设为了0.2(范围为0.0~1.0)
g_pMaterial].Diffuse.a= 0.2f;
3.纹理的Alpha分量
作为不可一世的“高富帅”——纹理,既然它在物体表面上使用了,就必须首先满足它的要求,那么,像素的Alpha值就是纹理Alpha混合之后的值了。
所以这时候混合后的像素就取决于纹理的Alpha混合方式。而纹理Alpha混合方式决定了纹理Alpha混合之后的Alpha值是取自材质,还是取自纹理,抑或是取自这两者的某种运算。
若是取自纹理,我们就这样写:
m_pd3dDevice->SetTextureStageState( 0,D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
我是旁白:可以使用DirectX SDK中提供的DirectX Texture Tool来为我们的素材纹理图片创建Alpha通道
Alpha融合使用三步曲
1. 三步曲之一:启用Alpha融合
在Direct3D中,混合默认是被关闭着的,要启用Alpha融合的话,我们就通过设置D3DRS_ALPHABLENDENABLE渲染状态为true,即写上这句代码:
m_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
2. 三步曲之二:设置融合因子
启用了Alpha融合,第二步便是设置源融合因子和目标融合因子。前面我们已经详细讲解过源融合因子和目标融合因子的取值。源融合因子和目标融合因子分别可以在SetRenderState方法中第一个参数取D3DRS_SRCBLEND和D3DRS_DESTBLEND分别进行设置,第二个参数都是在一个D3DBLEND枚举体中进行的取值。
比如这样写,就是源融合因子=( A_src , A_src , A_src , A_src),目标融合因子=(1- A_src , 1-A_src , 1-A_src , 1-A_src ),这是我们最常用的Alpha混合因子取法:
3. 三步曲之三:设置Alpha融合运算方式
因为Direct3D中有默认的融合运算方式D3DBLENDOP_ADD,即源像素计算结果与目标像素的计算结果相加。所以这一步其实可以省略,但是如果不想用这种融合运算方式,我们可以加上这一步,用SetRenderState方法来改成我们需要的运算方式,比如如下的代码,便将融合运算方式改成了D3DBLENDOP_SUBTRACT,即源像素计算结果与目标像素的计算结果相减:
g_pd3dDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_SUBTRACT)
4. 以代码为载体理解
依旧是把这三步曲整合一下,在Direct3D中运用Alpha混合技术,其实最少只用写3句代码,第三步通常可以省略,即如下的代码:
SetTextureStageState
先说明几个概念:
1, Multipass(多通道)
将一个任务划分成几个阶段,由多个pass处理不同阶段,后续pass总是处理前一个pass的结果。例如复杂的光照方程可以分成几个pass来计算。
用不同的纹理通过多个pass来多次渲染一个图元,这样可以实现许多很酷的特效。例如LightMap,它就是用不同的纹理来表示复杂的光、影效果。
2, Multitexture(多纹理)
很显然,pass越多,效率越低。为了降低pass的数量,有些硬件加速卡支持在一个pass中渲染两个或更多的纹理,这种技术就叫做multitexture。D3D在一个pass中最多支持8个纹理的混合。
3, Pipeline(管道)
可以将管道想像成一条流水线,它完成某项任务。
4, Stage
一个管道可以由多个stage组成,这些stage同时运行,所以管道的速度取决于最慢的stage。
5, Texture blending cascade
Texture blending cascade是一个pipeline,它完成在一个pass中混合多个纹理这个任务。
6, Texture Stage(也叫做texture unit)
Texture blending cascade由许多Texture Stage构成,每个Texture Stage混合两个纹理(或经过计算的顶点集),通常是RGB和Alpha值,并将结果(经过计算的顶点集)传递给下一个Texture Stage。
理解了上面的那些概念,理解SetTextureStageState函数就很容易了。
HRESULT SetTextureStageState(
DWORD Stage,
D3DTEXTURESTAGESTATETYPE Type,
DWORD Value
);
1, Stage参数,D3D支持8个Texture Unit,索引值由0~7,通过此参数你可以指定是哪一个Texture Unit。
2, Type参数,用来选择Texture Stage不同的状态,如D3DTSS_COLOROP代表颜色混合操作,D3DTSS_ALPHAOP代表ALPHA值混合操作。
3, Value参数,根据不同的Type,来设置其状态值。如:SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_ADD ),就是指在第一个Texture Unit中,将两个颜色值的混合操作设定为累加。
D3DTSS_COLORARG1等其余的参数在D3D Document里写的很详细,就不多说了。
/
/
/
HRESULT SetTextureStageState(
DWORD Stage,
D3DTEXTURESTAGESTATETYPE Type,
DWORD Value
);
stage这个参数是指第几层纹理,1.2.3...9,,这个版本的dx最多支持9层纹理。
Type:Defines the type of operation that atexture stage will perform.//定义对该纹理的哪个属进行设置,值很多。。。
Value: 指的是前面所选属性的值
type:
D3DTSS_ALPHAOP = 4, //alpha通道的运算,
D3DTSS_COLOROP = 1, //颜色的运算
//这里的op 是operations,指对前面设置的颜色进行运算
//既后面的2个type:D3DTSS_COLORARG1,D3DTSS_COLORARG2
//或D3DTSS_ALPHAARG1,D3DTSS_ALPHAARG2 = 6
value:
D3DTOP_DISABLE =1, //该纹理无效,既不显示
D3DTOP_SELECTARG1 =2, //Arg1
D3DTOP_SELECTARG2 =3, //Arg2
D3DTOP_MODULATE =4, //Arg1 * Arg2
D3DTOP_MODULATE2X =5, //Arg1 * Arg2 * 2
D3DTOP_MODULATE4X =6, //Arg1 * Arg2 * 4
D3DTOP_ADD =7, // Arg1 + Arg2
D3DTOP_ADDSIGNED =8, //(Arg1 + Arg2 - 0.5)
D3DTOP_ADDSIGNED2X = 9, //(Arg1 + Arg2 -0.5)*2
D3DTOP_SUBTRACT = 10, //Arg1 - Arg2
D3DTOP_ADDSMOOTH = 11, //Arg1 + Arg2 – Arg1* Arg2
D3DTOP_BLENDDIFFUSEALPHA = 12, //将Arg1与Arg2使用Alpha值进行线性插值。这是标准的Alpha混合效果。纹理参数常数 TextureArgumentConstants同样可以设置到Value里
D3DTOP_BLENDTEXTUREALPHA = 13,
D3DTOP_BLENDFACTORALPHA = 14,
D3DTOP_BLENDTEXTUREALPHAPM = 15,
D3DTOP_BLENDCURRENTALPHA = 16,
D3DTOP_PREMODULATE = 17,
D3DTOP_MODULATEALPHA_ADDCOLOR = 18,
D3DTOP_MODULATECOLOR_ADDALPHA = 19,
D3DTOP_MODULATEINVALPHA_ADDCOLOR = 20,
D3DTOP_MODULATEINVCOLOR_ADDALPHA = 21,
D3DTOP_BUMPENVMAP = 22,
D3DTOP_BUMPENVMAPLUMINANCE = 23,
D3DTOP_DOTPRODUCT3 = 24,
D3DTOP_MULTIPLYADD = 25, //SRGBA = Arg1 +Arg2 * Arg3
D3DTOP_LERP = 26,
D3DTOP_FORCE_DWORD = 0x7fffffff,
type:
D3DTSS_COLORARG1 = 2,
D3DTSS_COLORARG2 = 3,
D3DTSS_ALPHAARG1 = 5,
D3DTSS_ALPHAARG2 = 6,
D3DTSS_COLORARG0 = 26,
D3DTSS_ALPHAARG0 = 27,
D3DTSS_RESULTARG = 28,
value:
这里的TA指的是texture arguments ,
D3DTA_CURRENT 表示上一个Stage混合的结果。Stage=0时,这个值表示的是D3DTA_DIFFUSE
D3DTA_CONSTANT //给当前纹理一个固定的值;
D3DTA_DIFFUSE; //diffuse的值作为参数 diffuse 可能有多个来源。。比如材质,vertex
D3DTA_SELECTMASK //Mask valuefor all arguments; not used when setting texture arguments 这句话不理解啊,为什么要伪装呢
D3DTA_SPECULAR //取spercular 的值作为参数 来源同diffuse
D3DTA_TEMP //待定。。
D3DTA_TEXTURE //用纹理的颜色值作为参数
D3DTA_TFACTOR //待定。。
D3DTSS_BUMPENVMAT00 = 7,
D3DTSS_BUMPENVMAT01 = 8,
D3DTSS_BUMPENVMAT10 = 9,
D3DTSS_BUMPENVMAT11 = 10,
D3DTSS_TEXCOORDINDEX = 11,
D3DTSS_BUMPENVLSCALE = 22,
D3DTSS_BUMPENVLOFFSET = 23,
D3DTSS_TEXTURETRANSFORMFLAGS = 24,
D3DTSS_CONSTANT = 32,
D3DTSS_FORCE_DWORD = 0x7fffffff,
m_pD3DDevice->SetTexture( 0,m_pTexture0 ); // 土地材質
m_pD3DDevice->SetTextureStageState( 0, D3DTSS_TEXCOORDINDEX, 0 );
// TextureStage 0 (不做任何改變)
m_pD3DDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_SELECTARG1 );
m_pD3DDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
m_pD3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_DISABLE );
//------------
m_pD3DDevice->SetTexture( 1,m_pTexture1 ); // 草地材質
m_pD3DDevice->SetTextureStageState( 1, D3DTSS_TEXCOORDINDEX, 0 );
// TextureStage 1 (使用頂點alpha值計算混合)
m_pD3DDevice->SetTextureStageState( 1, D3DTSS_COLOROP, D3DTOP_BLENDDIFFUSEALPHA);
m_pD3DDevice->SetTextureStageState( 1, D3DTSS_COLORARG1, D3DTA_TEXTURE );
m_pD3DDevice->SetTextureStageState( 1, D3DTSS_COLORARG2, D3DTA_CURRENT );
m_pD3DDevice->SetTextureStageState( 1, D3DTSS_ALPHAOP, D3DTOP_DISABLE );
//------------
// TextureStage 2 (使用光影遮罩圖)// 光影材質
m_pD3DDevice->SetTexture( 2, m_pTexture2 );
m_pD3DDevice->SetTextureStageState( 2, D3DTSS_TEXCOORDINDEX, 1 );
m_pD3DDevice->SetTextureStageState( 2, D3DTSS_COLOROP, D3DTOP_MODULATE2X );
m_pD3DDevice->SetTextureStageState( 2, D3DTSS_COLORARG1, D3DTA_TEXTURE );
m_pD3DDevice->SetTextureStageState( 2, D3DTSS_COLORARG2, D3DTA_CURRENT );
m_pD3DDevice->SetTextureStageState( 2, D3DTSS_ALPHAOP, D3DTOP_DISABLE );
//------------
// TextureStage 3 (使用頂點值計算陰影)
m_pD3DDevice->SetTextureStageState( 3, D3DTSS_COLOROP, D3DTOP_MODULATE );
m_pD3DDevice->SetTextureStageState( 3, D3DTSS_COLORARG1, D3DTA_DIFFUSE );
m_pD3DDevice->SetTextureStageState( 3, D3DTSS_COLORARG2, D3DTA_CURRENT );
m_pD3DDevice->SetTextureStageState( 3, D3DTSS_ALPHAOP, D3DTOP_DISABLE );
黑暗纹理
通过纹理映射来模拟逐像素光照效果,通常是将第一层纹理设置为物体原来的表面纹理,将第二层纹理设置为光照纹理,然后将两张纹理的颜色相乘,所以有时将两张纹理的颜色相乘称为光照映射(light mapping)。由于这种技术经常被用于使一张纹理变暗,有时也称为黑暗映射(dark mapping)。
混合纹理与顶点漫反射颜色
当很强的阳光照射在物体表面上时,会使它表面的颜色变得更加明亮,这可以通过将纹理与顶点的漫反射颜色相混合来模拟这种效果。当一个白色材质反射一个方向光时,反射量越多,就意味着纹理颜色在最终显示结果中所占的成分越少。因此,那些被光直接照射到表面会呈现出白色。示例代码如下:
// setup light
ZeroMemory(&g_light,sizeof(D3DLIGHT9));
g_light.Type = D3DLIGHT_DIRECTIONAL;
g_light.Diffuse.r = 0.5f;
g_light.Diffuse.g = 0.5f;
g_light.Diffuse.b = 0.5f;
D3DXVECTOR3 light_dir(0, 0, 10);
D3DXVec3Normalize((D3DXVECTOR3*) &g_light.Direction, &light_dir);
// setup material
ZeroMemory(&g_material,sizeof(D3DMATERIAL9));
g_material.Ambient.r = 1.0f;
g_material.Ambient.g = 1.0f;
g_material.Ambient.b = 1.0f;
g_material.Ambient.a = 1.0f;
g_material.Diffuse.r = 0.7f;
g_material.Diffuse.g = 0.7f;
g_material.Diffuse.b = 0.7f;
g_material.Diffuse.a = 0.5f;
pd3dDevice->SetRenderState(D3DRS_LIGHTING,TRUE);
pd3dDevice->SetRenderState(D3DRS_AMBIENT, 0x00808080);
pd3dDevice->SetLight(0, &g_light);
pd3dDevice->LightEnable(0, TRUE);
pd3dDevice->SetMaterial(&g_material);
pd3dDevice->SetTexture(0,g_base_texture);
pd3dDevice->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_ADD);
pd3dDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
发光映射
发光映射(glowing mapping)与黑暗映射正好相反,它对于模拟那些具有独立于基础贴图的发光部分的物体很有用,比如模拟发光二极管、按钮、建筑物内的灯光、太空船上的灯光等。发光映射应仅影响基础贴图上的发光区域,而不影响其他部分。因此需要对发光效果做加法,而不是做乘法。
pd3dDevice->SetTexture(0,g_base_texture);
pd3dDevice->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
pd3dDevice->SetTexture(1,g_dark_texture);
pd3dDevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0);
pd3dDevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
pd3dDevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT);
pd3dDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_ADD);
细节映射
如果要模拟一块粗糙的石灰泥墙壁,可以通过细节映射(detail mapping)来实现。实现过程是:将基础贴图(也就是第一张纹理)的颜色未经修改便作为第二个纹理操作阶段中的第二个参数,然后通过D3DTOP_ADDSIGNED将灰色的细节纹理与基础贴图相加。这个操作本质上是做了一个加法,只是使用了有符号的颜色值来代替平时使用的无符号值。在对两张纹理的像素颜色进行D3DTOP_ADDSIGNED操作时,它将参数的每个成分相加后再减去偏移量0.5,从而使有效值域变为-0.5 ~ 0.5。对一些比较旧的显卡,当其不能支持D3DTOP_ADDSIGNED操作时,可以使用D3DTOP_MODULATE2X代替D3DTOP_ADDSIGNED操作进行模拟。
在细节贴图中较亮的灰色纹理元素会使基础贴图变得更亮,而较暗的灰色纹理元素会使基础贴图变得更暗。由此可使物体呈现出粗糙的表面,从而使之看上去更为真实。示例代码如下:
pd3dDevice->SetTexture(0,g_base_texture);
pd3dDevice->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
pd3dDevice->SetTextureStageState(0,D3DTSS_COLORARG1, D3DTA_TEXTURE);
pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
pd3dDevice->SetTexture(1, g_detail_texture);
pd3dDevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0);
pd3dDevice->SetTextureStageState(1,D3DTSS_COLORARG1, D3DTA_TEXTURE);
pd3dDevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT);
hr = pd3dDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_ADDSIGNED);
if(FAILED(hr))
pd3dDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MODULATE2X);
ALPHA混合操作
Direct3D在渲染一个场景时,它可以结合几种来源的颜色信息:顶点、当前材质、纹理贴图、先前写入渲染目标的颜色信息,然后将其中的一些颜色混合起来。同时也可以使用Alpha来指定Direct3D该以怎样的权重混合这些颜色,Alpha信息可以存储在顶点中、材质中、纹理贴图中。Alpha值为0表示完全透明,Alpha值为1表示不透明,其余0~1之间的值表示不同程度的透明。
如果要从一张纹理中获取Alpha值,应将D3DTA_TEXTURE作为Alpha参数。
如果要使用来自顶点中的Alpha值,应将D3DTA_DIFFUSE作为Alpha参数,并确保渲染状态D3DRS_DIFFUSEMATERIALSOURCE被设置为D3DMCS_COLOR1(这也是默认状态)。
如果要使用来自材质中的Alpha值,应将D3DTA_DIFFUSE作为Alpha参数,并确保渲染状态D3DRS_DIFFUSEMATERIALSOURCE被设置为D3DMCS_MATERIAL。
如果未用SetRenderState()设置D3DRS_DIFFUSEMATERIALSOURCE参数,则从默认来源(即顶点)获取漫反射颜色。
pd3dDevice->SetTexture(0, g_texture);
pd3dDevice->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
pd3dDevice->SetTextureStageState(0,D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE,TRUE);
pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
纹理坐标自动生成
在Direct3D程序中,不仅可以在模型载入阶段或渲染阶段指定物体的纹理坐标,还可以通过Direct3D渲染引擎自动生成纹理坐标,用于诸如环境映射等特殊的视觉效果。与手动设置纹理坐标相比,纹理坐标自动生成在Direct3D坐标变换和光照流水线中完成,执行速度更快。
Direct3D系统可以使用经过变换的摄像机空间顶点位置坐标、法线信息来生成纹理坐标。如果使用纹理坐标自动生成,那么在顶点中就可以不用包含纹理坐标数据,从而可以降低图形渲染时的数据传输量。纹理坐标自动生成主要用于产生一些特殊效果,在大多数情况下还是手工为每个顶点指定纹理坐标。
通过调用SetTextureStageState()并将第二个参数设置为D3DTSS_TEXCOORDINDEX来控制Direct3D系统如何自动生成纹理坐标。
D3DTSS_TEXCOORDINDEX用于指定特定纹理层使用顶点中的第几组纹理坐标,但如果指定了上表中的成员值,Direct3D将忽略顶点中的纹理坐标,转而使用自动生成的纹理坐标。
D3DTSS_TEXTURETRANSFORMFLAGS用来控制生成的纹理坐标的输出,在大多数情况下纹理坐标是二维的,即将D3DTSS_TEXTURETRANSFORMFLAGS设置为D3DTTFF_COUNT2。但当绘制线段或三维纹理时,纹理坐标可能是一维或三维的。
纹理坐标变换
Direct3D提供了对生成的纹理坐标进行坐标变换的功能,与顶点坐标变换相类似,可以指定一个4x4的纹理坐标变换矩阵,把它与生成的纹理坐标相乘,然后将变换之后的纹理坐标输出至Direct3D渲染流水线。使用纹理坐标变换可以对纹理坐标进行诸如平移、旋转和缩放等三维变换。纹理坐标变换对于生成一些特殊效果是非常有用的,它不用直接修改顶点的纹理坐标。例如可以通过一个简单的平移矩阵对纹理坐标进行变换,从而使物体表面上的纹理不断变换位置,产生动画效果。纹理坐标自动生成在三维图形程序中最广泛的应用是环境映射。
可通过函数IDirect3DDevice9::SetTransform()来设置4x4的纹理坐标变换矩阵,它以D3DTS_TEXTURE0~ D3DTS_TEXTURE7作为第一个参数,表示设置纹理层0~7的纹理矩阵。下列代码对纹理层0设置了一个将纹理坐标u、v缩小到原来一半的纹理矩阵:
D3DXMATRIX mat;
D3DXMatrixIdentity(&mat);
mat._11 = 0.5f;
mat._22 = 0.5f;
pd3dDevice->SetTransform(D3DTS_TEXTURE0, &mat);
下面的代码将原来的纹理坐标平移(1.0, 1.0, 0)个单位。
D3DXMATRIX mat;
D3DXMatrixIdentity(&mat);
mat._41 = 1.0f;
mat._42 = 1.0f;
mat._43 = 0.0f;
pd3dDevice->SetTransform(D3DTS_TEXTURE0, &mat);
示例程序通过下列代码对自动生成的纹理坐标进行变换:
// texture coordinate transform
D3DXMATRIX mat_texture, mat_scale,mat_trans;
D3DXMatrixIdentity(&mat_texture);
D3DXMatrixScaling(&mat_scale, 0.5f, -0.5f, 1.0f);
D3DXMatrixTranslation(&mat_trans, 0.5f, 0.5f, 1.0f);
mat_texture = mat_texture * mat_scale *mat_trans;
pd3dDevice->SetTransform(D3DTS_TEXTURE0, &mat_texture);
立方体环境映射
立方体环境映射图有时又称为立方体映射图,是一组包含物体周围环境图像的纹理贴图,好像物体在立方体的中心。立方体环境图的每个面覆盖水平和垂直方向上各90度视角区域,一共6个面.
球形环境映射
球形环境映射图(或称为球形映射图)和立方体环境映射图类似,也是一幅包含周围场景图像的特殊纹理。和立方体环境映射图不同的是,球形环境映射图不直接代表物体周围的环境。球形映射图就好像通过鱼眼(fish-eye)凸透镜观察到的景象一样,是一个物体周围环境360度全方位视域的三维表现。
首先使用一张球形的背景图作为环境映射纹理,接着在回调函数OnCreateDevice()中加载纹理和网格模型,接着在回调函数OnResetDevice()中自动生成纹理坐标,并设置纹理阶段颜色混合方法,最后在回调函数OnFrameRender()中渲染茶壶:
V_RETURN(D3DXCreateTextureFromFile(pd3dDevice,L"spheremap.bmp", &g_env_texture));
ID3DXBuffer* material_buffer;
V_RETURN(D3DXLoadMeshFromXW(L"teapot.x", D3DXMESH_MANAGED, pd3dDevice,NULL, &material_buffer, NULL, &g_num_materials, &g_mesh));
D3DXMATERIAL* xmaterials = (D3DXMATERIAL*)material_buffer->GetBufferPointer();g_mesh_materials = newD3DMATERIAL9[g_num_materials];
for(DWORD i = 0; i < g_num_materials; i++){ g_mesh_materials[i] =xmaterials[i].MatD3D;
// .x file do not save ambient data, so set ithere. g_mesh_materials[i].Ambient = g_mesh_materials[i].Diffuse; }
material_buffer->Release();
pd3dDevice->SetTexture(0,g_env_texture);
pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
pd3dDevice->SetTextureStageState(0,D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_SPHEREMAP);
pd3dDevice->SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS,D3DTTFF_COUNT2);
// Clear the render target and thezbuffer V( pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,D3DCOLOR_ARGB(0, 0, 0, 0), 1.0f, 0) );
// Render the scene
if( SUCCEEDED( pd3dDevice->BeginScene() ) )
{ for(DWORD i = 0; i < g_num_materials; i++)
{
pd3dDevice->SetMaterial(&g_mesh_materials[i]);
g_mesh->DrawSubset(i);
}
RenderText();
V(g_button_dlg.OnRender(fElapsedTime));
V( pd3dDevice->EndScene() );
}
D3D中的texture应用示例
本文列举了Direct3D中各种纹理应用实现:黑暗贴图,发光贴图,漫反射映射贴图,细节纹理,纹理混合,有较详尽的注解。其中黑暗贴图,发光贴图,细节纹理都是采用多重纹理的方法实现(也可以采用多次渲染混合实现)。
示例代码使用Beginning direct3D game programming中的框架,省去不少事情,可以专注纹理话题。代码: 点此下载
下面来看代码与效果:
正常的纹理贴图效果:
正常的纹理贴图代码:
黑暗纹理贴图效果:
黑暗纹理贴图代码:
1
// 黑暗映射纹理
2
void drawDarkMapTexture()
3
{
4 // Multi texture:多重纹理,此处为两重纹理
5 // finalColor = destPixelColor * sourcePixelColor
6 // 设置box纹理贴图
7 Device->SetTexture(0, texBox);
8 Device->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
9 Device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); // 颜色来源-材质
10 Device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1); // 使用当前颜色作为第一个texture stage的输出
11
12 // 设置黑暗纹理贴图
13 Device->SetTexture(1, texAlpha);
14 Device->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0);
15 Device->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE); // 颜色来源-材质
16 Device->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT); // 颜色来源-前一个texture stage
17 Device->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MODULATE); // 颜色混合:相乘
18
19 // 描绘box
20 Box->draw(0, 0, 0);
21}
漫反射映射贴图效果:夜光镜效果
漫反射映射贴图代码:
1
// 漫射光映射纹理
2
void drawDiffuseTexture()
3
{
4 // 设置box纹理贴图
5 Device->SetTexture(0, texBox);
6 Device->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
7 Device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); // 颜色来源-材质
8 Device->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE); // 颜色来源-漫反射
9 Device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE); // 颜色混合
10
11 // 设置材质:绿色材质实现类似夜光镜的效果
12 Device->SetMaterial(&d3d::GREEN_MTRL);
13
14 // 描绘box
15 Box->draw(0, 0, 0);
16}
发光映射纹理贴图效果:
发光映射纹理贴图代码:
1
// 发光映射纹理
2
void drawGlowMapTexture()
3
{
4 // Multi texture:多重纹理,此处为两重纹理
5 // finalColor = sourcePixelColor * 1.0 + destPixelColor * 1.0
6 // 设置box纹理贴图
7 Device->SetTexture(0, texBox);
8 Device->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
9 Device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); // 颜色来源-材质
10 Device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1); // 使用当前颜色作为第一个texture stage的输出
11
12 // 设置黑暗纹理贴图
13 Device->SetTexture(1, texAlpha);
14 Device->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0);
15 Device->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE); // 颜色来源-材质
16 Device->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT); // 颜色来源-前一个texture stage
17 Device->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_ADD); // 颜色混合:相加
18
19 // 描绘box
20 Box->draw(0, 0, 0);
21}
细节映射纹理贴图:实现粗糙的凹凸效果
细节映射纹理贴图代码:
1
// 细节映射纹理:实现凹凸效果
2
void drawDetailMapTexture()
3
{
4 // Multi texture:多重纹理,此处为两重纹理
5 // finalColor = sourcePixelColor * destPixelColor + destPixelColor * sourcePixelColor
6 // 设置box纹理贴图
7 Device->SetTexture(0, texBox);
8 Device->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
9 Device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); // 颜色来源-材质
10 Device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1); // 使用当前颜色作为第一个texture stage的输出
11
12 // 设置细节纹理贴图
13 Device->SetTexture(1, texDetail);
14 Device->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0);
15 Device->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE); // 颜色来源-材质
16 Device->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT); // 颜色来源-前一个渲染通道
17 Device->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_ADDSIGNED); // 颜色混合
18
19 // 描绘box
20 Box->draw(0, 0, 0);
21}
alpha纹理混合效果:多次渲染实现
alph纹理混合代码:
1
// alpha混合纹理
2
void drawAlphaBlendTexture()
3
{
4 // 多次渲染实现纹理混合
5 // finalColor = sourcePixelColor * sourceBlendFactor + destPixelColor * destBlendFactor
6 // 设置纹理混合参数
7 Device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); // alpha值来自纹理
8 Device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
9
10 // 设置混合因子实现透明效果
11 Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
12 Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
13
14 //使用box纹理贴图实现第一次渲染,无alpha混合
15 Device->SetTexture(0, texBox);
16 Device->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
17 Device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); // 颜色来源-材质
18 Device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1); // 使用当前颜色作为第一个texture stage的输出
19
20 // 第一次描绘box
21 Box->draw(&boxWorldMatrix, 0, 0);
22
23 //使用带alpha值得flower纹理贴图实现第二次渲染,有alpha混合
24 Device->SetTexture(0, texAlphaFlower);
25 Device->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
26 Device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); // 颜色来源-材质
27 Device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1); // 使用当前颜色作为第一个texture stage的输出
28
29 // 打开纹理混合
30 Device->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
31
32 // 第一次描绘box
33 Box->draw(&boxWorldMatrix, 0, 0);
34
35 // 关闭纹理混合
36 Device->SetRenderState(D3DRS_ALPHABLENDENABLE, false);
37}