Logo de Torre de Babel
Portada Libros Artículos Perfil Scholar

Shaders en la práctica (V) - Colorear según la intensidad de luz incidente

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:


// La dirección de la luz se establecerá externamente
uniform vec3 dirLuz;

// Cálculo de la intensidad de luz recibida
varying float intensidad;

void main()
{
    // Producto punto entre la dirección de la luz
    // y la normal del vértice que esta procesándose
    intensidad = dot(dirLuz, gl_Normal);

   // No se altera la posición
    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}

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:


// Parámetro procedente del VS
varying float intensidad;

void main()
{
    // Dependiendo de la intensidad
    if (intensidad > 0.95) // se asigna un color blanco
        gl_FragColor = vec4(1.0,1.0,1.0,1.0);
    else if (intensidad > 0.75) // rojo
        gl_FragColor = vec4(1.0,0.0,0.0,1.0);
    else if (intensidad > 0.5) // verde
        gl_FragColor= vec4(0.0,1.0,0.0,1.0);
    else if (intensidad > 0.25) // azul
        gl_FragColor = vec4(0.0,0.0,1.0,1.0);
    else // o amarillo
        gl_FragColor = vec4(1.0,1.0,0.0,1.0);
}

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).


Publicado el 7/9/2010

Curso de shaders

Torre de Babel - Francisco Charte Ojeda - Desde 1997 en la Web