标签:text 描述 函数 数据 如何 lib 可见 方向 alpha
原文 http://www.johanfalk.eu/blog/sharpdx-beginners-tutorial-part-4-drawing-a-triangle
现在我们有了一个Direct3D初始化的窗口,现在是时候绘制一些东西了,就像所有其他教程一样,我们也将开始绘制一个三角形!要渲染我们的第一个三角形,实际上我们必须添加很多部分,所以让我们开始吧。
要创建三角形,我们将使用顶点。顶点是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) };
我们刚刚创建的顶点数组存储在系统内存中,但是为了渲染我们的对象,我们需要将数据传输到视频内存。为此,我们将使用缓冲区。在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();
[...]
}
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并写入:
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();
[...]
}
我们现在有一个顶点缓冲区,它有我们的顶点数据。但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();
[...]
}
在我们绘制任何东西之前,我们必须指定视口。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)并将其设置为窗口的整个宽度和高度。
完成所有这些工作后,终于可以在屏幕上绘制三角形!这只是两个方法调用,我们在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
标签:text 描述 函数 数据 如何 lib 可见 方向 alpha
原文地址:https://www.cnblogs.com/lonelyxmas/p/10804116.html