Tutorial Sintaxis GLSL

En este tutorial veremos la sintaxis de GLSL, nos enfocaremos en el uso de la versión 3.3 como mínimo, en el Tutorial Introducción a GLSL vimos todos los detalles teóricos que debemos conocer antes de empezar, veamos nuestro primer fragmento de código.

#version 330

/* 
directiva para el preprocesador
define la version a utilizar 
*/

void main(){
   // codigo del shader
}

La primera línea de código #version 330 define la versión con la que trabajaremos, 3.3 se identifica como 330, si deseáramos, por ejemplo, usar la versión 4.3 usaríamos la directiva #version 340.

Vemos también la manera de añadir comentarios, igual que el lenguaje C usamos // para definir una línea de comentarios y /* */ para agregar un comentario en varias líneas.

La función void main() { } a diferencia de C no debe retornar valor, podemos definir otras funciones que devuelvan un valor del tipo indicado y con cero, uno o varios argumentos, como en C la función main es el punto de inicio del programa.

Tipos de Datos


A continuación se describen los tipos de datos con los que cuenta GLSL, algunos ya los conocemos del lenguaje C, a diferencia de este no contamos con punteros ni cadenas de caracteres.

Tenemos solo cuatro tipos básicos:
  • bool: para definir un tipo booleano, true o false.
  • int: valor numérico entero con signo, positivo o negativo.
  • uint: valor numérico entero sin signo, positivo.
  • float: define un tipo punto flotante.

bool bValue = false; // Boolean true or false
int iValue = 42; // Signed integer
uint uiValue = 3929u; // unsigned integer
float fValue = 42.0f; // Floating point value

Los vectores pueden tener 2, 3 o 4 componentes de uno de los tipos básicos, tenemos:
  • vec2, vec3, vec4: vector de 2, 3, 4 componentes de tipo float 
    ivec2, ivec3, ivec4: vector de 2, 3, 4 componentes de tipo int 
    uvec2, uvec3, uvec4: vector de 2, 3, 4 componentes de tipo uint 
    bvec2, bvec3, bvec4: vector de 2, 3, 4 componentes de tipo bool 
Las matrices siempre son de tipo float, se definen con la palabra mat seguido de un número para matrices cuadradas, ejemplo: mat3, matriz de 3 columnas y 3 filas, también podemos definir numero de columnas y filas diferentes, ejemplo: mat2x3, matriz de 2 columnas y 3 filas.

GLSL soporta los siguientes tipos de matrices: mat2, mat2x2, mat3, mat3x3, mat4, mat4x4, mat2x3, mat2x4, mat3x2, mat3x4, mat4x2, mat4x3.

Inicializar Variables


Las variables son inicializadas usando el operador “=”, para los vectores y matrices lo hacemos mediante el constructor del tipo correspondiente.

float a = 1.0; // Ok
bool switch = false; // Ok
ivec3 a = ivec3(1, -2, 3); // Ok
uvec2 a = uvec2(-1, 2); // Error, uvec2 solo admite enteros sin signo
vec3 a(1.0, 2.0); // Error, debemos usar el constructor
vec3 a = vec3(1.0, 2.0); // Ok

// Podemos combinar varios tipos para crear uno nuevo,
// solo tener en cuenta que el numero de componentes corresponda
vec4 a = vec4(1.0, vec3(0.0, 1.0, 0.0)); 
vec4 a = vec4(vec3(0.0, 1.0, 0.0), 0.9);
vec4 a = vec4(vec2(1.0, 1.0), vec2(0.5, 0.5));

En los vectores podemos usar los siguientes nombres para acceder a cada uno de sus componentes, también podemos hacerlo a través de su índice, como si fuese un arreglo.

Una matriz puede verse como un arreglo de vectores, si deseamos establecer la primera columna de una matriz, lo haríamos de este modo:

mat4 mvp; 
// establecer la primera columna de la matrix 4 x 4
mvp[0] = vec4(0.0f, 1.0f, 0.0f, 1.0f);

// usando el contructor para inicializar la matriz
mat4 model = mat4(1.0f, 0.0f, 0.0f, 0.0f,
                  0.0f, 1.0f, 0.0f, 0.0f,
                  0.0f, 0.0f, 1.0f, 0.0f,
                  0.0f, 0.0f, 0.0f, 1.0f);

// constructor para crear la matriz identidad
mat4 identity = mat4(1.0f);

Grupo de nombres usados para acceder a las cuatro componente de un vector que represente una posición {x, y, z, w}, cuando el vector representa un color {r, g, b, a}, si representa una coordenada de textura {s, t, p, q}, el primer nombre de cada grupo accede a la primera componente del vector, por lo que da igual usar x, r, s esto ocurre con los demás nombres, decidimos si usar uno u otro solo por claridad.

vec3 p;
p.x = 2; // Establece la primera componente del vector, equivale a: p.r = 2; p.s = 2; p[0] = 2;
p.w = 4; // Error, solo tenemos 3 componentes, p[3] no existe en un vec3.

En GLSL en posible reordenar las componentes de un vector para crear uno nuevo, esto se le llama: “swizzle”, para hacerlo colocamos el nombre de componente en el orden deseado, no solo podemos cambiar el orden, también podemos repetir el nombre cuantas veces sea necesario para conseguir la cantidad de componentes finales, veamos un ejemplo:

// Color en formato RGB
vec3 color_rgb = vec4(0.5, 0.2, 1.0);
// Color en formato BGR
vec3 color_bgr = color_rgb.bgr; // equivale a usar: color_rgb.zyx

vec4 pos0 = vec4(3, 5, 2, 1);
vec3 pos1 = pos0.xxy; // Ok
vec2 pos2 = pos0.yyy; // Error, se requieren 3 componentes

No podemos mezclar los grupos de nombres, es un error usar por ejemplo: pos0.xt.

Control de Flujo


Al igual que el lenguaje C contamos son las sentencias if-else para la toma de decisiones, también es posible utilizar swicth  el uso de ambos es el común que conocemos de lenguajes como el ya mencionado C/C++, C# o Java.

if(a < threshold)
   lightColor = vec4(1.0, 0.0, 0.0, 1.0);
else if(a == threshold) 
   lightColor = vec4(0.0, 1.0, 0.0, 1.0);
else if((a > threshold + 1) && (a <= threshold + 2))
{
   lightColor = vec4(0.0, 0.0, 1.0, 1.0);
   lightIntesity = vec4(0.85);
}
else
   lightColor = vec4(1.0, 1.0, 1.0, 1.0);

Si el if contiene una sola instrucción podemos omitir las llaves, ahora mostramos un ejemplo de swicth.

switch(var)
{
   case 1: // cuando var = 1
      lightColor = vec4(1.0, 0.0, 0.0, 1.0);
      break;
   case 2: // cuando var = 2
      lightColor = vec4(0.0, 1.0, 0.0, 1.0);
      break;
   default: // en cualquier otro caso
     lightColor = vec4(1.0, 1.0, 1.0, 1.0);
}

Tenemos disponibles las siguientes sentencias de repetición: for, while, do-while, también son bastante familiares por lo que solo mostramos un ejemplo de su sintaxis.

for(int i = 0; i < 5; i++) {
     n = n * i + 1;
}

while(m < 20) {
     m = (m + 1) * 2;
}

do {
     p = p / 2.0;
} while(p >= 10.5);

Recordemos siempre tener cuidado con los ciclos infinitos.

Comentarios

Temas relacionados

Entradas populares de este blog

tkinter Grid

Controles y Contenedores JavaFX 8 - I

tkinter Canvas

Histogramas OpenCV Python