En aquest tema, explorarem tècniques d'ombrejat avançades que permeten crear efectes visuals més realistes i complexos en les aplicacions gràfiques. Aquestes tècniques inclouen l'ús de shaders personalitzats, models d'il·luminació avançats i altres mètodes per millorar la qualitat de la renderització.
Continguts
Introducció als Shaders Avançats
Els shaders són programes que s'executen en la GPU i controlen com es renderitzen els píxels i els vèrtexs. Els shaders avançats permeten implementar tècniques d'ombrejat més complexes que les que es poden aconseguir amb els shaders bàsics.
Tipus de Shaders
- Vertex Shader: Processa cada vèrtex individualment.
- Fragment Shader: Processa cada fragment (píxel potencial) individualment.
- Geometry Shader: Processa primitives geomètriques com triangles, línies, etc.
- Tessellation Shaders: Divideixen les primitives en parts més petites per a un detall més gran.
- Compute Shaders: Permeten càlculs generals a la GPU.
Model d'Il·luminació Phong
El model d'il·luminació Phong és un dels models més utilitzats per simular la reflexió de la llum en superfícies. Es compon de tres components principals:
- Ambient: Llum ambiental que afecta tota la superfície de manera uniforme.
- Difusa: Llum que es dispersa en totes direccions des de la superfície.
- Especular: Llum que es reflecteix en una direcció específica, creant punts brillants.
Implementació del Model Phong
#version 330 core in vec3 FragPos; in vec3 Normal; out vec4 FragColor; uniform vec3 lightPos; uniform vec3 viewPos; uniform vec3 lightColor; uniform vec3 objectColor; void main() { // Ambient float ambientStrength = 0.1; vec3 ambient = ambientStrength * lightColor; // Difusa vec3 norm = normalize(Normal); vec3 lightDir = normalize(lightPos - FragPos); float diff = max(dot(norm, lightDir), 0.0); vec3 diffuse = diff * lightColor; // Especular float specularStrength = 0.5; vec3 viewDir = normalize(viewPos - FragPos); vec3 reflectDir = reflect(-lightDir, norm); float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32); vec3 specular = specularStrength * spec * lightColor; vec3 result = (ambient + diffuse + specular) * objectColor; FragColor = vec4(result, 1.0); }
Model d'Il·luminació Blinn-Phong
El model Blinn-Phong és una variació del model Phong que utilitza un vector de mig camí per calcular la component especular, fent-lo més eficient en alguns casos.
Implementació del Model Blinn-Phong
#version 330 core in vec3 FragPos; in vec3 Normal; out vec4 FragColor; uniform vec3 lightPos; uniform vec3 viewPos; uniform vec3 lightColor; uniform vec3 objectColor; void main() { // Ambient float ambientStrength = 0.1; vec3 ambient = ambientStrength * lightColor; // Difusa vec3 norm = normalize(Normal); vec3 lightDir = normalize(lightPos - FragPos); float diff = max(dot(norm, lightDir), 0.0); vec3 diffuse = diff * lightColor; // Especular float specularStrength = 0.5; vec3 viewDir = normalize(viewPos - FragPos); vec3 halfwayDir = normalize(lightDir + viewDir); float spec = pow(max(dot(norm, halfwayDir), 0.0), 32); vec3 specular = specularStrength * spec * lightColor; vec3 result = (ambient + diffuse + specular) * objectColor; FragColor = vec4(result, 1.0); }
Mapeig de Normals
El mapeig de normals és una tècnica que permet simular detalls de superfície complexos utilitzant una textura de normals. Això permet afegir detalls sense augmentar el nombre de polígons.
Implementació del Mapeig de Normals
#version 330 core in vec3 FragPos; in vec3 Normal; in vec2 TexCoords; out vec4 FragColor; uniform sampler2D normalMap; uniform vec3 lightPos; uniform vec3 viewPos; uniform vec3 lightColor; uniform vec3 objectColor; void main() { // Obtenir la normal del mapa de normals vec3 normal = texture(normalMap, TexCoords).rgb; normal = normalize(normal * 2.0 - 1.0); // Convertir de [0,1] a [-1,1] // Ambient float ambientStrength = 0.1; vec3 ambient = ambientStrength * lightColor; // Difusa vec3 lightDir = normalize(lightPos - FragPos); float diff = max(dot(normal, lightDir), 0.0); vec3 diffuse = diff * lightColor; // Especular float specularStrength = 0.5; vec3 viewDir = normalize(viewPos - FragPos); vec3 reflectDir = reflect(-lightDir, normal); float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32); vec3 specular = specularStrength * spec * lightColor; vec3 result = (ambient + diffuse + specular) * objectColor; FragColor = vec4(result, 1.0); }
Parallax Mapping
El parallax mapping és una tècnica que millora el mapeig de normals afegint una sensació de profunditat a les textures. Això es fa desplaçant les coordenades de textura en funció de la vista de l'observador.
Implementació del Parallax Mapping
#version 330 core in vec3 FragPos; in vec3 Normal; in vec2 TexCoords; out vec4 FragColor; uniform sampler2D normalMap; uniform sampler2D heightMap; uniform vec3 lightPos; uniform vec3 viewPos; uniform vec3 lightColor; uniform vec3 objectColor; vec2 ParallaxMapping(vec2 texCoords, vec3 viewDir) { float height = texture(heightMap, texCoords).r; float scale = 0.1; // Escala de desplaçament return texCoords - viewDir.xy * (height * scale); } void main() { // Calcular la direcció de la vista vec3 viewDir = normalize(viewPos - FragPos); // Aplicar parallax mapping vec2 parallaxTexCoords = ParallaxMapping(TexCoords, viewDir); // Obtenir la normal del mapa de normals vec3 normal = texture(normalMap, parallaxTexCoords).rgb; normal = normalize(normal * 2.0 - 1.0); // Convertir de [0,1] a [-1,1] // Ambient float ambientStrength = 0.1; vec3 ambient = ambientStrength * lightColor; // Difusa vec3 lightDir = normalize(lightPos - FragPos); float diff = max(dot(normal, lightDir), 0.0); vec3 diffuse = diff * lightColor; // Especular float specularStrength = 0.5; vec3 reflectDir = reflect(-lightDir, normal); float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32); vec3 specular = specularStrength * spec * lightColor; vec3 result = (ambient + diffuse + specular) * objectColor; FragColor = vec4(result, 1.0); }
Exercicis Pràctics
Exercici 1: Implementar el Model d'Il·luminació Phong
- Escriu un shader que implementi el model d'il·luminació Phong.
- Prova el shader amb diferents configuracions de llum i materials.
Exercici 2: Implementar el Mapeig de Normals
- Escriu un shader que utilitzi una textura de normals per simular detalls de superfície.
- Prova el shader amb diferents textures de normals.
Exercici 3: Implementar el Parallax Mapping
- Escriu un shader que utilitzi parallax mapping per afegir profunditat a les textures.
- Prova el shader amb diferents textures de desplaçament.
Conclusió
En aquesta secció, hem explorat diverses tècniques d'ombrejat avançades que permeten crear efectes visuals més realistes i complexos. Hem après a implementar els models d'il·luminació Phong i Blinn-Phong, així com tècniques com el mapeig de normals i el parallax mapping. Aquestes tècniques són fonamentals per millorar la qualitat visual de les aplicacions gràfiques i oferir una experiència més immersiva als usuaris.
En el següent tema, explorarem la renderització instanciada, una tècnica que permet renderitzar múltiples còpies d'un objecte de manera eficient.
Curs de Programació OpenGL
Mòdul 1: Introducció a OpenGL
- Què és OpenGL?
- Configurar el Teu Entorn de Desenvolupament
- Crear el Teu Primer Programa OpenGL
- Entendre el Pipeline d'OpenGL
Mòdul 2: Renderització Bàsica
- Dibuixar Formes Bàsiques
- Entendre les Coordenades i les Transformacions
- Coloració i Ombrejat
- Ús de Buffers
Mòdul 3: Tècniques de Renderització Intermèdies
- Textures i Mapeig de Textures
- Il·luminació i Materials
- Barreja i Transparència
- Prova de Profunditat i Prova de Plantilla
Mòdul 4: Tècniques de Renderització Avançades
Mòdul 5: Optimització del Rendiment
- Optimitzar el Codi OpenGL
- Ús d'Objectes de Matriu de Vèrtexs (VAOs)
- Gestió Eficient de la Memòria
- Perfilat i Depuració