El mapeo de sombras es una técnica avanzada de renderizado que permite agregar sombras realistas a una escena 3D. Esta técnica es ampliamente utilizada en gráficos por computadora para mejorar la percepción de profundidad y realismo.
Conceptos Clave
- Mapa de Sombras (Shadow Map): Es una textura que almacena la información de profundidad desde la perspectiva de la fuente de luz.
- Proyección de Sombras: Proceso de proyectar las sombras en la escena desde la perspectiva de la cámara.
- Comparación de Profundidad: Técnica para determinar si un fragmento está en sombra comparando su profundidad con la almacenada en el mapa de sombras.
Pasos para Implementar el Mapeo de Sombras
- Generar el Mapa de Sombras
Primero, renderizamos la escena desde la perspectiva de la fuente de luz y almacenamos la información de profundidad en una textura.
// Configuración del framebuffer para el mapa de sombras GLuint depthMapFBO; glGenFramebuffers(1, &depthMapFBO); // Crear la textura para el mapa de sombras GLuint depthMap; glGenTextures(1, &depthMap); glBindTexture(GL_TEXTURE_2D, depthMap); glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, SHADOW_WIDTH, SHADOW_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); float borderColor[] = { 1.0, 1.0, 1.0, 1.0 }; glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor); // Adjuntar la textura al framebuffer glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthMap, 0); glDrawBuffer(GL_NONE); glReadBuffer(GL_NONE); glBindFramebuffer(GL_FRAMEBUFFER, 0);
- Renderizar la Escena desde la Perspectiva de la Luz
// Configurar la vista y proyección desde la perspectiva de la luz glm::mat4 lightProjection, lightView; glm::mat4 lightSpaceMatrix; float near_plane = 1.0f, far_plane = 7.5f; lightProjection = glm::ortho(-10.0f, 10.0f, -10.0f, 10.0f, near_plane, far_plane); lightView = glm::lookAt(lightPos, glm::vec3(0.0f), glm::vec3(0.0, 1.0, 0.0)); lightSpaceMatrix = lightProjection * lightView; // Renderizar la escena a la textura del mapa de sombras glViewport(0, 0, SHADOW_WIDTH, SHADOW_HEIGHT); glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO); glClear(GL_DEPTH_BUFFER_BIT); shadowShader.use(); shadowShader.setMat4("lightSpaceMatrix", lightSpaceMatrix); renderScene(shadowShader); glBindFramebuffer(GL_FRAMEBUFFER, 0);
- Renderizar la Escena con Sombras
// Configurar la vista y proyección desde la perspectiva de la cámara glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f); glm::mat4 view = camera.GetViewMatrix(); shader.use(); shader.setMat4("projection", projection); shader.setMat4("view", view); // Pasar la matriz de espacio de luz y el mapa de sombras al shader shader.setMat4("lightSpaceMatrix", lightSpaceMatrix); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, depthMap); shader.setInt("shadowMap", 1); // Renderizar la escena con sombras renderScene(shader);
- Shader para Comparación de Profundidad
Vertex Shader:
#version 330 core layout (location = 0) in vec3 aPos; layout (location = 1) in vec3 aNormal; layout (location = 2) in vec2 aTexCoords; out vec2 TexCoords; out vec4 FragPosLightSpace; uniform mat4 model; uniform mat4 view; uniform mat4 projection; uniform mat4 lightSpaceMatrix; void main() { TexCoords = aTexCoords; vec4 fragPos = model * vec4(aPos, 1.0); FragPosLightSpace = lightSpaceMatrix * fragPos; gl_Position = projection * view * fragPos; }
Fragment Shader:
#version 330 core out vec4 FragColor; in vec2 TexCoords; in vec4 FragPosLightSpace; uniform sampler2D shadowMap; uniform vec3 lightPos; uniform vec3 viewPos; float ShadowCalculation(vec4 fragPosLightSpace) { // Transformar coordenadas de fragmento a espacio de textura vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w; projCoords = projCoords * 0.5 + 0.5; float closestDepth = texture(shadowMap, projCoords.xy).r; float currentDepth = projCoords.z; float shadow = currentDepth > closestDepth ? 1.0 : 0.0; return shadow; } void main() { float shadow = ShadowCalculation(FragPosLightSpace); vec3 color = vec3(1.0) * (1.0 - shadow); FragColor = vec4(color, 1.0); }
Ejercicio Práctico
Ejercicio
Implementa el mapeo de sombras en una escena simple con una fuente de luz direccional y varios objetos. Asegúrate de seguir los pasos descritos anteriormente y ajustar los parámetros según sea necesario.
Solución
- Configura el framebuffer y la textura para el mapa de sombras.
- Renderiza la escena desde la perspectiva de la luz y almacena la información de profundidad.
- Renderiza la escena desde la perspectiva de la cámara, utilizando el mapa de sombras para calcular las sombras.
Resumen
En esta sección, hemos aprendido cómo implementar el mapeo de sombras en OpenGL. Esta técnica mejora significativamente el realismo de las escenas 3D al agregar sombras precisas y dinámicas. Hemos cubierto los conceptos clave, los pasos de implementación y proporcionado un ejercicio práctico para reforzar el aprendizaje. En el siguiente módulo, exploraremos otros efectos especiales y técnicas avanzadas de renderizado.
Curso de Programación OpenGL
Módulo 1: Introducción a OpenGL
- ¿Qué es OpenGL?
- Configuración de tu Entorno de Desarrollo
- Creando tu Primer Programa OpenGL
- Entendiendo el Pipeline de OpenGL
Módulo 2: Renderizado Básico
- Dibujando Formas Básicas
- Entendiendo Coordenadas y Transformaciones
- Coloreado y Sombreado
- Usando Buffers
Módulo 3: Técnicas de Renderizado Intermedio
- Texturas y Mapeo de Texturas
- Iluminación y Materiales
- Mezcla y Transparencia
- Pruebas de Profundidad y Plantilla
Módulo 4: Técnicas de Renderizado Avanzado
Módulo 5: Optimización del Rendimiento
- Optimizando Código OpenGL
- Usando Objetos de Array de Vértices (VAOs)
- Gestión Eficiente de Memoria
- Perfilado y Depuración