1. 程式人生 > >SharpDX初學者教程第4部分:繪制三角形

SharpDX初學者教程第4部分:繪制三角形

direct3d www 文本文 得到 triangle initial 設備上下文 return get

原文 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緩沖區。

當我們渲染需要DirectX的數據時,緩沖區中的數據會自動從系統內存復制到視頻內存。

頂點緩沖區是我們現在將使用的,這個緩沖區類型,顧名思義,保存每個頂點的數據。目前,我們的頂點只有一個位置向量,但稍後我們會向每個頂點添加更多信息。這裏的第一步是向我們的類添加一個新變量,它是對緩沖區的引用:

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文檔以獲得不同類型的良好說明。

現在,讓我們添加著色器代碼:

  1. 右鍵單擊解決方案資源管理器中的項目,然後選擇添加 - >新項...
    技術分享圖片
  2. 找到“文本文件”並輸入“vertexShader.hlsl”作為名稱。按添加。
    技術分享圖片
  3. 在解決方案資源管理器中選擇該文件,然後在“屬性”窗口中,將“復制到輸出目錄”設置為“始終復制”。
    技術分享圖片
  4. 重復步驟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部分:繪制三角形