支持 WebGL1 + WebGL2
Supporting WebGL1 + WebGL2
我有一个使用 WebGL1 渲染东西的库。
它大量使用浮动纹理和实例渲染。
现在似乎对 WebGL1 的支持很奇怪,有些设备支持例如 WebGL2,其中这些扩展是核心,但不支持 WebGL1,或者支持它但不支持扩展。
同时,对 WebGL2 的支持并不令人惊奇。也许有一天会是,但现在不是。
我开始研究如何支持这两个版本。
对于着色器,我认为我基本上可以摆脱 #define
ing 事情。例如,#define texture2D texture
和其他类似的东西。
当涉及到扩展时,问题就变得更多了,因为扩展对象不再存在。
作为实验,我尝试将扩展属性复制到上下文对象中,例如gl.drawArraysInstanced = (...args) => ext.drawArraysInstancedANGLE(...args)
.
在纹理方面,不需要改变太多,也许在WebGL1 运行ning时添加gl.RGBA8 = gl.RGBA
之类的东西,因此在[=41=时会"just work" ]在 WebGL2 中。
那么问题来了...有人试过吗?
我担心它会影响性能,尤其是函数调用的额外间接性。
如果假设它可以 运行 在 WebGL1 中,它也会让阅读代码变得不那么明显。毕竟,没有 WebGL1 上下文有 drawArraysInstanced
或 RGBA8
。这也会困扰 Typescript 打字和其他小事。
另一种选择是在整个代码中添加分支。两个版本的着色器(或 #ifdef
技巧),每个需要纹理格式的地方都有很多分支,每个地方都有实例化。
到处都是这样的东西是非常丑陋的:
if (version === 1) {
instancedArrays.vertexAttribDivisorANGLE(m0, 1);
instancedArrays.vertexAttribDivisorANGLE(m1, 1);
instancedArrays.vertexAttribDivisorANGLE(m2, 1);
instancedArrays.vertexAttribDivisorANGLE(m3, 1);
} else {
gl.vertexAttribDivisor(m0, 1);
gl.vertexAttribDivisor(m1, 1);
gl.vertexAttribDivisor(m2, 1);
gl.vertexAttribDivisor(m3, 1);
}
最后,也许还有第三种我没有想到的方法。
有什么建议吗?
不幸的是,我认为大多数答案将主要基于意见。
第一个问题是为什么两者都支持?如果你的想法在 WebGL1 上运行良好,那么就使用 WebGL1。如果您绝对必须拥有 WebGL2 功能,那么请使用 WebGL2 并意识到许多设备不支持 WebGL2。
如果您打算这样做 twgl tries to make it easier by providing a function that copies all the WebGL1 extensions into their WebGL2 API positions。对于像你提到的,而不是
ext = gl.getExtension('ANGLE_instanced_arrays');
ext.drawArraysInstancedANGLE(...)
你改为
twgl.addExtensionsToContext(gl);
gl.drawArraysInstanced(...);
我认为不会有任何明显的性能差异。特别是因为这些函数在一帧中只被调用几百次,所以包装不会成为您代码中的瓶颈。
重点并不是要同时支持 WebGL1 和 WebGL2。相反,它只是为了让您为两个 API 编写代码的方式相同。
不过,这 2 个 API 确实存在差异。例如,要在 WebGL1 中使用 FLOAT RGBA 纹理,您可以使用
gl.texImage2D(target, level, gl.RGBA, width, height, 0, gl.RGBA, gl.FLOAT, ...)
在 WebGL2 中是
gl.texImage2D(target, level, gl.RGBA32F, width, height, 0, gl.RGBA, gl.FLOAT, ...)
在这种情况下,如果您尝试调用与 WebGL1 相同的名称,WebGL2 将失败。 There are other differences 还有。
将在 WebGL1 和 WebGL2 中正常工作。该规范特别指出,该组合会在 WebGL2 上生成 RGBA8。
请注意,您需要 RGBA8 的示例并不正确。
gl.texImage2D(target, level, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, ...)
不过最大的不同是,如果可以使用 WebGL1,则没有理由使用 WebGL2。或者,反之亦然,如果您需要 WebGL2,那么您可能无法轻易退回到 WebGL1
例如,您提到使用着色器定义,但您打算如何处理 WebGL2 中 WebGL1 中没有的功能。 textureFetch
或 mod %
运算符或整数属性等功能......如果您需要这些功能,您主要需要编写仅 WebGL2 着色器。如果您不需要这些功能,那么一开始就没有必要使用 WebGL2。
当然,如果你真的想去做,如果用户有 WebGL2,你可能想制作一个更漂亮的渲染器,如果用户有 WebGL1,则回退到更简单的渲染器。
TD;LR IMO 选择一个或另一个
我在编写我的库的文档时发现了这个问题,它有很多目标,但其中之一就是这个——同时支持 WebGL1 和 WebGL2 以实现更高的跨设备兼容性。
https://xemantic.github.io/shader-web-background/
例如,我发现 BrowserStack 三星手机在 WebGL1 中不支持渲染浮点纹理,而在 WebGL2 中则完全没问题。同时,WebGL2 永远不会出现在 Apple 设备上,但对半浮点纹理的渲染得到了很好的支持。
我的库不提供完整的 WebGL 抽象,而是为片段着色器配置管道。以下是 GitHub 上的源代码,其中包含取决于版本的 WebGL 策略代码:
https://github.com/xemantic/shader-web-background/blob/main/src/main/js/webgl-utils.js
因此,要回答您的问题,它是可行且可取的,但是对于每个 WebGL 功能而言,以完全通用的方式进行操作可能非常具有挑战性。我想第一个要问的问题是“共同点是什么?”在支持的扩展方面。
我有一个使用 WebGL1 渲染东西的库。 它大量使用浮动纹理和实例渲染。
现在似乎对 WebGL1 的支持很奇怪,有些设备支持例如 WebGL2,其中这些扩展是核心,但不支持 WebGL1,或者支持它但不支持扩展。
同时,对 WebGL2 的支持并不令人惊奇。也许有一天会是,但现在不是。
我开始研究如何支持这两个版本。
对于着色器,我认为我基本上可以摆脱 #define
ing 事情。例如,#define texture2D texture
和其他类似的东西。
当涉及到扩展时,问题就变得更多了,因为扩展对象不再存在。
作为实验,我尝试将扩展属性复制到上下文对象中,例如gl.drawArraysInstanced = (...args) => ext.drawArraysInstancedANGLE(...args)
.
在纹理方面,不需要改变太多,也许在WebGL1 运行ning时添加gl.RGBA8 = gl.RGBA
之类的东西,因此在[=41=时会"just work" ]在 WebGL2 中。
那么问题来了...有人试过吗?
我担心它会影响性能,尤其是函数调用的额外间接性。
如果假设它可以 运行 在 WebGL1 中,它也会让阅读代码变得不那么明显。毕竟,没有 WebGL1 上下文有 drawArraysInstanced
或 RGBA8
。这也会困扰 Typescript 打字和其他小事。
另一种选择是在整个代码中添加分支。两个版本的着色器(或 #ifdef
技巧),每个需要纹理格式的地方都有很多分支,每个地方都有实例化。
到处都是这样的东西是非常丑陋的:
if (version === 1) {
instancedArrays.vertexAttribDivisorANGLE(m0, 1);
instancedArrays.vertexAttribDivisorANGLE(m1, 1);
instancedArrays.vertexAttribDivisorANGLE(m2, 1);
instancedArrays.vertexAttribDivisorANGLE(m3, 1);
} else {
gl.vertexAttribDivisor(m0, 1);
gl.vertexAttribDivisor(m1, 1);
gl.vertexAttribDivisor(m2, 1);
gl.vertexAttribDivisor(m3, 1);
}
最后,也许还有第三种我没有想到的方法。
有什么建议吗?
不幸的是,我认为大多数答案将主要基于意见。
第一个问题是为什么两者都支持?如果你的想法在 WebGL1 上运行良好,那么就使用 WebGL1。如果您绝对必须拥有 WebGL2 功能,那么请使用 WebGL2 并意识到许多设备不支持 WebGL2。
如果您打算这样做 twgl tries to make it easier by providing a function that copies all the WebGL1 extensions into their WebGL2 API positions。对于像你提到的,而不是
ext = gl.getExtension('ANGLE_instanced_arrays');
ext.drawArraysInstancedANGLE(...)
你改为
twgl.addExtensionsToContext(gl);
gl.drawArraysInstanced(...);
我认为不会有任何明显的性能差异。特别是因为这些函数在一帧中只被调用几百次,所以包装不会成为您代码中的瓶颈。
重点并不是要同时支持 WebGL1 和 WebGL2。相反,它只是为了让您为两个 API 编写代码的方式相同。
不过,这 2 个 API 确实存在差异。例如,要在 WebGL1 中使用 FLOAT RGBA 纹理,您可以使用
gl.texImage2D(target, level, gl.RGBA, width, height, 0, gl.RGBA, gl.FLOAT, ...)
在 WebGL2 中是
gl.texImage2D(target, level, gl.RGBA32F, width, height, 0, gl.RGBA, gl.FLOAT, ...)
在这种情况下,如果您尝试调用与 WebGL1 相同的名称,WebGL2 将失败。 There are other differences 还有。
将在 WebGL1 和 WebGL2 中正常工作。该规范特别指出,该组合会在 WebGL2 上生成 RGBA8。
请注意,您需要 RGBA8 的示例并不正确。
gl.texImage2D(target, level, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, ...)
不过最大的不同是,如果可以使用 WebGL1,则没有理由使用 WebGL2。或者,反之亦然,如果您需要 WebGL2,那么您可能无法轻易退回到 WebGL1
例如,您提到使用着色器定义,但您打算如何处理 WebGL2 中 WebGL1 中没有的功能。 textureFetch
或 mod %
运算符或整数属性等功能......如果您需要这些功能,您主要需要编写仅 WebGL2 着色器。如果您不需要这些功能,那么一开始就没有必要使用 WebGL2。
当然,如果你真的想去做,如果用户有 WebGL2,你可能想制作一个更漂亮的渲染器,如果用户有 WebGL1,则回退到更简单的渲染器。
TD;LR IMO 选择一个或另一个
我在编写我的库的文档时发现了这个问题,它有很多目标,但其中之一就是这个——同时支持 WebGL1 和 WebGL2 以实现更高的跨设备兼容性。
https://xemantic.github.io/shader-web-background/
例如,我发现 BrowserStack 三星手机在 WebGL1 中不支持渲染浮点纹理,而在 WebGL2 中则完全没问题。同时,WebGL2 永远不会出现在 Apple 设备上,但对半浮点纹理的渲染得到了很好的支持。
我的库不提供完整的 WebGL 抽象,而是为片段着色器配置管道。以下是 GitHub 上的源代码,其中包含取决于版本的 WebGL 策略代码:
https://github.com/xemantic/shader-web-background/blob/main/src/main/js/webgl-utils.js
因此,要回答您的问题,它是可行且可取的,但是对于每个 WebGL 功能而言,以完全通用的方式进行操作可能非常具有挑战性。我想第一个要问的问题是“共同点是什么?”在支持的扩展方面。