Modelo de Iluminación Phong en 2D

El modelo de iluminación Phong, o modelo de reflexión Phong, o simplemente Phong Lighting es un modelo empírico de iluminación local de puntos sobre una superficie (1973). Este describe el comportamiento de la reflexión de la luz sobre una superficie expresada como una combinación de un término ambiental, término difuso (reflexión difusa) y un término especular (reflexión especular).

Modelo de Iluminación Phong. Extraído de Wikipedia

La mayoría de los sistemas gráficos aplican este modelo para la iluminación de modelos geométricos o volumétricos. Sin embargo, éstos requieren modelos 3D para su cálculo. Entonces, ¿es posible realizar dicho modelo en un sistema de despliegue con primitivas de despliegue 2D?La respuesta es sí. La idea principal es simular el proceso de visualización completo asumiendo la presencia de una instrucción DrawPixel que solo enciende/apaga un píxel de un color sobre cualquier dispositivo de salida. Obviamente, esto requiere un arduo trabajo porque se deben definir todas las estructuras de datos algebraicas necesarias para dicho proceso (matrices, vectores y sus operaciones asociadas).

Respecto a la teoría detrás del modelo de iluminación Phong … hay mucho de ello en la red. La mayoría basado en sistemas de despliegue gráfico estándar como OpenGL, WebGL o Direct3D. En la página de Widget 101 (¡amigos de este blog!) hay información referente a los modelos de iluminación local y el modelo de iluminación Phong.

Para probar un sistema de despliegue de píxeles sobre el dispositivo de salida (2D) se puede emplear cualquier modelo geométrico sobre cualquier lenguaje de programación. Sin embargo, en este post utilizaremos un Canvas de HTML5 sobre Javascript y sin emplear un modelo geométrico sino una simple ecuación 🙂

La idea es la siguiente: dibujar un círculo en 2D empleando la ecuación del círculo x^2 + y^2 = r^2 para un círculo centrado en el (0,0). Luego, se obtiene la coordenada z aplicando la análoga a la anterior pero 3D, es decir, una esfera: x^2 + y^2 + z^2= r^2. Con ello, ya se obtiene un punto 3D (x,y,z) y partiendo del origen (0,0,0) se construye su vector normal N. Con todos esos valores y fijando posiciones fijas para la posición de la luz y vector de visualización se calcula la iluminación con el modelo Phong. Veamos este proceso paso a paso.

Dibujar la esfera

Emplearemos el template de dibujado de píxel de HTML5 para empezar a trabajar. El primer paso es definir el radio r del círculo a dibujar (por ejemplo r = 150 píxeles). Ahora se recorre píxel a píxel todo el cuadrado que envuelve al círculo (bounding box), es decir, dado que se asume está centrado en la posición (0,0) entonces su dominio es [-r_y, +r_y] en el eje Y y [-r_x, +r_x]. Para cada posición la idea es determinar si dicho píxel está o no dentro del círculo. El código en JS sería algo como:

var r = 150;
for (y = -r; y <= r; y++)
{
  for (x = -r; x <= r; x++)
  {
    if (Math.pow(x,2) + Math.pow(y,2) <= Math.pow(r,2))
      setPixel(context, x, y, 0, 255, 128);
  }
}

Obviamente, en dicho código no es eficiente pero explica el concepto claramente. En la línea 6 se observa el condicional que verifica si el punto (x,y) pertenece o no al círculo. Al hacer esta operación el resultado visual es el siguiente:
circle2D1
¿Por qué aparece cortado el círculo? Solo se muestra 1/4 del círculo, y esto es debido a que se encuentra centrado en el origen. Para poder verlo completo se debe trasladar hacia el centro:

offsetX = canvas.width / 2;
offsetY = canvas.height / 2;
// ...
setPixel(context, x + offsetX, y + offsetY, 0, 255, 128);

El resultado visual ahora es:
circle2D2

Ya en el mundo 3D

Ya el primer paso está listo, ahora para llevar el círculo a una esfera (ya sabemos que puntos (x,y) pertenecen dentro del círculo) se calcula su coordenada z. Para ello, se despeja z de la ecuación de la esfera centrada en el origen quedando como z = \sqrt{r^2 - x^2 - y^2}. Con ello, ya tenemos un punto P = (x,y,z) sobre la superficie de la esfera. Adicionalmente, si a dicho punto P se le resta el origen y se normaliza, tendremos el vector normal N para cada punto.

Ya con dichos valores, queda configurar los parámetros de la luz, el material de la esfera y el observador. Un ejemplo de ello puede ser:

var lightA = [0.2, 0.2, 0.2]; // término ambiental de la luz
var lightD = [0.8, 0.5, 0.9]; //término difuso de la luz
var lightS = [1.0, 0.9, 1.0]; //término especular de la luz
var lightP = [1.0, 0.5, 2.5]; //posición de la luz
var Ka = [0.9, 0.1, 0.1]; //componente ambiental del material
var Kd =[0.1, 0.9, 0.5]; //componente difuso del material
var Ks = [0.9,0.9, 0.9]; //componente especular del material
var shininess = 160; //término constante para el material
var V = normalize(O - P); //O es el origen (0,0,0) y P un punto. V el vector visión
var L = normalize(lightP - P); //L representa el vector normalizado de la luz

Entonces, la iluminación L de un punto P con vector normal N empleando el modelo de iluminación Phong (para una sola luz), es el resultado de combinar los 3 términos como:
L = (lightA \times Ka) + (Kd \times lightD \times (L \cdot N)) + (Ks \times (R \cdot V)^{shininess} \times lightS)

En esta ecuación, la única incógnita es el vector R, el cual se calcula como R = 2 \times (L \cdot N) \times N - L. Para este cálculo se puede utilizar el modelo Blinn-Phong el cual es una modificación al modelo Phong asumiendo que el observador y la luz están en el infinito.

Así, empleando nuestros valores, podemos componer nuestra esfera en ambiental + difuso + especular y obtener el resultado deseado 😀

phong2D

De izquierda a derecha: Ambiental, Difuso, Especular, modelo Phong

Es importante recordar que todos nuestros valores están en el rango [0,1]. Para obtener un color debe ser escalado al rango [0,255]. Luego de las operaciones de suma de los términos, es posible que dichos valores se encuentren fuera del rango [0,1] por lo que se debe garantizar que no estén fuera de éste (truncamiento a 0 y 1) antes de llevar al rango de color.

Ya en este punto, es posible agregar componentes/controladores para modificar cada uno de los parámetros de la luz y del material, así como mover la posición de la luz con el puntero (mouse o touch) a fin de obtener otros resultados

completePhong2D

Resumen: El proceso de iluminación local (en este post modelo Phong) se puede aplicar en cualquier sistema que nos provea ¡¡un simple DrawPixel!! 🙂

Acerca de smittynpro

Escribiendo algunas cosas de computación gráfica
Esta entrada fue publicada en Algoritmos, Código y etiquetada , , , . Guarda el enlace permanente.

6 respuestas a Modelo de Iluminación Phong en 2D

  1. aarroyoc dijo:

    Muy interesante. Un resultado sorprendente usando solo 2D. Lo que da de si el álgebra recreativa.

    Me gusta

  2. disculpa amigo podrias ayudarme aplicandolo en un cubo haciendo con el mismo metodo que aplicaste en la esfera

    Me gusta

    • esmitt dijo:

      Hola, un cubo son 6 planos, cada plano tiene un vector normal canónico, la idea es muy similar pero ahora en vez de r, tienes a l, que representa la dimensión de un lado del cubo.

      Me gusta

  3. Hola, ¿amigo conoces otro modelo que NO sea el phong?

    Me gusta

    • smittynpro dijo:

      Hola, si claro. Depende del efecto que quieres lograr. Si quieres reflexión especular puedes emplear Blinn-Phong, Cook-Torrance o el modelo de Ward. Si buscas reflexión difusa, puedes intentar Oren-Nayar o Minnaert. Hay otros más, pero esos resultan los más simples de implementar.

      Me gusta

Replica a esmitt Cancelar la respuesta