WebGL
GLSL

WebGLで2DシンプルなGLSLを書く

Demo: https://codepen.io/mo4_9/pen/eGywGp

GLSLを書くときは、2DならWebGL、3DならThree.jsで書くのがよさげ。
今回は2D表現なのでWebGLで書いてみた。

index.html
<script id="vertexShader" type="x-shader/x-vertex"> 
  attribute vec2 a_position;
  void main() {
     gl_Position = vec4(a_position, 0, 1);
  }
</script>

<script id="fragmentShader" type="x-shader/x-fragment"> 
  precision mediump float;
  uniform float u_time;
  void main(void) {
    gl_FragColor = vec4(abs(sin(u_time)), abs(cos(u_time)), 0., 1.);
  }
</script>
script.js
let canvas,
    gl,
    positionLocation,
    timeLocation,
    vbo;

start();
function start() {
  canvas = document.createElement('canvas');
  canvas.width = window.innerWidth;
  canvas.height = window.innerHeight;
  canvas.style.width = canvas.width + 'px';
  canvas.style.height = canvas.height + 'px';
  const wrapper = document.querySelector('.wrapper');
  wrapper.appendChild(canvas);

  gl = canvas.getContext("webgl") || canvas.getContext('experimental-webgl');
  gl.viewportWidth = canvas.width;
  gl.viewportHeight = canvas.height;

  initShader();
  initVbo();

  requestAnimationFrame(tick);
}

function initShader() {
  const vs = create_shader("vertexShader");
  const fs = create_shader("fragmentShader");
  const program = create_program(vs,fs);
  positionLocation = gl.getAttribLocation(program, 'a_position');
  timeLocation = gl.getUniformLocation(program, 'u_time');
}

function initVbo() {
  const vertices = [
    -1.0, -1.0,
    1.0, -1.0,
    -1.0,  1.0,
    -1.0,  1.0,
    1.0, -1.0,
    1.0,  1.0
  ];
  vbo = create_vbo(vertices);
  vbo.itemSize = 2;
}

function tick(time){
    gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

    gl.enableVertexAttribArray(positionLocation);
    gl.vertexAttribPointer(positionLocation, vbo.itemSize, gl.FLOAT, false, 0, 0);
    gl.uniform1f(timeLocation, time/1000);

    // draw
    gl.drawArrays(gl.TRIANGLES, 0, 6);

    requestAnimationFrame(tick);
}


// wgld.org
function create_shader(id) {
    let shader;
    let scriptElement = document.getElementById(id);
    if(!scriptElement) return;
    switch(scriptElement.type){
        case 'x-shader/x-vertex':
            shader = gl.createShader(gl.VERTEX_SHADER);
            break;
        case 'x-shader/x-fragment':
            shader = gl.createShader(gl.FRAGMENT_SHADER);
            break;
        default:
            return;
    }
    console.log(scriptElement.text)
    gl.shaderSource(shader, scriptElement.text);
    gl.compileShader(shader);

    if(gl.getShaderParameter(shader, gl.COMPILE_STATUS)){
        return shader;
    }else{
        alert(gl.getShaderInfoLog(shader));
    }
}
function create_program(vs, fs){
    let program = gl.createProgram();
    gl.attachShader(program, vs);
    gl.attachShader(program, fs);
    gl.linkProgram(program);
    if(gl.getProgramParameter(program, gl.LINK_STATUS)){
        gl.useProgram(program);
        return program;
    } else {
        alert(gl.getProgramInfoLog(program));
    }
}
function create_vbo(data){
    let vbo = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.STATIC_DRAW);
    // gl.bindBuffer(gl.ARRAY_BUFFER, null);
    return vbo;
}

参考
https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API
https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext
http://wbgl.org/
https://codepen.io/kenjiSpecial/pen/meEebx?editors=1010
https://stackoverflow.com/questions/45131804/how-to-set-a-time-uniform-in-webgl