En esta sección, aprenderemos a renderizar un triángulo simple utilizando Direct3D. Este es un paso fundamental para entender cómo funcionan los gráficos en 3D y cómo Direct3D maneja el proceso de renderizado.

Objetivos

  • Entender los conceptos básicos de la renderización de primitivas.
  • Configurar los buffers necesarios para renderizar un triángulo.
  • Escribir y utilizar shaders básicos para el renderizado.
  • Ejecutar el bucle de renderizado para mostrar el triángulo en pantalla.

Conceptos Clave

  1. Buffers de Vértices: Almacenan los datos de los vértices que componen las primitivas.
  2. Shaders: Programas que se ejecutan en la GPU para procesar los vértices y píxeles.
  3. Pipeline de Renderizado: Secuencia de etapas que transforman los datos de los vértices en píxeles en la pantalla.

Paso 1: Configuración del Buffer de Vértices

Primero, necesitamos definir los vértices del triángulo y crear un buffer para almacenarlos.

Definición de los Vértices

struct Vertex {
    float x, y, z; // Posición del vértice
    float r, g, b; // Color del vértice
};

Vertex vertices[] = {
    { 0.0f,  0.5f, 0.0f, 1.0f, 0.0f, 0.0f }, // Vértice superior (rojo)
    { 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f }, // Vértice inferior derecho (verde)
    {-0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f }  // Vértice inferior izquierdo (azul)
};

Creación del Buffer de Vértices

ID3D11Buffer* vertexBuffer = nullptr;

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;

HRESULT hr = device->CreateBuffer(&bufferDesc, &initData, &vertexBuffer);
if (FAILED(hr)) {
    // Manejo de errores
}

Paso 2: Escribir y Compilar Shaders

Vertex Shader

cbuffer ConstantBuffer : register(b0) {
    matrix worldViewProjection;
};

struct VS_INPUT {
    float3 pos : POSITION;
    float3 color : COLOR;
};

struct PS_INPUT {
    float4 pos : SV_POSITION;
    float3 color : COLOR;
};

PS_INPUT VS(VS_INPUT input) {
    PS_INPUT output;
    output.pos = mul(float4(input.pos, 1.0f), worldViewProjection);
    output.color = input.color;
    return output;
}

Pixel Shader

struct PS_INPUT {
    float4 pos : SV_POSITION;
    float3 color : COLOR;
};

float4 PS(PS_INPUT input) : SV_TARGET {
    return float4(input.color, 1.0f);
}

Compilación de Shaders

ID3DBlob* vsBlob = nullptr;
ID3DBlob* psBlob = nullptr;
ID3DBlob* errorBlob = nullptr;

hr = D3DCompileFromFile(L"shader.hlsl", nullptr, nullptr, "VS", "vs_5_0", 0, 0, &vsBlob, &errorBlob);
if (FAILED(hr)) {
    // Manejo de errores
}

hr = D3DCompileFromFile(L"shader.hlsl", nullptr, nullptr, "PS", "ps_5_0", 0, 0, &psBlob, &errorBlob);
if (FAILED(hr)) {
    // Manejo de errores
}

Paso 3: Configuración del Pipeline de Renderizado

Creación de Input Layout

D3D11_INPUT_ELEMENT_DESC layout[] = {
    { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
    { "COLOR", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }
};

ID3D11InputLayout* inputLayout = nullptr;
hr = device->CreateInputLayout(layout, ARRAYSIZE(layout), vsBlob->GetBufferPointer(), vsBlob->GetBufferSize(), &inputLayout);
if (FAILED(hr)) {
    // Manejo de errores
}

Configuración del Pipeline

context->IASetInputLayout(inputLayout);
context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

UINT stride = sizeof(Vertex);
UINT offset = 0;
context->IASetVertexBuffers(0, 1, &vertexBuffer, &stride, &offset);

context->VSSetShader(vertexShader, nullptr, 0);
context->PSSetShader(pixelShader, nullptr, 0);

Paso 4: Ejecutar el Bucle de Renderizado

Bucle de Renderizado

while (true) {
    // Limpiar el render target
    float clearColor[] = { 0.0f, 0.0f, 0.0f, 1.0f };
    context->ClearRenderTargetView(renderTargetView, clearColor);

    // Dibujar el triángulo
    context->Draw(3, 0);

    // Presentar el buffer
    swapChain->Present(1, 0);
}

Ejercicio Práctico

Ejercicio

  1. Modifica los colores de los vértices para que el triángulo tenga un gradiente de color diferente.
  2. Cambia la posición de los vértices para que el triángulo se dibuje en una ubicación diferente en la pantalla.

Solución

  1. Cambia los valores de color en la definición de los vértices.
  2. Modifica los valores de posición en la definición de los vértices.
Vertex vertices[] = {
    { 0.0f,  0.5f, 0.0f, 1.0f, 1.0f, 0.0f }, // Vértice superior (amarillo)
    { 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 1.0f }, // Vértice inferior derecho (cian)
    {-0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f }  // Vértice inferior izquierdo (magenta)
};

Conclusión

En esta sección, hemos aprendido a renderizar un triángulo simple utilizando Direct3D. Hemos cubierto la configuración de los buffers de vértices, la escritura y compilación de shaders, y la configuración del pipeline de renderizado. Estos conceptos son fundamentales para cualquier aplicación gráfica y servirán como base para temas más avanzados en DirectX.

© Copyright 2024. Todos los derechos reservados