1.1 WebGL1 与 WebGL2 该选哪条


文档摘要

1.1 WebGL1 与 WebGL2 该选哪条 本节摘要:WebGL1 对应 OpenGL ES 2.0 的浏览器接口,WebGL2 对应 ES 3.0。两者不是“新旧皮肤”,而是能力边界:顶点数组对象、多渲染目标、三维与整数纹理、实例化绘制、变换反馈,在 1.0 里要靠扩展或根本没有,在 2.0 里是上下文自带。选型应对赌目标浏览器覆盖面和效果清单,而不是默认追新。 学习目标 阅读完本节,你应当能够: 用一句对照说清 WebGL1 与 WebGL2 各自对齐哪套移动图形标准 列出至少六项 2.0 内置、1.

1.1 WebGL1 与 WebGL2 该选哪条

本节摘要:WebGL1 对应 OpenGL ES 2.0 的浏览器接口,WebGL2 对应 ES 3.0。两者不是“新旧皮肤”,而是能力边界:顶点数组对象、多渲染目标、三维与整数纹理、实例化绘制、变换反馈,在 1.0 里要靠扩展或根本没有,在 2.0 里是上下文自带。选型应对赌目标浏览器覆盖面和效果清单,而不是默认追新。

学习目标

阅读完本节,你应当能够:

  1. 用一句对照说清 WebGL1 与 WebGL2 各自对齐哪套移动图形标准
  2. 列出至少六项 2.0 内置、1.0 缺失或仅扩展提供的能力
  3. 解释为什么“先拿 webgl 再降级”和“先拿 webgl2 再降级”是两种产品策略
  4. 针对产品页、数据可视化、后处理管线给出版本建议
  5. 识别把 WebGL2 对象拿到 WebGL1 上下文里用的典型报错模式

版本号背后是两套能力合同

同一块 canvas,getContext("webgl")getContext("webgl2") 返回的不是同一个状态机的两个皮肤。前者合同写的是 ES 2.0:着色器是 GLSL ES 1.00,没有顶点数组对象作为核心对象,没有多渲染目标作为标配,纹理以二维为主。后者合同写的是 ES 3.0:着色器升到 GLSL ES 3.00,VAO、MRT、三维纹理、整数纹理、Uniform Buffer、变换反馈进入核心。

把这事想成两种供电标准,比想成软件升级更准确。插座外形可以长得很像,电压和针脚数不同,插错会直接没电。很多教程让你“检测不到 2 就回退到 1”,听起来稳妥,真正稳妥的前提是:你的渲染路径在两条合同下都能走完。一条依赖 gl.drawArraysInstanced 和多个颜色附件的后处理链,回退到 1.0 不是降画质,是整条链断掉。

我更倾向项目启动时先写效果清单,再决定上下文,而不是先拿得到什么就写什么。产品静态展示、单光源、一张反照率贴图,WebGL1 足够,还多覆盖一批老设备。要做延迟着色、体积雾、GPU 粒子反馈,WebGL2 不是加分项,是入场券。

效果清单 上下文合同 --------- ----------- 单通道前向 webgl 即可 MRT 后处理 必须 webgl2 或一堆扩展 实例化草地 2.0 内置;1.0 看 ANGLE_instanced_arrays 整数索引纹理 2.0;1.0 基本没得谈

问题:WebGL2 覆盖不够时怎么办?

覆盖面是产品问题,不是渲染问题。内部工具、新机为主的可视化,可以直接锁 2.0。面向国内广泛设备的营销页,锁 1.0 或提供 2.0 增强档。真正难受的是“主路径按 2.0 写、回退档还想看起来差不多”——那是两套渲染器,工作量接近翻倍。不要用一句 fallback 把翻倍藏起来。

能力差:从对象清单对照,而不是从宣传对照

下面这张表只列会改变架构的差异。各家浏览器实现细节会漂,但合同边界稳定。

能力 WebGL1 WebGL2
着色器语言 GLSL ES 1.00,attribute 与 varying GLSL ES 3.00,in 与 out,必须声明版本
顶点数组对象 VAO 扩展 OES_vertex_array_object 核心,应默认使用
多渲染目标 MRT 扩展 WEBGL_draw_buffers 核心
实例化绘制 扩展 ANGLE_instanced_arrays 核心
三维纹理 / 二维数组纹理 无或极弱 核心
整数纹理与非归一化采样 基本无 核心
变换反馈 核心
统一缓冲 UBO 核心
深度纹理 扩展 WEBGL_depth_texture 核心
多采样渲染缓冲 有限 更完整
循环与纹理采样限制 更严,循环常要常量边界 宽松许多

读这张表时不要把它当“2.0 全能”。WebGL2 仍然是 ES 3.0,不是桌面 GL 4,也不是 WebGPU。没有计算着色器,没有真正的通用存储缓冲那套现代抽象。它相对 1.0 的跃进,主要是把一批事实标准的扩展收编进核心,让你少写扩展检测,多写稳定代码路径。

VAO 值得单独对照。WebGL1 里每次绘制都要重新绑定缓冲、重新 vertexAttribPointer,状态散落在全局。扩展 VAO 能把这组状态打包,但你得检测。WebGL2 里 VAO 是默认公民:创建、绑定、记录属性布局,绘制时切一个对象即可。我见过从 1 迁到 2 却仍每帧指指针的代码,等于把 2.0 当 1.0 用,性能红利从第一天就丢掉一半。

着色器语法也不是换皮。1.0 的 attribute / varying / gl_FragColor 在 2.0 里要改成 in / out 并写 #version 300 es。片元着色器必须自己声明输出。有人把 1.0 字符串塞进 2.0 程序,编译失败日志一长串,根因只是版本指令缺失。这条坑与“对象混用”并列,属于合同没读就开工。

#version 300 es precision mediump float; in vec2 vUv; out vec4 outColor; uniform sampler2D uTex; void main() { outColor = texture(uTex, vUv); }

对照 1.0:没有 version 行,varying 代替 intexture2D 代替 texture,输出写到 gl_FragColor。两边各写一套工具函数并不丢人,丢人的是用宏把两套语法搅在同一文件里直到无法测试。

工程上怎么选:覆盖面、效果、维护三条轴一起看

选型我用三问,而不是“浏览器支持百分比”这一问。

第一问:效果清单一旦拿掉 MRT、实例化、三维纹理,还剩什么?若后处理是产品卖点,锁 2.0。若只是旋转一下产品模型,1.0 更老实。

第二问:失败策略是“看不到 3D”还是“看到简化的 3D”?前者可以只申请 2.0,给静态图兜底。后者意味着两套着色器和两套资源,预算要单列。

第三问:团队有没有人维护扩展矩阵?WebGL1 靠扩展追 2.0 的部分能力,矩阵会随浏览器漂移。内部工具我几乎总锁 2.0,省掉扩展分支。

场景 建议上下文 理由 别做的事
电商 3D 看款 WebGL1 为主 覆盖面优先,效果浅 为了法线贴图强上 2.0 却无回退
科学体数据 WebGL2 三维纹理是刚需 用二维切片在 1.0 里硬模拟还当正式方案
自定义后处理链 WebGL2 MRT 与浮点缓冲更干净 用多次切换 FBO 冒充 MRT 还要求 60 帧
教学最小三角形 两个都写一遍 对照学习本身就是目标 只教 1.0 却在评论区承诺“和 2.0 一样”
游戏草地与粒子 WebGL2 实例化与变换反馈 用 CPU 更新一万个矩阵还怪 GPU

申请上下文的概念顺序应当是:先决定合同,再拿对象。

function createGL(canvas, prefer2) { if (prefer2) { const gl2 = canvas.getContext("webgl2"); if (gl2) return { gl: gl2, version: 2 }; } const gl1 = canvas.getContext("webgl"); if (gl1) return { gl: gl1, version: 1 }; return { gl: null, version: 0 }; }

这段代码本身没有立场。立场在 prefer2 从哪来:它应来自效果清单,而不是来自“机器上试了一下能拿到 2”。开发机几乎总能拿到 2,用户机不一定。用开发机的成功当产品策略,是 WebGL 项目里最常见的乐观病。

⚠️ 常见坑:在 WebGL1 上下文里调用 createVertexArray,或把 WebGL2 的纹理内部格式拿到 1.0 的 texImage2D。报错信息常只说 invalid enum,不会告诉你“你拿错了合同”。

💡 关键直觉:版本选择是架构决策。回退不是免费的兼容,而是第二条产品路径。写不起第二条,就别在宣传里承诺第一条会自动降级。

扩展补丁和 2.0 内置的细账放到第 4.5 节。这里只需记住一条:WebGL1 的扩展能补上深度纹理、VAO、实例化、部分浮点纹理,补不上变换反馈和完整的三维纹理工作流。补得上的,仍要面对“扩展名在各浏览器不一致、精度声明不一致”。能锁 2.0 就锁,是我给内部工具的默认建议。

问题:已经上线的 WebGL1 项目要不要迁 2.0?

看你是否在用扩展模拟 2.0。若已经依赖 VAO、实例化、深度纹理、draw buffers,迁移的本质是删检测、改着色器版本、改纹理格式枚举,收益是少测几家扩展。若代码仍是每帧 vertexAttribPointer、单渲染目标、CPU 粒子,迁 2.0 几乎不提速,应先改数据路径,再谈版本。版本是合同,不是性能开关。

迁移现场:同一条绘制路径怎么拆成两套合同

我做过一次内部工具从 1.0 迁 2.0,真正耗时的不是改 getContext 字符串,而是三处合同裂缝。第一处是着色器:一百多份 1.00 源码,attribute 与 texture2D 散落各处。我们没有写正则替换器,而是按“前向不透明、透明、后处理、调试色块”分成四套 300 es 模板,旧文件对照模板手工搬公式。两周看起来慢,之后再也没有“某台机器编译失败只因漏了 version 行”的夜班。第二处是纹理内部格式。1.0 写 RGBA 对 RGBA 能混过去的地方,2.0 要对齐内部格式、类型、是否 renderable。浮点中间缓冲在 1.0 靠扩展过滤,迁过去后有的机子不能线性过滤半浮点,Bloom 变成马赛克。我们把 HDR 档做成能力开关:过滤失败就退回 8 位加曝光曲线,而不是假设 2.0 等于浮点自由。第三处是 VAO。旧代码每 draw 指一次指针,迁完仍这么写,帧时间几乎没动。直到把布局收进 VAO,CPU 侧绑定时间才掉下来。版本是入场券,习惯才是票根。

对外产品更麻烦。若市场部要求“老设备也能看 3D”,你要承认那是第二条渲染器:可能关掉 MRT 后处理,用前向单通道;可能关掉实例化,草地改成几簇静态网格。工作量按“两个产品”估,不要按“加一个 else”估。我见过 else 里仍调用 drawBuffers,老设备直接上下文丢失。丢失之后默认 FBO 也不一定还在,用户看见白屏,日志在他们手机里。启动时把 version 与关键扩展打进一次遥测,比事后猜有用。

桌面开发机几乎总能拿到 2.0,这会让人产生“合同已经统一”的错觉。真机抽样至少覆盖:一台较新的安卓、一台较旧的安卓、一台 iOS、一台公司标配 Windows。抽样不是为了百分百覆盖,是为了打掉开发机偏见。若四台里有一台只能 1.0,而效果清单依赖变换反馈,清单就必须改,而不是让那一台“尽量试试”。

问题:能不能用 WEBGL_debug_renderer_info 按 GPU 名称分支?

能,但分支会变成无法测试的森林。优先按能力对象分支:有没有 createVertexArray、有没有浮点渲染、最大颜色附件数。GPU 名称当遥测可以,当控制流会在驱动改名后失效。能力查询是合同语言,型号字符串是八卦。

上下文属性、抗锯齿、以及“拿到了 2 却像 1”

getContext 的第二参数是另一张小合同:alpha、depth、stencil、antialias、preserveDrawingBuffer、powerPreference。深度缓冲默认通常有,但你可以关掉,关掉后 3.3 节的测试无附件可用。抗锯齿只作用于默认 FBO,用户 FBO 要另走多重采样。preserveDrawingBuffer 为 true 便于截图,可能让实现放弃某些优化。powerPreference 在有的浏览器能申请独显,失败则静默。这些属性在 1.0 与 2.0 都存在,但与版本能力正交。版本决定对象模型,属性决定默认屏的附件与功耗。两者都要在启动时一次选定,运行时改不了。选定后打印出来,避免测试机与生产机属性不一致还怪着色器。

“拿到了 2 却像 1”的清单:不用 VAO、不用实例化、不用 UBO、着色器仍按 1.00 思维写循环展开、纹理仍按 RGBA8 走 HDR。清单上全中,帧时间不会因版本下降。迁移验收应包含:至少一处 VAO、至少一处 300 es 的 in/out、至少一处 2.0 内部格式。没有行为变化的迁移,只是改了字符串,不能写进业绩。

要点速记

  • 合同不同:WebGL1 对齐 ES 2.0,WebGL2 对齐 ES 3.0,不是同一 API 的两个版本号。
  • 架构级差异:VAO、MRT、三维纹理、整数纹理、变换反馈、UBO 会改变你怎么拆帧,而不只是怎么调函数。
  • 着色器不兼容:1.00 与 300 es 的限定符、采样函数、输出方式都不同,不要用一份源码硬扛两套。
  • 回退有价:真正的 fallback 是第二条渲染路径,不是一句 getContext 失败就换字符串。
  • 内部工具锁 2:扩展矩阵的维护成本通常高于放弃一批老设备。
  • 迁移看依赖:已经靠扩展活着的项目,迁 2.0 是收编;仍停在 1.0 习惯上的项目,迁了也不快。

下一节对照固定管线记忆和可编程现实:浏览器里没有“打开灯光”这回事,着色器从第一天就是强制项。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U