Use WebGL to draw heat maps

1. Heat map

A heat map needs to be drawn in the project. The heat map is actually a numerical value that is distinguished by color. The value of each point needs to be mapped to a specified color according to the color mapping table (palette). Three numerical fields are required, which can be drawn in a parallel coordinate system (two numerical fields determine the x and y axes respectively, and one numerical field determines the coloring). The effect is as follows:
insert image description here
In fact, it is to assign a specified color to each point. Both echarts and canvas can easily realize the effect of heat map (using createImageData). Since I have learned WebGL before, I want to use webgl to realize the effect of heat map.

  • How to use webgl to draw it?

The heat map is composed of colored points, so you only need to think about how to use webgl to draw each colored point, then the effect of the heat map can be formed naturally. In webgl, there are vertex shaders and fragment shaders, one is used to calculate the vertex position, and the other is used to calculate the color value, so the key is to pass the data to these two shaders.

2. WebGL draws multiple points

buffer object

It is very convenient to draw a point in webgl, the code is as follows:

  //顶点着色器
  const VERTEX_SHADER_SOURCE = `
    void main() {
      gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
      gl_PointSize = 10.0;
    }  
  `
  //片元着色器
  const FRAGMENT_SHADER_SOURCE = `
    void main() {
      gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
    }   
  `
  //创建着色器
  const program = initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE)
  gl.drawArrays(gl.POINTS, 0, 1)

If you want to draw multiple points at the same time, you need to use the buffer object it provides , which can pass the data of multiple vertices to the vertex shader at one time.

attribute variable

The attribute is used to store the input of each vertex in the vertex shader, including information such as vertex position coordinates, texture coordinates, and color, but it can only be used in the vertex shader.
The buffer is the data sent by the program to the GPU, and the attribute is used to obtain the required data from the buffer and provide it to the vertex shader.

use buffer

The buffer object is a storage area in WebGL, and all the vertex data to be drawn can be stored in the buffer object. Create a buffer first, and then write vertex data into it, then you can color the vertex at one time and pass in the data of the attribute variable of multiple vertices.
insert image description here

First you need to define all the data to be written to the buffer object.

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

Then use the buffer object to pass the data of multiple vertices to the vertex shader. There are five main steps:
1. Create a buffer object

const vertexBuffer = gl.createBuffer();

2. Bind the buffer object

gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);

3. Write data to the buffer object

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

4. Assign the buffer object to an attribute variable

gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);

5. Activate the attribute variable

gl.enableVertexAttribArray(a_Position);
  • use buffer code
// 获取attribute变量位置
const a_Position = gl.getAttribLocation(gl.program, 'a_Position');
if (a_Position < 0) {
    
    
  console.log('Failed to get the storage location of a_Position');
  return;
}
// 向缓冲区对象写入的数据
const vertices = new Float32Array([
  -0.5, 0.5,
  -0.5, -0.5,
  0.5, 0.5
])
const vertexBuffer = gl.createBuffer();//创建缓冲区对象
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);// 绑定缓冲区对象到gl.ARRAY_BUFFER上,gl.ARRAY_BUFFER表示缓冲区对象中包含了顶点数据
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);// 将数据写入缓冲区对象,gl.STATIC_DRAW代表只向缓冲区写入一次数据
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0); // 调用顶点缓冲,将缓冲数据传给a_Position
gl.enableVertexAttribArray(a_Position);// 激活a_Position使用缓冲数组

  • shader code

In the shader, variables generally named beginning with gl_ are built-in variables of the shader. A variable declaration generally includes <storage qualifier> <data type> <variable name>. In the following code, attribute represents the storage qualifier, vec is the data type, and a_Position is the variable name.

const vs_source = `
  attribute vec4 a_Position;
  void main() {
    gl_Position = a_Position;
    gl_PointSize = 10.0;
  }
`;
// 片元着色器
const fs_source = `
  void main() {
    gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
  }
`;
  • full code
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>webgl绘制多个点</title>
</head>

<body>
  <canvas id="canvas" width="400" height="400"></canvas>
  <script>
    // 顶点着色器
    const vs_source = `
      attribute vec4 a_Position;
      void main() {
        gl_Position = a_Position;
        gl_PointSize = 10.0;
      }
    `;
    // 片元着色器
    const fs_source = `
      void main() {
        gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
      }
    `;
    const canvas = document.getElementById('canvas');
    const gl = canvas.getContext('webgl');
  
    function initShader() {
      
      
      // 创建shader
      const vs_shader = gl.createShader(gl.VERTEX_SHADER);
      gl.shaderSource(vs_shader, vs_source);
      gl.compileShader(vs_shader);
      if (!gl.getShaderParameter(vs_shader, gl.COMPILE_STATUS)) {
      
      
        const error = gl.getShaderInfoLog(vs_shader);
        console.log('Failed to compile vs_shader:' + error);
        gl.deleteShader(vs_shader);
        return;
      }
      const fs_shader = gl.createShader(gl.FRAGMENT_SHADER);
      gl.shaderSource(fs_shader, fs_source);
      gl.compileShader(fs_shader);
      if (!gl.getShaderParameter(fs_shader, gl.COMPILE_STATUS)) {
      
      
        const error = gl.getShaderInfoLog(fs_shader);
        console.log('Failed to compile fs_shader:' + error);
        gl.deleteShader(fs_shader);
        return;
      }
  
      // 创建program
      const program = gl.createProgram();
      gl.attachShader(program, vs_shader);
      gl.attachShader(program, fs_shader);
      gl.linkProgram(program);
      if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
      
      
        const error = gl.getProgramInfoLog(program);
        console.log('无法链接程序对象:' + error);
        gl.deleteProgram(program);
        gl.deleteShader(fs_shader);
        gl.deleteShader(vs_shader);
        return;
      }
      gl.useProgram(program);
      gl.program = program;

      // 获取attribute变量位置
      const a_Position = gl.getAttribLocation(gl.program, 'a_Position');
      if (a_Position < 0) {
      
      
        console.log('Failed to get the storage location of a_Position');
        return;
      }
      // 向缓冲区对象写入的数据
      const vertices = new Float32Array([
        -0.5, 0.5,
        -0.5, -0.5,
        0.5, 0.5
      ])
      const vertexBuffer = gl.createBuffer();//创建缓冲区对象
      gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);// 绑定缓冲区对象到gl.ARRAY_BUFFER上,gl.ARRAY_BUFFER表示缓冲区对象中包含了顶点数据
      gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);// 将数据写入缓冲区对象,gl.STATIC_DRAW代表只向缓冲区写入一次数据
      gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0); // 调用顶点缓冲,将缓冲数据传给a_Position
      gl.enableVertexAttribArray(a_Position);// 激活a_Position使用缓冲数组
    }
  
    initShader();
    
    gl.clearColor(0.0, 0.0, 0.0, 1.0);
    gl.clear(gl.COLOR_BUFFER_BIT);
    gl.drawArrays(gl.POINTS, 0, 3);//绘制3个点
  </script>
</body>
</html>
  • Effect

insert image description here

3. WebGL draws multiple colored points

The next step is to draw the colored points, and the color data of each point needs to be passed in.

varying

Varying generally exists in both the vertex shader and the fragment shader, and its function is to transfer data from the vertex shader to the fragment shader.

// 顶点着色器
const vs_source = `
  attribute vec4 a_Position;
  attribute float a_PointSize;
  attribute vec4 a_Color;
  varying vec4 v_Color;
  void main() {
    gl_Position = a_Position;
    gl_PointSize = a_PointSize;
    v_Color = a_Color;
  }
`;
// 片元着色器
const fs_source = `
  precision mediump float;
  varying vec4 v_Color;
  void main() {
    gl_FragColor = v_Color;
  }
`;

Above, the vertex shader receives and sets the position and size of the vertex through a_Position and a_PointSize respectively, obtains the color from the program through a_Color and passes it to the fragment shader through v_Color.
The fragment shader first sets float to medium precision, then receives the color from the vertex shader through v_Color and sets it to the built-in variable gl_FragColor, where the vertex pixel color is determined by the built-in variable gl_FragColor.

read buffer

Buffer data, 7 as a group, the first two data represent the vertex position, the third codes the vertex size, and the 4-7 represents the color of the vertex.

const vertices = new Float32Array([
  -0.5, 0.5, 10.0, 1.0, 0.0, 0.0, 1.0,
  -0.5, -0.5, 20.0, 0.0, 1.0, 0.0, 1.0,
  0.5, 0.5, 30.0, 0.0, 0.0, 1.0, 1.0
])
  • How to read the corresponding vertex position, size and color data?

gl.vertexAttribPointer() can specify the rules of the read buffer.
insert image description here
Set buffered read rules and enable buffered objects

//设置缓冲读取规则
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, SIZE * 7, 0); // 将缓冲数据中一组7个数据中的前2个数据传给a_Position
gl.vertexAttribPointer(a_PointSize, 1, gl.FLOAT, false, SIZE * 7, SIZE * 2); // 将缓冲数据中一组7个数据中的第3(偏移2个数据取1一个)个数据传给a_PointSize
gl.vertexAttribPointer(a_Color, 4, gl.FLOAT, false, SIZE * 7, SIZE * 3); //将缓冲数据中一组7个数据中的第4-7(偏移3个数据取4个)个数据传给a_Color
//启用缓冲对象
gl.enableVertexAttribArray(a_Position);// 激活a_Position使用缓冲数组
gl.enableVertexAttribArray(a_PointSize);// 激活a_Position使用缓冲数组
gl.enableVertexAttribArray(a_Color);// 激活a_Color使用缓冲数组
  • Effect

Three points of different sizes and colors are drawn.
insert image description here

4. Drawing of heat map

Next, the drawing of the heat map is very simple, just pass the position information and color value of each point to the shader using the buffer.
The buffer data can be defined as follows, 6 as a group, the first 2 represent the position, and the last 4 represent the color (the color of each point is calculated according to the color mapping table).

const vertices = new Float32Array([
  -0.5, 0.5, 1.0, 0.0, 0.0, 1.0,
  -0.5, -0.5,  0.0, 1.0, 0.0, 1.0,
  0.5, 0.5,  0.0, 0.0, 1.0, 1.0
  ......
])

Guess you like

Origin blog.csdn.net/weixin_43288600/article/details/130486986