前言
大家好,我是南木元元,热衷分享有趣实用的文章。
热力图
项目中需要绘制热力图,热力图其实就是数值大小用颜色来进行区分,每个点的数值需根据颜色映射表(调色板)映射为指定颜色。需要3个数值字段,可绘制在平行坐标系中(2个数值字段分别确定x、y轴,1个数值字段确定着色)。效果如下:
其实就是对每个点赋予指定颜色,echarts和canvas都很容易实现热力图(使用createImageData)的效果,由于之前学习过WebGL,于是就想着用webgl来实现热力图的效果。
- 如何使用webgl来进行绘制呢?
热力图是由一个个彩色的点构成,所以,只需要思考如何使用webgl绘制出一个个彩色的点,那么就自然能形成热力图的效果。而webgl中有顶点着色器和片元着色器,一个用于计算顶点位置,一个用于计算颜色值,所以,关键就是把数据传个这两个着色器。
WebGL绘制多个点
缓冲区对象
webgl中绘制一个点很方便,代码如下:
//顶点着色器
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)
如果想同时绘制多个点,就需要用到它所提供的缓冲区对象,它可以一次性向顶点着色器传入多个顶点的数据。
attribute变量
attribute用来存储顶点着色器中每个顶点的输入,包括顶点位置坐标、纹理坐标和颜色等信息,但是只能用于顶点着色器。
缓冲是程序发送给GPU的数据,attribute用来从缓冲中获取所需数据,并将它提供给顶点着色器。
使用缓冲区
缓冲区对象是WebGL中的一块存储区,可以在缓冲区对象中保存想要绘制的所有顶点数据。先创建一个缓冲区,然后向其中写入顶点数据,就能一次性向顶点着色其传入多个顶点的attribute变量的数据。
首先需要定义所有要向缓冲区对象写入的数据。
const vertices = new Float32Array([
-0.5, 0.5,
-0.5, -0.5,
0.5, 0.5
])
然后使用使用缓冲区对象向顶点着色器传入多个顶点的数据,主要有五步:
1.创建缓冲区对象
const vertexBuffer = gl.createBuffer();
2.绑定缓冲区对象
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
3.向缓冲区对象中写入数据
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
4.将缓冲区对象分配给一个attribute变量
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
5.激活attribute变量
gl.enableVertexAttribArray(a_Position);
- 使用缓冲区代码
// 获取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使用缓冲数组
- 着色器代码
在着色器内,一般命名以gl_开头的变量是着色器的内置变量。变量声明一般包含<存储限定符><数据类型><变量名称>,下面代码中,attribute表示存储限定符,vec是数据类型,a_Position为变量名称。
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);
}
`;
- 完整代码
<!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>
- 效果
WebGL绘制多个彩色点
接下来就是彩色点的绘制,需要传入每个点的颜色数据。
varying 可变量
varying一般同时存在顶点着色器和片元着色器中,它的作用是从顶点着色器向片元着色器传输数据。
// 顶点着色器
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;
}
`;
上面,顶点着色器通过a_Position、a_PointSize分别接收并设置顶点的位置和大小,通过a_Color从程序获取颜色并通过v_Color传递给片元着色器。
片元着色器,首先设置了float为中等精度,然后通过v_Color接收来自顶点着色器的颜色并将其设置给内置变量gl_FragColor,其中通过内置变量gl_FragColor来确定顶点像素颜色。
读取缓冲区
缓冲区数据,7个为一组,前两个数据代表顶点位置,第3个代码顶点大小,第4-7个就代表顶点的颜色。
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
])
- 如何读取出相应的顶点位置、大小以及颜色数据?
gl.vertexAttribPointer()可以指定读取缓冲的规则。
设置缓冲读取规则和启用缓冲对象
//设置缓冲读取规则
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使用缓冲数组
- 效果
绘制出了3个不同大小、不同颜色的点。
热力图的绘制
接下来热力图的绘制就很简单了,只将每个点的位置信息和颜色值使用缓冲区传给着色器就可以。
可以如下来定义缓冲数据,6个为一组,前2个代表位置,后4个代表颜色(每个点的颜色是根据颜色映射表进行计算得到的)。文章来源:https://www.toymoban.com/news/detail-799576.html
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
......
])
结语
🔥如果此文对你有帮助的话,欢迎💗关注、👍点赞、⭐收藏、✍️评论,支持一下博主~文章来源地址https://www.toymoban.com/news/detail-799576.html
到了这里,关于快速上手WebGL,代码+图解手把手教你使用WebGL一步步实现热力图的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!