Buscar..


Modelo de iluminación phong

NOTA: Este ejemplo es WIP, se actualizará con diagramas, imágenes, más ejemplos, etc.

¿Qué es Phong?

Phong es un modelo de luz muy básico, pero de aspecto real para superficies que tiene tres partes: iluminación ambiental, difusa y especular.

Iluminación ambiental:

La iluminación ambiental es la más simple de las tres partes para entender y calcular. La iluminación ambiental es una luz que inunda la escena e ilumina el objeto uniformemente en todas las direcciones.

Las dos variables en la iluminación ambiental son la fuerza del ambiente y el color del ambiente. En tu sombreador de fragmentos, lo siguiente funcionará para el ambiente:

in vec3 objColor;

out vec3 finalColor;

uniform vec3 lightColor;

void main() {
   float ambientStrength = 0.3f;
   vec3 ambient = lightColor * ambientStrength;
   finalColor = ambient * objColor;
}

Iluminación difusa:

La iluminación difusa es un poco más compleja que la ambiental. La luz difusa es luz direccional, lo que significa que las caras que miran hacia la fuente de luz estarán mejor iluminadas y las que apuntan hacia afuera se oscurecerán debido a la forma en que la luz las golpea.

Nota: la iluminación difusa requerirá el uso de normales para cada cara, que no mostraré cómo calcular aquí. Si quieres aprender cómo hacerlo, consulta la página de matemáticas 3D.

Para modelar el reflejo de la luz en gráficos de computadora se utiliza una función de distribución de reflectancia bidireccional (BRDF). BRDF es una función que proporciona la relación entre la luz reflejada a lo largo de una dirección saliente y la luz incidente de una dirección entrante.

Una superficie difusa perfecta tiene un BRDF que tiene el mismo valor para todas las direcciones incidentes y salientes. Esto reduce sustancialmente los cálculos y, por lo tanto, se usa comúnmente para modelar superficies difusas, ya que es físicamente plausible, aunque no haya materiales difusos puros en el mundo real. Este BRDF se llama reflexión de Lambert porque obedece la ley de coseno de Lambert.

La reflexión de Lambert se utiliza a menudo como modelo para la reflexión difusa. Esta técnica hace que todos los polígonos cerrados (como un triángulo dentro de una malla 3D) reflejen la luz por igual en todas las direcciones cuando se procesa. El coeficiente de difusión se calcula a partir del ángulo entre el vector normal y el vector de luz.

f_Lambertian = max( 0.0, dot( N, L )

donde N es el vector normal de la superficie y L es el vector hacia la fuente de luz.

Cómo funciona

En general, el producto de punto de 2 vectores es igual al coseno del ángulo entre los 2 vectores multiplicado por la magnitud (longitud) de ambos vectores.

dot( A, B ) == length( A ) * length( B ) * cos( angle_A_B ) 

De esto se deduce que el producto de puntos de 2 vectores unitarios es igual al coseno del ángulo entre los 2 vectores, porque la longitud de un vector unitario es 1.

uA = normalize( A )
uB = normalize( B )
cos( angle_A_B ) == dot( uA, uB )

introduzca la descripción de la imagen aquí

Si observamos la función cos (x) entre los ángulos -90 ° y 90 °, podemos ver que tiene un máximo de 1 en un ángulo de 0 ° y baja a 0 en los ángulos de 90 ° y -90 °.

introduzca la descripción de la imagen aquí

Este comportamiento es exactamente lo que queremos para el modelo de reflexión. Cuando el vector nromal de la superficie y la dirección a la fuente de luz están en la misma dirección (el ángulo entre es 0 °), queremos un máximo de reflexión. En contraste, si los vectores son ortonormalizados (el ángulo intermedio es de 90 °), entonces queremos un mínimo de reflexión y queremos un funcionamiento funcional suave y continuo entre los dos bordes de 0 ° y 90 °.

introduzca la descripción de la imagen aquí

Si la luz se calcula por vértice, la reflexión se calcula para cada esquina de la primitiva. Entre las primitivas, las reflexiones se interpolan según sus coordenadas baricéntricas. Ver las reflexiones resultantes sobre una superficie esférica:

introduzca la descripción de la imagen aquí

Ok, para comenzar con nuestro sombreador de fragmentos, necesitaremos cuatro entradas.

  • Normales de vértices (deben estar en el búfer y especificados por punteros de atributo de vértice)
  • Posición del fragmento (debe salir del sombreado de vértice en el sombreador de fragmentos)
  • Posición de la fuente de luz (uniforme)
  • Color claro (uniforme)
in vec3 normal;
in vec3 fragPos;
    
out vec3 finalColor;
    
uniform vec3 lightColor;
uniform vec3 lightPos;
uniform vec3 objColor;

Dentro de main es donde necesitamos hacer algunos cálculos. Todo el concepto de iluminación difusa se basa en el ángulo entre la dirección normal y la luz. Cuanto mayor es el ángulo, menos luz hay hasta 90 ° donde no hay luz en absoluto.

Antes de que podamos comenzar a calcular la cantidad de luz, necesitamos el vector de dirección de la luz. Esto se puede recuperar simplemente restando la posición de la luz de la posición del fragmento que devuelve un vector de la posición de la luz apuntando a la posición del fragmento.

vec3 lightDir = lightPos-fragPos;

Además, siga adelante y normalice los vectores normal y lightDir para que tengan la misma longitud con la que trabajar.

normal  = normalize(normal);
lightDir = normalize(lightDir);

Ahora que tenemos nuestros vectores, podemos calcular la diferencia entre ellos. Para hacer esto, vamos a utilizar la función de producto punto. Básicamente, esto toma 2 vectores y devuelve el cos () del ángulo formado. Esto es perfecto porque a 90 grados producirá 0 y a 0 grados dará 1. Como resultado, cuando la luz apunta directamente al objeto, estará completamente iluminado y viceversa.

float diff = dot(normal, lightDir);

Hay una cosa más que tenemos que hacer con el número calculado, tenemos que asegurarnos de que siempre sea positivo. Si lo piensas, un número negativo no tiene sentido en contexto porque eso significa que la luz está detrás de la cara. Podríamos usar una sentencia if, o podemos usar la función max() que devuelve el máximo de dos entradas.

diff = max(diff, 0.0);

Una vez hecho esto, ahora estamos listos para calcular el color de salida final para el fragmento.

vec3 diffuse = diff * lightColor;
finalColor = diffuse * objColor;

Debe tener un aspecto como este: introduzca la descripción de la imagen aquí

Iluminación especular:

Trabajo en progreso, vuelva a consultar más tarde.

Conjunto

Trabajo en progreso, vuelva a consultar más tarde.

El siguiente código e imagen muestran estos tres conceptos de iluminación combinados.



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow