1. Shaders

Shaders #

Son programas que se ejecutan en la GPU encargada de definir el color de los pixeles. Existen 3 tipos de shaders:

  • Vertex shader: se ejecutan por cada vértice del elemento que se quiere renderizar. Retornan la posición del vértice deseada.
  • Fragment Shader: se ejecutan por cada fragmento visible de la imagen. Retornan el color de cada pixel.
  • Geometry shader (opcional): se ejecutan por cada cara del modelo que se desea renderizar, pueden crear nuevos vertices.
En OpenGL y OpenGL ES los shaders están escritos en GLSL.

Rendering pipeline

P5 Nativo #

Con la funcion LoadShader() se carga el vertex shader y fragment shader. Posteriormente con la funcion shader() se aplicar el shader.

Los shaders solo se pueden utilizar en el modo WEBGL

Tomado de p5.loadShader()

Script p5 mandelbrot.js
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
let mandel;
function preload() {
    // load the shader definitions from files
    mandel = loadShader('/showcase/sketches/mandelbrot/shaders/shader.vert', '/showcase/sketches/mandelbrot/shaders/shader.frag');
}
function setup() {
    createCanvas(725, 725, WEBGL);
    // use the shader
    shader(mandel);
    noStroke();
    mandel.setUniform('p', [-0.74364388703, 0.13182590421]);
    describe('zooming Mandelbrot set. a colorful, infinitely detailed fractal.');
}

function draw() {
    mandel.setUniform('r', 1.5 * exp(-6.5 * (1 + sin(millis() / 2000))));
    quad(-1, -1, 1, -1, 1, 1, -1, 1);
}
Vertex shader shader.vert
1
2
3
4
5
6
precision highp float;
varying vec2 vPos;
attribute vec3 aPosition;
void main() {
    vPos = (gl_Position = vec4(aPosition, 1.0)).xy;
}
Fragment shader shader.frag
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
precision highp float;
varying vec2 vPos;
uniform vec2 p;
uniform float r;
const int I = 500;
void main() {
    vec2 c = p + vPos * r, z = c;
    float n = 0.0;
    for(int i = I; i > 0; i--) {
        if(z.x * z.x + z.y * z.y > 4.0) {
            n = float(i) / float(I);
            break;
        }
        z = vec2(z.x * z.x - z.y * z.y, 2.0 * z.x * z.y) + c;
    }
    gl_FragColor = vec4(0.5 - cos(n * 17.0) / 2.0, 0.5 - cos(n * 13.0) / 2.0, 0.5 - cos(n * 23.0) / 2.0, 1.0);
}

Setup Treegl #

Con la función readShader() se especifica el fragment shader que se desea cargar y los parametros para generar el vertexShader, como las matrices de vista, modelo, perspectiva y varyings como position2, position3, textCoords.

Script p5 setupTreegl.js
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
let testShader;
let slider;

function preload() {

    // Load shader with position2 varying
    const params = {
        precision: Tree.highp,
        matrices: Tree.NONE,
        varyings: Tree.position2
    }

    testShader = readShader('/showcase/sketches/mandelbulb/shader/setupTreegl/setup.frag', params);
}

function setup() {
    let canvas = createCanvas(725, 725, WEBGL);

    // Disable page scrolling when mouse over canvas
    parent.disableScroll(canvas.canvas);

    shader(testShader);

    slider = createSlider(0, 255, 100);
    slider.position(10, 10);
}

function draw() {
    background(0);
    testShader.setUniform('b', slider.value() / 255);
    quad(-1, -1, 1, -1, 1, 1, -1, 1);
}
Fragment shader setup.frag
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
precision highp float;
varying vec2 position2;

uniform float b;

void main() {
    vec2 pos = (position2 + 1.) / 2.;

    gl_FragColor = vec4(vec3(pos, b), 1.);
}

Referencias #