WebGL

Posted on April 9, 2022
Tags: codeetc

1 Abstract

3 helper functions + 1 subhelper:

Main pipeline:

  1. ShaderProgram: Vertex Shader + Fragment Shader into initShaderProgram
  2. Buffer: Build buffers with initBuffers
  3. ShaderProgram + Buffer = Scene

2 Code

    <script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/3.4.2/gl-matrix-min.js" integrity="sha512-eV9ExyTa3b+YHr99IBTYpwk4wbgDMDlfW8uTxhywO8dWb810fGUSKDgHhEv1fAqmJT4jyYnt1iWWMW4FRxeQOQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script>
      

//----------------------------------------------------------------------
      // initBuffers
      //
      // Initialize the buffers we'll need. For this demo, we just
      // have one object -- a simple two-dimensional square.
      //
      const = initBuffers(gl) => {
      
        // Create a buffer for the square's positions.
      
        const positionBuffer = gl.createBuffer();
      
        // Select the positionBuffer as the one to apply buffer
        // operations to from here out.
      
        gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
      
        // Now create an array of positions for the square.
      
        const positions = [
           1.0,  1.0,
          -1.0,  1.0,
           1.0, -1.0,
          -1.0, -1.0,
        ];
      
        // Now pass the list of positions into WebGL to build the
        // shape. We do this by creating a Float32Array from the
        // JavaScript array, then use it to fill the current buffer.
      
        gl.bufferData(gl.ARRAY_BUFFER,
                      new Float32Array(positions),
                      gl.STATIC_DRAW);
      
        return {
          position: positionBuffer,
        };
      }

//-----------------------------------------------------------------------
      // Draw the scene.
      //
      const drawScene= (gl, programInfo, buffers) => {
        gl.clearColor(0.0, 0.0, 0.0, 1.0);  // Clear to black, fully opaque
        gl.clearDepth(1.0);                 // Clear everything
        gl.enable(gl.DEPTH_TEST);           // Enable depth testing
        gl.depthFunc(gl.LEQUAL);            // Near things obscure far things
      
        // Clear the canvas before we start drawing on it.
      
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
      
        // Create a perspective matrix, a special matrix that is
        // used to simulate the distortion of perspective in a camera.
        // Our field of view is 45 degrees, with a width/height
        // ratio that matches the display size of the canvas
        // and we only want to see objects between 0.1 units
        // and 100 units away from the camera.
      
        const fieldOfView = 45 * Math.PI / 180;   // in radians
        const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
        const zNear = 0.1;
        const zFar = 100.0;
        const projectionMatrix = glMatrix.mat4.create();
      
        // note: glmatrix.js always has the first argument
        // as the destination to receive the result.
        glMatrix.mat4.perspective(projectionMatrix,
                         fieldOfView,
                         aspect,
                         zNear,
                         zFar);
      
        // Set the drawing position to the "identity" point, which is
        // the center of the scene.
        const modelViewMatrix = glMatrix.mat4.create();
      
        // Now move the drawing position a bit to where we want to
        // start drawing the square.
      
        glMatrix.mat4.translate(modelViewMatrix,     // destination matrix
                       modelViewMatrix,     // matrix to translate
                       [-0.0, 0.0, -6.0]);  // amount to translate
      
        // Tell WebGL how to pull out the positions from the position
        // buffer into the vertexPosition attribute.
        {
          const numComponents = 2;
          const type = gl.FLOAT;
          const normalize = false;
          const stride = 0;
          const offset = 0;
          gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position);
          gl.vertexAttribPointer(
              programInfo.attribLocations.vertexPosition,
              numComponents,
              type,
              normalize,
              stride,
              offset);
          gl.enableVertexAttribArray(
              programInfo.attribLocations.vertexPosition);
        }
      
        // Tell WebGL to use our program when drawing
      
        gl.useProgram(programInfo.program);
      
        // Set the shader uniforms
      
        gl.uniformMatrix4fv(
            programInfo.uniformLocations.projectionMatrix,
            false,
            projectionMatrix);
        gl.uniformMatrix4fv(
            programInfo.uniformLocations.modelViewMatrix,
            false,
            modelViewMatrix);
      
        {
          const offset = 0;
          const vertexCount = 4;
          gl.drawArrays(gl.TRIANGLE_STRIP, offset, vertexCount);
        }
      }

//-----------------------------------------------------------------------
      // Initialize a shader program, so WebGL knows how to draw our data
      //
      const initShaderProgram = (gl, vsSource, fsSource) => {
        const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource);
        const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource);
        // Create the shader program
        const shaderProgram = gl.createProgram();
        gl.attachShader(shaderProgram, vertexShader);
        gl.attachShader(shaderProgram, fragmentShader);
        gl.linkProgram(shaderProgram);
        // If creating the shader program failed, alert
        if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
          alert('Unable to initialize the shader program: ' + gl.getProgramInfoLog(shaderProgram));
          return null;
        }
        return shaderProgram;
      }
      
//---------------------------------------------------------------------------
      // creates a shader of the given type, uploads the source and
      // compiles it.
      // SUBHELPER function to be used in initShaderProgram
      const loadShader = (gl, type, source) => {
        const shader = gl.createShader(type);
        // Send the source to the shader object
        gl.shaderSource(shader, source);
        // Compile the shader program
        gl.compileShader(shader);
        // See if it compiled successfully
        if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
          alert('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader));
          gl.deleteShader(shader);
          return null;
        }
        return shader;
      }

//Everything above are helper functions for main

//MAIN START-----------------------------------------------------
      const main = async () => {
       
            let ctxcanv = document.querySelector("#tutorial");
            var gl = ctxcanv.getContext('webgl');
            var rect = ctxcanv.getBoundingClientRect();
    //--------------- Vertex shader program-------------START
            const vsSource = `
              attribute vec4 aVertexPosition;
              uniform mat4 uModelViewMatrix;
              uniform mat4 uProjectionMatrix;
              void main() {
                gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition;
              }
            `;
    //--------------- Vertex shader program---------------END

    //--------------- Fragment shader program-----------START
            const fsSource = `
              void main() {
                gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
              }
            `;
    //--------------- Fragment shader program-------------END

          // Initialize a shader program; this is where all the lighting
          // for the vertices and so forth is established.
          const shaderProgram = initShaderProgram(gl, vsSource, fsSource);
    
          // Collect all the info needed to use the shader program.
          // Look up which attribute our shader program is using
          // for aVertexPosition and look up uniform locations.
          const programInfo = {
            program: shaderProgram,
            attribLocations: {
              vertexPosition: gl.getAttribLocation(shaderProgram, 'aVertexPosition'),
            },
            uniformLocations: {
              projectionMatrix: gl.getUniformLocation(shaderProgram, 'uProjectionMatrix'),
              modelViewMatrix: gl.getUniformLocation(shaderProgram, 'uModelViewMatrix'),
            },
          };
    
          // Here's where we call the routine that builds all the
          // objects we'll be drawing.
          const buffers = initBuffers(gl);
        
          // Draw the scene
          drawScene(gl, programInfo, buffers);
      }
      
      //INIT main()
      window.addEventListener("load", () => {main();});
    
    </script>

3 Shader

4 Snipped from other parts of the web

      <script>
         /*================Creating a canvas=================*/
         var canvas = document.getElementById('my_Canvas');
         gl = canvas.getContext('experimental-webgl'); 

         /*==========Defining and storing the geometry=======*/

         var vertices = [
            -0.5,0.5,0.0,
            0.0,0.5,0.0,
            -0.25,0.25,0.0, 
         ];

         // Create an empty buffer object to store the vertex buffer
         var vertex_buffer = gl.createBuffer();

         //Bind appropriate array buffer to it
         gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);

         // Pass the vertex data to the buffer
         gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

         // Unbind the buffer
         gl.bindBuffer(gl.ARRAY_BUFFER, null);

         /*=========================Shaders========================*/

         // vertex shader source code
         var vertCode =
            'attribute vec3 coordinates;' +

            'void main(void) {' +
               ' gl_Position = vec4(coordinates, 1.0);' +
               'gl_PointSize = 10.0;'+
            '}';

         // Create a vertex shader object
         var vertShader = gl.createShader(gl.VERTEX_SHADER);
         
         // Attach vertex shader source code
         gl.shaderSource(vertShader, vertCode);

         // Compile the vertex shader
         gl.compileShader(vertShader);

         // fragment shader source code
         var fragCode =
            'void main(void) {' +
               ' gl_FragColor = vec4(0.0, 0.0, 0.0, 0.1);' +
            '}';

         // Create fragment shader object
         var fragShader = gl.createShader(gl.FRAGMENT_SHADER);

         // Attach fragment shader source code
         gl.shaderSource(fragShader, fragCode);

         // Compile the fragmentt shader
         gl.compileShader(fragShader);
         
         // Create a shader program object to store
         // the combined shader program
         var shaderProgram = gl.createProgram();

         // Attach a vertex shader
         gl.attachShader(shaderProgram, vertShader); 

         // Attach a fragment shader
         gl.attachShader(shaderProgram, fragShader);

         // Link both programs
         gl.linkProgram(shaderProgram);

         // Use the combined shader program object
         gl.useProgram(shaderProgram);

         /*======== Associating shaders to buffer objects ========*/

         // Bind vertex buffer object
         gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);

         // Get the attribute location
         var coord = gl.getAttribLocation(shaderProgram, "coordinates");

         // Point an attribute to the currently bound VBO
         gl.vertexAttribPointer(coord, 3, gl.FLOAT, false, 0, 0);

         // Enable the attribute
         gl.enableVertexAttribArray(coord);

         /*============= Drawing the primitive ===============*/

         // Clear the canvas
         gl.clearColor(0.5, 0.5, 0.5, 0.9);

         // Enable the depth test
         gl.enable(gl.DEPTH_TEST);
 
         // Clear the color buffer bit
         gl.clear(gl.COLOR_BUFFER_BIT);

         // Set the view port
         gl.viewport(0,0,canvas.width,canvas.height);

         // Draw the triangle
         gl.drawArrays(gl.POINTS, 0, 3);
      </script>

5 Nextjs plot graph

plotting a graph in webgl

import Head from 'next/head'
import Image from 'next/image'
import styles from '../styles/Home.module.css'
import Shit from '../components/Shit.jsx'
import React, { useState, useEffect,useReducer,useRef } from 'react';
import { mat4 } from 'gl-matrix';

const ptnum = -1

const createPoints = (x,y) => {
    const xnormed = 1/Math.max(...x);
    const ynormed = 1/Math.max(...y);
    const ret = [];
    for(let i = 0; i < x.length; i++){
        ret[i*2] = x[i]*xnormed;
        ret[(i*2)+1] = y[i]*ynormed;
    }
    ptnum = x.length;
    return ret;
}

const initBuffers = (gl) => {
    const positionBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);

    //---------------------------------------------------------graph plotting

    const xdata = [...Array(5).keys()];
    const ydata = xdata.map((x)=>x*x);
    const positions = createPoints(xdata,ydata);
    // const positions = createPoints([1,2,3,4,5],[1,9,1,9,1]);
    console.log(positions);
    gl.bufferData(gl.ARRAY_BUFFER,
                      new Float32Array(positions),
                      gl.STATIC_DRAW);
    return {
          position: positionBuffer,
        };
}

const loadShader = (gl, type, source) => {
        const shader = gl.createShader(type);
        // Send the source to the shader object
        gl.shaderSource(shader, source);
        gl.compileShader(shader);      
        if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
          alert('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader));
          gl.deleteShader(shader);
          return null;
        }     
        return shader;
}

const initShaderProgram = (gl, vsSource, fsSource) => {
        const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource);
        const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource);
        // Create the shader program
        const shaderProgram = gl.createProgram();
        gl.attachShader(shaderProgram, vertexShader);
        gl.attachShader(shaderProgram, fragmentShader);
        gl.linkProgram(shaderProgram);
        // If creating the shader program failed, alert
        if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
          alert('Unable to initialize the shader program: ' + gl.getProgramInfoLog(shaderProgram));
          return null;
        }
        return shaderProgram;
}
const drawScene= (gl, programInfo, buffers,delta) => {
        gl.clearColor(0.0, 0.0, 0.0, 1.0);  // Clear to black, fully opaque
        gl.clearDepth(1.0);                 // Clear everything
        gl.enable(gl.DEPTH_TEST);           // Enable depth testing
        gl.depthFunc(gl.LEQUAL);            // Near things obscure far things
      
        // Clear the canvas before we start drawing on it.
      
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
      
        // Create a perspective matrix, a special matrix that is
        // used to simulate the distortion of perspective in a camera.
        // Our field of view is 45 degrees, with a width/height
        // ratio that matches the display size of the canvas
        // and we only want to see objects between 0.1 units
        // and 100 units away from the camera.
      
        const fieldOfView = 45 * Math.PI / 180;   // in radians
        const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
        const zNear = 0.1;
        const zFar = 100.0;
        console.log(mat4)
        const projectionMatrix = mat4.create();
      
        // note: glmatrix.js always has the first argument
        // as the destination to receive the result.
        mat4.perspective(projectionMatrix,
                         fieldOfView,
                         aspect,
                         zNear,
                         zFar);
      
        // Set the drawing position to the "identity" point, which is
        // the center of the scene.
        const modelViewMatrix = mat4.create();
      
        // Now move the drawing position a bit to where we want to
        // start drawing the square.
      
        mat4.translate(modelViewMatrix,     // destination matrix
                       modelViewMatrix,     // matrix to translate
                       [-0.0, 0.0, -6.0]);  // amount to translate
        mat4.rotate(modelViewMatrix,  // destination matrix
              modelViewMatrix,  // matrix to rotate
              delta,   // amount to rotate in radians
              [0, 0, 1]);       // axis to rotate around
      
        // Tell WebGL how to pull out the positions from the position
        // buffer into the vertexPosition attribute.
        {
          const numComponents = 2;
          const type = gl.FLOAT;
          const normalize = false;
          const stride = 0;
          const offset = 0;
          gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position);
          gl.vertexAttribPointer(
              programInfo.attribLocations.vertexPosition,
              numComponents,
              type,
              normalize,
              stride,
              offset);
          gl.enableVertexAttribArray(
              programInfo.attribLocations.vertexPosition);
        }
      
        // Tell WebGL to use our program when drawing
      
        gl.useProgram(programInfo.program);
      
        // Set the shader uniforms
      
        gl.uniformMatrix4fv(
            programInfo.uniformLocations.projectionMatrix,
            false,
            projectionMatrix);
        gl.uniformMatrix4fv(
            programInfo.uniformLocations.modelViewMatrix,
            false,
            modelViewMatrix);
      
        {
          const offset = 0;
          const vertexCount = 4;
          //gl.drawArrays(gl.TRIANGLE_STRIP, offset, vertexCount);
          gl.drawArrays(gl.POINTS,0,ptnum)
        }
}

const MyWebGL = () => {
    const width = 400;
    const height = 400;
    const canvasRef = useRef();
    const vsSource = `
        attribute vec4 aVertexPosition;
        uniform mat4 uModelViewMatrix;
        uniform mat4 uProjectionMatrix;
        void main() {
            gl_PointSize = 10.0;
            gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition;
        }
    `;
    const fsSource = `
        void main() {
            gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
        }
        `;
    const initWebGL = () => {
        let mycanvas = canvasRef.current;
        let gl = mycanvas.getContext('webgl2');
        console.log(gl);
        const shaderProgram = initShaderProgram(gl, vsSource, fsSource);
        const programInfo = {
            program: shaderProgram,
            attribLocations: {
              vertexPosition: gl.getAttribLocation(shaderProgram, 'aVertexPosition'),
            },
            uniformLocations: {
              projectionMatrix: gl.getUniformLocation(shaderProgram, 'uProjectionMatrix'),
              modelViewMatrix: gl.getUniformLocation(shaderProgram, 'uModelViewMatrix'),
            },
          };
        const buffers = initBuffers(gl);
        const delta = 0.0;
        drawScene(gl, programInfo, buffers,delta); //first render
        
        
        document.addEventListener('keydown',animateFrame(gl,programInfo,buffers,delta));
    }
    
    const animateFrame = (gl,programInfo,buffers,delta) => {
        const animateFramehelper = (e)=>{
                if(e.code == "KeyG"){
                console.log(e.code)
                delta = delta + 0.2;
                // console.log(toggle)
                drawScene(gl, programInfo, buffers, delta);
                }
            }
        return animateFramehelper;
    }
    useEffect(()=>{
        initWebGL();
        return(() => {
            document.removeEventListener('keydown',animateFrame);
        })
    });
    return(
        <div>
            <canvas ref={canvasRef} id="tutorial" width={width} height={height} style={{border: "1px solid black"}} />
        </div>
    )
}

export default function Home() {
  return (
    <div>
        <MyWebGL/>
    </div>
  )}