📅  最后修改于: 2023-12-03 14:48:25.785000             🧑  作者: Mango
WebGL 是一种基于 OpenGL ES 的 3D 图形库,可以在网页上使用 JavaScript 进行 GPU 加速的渲染。多维数据集(Multidimensional Dataset)则是指由多维数组构成的数据集合,如图像、声音等。在使用 WebGL 进行数据可视化时,可以使用“纹理”(texture)的方式将多维数据集渲染到网页上,实现更加丰富的可视化效果。
本文将介绍如何使用 WebGL 渲染多维数据集。
以下是一个使用 WebGL 渲染多维数据集的示例程序:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>WebGL 多维数据集示例</title>
</head>
<body>
<canvas id="glcanvas" width="800" height="600"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.8.1/gl-matrix-min.js"></script>
<script>
// 创建 WebGL 上下文对象
const gl = document.getElementById('glcanvas').getContext('webgl');
// 定义顶点着色器
const vertexShaderSource = `
attribute vec3 aVertexPosition;
attribute vec2 aTextureCoord;
varying highp vec2 vTextureCoord;
void main() {
gl_Position = vec4(aVertexPosition, 1.0);
vTextureCoord = aTextureCoord;
}
`;
// 定义片元着色器
const fragmentShaderSource = `
precision highp float;
uniform sampler2D uSampler;
varying highp vec2 vTextureCoord;
void main() {
gl_FragColor = texture2D(uSampler, vTextureCoord);
}
`;
// 创建程序对象
const program = createProgram(gl, vertexShaderSource, fragmentShaderSource);
// 获取属性和 uniform 变量
const positionAttributeLocation = gl.getAttribLocation(program, 'aVertexPosition');
const textureCoordAttributeLocation = gl.getAttribLocation(program, 'aTextureCoord');
const samplerUniformLocation = gl.getUniformLocation(program, 'uSampler');
// 创建纹理对象
const texture = createTexture(gl);
// 绑定纹理到纹理单元 0
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture);
// 设置纹理参数
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
// 加载数据集到纹理
const dataset = new Float32Array([
1.0, 0.0, 0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 1.0, 1.0, 1.0, 1.0
]);
const numCols = 3;
const numRows = 2;
loadTextureData(gl, texture, numCols, numRows, dataset);
// 设置顶点数据和纹理坐标数据
const positions = new Float32Array([
-1.0, 1.0, 0.0,
-1.0, -1.0, 0.0,
1.0, -1.0, 0.0,
1.0, 1.0, 0.0
]);
const textureCoords = new Float32Array([
0.0, 0.0,
0.0, 1.0,
1.0, 1.0,
1.0, 0.0
]);
// 创建顶点数组对象
const vao = createVertexArray(gl, program, positionAttributeLocation, textureCoordAttributeLocation);
// 渲染
render(gl, vao, program, samplerUniformLocation, texture);
// 创建程序对象
function createProgram(gl, vertexShaderSource, fragmentShaderSource) {
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(vertexShader, vertexShaderSource);
gl.shaderSource(fragmentShader, fragmentShaderSource);
gl.compileShader(vertexShader);
gl.compileShader(fragmentShader);
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
return program;
}
// 创建纹理对象
function createTexture(gl) {
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
return texture;
}
// 加载数据集到纹理
function loadTextureData(gl, texture, numCols, numRows, data) {
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, numCols, numRows, 0, gl.RGB, gl.FLOAT, data);
}
// 创建顶点数组对象
function createVertexArray(gl, program, positionAttributeLocation, textureCoordAttributeLocation) {
const vao = gl.createVertexArray();
gl.bindVertexArray(vao);
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
gl.vertexAttribPointer(positionAttributeLocation, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(positionAttributeLocation);
const textureCoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, textureCoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, textureCoords, gl.STATIC_DRAW);
gl.vertexAttribPointer(textureCoordAttributeLocation, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(textureCoordAttributeLocation);
return vao;
}
// 渲染
function render(gl, vao, program, samplerUniformLocation, texture) {
gl.useProgram(program);
gl.bindVertexArray(vao);
gl.uniform1i(samplerUniformLocation, 0);
gl.drawArrays(gl.TRIANGLE_FAN, 0, 4);
}
</script>
</body>
</html>
该程序使用纹理对象将一个 3x2 的数据集渲染到屏幕上,效果如下:
我们对上述代码进行解释:
const gl = document.getElementById('glcanvas').getContext('webgl');
在这一步中,通过 document.getElementById
方法获取到 <canvas>
元素,并调用其 getContext
方法创建一个 WebGL 上下文对象。如果创建失败,则返回 null
。
在 WebGL 中,我们需要使用着色器(shader)来对顶点和片元进行处理。在本示例中,我们使用了两个着色器来完成渲染。
顶点着色器:
attribute vec3 aVertexPosition;
attribute vec2 aTextureCoord;
varying highp vec2 vTextureCoord;
void main() {
gl_Position = vec4(aVertexPosition, 1.0);
vTextureCoord = aTextureCoord;
}
片元着色器:
precision highp float;
uniform sampler2D uSampler;
varying highp vec2 vTextureCoord;
void main() {
gl_FragColor = texture2D(uSampler, vTextureCoord);
}
我们使用 gl.createShader
方法创建着色器对象,使用 gl.shaderSource
方法为其指定源代码,使用 gl.compileShader
方法编译着色器,最后使用 gl.createProgram
方法将两个着色器链接成一个程序对象。
程序对象创建完成后,我们需要通过其 gl.getAttribLocation
和 gl.getUniformLocation
方法获取其属性和 uniform 变量的位置。
const positionAttributeLocation = gl.getAttribLocation(program, 'aVertexPosition');
const textureCoordAttributeLocation = gl.getAttribLocation(program, 'aTextureCoord');
const samplerUniformLocation = gl.getUniformLocation(program, 'uSampler');
在本示例中,我们使用了纹理对象将数据集渲染到屏幕上。首先,我们需要使用 gl.createTexture
方法创建纹理对象。然后,我们使用 gl.bindTexture
方法将其绑定到当前纹理单元(texture unit)上。在本示例中,我们使用纹理单元 0。
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
在本示例中,我们使用了浮点型的纹理数据。因此,我们需要使用 gl.texImage2D
方法将数据集上传到纹理对象中。
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, numCols, numRows, 0, gl.RGB, gl.FLOAT, data);
注意,这里的 data
参数应该是一个 Float32Array 对象。
我们使用两个数组来存储顶点数据和纹理坐标数据:
const positions = new Float32Array([
-1.0, 1.0, 0.0,
-1.0, -1.0, 0.0,
1.0, -1.0, 0.0,
1.0, 1.0, 0.0
]);
const textureCoords = new Float32Array([
0.0, 0.0,
0.0, 1.0,
1.0, 1.0,
1.0, 0.0
]);
注意,顶点数据应该能准确地映射到纹理数据。在本示例中,顶点数据是一个正方形,而纹理数据是一个长方形,因此纹理坐标的范围不是 $[0,1]$,而是 $[0,\frac{n}{m}]$,其中 $n$ 是列数,$m$ 是行数。
这一步中,我们使用 gl.createVertexArray
方法创建一个顶点数组对象。然后,我们使用 gl.bindVertexArray
方法将其绑定到当前上下文中。
接着,我们使用 gl.createBuffer
方法创建缓冲区对象,并使用 gl.bindBuffer
方法将其绑定到当前上下文中。然后,我们使用 gl.bufferData
方法将数据上传到缓冲区中,并使用 gl.vertexAttribPointer
方法将缓冲区中的数据关联到程序对象中的属性变量上。
const vao = gl.createVertexArray();
gl.bindVertexArray(vao);
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
gl.vertexAttribPointer(positionAttributeLocation, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(positionAttributeLocation);
const textureCoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, textureCoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, textureCoords, gl.STATIC_DRAW);
gl.vertexAttribPointer(textureCoordAttributeLocation, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(textureCoordAttributeLocation);
渲染时,我们需要使用 gl.useProgram
方法将程序对象绑定到当前上下文中,使用 gl.bindVertexArray
方法将顶点数组对象绑定到当前上下文中,使用 gl.uniform1i
方法将纹理单元绑定到该程序对象中的 uniform 变量上,最后使用 gl.drawArrays
方法进行渲染。
gl.useProgram(program);
gl.bindVertexArray(vao);
gl.uniform1i(samplerUniformLocation, 0);
gl.drawArrays(gl.TRIANGLE_FAN, 0, 4);
注意,我们使用了 gl.TRIANGLE_FAN
作为渲染类型,因为我们只有一个四边形。在实际情况中,渲染类型应该根据实际情况进行选择。
至此,创建一个使用 WebGL 渲染多维数据集的程序就完成了。