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
- Buffers de Vértices: Almacenan los datos de los vértices que componen las primitivas.
- Shaders: Programas que se ejecutan en la GPU para procesar los vértices y píxeles.
- 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
- Modifica los colores de los vértices para que el triángulo tenga un gradiente de color diferente.
- 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
- Cambia los valores de color en la definición de los vértices.
- 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.
Curso de Programación DirectX
Módulo 1: Introducción a DirectX
- ¿Qué es DirectX?
- Configuración del Entorno de Desarrollo
- Entendiendo la API de DirectX
- Creando Tu Primera Aplicación DirectX
Módulo 2: Conceptos Básicos de Direct3D
- Introducción a Direct3D
- Inicializando Direct3D
- Renderizando un Triángulo
- Manejando el Bucle de Renderizado
Módulo 3: Trabajando con Shaders
- Introducción a los Shaders
- Escribiendo Vertex Shaders
- Escribiendo Pixel Shaders
- Compilando y Usando Shaders
Módulo 4: Técnicas Avanzadas de Renderizado
Módulo 5: Modelos 3D y Animación
Módulo 6: Optimización del Rendimiento
- Perfilado y Depuración
- Optimizando el Rendimiento de Renderizado
- Gestión de Memoria
- Multithreading en DirectX