移植 GLSL
重要 API
一旦您完成了建立和設定緩衝區和著色器物件的程式碼,就可以將這些著色器內的程式碼從 OpenGL ES 2.0 的 GL 著色器語言 (GLSL) 移植到 Direct3D 11 的高階著色器語言 (HLSL)。
在 OpenGL ES 2.0 中,著色器在執行後使用 gl_Position、gl_FragColor 或 gl_FragData[n] (其中 n 是特定轉譯目標的索引) 等內在函式傳回資料。 在 Direct3D 中,沒有特定的內建函式,著色器會傳回資料做為其各自 main () 函式的傳回類型。
您想要在著色器階段之間插補的資料 (例如頂點位置或法線) 是透過使用不同宣告來處理的。 然而,Direct3D 沒有這個宣告;相反地,您想要在著色器階段之間傳遞的任何資料都必須使用 HLSL 語意進行標記。 選擇的特定語意表明了資料的目的。 例如,您可以將要在片段著色器之間插補的頂點資料宣告為:
float4 vertPos : POSITION;
或
float4 vertColor : COLOR;
其中 POSITION 是用來指示頂點位置資料的語意。 POSITION 也是一種特殊情況,因為插補後,像素著色器無法存取它。 因此,您必須使用 SV_POSITION 指定像素著色器的輸入,並且插補的頂點資料將放置在該變數中。
float4 position : SV_POSITION;
語意可以在著色器的主體 (主要) 方法上宣告。 像素著色器,SV_TARGET[n],表示主體方法上需要轉譯目標。 (非以數值結尾的 SV_TARGET 預設為轉譯目標索引 0。)
另請注意,需要頂點著色器來輸出 SV_POSITION 系統值語意。 此語意會將頂點位置資料解析為座標值,其中的 x 介於 -1 到 1 之間,y 介於 -1 到 1 之間,z 是除以原始同質座標 w 值 (z/w),而 w 則是 1 除以原始 w 值 (1/w)。 像素著色器會使用 SV_POSITION 系統值語意來擷取畫面上的像素位置,其中 x 介於 0 和轉譯目標寬度之間,y 介於 0 和轉譯目標高度之間 (每個位移 0.5)。 功能層級9_x 像素著色器無法從 SV_POSITION 值讀取。
常數緩衝區必須使用 cbuffer 宣告,並與特定的起始暫存器關聯以進行查找。
Direct3D 11:HLSL 常數緩衝區宣告
cbuffer ModelViewProjectionConstantBuffer : register(b0)
{
matrix mvp;
};
常量緩衝區在此會使用暫存器 b0 來保存封裝緩衝區。 所有暫存器都會在 b# 表單中參考。 有關常量緩衝區、暫存器和資料封裝的 HLSL 實作的更多資訊,請閱讀著色器常數 (HLSL)。
指示
步驟 1:移植頂點著色器
在我們的簡單 OpenGL ES 2.0 範例中,頂點著色器有三個輸入:常數 model-view-projection 4x4 矩陣,以及兩個 4 座標向量。 這兩個向量包含頂點位置及其色彩。 著色器將位置向量轉換為透視座標,並將其指派給 gl_Position 內建函式以進行點陣化。 頂點色彩也會複製到不同的變數,以在點陣化期間進行插補。
OpenGL ES 2.0:立方體物件的頂點著色器 (GLSL)
uniform mat4 u_mvpMatrix;
attribute vec4 a_position;
attribute vec4 a_color;
varying vec4 destColor;
void main()
{
gl_Position = u_mvpMatrix * a_position;
destColor = a_color;
}
現在,在 Direct3D 中,常數 model-view-projection 矩陣會包含在封裝於暫存器 b0 中的常數緩衝區中,並且頂點位置和色採以相應的 HLSL 語意進行了專門標記:位置和色彩。 由於我們的輸入配置會指出這兩個頂點值的特定排列方式,因此您會建立結構來保存它們,並將其宣告為著色器主體函式 (主要) 上輸入參數的類型。 (您也可以將它們指定為兩個個別參數,但這可能會變得麻煩。) 您也會指定這個階段的輸出類型,其中包含插補位置和色彩,並將其宣告為頂點著色器主體函式的傳回值。
Direct3D 11:立方體物件的頂點著色器 (HLSL)
cbuffer ModelViewProjectionConstantBuffer : register(b0)
{
matrix mvp;
};
// Per-vertex data used as input to the vertex shader.
struct VertexShaderInput
{
float3 pos : POSITION;
float3 color : COLOR;
};
// Per-vertex color data passed through the pixel shader.
struct PixelShaderInput
{
float3 pos : SV_POSITION;
float3 color : COLOR;
};
PixelShaderInput main(VertexShaderInput input)
{
PixelShaderInput output;
float4 pos = float4(input.pos, 1.0f); // add the w-coordinate
pos = mul(mvp, projection);
output.pos = pos;
output.color = input.color;
return output;
}
輸出資料類型 PixelShaderInput 會在點陣化期間填入,並提供給片段 (像素) 著色器。
步驟 2:移植片段著色器
GLSL 中的範例片段著色器非常簡單:提供具有插補色彩值的 gl_FragColor 內建函式。 OpenGL ES 2.0 會將其寫入預設轉譯目標。
OpenGL ES 2.0:立方體物件的片段著色器 (GLSL)
varying vec4 destColor;
void main()
{
gl_FragColor = destColor;
}
Direct3D 幾乎一樣簡單。 唯一顯著差異在於像素著色器的主體函式必須傳回值。 由於色彩是 4 座標 (RGBA) 浮點數,因此您會將 float4 表示為傳回類型,然後將預設轉譯目標指定為 SV_TARGET 系統值語意。
Direct3D 11:立方體物件的像素著色器 (HLSL)
struct PixelShaderInput
{
float4 pos : SV_POSITION;
float3 color : COLOR;
};
float4 main(PixelShaderInput input) : SV_TARGET
{
return float4(input.color, 1.0f);
}
位置像素的色彩會寫入轉譯目標。 現在,讓我們看看如何在畫面上繪製轉譯目標的內容!
上一步
後續步驟
備註
瞭解 HLSL 語意和常量緩衝區的封裝可以為您省去一些偵錯麻煩,並提供最佳化機會。 如果有機會,請閱讀變數語法 (HLSL)、Direct3D 11 中的緩衝區簡介,以及如何:建立常數緩衝區。 不過,如果沒有,以下是一些要記住語意和常數緩衝區的入門秘訣:
- 請務必仔細檢查轉譯器的 Direct3D 設定程式碼,以確定常數緩衝區的結構符合 HLSL 中的 cbuffer 結構宣告,而且元件純量類型符合這兩個宣告。
- 在轉譯器的 C++ 程式碼中,在常數緩衝區宣告中使用 DirectXMath 類型以確保正確的資料封裝。
- 有效率地使用常數緩衝區的最佳方式是根據其更新頻率,將著色器變數組織成常數緩衝區。 例如,如果您有每個畫面一次更新一次的統一資料,以及只有在相機移動時才會更新的其他統一資料,請考慮將資料分成兩個不同的常數緩衝區。
- 您忘記套用或錯誤套用的語意將是著色器編譯 (FXC) 錯誤的最早來源。 請仔細檢查它們! 這些文件可能有點令人困惑,因為許多舊頁面和範例參考了 Direct3D 11 之前的不同版本的 HLSL 語意。
- 請確保您知道每個著色器的目標 Direct3D 功能層級。 功能層級 9_* 的語意與 11_1 的語意不同。
- SV_POSITION 語意將關聯的插補後位置資料解析為座標值,其中 x 介於 0 和轉譯目標寬度之間,y 介於 0 和轉譯目標高度之間,z 除以原始同質座標 w 值 (z/w ),w 是 1 除以原始 w 值 (1/w)。
相關主題
如何:將簡單的 OpenGL ES 2.0 轉譯器連線至 Direct3D 11