WebGL的最佳实践

1,706 阅读4分钟

翻译自Mozila开发者文档-WebGL_best_practices

本文提供的建议和技巧可以用来提高的WebGL的表现。遵循这些建议可以使您的Web程序的兼容更多的设备和浏览器,并且提高性能。

需要避免的事情

  • 你应该确保程序运行中不产生任何WebGL报错(这些报错是通过getError()捕获的)。在Firefox的默认设置下,WebGL报错将在控制台中产生一个特殊的JavaScript警告。建议你打开webgl.verbose偏好设置。设置webgl.verbose将使每一个WebGL的错误,和其他一些WebGL的问题,合并为一个带有描述的JavaScript警告消息。你应该不想让一大波报错喷涌到控制台吧?当然。
  • 不应该在WebGL的着色器使用#ifdef GL_ES语句;尽管一些早期的例子中这样做了,不过这是没有必要的。
  • 除非你真的需要这么做,否则不要显式设置片段着色器的精度为highp。试着用mediump精度。使用highp精度的片段着色器可能导致你的程序在最新的手机硬件上不工作。从Firefox 11开始,webgl.getShaderPrecisionFormat()方法可以用来检查硬件对highp精度的支持,并且输出当前平台所有能支持的精度。

要记住的东西

  • 一些WebGL的功能依赖于客户端。在使用这些功能之前,你应该使用webgl.getParameter()函数来检查哪些功能是被支持的。例如,一个2D纹理能被支持的最大尺寸是由webgl.getParameter(webgl.max_texture_size)的值决定的。在Firefox 10中,可以设置名为webgl.min_capability_mode的偏好设置,允许用户模拟WebGL最小值环境,来测试程序可移植性。
  • 需要注意的是,你只能在webgl.getParameter(webgl.MAX_VERTEX_TEXTURE_IMAGE_UNITS)大于0的情况下在顶点着色器中使用纹理(译者:即使用顶点纹理拾取)。通常情况下,移动硬件并不支持此功能。
  • 大多数WebGL扩展是否可用取决于客户端。当你使用WebGL扩展的时候,尽量做到向下兼容。Firefox 10中的webgl.disable-extensions偏好设置,可以模拟所有扩展不可用的情况,借此来测试程序的可移植性。
  • 即使oes_texture_float扩展支持的情况下,在某些移动硬件上,渲染图像到浮点尺寸的纹理也可能失败。要检查是否真的可以,需要调用webgl.checkFramebufferStatus()方法。

一般性能的技巧

  • 任何需要同步CPU和GPU的操作都可能非常慢,所以如果可能的话,你应该尽量避免在主循环中这样做。这包括webgl中的操作:getError()readPixels(),和finish()。webgl中getter的调用如getParameter()getUniformLocation()也很缓慢,所以尽量在一个JavaScript变量中缓存获取结果。
  • 合并绘制操作将提高性能。如果你有1000个精灵需要绘制,试图只用一次drawarrays()drawelements()调用完成。你可以使用DRAW_TRIANGLE模式,来实现一次drawarrays()调用绘制很多不连续的对象。
  • 减少状态切换也将提高性能。比如,你可以将多个图像打包成一个单一的纹理,并通过设置恰当的纹理坐标来使用它们,这可以帮助做更少的纹理切换,从而提高性能。
  • 使用小纹理比使用大纹理性能更好。因此,mipmapping技术可以提高性能。
  • 简单比复杂的着色器执行。特别是,如果你从中删除一个if语句,这将使它运行速度更快。math函数如log()的开销也是相当大的。
  • 总是保证vertex attrib 0 array启用。如果你在vertex attrib 0 array没有启用的情况下进行绘制,桌面端运行OpenGL时,浏览器会做复杂的模拟(例如在Mac OSX上)。这是因为在桌面端的OpenGL,如果在vertex attrib 0 array没有启用的情况下是绘制不出任何东西的。你可以使用bindAttribLocation()强迫数组使用0号地址,并使用enableVertexAttribArray()启用数组。
  • 如果你使用drawElements()绘制,尽量保证耗尽整个元素数组缓冲区的资源。这是如果你在乎性能,不要只利用数组缓冲区的一部分:保持first=0并且count=数组缓冲区的大小。这样做的原因是,浏览器必须验证所有的索引都在范围内,因此,当你使用整个元素数组缓冲区时很容易,但缓存任意子数组将更难。
  • 出于同样的原因,调用bufferData()或者bufferSubData()更新数组缓冲区将导致下一次drawElements()调用要慢,因为缓存失效了。