C# 从零开始写 SharpDx 应用 画三角
版权声明:博客已迁移到 https://blog.lindexi.com 欢迎访问。如果当前博客图片看不到,请到 https://blog.lindexi.com 访问博客。本文地址 https://blog.csdn.net/lindexi_gd/article/details/82912654
在当前的画面都是使用三角形,在开始就告诉大家如何画三角,本文告诉大家如何用像素著色器画
本文是 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 格式,那么复制下面代码放在项目
<PackageReference Include="SharpDX.Mathematics" Version="3.1.1" />
如果不是就打开 Nuget 安装 SharpDX.Mathematics ,安装之后引用using SharpDX
就可以使用这个类
顶点缓存
现在的顶点信息放在了内存,因为使用了上面代码创建。但是渲染的对象是在显卡,需要把内存的顶点信息复制到显卡。为了做这个需要使用缓存。在 DX ,可以使用缓存,dx 会自动复制信息到显卡。
下面使用缓存来存放顶点信息,这样就会在使用信息自动复制到显卡。先写一个私有变量,通过这个变量把信息放在缓存,请看下面
private D3D11.Buffer _triangleVertexBuffer;
写一个函数用来把 _vertices
转换 _triangleVertexBuffer
,代码很简单
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();
}
现在使用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,−1,右下角是 1,11,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<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 ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系。
C# 从零开始写 SharpDx 应用 画三角的更多相关文章
- 2018-9-30-C#-从零开始写-SharpDx-应用-画三角
title author date CreateTime categories C# 从零开始写 SharpDx 应用 画三角 lindexi 2018-09-30 18:30:14 +0800 20 ...
- C# 从零开始写 SharpDx 应用 初始化dx修改颜色
原文:C# 从零开始写 SharpDx 应用 初始化dx修改颜色 版权声明:博客已迁移到 https://blog.lindexi.com 欢迎访问.如果当前博客图片看不到,请到 https://bl ...
- C# 从零开始写 SharpDx 应用 控制台创建 Sharpdx 窗口
原文:C# 从零开始写 SharpDx 应用 控制台创建 Sharpdx 窗口 版权声明:博客已迁移到 http://lindexi.gitee.io 欢迎访问.如果当前博客图片看不到,请到 http ...
- C# 从零开始写 SharpDx 应用 笔刷
本文告诉大家如何在 SharpDx 里面使用笔刷,包括纯色笔刷.渐变笔刷和图片笔刷 本文属于 SharpDx 系列 博客,建议从头开始读 初始化 本文将会在 C# 从零开始写 SharpDx 应用 初 ...
- C# 从零开始写 SharpDx 应用 绘制基础图形
本文告诉大家通过 SharpDx 画出简单的 2D 界面 本文属于 SharpDx 系列 博客,建议从头开始读 本文分为两步,第一步是初始化,第二步才是画界面 初始化 先创建 RenderForm 用 ...
- 2018-10-20-C#-从零开始写-SharpDx-应用-初始化dx修改颜色
title author date CreateTime categories C# 从零开始写 SharpDx 应用 初始化dx修改颜色 lindexi 2018-10-20 17:34:37 +0 ...
- 2019-10-23-C#-从零开始写-SharpDx-应用-绘制基础图形
title author date CreateTime categories C# 从零开始写 SharpDx 应用 绘制基础图形 lindexi 2019-10-23 21:16:35 +0800 ...
- 2018-8-17-C#-从零开始写-SharpDx-应用-控制台创建-Sharpdx-窗口
title author date CreateTime categories C# 从零开始写 SharpDx 应用 控制台创建 Sharpdx 窗口 lindexi 2018-8-17 9:3:3 ...
- 2019-8-30-C#-从零开始写-SharpDx-应用-笔刷
title author date CreateTime categories C# 从零开始写 SharpDx 应用 笔刷 lindexi 2019-8-30 8:50:0 +0800 2019-6 ...
随机推荐
- thinkphp用swiftmailer发邮件demo
QQ邮箱 include_once APPPATH . 'libraries/swiftmailer/swift_required.php'; $transport = Swift_SmtpTrans ...
- 【solr】schemaFactory配置相关schema.xml
schemaFactory配置相关schema.xml 关于schemaFactory的配置困扰我半天啦,下面来总结一下. 话说,好像是从5.0以后就已经没有schema.xml啦,这是由于Solr ...
- Hibernate4的注解 (持续更新范例中)
作用:使得Hibernate程序的开发大大的简化.利用注解后,可不用定义持久化类对应的*.hbm.xml,而直接以注解方式写入持久化类的实现中. 注解配置持久化类常用注解. 注解 含义和作用 @Ent ...
- Birt设置导出格式和去掉多余按钮的方法
1.设置导出格式: webcontent>birt>pages>dialog>ExportReportDialogFragment.jsp页面: 找到for ( int i = ...
- NOIP2016参赛日志+总结
这个故事告诉我们,成绩出来之前一定要装弱.这些文字是作者拿到程序后测了洛谷民间数据后写的. 2016.11.18 Day 0 早上五点半起床,洗漱完毕,吃了早饭,收拾收拾,七点半从家出发,去了 ...
- WordPress资料收集,以后整理
WordPress主题开发:实现分页功能 http://www.cnblogs.com/tinyphp/p/6361901.html WordPress如何调取显示指定文章 https://www.d ...
- eclipse中部署web项目
因为eclipse是免费的,所以很多企业都会选用eclipse作为开发工作,那么我们就需要熟练使用eclipse部署web项目. 第一步:选择window -> preferences ,选择s ...
- 【python之路16】作业
#!usr/bin/env python # -*- coding:utf-8 -*- # 数据库中原有 old_dict = { "#1": {'hostname': 'c1', ...
- Django用户登陆以及跳转后台管理页面2
请先写好以下,再来替换文件 Django用户登陆以及跳转后台管理页面1http://www.cnblogs.com/ujq3/p/7891774.html from django.shortcuts ...
- Linux平台的SVN服务器的配置及搭建
https://jingyan.baidu.com/article/54b6b9c08b35382d593b477c.html 一.安装SVN 1 在Linux平台上,SVN的软件包名称是subv ...