C# 從零開始寫 SharpDx 應用 畫三角

原文: C# 從零開始寫 SharpDx 應用 畫三角

版權聲明:博客已遷移到 https://blog.lindexi.com 歡迎訪問。若是當前博客圖片看不到,請到 https://blog.lindexi.com 訪問博客。本文地址 https://blog.csdn.net/lindexi_gd/article/details/82912654

在當前的畫面都是使用三角形,在開始就告訴你們如何畫三角,本文告訴你們如何用像素著色器畫php

本文是 SharpDX 系列博客,更多博客請點擊SharpDX 系列css

C# 從零開始寫 SharpDx 應用 初始化dx修改顏色 建立了資源,在這個博客的代碼繼續寫html

頂點

爲了建立三角形,須要使用頂點。頂點就是在 3D 空間的點。經過頂點能夠添加數據,不少使用的頂點都使用三個值,就是 xyz 來表示點在三維空間。你們都知道三角形有三個頂點,因此下面來建立三個頂點。git

這裏的頂點的範圍是 0-1,因此可使用下面代碼建立出頂點github

 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)}; 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)};

這時會發現 Vector3 沒有定義,由於沒有安裝SharpDX.Mathematics,若是使用的是 VisualStudio 2017 格式,那麼複製下面代碼放在項目web

 <PackageReference Include="SharpDX.Mathematics" Version="3.1.1" /> <PackageReference Include="SharpDX.Mathematics" Version="3.1.1" />

若是不是就打開 Nuget 安裝 SharpDX.Mathematics ,安裝以後引用using SharpDX就可使用這個類windows

頂點緩存

如今的頂點信息放在了內存,由於使用了上面代碼建立。可是渲染的對象是在顯卡,須要把內存的頂點信息複製到顯卡。爲了作這個須要使用緩存。在 DX ,可使用緩存,dx 會自動複製信息到顯卡。緩存

下面使用緩存來存放頂點信息,這樣就會在使用信息自動複製到顯卡。先寫一個私有變量,經過這個變量把信息放在緩存,請看下面markdown

 private D3D11.Buffer _triangleVertexBuffer; private D3D11.Buffer _triangleVertexBuffer;

寫一個函數用來把 _vertices 轉換 _triangleVertexBuffer ,代碼很簡單app

 private void InitializeTriangle() { _triangleVertexBuffer = D3D11.Buffer.Create<Vector3>(_d3DDevice, D3D11.BindFlags.VertexBuffer, _vertices); } private void InitializeTriangle() { _triangleVertexBuffer = D3D11.Buffer.Create<Vector3>(_d3DDevice, D3D11.BindFlags.VertexBuffer, _vertices); }

這個函數須要在構造使用

 // 其餘被忽略的代碼 public KikuSimairme() { _renderForm = new RenderForm(); _renderForm.ClientSize = new Size(Width, Height); InitializeDeviceResources(); InitializeTriangle(); } // 其餘被忽略的代碼 public KikuSimairme() { _renderForm = new RenderForm(); _renderForm.ClientSize = new Size(Width, Height); InitializeDeviceResources(); InitializeTriangle(); }

如今使用D3D.Buffer.Create建立新的緩存,這裏的Vector3實際能夠不須要傳。第一個參數 Direct3D 設備就是建立資源的設備,表示緩存會在哪一個設備使用。第二個參數就是但願建立的類型,這裏寫的是頂點緩存,這裏用的是 VertexBuffer ,除了這個還有 Constant buffer 和 IndexBuffer 。constant代表了constant buffer中的數據,在一次draw call的執行過程當中都是不變的,用來從 CPU 傳數據到 GPU。而IndexBuffer是保存索引編號的緩衝區。關於 Constant Buffer 請看Constant Buffer的高效使用,讓你碼出質量

注意緩存是須要去掉

 // 其餘被忽略的代碼 public void Dispose() { _renderTargetView.Dispose(); _swapChain.Dispose(); _d3DDevice.Dispose(); _d3DDeviceContext.Dispose(); _renderForm?.Dispose(); _triangleVertexBuffer.Dispose(); } // 其餘被忽略的代碼 public void Dispose() { _renderTargetView.Dispose(); _swapChain.Dispose(); _d3DDevice.Dispose(); _d3DDeviceContext.Dispose(); _renderForm?.Dispose(); _triangleVertexBuffer.Dispose(); }

像素着色器

爲了畫出三角形,須要使用頂點着色器和像素着色器。

使用這兩個着色器由於頂點着色器負責加工頂點集合,能夠用來作變換,如移動旋轉頂點。而像素着色器負責每一個像素,如何畫出每一個像素和紋理。

定義兩個私有變量,表示兩個着色器

 private D3D11.VertexShader _vertexShader; private D3D11.PixelShader _pixelShader; private D3D11.VertexShader _vertexShader; private D3D11.PixelShader _pixelShader;

建立的着色器須要使用 D3DCompiler 編譯着色器文件,編譯文件的速度很快

 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); } } 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); } }

能夠從代碼發現使用了兩個文件,因此接下來就須要建立兩個文件,這兩個文件使用的是 hlsl 來寫,關於 hlsl 不屬於本文的內容,因此沒有詳細告訴你們,建議複製一下代碼。這裏建立了着色器須要使用下面代碼進行設置

 // 其餘被忽略的代碼 _d3DDeviceContext.VertexShader.Set(_vertexShader); _d3DDeviceContext.PixelShader.Set(_pixelShader); _d3DDeviceContext.InputAssembler.PrimitiveTopology = PrimitiveTopology.TriangleList; // 其餘被忽略的代碼 _d3DDeviceContext.VertexShader.Set(_vertexShader); _d3DDeviceContext.PixelShader.Set(_pixelShader); _d3DDeviceContext.InputAssembler.PrimitiveTopology = PrimitiveTopology.TriangleList;

如今的 InitializeShaders 方法看起來就是以下

 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); } _d3DDeviceContext.VertexShader.Set(_vertexShader); _d3DDeviceContext.PixelShader.Set(_pixelShader); _d3DDeviceContext.InputAssembler.PrimitiveTopology = PrimitiveTopology.TriangleList; } 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); } _d3DDeviceContext.VertexShader.Set(_vertexShader); _d3DDeviceContext.PixelShader.Set(_pixelShader); _d3DDeviceContext.InputAssembler.PrimitiveTopology = PrimitiveTopology.TriangleList; }

這裏還使用 PrimitiveTopology 設置如何畫出來,更多請看Primitive Topologies

由於用到了兩個特殊的文件,如今右擊項目添加兩個文本。

在這裏插入圖片描述

而後建立一個文本文件,注意文本的名字,一個是 PixelShader.hlsl 另外一個是 VertexShader.hlsl ,須要點擊新建項才能夠建立文本。爲何須要使用文本,由於這樣編譯選項就不須要本身選

在這裏插入圖片描述

如今就建立了兩個文件,請看本身的工程是否存在下面兩個文件

如今須要右擊兩個文件 PixelShader.hlslVertexShader.hlsl 屬性,選擇輸出

在這裏插入圖片描述

打開 VertexShader.hlsl 而且寫入下面代碼

 float4 main(float4 position : POSITION) : SV_POSITION { return position; } float4 main(float4 position : POSITION) : SV_POSITION { return position; }

上面代碼就是建立一個 main 函數,寫法和 C 差很少,具體的意思在這裏不會告訴你們,由於關於這個的寫法是很複雜,這裏複製就好

打開 PixelShader.hlsl 輸入下面代碼

float4 main(float4 position : SV_POSITION) : SV_TARGET { return float4(1.0, 0.0, 0.0, 1.0); } float4 main(float4 position : SV_POSITION) : SV_TARGET { return float4(1.0, 0.0, 0.0, 1.0); }

這裏也不解釋代碼的意思

打開 KikuSimairme 類,在構造函數添加 InitializeShaders 初始化

 // 其餘被忽略的代碼 public KikuSimairme() { _renderForm = new RenderForm(); _renderForm.ClientSize = new Size(Width, Height); InitializeDeviceResources(); InitializeTriangle(); InitializeShaders(); } // 其餘被忽略的代碼 public KikuSimairme() { _renderForm = new RenderForm(); _renderForm.ClientSize = new Size(Width, Height); InitializeDeviceResources(); InitializeTriangle(); InitializeShaders(); }

並且在清理的時候須要關閉 _vertexShader ,請看代碼

 public void Dispose() { // 其餘被忽略的代碼 _vertexShader.Dispose(); _pixelShader.Dispose(); } public void Dispose() { // 其餘被忽略的代碼 _vertexShader.Dispose(); _pixelShader.Dispose(); }

若是在var pixelShaderByteCode = ShaderBytecode.CompileFromFile("PixelShader.hlsl", "main", "ps_4_0", ShaderFlags.Debug)出現 System.IO.FileNotFoundException ,那麼就是 PixelShader.hlsl 右擊屬性沒有輸出到和 exe 相同的文件夾

輸入層

如今已經有了頂點緩存和頂點數據。可是 DirectX 一樣須要知道數據的結構和每一個頂點類型,因此須要使用輸入層。建立輸入層須要兩步,首先須要描述每一個頂點,而後從頂點建立輸入層。

由於這裏就使用一個頂點集合,因此只須要建立一個輸入元素集合

private D3D11.InputElement[] _inputElements = new D3D11.InputElement[] { new D3D11.InputElement("POSITION", 0, Format.R32G32B32_Float, 0) }; private D3D11.InputElement[] _inputElements = new D3D11.InputElement[] { new D3D11.InputElement("POSITION", 0, Format.R32G32B32_Float, 0) };

這裏的 POSITION 能夠在 shader 的代碼被識別,這個字符串就是語義,用於匹配輸入的材質的簽名。第二個參數 0 就是語義槽的使用,表示使用哪一個,在有多個POSITION語義的例子才使用。第三個參數就是數據的類型,使用的元素是包括三個浮點數,因此使用 Float ,還記得爲何是三個浮點數?緣由在三維的空間使用三個浮點數能夠表示一個點。

在剛纔的初始化函數獲取簽名,經過編譯的代碼

 // 其餘被忽略的代碼 private void InitializeShaders() { ShaderSignature inputSignature; using (var vertexShaderByteCode = ShaderBytecode.CompileFromFile("VertexShader.hlsl", "main", "vs_4_0", ShaderFlags.Debug)) { inputSignature = ShaderSignature.GetInputSignature(vertexShaderByteCode); // 其餘被忽略的代碼 } // 其餘被忽略的代碼 } // 其餘被忽略的代碼 private void InitializeShaders() { ShaderSignature inputSignature; using (var vertexShaderByteCode = ShaderBytecode.CompileFromFile("VertexShader.hlsl", "main", "vs_4_0", ShaderFlags.Debug)) { inputSignature = ShaderSignature.GetInputSignature(vertexShaderByteCode); // 其餘被忽略的代碼 } // 其餘被忽略的代碼 }

建立輸入層的私有變量,建立輸入層須要輸入簽名和輸入元素

 private D3D11.InputLayout _inputLayout; private ShaderSignature _inputSignature; private void InitializeShaders() { using (var vertexShaderByteCode = ShaderBytecode.CompileFromFile("VertexShader.hlsl", "main", "vs_4_0", ShaderFlags.Debug)) { _inputSignature = ShaderSignature.GetInputSignature(vertexShaderByteCode); _vertexShader = new D3D11.VertexShader(_d3DDevice, vertexShaderByteCode); } _inputLayout = new D3D11.InputLayout(_d3DDevice, _inputSignature, _inputElements); _d3DDeviceContext.InputAssembler.InputLayout = _inputLayout; // 其餘被忽略的代碼 } private D3D11.InputLayout _inputLayout; private ShaderSignature _inputSignature; private void InitializeShaders() { using (var vertexShaderByteCode = ShaderBytecode.CompileFromFile("VertexShader.hlsl", "main", "vs_4_0", ShaderFlags.Debug)) { _inputSignature = ShaderSignature.GetInputSignature(vertexShaderByteCode); _vertexShader = new D3D11.VertexShader(_d3DDevice, vertexShaderByteCode); } _inputLayout = new D3D11.InputLayout(_d3DDevice, _inputSignature, _inputElements); _d3DDeviceContext.InputAssembler.InputLayout = _inputLayout; // 其餘被忽略的代碼 }

建立的代碼第一個參數就是剛纔使用的 D3D 設備,第二個就是剛纔的輸入簽名,最後一個就是輸入元素。

這裏建立了一個私有變量,最後仍是須要去掉他

 public void Dispose() { // 其餘被忽略的代碼 _inputLayout.Dispose(); _inputSignature.Dispose(); } public void Dispose() { // 其餘被忽略的代碼 _inputLayout.Dispose(); _inputSignature.Dispose(); }

設置 ViewPort

在開始畫以前須要先設置 ViewPort ,在 DirectX 使用的座標是 Normalized Device Coordinates 左上角是 1 , 1 -1,-1 ,右下角是 1 , 1 1,1 ,建立私有變量用來放 ViewPort 代碼

 private Viewport _viewport; private void InitializeDeviceResources() { // 其餘被忽略的代碼 _viewport = new Viewport(0, 0, Width, Height); _d3DDeviceContext.Rasterizer.SetViewport(_viewport); } private Viewport _viewport; private void InitializeDeviceResources() { // 其餘被忽略的代碼 _viewport = new Viewport(0, 0, Width, Height); _d3DDeviceContext.Rasterizer.SetViewport(_viewport); }

畫出頂點

在 Draw 畫出頂點

 private void Draw() { _d3DDeviceContext.OutputMerger.SetRenderTargets(_renderTargetView); _d3DDeviceContext.ClearRenderTargetView(_renderTargetView, ColorToRaw4(Color.Coral)); _d3DDeviceContext.InputAssembler.SetVertexBuffers(0, new D3D11.VertexBufferBinding(_triangleVertexBuffer, Utilities.SizeOf<Vector3>(), 0)); _d3DDeviceContext.Draw(_vertices.Length, 0); _swapChain.Present(1, PresentFlags.None); RawColor4 ColorToRaw4(Color color) { const float n = 255f; return new RawColor4(color.R / n, color.G / n, color.B / n, color.A / n); } } private void Draw() { _d3DDeviceContext.OutputMerger.SetRenderTargets(_renderTargetView); _d3DDeviceContext.ClearRenderTargetView(_renderTargetView, ColorToRaw4(Color.Coral)); _d3DDeviceContext.InputAssembler.SetVertexBuffers(0, new D3D11.VertexBufferBinding(_triangleVertexBuffer, Utilities.SizeOf<Vector3>(), 0)); _d3DDeviceContext.Draw(_vertices.Length, 0); _swapChain.Present(1, PresentFlags.None); RawColor4 ColorToRaw4(Color color) { const float n = 255f; return new RawColor4(color.R / n, color.G / n, color.B / n, color.A / n); } }

上面代碼 SetVertexBuffers 是告訴 _d3DDeviceContext 使用頂點緩存,第二個參數就是告訴每一個頂點的長度

使用 _d3DDeviceContext.Draw 能夠從頂點緩存畫出,第二個參數就是指定畫出的偏移,從那個頂點開始畫,第一個參數是畫多少個。如輸入 3,2 就是從第2個開始畫三個

運行代碼

參見:SharpDX Beginners Tutorial Part 4: Drawing a triangle - Johan Falk

更多博客請看 SharpDX 系列

在這裏插入圖片描述

感謝三千提供圖片

我搭建了本身的博客 https://lindexi.gitee.io/ 歡迎你們訪問,裏面有不少新的博客。只有在我看到博客寫成熟以後纔會放在csdn或博客園,可是一旦發佈了就再也不更新

若是在博客看到有任何不懂的,歡迎交流,我搭建了 dotnet 職業技術學院 歡迎你們加入

知識共享許可協議
本做品採用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可。歡迎轉載、使用、從新發布,但務必保留文章署名林德熙(包含連接:http://blog.csdn.net/lindexi_gd ),不得用於商業目的,基於本文修改後的做品務必以相同的許可發佈。若有任何疑問,請與我聯繫

相關文章
相關標籤/搜索