GLSL Vertex Attibute

El vertex shader es la primera etapa programable en el OpenGL pipeline, es el encargado de procesar y transformar la geometría de una figura, se ejecutara una vez por cada vértice que compone dicha figura, antes de comenzar la ejecución del vertex shader primero debemos establecer los datos de entrada, podemos tener de dos tipos: vertex attibute y uniform variable.

Vertex Attibute (in/out)


Contiene la información correspondiente a cada vértice (posición, color, textura, normal, etc.), esta información la provee la CPU mediante el uso de buffers.

Pueden ser de dos tipos in y out, in establece el vertex attibute como un dato de entrada al que podemos tener acceso en el vertex shader, out indica que se trata de un dato de salida, el mismo pasará a la siguiente etapa como un dato de entrada (in).


Transferir Datos de CPU a GPU


Los Vertex Buffer Object (VBO) nos sirven para definir un bloque de memoria en la VRAM de la GPU, con ello podemos enviar grandes cantidades de datos de una manera eficiente.

glGenBuffers(2, buffer);
glBindBuffer(GL_ARRAY_BUFFER, buffer[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertex), vertex, GL_STATIC_DRAW);

La primera línea obtiene dos identificadores disponibles para los buffers.

La función glGenBuffer() crea el buffer del tipo indicado “GL_ARRAY_BUFFER” y le asigna el identificador buffer[0], si el buffer ha sido creado previamente este solo se activa, si pasamos 0 como identificador el buffer es deshabilitado.

Finalmente glBufferData() inserta los datos en la memoria, debemos indicar el tipo de buffer, el tamaño de los datos, los datos y el uso que le daremos a los mismos “GL_STATIC_DRAW”.

glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, NULL);
glEnableVertexAttribArray(0);

Debemos indicarle al vertex shader como interpretar los datos que le enviamos, lo hacemos con la función glVertexAttribPointer() el primer parámetro indica la ubicación del vertex attribute al que pertenecen los datos, el GLSL layout (location = 0) establece la ubicación 0, seguido indicamos la cantidad de componentes, el tipo de datos, si los datos deben ser normalizados, desplazamiento en byte entre dos elementos consecutivos del arreglo de datos.

// vertices para generar un trinagulo 2D
static const float vertex[] =
{
 0.85f, -0.85f, 0.0f, 1.0f,
       -0.85f, -0.85f, 0.0f, 1.0f,
 0.85f,  0.85f, 0.0f, 1.0f
};

// color RGBA para cada vertice
static const float color[] =
{
 1.0f, 0.0f, 0.0f, 1.0f,
 0.0f, 1.0f, 0.0f, 1.0f,
 0.0f, 0.0f, 1.0f, 1.0f
};

Estos son los datos de vértice y color necesarios para dibujar un triángulo, para cada vértice se define un color diferente, la figura es 2D la componentes Z permanece en cero, la componente W la establecemos a uno.

const GLchar* vertex_shader_source =
{
     "#version 330                             \n"
 "                                         \n"
 "layout (location = 0) in vec4 vPosition; \n"
 "layout (location = 1) in vec4 vColor;    \n"
     "                                         \n"
 "out vec4 color;                          \n"
 "                                         \n"
    "void main(void)                          \n"
    "{                                        \n"
    "   gl_Position = vPosition;              \n"
 "   color = vColor;                       \n"
    "}                                        \n"
};

El código GLSL para el vertex shader contiene los atributos de entrada: vPosition y vColor, que corresponden a la posición y color de cada vértice, y el atributo de salida color es usado simplemente para enviar el color a la siguiente etapa, el Fragment Shader, gl_Position es una variable de salida propia del vertex shader, sirve para indicar la posición de un vértice.

vPosition y vColor son interpretados por el vertex shader según lo establecido mediante la función glVertexAttribPointer(), además debemos utilizar la función glEnableVertexAttribArray(n) para establecer la posición (location = n) a la que pertenecen los datos, 0 para vPosition y 1 para vColor.

glBindBuffer(GL_ARRAY_BUFFER, buffer[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertex), vertex, GL_STATIC_DRAW);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, NULL);
glEnableVertexAttribArray(0);

glBindBuffer(GL_ARRAY_BUFFER, buffer[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(color), color, GL_STATIC_DRAW);
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, NULL);
glEnableVertexAttribArray(1);

Un Vertex Array Object (VAO) nos permite manipular de forma sencilla un conjunto de buffers, antes de comenzar a crear un usar los VBO primero creamos un VAO y lo activamos, los VBO que creemos será administrados por este VAO, lo desactivamos usando la función glBindVertexArray(0)
.
GLuint vao_triangle = 0;
glGenVertexArrays(1, &vao_triangle);
glBindVertexArray(vao_triangle);
...
glBindVertexArray(0);

Para desplegar la figura primero activar el shader program que deseamos utilizar, el mismo debió ser compilado y enlazado correctamente, como lo hicimos en el tutorial anterior, lo segundo que haremos será activar el VAO correspondiente a los datos que deseamos desplegar, finalmente usamos el comando de dibujo, glDrawArray(GL_TRIANGLES, 0, 3), indica que la figura de debe dibujar usando triangulación, el vértice inicial es el cero y la cantidad de vértices a dibujar es tres.

Para desplegar la figura primero activar el shader program que deseamos utilizar, el mismo debió ser compilado y enlazado correctamente, como lo hicimos en el tutorial anterior, lo segundo que haremos será activar el VAO correspondiente a los datos que deseamos desplegar, finalmente usamos el comando de dibujo, glDrawArray(GL_TRIANGLES, 0, 3), indica que la figura de debe dibujar usando triangulación, el vértice inicial es el cero y la cantidad de vértices a dibujar es tres.

Esta función inicia el vertex shader y establece que el mismo debe ejecutarse tres veces, una por cada vértice que compone el triángulo, en cada ejecución vPosition y vColor contendrán la posición y color del vértice correspondiente.

const GLchar* fragment_shader_source =
{
    "#version 330                       \n"
    "                                   \n"
    "in vec4 color;                     \n"
    "out vec4 frag_color;               \n"
    "                                   \n"
    "void main(void)                    \n"
    "{                                  \n"
    "   frag_color = color;             \n"
    "}                                  \n"
};

Este es el fragment shader, aunque el tutorial no trata sobre él, lo mostramos porque es necesario en OpenGL Moderno, es bastante simple, solamente toma el color enviado desde el vertex shader y lo pasa como salida hacia el frame buffer.

render triangle opengl
GitHub: GLSL Vertex Shader

Comentarios

Temas relacionados

Entradas populares de este blog

tkinter Grid

tkinter Canvas

Histogramas OpenCV Python

Python Binance API