04 - Introducción a los shaders (GLSL)

Para entendernos, los shaders son instrucciones que se ejecutan directamente en la GPU de la tarjeta gráfica. Las GPUs (Unidades de Procesamiento Gráfico) están especializadas en realizar ciertos tipos de operaciones que hacen que la ejecución de shaders en ellas acelere notablemente el rendimiento de nuestras aplicaciones así como también le ahorran ejecución a la CPU de nuestro PC. Es por todo esto que se recomienda el uso de shaders.

Con el tiempo se han desarrollado muchas ténicas de renderizado gracias a los shaders que han permitido llegar al nivel de detalle que podemos ver en los juegos actuales.

En el caso de OpenGL, vamos a introducir 2 tipos de shaders inicialmente, los VertexShaders que se ejecutan por cada vértice que le introduzcamos al pipeline (este término se usa en muchos de los artículos que encontraréis por internet y se refiere al trabajo en cadena que acaba con el renderizado final) y, por otra parte, los FragmentShaders (PixelShaders en Directx) que se ejecutan por cada 'fragmento' que se renderice,  estos fragmentos los podemos identificar por píxeles por ahora aunque no lo son al 100%.



Al igual que hicimos con las texturas, lo primero será cargar los shaders que vamos a usar que, en este caso serán 2:

-VertexShader:

            string dsVertexShader = @"
            void main()
            {
                gl_FrontColor = gl_Color;
                gl_Position = ftransform();
            }"
Tan solo realiza 2 acciones, asigna el color del vertice entrante al del vertice saliente y le asigna a la posición del vertice en pantalla la función fttransform() que lo que hace es multiplicar el vector de posición por las matrices de transformación necesarias, estas mtrices las explicaremos más adelante también para poder realizar operaciones más complejas en los shaders.

-FragmentShader:

            string dsFragmentShader = @"
            void main()
            {
                gl_FragColor = gl_Color;
            }"

Que simplemente le asigna al color de salida el color que le llega de entrada. Es decir, no se realiza ninguna operación con el color.

Ahora crearemos una función para crear un shader a partir de un string:

        int LoadShader(string fsShader, ShaderType foType)
        {
            int id = GL.CreateShader(foType);
            GL.ShaderSource(id, fsShader);
            GL.CompileShader(id);
            return id;
        }
Usando estas funciones:

            int vertexShader = LoadShader(dsVertexShader, ShaderType.VertexShader);
            int pixelShader = LoadShader(dsFragmentShader, ShaderType.FragmentShader);

 Ahora, una vez cargados los shaders hay que crear un programa que los contenga:

        int CreateProgram(int fiVertexShader, int fiFragmentShader)
        {
            int program = GL.CreateProgram();
            GL.AttachShader(program, fiVertexShader);
            GL.AttachShader(program, fiFragmentShader);
            GL.LinkProgram(program);
            GL.UseProgram(program);
            string programInfoLog;
            GL.GetProgramInfoLog(program, out programInfoLog);
         
            //La variable programInfoLog contiene los errores producidos en el shader en caso de haberlos al compilar
         
            return program;
        }
Usando esta función compilaremos los dos shaders en un programa:
        int diProgram = CreateProgram(vertexShader, pixelShader); 
Si esto que acabamos de hacer lo incluimos en el OnLoad() de nuestra clase base, conseguiremos que se usen estos dos shaders para renderizar los dos triangulos del post anterior pero sin texturizar. Ahora, para jugar un poco podríamos modificar el FragmentShader por ejemplo así:
gl_FragColor = gl_Color*0.5;
Esto oscurecerá un poco los colores de manera que observaremos que realmente es el FragmentShader el que está decidiendo los colores a renderizar.

Hemos transformado nuestro pequeño componente renderizador de la Fixed Function Pipeline a la Programable Pipeline lo que os ayudará a saber que esto solo significa que se están usando shaders para el renderizado, estos términos también los veréis aparecer en muchos de los artículos que circulan por internet.

Eso es todo por hoy, tendremos que seguir con el texturiado con shaders pero eso, será en otro post.

2 comentarios:

  1. Estupendo socio, otra entrada en la que sigo los pasos y me compila todo estupendamente. La cosa es que aún no le encuentro mucha utiilidad a los Shaders, simplemente me cambia el contraste de la imagen si modifico el multiplicador del FragmentShader.

    A ver si en el siguiente tuto le veo alguna utilidad práctica al utilizar los Shaders con las texturas.

    Increible saga de tutos de OpenTK socio, ojalá nunca se acabe. Saludos! ^^

    ResponderEliminar
  2. Hola. ojalá sigan los tutoriales, es el año 2016 y ando empezando en opengl y c#

    ResponderEliminar