In this section, we will learn how to render a simple triangle using Direct3D. This is a fundamental step in understanding how to draw shapes and eventually more complex 3D models. We will cover the following steps:

  1. Setting Up the Vertex Buffer
  2. Creating the Vertex Shader
  3. Creating the Pixel Shader
  4. Drawing the Triangle

  1. Setting Up the Vertex Buffer

A vertex buffer is a memory buffer that holds vertex data. Each vertex can contain information such as position, color, texture coordinates, etc. For our triangle, we will define three vertices.

Code Example

// Define the vertex structure
struct Vertex {
    DirectX::XMFLOAT3 position; // Position of the vertex
    DirectX::XMFLOAT4 color;    // Color of the vertex
};

// Define the vertices of the triangle
Vertex vertices[] = {
    { DirectX::XMFLOAT3(0.0f, 0.5f, 0.0f), DirectX::XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f) }, // Top vertex (red)
    { DirectX::XMFLOAT3(0.5f, -0.5f, 0.0f), DirectX::XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f) }, // Bottom-right vertex (green)
    { DirectX::XMFLOAT3(-0.5f, -0.5f, 0.0f), DirectX::XMFLOAT4(0.0f, 0.0f, 1.0f, 1.0f) } // Bottom-left vertex (blue)
};

// Create the vertex buffer
D3D11_BUFFER_DESC bufferDesc = {};
bufferDesc.Usage = D3D11_USAGE_DEFAULT;
bufferDesc.ByteWidth = sizeof(vertices);
bufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
bufferDesc.CPUAccessFlags = 0;

D3D11_SUBRESOURCE_DATA initData = {};
initData.pSysMem = vertices;

ID3D11Buffer* vertexBuffer;
HRESULT hr = device->CreateBuffer(&bufferDesc, &initData, &vertexBuffer);
if (FAILED(hr)) {
    // Handle error
}

Explanation

  • Vertex Structure: Defines the format of each vertex, including position and color.
  • Vertices Array: Contains the three vertices of the triangle.
  • Buffer Description: Specifies the usage, size, and binding flags for the vertex buffer.
  • Subresource Data: Points to the vertex data.
  • Create Buffer: Creates the vertex buffer on the GPU.

  1. Creating the Vertex Shader

The vertex shader processes each vertex and transforms it from model space to screen space.

Code Example

// Vertex Shader (VertexShader.hlsl)
struct VS_INPUT {
    float3 position : POSITION;
    float4 color : COLOR;
};

struct VS_OUTPUT {
    float4 position : SV_POSITION;
    float4 color : COLOR;
};

VS_OUTPUT main(VS_INPUT input) {
    VS_OUTPUT output;
    output.position = float4(input.position, 1.0f);
    output.color = input.color;
    return output;
}

Explanation

  • VS_INPUT: Input structure containing the vertex position and color.
  • VS_OUTPUT: Output structure containing the transformed position and color.
  • main: The main function of the vertex shader that transforms the vertex position and passes the color through.

  1. Creating the Pixel Shader

The pixel shader determines the color of each pixel.

Code Example

// Pixel Shader (PixelShader.hlsl)
struct PS_INPUT {
    float4 position : SV_POSITION;
    float4 color : COLOR;
};

float4 main(PS_INPUT input) : SV_TARGET {
    return input.color;
}

Explanation

  • PS_INPUT: Input structure containing the interpolated position and color.
  • main: The main function of the pixel shader that returns the color of the pixel.

  1. Drawing the Triangle

Finally, we need to set up the pipeline and draw the triangle.

Code Example

// Set the vertex buffer
UINT stride = sizeof(Vertex);
UINT offset = 0;
context->IASetVertexBuffers(0, 1, &vertexBuffer, &stride, &offset);

// Set the primitive topology
context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

// Set the input layout, vertex shader, and pixel shader
context->IASetInputLayout(inputLayout);
context->VSSetShader(vertexShader, nullptr, 0);
context->PSSetShader(pixelShader, nullptr, 0);

// Draw the triangle
context->Draw(3, 0);

Explanation

  • IASetVertexBuffers: Binds the vertex buffer to the input assembler stage.
  • IASetPrimitiveTopology: Sets the primitive topology to a triangle list.
  • IASetInputLayout: Sets the input layout that matches the vertex shader input.
  • VSSetShader: Sets the vertex shader.
  • PSSetShader: Sets the pixel shader.
  • Draw: Draws the triangle using the vertex buffer.

Practical Exercise

Task

  1. Modify the vertex colors to create a gradient effect.
  2. Change the vertex positions to form a different shape, such as a square.

Solution

  1. Gradient Effect: Change the colors in the vertices array.
  2. Different Shape: Add more vertices and adjust the Draw call to draw more primitives.

Example

// Define the vertices of a square
Vertex vertices[] = {
    { DirectX::XMFLOAT3(-0.5f, 0.5f, 0.0f), DirectX::XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f) }, // Top-left vertex (red)
    { DirectX::XMFLOAT3(0.5f, 0.5f, 0.0f), DirectX::XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f) }, // Top-right vertex (green)
    { DirectX::XMFLOAT3(0.5f, -0.5f, 0.0f), DirectX::XMFLOAT4(0.0f, 0.0f, 1.0f, 1.0f) }, // Bottom-right vertex (blue)
    { DirectX::XMFLOAT3(-0.5f, -0.5f, 0.0f), DirectX::XMFLOAT4(1.0f, 1.0f, 0.0f, 1.0f) } // Bottom-left vertex (yellow)
};

// Update the Draw call to draw a square
context->Draw(4, 0);

Conclusion

In this section, we learned how to render a simple triangle using Direct3D. We covered setting up the vertex buffer, creating the vertex and pixel shaders, and drawing the triangle. This foundational knowledge will be essential as we move on to more complex rendering techniques in the next modules.

© Copyright 2024. All rights reserved