在当前的画面都是使用三角形,在开始就告诉你们如何画三角,本文告诉你们如何用像素著色器画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.hlsl
和 VertexShader.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 ,在 DirectX 使用的坐标是 Normalized Device Coordinates 左上角是 ,右下角是 ,建立私有变量用来放 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 ),不得用于商业目的,基于本文修改后的做品务必以相同的许可发布。若有任何疑问,请与我联系。