畢業設計選的是HLSL編程相關的,於是開始學習。
理論方面的不介紹了,這方面資料很多。只是記錄一些編程實踐相關的東西。
首先要準備一個HLSL程式啟動並執行基本架構。這裡我不使用MS提供的DXUT,太過複雜且使你對底層的來龍去脈懵懵懂懂,DX方面的龍書《Introduction to 3D Game Programming with DirectX 9.0》介紹了一個架構十分簡潔明了,但考慮到以後我的程式的複雜度和可擴充性我使用一個物件導向的DX程式架構,主要參考《DX即時渲染技術詳解》(kelly dempski著)。基本有4個檔案
1.Application.h 提供一個基本的CHostApplication類 主要包括 初始化D3D,建立裝置,建立視窗,枚舉模式等基本函數以及 情境初始化,渲染,丟失,銷毀的虛函數。其中最主要的是提供一個Go()函數,這是處理訊息迴圈,情境渲染的主函數。 包括的成員變數有 D3D對象,D3D裝置對象,視窗控制代碼,視窗長寬等。
2.Application.cpp 實現CHostApplication類提供的成員函數。
3.Executable.h 提供2個函式宣告。程式入口函數及訊息處理回呼函數。
4.Executable.CPP 程式執行主體。在main()中建立CHostApplication衍生類別的對象,之後進入訊息迴圈及情境渲染主體Go().
一些基本的D3D初始化及視窗函數在CHostApplication基類中已經實現,我們只要將重心移到CHostApplication的衍生類別,在衍生類別中重寫CHostApplication基類的渲染初始化,渲染操作,結束渲染等函數用以實現特定情境的繪製工作。
下面介紹這個HLSL程式:旋轉的三角形。
1.在VertexApplication.h中。構建CHostApplication類的衍生類別 CVertexApplication
#include "Application.h"
class CVertexApplication : public CHostApplication
{
public:
BOOL FillVertexBuffer();
BOOL CreateVertexBuffer();
void DestroyVertexBuffer();
CVertexApplication();
virtual ~CVertexApplication();
//This function will be called after the D3D object is
//created. Here we'll actually create the vertex buffer.
virtual BOOL PostInitialize();
//This function will clean up the vertex buffer prior to
//the destruction of the device
virtual BOOL PreTerminate();
//These functions will ensure that lost vertex buffers are
//recreated once the device is reset
virtual BOOL PreReset();
virtual BOOL PostReset();
//This function will actually render the data.
virtual void Render();
//This is the pointer to our vertex buffer
LPDIRECT3DVERTEXBUFFER9 m_pVertexBuffer;
IDirect3DVertexShader9* m_pVertexShader;
LPD3DXCONSTANTTABLE m_pConstantTable_VS;
D3DXMATRIXA16 m_matWorld,m_matView,m_matProj;
};
注意最後我添加了用於構建HLSL程式的頂點渲染器對象及常量表。
2. 在VertexApplication.cpp 檔案中實現上述相關函數。給出關鍵代碼
BOOL CVertexApplication::PostInitialize()
{
if (FAILED(EasyCreateWindowed(m_hWnd, D3DDEVTYPE_HAL,
D3DCREATE_HARDWARE_VERTEXPROCESSING)))
return FALSE;
// 建立頂點緩衝區
if(!CreateVertexBuffer())
return FALSE;
// 填充頂點緩衝區
if(!FillVertexBuffer())
return FALSE;
//編譯頂點渲染器代碼
LPD3DXBUFFER pCode_VS;
DWORD dwShaderFlags = D3DXSHADER_SKIPOPTIMIZATION|D3DXSHADER_DEBUG;
if(FAILED((D3DXCompileShaderFromFile( L"HLSLTransform.vsh", NULL, NULL, "VS",
"vs_2_0", dwShaderFlags, &pCode_VS,
NULL, &m_pConstantTable_VS))))
return FALSE;
//建立頂點渲染器
if(FAILED((m_pD3DDevice->CreateVertexShader((DWORD*)pCode_VS->GetBufferPointer(),
&m_pVertexShader ))))
return FALSE;
pCode_VS->Release();
//構造觀察矩陣
D3DXVECTOR3 vEyePt( 0.0f, 0.0f,-5 );
D3DXVECTOR3 vLookatPt( 0.0f, 0.0f, 0.0f );
D3DXVECTOR3 vUpVec( 0.0f, 1.0f, 0.0f );
D3DXMatrixLookAtLH( &m_matView, &vEyePt, &vLookatPt, &vUpVec );
//構造投影矩陣
float fAspectRatio = (float)m_WindowWidth/m_WindowHeight;
D3DXMatrixPerspectiveFovLH( &m_matProj, D3DX_PI/4, fAspectRatio, 1.0f, 100.0f );
//關閉光照處理, 預設情況下使用光照處理
m_pD3DDevice->SetRenderState( D3DRS_LIGHTING, false );
//設定剔出模式,為不剔出任何面
m_pD3DDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );
return TRUE;
}
以上是渲染情境前的初始化操作。編譯了HLSL代碼並建立了頂點渲染器對象。
下面是情境繪製代碼
void CVertexApplication::Render()
{
//Here we regenerate the data and draw the points
//FillVertexBuffer();
HRESULT hr = 0;
//建立世界矩陣
UINT iTime = timeGetTime() % 2000;
FLOAT fAngle = iTime * (2.0f * D3DX_PI) / 2000.0f;
D3DXMatrixIdentity( &m_matWorld );
D3DXMatrixRotationY( &m_matWorld, fAngle );
//設定全域變數
D3DXMATRIX mWorldViewProj = m_matWorld * m_matView * m_matProj;
m_pConstantTable_VS->SetMatrix( m_pD3DDevice, "matWorldViewProj", &mWorldViewProj);
//設定頂點渲染器對象
hr = m_pD3DDevice->SetVertexShader(m_pVertexShader);
if(FAILED(hr))
return;
// 繪製三角形
m_pD3DDevice->SetStreamSource(0,m_pVertexBuffer,0,sizeof(SIMPLE_VERTEX));
m_pD3DDevice->SetFVF(D3DFVF_SIMPLEVERTEX);
m_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 1);
}
在繪製情境的過程中,通過不斷改變世界矩陣而改變mWorldViewProj 局部變數值,然後綁定該變數到HLSL代碼檔案中的全域變數matWorldViewProj,我們可以先來看看HLSL代碼檔案:
//==============================================================
// Desc: 頂點渲染器代碼
//==============================================================
//座標變換矩陣
float4x4 matWorldViewProj;
//--------------------------------------------------------------
// Desc: 輸出結構
//--------------------------------------------------------------
struct VS_OUTPUT
{
float4 Pos : POSITION;
float4 Color : COLOR;
};
//--------------------------------------------------------------
// Desc: 頂點渲染
//--------------------------------------------------------------
VS_OUTPUT VS( float4 Pos: POSITION, float4 Color: COLOR )
{
VS_OUTPUT Out = (VS_OUTPUT) 0;
Out.Pos = mul(Pos, matWorldViewProj);
Out.Color = Color;
return Out;
}
可以看到,對於固定流水線中頂點緩衝中的每個頂點的位置POSITION和顏色COLOR作為輸入參數傳遞給VS函數,然後將頂點位置通過Pos*matWorldViewProj達到變換頂點座標的效果之後返回該頂點座標,而HLSL代碼檔案中的matWorldViewProj全域變數綁定到了固定流水線中mWorldViewProj這個局部變數的地址,隨著mWorldViewProj的改變(mWorldViewProj每一幀都在變化),matWorldViewProj同樣改變著,故而使頂點座標達到了變換的效果,之後在頂點緩衝區中的頂點座標為變換後的座標,將之Draw出來即可。
:
完整的工程檔案地址: