Primer acercamiento a Shaders con GLSL

abril 8 20102 comentarios

Guardado en : Programación, Software Libre

Abstract:
Se brinda una introducción al manejo de shaders, particularmente en el lenguaje GLSL. Se muestra su forma de trabajo, su utilización, la instalación de las bibliotecas necesarias para trabajar con GLSL y al final se muestra una pequeña aplicación que hace uso de Shaders.

Introducción

OpenGL (Open Graphics Library) es una especificación desarrollada por Silicon Graphics Inc. para la creación de aplicaciones que produzcan gráficos 2D y 3D.
Para trabajar con OpenGL es recomendable utlizar GLUT (OpenGL Utility Toolkit) el cual es una biblioteca escrita por Mark Kilgard para facilitar el desarrollo de aplicaciones con OpenGL sin entrar en detalles minuciosos de cualquier entorno en particular. No debe confundirse con GLU (OpenGL Utility Library) el cual es un conjunto de funciones de dibujado basadas en las primitivas de OpenGL.

Antecedentes

Anteriormente solo era posible configurar el funcionamiento de la tarjeta gráfica (si la tarjeta lo soportaba) y utilizar los modelos definidos por OpenGL para iluminación, sombreado, etc. No era posible programar la tarjeta gráfica. A partir de 2001 con el advenimiento de las tarjetas NVIDIA GeForce 3 y ATI Raedon 8500 fue posible alcanzar virtualmente grandiosos efectos creando pequeños programas de longitud fija que serían ejecutados por el GPU. Pero tales programas eran difíciles de codificar puesto que se escribían en un lenguaje pseudo-ensamblador y tenían algunas limitaciones.

GLSL (OpenGL Shading Lenguage) es un lenguaje de programación (similar a C/C++) creado por OpenGL ARB (Architecture Review Board) para crear programas (llamados shaders ) que serán ejecutados en la GPU.

GLSL permitió utilizar shaders más complejos y de tamaño variable, subrutinas, ciclos, arreglos de longitud variable, pero sobre todo un lenguaje de programación más familiar para la mayoría de los desarrolladores.

Ventajas

La ventaja de usar shaders es la gran mejora en el despeño de cálculos de ilumincación y transformación de geometría. Por su naturaleza intrínsecamente paralelizable, los shaders pueden aplicar transformaciones sobre un gran conjunto de elementos al mismo tiempo, e.g., cada pixel en un area de la pantalla. Con shaders es posible crear un modelo de ilumincación propio en lugar de usar el modelo fijado para la ilumincación de OpenGL.

Inicialmente los shaders fueron creados para realizar el sombreado (shading) de pixeles (i.e., la variación de niveles de oscuridad en un modelo) y es precisamente de esta técnica por la cual proviene su nombre: shader.

Otro gran beneficio de los shaders es que podemos transferir carga de trabajo, como el movimiento de algunos gráficos, del CPU al GPU, liberando al CPU para que pueda realizar tareas adicionales. También es posible manipular sistemas de partículas, renderizado de alto rango dinámico HDR, iluminación pixel por pixel, cómputo de propósito genreal (GPGPU), etc.

Existen dos tipos de shaders en GLSL: Vertex Shader y Fragment Shader. Ambos forman parte del pipeline de rendering de OpenGL.

Figure 1:

Pipeline, de vértices a fragmentos.

Vertex Shader

Este tipo de shader se ejecutan sobre cada uno de los vértices de entrada al procesador gráfico. Proporcionan un control general sobre todos los vértices, sus datos asociados y su manipulación (transformaciones, normalización, iluminación, color, etc.). Cuando un conjunto completo de vertex shaders es compilado y enlazado, se genera un vertex shader ejecutable que trabaja sobre el procesador de vértices.

Desde otro punto de vista, el vertex shader es un archivo fuente que contiene el código de entrada para el procesador gráfico.

Fragment Shader

Trabaja sobre cada fragmento generado durante el proceso de rasterización, al igual que el vertex shader permite manipular vértices, el fragment shader permite manipular fragmentos (aplicación de texturas, niebla, convoluciones, etc). Cuando un conjunto de fragment shaders es compilado, se genera un fragment shader ejecutable que trabaja sobre el procesador de fragmentos. Un shader de fragmentos no puede cambiar una pocisión de algún fragmento, así como tampoco es permitido el acceso a fragmentos vecinos.

Desde otro punto de vista, el fragment shader es un archivo que contiene el código fuente que se ejecutará en el procesador de fragmentos.

Para fines ilustrativos se nombrará vertShader.glsl al archivo que contiene el código fuente del vertex shader y fragShader.glsl al respectivo archivo del fragment shader. Realmente el nombre y la extensión de estos archivos es irrelevante.

Tipos de datos

Al igual que en C, existen tipos de datos en GLSL para respresentar
diferentes valores, a continuación se presentan los más elementales.

Table 1:
Tipos de datos en GLSL.
Tipo glsl Tamaño Tipo C Descripción
float 32 bits float Valor de coma flotante.
int 32 bits int Valor entero
bool 32 bits int Valor condcional (falso o verdadero).
sampler 32 bits int Manejador de texturas.

Existen vectores de tipos de datos que nos permiten manipular un conjunto (arreglo) del mismo tipo de dato.

vec2, vec3, vec4
Vectores de flotantes de dos, tres y cuatro entradas
pectivamente.
ivec2, ivec3, ivec4
Vectores de enteros de dos, tres y cuatro entradas
respectivamente.
bvec2, bvec3, bvec4
Vectores de booleanos de dos, tres y cuatro entradas
respectivamente.
mat2, mat3, mat4
Matríz de flotantes de 2×2, 3×3 y 4×4 respectivamente.

Instalando y configurando GLSL.

Antes de utlizar shaders en GLSL, se deben tener instaladas algunas bibliotecas que faciltan su desarrollo. Tal es el caso de GLUT y GLEW.

La instalación de GLUT se puede hacer desde cualquier administrador de paquetes. Solo basta con instalar:

  • freeglut3
  • freeglut3-dev
  • libglut3
  • libglut3-dev

Adicionalmente se deben tener los paquetes virtuales:

  • freeglut-dev
  • libglut
  • libglut-dev
  • libhugs-glut

GLEW proporciona mecanismos eficientes que nos permiten determinar cuales extensiones de OpenGL son soportadas por nuestra plataforma.

Para instalar GLEW se puede obtener de:

http://sourceforge.net/projects/glew/files/glew/1.5.3/glew-1.5.3.tgz/download

Lo cual nos permite obtener un archvio llamado glew-1.5.3.tgz. Ahora abrimos una temrminal y escribimos:

$ tar zxvf glew-1.5.3.tgz
$ cd glew-1.5.3.tgz/
$ sudo make
$ sudo make install

Lo cual debe instalar las bibliotecas de GLEW en /usr/include/GL y /usr/lib

Para obtener información adicional se puede consultar:

http://glew.sourceforge.net/install.html

Para compilar en OpenGL:

gcc -lglut -lGL example.c

Para compilar en GLEW:

gcc -lGLEW -lglut -lGL example.c

Prueba de soporte para GLSL

Para comprobar que nuestra instalación de GLEW y de OpenGL ha sido correcta, así como para verificar si nuestra plataforma y tarjeta gráfica soportan los shaders de GLSL, debemos compilar y ejecutar el siguiente código.

/*     *** glslSupport.c ***
@author Jorge Alejandro Gutierrez Orozco
Checks whether the platform supports GLSL shaders with GLEW.
compilation:
    gcc -lglut -lGL -lGLEW glslSupport.c -o glslSupport
execution
    ./glslSupport
*/
 
#include <GL/glew.h>
 
#include <GL/glut.h>
 
#include <stdio.h>
 
#include <stdlib.h>
 
int main(int argc, char *argv[])
{
    glutInit(&amp;argc, argv);
    glutCreateWindow("Are Shaders Suported ?");
 
    /*
    you need to create a valid OpenGL rendering context
    */
    GLenum err = glewInit();
 
    printf("\nis GLEW suported?");
    if (GLEW_OK != err)
    {
       /* glewInit failed */
       printf("\n\tStatus: Error - %s\n", glewGetErrorString(err));
    }
    else
    {
        printf("\n\tStatus: Using GLEW %s\n", glewGetString(GLEW_VERSION));
    }
 
    printf("\nAre vertex and fragments programs suported?");
    if (GLEW_ARB_vertex_program &amp;&amp; GLEW_ARB_fragment_program)
    {
      printf("\n\tIt is safe to use the ARB_programs extensions here.\n\n ");
    }
    else
    {
        printf("\n\tNot Totally ready \n\n");
        exit(1);
    }
 
    return EXIT_SUCCESS;
}

Uso de Shaders

Existen una serie de procesos que se deben hacer para utilizar un shader:

  • Obtener del código fuente del shader a partir de un archivo.
  • Crear los objetos shaders y el objeto programa.
  • Cargar los objetos shaders con el código leído del archivo fuente .
  • Compilar los Objetos Shaders
  • Adjuntar los shaders al objeto programa.
  • Ligar el objeto programa con nuestra aplicación.
  • Indicar a nuestra aplicación que utilice nuestro objeto programa en lugar de las funciones de OpenGL.

Un objeto shader representa el código fuente, un objeto programa representa una parte usable del pipeline de rendering. El objeto programa contiene tanto al objeto vertex shader como al objeto fragment shader.

Descripción

Antes de empezar a utilizar shaders, debemos crear una aplicación OpenGL, puede ser cualquier ejemplo, sería bueno compilarlo y ejecutarlo para comprobar que al menos las bibliotecas de GLUT están correctamente instaladas y nuestro hardware está configurado adecuadamente.

Después debemos agregar la biblioteca de GLEW.

#include <GL/glew.h>

Se deben definir los objetos de los shaders y el objeto programa que los contendrá de manera que sean accesibles de cualquier lugar de nuestra aplicación.

GLhandleARB vShaderObj,fShaderObj,programObject;

A continuación debemos obtener el código fuente de ambos shaders a partir de los archivos.

char *vertexSource = textfileRead("vertShader.glsl");
char *fragmentSource = textfileRead("fragShader.glsl");

Crear los objetos shaders y el objeto programa.

vShaderObj = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);
fShaderObj = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);
programObject = glCreateProgramObjectARB();

Agregar el código fuente obtenido a nuestros objetos shaders.

const char * vs = vertexSource;
const char * fs = fragmentSource;
 
glShaderSourceARB(vShaderObj, 1, &amp;vs,NULL);
glShaderSourceARB(fShaderObj, 1, &amp;fs,NULL);

Donde:

vShaderObj
Objeto shader al cual se le agregará el código fuente obtenido.
1
Número de cadenas enviadas al objeto shader.
vs
Apuntador al código fuente.
NULL
Todas las cadenas son terminadas con NULL.

Para compilar los objetos shaders:

glCompileShaderARB(vShaderObj);
glCompileShaderARB(fShaderObj);

Se debe enlazar los objetos shaders compilados a nuestro objeto programa:

glAttachObjectARB(programObject,vShaderObj);
glAttachObjectARB(programObject,fShaderObj);

Ligar el objeto programa con nuestra aplicación e indicarle que lo use.

glLinkProgramARB(programObject);
 
glUseProgramObjectARB(programObject);

Eliminar un shader

Antes de eliminar un shader se debe desligar de todos los objetos.

void glDetachObjectARB(GLhandleARB container, GLhandleARB attached);
void glDeleteObjectARB(GLhandleARB object);

Ejemplos

Para efectos ilusatrtivos se ha implementado el siguiente ejemplo:

Código de ejemplo glsl

Sin shaders la imagen original resulta:

Veamos el siguiente fragment shader:

/*
fragShader.glsl
*/
void main (void)
{
   gl_FragColor = vec4 (0.5, 0.4, 0.3, 1.0);
}

Y también agregamos el siguiente vertex shader:

/*
vertShader.glsl
*/
void main(void)
{
 vec4 v = gl_Vertex;
 v.x = v.x * 0.2;
 v.y = v.y * 0.6;
 gl_Position = gl_ModelViewProjectionMatrix * v;
}

La imagen obtenida sería similar a la siguiente:

Como se puede observar a la figura, utilizando el mismo código es posible hacer modificaciones a un modelo, en este sencillo ejemplo se escala la figura en el vertex shader y se le cambia el color en el fragment shader.

Problemas comunes.

AL momento de compilar se pueden generar los siguientes errores:

 cannot find -lgl
 cannot find -lglew
ld returned 1 exit status

Basta con reemplazar -lgl y -lglew por -lGL y -lGLEW respectivamente. En sistemas Unix la distinción entre mayúsculas y minúsculas es importante, se observa que -lglut es con minúsculas y además que al incluir las cabeceras en el código estas deben ser con mayúsculas. En caso de que la sintaxis sea correcta lo más seguro es que se hayan instalado incorrectamente las bibliotecas de GLEW o GLUT.

Si la compilación es exitosa, es posible que en la ejecución se presente el siguiente error:

 Segmentation fault

Esto normalmente ocurre cuando GLEW no ha sido aún inicializado, se debe agregar glewInit(); antes de cualquier llamada a alguna función de GLEW.

Si el error persiste entonces los archivos que contienen la fuente de los shaders no han sido encontrados, se debe revisar el nombre de los archivos y la ruta para verificar que su acceso es correcto.

Quizá te interese :

Acerca del autor:

2 Respuestas a “Primer acercamiento a Shaders con GLSL”

  1. [...] This post was mentioned on Twitter by Daniel Doctor. Daniel Doctor said: RT @tweetmeme Primer acercamiento a Shaders con GLSL http://bit.ly/bk9GqG [...]

  2. alejandro dice:

    en GLSL se permite la creacion de arrays sin tamaño, para despues volver a declararlos con tamaño. Me hace falta una situacion en que esto verdaderamente sea util, y haga falta. Alguien que haya trabajado con glsl y tenga mas experiencia podria ayudarme..

Deja un comentario


Licencia y uso

Las técnicas demostradas en los tutoriales pueden ser utilizadas sin ninguna limitación y tampoco es obligatorio dar una atribución.


Los textos, imágenes y tutoriales son propiedad de sus respectivos autores, nuestro contenido se encuentra bajo licencia Creative Commons Share-Alike.

Escribe algo para el sitio

El escribir un tutorial o un artículo, mandarnos un enlace para Ubicuos, no solamente es una forma de obtener publicidad, si no también de dar algo a la comunidad y nosotros te lo recompensamos con los premios del mes! Leer más de nuestras promociones

¿Sugerencias?

Este es TU sitio, si tienes sugerencias o ideas de cómo podemos mejorarlo para ti, ¡Por favor háznoslos saber!

Hacemos nuestro mayor esfuerzo en proporcionar un sitio útil y amigable y esperamos que disfrutes tu tiempo aquí.

Ayuda a Difundir

Te gusta Ubicuos?

Ve las formas en que nos puedes apoyar.

Apoyando a Ubicuos.com

Submit your linkClose

-->