遊戲開發基礎(十八)

來源:互聯網
上載者:User

第十八章
像素著色器(pixel shader)是對每個像素進行光柵化處理期間運行在圖形卡GPU上的一段程式.(不同於頂點著色器,Direct3D不會以軟體運算方式來類比像素著色器)像素著色器實質上市取代了固定功能流水線中的多重紋理(multitexturing)環節,而且賦予了直接操作單個像素以及訪問每個像素的紋理座標的能力。這種對像素和紋理座標直接存取的能力使得能夠獲得各種各樣的特殊效果,例如多重紋理,景深(depth of field),雲彩類比,火焰類比以及比較複雜的陰影技術

可以通過檢查D3DCAPS9結構成員PixelShaderVersion,並與宏D3DPS_VERSION進行比較,來測試圖形卡是否支援某個頂點著色器版本
例:
 // If the device's supported version is less than version 2.0
 if (caps.PixelShaderVersion < D3DPS_VERSION(2,0))
  // then pixel shader version 2.0 is not supported on this device

多重紋理可能是像素著色器實現的最簡單的技術,由於像素著色器取代了固定功能流水線中的多重紋理環節,應該對多重紋理環節及其功能有基本的理解

多重紋理是一種較為複雜的運算,當時認為它屬於進階主題,此外,固定功能的多重紋理已被新的而且更為強大的像素著色器所取代,因此沒有在固定功能多重紋理環節投入精力展開討論是有意義的

多重紋理涵含的思想與融合有些關聯,在第7章中,瞭解了如何將進行中光柵化的像素與先前已寫入後台緩衝中的像素進行融合,以獲得某種特殊效果。將這種思想沿用到多重紋理中,即,同時啟用若干層紋理,並定義這些紋理的融合方式,以獲得某種特殊效果
。多重紋理的一種典型應用就是進行光照運算,在頂點運算階段不打算使用Direct3D的光照模型,而是使用一種特殊的紋理圖,即光照紋理圖(light maps),它規定了某一表面是如何被照亮的。例如,假定希望在一個較大的板條箱上投射一個聚光燈,可以結構D3DLIGHT9來定義該聚光燈,也可將代錶板條箱的紋理圖與代表聚光燈的紋理圖進行融合

由第7章介紹的融合技術可知,最終得到的映像與這些紋理的融合方式有關,在固定功能多重紋理環節,是通過紋理繪製狀態對融合方程進行控制的,而藉助像素著色器,可以在代碼中以可程式化方式將融合函數寫為一個簡單運算式,這就使得能夠以任何方式對多個紋理進行融合運算

通過對多個紋理進行融合來照亮板條箱與Direct3D的光照計算模型相比有以下優點:
#光照已預先計算好,並儲存在聚光燈光照紋理圖中,這樣,在程式運行時,就不需要再對光照進行運算,從而節省了運算時間,當然,只有對於靜止的物體和固定光源方可預先進行光照計算
# 由於光照紋理圖是預先計算好的,可採用比Direct3D更精確,更複雜的光照模型(從而可以在更逼真的情境中獲得更好的光照結果)

注意:多重紋理環節一般用於為靜態物體實現一個完整的光照計算引擎,例如,現有一個儲存了物體顏色資訊的紋理圖,如板條箱紋理圖,此外還有一個用於儲存漫反射表面明暗度的漫射光紋理圖(diffuse light map),一個用於儲存鏡面反射表面的明暗度的鏡面光紋理圖(specular light map),一個用於儲存表面上所覆蓋的霧的總量的霧效紋理圖(fog map),以及一個用於儲存表面的微觀高頻細節的細節紋理圖(detail map),當需要將這些紋理合成時,只需要對預先計算好的紋理圖進行查詢即可高效地為情境增加光照,上色並增加細節

注意:聚光燈紋理圖(spotlight light map)是基本光照紋理圖中較簡單的一種,通常,當給定情境和光源時,用專門的程式來產生光照紋理圖,用專門的程式來產生光照紋理圖

啟用多重紋理
紋理可用方法 IDirect3DDevice9::SetTexture來啟用,採樣器狀態(sampler state)可用方法 IDirect3DDevie9::SetSamplerState來設定
例:

HRESULT IDirect3DDevice9::SetTexture(DWORD Stage,IDirect3DBaseTexture9* pTexture);

HRESULT IDirect3DDevice9::SetSamplerState(DWORD Sampler,D3DSAMPLERSTATETYPE Type,DWORD Value);

注意:一個特定採樣器的級數(一下簡稱為採樣級)索引(sampler stage index)i與第i層紋理是關聯的,即,第i個採樣級為第i層紋理指定了採樣器狀態

紋理層/採樣級索引標識了想要將紋理/採樣器設定成的紋理層/採樣級,這樣,就可啟用多個紋理,並通過不同的層索引來為這些紋理設定相應的採樣狀態,在此之前,總是將層索引指定為0(表示第一層索引),這是因為那時只需要使用一層紋理就夠了,如果需要3層紋理,可用層序號0,1,2分別標識每一層紋理:
//set first texture and corresponding sampler states
Device->SetTexture(0,Tex1);
Device->SetSamplerState(0,D3DSAMP_MAGFILTER,D3DTEXF_LINEAR);
Device->SetSamplerState(0,D3DSAMP_MINFILTER,D3DTEXF_LINEAR);
Device->SetSamplerState(0,D3DSAMP_MIPFILTER,D3DTEXF_LINEAR);

// set second texture and corresponding sampler states
Device->SetTexture(1,Tex2);
Device->SetSamplerState(1,D3DSAMP_MAGFILTER,D3DTEXF_LINEAR);
Device->SetSamplerState(1,D3DSAMP_MINFILTER,D3DTEXF_LINEAR);
Device->SetSamplerState(1,D3DSAMP_MIPFILTER,D3DTEXF_LINEAR);

// set third texture and corresponding sampler states
Device->SetTexture(2,Tex3);
Device->SetSamplerState(2,D3DSAMP_MAGFILTER,D3DTEXF_LINEAR);
Device->SetSamplerState(2,D3DSAMP_MINFILTER,D3DTEXF_LINEAR);
Device->SetSamplerState(2,D3DSAMP_MIPFILTER,D3DTEXF_LINEAR);

多重紋理座標

每個3D三角形,需要在紋理圖中為之定義一個相應的三角形,以確定被映射到該3D三角形中的紋理資料,以前是通過為每個頂點增加紋理座標來實現的,這樣,定義了一個三角形的每3個頂點在紋理中就定義了一個相應的三角形

現在需要使用多重紋理,對於定義了三角形的每3個頂點,需要在啟用的每個紋理中定義相應的三角形,為此,可為每個頂點額外增加若干紋理座標組----每對紋理座標組應於某一層被啟用的紋理,例如,如果將3層紋理進行融合(這3層紋理已被啟用),則每個頂點都必須有3對分別與這3層紋理對應的紋理座標,這樣需要使用具有3層紋理的多重紋理頂點結構就可這樣來定義:

struct MultiTexVertex
{
 MultiTexVertex(float x,float y ,float z,
   float u0,float v0,
   float u1,float v1,
   float u2,float v2)
 {
  _x = x;_y = y;_z = z;
  _u0 = u0;_v0 = v0;
  _u1 = u1;_v1 = v1;
  _u2 = u2;_v2 = v2;
 }

 float _x ,_y,_z;
 float _u0,_v0;
 float _u1,_v1;
 float _u2,_v2; 

 static const DWORD FVF;
}

cosnt DWORD MultiTexVertex::FVF = D3DFVF_XYZ|D3DFVF_TEX3;
#D3DFVF_TEX3 表面該結構包含了3個紋理座標組,固定功能流水線至多支援8層紋理,如果想使用的紋理層數超過8,則必須使用頂點聲明和可程式化頂點流水線

在較新的像素著色器版本中,可用一個紋理座標組來索引多個紋理,這樣就無需使用多個頂點座標組,當然,這樣做的前提是對每個紋理層都是用了相同的紋理座標,如果每層紋理中的紋理座標存在差異,則需要多個紋理座標組

像素著色器的輸入和輸出

像素著色器的輸入包括每個像素顏色和紋理座標

注意 頂點的顏色應在圖元的整個表面上進行插值處理
每個像素的紋理座標其實就是指定了紋理中將被映射到當前像素的紋理元的座標(u,v).在進入像素著色器之前,Direct3D 先根據頂點顏色和頂點紋理座標計算出每個像素的顏色和紋理座標。輸入像素著色器的顏色和紋理座標組的個數由頂點著色器輸出的顏色和紋理座標組的個數決定,例如,如果一個頂點著色器輸出兩種顏色值和3個紋理座標組,則Direct3D將計算出每個像素的兩種顏色值和3個紋理座標組,並將這些結果輸入像素著色器。我們需藉助語義文法來將輸入的顏色和紋理座標映射為像素著色器程式中的變數
struct PS_INPUT
{
 vector c0:COLOR0;
 vector c1:COLOR1;
 float2 t0:TEXCOORD0;
 float2 t1:TEXCOORD1;
 float2 t2:TEXCOORD2;
};

就輸出而言,像素著色器將輸出計算所得的每個像素的單個顏色值
struct PS_OUTPUT
{
 vector finalPixelColor:COLOR0;
};

使用像素著色器的步驟:
1編寫像素著色器程式並進行編譯
2建立一個IDirect3DPixelShader9介面對象,以表示基於經過編譯的著色器代碼的像素著色器
3用IDirect3DDevice9::SetPixelShader方法啟用像素著色器

像素著色器使用完畢後,必須對其進行銷毀

像素著色器的編寫和編譯
像素著色器的編譯方式與頂點著色器完全相同,首先,必須編寫好一個像素著色器程式,用HLSL語言來編寫著色器程式,用函數D3DXCompileShaderFromFile對著色器程式進行編譯、

注意,使用像素著色器,必須將編譯目標修改為像素著色器目標(如ps_2_0)而非頂點著色器目標(如vs_2_0),編譯目標是通過函數D3DXCompileShaderFromFile中的一個參數來指定的

像素著色器的建立
一旦著色器代碼經過了編譯,就可藉助下述方法擷取指向IDirect3DPixelShader9介面的指標,該介面代表了一個像素著色器
HRESULT CreatePixelShader(
    CONST DWORD *pFunction,
    IDirect3DPixelShader9** ppShader);

#pFunction 指向經過編譯的著色器代碼的指標
#ppShader 返回一個指向IDirect3DPixelShader9介面的指標
例:假定變數shader是ID3DXBuffer介面的對象,它包含了經過編譯的著色器代碼,為了擷取IDirect3DPixelShader9介面的指標,例:
IDirect3DPixelShader9* MultiTexPS = 0;
hr = Device->CreatePixelShader((DWORD*)Shader->GetBufferPointer(),
    &MultiTexPS);

像素著色器的設定
當擷取了代表像素著色器的介面IDirect3DPixelShader9指標後,啟用:
HRESULT SetPixelShader(IDirect3DPixelShader9* pShader);
該方法只接收一個參數,可將指定希望啟用的像素著色器的指標傳給該參數,
例:
Device->SetPixelShader(MultiTexPS);

像素著色器的銷毀
介面IDirect3DPixelShader9在使用完畢後必須調用其自身的Release方法來釋放它所佔用的資源
d3d::Release<IDirect3DPixelShader9*>(MultiTexPS);

HLSL採樣器對象
要在像素著色器對紋理進行採樣,可使用專門與tex*相關的HLSL內建函數。
注意,採樣是指根據像素的紋理座標和採樣狀態(texture filter state,紋理過濾器狀態)來檢索某一像素所對應的紋理元

通常,這些函數都需要指定兩件事
#用於檢索紋理的紋理座標(u,v);
#想要檢索的特定紋理
紋理座標(u,v)當然是作為像素著色器的輸入,想要檢索的特定紋理在像素著色器中用一個特別的HLSL對象---採樣器(sampler)來標識,可將sampler對象視作標識紋理層和採樣級的對象。
例:假定使用3層紋理,就意味著應該能夠在像素著色器中引用每層紋理,在像素著色器程式中可以這樣寫:
sampler FirstTex;
sampler SecondTex;
sampler ThirdTex;

Direct3D將把每一個sampler對象唯一地與某一紋理層關聯起來,在應用程式中,只需找出sampler對象所對應的紋理層,然後為該紋理層設定合適的紋理及其相應的採樣器狀態,下面代碼示範如何為FirstTex設定紋理和採樣器狀態
//Create texture
IDirect3DTexture9* Tex;
D3DXCreateTextureFromFile(Device,"tex.bmp",&Tex);
...
// get handle to constant
FirstTexHandle = MultiTexCT->GetConstantByName(0,"FirstTex");

// Get a description of the constant;
D3DXCONSTANT_DESC FirstTexDesc;
UINT count ;
MultiTexCT->GetConstantDesc(FirstTexHandle,&FirstTexDesc,&count);
...
// set texture/campler states for the sampler FirstTex ,we identify
// the stage FirstTex is associated with form the
// D3DXCONSTANT_DESC::RegisterIndex member;
Device->SetTexture(FirstTexDesc.RegisterIndex,Tex);
Device->SetSamplerState(FirstTexDesc.RegisterIndex,D3DSAMP_MAGFILTER,D3DTEXF_LINEAR);
Device->SetSamplerState(FirstTexDesc.RegisterIndex,D3DSAMP_MINFILTER,D3DTEXF_LINEAR);
Device->SetSamplerState(FirstTexDesc.RegisterIndex,D3DSAMP_MIPFILTER,D3DTEXF_LINEAR);

注意,除了使用sampler類型外,還可使用更具體,類型檢查更嚴格的sampler1D,sampler2D,sampler3D以及samplerCube類型,這些類型在型別安全上更為突出,並能夠保證上述類型僅可用於相應的tex*函數中,例如,一個sampler2D對象只能用在tex2D*函數中,類似地,一個sampler3D對象只能用在tex3D*函數中.
(完)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.