Los PS no tienen acceso a las propiedades del vértice del que procede un cierto fragmento, como puede ser la normal asociada. Es lógico, ya que no tiene sentido realizar cálculos sobre la geometría para cada uno de los fragmentos de la escena, es mucho más eficiente llevarlos a cabos por vértice, es decir, en el VS.
Con la pareja de VS-PS desarrollados a continuación se pretende colorear la escena a la que se apliquen usando únicamente cinco colores: blanco, rojo, verde, azul y amarillo. El color asignado a cada fragmento dependerá de la intensidad con la que esté incidiendo la luz en el mismo. Dicha intensidad dependerá de dos parámetros: la posición en que esté situada la luz y la normal de cada vértice de la geometría.
El trabajo del VS será, precisamente, calcular ese nivel de intensidad, almacenándolo en un parámetro varying para que el PS pueda usarla posteriormente. El código del VS es el mostrado a continuación:
Al declarar como uniform
el parámetro dirLuz
, éste vendrá determinado por la aplicación de forma que la dirección de donde procede la luz pueda alterarse fácilmente.
La intensidad se calcula mediante el operador dot
(producto punto entre vectores), lo cual equivaldría a hallar el coseno del ángulo que forman el rayo incidente de luz y la normal del vértice. El resultado será un valor comprendido entre 0.0 y 1.0, valor que queda almacenado en intensidad
.
La última sentencia del VS establece la posición del vértice que, como puede verse, no se altera en ningún momento, sencillamente se le aplica la matriz de modelo-vista y transformación. No habría ningún problema en aplicar también transformaciones como las descritas antes, en los VS de ejemplo de entradas previas.
En la implementación del PS ignoraremos por completo la información de color almacenada en gl_Color
(de hecho en el VS no se ha asignado valor alguno a gl_FrontColor
ni gl_BackColor
), asignando a gl_FragColor
un color u otro dependiendo de la intensidad calculada previamente en el VS.
Las versiones actuales de VS y PS permiten emplear ciertas estructuras de control, entre ellas condicionales del tipo if-then-else
y también bucles for
y while
. Al codificar el PS será necesario asignar a gl_FragColor
un valor u otro dependiendo de la evaluación del parámetro intensidad
, tarea para la que he usado un clásico if-else
como se aprecia en el código siguiente:
Una vez compilados y enlazados al programa los shaders, será necesario cargar algún modelo geométrico en la escena, por ejemplo la tetera, y a continuación definir la dirección de la luz. Shader Maker dispone de una página, con el título Uniforms, en la que aparecen los parámetros de comunicación entre los shaders y la aplicación, de forma que sea posible modificarlos durante la ejecución.
En la figura inferior puede verse el resultado de la ejecución de estos shaders. Rotando la tetera, así como alterando la dirección de la luz con los controles que hay en la parte inferior, el coloreado va cambiando de manera continua.
La aplicación de texturas en un PS es tan sencilla como el uso del color interpolado que se almacena en gl_Color
. No hay más que tomar las coordenadas de textura y usarlas para recuperar el color de la textura, con una sentencia del tipo gl_FragColor = texture2D(textura, gl_TexCoord[0].st)
.