WebGL3(WebGL or WebGPU?)
1 WebGL介绍
WebGL(Web Graphics Library)是一种基于OpenGL ES的JavaScript API,用于在网页浏览器中实现高性能的3D图形渲染,无需安装额外插件。它通过着色器(Shader)编程直接调用GPU能力,支持复杂的3D场景、物理模拟和实时渲染效果。
WebGL完全集成于HTML5 Canvas,可与JavaScript、CSS3和Web API无缝协作,广泛应用于游戏开发、数据可视化、虚拟/增强现实(VR/AR)、科学模拟和交互式艺术等领域,为现代Web应用带来沉浸式视觉体验。主流浏览器(如Chrome、Firefox、Safari和Edge)均提供支持,是构建跨平台3D网页内容的标准技术。
要验证当前是否支持webgl。直接打开:https://get.webgl.org/
关于图形的部分,直接可以在网页查看。chrome://gpu/
我的输入如下:
官方API手册:WebGL 2.0 Specification
另一个API手册:WebGL: 2D and 3D graphics for the web - Web APIs | MDN
单页速查手册:https://www.khronos.org/files/webgl/webgl-reference-card-1_0.pdf
互动学习教程:WebGL2 理论基础
2 WebGL例子
2.1 三角形
代码
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>WebGL Triangle</title><style>body { margin: 0; overflow: hidden; }canvas { display: block; }</style>
</head>
<body><canvas id="glcanvas" width="400" height="400"></canvas>
<script>
const canvas = document.getElementById('glcanvas');
const gl = canvas.getContext('webgl');const vsSource = `attribute vec2 a_position;void main() {gl_Position = vec4(a_position, 0.0, 1.0);}
`;
const fsSource = `void main() {gl_FragColor = vec4(1, 0, 0, 1); // 红色}
`;// 编译着色器
function compileShader(gl, type, source) {const shader = gl.createShader(type);gl.shaderSource(shader, source);gl.compileShader(shader);return shader;
}const vertexShader = compileShader(gl, gl.VERTEX_SHADER, vsSource);
const fragmentShader = compileShader(gl, gl.FRAGMENT_SHADER, fsSource);const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);// 设置顶点数据
const vertices = new Float32Array([0, 1,-1, -1,1, -1
]);
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);// 启用属性
gl.useProgram(program);
const aPosition = gl.getAttribLocation(program, 'a_position');
gl.enableVertexAttribArray(aPosition);
gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, 0, 0);// 绘制
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, 3);
</script></body>
</html>
效果
2.2 渐变色
代码
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>WebGL Triangle</title><style>body { margin: 0; overflow: hidden; }canvas { display: block; }</style>
</head>
<body><canvas id="glcanvas" width="400" height="400"></canvas><canvas id="glcanvas" width="400" height="400"></canvas><script>const canvas = document.getElementById('glcanvas');const gl = canvas.getContext('webgl');const vs = `attribute vec2 a_position;varying vec2 v_pos;void main() {v_pos = a_position;gl_Position = vec4(a_position, 0, 1);}`;const fs = `precision mediump float;varying vec2 v_pos;void main() {gl_FragColor = vec4(v_pos * 0.5 + 0.5, 1.0, 1.0); // x y 映射到 RGB}`;const compile = (type, src) => {const s = gl.createShader(type);gl.shaderSource(s, src);gl.compileShader(s);return s;};const prog = gl.createProgram();gl.attachShader(prog, compile(gl.VERTEX_SHADER, vs));gl.attachShader(prog, compile(gl.FRAGMENT_SHADER, fs));gl.linkProgram(prog);gl.useProgram(prog);const quad = new Float32Array([-1, -1, 1, -1, -1, 1,-1, 1, 1, -1, 1, 1]);const buf = gl.createBuffer();gl.bindBuffer(gl.ARRAY_BUFFER, buf);gl.bufferData(gl.ARRAY_BUFFER, quad, gl.STATIC_DRAW);const pos = gl.getAttribLocation(prog, 'a_position');gl.enableVertexAttribArray(pos);gl.vertexAttribPointer(pos, 2, gl.FLOAT, false, 0, 0);gl.drawArrays(gl.TRIANGLES, 0, 6);</script></body>
</html>
效果
2.3 旋转立方体
代码
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>WebGL Triangle</title><style>body { margin: 0; overflow: hidden; }canvas { display: block; }</style>
</head>
<body><script src="https://cdn.jsdelivr.net/npm/three@0.152.2/build/three.min.js"></script>
<canvas id="canvas"></canvas>
<script>
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, 1, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({canvas: document.getElementById("canvas")});
renderer.setSize(400, 400);const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial({color: 0x00ff00, wireframe: true});
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);camera.position.z = 3;function animate() {requestAnimationFrame(animate);cube.rotation.x += 0.01;cube.rotation.y += 0.01;renderer.render(scene, camera);
}
animate();
</script></body>
</html>
效果
3 WebGL的实现
其实就是ANGLE,项目主页:https://github.com/google/angle
这个其实也是谷歌维护的项目,本质上,就是就是提供了一套opengl es的接口。为什么不用原生的opengl es呢?据说原因有几个,一个是opengl es的驱动一般是原厂提供,质量参差不齐。第二个是直接让webgl操作gles给的权限太大,相当于直接可以操作硬件了,最后有个中间层,可以跨Vulkan、D3D、Metal,相当于手机,苹果,PC全包,提供的兼容性更好。
对于谷歌安卓的原生应用,考虑到性能,还是直接使用的opengl es,但是Flutter现在也都是使用ANGLE了。
在windows下的库是libEGL_angle.so。
策略一览:
浏览器 | 是否默认用 ANGLE | 特殊说明 |
---|---|---|
Chrome(Windows/Android) | ✅ 默认用 ANGLE | 后端通常是 Vulkan 或 D3D11/12 |
Edge | ✅ 默认用 ANGLE | 同 Chrome |
ChromeOS | ✅ 用 ANGLE | 为稳定性优化 |
Firefox | ❌ 默认直接调用 GLES(Linux/Android)或 OpenGL(桌面) | 可选用 ANGLE(WebRender) |
Safari | ❌ 不用 ANGLE | 使用系统 Metal/OpenGL 实现(macOS/iOS) |
Chromium Embedded Framework (CEF) | ✅ 可配置是否用 ANGLE | 大多默认开启 |
转换的内容如下:
-
Windows 平台默认将 OpenGL ES → Direct3D 11
-
macOS 平台:OpenGL ES → Metal
-
Linux 或特定构建:OpenGL ES → Vulkan(或原生 OpenGL)
以glDrawArrays()为例,在windows下的ANGLE调用的就是Direct3D,过程如下:
[ WebGL JS ]
↓
[ glDrawArrays(...) ] ← 用户 JS 层
↓
[ libGLESv2/libANGLE ] ← ANGLE OpenGL ES API 接口层
↓
[ Context::drawArrays(...) ] ← 通用 ANGLE 调度
↓
[ RendererD3D::drawArrays(...) ] ← 后端实现(如 D3D11)
↓
[ ID3D11DeviceContext::Draw(...) ] ← GPU 驱动调用
4 WebGPU
在上面说过,WebGL是基于ANGLE,而ANGLE是opengl es接口。那么就有一个问题,那就是opengl es是opengl的简化版,天生性能不足。
特性 | OpenGL | OpenGL ES |
---|---|---|
定位 | 桌面/工作站高性能图形(PC、Mac) | 嵌入式/移动设备(手机、平板、车载系统) |
复杂度 | 功能全面,API 较庞大 | 精简版,移除冗余功能,保留核心渲染能力 |
标准组织 | Khronos Group | Khronos Group |
派生关系 | OpenGL ES 是 OpenGL 的子集 | 基于 OpenGL 裁剪,专为低功耗优化 |
具体限制如下:
类型 | 是否支持 | 说明 |
---|---|---|
计算着色器(Compute Shader) | ❌ 不支持 | 无法做 GPGPU 计算 |
几何着色器(Geometry Shader) | ❌ 不支持 | 不能动态生成图元 |
高级渲染管线 | ❌ 不支持 | 无 Tessellation |
延迟渲染(Deferred Shading) | ✅ 可模拟 | 可用 FBO + MRT 实现 |
实时阴影 | ✅ 可模拟 | 用 shadow map 技术 |
后处理特效 | ✅ 可实现 | 用多个 FBO + shader 实现 |
为了提供更高的性能和功能。所以,在之后的规范中,又搞出来了WebGPU,模式和上面大差不差,规范地址:WebGPU
特性 | WebGL | WebGPU |
---|---|---|
基于 API | OpenGL ES(老旧) | Vulkan / D3D12 / Metal(现代) |
浏览器实现方式 | 通常通过 ANGLE 转译为 Vulkan/D3D/Metal | 直接调用系统的底层图形 API |
ANGLE 依赖 | ✅ 是核心依赖 | ❌ 默认不依赖 |
性能开销 | 较高(多一层抽象) | 更低,更靠近底层硬件 |
现代 GPU 特性支持 | 弱,受 GLES 限制 | 强,支持计算着色器、并行指令、精细控制等 |
谷歌也提供了一个叫做Dawn的中间层。 地址是:https://github.com/google/dawn
5 WebGPU的例子
自己看吧,比我搞的简单例子强多了。
three.js examples
WebGPU Samples
6 原生/WebGL/WebGPU性能比较
数据来自deepseek
测试场景
一个包含大量动态物体的3D场景:
-
100,000个旋转的立方体
-
每帧更新所有物体的变换矩阵
-
使用相同的着色器程序
-
相同的渲染质量设置
在配备NVIDIA GTX 1080的测试机器上,渲染100,000个立方体的帧率(FPS):
技术 | 平均FPS | 特点 |
---|---|---|
原生OpenGL | 45 FPS | 直接硬件访问,但每个物体单独绘制调用 |
原生Vulkan | 120 FPS | 多线程命令缓冲,更低的驱动开销 |
WebGL 2.0 | 12 FPS | 高绘制调用开销,JavaScript瓶颈 |
WebGPU | 85 FPS | 接近原生性能,支持多线程和计算着色器 |
从这个测试来看,WebGPU比WebGL提升够大的,差不多7倍了。
Vulkan比OpenGL提升也够大的,差不多3倍。
WebGPU是OpenGL两倍也是没想到。。。