Yugong SeriesAugust 2023 WEBGL Special Topic – Vertex Method and Index Method of Drawing Methods

Article directory

  • foreword
  • 1. Vertex method
    • 1. The concept of vertex method
    • 2. Case
  • Second, the index method
    • 1. The concept of indexing
    • 2. Case
  • Summarize

Foreword

The drawing methods of WEBGL mainly include the following types:

  1. gl.drawArrays: Draw graphics directly using vertex arrays. This method needs to pass in an enumeration type to represent the basic graphics type to be drawn, for example, gl.TRIANGLES means to use triangles to draw, gl.POINTS means to use points to draw, gl.LINES means to use line segments to draw, etc. This method can modify the drawn graphics by modifying the vertex data in the vertex array.

  2. gl.drawElements: Draw graphics using an indexed array. This method is similar to gl.drawArrays, but needs to pass in an index array to indicate the order of vertices when drawing graphics. Using an index array can effectively reduce the number of repeated vertices and improve drawing performance.

  3. gl.drawArraysInstanced: Use vertex arrays and instanced arrays to draw multiple similar graphics. This method needs to pass in an instantiated array to represent the attributes of multiple instances, such as color, position, etc. This method can be used to draw a large number of similar graphics, thereby improving drawing performance.

  4. gl.drawElementsInstanced: Draw multiple similar graphics using indexed arrays and instanced arrays. This method is similar to gl.drawArraysInstanced, but needs to pass in an index array to indicate the order of vertices. Using an index array can effectively reduce the number of repeated vertices and improve drawing performance.

  5. gl.drawRangeElements: Use index array to draw part of graphics. This method can specify the start index and end index of drawing, so that only part of the graphics can be drawn. This method can be used to draw complex graphics, such as different parts of the model.

These drawing methods can be selected and used according to actual needs and scenarios. In practical applications, more complex graphics and effects can also be achieved by combining multiple drawing methods.

1. Vertex method

1. The concept of vertex method

The vertex method of WebGL refers to the use of vertex data to describe 3D objects in WebGL. Every 3D object is made up of a series of vertices that are organized into a mesh of triangles, forming a surface.

The benefit of using vertex data is that it can greatly reduce the amount of data that needs to be transferred. Typically, 3D objects contain a large amount of vertex data, but many of these data are redundant, and the amount of data can be reduced by having multiple triangles share the same vertex.

In WebGL, vertex data usually consists of the following information:

  1. Position information in 3D space: represented by three floating point numbers (x, y, z).

  2. Vertex texture coordinates: represented by two floating-point numbers (u, v).

  3. Vertex normal vector: represented by three floating-point numbers (x, y, z), used to calculate lighting.

  4. Other attributes: such as vertex color, transparency, etc.

In WebGL, the general steps to use the vertex method are as follows:

  1. Create a vertex shader program to process vertex data.

  2. Create a fragment shader program that processes pixel colors.

  3. Load 3D model data and convert it to a format that WebGL can use. Typically, model data is loaded from external files, such as OBJ, PLY, etc.

  4. Pass the vertex data to the vertex shader program for rendering the 3D model.

  5. Pixel colors are calculated in the fragment shader program and output to the screen.

  6. Finally, the rendered result is displayed on the screen using a WebGL context.

By using the vertex method, WebGL can render a large number of complex 3D objects very efficiently to achieve a more realistic 3D scene.

2. Case

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <script src="../lib/index.js"></script>
  <style>
    * {<!-- -->
      margin: 0;
      padding: 0;
    }

    canvas{<!-- -->
      margin: 50px auto 0;
      display: block;
      background: yellow;
    }
  </style>
</head>
<body>
  <canvas id="canvas" width="400" height="400">
    This browser does not support canvas
  </canvas>
</body>
</html>
<script>

  const ctx = document. getElementById('canvas')

  const gl = ctx.getContext('webgl')

  // Create shader source code
  const VERTEX_SHADER_SOURCE = `
    attribute vec4 aPosition;
    attribute vec4 aColor;
    varying vec4 vColor;

    uniform mat4 mat;
    void main() {
      gl_Position = mat * aPosition;
      vColor = aColor;
    }
  `; // vertex shader

  const FRAGMENT_SHADER_SOURCE = `
    precision lowp float;
    varying vec4 vColor;

    void main() {
      gl_FragColor = vColor;
    }
  `; // fragment shader

  const program = initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE)

  const aPosition = gl.getAttribLocation(program, 'aPosition');
  const aColor = gl. getAttribLocation(program, 'aColor');
  const mat = gl.getUniformLocation(program, 'mat');

  // vertices
  const v0 = [1,1,1];
  const v1 = [-1,1,1];
  const v2 = [-1,-1,1];
  const v3 = [1,-1,1];
  const v4 = [1,-1,-1];
  const v5 = [1,1,-1];
  const v6 = [-1,1,-1];
  const v7 = [-1,-1,-1];
  const points = new Float32Array([
    ...v0,...v1,...v2, ...v0,...v2, ...v3, // previous
    ...v0,...v3,...v4, ...v0,...v4, ...v5, // right
    ...v0,...v5,...v6, ...v0,...v6, ...v1, // above
    ...v1,...v6,...v7, ...v1,...v7, ...v2, // left
    ...v7,...v4,...v3, ...v7,...v3, ...v2, // bottom
    ...v4,...v7,...v6, ...v4,...v6, ...v5, // after
  ])

  const buffer = gl. createBuffer();

  const BYTES = points. BYTES_PER_ELEMENT;

  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);

  gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW);

  gl.vertexAttribPointer(aPosition, 3, gl.FLOAT, false, 0, 0);

  gl. enableVertexAttribArray(aPosition)

  const colorData = new Float32Array([
    1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,
    0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,
    0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,
    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,
  ])
  const colorBuffer = gl. createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
  gl.bufferData(gl.ARRAY_BUFFER, colorData, gl.STATIC_DRAW);
  gl.vertexAttribPointer(aColor, 3, gl.FLOAT, false, 0, 0);
  gl. enableVertexAttribArray(aColor)


  let eyex = 3;
  let eyey = 3;
  let eyez = 5;

  let deg = 0;
  function draw() {<!-- -->
    deg += 0.01;
    const rotate = getRotateMatrix(deg);
    const vm = getViewMatrix(eyex,eyey,eyez,0.0,0.0,0.0,0.0,0.6,0.0);
    const perspective = getPerspective(30, ctx. width / ctx. height, 100, 1);
    gl.enable(gl.DEPTH_TEST);
    gl.uniformMatrix4fv(mat, false, mixMatrix(mixMatrix(perspective, vm), rotate));
    gl.drawArrays(gl.TRIANGLES, 0, points.length / 3);

    requestAnimationFrame(draw)
  }

  draw()
</script>

2. Index method

1. The concept of indexing

Indexing in WEBGL is a technique to optimize rendering performance, which can reduce the number of repeatedly calculated vertices, reduce the amount of transmitted data, and improve rendering efficiency.

The index method can access the data in the vertex array (Vertex Array) through an index array (Index Array), where each element in the index array is a pointer to a vertex in the vertex array. The benefit of using indexed arrays is that performance can be improved by reducing the number of times vertex data is recalculated by utilizing the same vertex data reuse.

In WEBGL, using indexing requires the use of two buffers: one for storing vertex data and the other for storing index data. During the rendering process, WEBGL will read vertex data from the vertex buffer, and then access the vertex data according to the index array in the index buffer.

The following is a WEBGL rendering process using the index method:

1. Create and bind vertex buffer and index buffer;

2. Write vertex data to the vertex buffer;

3. Write the index array into the index buffer;

4. Use the gl.drawElements function for rendering, which reads the vertex data in the vertex buffer and the index array in the index buffer, and then draws the triangle.

The index method is very commonly used in 3D model rendering, which can not only improve rendering efficiency, but also reduce the amount of transmitted data and reduce the occupation of network bandwidth.

2. Case

gl. drawElements(mode, count, type, offset)

  • mode is the same as gldrawArrays0
  • count number of vertices to draw
  • type vertex data type
  • The position where the offset index array starts to draw

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <script src="../lib/index.js"></script>
  <style>
    * {<!-- -->
      margin: 0;
      padding: 0;
    }

    canvas{<!-- -->
      margin: 50px auto 0;
      display: block;
      background: yellow;
    }
  </style>
</head>
<body>
<canvas id="canvas" width="400" height="400">
  This browser does not support canvas
</canvas>
</body>
</html>
<script>

  const ctx = document. getElementById('canvas')

  const gl = ctx.getContext('webgl')

  // Create shader source code
  const VERTEX_SHADER_SOURCE = `
    attribute vec4 aPosition;
    attribute vec4 aColor;
    varying vec4 vColor;

    uniform mat4 mat;
    void main() {
      gl_Position = mat * aPosition;
      vColor = aPosition;
    }
  `; // vertex shader

  const FRAGMENT_SHADER_SOURCE = `
    precision lowp float;
    varying vec4 vColor;

    void main() {
      gl_FragColor = vColor;
    }
  `; // fragment shader

  const program = initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE)

  const aPosition = gl.getAttribLocation(program, 'aPosition');
  const aColor = gl. getAttribLocation(program, 'aColor');
  const mat = gl.getUniformLocation(program, 'mat');

  const vertices = new Float32Array([
     1, 1, 1,
    -1, 1, 1,
    -1,-1, 1,
     1,-1, 1,
     1,-1,-1,
     1, 1, -1,
    -1, 1, -1,
    -1,-1,-1,
  ])

  const buffer = gl. createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
  gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
  gl.vertexAttribPointer(aPosition, 3, gl.FLOAT, false, 0, 0);
  gl. enableVertexAttribArray(aPosition)

  const indeces = new Uint8Array([
    0,1,2,0,2,3,
    0,3,4,0,4,5,
    0,5,6,0,6,1,
    1,6,7,1,7,2,
    7,4,3,7,3,2,
    4,6,7,4,6,5,
  ])
  const indexBuffer = gl. createBuffer();
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indeces, gl.STATIC_DRAW);

  let eyex = 3;
  let eyey = 3;
  let eyez = 5;

  let deg = 0;
  function draw() {<!-- -->
    deg += 0.01;
    const rotate = getRotateMatrix(deg);
    const vm = getViewMatrix(eyex,eyey,eyez,0.0,0.0,0.0,0.0,0.6,0.0);
    const perspective = getPerspective(30, ctx. width / ctx. height, 100, 1);
    gl.enable(gl.DEPTH_TEST);
    gl.uniformMatrix4fv(mat, false, mixMatrix(mixMatrix(perspective, vm), rotate));
    gl.drawElements(gl.TRIANGLES, indeces.length, gl.UNSIGNED_BYTE, 0);

    requestAnimationFrame(draw)
  }

  draw()
</script>

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <script src="../lib/index.js"></script>
  <style>
    * {<!-- -->
      margin: 0;
      padding: 0;
    }

    canvas{<!-- -->
      margin: 50px auto 0;
      display: block;
      background: yellow;
    }
  </style>
</head>
<body>
<canvas id="canvas" width="400" height="400">
  This browser does not support canvas
</canvas>
</body>
</html>
<script>

  const ctx = document. getElementById('canvas')

  const gl = ctx.getContext('webgl')

  // Create shader source code
  const VERTEX_SHADER_SOURCE = `
    attribute vec4 aPosition;
    attribute vec4 aColor;
    varying vec4 vColor;

    uniform mat4 mat;
    void main() {
      gl_Position = mat * aPosition;
      vColor = aColor;
    }
  `; // vertex shader

  const FRAGMENT_SHADER_SOURCE = `
    precision lowp float;
    varying vec4 vColor;

    void main() {
      gl_FragColor = vColor;
    }
  `; // fragment shader

  const program = initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE)

  const aPosition = gl.getAttribLocation(program, 'aPosition');
  const aColor = gl. getAttribLocation(program, 'aColor');
  const mat = gl.getUniformLocation(program, 'mat');

  const vertices = new Float32Array([
    // 0123
    1, 1, 1,
    -1, 1, 1,
    -1,-1, 1,
    1,-1, 1,
    // 0345
    1, 1, 1,
    1,-1, 1,
    1,-1,-1,
    1, 1, -1,
    // 0156
    1, 1, 1,
    1, 1, -1,
    -1, 1, -1,
    -1, 1, 1,
    //1267
    -1, 1, 1,
    -1,1, -1,
    -1, -1, -1,
    -1,-1,1,
    // 2347
    -1,-1, 1,
    1,-1, 1,
    1,-1,-1,
    -1,-1,-1,
    //4567
    1,-1,-1,
    1, 1, -1,
    -1, 1, -1,
    -1,-1,-1,
  ])

  /*
     1, 1, 1, 0
    -1, 1, 1, 1
    -1,-1, 1, 2
     1,-1, 1, 3
     1,-1,-1, 4
     1, 1, -1, 5
    -1, 1, -1, 6
    -1,-1,-1, 7
  * */
  const buffer = gl. createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
  gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
  gl.vertexAttribPointer(aPosition, 3, gl.FLOAT, false, 0, 0);
  gl. enableVertexAttribArray(aPosition)

  const colors = new Float32Array([
    0.4,0.4,1.0,0.4,0.4,1.0,0.4,0.4,1.0,0.4,0.4,1.0,
    0.4,1.0,0.4,0.4,1.0,0.4,0.4,1.0,0.4,0.4,1.0,0.4,
    1.0,0.4,0.4,1.0,0.4,0.4,1.0,0.4,0.4,1.0,0.4,0.4,
    1.0,1.0,0.4,1.0,1.0,0.4,1.0,1.0,0.4,1.0,1.0,0.4,
    1.0,0.0,1.0,1.0,0.0,1.0,1.0,0.0,1.0,1.0,0.0,1.0,
    0.0,1.0,1.0,0.0,1.0,1.0,0.0,1.0,1.0,0.0,1.0,1.0,
  ])

  const colorBuffer = gl. createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
  gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW);
  gl.vertexAttribPointer(aColor, 3, gl.FLOAT, false, 0, 0);
  gl. enableVertexAttribArray(aColor)

  const indeces = new Uint8Array([
    0,1,2,0,2,3,
    4,5,6,4,6,7,
    8,9,10,8,10,11,
    12,13,14,12,14,15,
    16,17,18,16,18,19,
    20,21,22,20,22,23,
  ])
  const indexBuffer = gl. createBuffer();
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indeces, gl.STATIC_DRAW);

  let eyex = 3;
  let eyey = 3;
  let eyez = 5;

  let deg = 0;
  function draw() {<!-- -->
    deg += 0.01;
    const rotate = getRotateMatrix(deg);
    const vm = getViewMatrix(eyex,eyey,eyez,0.0,0.0,0.0,0.0,0.6,0.0);
    const perspective = getPerspective(30, ctx. width / ctx. height, 100, 1);
    gl.enable(gl.DEPTH_TEST);
    gl.uniformMatrix4fv(mat, false, mixMatrix(mixMatrix(perspective, vm), rotate));
    gl.drawElements(gl.TRIANGLES, indeces.length, gl.UNSIGNED_BYTE, 0);

    requestAnimationFrame(draw)
  }

  draw()
</script>

Summary

The WEBGL index method is a technology to optimize the WEBGL drawing process. Compared with the vertex method, it has the following aspects:

  1. Optimized triangle drawing performance: Use indexing to reduce unnecessary repeated vertex processing, thereby reducing processing time and memory consumption when drawing triangles.

  2. Drawing large-scale models: When drawing large-scale models, using the index method can significantly improve drawing efficiency, reduce memory consumption, and reduce GPU load.

  3. Dynamic model deformation: When the model is deformed, such as character animation, using the index method can avoid recalculation and transfer of vertex data, save memory and processing time, and make animation smoother.

  4. Multiple Mesh Combination Drawing: When a model with multiple mesh combinations needs to be drawn, it is easier to manage and combine the data using the indexing method.

The WEBGL indexing method can help us optimize the performance and efficiency of the WEBGL drawing process, and improve the user experience and performance of web pages.