2d rendering engine using directx 12 in csharp

To create a 2D rendering engine using DirectX 12 in C#, we need to perform the following steps:

  1. Set up the DirectX 12 device and command queue.
main.cs
using SharpDX.Direct3D12;

// Create device
Device device = new Device(null, SharpDX.Direct3D.FeatureLevel.Level_11_0);

// Create command queue
CommandQueueDescription queueDesc = new CommandQueueDescription(CommandListType.Direct);
CommandQueue commandQueue = device.CreateCommandQueue(queueDesc);
300 chars
9 lines
  1. Load the 2D sprites into video memory.
main.cs
using SharpDX.Direct3D12;
using SharpDX.Direct3D;
using SharpDX.DXGI;

// Create texture resource
Resource texture = Resource.FromFile(device, "sprite.png");

// Create shader resource view
ShaderResourceViewDescription srvDesc = new ShaderResourceViewDescription();
srvDesc.Format = texture.Description.Format;
srvDesc.Dimension = ShaderResourceViewDimension.Texture2D;
srvDesc.Texture2D.MipLevels = texture.Description.MipLevels;
srvDesc.Texture2D.MostDetailedMip = 0;
ShaderResourceView textureView = new ShaderResourceView(device, texture, srvDesc);
554 chars
15 lines
  1. Create the vertex and index buffers for the sprites.
main.cs
using SharpDX.Direct3D12;
using SharpDX.Direct3D;
using SharpDX.DXGI;

// Define vertex structure
[StructLayout(LayoutKind.Sequential)]
struct SpriteVertex
{
    public Vector2 Position;
    public Vector2 TextureCoord;
}

// Create vertex buffer
SpriteVertex[] vertices = new SpriteVertex[4];
vertices[0].Position = new Vector2(0, 0);
vertices[0].TextureCoord = new Vector2(0, 0);
vertices[1].Position = new Vector2(1, 0);
vertices[1].TextureCoord = new Vector2(1, 0);
vertices[2].Position = new Vector2(0, 1);
vertices[2].TextureCoord = new Vector2(0, 1);
vertices[3].Position = new Vector2(1, 1);
vertices[3].TextureCoord = new Vector2(1, 1);
VertexBuffer vertexBuffer = new VertexBuffer(device, Utilities.SizeOf<SpriteVertex>() * 4, ResourceUsage.Default, BindFlags.VertexBuffer);
vertexBuffer.UploadData(vertices);

// Create index buffer
ushort[] indices = new ushort[] { 0, 1, 2, 3 };
IndexBuffer indexBuffer = new IndexBuffer(device, sizeof(ushort) * 4, ResourceUsage.Default, IndexFormat.SixteenBits, BindFlags.IndexBuffer);
indexBuffer.UploadData(indices);
1067 chars
30 lines
  1. Write the vertex and pixel shaders for rendering the sprites.
struct VertexInput
{
    float2 position : POSITION;
    float2 texcoord : TEXCOORD;
};

struct PixelInput
{
    float4 position : SV_POSITION;
    float2 texcoord : TEXCOORD;
};

PixelInput main(VertexInput input)
{
    PixelInput output;
    output.position = float4(input.position, 0, 1);
    output.texcoord = input.texcoord;
    return output;
}

Texture2D texture : register(t0);
SamplerState sampler : register(s0);

float4 main(PixelInput input) : SV_Target
{
    float4 color = texture.Sample(sampler, input.texcoord);
    return color;
}
548 chars
29 lines
  1. Create the pipeline state object to render the sprites.
main.cs
using SharpDX.Direct3D12;
using SharpDX.D3DCompiler;

// Compile shaders
ShaderBytecode vertexShader = new ShaderBytecode(ShaderCompiler.CompileFromFile("sprite.vs", "main", "vs_5_0"));
ShaderBytecode pixelShader = new ShaderBytecode(ShaderCompiler.CompileFromFile("sprite.ps", "main", "ps_5_0"));

// Describe and create the graphics pipeline state object
GraphicsPipelineStateDescription psoDesc = new GraphicsPipelineStateDescription();
psoDesc.InputLayout = new InputLayoutDescription(SpriteVertex.Layout);
psoDesc.VertexShader = new ShaderBytecode(vertexShader);
psoDesc.PixelShader = new ShaderBytecode(pixelShader);
psoDesc.PrimitiveTopologyType = PrimitiveTopologyType.Triangle;
psoDesc.RasterizerState = RasterizerStateDescription.Default();
psoDesc.BlendState = BlendStateDescription.Default();
psoDesc.DepthStencilFormat = Format.D24_UNorm_S8_UInt;
psoDesc.SampleMask = int.MaxValue;
psoDesc.PrimitiveRestartEnable = false;
psoDesc.DepthStencilState = DepthStencilStateDescription.Default();
psoDesc.RenderTargetCount = 1;
psoDesc.SampleDescription = new SampleDescription(1, 0);
psoDesc.DSVFormat = Format.D24_UNorm_S8_UInt;
psoDesc.RTVFormats[0] = Format.R8G8B8A8_UNorm;
GraphicsPipelineState pipelineState = new GraphicsPipelineState(device, psoDesc);
1266 chars
25 lines
  1. Render the sprites onto the screen.
main.cs
using SharpDX.Direct3D12;
using SharpDX.DXGI;

// Begin rendering
CommandList commandList = new CommandList(device, CommandListType.Direct, commandQueue);
commandList.SetRenderTargets(device.MainRenderTargetView, device.DepthStencilView);
commandList.ClearRenderTargetView(device.MainRenderTargetView, Color.CornflowerBlue);
commandList.ClearDepthStencilView(device.DepthStencilView, ClearFlags.Depth | ClearFlags.Stencil, 1f, 0);

// Bind pipeline state object and vertex/index buffers
commandList.SetPipelineState(pipelineState);
commandList.SetVertexBuffer(0, vertexBuffer);
commandList.SetIndexBuffer(indexBuffer);

// Draw sprite
commandList.SetGraphicsRootDescriptorTable(0, textureView);
commandList.DrawIndexedInstanced(4, 1, 0, 0, 0);

// End rendering
commandList.ResourceBarrierTransition(device.MainRenderTarget, ResourceStates.RenderTarget, ResourceStates.Present);
commandList.Close();
commandQueue.ExecuteCommandList(commandList);
device.Present();
964 chars
24 lines

By following these steps, we can create a basic 2D rendering engine using DirectX 12 in C#. From here, we can add additional features such as sprite batching, sprite animation, and more advanced shader effects.

gistlibby LogSnag