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:

  1. Ambient: Llum ambiental que afecta tota la superfície de manera uniforme.
  2. Difusa: Llum que es dispersa en totes direccions des de la superfície.
  3. 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

  1. Escriu un shader que implementi el model d'il·luminació Phong.
  2. Prova el shader amb diferents configuracions de llum i materials.

Exercici 2: Implementar el Mapeig de Normals

  1. Escriu un shader que utilitzi una textura de normals per simular detalls de superfície.
  2. Prova el shader amb diferents textures de normals.

Exercici 3: Implementar el Parallax Mapping

  1. Escriu un shader que utilitzi parallax mapping per afegir profunditat a les textures.
  2. 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.

© Copyright 2024. Tots els drets reservats