SharpDX初学者教程第4部分:绘制三角形
原文 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文档以获得不同类型的良好说明。
现在,让我们添加着色器代码:
- 右键单击解决方案资源管理器中的项目,然后选择添加 - >新项...
- 找到“文本文件”并输入“vertexShader.hlsl”作为名称。按添加。
- 在解决方案资源管理器中选择该文件,然后在“属性”窗口中,将“复制到输出目录”设置为“始终复制”。
- 重复步骤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
public class Game : IDisposable
{
private RenderForm renderForm; private const int Width = ;
private const int Height = ; private D3D11.Device d3dDevice;
private D3D11.DeviceContext d3dDeviceContext;
private SwapChain swapChain;
private D3D11.RenderTargetView renderTargetView;
private Viewport viewport; // Shaders
private D3D11.VertexShader vertexShader;
private D3D11.PixelShader pixelShader;
private ShaderSignature inputSignature;
private D3D11.InputLayout inputLayout; private D3D11.InputElement[] inputElements = new D3D11.InputElement[]
{
new D3D11.InputElement("POSITION", , Format.R32G32B32_Float, )
}; // Triangle vertices
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 D3D11.Buffer triangleVertexBuffer; /// <summary>
/// Create and initialize a new game.
/// </summary>
public Game()
{
// Set window properties
renderForm = new RenderForm("My first SharpDX game");
renderForm.ClientSize = new Size(Width, Height);
renderForm.AllowUserResizing = false; InitializeDeviceResources();
InitializeShaders();
InitializeTriangle();
} /// <summary>
/// Start the game.
/// </summary>
public void Run()
{
// Start the render loop
RenderLoop.Run(renderForm, RenderCallback);
} private void RenderCallback()
{
Draw();
} private void InitializeDeviceResources()
{
ModeDescription backBufferDesc = new ModeDescription(Width, Height, new Rational(, ), Format.R8G8B8A8_UNorm); // Descriptor for the swap chain
SwapChainDescription swapChainDesc = new SwapChainDescription()
{
ModeDescription = backBufferDesc,
SampleDescription = new SampleDescription(, ),
Usage = Usage.RenderTargetOutput,
BufferCount = ,
OutputHandle = renderForm.Handle,
IsWindowed = true
}; // Create device and swap chain
D3D11.Device.CreateWithSwapChain(DriverType.Hardware, D3D11.DeviceCreationFlags.None, swapChainDesc, out d3dDevice, out swapChain);
d3dDeviceContext = d3dDevice.ImmediateContext; viewport = new Viewport(, , Width, Height);
d3dDeviceContext.Rasterizer.SetViewport(viewport); // Create render target view for back buffer
using (D3D11.Texture2D backBuffer = swapChain.GetBackBuffer<D3D11.Texture2D>())
{
renderTargetView = new D3D11.RenderTargetView(d3dDevice, backBuffer);
}
} private void InitializeShaders()
{
// Compile the vertex shader code
using (var vertexShaderByteCode = ShaderBytecode.CompileFromFile("vertexShader.hlsl", "main", "vs_4_0", ShaderFlags.Debug))
{
// Read input signature from shader code
inputSignature = ShaderSignature.GetInputSignature(vertexShaderByteCode); vertexShader = new D3D11.VertexShader(d3dDevice, vertexShaderByteCode);
} // Compile the pixel shader code
using (var pixelShaderByteCode = ShaderBytecode.CompileFromFile("pixelShader.hlsl", "main", "ps_4_0", ShaderFlags.Debug))
{
pixelShader = new D3D11.PixelShader(d3dDevice, pixelShaderByteCode);
} // Set as current vertex and pixel shaders
d3dDeviceContext.VertexShader.Set(vertexShader);
d3dDeviceContext.PixelShader.Set(pixelShader); d3dDeviceContext.InputAssembler.PrimitiveTopology = PrimitiveTopology.TriangleList; // Create the input layout from the input signature and the input elements
inputLayout = new D3D11.InputLayout(d3dDevice, inputSignature, inputElements); // Set input layout to use
d3dDeviceContext.InputAssembler.InputLayout = inputLayout;
} private void InitializeTriangle()
{
// Create a vertex buffer, and use our array with vertices as data
triangleVertexBuffer = D3D11.Buffer.Create<Vector3>(d3dDevice, D3D11.BindFlags.VertexBuffer, vertices);
} /// <summary>
/// Draw the game.
/// </summary>
private void Draw()
{
// Set back buffer as current render target view
d3dDeviceContext.OutputMerger.SetRenderTargets(renderTargetView); // Clear the screen
d3dDeviceContext.ClearRenderTargetView(renderTargetView, new SharpDX.Color(, , )); // Set vertex buffer
d3dDeviceContext.InputAssembler.SetVertexBuffers(, new D3D11.VertexBufferBinding(triangleVertexBuffer, Utilities.SizeOf<Vector3>(), )); // Draw the triangle
d3dDeviceContext.Draw(vertices.Count(), ); // Swap front and back buffer
swapChain.Present(, PresentFlags.None);
} public void Dispose()
{
inputLayout.Dispose();
inputSignature.Dispose();
triangleVertexBuffer.Dispose();
vertexShader.Dispose();
pixelShader.Dispose();
renderTargetView.Dispose();
swapChain.Dispose();
d3dDevice.Dispose();
d3dDeviceContext.Dispose();
renderForm.Dispose();
}
}
SharpDX初学者教程第4部分:绘制三角形的更多相关文章
- SharpDX初学者教程第5部分:着色三角形
原文 http://www.johanfalk.eu/blog/sharpdx-beginners-tutorial-part-5-coloring-the-triangle 在第4部分中,我们创建了 ...
- SharpDX初学者教程第3部分:初始化DirectX
原文 http://www.johanfalk.eu/blog/sharpdx-beginners-tutorial-part-3-initializing-directx 在这部分中,我们将初始化D ...
- SharpDX初学者教程第2部分:创建窗口
原文 http://www.johanfalk.eu/blog/sharpdx-tutorial-part-2-creating-a-window 在第二篇教程中,我们将介绍如何创建一个稍后将呈现的简 ...
- SharpDX初学者教程第1部分:在Visual Studio 2013中设置SharpDX项目
原文 http://www.johanfalk.eu/blog/sharpdx-tutorial-part-1-setting-up-a-sharpdx-project-in-visual-studi ...
- CSS 魔法系列:纯 CSS 绘制三角形(各种角度)
我们的网页因为 CSS 而呈现千变万化的风格.这一看似简单的样式语言在使用中非常灵活,只要你发挥创意就能实现很多比人想象不到的效果.特别是随着 CSS3 的广泛使用,更多新奇的 CSS 作品涌现出来. ...
- Android OpenGL 入门示例----绘制三角形和正方形
Android上对OpenGl的支持是无缝的,所以才有众多3D效果如此逼真的游戏,在Camera的一些流程中也有用到GLSurfaceView的情况.本文记录OpenGL在Android上的入门级示例 ...
- PANDAS 数据分析初学者教程
Pandas 初学者教程 2018-05-19 六尺巷人 对于数据科学家,无论是数据分析还是数据挖掘来说,Pandas是一个非常重要的Python包.它不仅提供了很多方法,使得数据处理非常 ...
- WPF进阶教程 - 使用Decorator自定义带三角形的边框
原文:WPF进阶教程 - 使用Decorator自定义带三角形的边框 写下来,备忘. Decorator,有装饰器.装饰品的意思,很容易让人联想到设计模式里面的装饰器模式.Decorator类负责包装 ...
- 1.opengl绘制三角形
顶点数组对象:Vertex Array Object,VAO,用于存储顶点状态配置信息,每当界面刷新时,则通过VAO进行绘制. 顶点缓冲对象:Vertex Buffer Object,VBO,通过VB ...
随机推荐
- ajax实例解析
function showHint(str) { var xmlhttp; if (str.length==0) { document.getElementById("txtHint&quo ...
- nginx日志修改时间格式为年月日时分秒
先解除这段注释,使用自定义日志格式 $time_iso8601 生成格式:--20T09::+: $time_local 生成格式: /Apr/::: + 还是选择年月日时分秒看起来舒服一点
- mysql过多sleep连接 修改timeout配置节约连接数 配置连接数
数据库连接数量我设置了16384,最大值 ; 对于mysql8在设置一下这个 SET GLOBAL mysqlx_max_connections = ; 可以使用 命令查看自己的设置 SHOW var ...
- Vue2.0史上最全入坑教程(上)—— 搭建Vue脚手架(vue-cli)
ps: 想了解更多vue相关知识请点击VUE学习目录汇总 Vue作为前端三大框架之一截至到目前在github上以收获44,873颗星,足以说明其以悄然成为主流.16年10月Vue发布了2.x版本,经过 ...
- Vue--公有组件以及组件的使用和特点
组件的作用:为了能够让功能与功能之间互不影响,使代码更加清晰整洁 1 <!DOCTYPE html> <html lang="en"> <head&g ...
- OpenLayers使用点要素作为标记
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head ...
- python-None
今天偶然间,有人问了一个问题,项目中出现了一个这样的错误. 看到后,就想到是前后数据类型不一致.当时他定义了一些默认初始值为None(刚接触python代码,之前是c,java),然后就后边出现了这样 ...
- 构建PaaS的开源平台:CloudFoundry
CloudFoundry的架构: 点评:这是vmware用ruby开发的一个paas,由于对ruby不太熟悉,还是比较难理解. refer to :http://www.oschina.net/que ...
- Vue. 之 Element获取table中选中的行
Vue. 之 Element获取table中选中的行 问题描述: 如下截图,在Table中选择数据后,然后在点击“统计”按钮,获取Table表中选择的行 解决方案: 1. 给“统计”这个按钮添加一个点 ...
- Eslint报错的翻译
若在git中出现这个 http://eslint.org/docs/rules/eol-last 他是提醒你:在文件末尾要求或禁止换行 比如代码如下: 若在git中出现这个 https://eslin ...