WebGL Tips
WebGL notes
Khronos定制的WebGL specification,Chrome和Firefox在实现desktop的WEBGL时,开发了一个图形API库ANGLE。ANGLE提供了webgl的API接口,在非原生openGL-es的平台上,通过其他图像API对WGL的API进行模拟和映射。
由于js是动态脚本语言同时非强类型,在js脚本对API的调用较为随意,而图形API的原始API是基于C/C++的。所以ANGLE在gl方法调用时为了确保每次API调用都不会使得底层使用的图像API crash,做了大量的验证。这部分overhead相对于原生的directX和openGL来说是比较大的。所以在desktop中调用webgl会有更多driver层的overhead。
同时对于desktop的WebGL和mobile的webgl来说,由于ANGLE做了一部分保护,部分API的行为方式在不同平台根据实现的差异上会有不同。例如在desktop chrome和iOS webview中,gl.BindBuffer()
方法的buffer对象传入undefined
是不会报错的,而在Android的微信tencent X5浏览器中就会直接报错。
WebGL insight Tips
- WebGL Report 可以查看浏览器的WebGL支持情况,尤其是在调试浏览器或设备相关的问题。
- 避免在渲染loop中进行对象内存分配,尽可能重用对象和数组,尽量避免使用
map
和filter
等内置数据方法。每个新对象的内存分配会有一些GC相关的工作,在一些情况下GC会阻塞渲染循环造成几帧的卡顿。 - 在没有必要的情况下,不要使用alpha,depth,stencil,antialias以及将
preserveDrawingBuffer
设置为false,这样会节约一部分内存。同时需要时刻注意在使用完alpha,depth,antialias之后需要将context状态显示关闭。 - 获取attribute和uniform location只在初始化时进行。
- int precision 标志在vertex 和fragment shader中并不相同,使用int precision会导致不正确的渲染效果。
- 考虑兼容性,在varying和uniform属性是,vs和ps中保持相同的大小,同时在GLSL ES的规范中。考虑使用vec4替代float数组,vec4会有更紧密的内存排布.
- 非2次幂的贴图只支持linear或nearest filter以及clamp-to-border,clampmode 支持clamp-to-edge wrapping. Mipmap filtering 和 repeat wrapping不支持。
- 在
WEBGL_draw_buffers
extesnion下,当使用超过一个draw buffer时,如果不需要往某个draw buffer绘制,将gl.NONE 传递给draw buffer的参数留别。 - 始终使用”use strict” 声明编写代码,许多被隐藏的错误会被转换为运行时异常抛出。”use strict”使得在特定情况下浏览器可以对代码进行更好的优化。
- Code linters,如JSHint对保持JS代码整洁以及更少错误来说是一个非常有用的工具。
- 始终创建一个新的Texture而不是改变原先贴图的大小以及格式。
- 避免使用
gl.TRIANGLE_FAN
,gl.TRIANGLE_FAN
是在CPU端进行模拟的。 - 在合适的情况下使用
gl.STATIC_DRAW
标记1buffer,gl.STATIC_DRAW
使得浏览器和驱动层可以对静态数据的buffer有更多性能上的优化。 - 确保有一个
attribute
被绑定到gl.bindAttribLocation
的location 0
.否则在非openGL-ES得平台上会有较高的overhead,如MacOSX 以及desktop Linux. - 尽可能使用
transferable objects
在Web Workers直接传递数据。 typed arrays
有更高的性能相对于JS的array。- 在fragment shaders中使用
mediump
精度会有更好的设备兼容性,但还是有可能在缺少足够测试的情况下影响渲染结果。 - 使用
highp
精度会有更大的性能消耗但是渲染结果更加准确,在vertex shader中推荐使用highp
. - 使用
mediump
和lowp
precision进行设备shader的兼容性测试,在Chrome中可以使用emulate
precision使用软件来模拟测试。 - 使用RGB framebuffer时,始终实现RGBA格式的fallback在RGB framebuffer不支持的情况下。使用
gl.checkFramebufferStatus
获得更多信息。 - 使用vertex array objects(VAOs)和interleave static vertex data可以减少许多GL的API调用。考虑性能,尽量不要在每帧更新uniform,而只在uniform变化时更新。
- 如果在缩放浏览器的窗口大小会有大幅度的性能提升,可以考虑在鼠标拖动的互动时使用一半分辨率的framebuffer。
- 分散较慢的任务跨帧执行可以优化加载时间。
- 确保所有的WebGL方法调用全部都在
requestAnimationFrame
方法中执行。 - GLSL方法
textureProj
,如vec4 color = textureProj(sampler,uv.xyw)
可以使用vec4 color = texture(sampler,uv.xy/uv.w)
进行模拟。 - 避免使用Wavefront OBJ或COLLADA这类text-based 3D 数据格式进行资源分发。使用glTF或SRC这类优化过的Web资源格式作为替代方案。
- 使用OES_element_index_uint 在一次draw call中绘制indexed超过uint16.Max 65535的模型。
- 平滑的相机移动可以使用cosine-base插值对位置和旋转进行计算。相对于线性插值,余弦插值有更少的性能消耗以及更简单的计算公式。