前言
大家好,我是南木元元,熱衷分享有趣實用的文章。
熱力圖
項目中需要繪制熱力圖,熱力圖其實就是數(shù)值大小用顏色來進(jìn)行區(qū)分,每個點的數(shù)值需根據(jù)顏色映射表(調(diào)色板)映射為指定顏色。需要3個數(shù)值字段,可繪制在平行坐標(biāo)系中(2個數(shù)值字段分別確定x、y軸,1個數(shù)值字段確定著色)。效果如下:
其實就是對每個點賦予指定顏色,echarts和canvas都很容易實現(xiàn)熱力圖(使用createImageData)的效果,由于之前學(xué)習(xí)過WebGL,于是就想著用webgl來實現(xiàn)熱力圖的效果。
- 如何使用webgl來進(jìn)行繪制呢?
熱力圖是由一個個彩色的點構(gòu)成,所以,只需要思考如何使用webgl繪制出一個個彩色的點,那么就自然能形成熱力圖的效果。而webgl中有頂點著色器和片元著色器,一個用于計算頂點位置,一個用于計算顏色值,所以,關(guān)鍵就是把數(shù)據(jù)傳個這兩個著色器。
WebGL繪制多個點
緩沖區(qū)對象
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);
}
`
//創(chuàng)建著色器
const program = initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE)
gl.drawArrays(gl.POINTS, 0, 1)
如果想同時繪制多個點,就需要用到它所提供的緩沖區(qū)對象,它可以一次性向頂點著色器傳入多個頂點的數(shù)據(jù)。
attribute變量
attribute用來存儲頂點著色器中每個頂點的輸入,包括頂點位置坐標(biāo)、紋理坐標(biāo)和顏色等信息,但是只能用于頂點著色器。
緩沖是程序發(fā)送給GPU的數(shù)據(jù),attribute用來從緩沖中獲取所需數(shù)據(jù),并將它提供給頂點著色器。
使用緩沖區(qū)
緩沖區(qū)對象是WebGL中的一塊存儲區(qū),可以在緩沖區(qū)對象中保存想要繪制的所有頂點數(shù)據(jù)。先創(chuàng)建一個緩沖區(qū),然后向其中寫入頂點數(shù)據(jù),就能一次性向頂點著色其傳入多個頂點的attribute變量的數(shù)據(jù)。
首先需要定義所有要向緩沖區(qū)對象寫入的數(shù)據(jù)。
const vertices = new Float32Array([
-0.5, 0.5,
-0.5, -0.5,
0.5, 0.5
])
然后使用使用緩沖區(qū)對象向頂點著色器傳入多個頂點的數(shù)據(jù),主要有五步:
1.創(chuàng)建緩沖區(qū)對象
const vertexBuffer = gl.createBuffer();
2.綁定緩沖區(qū)對象
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
3.向緩沖區(qū)對象中寫入數(shù)據(jù)
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
4.將緩沖區(qū)對象分配給一個attribute變量
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
5.激活attribute變量
gl.enableVertexAttribArray(a_Position);
- 使用緩沖區(qū)代碼
// 獲取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;
}
// 向緩沖區(qū)對象寫入的數(shù)據(jù)
const vertices = new Float32Array([
-0.5, 0.5,
-0.5, -0.5,
0.5, 0.5
])
const vertexBuffer = gl.createBuffer();//創(chuàng)建緩沖區(qū)對象
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);// 綁定緩沖區(qū)對象到gl.ARRAY_BUFFER上,gl.ARRAY_BUFFER表示緩沖區(qū)對象中包含了頂點數(shù)據(jù)
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);// 將數(shù)據(jù)寫入緩沖區(qū)對象,gl.STATIC_DRAW代表只向緩沖區(qū)寫入一次數(shù)據(jù)
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0); // 調(diào)用頂點緩沖,將緩沖數(shù)據(jù)傳給a_Position
gl.enableVertexAttribArray(a_Position);// 激活a_Position使用緩沖數(shù)組
- 著色器代碼
在著色器內(nèi),一般命名以gl_開頭的變量是著色器的內(nèi)置變量。變量聲明一般包含<存儲限定符><數(shù)據(jù)類型><變量名稱>,下面代碼中,attribute表示存儲限定符,vec是數(shù)據(jù)類型,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() {
// 創(chuàng)建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;
}
// 創(chuàng)建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('無法鏈接程序?qū)ο螅? + 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;
}
// 向緩沖區(qū)對象寫入的數(shù)據(jù)
const vertices = new Float32Array([
-0.5, 0.5,
-0.5, -0.5,
0.5, 0.5
])
const vertexBuffer = gl.createBuffer();//創(chuàng)建緩沖區(qū)對象
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);// 綁定緩沖區(qū)對象到gl.ARRAY_BUFFER上,gl.ARRAY_BUFFER表示緩沖區(qū)對象中包含了頂點數(shù)據(jù)
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);// 將數(shù)據(jù)寫入緩沖區(qū)對象,gl.STATIC_DRAW代表只向緩沖區(qū)寫入一次數(shù)據(jù)
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0); // 調(diào)用頂點緩沖,將緩沖數(shù)據(jù)傳給a_Position
gl.enableVertexAttribArray(a_Position);// 激活a_Position使用緩沖數(shù)組
}
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繪制多個彩色點
接下來就是彩色點的繪制,需要傳入每個點的顏色數(shù)據(jù)。
varying 可變量
varying一般同時存在頂點著色器和片元著色器中,它的作用是從頂點著色器向片元著色器傳輸數(shù)據(jù)。
// 頂點著色器
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分別接收并設(shè)置頂點的位置和大小,通過a_Color從程序獲取顏色并通過v_Color傳遞給片元著色器。
片元著色器,首先設(shè)置了float為中等精度,然后通過v_Color接收來自頂點著色器的顏色并將其設(shè)置給內(nèi)置變量gl_FragColor,其中通過內(nèi)置變量gl_FragColor來確定頂點像素顏色。
讀取緩沖區(qū)
緩沖區(qū)數(shù)據(jù),7個為一組,前兩個數(shù)據(jù)代表頂點位置,第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
])
- 如何讀取出相應(yīng)的頂點位置、大小以及顏色數(shù)據(jù)?
gl.vertexAttribPointer()可以指定讀取緩沖的規(guī)則。
設(shè)置緩沖讀取規(guī)則和啟用緩沖對象
//設(shè)置緩沖讀取規(guī)則
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, SIZE * 7, 0); // 將緩沖數(shù)據(jù)中一組7個數(shù)據(jù)中的前2個數(shù)據(jù)傳給a_Position
gl.vertexAttribPointer(a_PointSize, 1, gl.FLOAT, false, SIZE * 7, SIZE * 2); // 將緩沖數(shù)據(jù)中一組7個數(shù)據(jù)中的第3(偏移2個數(shù)據(jù)取1一個)個數(shù)據(jù)傳給a_PointSize
gl.vertexAttribPointer(a_Color, 4, gl.FLOAT, false, SIZE * 7, SIZE * 3); //將緩沖數(shù)據(jù)中一組7個數(shù)據(jù)中的第4-7(偏移3個數(shù)據(jù)取4個)個數(shù)據(jù)傳給a_Color
//啟用緩沖對象
gl.enableVertexAttribArray(a_Position);// 激活a_Position使用緩沖數(shù)組
gl.enableVertexAttribArray(a_PointSize);// 激活a_Position使用緩沖數(shù)組
gl.enableVertexAttribArray(a_Color);// 激活a_Color使用緩沖數(shù)組
- 效果
繪制出了3個不同大小、不同顏色的點。
熱力圖的繪制
接下來熱力圖的繪制就很簡單了,只將每個點的位置信息和顏色值使用緩沖區(qū)傳給著色器就可以。
可以如下來定義緩沖數(shù)據(jù),6個為一組,前2個代表位置,后4個代表顏色(每個點的顏色是根據(jù)顏色映射表進(jìn)行計算得到的)。文章來源:http://www.zghlxwxcb.cn/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
......
])
結(jié)語
??如果此文對你有幫助的話,歡迎??關(guān)注、??點贊、?收藏、??評論,支持一下博主~文章來源地址http://www.zghlxwxcb.cn/news/detail-799576.html
到了這里,關(guān)于快速上手WebGL,代碼+圖解手把手教你使用WebGL一步步實現(xiàn)熱力圖的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!