1

背景

在使用WebGL绘制图形的时候,大多数情况下,绘制一个图形的时候,其各个图元都是相连的。 但是在一些情况下,我们需要绘制图元不相连的图形,如果绘制的模式是gl.TRAINGLES或者gl.LINES,也是可以达到的,但是如果绘制的模式是gl.TRAINGLE_STRIP,gl.TRAINGLE_FAN,gl.LINE_STRIP,gl.LINE_LOOP的时候,就没法在一次绘制下实现绘制多个不相连的图元了。 一般的做法就是,通过循环,多次绘制。比如如下代码:

for (var i = 0; i < num_objects; i++) {
    gl.drawArrays(gl.TRIANGLES,0,count);
}

我们知道,每次调用一次gl.drawArrays或者gl.drawElements方法都是一次很高的系统开销,如果调用方法的次数很多,会导致程序的性能降低。
在OPENGL中,一种解决方法是可以通过glMultiDrawElements方法来批量绘制多个图元。但是这个函数在WebGL中并不支持。而且使用这个函数,仍然需要将每一个分散的图形维护一组单独的顶点坐标/纹理坐标,这个是免不了的,这些数据仍然需要分开上传,还是会消耗一定的资源。
在WebGL2中,可以通过图元重启的特性来解决这个问题。

图元重启

前面说过,如果绘制模式是gl.TRAINGLE_STRIP,gl.TRAINGLE_FAN,gl.LINE_STRIP,gl.LINE_LOOP的时候
,绘制的所有点都是按照特定的顺序被连接在一起的,以形成复杂的图形,也就是说最终的图形一定是又多个相连的三角形或者线段组成,而不能是由分散的三角形或者线段组成。
如果要绘制分散的三角形或者线段,一种是前面所说的循环的方法;还有另外一种方式,就是图元重启(Primitive restart)。
图元重启可以绘制分散的三角形或者线段。所谓图元重启,就是当我们使用gl.drawElements方法绘制图形的时候,可以在索引数组里面指定特定的重启标志,当drawElements方法遇到重启标志的时候,就会从头开始重新绘制一个图元,比如下面的索引数组

var flag = primitiveRestartFlag;
var indices = [0,1,2,3,4,flag,5,6,7,8,9]

假设绘制的模式是gl.TRAINGLE_FAN,那么如果没有重启标志,点0和点1-9 会组成一个以点0位中心的扇形,现在加入了重启标志,那么点0会和点1-4组成一个以点0为中心的扇形;之后遇到了flag,此时图元重启,遇到这个值的时候,WebGL不会继续绘制图元,而是结束上一段绘制,然后重新启动新的绘制,也就是說用后面的索引所指定的顶点来从头绘制一个图形;会绘制一个以点5和点6-9组成的以点5位中心点的扇形。

启动图元重启功能

在OPENGL中,可以通过以下方法启动图元重启功能:

glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX);

而在WEBGL2中,图元重启功能默认是开启的,而且总是开启的,不能通过gl.enable和gl.disable方法来控制。
参考WebGL2 文档:https://www.khronos.org/regis...

图元重启标志

之前提到了图元重启是在遇到特定的标志才重启的,那么这个标志应该是多少了,一般而言gl.drawElements方法的索引值的类型可以是以下几种:

  • gl.UNSIGNED_BYTE
  • gl.UNSIGNED_SHORT
  • gl.UNSIGNED_INT

那么分别对应的重启的标志就是

  • 2^8 - 1
  • 2^16 - 1
  • 2^32 - 1

也就是說重启的标志的数值就是indices数组所能允许的最大值。这个值一般来说是不会被用到的,拿来当标志正好。

代码片段

下面的代码,在定义的indices数组中加入了图元重启标志:

/*https://www.khronos.org/registry/webgl/specs/latest/2.0/#5.18
 *WebGL 2.0 behaves as though PRIMITIVE_RESTART_FIXED_INDEX were always enabled. 
*/
var MAX_UNSIGNED_SHORT = 65535;
var num_vertices = 7;
 var indices = new Uint16Array([
       0, 1, 2, MAX_UNSIGNED_SHORT, 2, 3, 1
  ]);

netcy
204 声望120 粉丝

欢迎对canvas、webgl、图形学感兴趣的读者订阅专栏。