游戏开发者的 WebGL 基础
WebGL 让你在浏览器里直接访问 GPU。它是 3D(以及高性能 2D)网页游戏的基础。这篇教程覆盖核心内容。
1)获取一个 WebGL 上下文
js
const canvas = document.getElementById('game')
const gl = canvas.getContext('webgl2') || canvas.getContext('webgl')
if (!gl) {
// 回退或错误提示
console.error('WebGL not supported')
}可用时优先选择 webgl2——它已经被广泛支持,并提供更好的特性。
2)渲染管线(简化版)
- 顶点着色器对每个顶点运行一次,确定几何体的位置。
- 片元着色器对每个像素运行一次,决定输出颜色。
- buffer 持有数据(位置、UV、颜色)。
- 纹理是着色器采样的图像。
3)一对最小化 shader
顶点着色器:
glsl
#version 300 es
in vec2 a_position;
void main() {
gl_Position = vec4(a_position, 0.0, 1.0);
}片元着色器:
glsl
#version 300 es
precision mediump float;
out vec4 fragColor;
void main() {
fragColor = vec4(1.0, 0.5, 0.2, 1.0); // 橙色
}4)编译并链接 shader
js
function createShader(gl, type, source) {
const shader = gl.createShader(type)
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, vs, fs) {
const program = gl.createProgram()
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
}5)创建 buffer
js
const positions = new Float32Array([
-0.5, -0.5,
0.5, -0.5,
0.0, 0.5,
])
const buffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, buffer)
gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW)6)把 buffer 连接到 shader
js
const positionLoc = gl.getAttribLocation(program, 'a_position')
gl.enableVertexAttribArray(positionLoc)
gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0)7)游戏循环
js
function render() {
gl.viewport(0, 0, canvas.width, canvas.height)
gl.clearColor(0, 0, 0, 1)
gl.clear(gl.COLOR_BUFFER_BIT)
gl.useProgram(program)
gl.drawArrays(gl.TRIANGLES, 0, 3)
requestAnimationFrame(render)
}
render()8)加载纹理
js
function loadTexture(gl, url) {
const texture = gl.createTexture()
gl.bindTexture(gl.TEXTURE_2D, texture)
// 图片加载前的占位像素
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE,
new Uint8Array([255, 0, 255, 255]))
const img = new Image()
img.onload = () => {
gl.bindTexture(gl.TEXTURE_2D, texture)
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img)
gl.generateMipmap(gl.TEXTURE_2D)
}
img.src = url
return texture
}9)常见的坑
- 2 的幂次纹理在 WebGL 2 里不是严格要求,但搭配 mipmap 会更好。
- 上下文丢失:处理
webglcontextlost事件——移动浏览器在内存压力下会回收上下文。 - 浮点纹理需要检查扩展(
EXT_color_buffer_float)。
10)什么时候用库
原生 WebGL 很啰嗦。对大多数游戏,可以考虑:
- Three.js —— 功能完整的 3D
- PixiJS —— 高性能 2D 渲染
- PlayCanvas —— 自带编辑器的游戏引擎
什么时候用原生 WebGL:
- 需要最大化控制
- 需要自定义渲染器
- 想搞清楚底层到底怎么工作
相关阅读
- 游戏的 WebGPU
- Canvas 2D 游戏循环
- 发布一个快速加载的网页游戏
- 2026 年的网页游戏技术栈 —— 什么时候选 WebGL、WebGPU 或 Wasm
- 游戏物理库 —— 通过 WebGL 渲染的物理引擎
- 网页游戏引擎对比 —— Three.js、PixiJS、PlayCanvas 等
外部资源
- MDN: WebGL API —— 完整 API 参考
- WebGL2 Fundamentals —— 深入的 WebGL2 系列教程
- The Book of Shaders —— GLSL 片元着色器交互式指南
- Three.js documentation —— 最流行的 WebGL 框架