Home

Published

- 2 min read

WebGL


Since WebGL is a Web API. A self contained HTML file is suffice to demo it.

Here’s the HTML file

The content is like this:

   <html lang="id">
<head>
  <style>
      #canvas {
          width: 100%;
          height: 100vw;
      }
  </style>
  <title>Triangle Demo</title>
</head>
<body>
<canvas id="canvas"></canvas>
<script type="text/javascript">

  const gl = canvas.getContext('webgl');

  function shaderLoader(gl, type, sourceCode) {
    const shader = gl.createShader(type);
    gl.shaderSource(shader, sourceCode);
    gl.compileShader(shader);

    // check nasty compilation warning
    if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
      alert(`An error occurred compiling the shaders ${type}: ${gl.getShaderInfoLog(shader)}`);
      gl.deleteShader(shader);
      return null;
    }
    return shader;
  }

  // origin 0,0
  const triangleVertices = {
    RED: {
      x: 0.75/Math.sqrt(3), y: -0.25
    },
    GREEN: {
      x: 0, y: 0.5
    },
    BLUE: {
      x: -0.75/Math.sqrt(3), y: -0.25
    }
  }

  const glProgram = ((gl) => {
    const program = gl.createProgram();
    const vertexShader = shaderLoader(gl, gl.VERTEX_SHADER, `
            precision mediump float;
            attribute vec2 a_position;
            uniform float u_time;
            varying vec2 v_texture;

            void main() {
              // texture based on original position
              v_texture = a_position;

              // rotate vertex around origin based on time
              gl_Position.xy = a_position;
              gl_Position.z = 0.0;
              gl_Position.w = 1.0;
              mat2 rotation = mat2(cos(u_time), sin(u_time), -sin(u_time), cos(u_time));
              gl_Position.xy *= rotation;
            }
          `);
    const fragmentShader = shaderLoader(gl, gl.FRAGMENT_SHADER, `
            precision mediump float;
            varying vec2 v_texture;

            void main() {
              // using dot product to interpolate radial colors
              float red = dot(v_texture, vec2(${triangleVertices.RED.x}, ${triangleVertices.RED.y}));
              float green = dot(v_texture, vec2(${triangleVertices.GREEN.x}, ${triangleVertices.GREEN.y}));
              float blue = dot(v_texture, vec2(${triangleVertices.BLUE.x}, ${triangleVertices.BLUE.y}));
              gl_FragColor = vec4(red, green, blue, 0.9);
            }
          `);
    gl.attachShader(program, vertexShader);
    gl.attachShader(program, fragmentShader);
    gl.linkProgram(program);
    console.log(gl.getProgramInfoLog(program));
    console.log('Program Linked')
    return program;
  })(gl)

  const pipelineStructure = {
    // pointer to program
    program: glProgram,

    // pointer to the buffers/vertex stream
    vertexBuffer: ((gl) => {
      const vertexBuffer = gl.createBuffer();
      gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
      gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
        triangleVertices.RED.x, triangleVertices.RED.y,
        triangleVertices.GREEN.x, triangleVertices.GREEN.y,
        triangleVertices.BLUE.x, triangleVertices.BLUE.y,
      ]), gl.STATIC_DRAW);
      return vertexBuffer;
    })(gl),

    // pointer to a_position (cursor)
    aPosition: ((gl) => {
      const aPosition = gl.getAttribLocation(glProgram, 'a_position');
      gl.enableVertexAttribArray(aPosition);
      gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, 0, 0);
      return aPosition;
    })(gl),

    // pointer to time
    uTime: ((gl) => {
      const uTime = gl.getUniformLocation(glProgram, 'u_time');
      return uTime;
    })(gl)
  }

  function render(gl, pipeline, time) {
    // scale the fps
    tick = time * 0.001;

    // reset background
    gl.clearColor(0.0, 0.0, 0.0, 1.0);
    gl.clear(gl.COLOR_BUFFER_BIT);

    {
      // send position data
      gl.bindBuffer(gl.ARRAY_BUFFER, pipeline.vertexBuffer);
      gl.vertexAttribPointer(pipeline.aPosition, 2, gl.FLOAT, false, 0, 0);
      gl.enableVertexAttribArray(pipeline.aPosition);
    }
    gl.useProgram(pipeline.program);
    {
      // variate tick/time
      gl.uniform1f(pipeline.uTime, tick);
    }
    gl.drawArrays(gl.TRIANGLE_STRIP, 0, 3);
  }

  function main() {
    const renderLoop = (time) => {
      render(gl, pipelineStructure, time);
      requestAnimationFrame(renderLoop);
    }
    requestAnimationFrame(renderLoop);
  }

  main()
</script>
</body>
</html>

Related Posts

There are no related posts yet. 😢