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文档以获得不同类型的良好说明。

现在,让我们添加着色器代码:

  1. 右键单击解决方案资源管理器中的项目,然后选择添加 - >新项...
  2. 找到“文本文件”并输入“vertexShader.hlsl”作为名称。按添加。
  3. 在解决方案资源管理器中选择该文件,然后在“属性”窗口中,将“复制到输出目录”设置为“始终复制”。
  4. 重复步骤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

猜你喜欢

转载自www.cnblogs.com/lonelyxmas/p/10804116.html