WebGL:

Parte I

Un Triángulo

@xpktro - LimaJS

Programme

  • What
  • Why
  • How
  • Then?
  • Refs.
  • ?

What

WebGL es un estándar que define un API cuyo propósito es rasterizar gráficos ejecutando código en la GPU con todas las ventajas que esto implica.

 

Esta API conforma casi por completo con OpenGL ES

Why

Las GPU son extremadamente eficaces para realizar ciertos tipos de cálculos (colores, iluminación, perspectiva).

 

Aprovechar esto nos permite producir gráficos complejos a altas velocidades.

 

Y más 👀

How

Hoy empezaremos una serie de charlas cuyo objetivo será explicar el nivel más bajo de interacción con el WebGL API.

 

Existe una gran cantidad de librerías que abstraen este manejo de la API pero considero importante demostrar cómo funciona realmente para sacarle (potencialmente) mayor provecho.

VS

FS

Varyings

Vertex Shader (GLSL)

Fragment Shader (GLSL)

Compile

Link

GPU

Uniforms

Vertex Array Object

Attribute

<canvas>

Attribute

Attribute

...

Array Buffer

Buffer

Buffer

WebGL Program

Float32Array

WebGL Context

in

out

in

Why

<canvas>

WebGL Context

(canvas.getContext)

Un canvas es necesario para obtener el contexto de dibujo y usar las funciones del WebGL API

  const canvas = document.createElement('canvas');
  document.body.appendChild(canvas);
  const gl = canvas.getContext('webgl2');

  if(!gl) {
    alert('Webgl2 Unsupported');
    return;
  }

How

How

Los shaders son pequeños programas que se ejecutan en la GPU y un programa de WebGL requiere 2 para funcionar.

 

El vertex shader se encarga de definir las posiciones de cada vértice en la pantalla y el fragment shader se encarga de dar el color de cada pixel a dibujar

VS

FS

Varyings

GPU

Uniforms

<canvas>

in

out

in

How

const VERTEX_SHADER_SOURCE = `#version 300 es
in vec4 position;

void main() {
  gl_Position = position;
}
`;

const FRAGMENT_SHADER_SOURCE = `#version 300 es
precision mediump float;
out vec4 fragColor;

void main() {
  fragColor = vec4(0, 0, 0.7, 1);
}
`;

Vertex Shader (GLSL)

Fragment Shader (GLSL)

1

1

-1

-1

R

G

B

A

(x, y, z, w)

How

Cada shader debe crearse y compilarse para ser usado en el programa

  const vertexShader = gl.createShader(gl.VERTEX_SHADER);
  gl.shaderSource(vertexShader, VERTEX_SHADER_SOURCE);
  gl.compileShader(vertexShader);

Vertex Shader (GLSL)

Fragment Shader (GLSL)

Compile

  const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
  gl.shaderSource(fragmentShader, FRAGMENT_SHADER_SOURCE);
  gl.compileShader(fragmentShader);

VS

FS

Varyings

GPU

Uniforms

<canvas>

WebGL Program

WebGL Context

How

  const program = gl.createProgram();
  gl.attachShader(program, vertexShader);
  gl.attachShader(program, fragmentShader);
  gl.linkProgram(program);

Vertex Array Object

Attribute

Attribute

Attribute

...

  const vao = gl.createVertexArray();
  gl.bindVertexArray(vao);

VS

in

How

La información que reciban los shaders (los atributos) se agrupa en un VAO

How

  const positionBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
  const positions = [
    0, 0.5,
    0.5, -0.5,
    -0.5, -0.5,
  ];
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
  

Array Buffer

Float32Array

Cada atributo va asociado a un array, este se define y llena de valores

How

  const positionAttributeLocation = gl.getAttribLocation(program, 'position');
  gl.vertexAttribPointer(positionAttributeLocation, 2, gl.FLOAT, false, 0, 0);
  gl.enableVertexAttribArray(positionAttributeLocation);

Vertex Array Object

Attribute

Array Buffer

Para cada atributo del programa, hay que definir de qué manera se extraerá data del buffer asociado

  const cssToRealPixels = window.devicePixelRatio || 1;
  canvas.width  = Math.floor(canvas.clientWidth  * cssToRealPixels);
  canvas.height = Math.floor(canvas.clientHeight * cssToRealPixels);

  gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);

How

Debemos darle el tamaño apropiado a nuestro canvas y definir qué parte de esta usará WebGL para renderizar

  gl.clearColor(0, 0, 0, 1.0);
  gl.clear(gl.COLOR_BUFFER_BIT);

  gl.useProgram(program);
  gl.drawArrays(gl.TRIANGLES, 0, 3);

How

Definimos un color de relleno y limpiamos el framebuffer con este.

Finalmente declaramos el uso del programa y programamos el dibujo con este.

How

El fragment shader puede recibir valores desde el vertex shader a través de los varyings.

 

Estos valores se interpolarán debido a la naturaleza del fragment shader.

 

Podemos usar esto para crear efectos de degradado.

const VERTEX_SHADER_SOURCE = `#version 300 es
in vec4 position;
in vec4 color;
out vec4 v_color;

void main() {
  gl_Position = position;
  v_color = color;
}
`;

const FRAGMENT_SHADER_SOURCE = `#version 300 es
precision mediump float;
in vec4 v_color;
out vec4 fragColor;

void main() {
  fragColor = v_color;
}
`;

How

< nuevo atributo

< varying irá al FS

< varying del VS

< los valores irán variando por cada pasada del FS

How

  const colorBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
  const colors = [
    0, 0, 1,
    0, 1, 0,
    1, 0, 0
  ];
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);
  const colorAttributeLocation = gl.getAttribLocation(program, 'color');
  gl.vertexAttribPointer(colorAttributeLocation, 3, gl.FLOAT, false, 0, 0);
  gl.enableVertexAttribArray(colorAttributeLocation);

Sólo hay que crear un nuevo array buffer y asignarlo al atributo respectivo. De este se sacarán uno a uno los valores de color junto con los vértices.

Then?

Quedan pocas cosas como manejo de texturas y cómo definir uniforms.

 

Lo que vimos hasta ahora es una base suficiente para aprender todo el resto rápidamente.

 

En la próxima sesión veremos más detalles interesantes como animaciones y 3d (TBD).

Refs.

Material de Estudio

Fun

  • ShaderToy - Sandbox de creación de fragment shaders muy completo.
  • Vertex Shader Art - Sandbox de creación de vertex shaders.
  • GLSL Shader - Sandbox de creación de fragment shaders básico.

?

WebGL Part I

By Moisés Gabriel Cachay Tello

WebGL Part I

  • 1,070