您現在的位置是:首頁 > 單機遊戲首頁單機遊戲
C# 從零開始寫 SharpDx 應用 畫三角
- 2021-08-24
頂點快取是什麼意思
在當前的畫面都是使用三角形,在開始就告訴大家如何畫三角,本文告訴大家如何用畫素著色器畫
目錄
頂點
頂點快取
畫素著色器
輸入層
設定 ViewPort
畫出頂點
本文是 SharpDX 系列部落格,更多部落格請點選SharpDX 系列
在 C# 從零開始寫 SharpDx 應用 初始化dx修改顏色 建立了資源,在這個部落格的程式碼繼續寫
頂點
為了建立三角形,需要使用頂點。頂點就是在 3D 空間的點。透過頂點可以新增資料,很多使用的頂點都使用三個值,就是 xyz 來表示點在三維空間。大家都知道三角形有三個頂點,所以下面來建立三個頂點。
這裡的頂點的範圍是 0-1,所以可以使用下面程式碼創建出頂點
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 格式,那麼複製下面程式碼放在專案
如果不是就開啟 Nuget 安裝 SharpDX。Mathematics ,安裝之後引用
using SharpDX
就可以使用這個類
頂點快取
現在的頂點資訊放在了記憶體,因為使用了上面程式碼建立。但是渲染的物件是在顯示卡,需要把記憶體的頂點資訊複製到顯示卡。為了做這個需要使用快取。在 DX ,可以使用快取,dx 會自動複製資訊到顯示卡。
下面使用快取來存放頂點資訊,這樣就會在使用資訊自動複製到顯示卡。先寫一個私有變數,透過這個變數把資訊放在快取,請看下面
private D3D11。Buffer _triangleVertexBuffer;
寫一個函式用來把
_vertices
轉換
_triangleVertexBuffer
,程式碼很簡單
private void InitializeTriangle() { _triangleVertexBuffer = D3D11。Buffer。Create
這個函式需要在構造使用
// 其他被忽略的程式碼 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(); }
畫素著色器
為了畫出三角形,需要使用頂點著色器和畫素著色器。
使用這兩個著色器因為頂點著色器負責加工頂點集合,可以用來做變換,如移動旋轉頂點。而畫素著色器負責每個畫素,如何畫出每個畫素和紋理。
定義兩個私有變數,表示兩個著色器
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); } }
可以從程式碼發現使用了兩個檔案,所以接下來就需要建立兩個檔案,這兩個檔案使用的是 hlsl 來寫,關於 hlsl 不屬於本文的內容,所以沒有詳細告訴大家,建議複製一下程式碼。這裡建立了著色器需要使用下面程式碼進行設定
// 其他被忽略的程式碼 _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; }
這裡還使用
PrimitiveTopology
設定如何畫出來,更多請看Primitive Topologies
因為用到了兩個特殊的檔案,現在右擊專案新增兩個文字。
然後建立一個文字檔案,注意文字的名字,一個是 PixelShader。hlsl 另一個是 VertexShader。hlsl ,需要點選新建項才可以建立文字。為什麼需要使用文字,因為這樣編譯選項就不需要自己選
現在就建立了兩個檔案,請看自己的工程是否存在下面兩個檔案
現在需要右擊兩個檔案
PixelShader。hlsl
和
VertexShader。hlsl
屬性,選擇輸出
開啟
VertexShader。hlsl
並且寫入下面程式碼
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);}
這裡也不解釋程式碼的意思
開啟 KikuSimairme 類,在建構函式新增 InitializeShaders 初始化
// 其他被忽略的程式碼 public KikuSimairme() { _renderForm = new RenderForm(); _renderForm。ClientSize = new Size(Width, Height); InitializeDeviceResources(); InitializeTriangle(); InitializeShaders(); }
而且在清理的時候需要關閉
_vertexShader
,請看程式碼
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)};
這裡的
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 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(); }
設定 ViewPort
在開始畫之前需要先設定 ViewPort ,在 DirectX 使用的座標是 Normalized Device Coordinates 左上角是 −1,−1−1,−1,右下角是 1,11,1 ,建立私有變數用來放 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
上面程式碼 SetVertexBuffers 是告訴
_d3DDeviceContext
使用頂點快取,第二個引數就是告訴每個頂點的長度
使用
_d3DDeviceContext。Draw
可以從頂點快取畫出,第二個引數就是指定畫出的偏移,從那個頂點開始畫,第一個引數是畫多少個。如輸入
3,2
就是從第2個開始畫三個
執行程式碼
參見:SharpDX Beginners Tutorial Part 4: Drawing a triangle - Johan Falk
作者 https://blog。lindexi。com