背景
在使用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
]);