← SEARCHABLE ARCHIVE
DirectX 12 · 2026-07-04

Representación triangular nativa de DirectX 12

Dibujar un triángulo con Lista de comandos y Barrera de recursos

INTERACTIVE READING MODE →

Los artículos escritos en este blog fueron creados con el objetivo de enseñar. Además, la información del artículo puede ser incorrecta, así que tenga cuidado.

El flujo general de dibujar un triángulo en DirectX 12 es similar al de DX11, pero el desarrollador administra directamente el estado de los recursos y el envío de comandos. Inmediatamente después de crear el dispositivoDrawInstancedNo es una estructura que se pueda llamar.

objeto necesario

Device
CommandQueue
SwapChain
DescriptorHeap(RTV)
CommandAllocator
GraphicsCommandList
Fence
VertexBuffer
RootSignature
PipelineState

buffer de vértice

struct Vertex
{
    DirectX::XMFLOAT3 Position;
    DirectX::XMFLOAT4 Color;
};

const Vertex Vertices[] =
{
    { {  0.0f,  0.5f, 0.0f }, { 1, 0, 0, 1 } },
    { {  0.5f, -0.5f, 0.0f }, { 0, 1, 0, 1 } },
    { { -0.5f, -0.5f, 0.0f }, { 0, 0, 1, 1 } }
};

const UINT BufferSize = sizeof(Vertices);

CD3DX12_HEAP_PROPERTIES HeapProperties(D3D12_HEAP_TYPE_UPLOAD);
CD3DX12_RESOURCE_DESC BufferDescription =
    CD3DX12_RESOURCE_DESC::Buffer(BufferSize);

Device->CreateCommittedResource(
    &HeapProperties,
    D3D12_HEAP_FLAG_NONE,
    &BufferDescription,
    D3D12_RESOURCE_STATE_GENERIC_READ,
    nullptr,
    IID_PPV_ARGS(&VertexBuffer)
);

void* MappedData = nullptr;
VertexBuffer->Map(0, nullptr, &MappedData);
std::memcpy(MappedData, Vertices, BufferSize);
VertexBuffer->Unmap(0, nullptr);

VertexBufferView.BufferLocation = VertexBuffer->GetGPUVirtualAddress();
VertexBufferView.SizeInBytes = BufferSize;
VertexBufferView.StrideInBytes = sizeof(Vertex);

El ejemplo simplemente utilizó el montón de carga. En la práctica, copiar datos de vértices leídos con frecuencia en el montón predeterminado es ventajoso para el rendimiento del acceso a la GPU.

Grabar dibujo en la lista de comandos

CommandAllocator->Reset();
CommandList->Reset(CommandAllocator, PipelineState);

auto ToRenderTarget = CD3DX12_RESOURCE_BARRIER::Transition(
    BackBuffers[FrameIndex],
    D3D12_RESOURCE_STATE_PRESENT,
    D3D12_RESOURCE_STATE_RENDER_TARGET
);
CommandList->ResourceBarrier(1, &ToRenderTarget);

CommandList->RSSetViewports(1, &Viewport);
CommandList->RSSetScissorRects(1, &ScissorRect);
CommandList->OMSetRenderTargets(1, &CurrentRTV, FALSE, nullptr);

const float ClearColor[] = { 0.08f, 0.08f, 0.12f, 1.0f };
CommandList->ClearRenderTargetView(CurrentRTV, ClearColor, 0, nullptr);

CommandList->SetGraphicsRootSignature(RootSignature);
CommandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
CommandList->IASetVertexBuffers(0, 1, &VertexBufferView);
CommandList->DrawInstanced(3, 1, 0, 0);

auto ToPresent = CD3DX12_RESOURCE_BARRIER::Transition(
    BackBuffers[FrameIndex],
    D3D12_RESOURCE_STATE_RENDER_TARGET,
    D3D12_RESOURCE_STATE_PRESENT
);
CommandList->ResourceBarrier(1, &ToPresent);
CommandList->Close();

ID3D12CommandList* Lists[] = { CommandList };
CommandQueue->ExecuteCommandLists(1, Lists);
SwapChain->Present(1, 0);

BackBuffer va y viene entre el estado actual y el estado RenderTarget. Si omite esta transición de estado, se producirá un error en la capa de depuración o la pantalla no se dibujará correctamente.

El código triangular de DX12 es largo, pero su estructura es clara. Los tres pasos clave son registrar el comando, enviarlo a la cola y verificar la finalización de la GPU con Fence.