Los efectos de post-procesamiento son técnicas aplicadas a la imagen renderizada final para mejorar su calidad visual o añadir efectos especiales. Estos efectos se aplican después de que la escena ha sido completamente renderizada, de ahí el término "post-procesamiento". En este módulo, aprenderemos sobre algunos de los efectos de post-procesamiento más comunes y cómo implementarlos en DirectX.
Conceptos Clave
- Render Target: Un buffer donde se almacena la imagen renderizada antes de ser presentada en la pantalla.
- Shader de Post-Procesamiento: Un shader que se aplica a la imagen renderizada para modificar su apariencia.
- Framebuffer: Un conjunto de buffers que incluye el color, la profundidad y otros datos necesarios para renderizar una escena.
Efectos Comunes de Post-Procesamiento
- Bloom: Añade un resplandor a las áreas brillantes de la imagen.
- Depth of Field: Simula el efecto de enfoque y desenfoque de una cámara.
- Motion Blur: Añade un efecto de desenfoque a los objetos en movimiento.
- Color Correction: Ajusta los colores de la imagen para mejorar su apariencia.
Implementación de Efectos de Post-Procesamiento
Paso 1: Configuración del Render Target
Primero, necesitamos configurar un render target donde almacenaremos la imagen renderizada antes de aplicar los efectos de post-procesamiento.
// Crear un render target ID3D11Texture2D* pRenderTargetTexture = nullptr; D3D11_TEXTURE2D_DESC textureDesc = {}; textureDesc.Width = width; textureDesc.Height = height; textureDesc.MipLevels = 1; textureDesc.ArraySize = 1; textureDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; textureDesc.SampleDesc.Count = 1; textureDesc.Usage = D3D11_USAGE_DEFAULT; textureDesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE; HRESULT hr = device->CreateTexture2D(&textureDesc, nullptr, &pRenderTargetTexture); if (FAILED(hr)) { // Manejar error } // Crear un render target view ID3D11RenderTargetView* pRenderTargetView = nullptr; hr = device->CreateRenderTargetView(pRenderTargetTexture, nullptr, &pRenderTargetView); if (FAILED(hr)) { // Manejar error }
Paso 2: Renderizar la Escena al Render Target
Renderizamos la escena al render target en lugar de directamente a la pantalla.
// Establecer el render target context->OMSetRenderTargets(1, &pRenderTargetView, nullptr); // Renderizar la escena RenderScene();
Paso 3: Aplicar el Shader de Post-Procesamiento
Después de renderizar la escena, aplicamos el shader de post-procesamiento. Aquí hay un ejemplo de un shader simple que aplica un efecto de inversión de color.
Vertex Shader (PostProcessVS.hlsl)
struct VS_OUTPUT { float4 Pos : SV_POSITION; float2 TexCoord : TEXCOORD0; }; VS_OUTPUT main(float4 pos : POSITION, float2 texCoord : TEXCOORD0) { VS_OUTPUT output; output.Pos = pos; output.TexCoord = texCoord; return output; }
Pixel Shader (PostProcessPS.hlsl)
Texture2D sceneTexture : register(t0); SamplerState samplerState : register(s0); float4 main(float2 texCoord : TEXCOORD0) : SV_TARGET { float4 color = sceneTexture.Sample(samplerState, texCoord); // Invertir colores color.rgb = 1.0 - color.rgb; return color; }
Paso 4: Configurar y Ejecutar el Shader
Configuramos y ejecutamos el shader de post-procesamiento.
// Establecer el shader de post-procesamiento context->VSSetShader(pPostProcessVertexShader, nullptr, 0); context->PSSetShader(pPostProcessPixelShader, nullptr, 0); // Establecer el render target original (pantalla) context->OMSetRenderTargets(1, &pOriginalRenderTargetView, nullptr); // Dibujar un quad de pantalla completa context->Draw(6, 0);
Ejercicio Práctico
Ejercicio 1: Implementar un Efecto de Desenfoque Gaussiano
- Objetivo: Implementar un efecto de desenfoque gaussiano en la imagen renderizada.
- Instrucciones:
- Configura un render target.
- Renderiza la escena al render target.
- Aplica un shader de post-procesamiento que implemente un desenfoque gaussiano.
- Renderiza la imagen procesada a la pantalla.
Pista: Un desenfoque gaussiano se puede implementar mediante la convolución de la imagen con un kernel gaussiano.
Solución
Pixel Shader (GaussianBlurPS.hlsl)
Texture2D sceneTexture : register(t0); SamplerState samplerState : register(s0); float4 main(float2 texCoord : TEXCOORD0) : SV_TARGET { float4 color = float4(0, 0, 0, 1); float2 offsets[9] = { float2(-1, -1), float2(0, -1), float2(1, -1), float2(-1, 0), float2(0, 0), float2(1, 0), float2(-1, 1), float2(0, 1), float2(1, 1) }; float kernel[9] = { 1.0 / 16, 2.0 / 16, 1.0 / 16, 2.0 / 16, 4.0 / 16, 2.0 / 16, 1.0 / 16, 2.0 / 16, 1.0 / 16 }; for (int i = 0; i < 9; i++) { color += sceneTexture.Sample(samplerState, texCoord + offsets[i] * 0.005) * kernel[i]; } return color; }
Conclusión
En esta sección, hemos aprendido sobre los efectos de post-procesamiento y cómo implementarlos en DirectX. Hemos cubierto la configuración de render targets, la aplicación de shaders de post-procesamiento y hemos implementado un efecto de inversión de color. Además, hemos proporcionado un ejercicio práctico para implementar un desenfoque gaussiano. Con estos conocimientos, estarás preparado para explorar y crear tus propios efectos de post-procesamiento avanzados.
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