JavaScript WebGL draws a line

Introduction

next WebGL basic concepts , do a simple example of drawing a straight line.

Mainly refer to the following two articles:

Draw a line

I won't explain each function in detail below. I prefer to have a sense of the overall logic first, and then check the data as needed during actual use.

Create WebGL context

stay Basic concepts It is mentioned in that WebGL is used through the Canvas element:

  <canvas id="demo" width="300" height="200"></canvas>
  const canvasObj = document.querySelector("#demo");
  const glContext = canvasObj.getContext("webgl");

  if (!glContext) {
    alert("Browser does not support WebGL");
    return;
  }

Then prepare the vertex data.

Prepare vertex data and buffer

In WebGL, all objects are in 3D space. Drawing a line requires two vertices, and each vertex has a 3D coordinate:

let vertices = [
    -0.5, -0.5, 0.0,
    0.5, -0.5, 0.0
  ];

There are many types of buffers, and the type of vertex buffer object is gl.ARRAY_BUFFER .

  /**
   * Set buffer
   * @param {*} gl WebGL context
   * @param {*} vertexData vertex data 
   */
  function setBuffers(gl, vertexData) {
    // Create a blank buffer object
    const buffer = gl.createBuffer();
    // Bind target
    gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
    // WebGL does not support the direct use of JavaScript original array types, which requires conversion
    const dataFormat = new Float32Array(vertexData);
    // Initialize data store
    gl.bufferData(gl.ARRAY_BUFFER, dataFormat, gl.STATIC_DRAW);
  },

bufferData Method will copy the data to the current binding buffer object. This method provides parameters for managing the given data:

  • STATIC_DRAW: the contents of the buffer may be used frequently and will not be changed frequently.
  • DYNAMIC_DRAW: the contents of the buffer may be frequently used and changed.
  • STREAM_DRAW: the contents of the buffer may not be used often.

The data of the straight line will not change and will remain unchanged every rendering, so the type used here is STATIC_DRAW . Now that the vertex data is stored in the memory of the graphics card, start preparing the vertex shader.

Vertex Shader

Vertex shaders require GLSL ES Written in language, there are two forms of writing at the front end:

  • The script tag package is used as a DOM object.
  • Pure string.
<script id="shader" type="x-shader/x-vertex">
  attribute vec3 vertexPos;
  void main(void){
    gl_Position = vec4(vertexPos, 1);
  }
</script>

<script>
  const shader = document.getElementById('shader').innerHTML,
</script>

Each vertex has a 3D coordinate, and a vec3 type input variable vertexPos is created. Vec3 represents a triple floating-point vector.

main is the entry function, gl_Position is a built-in variable of the shader. A variable in GLSL has up to 4 components, and the last component is used for perspective division. gl_ The value of the position setting becomes the output of the vertex shader. Think back here Basic concepts The state machine mentioned in.

The following is the pure character form:

  /**
   * Create vertex shader
   * @param {*} gl WebGL context
   */
  function createVertexShader(gl) {
    // Vertex shader glsl code
    const source = `
      attribute vec3 vertexPos;
      void main(void){
        gl_Position = vec4(vertexPos, 1);
      }
    `;

    // Create shader
    const shader = gl.createShader(gl.VERTEX_SHADER);

    // Set vertex shader code
    gl.shaderSource(shader, source);

    // compile
    gl.compileShader(shader);

    // Judge whether the compilation is successful
    if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
      alert("Error compiling shader: " + gl.getShaderInfoLog(shader));
      gl.deleteShader(shader);
      return null;
    }

    return shader;
  }

In order for WebGL to use this shader, its source code must be compiled dynamically at run time.

  1. createShader The function creation type is gl.VERTEX_SHADER's shader object;
  2. compileShader Function.

Next, prepare the clip shader.

Fragment Shader

Fragment shaders are also used GLSL ES Language. What the clip shader does is calculate the final color output of the pixel. Here, it directly simplifies the specified output white. gl_FragColor is A built-in variable that represents color. The four components correspond to R, G, B and A respectively.

  /**
   * Create clip shader
   * @param {*} gl WebGL context
   */
  function createFragmentShader(gl) {
    // Fragment shader glsl code
    const source = `
      void main(void){
        gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
      }
    `;

    // Create shader
    const shader = gl.createShader(gl.FRAGMENT_SHADER);

    // Set clip shader code
    gl.shaderSource(shader, source);

    // compile
    gl.compileShader(shader);

    // Judge whether the compilation is successful
    if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
      alert("Error compiling shader: " + gl.getShaderInfoLog(shader));
      gl.deleteShader(shader);
      return null;
    }

    return shader;
  },

When both shaders are ready, they need to be linked and merged before they can be used.

Shader program

Shader program objects are versions of shaders that are merged and finally linked. When linking shaders to a program, it links the output of each shader to the input of the next shader. When the output and input do not match, you will get a connection error.

When the shader needs to be activated, the object is called as a parameter useProgram Function.

  /**
   * Initialize shader program
   * @param {*} gl WebGL context
   * @param {*} vertexShader Vertex Shader 
   * @param {*} fragmentShader Fragment Shader 
   */
  function initShaderProgram(gl, vertexShader, fragmentShader) {
    // Create shader object
    const shaderProgram = gl.createProgram();
    // Add shader
    gl.attachShader(shaderProgram, vertexShader);
    gl.attachShader(shaderProgram, fragmentShader);
    // Multiple shaders merge links
    gl.linkProgram(shaderProgram);
    // Check whether the creation is successful
    if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
      alert("Unable to initialize shader program: " + gl.getProgramInfoLog(shaderProgram));
      return null;
    }

    return shaderProgram;
  }

So far, the input vertex data has been sent to the GPU and indicates how the GPU processes it in vertex and fragment shaders. Finally, it's left to draw.

draw

  • vertexAttribPointer Function tells WebGL how to interpret vertex data;
  • enableVertexAttribArray Function enables vertex attributes, which are disabled by default;
  • The useProgram function activates the shader;
  • drawArrays Function to draw. The first parameter is the type of entity to be drawn. It is a straight line, so it is gl.LINE_STRIP .
  /**
   * Initialize shader program
   * @param {*} gl WebGL context
   * @param {*} shaderProgram Shader program object
   */
  function draw(gl, shaderProgram) {
    // Get the corresponding data index
    const vertexPos = gl.getAttribLocation(shaderProgram, "vertexPos");
    // Parse vertex data
    gl.vertexAttribPointer(vertexPos, 3, gl.FLOAT, false, 0, 0);
    // Enable vertex attributes, which are disabled by default.
    gl.enableVertexAttribArray(vertexPos);
    // Activate shader
    gl.useProgram(shaderProgram);
    // draw
    gl.drawArrays(gl.LINE_STRIP, 0, 2);
  }

example

This is Example , the overall logic is as follows:

  const canvasObj = document.querySelector("#demo");
  const glContext = canvasObj.getContext("webgl");
  let vertices = [-0.5, -0.5, 0.0, 0.5, -0.5, 0.0]; // vertex data 

  setBuffers(glContext, vertices); // Buffer data
  const vertexShader = createVertexShader(glContext); // Vertex Shader 
  const fragmentShader = createFragmentShader(glContext); // Fragment Shader 
  const shaderProgram = initShaderProgram(
    glContext,
    vertexShader,
    fragmentShader
  ); // Shader program object
  draw(glContext, shaderProgram); // draw

This involves a lot of methods and variables. I was really confused at the beginning. I will get used to it after I knock down the code several times.

Next, we will summarize some questions arising during the period, as shown in JavaScript WebGL Basics.

reference material

Keywords: Javascript Front-end html5 webgl

Added by svan_rv on Wed, 08 Dec 2021 13:19:34 +0200