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).
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 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: . 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 en el eje Y y . 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 pertenece o no al círculo. Al hacer esta operación el resultado visual es el siguiente:
¿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:
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 . 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:
En esta ecuación, la única incógnita es el vector R, el cual se calcula como . 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 😀
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
Resumen: El proceso de iluminación local (en este post modelo Phong) se puede aplicar en cualquier sistema que nos provea ¡¡un simple DrawPixel!! 🙂
Muy interesante. Un resultado sorprendente usando solo 2D. Lo que da de si el álgebra recreativa.
Me gustaMe gusta
Ciertamente, ¡¡y es solo una partecita de ello!!
Me gustaMe gusta
disculpa amigo podrias ayudarme aplicandolo en un cubo haciendo con el mismo metodo que aplicaste en la esfera
Me gustaMe gusta
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 gustaMe gusta
Hola, ¿amigo conoces otro modelo que NO sea el phong?
Me gustaMe gusta
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 gustaMe gusta