Introducción a los Shader (GLSL)

Los shaders no son mas que simples programas que se ejecutan en el procesador gráfico GPU, son escritos en el lenguaje GLSL, el cual es bastante similar a C, como cualquier otro programa los shaders deben ser compilados y enlazados, solo que el compilador y el enlazador o linker están integrados en la API OpenGL.

OpenGL cuenta con diferentes tipos de shaders, cada uno en su nivel correspondiente, de momento veremos los dos shaders requeridos Vertex Shader en el primer nivel y Fragment Shader en el último, los demos están ubicados entre estos dos y son opcionales.

opengl pipeline
Vertex shader, en su forma mas simple:

#version 330 core

void main()
{
     gl_Position = vec4(0,0,0,1);
}

Como cualquier programa tenemos el ya conocido método void main() { } este es el punto de entrada o inicio de la aplicación, gl_Position es una variable reservada del shader, nos sirve para indicar la posición de los vértices en los cuatro componentes (X, Y, Z, W), en este caso nos dice que debemos dibujar en la coordenada (0,0,0), el valor W lo establecemos a 1, mas adelante lo explicaremos, #version 330 core indica qué versión manejaremos.

Fragment Shader, establece el color de salida a verde:

#version 330 core

out vec4 fColor;

void main()
{
     fColor = vec4(0.0, 1.0, 0.0, 1.0);
}

Este shader presenta una variable de salida fColor de cuatro componentes que esta vez representan un color (R, G, B, A), sus canales rojo, verde, azul y opacidad, en este shader se genera el color de salida de la figura a dibujar.


Compilar y enlazar los shaders


Lo primero que haremos será crear un shader object que nos permitirá añadirle código para luego compilarlo, hacemos esto para ambos shaders, el código fuente puede ser leído de un archivo externo, o directamente de un arreglo de caracteres.

GLuint compileShader(std::string& source, GLenum type) {

    GLuint shader{ 0 };

    if (!source.empty()) {
        const GLchar* code = const_cast<GLchar*>(source.c_str());

        // crear el shader object
        shader = glCreateShader(type);
        // agregar el codigo al sahder
        glShaderSource(shader, 1, &code, NULL);
        // compilar el shader indicado
        glCompileShader(shader);

        logShader(shader, type == GL_FRAGMENT_SHADER ? "Fragment" : "Vertex");
    }

    return shader;
}

Una vez tengamos los shaders compilados debemos crear un programa que los ejecutará, para ello primero creamos el programa y luego enlazamos ambos shaders a este programa.

GLuint createProgram(GLuint vertex, GLuint fragment) {
    // crear el program object
    program = glCreateProgram();

    // añadir los shaders al programa
    glAttachShader(program, vertex);
    glAttachShader(program, fragment);

    // enlazar el programa
    glLinkProgram(program);

    logLink();

    // eliminar los shaders, ya no son necesarios
    glDeleteShader(vertex);
    glDeleteShader(fragment);

    return program;
}

Podemos obtener información sobre la compilación y enlazado de los shaders, los métodos logShader() y logLink() hacen estas tareas, son de mucha ayuda para detectar errores en nuestro código GLSL.

Dibujar en pantalla


Para dibujar en pantalla lo primero que haremos será limpiar la misma, glClear(GL_COLOR_BUFFER_BIT); limpiar el buffer de color, debemos indicarle al comando de dibujo que shader usaremos glUseProgram(program);

Finalmente usaremos el comando de dibujo glDrawArrays(GL_POINTS, 0, 1); este le indica al vertex shader como dibujar los vértices almacenados, GL_POINT es el modo de dibujo, indica que se debe dibujar un punto, 0 el índice del primer vértice y 1 la cantidad de vértices a dibujar.

De momento no tenemos vértices almacenados en memoria y nuestro vertex shader es estático tiene una posición fija definida, gl_Position = vec4(0,0,0,1); por lo que la salida será un punto justo en el centro de la pantalla, usamos glPointSize(15.0f); para aumentar el tamaño del punto y que este sea visible fácilmente.

class Tutorial_01 : public OpenGLWindow {

    void onstart() override {
        glPointSize(15.0f);
        shader_simple.compile("simple.vertex_shader", "simple.fragment_shader");
    }

    void onrender(double time) override {
        glClear(GL_COLOR_BUFFER_BIT);
        shader_simple.use();
        glDrawArrays(GL_POINTS, 0, 1);
    }

    OpenGLShader shader_simple;
};

OpenGLShader es la clase C++ que encapsula las tareas de creación, compilación, enlazado y detección de errores de los shaders.

opengl compilar shader
Esta es la salida de nuestro primer programa hecho con API moderna de OpenGL, de momento la posición y color del punto son fijos, una aplicación real como un juego por ejemplo requiere del manejo de millones de vértices y colores diferentes por lo que en la siguiente publicación veremos como enviarle un conjunto de vértices y colores a nuestros shaders.

GitHut: Vertex y Fragment Shader en OpenGL

Comentarios

Temas relacionados

Entradas populares de este blog

tkinter Grid

tkinter Canvas

Histogramas OpenCV Python

Python Binance API