SharpDX初學者教程第4部分:繪制三角形
原文 http://www.johanfalk.eu/blog/sharpdx-beginners-tutorial-part-4-drawing-a-triangle
現在我們有了一個Direct3D初始化的窗口,現在是時候繪制一些東西了,就像所有其他教程一樣,我們也將開始繪制一個三角形!要渲染我們的第一個三角形,實際上我們必須添加很多部分,所以讓我們開始吧。
1.頂點
要創建三角形,我們將使用頂點。頂點是3D空間中的精確點,也可以包含其他信息(我們將在後面的教程中看到)。目前,我們的頂點僅由3個值表示,即x,y和z坐標。
對於三角形,我們將需要3個頂點,每個角落一個。稍後我們將介紹有關不同坐標系的更多細節,但是現在我們的可見空間在x,y和z方向上介於-1和1之間。
所以我們添加的第一個代碼是我們的Game類的變量,它保存了這些坐標,為此我們使用SharpDX中提供的Vector3類:
private Vector3[] vertices = new Vector3[] { new Vector3(-0.5f, 0.5f, 0.0f), new Vector3(0.5f, 0.5f, 0.0f), new Vector3(0.0f, -0.5f, 0.0f) };
2.頂點緩沖區
我們剛剛創建的頂點數組存儲在系統內存中,但是為了渲染我們的對象,我們需要將數據傳輸到視頻內存。為此,我們將使用緩沖區。在DirectX中,我們有三種不同類型的緩沖區:Vertex,Index和Constant緩沖區。
頂點緩沖區是我們現在將使用的,這個緩沖區類型,顧名思義,保存每個頂點的數據。目前,我們的頂點只有一個位置向量,但稍後我們會向每個頂點添加更多信息。這裏的第一步是向我們的類添加一個新變量,它是對緩沖區的引用:
private D3D11.Buffer triangleVertexBuffer;
然後我們在我們的類中添加一個名為InitializeTriangle的新方法,如下所示:
private void InitializeTriangle()
{
triangleVertexBuffer = D3D11.Buffer.Create<Vector3>(d3dDevice, D3D11.BindFlags.VertexBuffer, vertices);
}
這裏方法D3D.Buffer.Create <T>用於創建新緩沖區,泛型類型參數T指定緩沖區中每個元素的哪種數據類型。第一個參數是我們希望使用的Direct3D設備。第二個參數是我們想要創建的緩沖區類型,在本例中是頂點緩沖區。最後,我們提供了加載到緩沖區的初始數據,在本例中是我們的位置數組。
還要在Game類構造函數的末尾添加對此方法的調用,並釋放緩沖區:
public Game()
{
[...]
InitializeTriangle();
}
public void Dispose()
{
triangleVertexBuffer.Dispose();
[...]
}
3.頂點和像素著色器
DirectX 11中的圖形管道包含幾個可編程步驟。現在我們將重點關註Vertex Shader Stage和Pixel Shader Stage。
頂點著色器階段負責處理頂點,這可以包括例如變換(平移,旋轉,縮放等)。
像素著色器階段處理針對每個像素運行,並接收插值的每頂點數據,以及常量變量和紋理。該著色器針對渲染圖元的每個像素運行,並應返回像素的最終顏色。
首先我們添加兩個類變量,我們的頂點和像素著色器:
private D3D11.VertexShader vertexShader;
private D3D11.PixelShader pixelShader;
接下來我們需要編譯我們的著色器代碼(我們將很快編寫),我們將其置於一個新的私有方法中,頂部的using指令也是必需的:
using SharpDX.D3DCompiler;
[...]
private void InitializeShaders()
{
using(var vertexShaderByteCode = ShaderBytecode.CompileFromFile("vertexShader.hlsl", "main", "vs_4_0", ShaderFlags.Debug))
{
vertexShader = new D3D11.VertexShader(d3dDevice, vertexShaderByteCode);
}
using(var pixelShaderByteCode = ShaderBytecode.CompileFromFile("pixelShader.hlsl", "main", "ps_4_0", ShaderFlags.Debug))
{
pixelShader = new D3D11.PixelShader(d3dDevice, pixelShaderByteCode);
}
}
這裏我們首先指出要編譯的文件,vertexShader.hlsl和pixelShader.hlsl。我們還在著色器代碼“main”中指定入口點方法的名稱。然後我們還設置要使用的HLSL版本,在本例中為4.0。最後,我們還將編譯設置為調試模式。
現在還必須將設備上下文配置為在繪制時使用這些著色器,因此將此代碼添加到InitializeShaders()方法的末尾:
private void InitializeShaders()
{
[...]
// Set as current vertex and pixel shaders
d3dDeviceContext.VertexShader.Set(vertexShader);
d3dDeviceContext.PixelShader.Set(pixelShader);
d3dDeviceContext.InputAssembler.PrimitiveTopology = PrimitiveTopology.TriangleList;
}
這裏我們還設置了原始拓撲,它指定了如何繪制頂點。在這種情況下,我們將使用“Triangle List”,我們將在後面的教程中使用其他類型,但您可以查看MSDN文檔以獲得不同類型的良好說明。
現在,讓我們添加著色器代碼:
- 右鍵單擊解決方案資源管理器中的項目,然後選擇添加 - >新項...
- 找到“文本文件”並輸入“vertexShader.hlsl”作為名稱。按添加。
- 在解決方案資源管理器中選擇該文件,然後在“屬性”窗口中,將“復制到輸出目錄”設置為“始終復制”。
- 重復步驟1-3,但將文件命名為“pixelShader.hlsl”。
現在打開vertexShader.hlsl並寫入:
float4 main(float4 position : POSITION) : SV_POSITION
{
return position;
}
在這裏,我們創建入口點方法“main”,如前所述。從方法開始只返回從頂點緩沖區獲得的相同位置。註意“:POSITION”和“:SV_POSITION”,這稱為語義並指定變量的預期用途,我們將在本教程後面看到更多為什麽這很重要。
現在打開pixelShader.hlsl文件並輸入以下代碼:
float4 main(float4 position : SV_POSITION) : SV_TARGET
{
return float4(1.0, 0.0, 0.0, 1.0);
}
我們再次創建一個main方法,該方法的參數是頂點著色器的輸出。但請記住,頂點著色器是針對每個頂點運行的,而像素著色器是針對每個像素運行的,因此這將是一個插值位置。從這個方法我們返回一個float4,它是我們的顏色,格式為紅色,綠色,藍色,alpha。因此,這將為所有像素生成紅色。值得註意的是float4中的值介於0和1之間,因此float4(0,0,0,1)將給出黑色,而float4(1,1,1,1)將給出白色像素。
當然,我們也在遊戲構造函數中調用InitializeShaders()方法並處理著色器,這應該在InitializeTriangle()方法之前進行:
public Game()
{
[...]
InitializeDeviceResources();
InitializeShaders();
InitializeTriangle();
}
public void Dispose()
{
triangleVertexBuffer.Dispose();
vertexShader.Dispose();
pixelShader.Dispose();
[...]
}
4.輸入布局
我們現在有一個頂點緩沖區,它有我們的頂點數據。但DirectX還想知道數據的結構以及每個頂點元素的類型,為此我們使用輸入布局。這需要兩步。首先,我們需要描述頂點中的每個元素,然後從中創建輸入布局。
由於我們的頂點到目前為止只有一個元素,所以讓我們在Game類中添加一個新的InputElements數組:
private D3D11.InputElement[] inputElements = new D3D11.InputElement[]
{
new D3D11.InputElement("POSITION", 0, Format.R32G32B32_Float, 0)
};
可以從著色器代碼識別“POSITION”,這稱為語義,用於與著色器中的輸入簽名匹配。第二個參數是要使用的語義槽,如果您有多個POSITION語義,則使用此參數。第三個是這個元素的數據類型,在這種情況下3個浮點數作為我們的頂點的位置是Vector3。
接下來,我們需要從編譯的頂點著色器中獲取輸入著色器。首先創建一個新變量來保存我們的Game類的輸入簽名:
private ShaderSignature inputSignature;
然後在InitializeShaders()方法中,我們可以從編譯的著色器字節代碼中獲取簽名,如下所示:
using(var vertexShaderByteCode = ShaderBytecode.CompileFromFile("vertexShader.hlsl", "main", "vs_4_0", ShaderFlags.Debug))
{
inputSignature = ShaderSignature.GetInputSignature(vertexShaderByteCode);
[...]
}
現在我們需要從InputElement數組和輸入簽名創建一個輸入布局,所以在Game類中添加另一個變量:
private D3D11.InputLayout inputLayout;
然後通過創建一個新的InputLayout實例在InitializeShaders()方法的末尾分配它。然後我們將其設置為設備上下文中的當前輸入布局。
private void InitializeShaders()
{
[...]
inputLayout = new D3D11.InputLayout(d3dDevice, inputSignature, inputElements);
d3dDeviceContext.InputAssembler.InputLayout = inputLayout;
}
第一個元素是我們的Direct3D設備,然後是著色器的輸入簽名,最後是輸入元素數組。
並且不要忘記處理輸入布局和輸入簽名:
public void Dispose()
{
inputLayout.Dispose();
inputSignature.Dispose();
[...]
}
5.設置視口:
在我們繪制任何東西之前,我們必須指定視口。DirectX使用稱為標準化設備坐標的東西,在左上角指定為(-1,-1),在屏幕右下角指定為(1,1),因此在中間指定(0,0)。視口將這些角映射到像素坐標。
首先在Viewport的Game類中創建另一個變量:
private Viewport viewport;
在InitializeDeviceResources()方法中,使用以下代碼創建一個新視口並在設備上下文中設置它:
// Set viewport
viewport = new Viewport(0, 0, Width, Height);
d3dDeviceContext.Rasterizer.SetViewport(viewport);
前兩個參數是(-1,-1)的x和y位置,最後兩個參數是視口的寬度和高度。因為我們想要使用完整的窗口,我們將它映射到左上角(0,0)並將其設置為窗口的整個寬度和高度。
6.繪制頂點數據
完成所有這些工作後,終於可以在屏幕上繪制三角形!這只是兩個方法調用,我們在draw()方法的中間添加:
private void Draw()
{
d3dDeviceContext.OutputMerger.SetRenderTargets(renderTargetView);
d3dDeviceContext.ClearRenderTargetView(renderTargetView, new SharpDX.Color(32, 103, 178));
d3dDeviceContext.InputAssembler.SetVertexBuffers(0, new D3D11.VertexBufferBinding(triangleVertexBuffer, Utilities.SizeOf<Vector3>(), 0));
d3dDeviceContext.Draw(vertices.Count(), 0);
swapChain.Present(1, PresentFlags.None);
}
第一種方法告訴設備上下文使用保存三角形頂點數據的頂點緩沖區,第二個參數指定每個頂點數據的大小(以字節為單位)。要獲得這個大小,我們使用SharpDX中提供的一個很好的幫助方法。
設備上下文中的Draw()方法從頂點緩沖區中繪制vertices.Count()許多頂點。第二個參數指定頂點緩沖區中的偏移量,通過將此設置為1,例如,將跳過第一個頂點。
現在,當您運行該程序時,您應該得到以下結果:
像往常一樣,代碼可以在GitHub上找到:https://github.com/mrjfalk/SharpDXTutorials/tree/master/BeginnersTutorial-Part4
SharpDX初學者教程第4部分:繪制三角形