新建網(wǎng)頁 1
在我們寫的程序里頂點(diǎn)和像素是很小的對象,它們由GPU來執(zhí)行,是固定功能管線的一部分。用我們自己寫的著色器程序替換一部分固定功能管線,在繪制效果上我們獲得很大的靈活性。我們不再局限于預(yù)定義的"固定"操作。
為了編寫著色器程序,我們需要一種高級著色器語言(High-Level Shading Language ,簡稱HLSL)
。 在DirectX 8中,著色器是用低級著色器匯編語言編寫的。幸運(yùn)的是,我們不必再用匯編語言來寫著色器了,DirectX 9支持一種高級著色器語言來寫。用HLSL寫著色器程序與使用高級語言有同樣的優(yōu)勢,像C++,它超越了匯編語言,即:
增加生產(chǎn)力—用高級語言比用低級語言寫程序更快、更容易。 我們可以花費(fèi)更多的時(shí)間關(guān)注于算法而不是代碼。
增加可讀性—用高級語言寫的程序更易讀,這意味著用高級語言編程更易于調(diào)試和維護(hù)。
大多數(shù)情況下,編譯器產(chǎn)生的匯編代碼比手寫有效率。
使用HLSL 編譯器,我們可以編譯我們的代碼到任何可用shader版本,使用匯編語言我們將不得不為每個(gè)需要的版本移植代碼。
HLSL 同C和C++語法很類似,所以縮短了學(xué)習(xí)曲線。
最后,如果你的顯卡不支持頂點(diǎn)和像素著色器的話,為了執(zhí)行著色器的例子程序你將需要轉(zhuǎn)換REF設(shè)備。使用REF設(shè)備意味著著色器例子運(yùn)行的會很慢,但它至少能顯示結(jié)果,讓我們?nèi)z查是否代碼可以被執(zhí)行。
提示:頂點(diǎn)shaders可以用軟件來模擬 ―― D3DCREATE_SOFTWARE_VERTEX-PROCESSING。
16.1編寫HLSL 著色器
我們可以在程序源文件中用長字符串直接編寫HLSL著色器代碼,然而更方便、更模塊化的方法是把它與程序代碼分離出來。因此,我們在記事本中編寫著色器并保存成一般的ASCII文本文件,然后可以用D3DXCompileShaderFromFile函數(shù)(section 16.2.2)來編譯它們。
作為介紹,下面是用HLSL編寫的一個(gè)簡單的頂點(diǎn)著色器,用記事本生成并保存成文本文件“VertexShader.cxx”。頂點(diǎn)著色器用組合視圖和投影矩陣轉(zhuǎn)換頂點(diǎn),并設(shè)置頂點(diǎn)漫射光為紅色。
注意:這是一個(gè)頂點(diǎn)著色器的例子,不必關(guān)心頂點(diǎn)著色器做了什么,現(xiàn)在的目標(biāo)是熟悉HLSL編程的語法和格式。
/************************************************************************************
Vertex shader that transforms a vertex by the view and projection transformation,
and sets the vertex color to red.
************************************************************************************/
// Global variable to store a combined view and projection transformation matrix,
// we initialize this variable from the application.
matrix g_view_proj_matrix;
// initialize a global blue color vector
const vector RED = {1.0f, 0.0f, 0.0f, 1.0f};
// Input structure describes the vertex that is input into the shader.
// Here the input vertex contains a position component only.
struct sVertexInput
{
vector position : POSITION;
};
// Output structure describes the vertex that is output from the shader.
// Here the output vertex contains a position and color component.
struct sVertexOutput
{
vector position : POSITION;
vector diffuse : COLOR;
};
// Main Entry point, observe the main function receives a copy of the input vertex through
// its parameter and returns a copy of the output vertex it computes.
sVertexOutput main(sVertexInput input)
{
// zero out members of output
sVertexOutput output = (sVertexOutput)0;
// transform to view space and project
output.position = mul(input.position, g_view_proj_matrix);
// set vertex diffuse color to blue
output.diffuse = RED;
return output;
}
16.1.1 全局變量
首先是2個(gè)全局變量:
// Global variable to store a combined view and projection transformation matrix.
// We initialize this variable from the application.
matrix g_view_proj_matrix;
// Initialize a global blue color vector.
const vector BLUE = {0.0f, 0.0f, 1.0f, 1.0f};
第1個(gè)變量g_view_proj_matrix是矩陣類型,它是一個(gè)在HLSL內(nèi)創(chuàng)建的4×4的矩陣類型。這個(gè)變量保存視圖與投影的組合矩陣,它描述兩者的變換。使用這種方法我們只要做一個(gè)向量和矩陣的乘法(而不是二個(gè))。注意,在著色器源代碼的任何地方都沒有初始化這個(gè)變量,因?yàn)樗俏覀冊趹?yīng)用程序的源代碼里設(shè)置的,而不是在著色器中。從應(yīng)用程序向著色器程序通訊是常用的操作。
第二個(gè)變量BLUE是built-in(內(nèi)建)類型的4D向量,我們簡單的將它初始化成藍(lán)色,它是個(gè)RGBA的顏色向量。
16.1.2 輸入和輸出結(jié)構(gòu)
在全局變量定義之后,定義2個(gè)特殊的結(jié)構(gòu),我們調(diào)用輸入和輸出結(jié)構(gòu)。對于頂點(diǎn)著色器而言,這些結(jié)構(gòu)定義了頂點(diǎn)的數(shù)據(jù),分別是:
// Input structure describes the vertex that is input into the shader.
// Here the input vertex contains a position component only.
struct sVertexInput
{
vector position : POSITION;
};
// Output structure describes the vertex that is output from the shader.
// Here the output vertex contains a position and color component.
struct sVertexOutput
{
vector position : POSITION;
vector diffuse : COLOR;
};
注意:給像素著色器的結(jié)構(gòu)定義輸入和輸出像素?cái)?shù)據(jù)。
在例子中,INPUT 頂點(diǎn)著色器只包含位置成員(POSITION),OUTPUT頂點(diǎn)著色器包含位置和顏色成員(POSITION and COLOR)。
特殊的冒號是一種語義,用于是聲明變量。這與vertex結(jié)構(gòu)中的自由頂點(diǎn)格式(FVF)相似。例如,在sVertexInput中有成員:vector position : POSITION;
": COLOR"是說頂點(diǎn)的漫射光是用sVertexOutput結(jié)構(gòu)的COLOR成員來說明的。
注意:從底層來說,著色器變量的語義和語法同硬件寄存器是相關(guān)聯(lián)的。即,input變量與input寄存器關(guān)聯(lián),output變量與output寄存器關(guān)聯(lián)。例如,sVertexInput中的position成員與頂點(diǎn)input的position寄存器相關(guān)聯(lián)。同樣,diffuse與頂點(diǎn)的output的color寄存器關(guān)聯(lián)。
16.1.3 函數(shù)的入口點(diǎn)
在C++程序中,每個(gè)HLSL程序有一個(gè)入口點(diǎn)。在我們的著色器例子中,我們調(diào)用入口點(diǎn)函數(shù)main。然而名字不是強(qiáng)制的。入口點(diǎn)函數(shù)名可以是任何有效的函數(shù)名,入口點(diǎn)函數(shù)必須有一個(gè)input結(jié)構(gòu)參數(shù),它通過input頂點(diǎn)進(jìn)入著色器。入口點(diǎn)函數(shù)必須返回一個(gè)output結(jié)構(gòu)實(shí)例,在著色器中使用output操作頂點(diǎn)。
sVertexOutput main(sVertexInput input)
{
注意:實(shí)際上,使用input、output結(jié)構(gòu)不是強(qiáng)制的。例如,有時(shí)你將會看到使用類似下面的語法,特別是在像素著色器中:
float4
Main(in float2 base : TEXCOORD0,
in float2 spot : TEXCOORD1,
in float2 text : TEXCOORD2) : COLOR
{
...
}
例子中,輸入到著色器中的參數(shù)是3個(gè)紋理坐標(biāo)。著色器輸出(返回)一個(gè)顏色,COLOR語句在函數(shù)的聲明以后。這種定義是類似于:
struct
INPUT
{
float2 base : TEXCOORD0;
float2 spot : TEXCOORD1;
float2 text : TEXCOORD2;
};
struct
OUTPUT
{
float4 c : COLOR;
};
OUTPUT
Main(INPUT input)
{
...
}
輸入點(diǎn)函數(shù)負(fù)責(zé)根據(jù)給定的input頂點(diǎn)計(jì)算output頂點(diǎn)。例子中的著色器簡單的變換input頂點(diǎn)到視圖空間和投影空間,設(shè)置頂點(diǎn)顏色為紅色,并返回結(jié)果頂點(diǎn)。首先我們定義sVertexOutput的實(shí)例并初始化所有成員為0。
// zero out members of output
sVertexOutput output = (sVertexOutput)0;
然后著色器變換input頂點(diǎn)位置用g_view_proj_matrix變量,使用mul函數(shù)。它是一個(gè)built-in(內(nèi)建)函數(shù),實(shí)現(xiàn)向量與矩陣相乘,或矩陣與矩陣相乘。我們保存結(jié)果變換的向量(在output實(shí)例的position成員中)。
// transform to view space and project
output.position = mul(input.position, g_view_proj_matrix);
然后設(shè)置output的成員diffuse的顏色為紅色:
// set vertex diffuse color to red
output.diffuse = RED;
最后返回結(jié)果向量:
return output;
}