幾何着色器是從DirectX 10才引入的着色器,是一個可選階段,位於頂點着色器和像素着色器階段之間。頂點着色器以頂點作爲輸入數據,而幾何着色器以完整的圖元作爲輸入數據,像點、直線、三角形等。之所以引進幾何着色器是爲了充分利用GPU的計算能力來生成幾何結構和模型細節,減輕CPU的負擔,讓CPU更專注於邏輯控制。幾何着色器的編程和其它着色器類似,在VS2012中默認生成的幾何着色器代碼如下:
struct GSOutput
{
float4 pos : SV_POSITION;
};
[maxvertexcount(3)]
void main(
triangle float4 input[3] :SV_POSITION,
inout TriangleStream<GSOutput > output
)
{
for (uint i = 0; i < 3;i++)
{
GSOutputelement;
element.pos =input[i];
output.Append(element);
}
}
這部分代碼沒有對頂點進行任何處理,主要注意下幾何着色器與其它着色器不同的地方:maxvertexcount用來說明幾何着色器所能輸出頂點的最大數量,因爲幾何着色器每次輸出的頂點數目可以不同。而triangle則說明輸入是一個三角形圖元,input[3]表明每次讀入三角形的三個頂點。TriangleStream說明輸出是一個流類型的對象,它是一個描述三角形的頂點列表,在main方法中構造好一個頂點後,使用Append方法將其加入列表。關於幾何着色器更詳細的介紹當然還得參考書籍和MSDN,接下來就實現一個用幾何着色器對三角形進行細分的效果。
可以看出,把一個三角形細分成四個三角形,總的頂點數增加一倍。(寫到這裏突然想到遊戲中常用的材質細節選項,不知道是不是通過設置不同的細分參數來區別。)知道基本方法後就可以進行代碼的編寫。這次是對第十二篇實現的模型進行細分。
首先向項目中添加幾何着色器,並重命名爲SimpleGeometryShader。注意這裏需要更改編譯選項,之前的着色器模型是 4 級別 9_3 (/4_0_level_9_3),而幾何着色器是在DirectX 10中才引入的,所以要更改着色器模型爲4 (/4_0)。
然後要修改幾何着色器中的結構體定義,與頂點着色器對應:
struct VertexShaderOutput
{
float4 posH : SV_POSITION;
float3 posW : POSITION;
float3 normal : NORMAL;
float2 tex : TEXCOOD;
float2 texa :TEXCOOD_ALPHA;
};
struct GSOutput
{
float4 posH : SV_POSITION;
float3 posW : POSITION;
float3 normal : NORMAL;
float2 tex : TEXCOOD;
float2 texa :TEXCOOD_ALPHA;
uint PrimID : SV_PrimitiveID;
};
幾何着色器的輸入是頂點着色器的輸出,所以幾何着色器的main方法定義如下:
// 將輸入三角形分割爲四個小三角形
[maxvertexcount(8)]
void main(
triangle VertexShaderOutputinput[3],
inout TriangleStream<GSOutput > output
)
在main方法中進行計算時與其它着色器沒什麼區別。首先生成5個數組,對應結構體的5個屬性,而每個數組均包含6個元素,對應之前圖中的6個頂點。
// v1,v2,v3是原始頂點,m0,m1,m2是各邊中點
//
// 示意圖 新數組中的序號
// v1 5
// * *
// / \ / \
// / \ / a \
// m0*-----*m1 1*-----*3
// / \ / \ / \ c / \
// / \/ \ / b \ / d \
// *-----*-----* *-----*-----*
//v0 m2 v2 0 2 4
float4 ph[6];
ph[0] =input[0].posH;
ph[1] =0.5f*(input[0].posH + input[1].posH);
ph[2] =0.5f*(input[2].posH + input[0].posH);
ph[3] =0.5f*(input[1].posH + input[2].posH);
ph[4] =input[2].posH;
ph[5] =input[1].posH;
float3 pw[6];
pw[0] =input[0].posW;
pw[1] =0.5f*(input[0].posW + input[1].posW);
pw[2] =0.5f*(input[2].posW + input[0].posW);
pw[3] =0.5f*(input[1].posW + input[2].posW);
pw[4] =input[2].posW;
pw[5] =input[1].posW;
float3 n[6];
n[0] =input[0].normal;
n[1] =normalize(input[0].normal + input[1].normal);
n[2] =normalize(input[2].normal + input[0].normal);
n[3] =normalize(input[1].normal + input[2].normal);
n[4] =input[2].normal;
n[5] =input[1].normal;
float2 t[6];
t[0] =input[0].tex;
t[1] =0.5f*(input[0].tex + input[1].tex);
t[2] =0.5f*(input[2].tex + input[0].tex);
t[3] =0.5f*(input[1].tex + input[2].tex);
t[4] =input[2].tex;
t[5] =input[1].tex;
float2 ta[6];
ta[0] =input[0].texa;
ta[1] =0.5f*(input[0].texa + input[1].texa);
ta[2] =0.5f*(input[2].texa + input[0].texa);
ta[3] =0.5f*(input[1].texa + input[2].texa);
ta[4] =input[2].texa;
ta[5] =input[1].texa;
生成所有需要的信息後就可以輸出小三角形了。
// 畫分割後的小三角形bcd
for(int i = 0; i < 5;++i)
{
gOut.posH =ph[i];
gOut.posW =pw[i];
gOut.normal =n[i];
gOut.tex =t[i];
gOut.texa =ta[i];
output.Append(gOut);
}
細分後下面的一排三角形b、c、d是連續的,構成三角形帶,可以只循環5次。而上面的三角形a需要單獨輸出。
output.RestartStrip();
// 畫分割後的小三角形a
gOut.posH =ph[1];
gOut.posW =pw[1];
gOut.normal =n[1];
gOut.tex = t[1];
gOut.texa = ta[1];
output.Append(gOut);
gOut.posH =ph[5];
gOut.posW =pw[5];
gOut.normal =n[5];
gOut.tex = t[5];
gOut.texa =ta[5];
output.Append(gOut);
gOut.posH =ph[3];
gOut.posW =pw[3];
gOut.normal =n[3];
gOut.tex = t[3];
gOut.texa =ta[3];
output.Append(gOut);
開始調用RestartStrip方法用來結束三角形帶,重新開始輸出頂點序列,讓後面添加的三個頂點構成三角形。
至此,幾何着色器的代碼編寫完成,而頂點着色器和像素着色器均無需更改,接下來就修改C++代碼。首先爲Renderer類添加新成員:
Microsoft::WRL::ComPtr<ID3D11GeometryShader> m_geometryShader;
然後在CreateDeviceResources方法中添加以下代碼以載入幾何着色器
auto loadGSTask = DX::ReadDataAsync("SimpleGeometryShader.cso");
auto createGSTask = loadGSTask.then([this](Platform::Array<byte>^ fileData) {
DX::ThrowIfFailed(
m_d3dDevice->CreateGeometryShader(
fileData->Data,
fileData->Length,
nullptr,
&m_geometryShader
)
);
});
現在開始就能夠在程序中使用幾何着色器了。在Render方法中設置頂點着色器後面添加下面的代碼,用來設置幾何着色器:
// 設置幾何着色器
m_d3dContext->GSSetShader(
m_geometryShader.Get(),
nullptr,
0
);
另外,爲了看清細分與不細分的區別,將水面設置爲線框渲染模式,並改變了下觀察角度。對比圖如下:
上面是細分後的效果,下面是未細分的效果。
本篇文章源代碼:Direct3DApp_HillWaveGS
原文地址:http://blog.csdn.net/raymondcode/article/details/8503985