一、概述
一种绘制效果通常由以下几部分构成:一个顶点着色器、一个像素着色器、一个需要设置的设备状态列表、一条或多条绘制路径。我们希望采用一种低效运行机制针对不同级别的图形硬件的绘制效果(即,在现有硬件条件下,物尽其用,实现与理想效果尽可能接近的效果)。显然,所有的绘制任务都是与某一种效果相关。所以,将这些任务封装到一个单元中是比较符合逻辑的。
Direct3D效果框架(effects framework)为上述的任务封装提供了一种机制,该机制能够将与绘制效果相关的任务封装到一个效果文件(effect file)中。在效果文件中实现各种效果有诸多优点。其中之一是无需重新编译应用程序的源代码便可改变某种效果的实现。这样就使得效果的更新过程,无论是修正 bug、简单的效果增强或利用最新的 3D 硬件性能都变得更容易。其次,它把与效果相关的所有部件都封装到了一个文件中,这就程序的维护带来了极大的便利。
二、手法与路径
一个效果文件中包含了一种或多种手法(technique)。手法是绘制某些特效的特定方法。即效果文件为绘制同样的特效提供了一种或多种不同的方式。由于一些硬件可能不支持某种效果的具体实现,所以,很有必要针对不同级别的硬件实现同样效果的不同版本。
每种手法都包含了一条或多条绘制路径(rendering pass)。绘制路径封装了设备状态、采样器以及(或)用于为该条特定绘制路径绘制几何体的着色器。使用多条路径的原因是由于想要实现某些特效,必须对每条路径,以不同的绘制状态和着色器将同一几何体进行多次绘制。
效果并不局限于在可编程流水线中使用,例如,效果也可用于固定功能流水线中对设备状态(如光照、材质和纹理等)进行控制。
三、效果文件中的设备状态
通常,要想正确实现某种效果,我们必须对设备状态(例如绘制状态、纹理状态、材质、光照、纹理等)进行设置。为了支持将某一完整的效果封装在一个效果文件中的这种能力,效果框架允许我们在效果文件中对设备状态进行设置。设备状态的设置应位于某一绘制路径的代码中,语法如下所示:
State = Value;
四、效果文件的使用步骤
1、创建效果
HRESULT D3DXCreateEffectFromFileA(
LPDIRECT3DDEVICE9 pDevice,
LPCSTR pSrcFile,
CONST D3DXMACRO* pDefines,
LPD3DXINCLUDE pInclude,
DWORD Flags,
LPD3DXEFFECTPOOL pPool,
LPD3DXEFFECT* ppEffect,
LPD3DXBUFFER* ppCompilationErrors);
LPDIRECT3DDEVICE9 pDevice,
LPCSTR pSrcFile,
CONST D3DXMACRO* pDefines,
LPD3DXINCLUDE pInclude,
DWORD Flags,
LPD3DXEFFECTPOOL pPool,
LPD3DXEFFECT* ppEffect,
LPD3DXBUFFER* ppCompilationErrors);
2、效果句柄的获取
使用某种手法的第一步是获取该手法的D3DXHANDLE句柄,获取方法如下:
D3DXHANDLE ID3DXEffect ::GetTechniqueByName(LPCSTR pName);
3、效果的激活
一旦获取了所要采取的手法的句柄,接下来我们必须激活该手法。方法如下:
HRESULT ID3DXEffect ::SetTechnique(D3DXHANDLE hTechnique);
4、效果的启用
为了使用某种效果绘制几何体,我们必须将所有的绘制函数调用都写在函数ID3DXEffect::Begin 和ID3DXEffect::End之间。这两个函数实质上分别起到了启用(enable)和禁用(disable)的功能。
HRESULT ID3DXEffect::Begin ( UINT* pPasses, DWORD Flags);
●pPasses 返回当前处于活动状态的手法中的路径数目。
●Flags 该参数可取自下列任何标记:
◆Zero(0) 指示效果要保存当前设备状态和着色器状态,并在效果完成后(调用IDEDXEffect::End 函数后)恢复这些状态。
◆D3DXFX_DONOTSAVESTATE 指示效果不保存也不恢复设备状态(着色器状态除外)。
◆D3DXFX_DONOTSAVESHADERSTATE 指示效果不保存也不必恢复着色器状态。
5、当前绘制路径的设置
在我们使用一种效果绘制任何几何体之前,我们必须指定所要使用的绘制路径。指定方法为:在绘制几何体之前,应首先调用 ID3DXEffect::BeginPass 方法,该方法接受一个标识了当前活动路径的参数,几何体绘制完毕后,还必须调用 ID3DXEffect::EndPass 方法来终止当前活动路径。即 ID3DXEffect::BeginPass 方法和 ID3DXEffect::EndPass 方法必须成对出现,完成实际绘制的代码应该放在这两个函数之间,而且这两个函数必须位于函数对 ID3DXEffect::Begin 和 ID3DXEffect::End 之间。
6、效果的终止
最终,当在每条路径中绘制完几何体后,我们应调用函数 ID3DXEffect::End 来禁用或终止该效果。
五、源代码
1、效果文件
// Globals
matrix ViewProjMatrix;// Structures
struct VS_INPUT
{vector position : POSITION;vector color : COLOR;
};struct VS_OUTPUT
{vector position : POSITION;vector diffuse : COLOR;
};struct PS_INPUT
{vector color : COLOR;
};struct PS_OUTPUT
{vector diffuse : COLOR;
};VS_OUTPUT vshader(VS_INPUT input)
{VS_OUTPUT output = (VS_OUTPUT)0;output.position = mul(input.position, ViewProjMatrix);output.diffuse = input.color;return output;
}PS_OUTPUT pshader(PS_INPUT input)
{// zero out members of outputPS_OUTPUT output = (PS_OUTPUT)0;output.diffuse = input.color;return output;
}sampler ShadeSampler = sampler_state
{MinFilter = POINT;MagFilter = POINT;MipFilter = NONE;
};technique MEFFECT
{pass P0{vertexShader = compile vs_1_1 vshader();pixelShader = compile ps_2_0 pshader();Sampler[0] = (ShadeSampler);}
}
2、程序代码
#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;
LPDIRECT3DVERTEXDECLARATION9 VertexDecl = NULL;
ID3DXEffect* m_Effect = NULL;
D3DXHANDLE m_TechHandle = NULL;
D3DXHANDLE ViewProjMatrixHandle = NULL;struct CUSTOMVERTEX
{FLOAT x, y, z;DWORD color;
};bool Effect()
{ID3DXBuffer* errorBuffer = 0;HRESULT hr = D3DXCreateEffectFromFile(g_pd3dDevice, // associated device"shade.txt", // effect filename0, // no preprocessor definitions0, // no ID3DXInclude interfaceD3DXSHADER_ENABLE_BACKWARDS_COMPATIBILITY, // compile flags0, // don't share parameters&m_Effect, // return effect&errorBuffer); // return error messages// output any error messagesif( errorBuffer ){::MessageBox(0, (char*)errorBuffer->GetBufferPointer(), 0, 0);errorBuffer->Release();}if(FAILED(hr)){::MessageBox(0, "D3DXCreateEffectFromFile() - FAILED", 0, 0);return false;}return true;
}bool VertexBuffer()
{if(g_pVB)return true;g_pd3dDevice->CreateVertexBuffer(3 * sizeof(CUSTOMVERTEX), 0,0,D3DPOOL_MANAGED,&g_pVB,0);CUSTOMVERTEX* v;g_pVB->Lock(0, 0, (void**)&v, 0);v[0].x = -1, v[0].y = -1, v[0].z = 0, v[0].color = D3DCOLOR_XRGB(255, 0, 0);v[1].x = 0, v[1].y = 1, v[1].z = 0, v[1].color = D3DCOLOR_XRGB(0, 255, 0);v[2].x = 1, v[2].y = -1, v[2].z = 0, v[2].color = D3DCOLOR_XRGB(0, 0, 255);g_pVB->Unlock();return true;
}bool VertexDeclaration()
{if(VertexDecl)return true;D3DVERTEXELEMENT9 decl[] = {{0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},{0, 12, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0},D3DDECL_END()};g_pd3dDevice->CreateVertexDeclaration(decl, &VertexDecl);g_pd3dDevice->SetVertexDeclaration(VertexDecl);return true;
}bool GetHandles()
{if(m_TechHandle)return true;m_TechHandle = m_Effect->GetTechniqueByName("MEFFECT");ViewProjMatrixHandle = m_Effect->GetParameterByName(0, "ViewProjMatrix");return true;
}HRESULT InitD3D(HWND hWnd)
{if (NULL == (g_pD3D = Direct3DCreate9(D3D_SDK_VERSION)))return E_FAIL;// Set up the structure used to create the D3DDeviceD3DPRESENT_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 D3DDeviceif (FAILED(g_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,D3DCREATE_SOFTWARE_VERTEXPROCESSING,&d3dpp, &g_pd3dDevice))){return E_FAIL;}return S_OK;
}VOID SetupMatrices()
{D3DXVECTOR3 vEyePt(0.0f, 0.0f, -5);D3DXVECTOR3 vLookatPt(0.0f, 0.0f, 0.0f);D3DXVECTOR3 vUpVec(0.0f, 1.0f, 0.0f);D3DXMATRIXA16 matView;D3DXMatrixLookAtLH(&matView, &vEyePt, &vLookatPt, &vUpVec);//D3DXMatrixPerspectiveFovLH()函数中的最远、最近距离为相对于视点的距离(即vEyePt中的距离)D3DXMATRIXA16 matProj;D3DXMatrixPerspectiveFovLH(&matProj, D3DX_PI / 4, 1.0f, 1.0f, 200.0f);D3DXMATRIX ViewProjMatrix = matView * matProj;m_Effect->SetMatrix(ViewProjMatrixHandle, &ViewProjMatrix);
}VOID Cleanup()
{if (g_pVB != NULL)g_pVB->Release();if (g_pd3dDevice != NULL)g_pd3dDevice->Release();if (g_pD3D != NULL)g_pD3D->Release();if (VertexDecl != NULL)VertexDecl->Release();if (m_Effect != NULL)m_Effect->Release();}int Render()
{g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(255, 255, 255), 1.0f, 0);if (SUCCEEDED(g_pd3dDevice->BeginScene())){m_Effect->SetTechnique( m_TechHandle );UINT numPasses = 0;m_Effect->Begin(&numPasses, 0);for(int i = 0; i < numPasses; i++){m_Effect->BeginPass(i);g_pd3dDevice->SetStreamSource(0, g_pVB, 0, sizeof(CUSTOMVERTEX));g_pd3dDevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);g_pd3dDevice->SetRenderState( D3DRS_LIGHTING, FALSE );g_pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);g_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLELIST,0,1);m_Effect->EndPass();}m_Effect->End();g_pd3dDevice->EndScene();}g_pd3dDevice->Present(NULL, NULL, NULL, NULL);return 0;
}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);
}void InitShader()
{VertexBuffer();VertexDeclaration();Effect();GetHandles();SetupMatrices();
}INT WINAPI wWinMain(HINSTANCE hInst, HINSTANCE, LPWSTR, INT)
{UNREFERENCED_PARAMETER(hInst);// Register the window classWNDCLASSEX wc ={sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L,GetModuleHandle(NULL), NULL, NULL, NULL, NULL,"D3D Tutorial", NULL};RegisterClassEx(&wc);// Create the application's windowHWND hWnd = CreateWindow("D3D Tutorial", "D3D: Effect with shader",WS_OVERLAPPEDWINDOW, 100, 100, 700, 700,NULL, NULL, wc.hInstance, NULL);if (SUCCEEDED(InitD3D(hWnd))){ShowWindow(hWnd, SW_SHOWDEFAULT);UpdateWindow(hWnd);InitShader();MSG msg;ZeroMemory(&msg, sizeof(msg));while (msg.message != WM_QUIT){if (PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE)){TranslateMessage(&msg);DispatchMessage(&msg);}elseRender();}}UnregisterClass("D3D Tutorial", wc.hInstance);return 0;
}