WebGL 与 3D 可视化
WebGL 概述
WebGL(Web Graphics Library)是基于 OpenGL ES 的浏览器 3D 图形 API,通过 GPU 进行硬件加速渲染。
WebGL 渲染管线
| 阶段 | 说明 | 可编程 |
|---|---|---|
| 顶点着色器 | 处理顶点位置、变换 | ✅ |
| 图元装配 | 将顶点组装为三角形 | ❌ |
| 光栅化 | 将三角形转为片元(像素) | ❌ |
| 片元着色器 | 计算每个片元的颜色 | ✅ |
| 输出合并 | 深度测试、混合 | ❌ |
WebGL 基础
着色器(Shader)
着色器使用 GLSL(OpenGL Shading Language)编写:
// 顶点着色器:处理每个顶点的位置
const vertexShaderSource = `
attribute vec2 a_position; // 顶点坐标(从 JS 传入)
uniform vec2 u_resolution; // 画布分辨率
void main() {
// 将像素坐标转换为 [-1, 1] 的裁剪空间
vec2 clipSpace = (a_position / u_resolution) * 2.0 - 1.0;
gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
}
`;
// 片元着色器:计算每个像素的颜色
const fragmentShaderSource = `
precision mediump float;
uniform vec4 u_color; // 颜色(从 JS 传入)
void main() {
gl_FragColor = u_color;
}
`;
初始化流程
function initWebGL(canvas: HTMLCanvasElement): WebGLRenderingContext | null {
const gl = canvas.getContext('webgl');
if (!gl) return null;
// 1. 编译着色器
const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);
// 2. 链接程序
const program = createProgram(gl, vertexShader!, fragmentShader!);
gl.useProgram(program);
// 3. 创建缓冲区并传入顶点数据
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
10, 20, 80, 20, 10, 80, // 三角形 1
10, 80, 80, 20, 80, 80, // 三角形 2
]), gl.STATIC_DRAW);
// 4. 设置属性指针
const posLoc = gl.getAttribLocation(program!, 'a_position');
gl.enableVertexAttribArray(posLoc);
gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, 0, 0);
// 5. 绘制
gl.drawArrays(gl.TRIANGLES, 0, 6);
return gl;
}
function createShader(gl: WebGLRenderingContext, type: number, source: string): WebGLShader | null {
const shader = gl.createShader(type);
if (!shader) return null;
gl.shaderSource(shader, source);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
console.error(gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
return null;
}
return shader;
}
function createProgram(
gl: WebGLRenderingContext, vs: WebGLShader, fs: WebGLShader
): WebGLProgram | null {
const program = gl.createProgram();
if (!program) return null;
gl.attachShader(program, vs);
gl.attachShader(program, fs);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
console.error(gl.getProgramInfoLog(program));
return null;
}
return program;
}
Three.js
Three.js 是最流行的 WebGL 封装库,大幅简化 3D 开发:
核心概念
import * as THREE from 'three';
// 1. 场景
const scene = new THREE.Scene();
// 2. 相机(透视相机)
const camera = new THREE.PerspectiveCamera(
75, // 视角(FOV)
window.innerWidth / window.innerHeight, // 宽高比
0.1, // 近平面
1000 // 远平面
);
camera.position.z = 5;
// 3. 渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
document.body.appendChild(renderer.domElement);
// 4. 创建物体
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshPhongMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
// 5. 添加灯光
scene.add(new THREE.AmbientLight(0x404040));
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(5, 5, 5);
scene.add(directionalLight);
// 6. 渲染循环
function animate(): void {
requestAnimationFrame(animate);
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render(scene, camera);
}
animate();
3D 数据可视化示例
// 3D 柱状图
function binderCreate3DBarChart(
scene: THREE.Scene,
data: Array<{ label: string; value: number }>,
): void {
const barWidth = 0.8;
const gap = 0.4;
data.forEach((item, i) => {
const height = item.value / 20; // 归一化高度
const geometry = new THREE.BoxGeometry(barWidth, height, barWidth);
const material = new THREE.MeshPhongMaterial({
color: new THREE.Color().setHSL(i / data.length, 0.7, 0.5),
});
const bar = new THREE.Mesh(geometry, material);
bar.position.x = i * (barWidth + gap);
bar.position.y = height / 2; // 底部对齐
scene.add(bar);
});
}
WebGL 2.0 与 WebGPU
| 特性 | WebGL 1.0 | WebGL 2.0 | WebGPU |
|---|---|---|---|
| 基础 | OpenGL ES 2.0 | OpenGL ES 3.0 | 新设计 |
| 3D 纹理 | ❌ | ✅ | ✅ |
| 实例化渲染 | 扩展 | ✅ | ✅ |
| MSAA | 扩展 | ✅ | ✅ |
| 计算着色器 | ❌ | ❌ | ✅ |
| 多线程 | ❌ | ❌ | ✅ |
| 浏览器支持 | 广泛 | 广泛 | Chrome 113+ |
常见面试问题
Q1: WebGL 的渲染管线是什么?
答案:
WebGL 渲染管线:顶点数据 → 顶点着色器(可编程,处理位置变换)→ 图元装配(组装三角形)→ 光栅化(转为像素片元)→ 片元着色器(可编程,计算颜色)→ 输出合并(深度测试、混合)→ 帧缓冲区。开发者主要编写的是顶点着色器和片元着色器。
Q2: Three.js 的核心概念有哪些?
答案:
Scene(场景容器)、Camera(相机,定义观察视角)、Renderer(渲染器,输出画面)、Mesh(网格体 = Geometry 几何体 + Material 材质)、Light(灯光)。渲染流程:创建场景 → 添加物体和灯光 → 设置相机 → 渲染循环。
Q3: Canvas 2D、WebGL、Three.js 如何选择?
答案:
- Canvas 2D:2D 图表、简单动画、像素操作
- WebGL:需要极致性能、自定义着色器效果
- Three.js:3D 场景、快速开发 3D 可视化
- Deck.gl/CesiumJS:地理信息 3D 可视化
Q4: WebGPU 和 WebGL 的区别?
答案:
WebGPU 是下一代图形 API,支持计算着色器(通用 GPU 计算)、更低的 CPU 开销、更好的多线程支持。WebGL 基于 OpenGL ES,API 较旧且状态机模型效率低。WebGPU 在 Chrome 113+ 可用,但生态尚不成熟。