
webgl 不是 html5 的一个“功能”,它是个独立的底层 API
想用 WebGL 渲染 3D,你得亲手管理顶点缓冲、着色器编译、帧缓冲绑定——它不提供 drawCube() 这种函数。所谓“html5 WebGL 入门”,其实是“在浏览器里调用 OpenGL ES 2.0 兼容接口”的过程。canvas 元素只是画布容器,getContext('webgl') 才真正开启 GPU 访问权限。
常见错误现象:getContext('webgl') 返回 NULL;控制台报错 WebGL not supported 或 Failed to initialize WebGL。
- 检查浏览器是否启用硬件加速(chrome 地址栏输入
chrome://settings/system) - 确认 Canvas 没被设为
display: none或宽高为 0 —— 即使隐藏也要先设置width/height属性值 - 部分集成显卡或虚拟机环境默认禁用 WebGL,可尝试加启动参数
--enable-webgl --ignore-gpu-blacklist(仅开发调试)
着色器代码必须手动编译链接,不能直接写 js 里
WebGL 没有内置着色器,gl.createShader 创建后,必须用 gl.shaderSource 注入 GLSL 字符串,再调用 gl.compileShader 和 gl.getShaderParameter(shader, gl.COMPILE_STATUS) 查错。漏掉任一环节,gl.linkProgram 必定失败。
典型错误信息:Error: 0:1: 'Attribute' : syntax error —— 多半是用了 WebGL 2 的语法(如 in/out)却在 WebGL 1 上运行。
立即学习“前端免费学习笔记(深入)”;
- WebGL 1 对应 GLSL ES 1.0:用
attribute/varying/uniform;WebGL 2 对应 GLSL ES 3.0:改用in/out - 着色器源码不能直接写成 JS 字符串模板(尤其含换行缩进),建议用
document.getElementById('vs').textContent从<script type="x-shader/x-vertex"></script>标签读取 - 编译失败时,务必调用
gl.getShaderInfoLog(shader),否则看不到具体哪行出错
矩阵运算没有现成方法,得靠第三方库或手写
WebGL 本身不提供 mat4.perspective() 或 vec3.normalize(),所有变换矩阵都得自己算或引入 gl-matrix 这类轻量库。新手常误以为 gl.uniformMatrix4fv 能自动做透视投影,结果只传了个单位阵,模型缩成一个点。
使用场景:相机视图(view)、投影(projection)、模型变换(model)三者相乘顺序必须是 projection × view × model,顺序颠倒会导致坐标系混乱。
-
gl-matrix的mat4.lookAt()第二个参数是“目标点”,不是方向向量;第三个参数是“上方向”,不能设成[0,0,0] - 别在每一帧重复创建新矩阵对象(如
new Float32Array(16)),复用已有数组 +mat4.identity()重置更省 GC 压力 - 如果用 Three.js 等高层库,本质仍是封装了这些步骤——但调试时若跳过理解,遇到
INVALID_OPERATION错误会完全无从下手
纹理加载异步,直接 bindTexture 会渲染黑块
gl.bindTexture(gl.TEXTURE_2D, texture) 后立刻 gl.drawArrays(),大概率得到纯黑或噪点——因为 Image 加载是异步的,GPU 还没拿到像素数据。WebGL 不会等图片 onload 完再执行绘制。
性能影响:频繁切换纹理(尤其是未压缩的 PNG)会触发 GPU 同步等待,帧率骤降。
- 必须监听
image.onload,在回调里调用gl.texImage2D(),并确保此时上下文仍有效 - WebP/DDS/KTX2 格式比 PNG 更适合 GPU 直传,但需服务端支持 MIME 类型,且 KTX2 需启用
EXT_texture_compression_ktx2扩展 - 调试时可在
texImage2D后加console.log(gl.checkFramebufferStatus(gl.FRAMEBUFFER))确认纹理是否就绪
真正卡住人的地方,往往不是语法,而是 GPU 状态和 CPU 执行流的时间差——比如你以为 draw 已执行,其实 shader 还在编译,纹理还没上传,矩阵还是上一帧的旧值。这些状态全靠手动跟踪,没法靠 console.log 自动补全。