GLantern 的绘制方式更新了!获得了技能:抗锯齿(一般绘制)、绘制效率提高、抗溢出变换。Bulletproof 作为直接继承项目,也得到了特性的更新。
另外,GLantern 的 live preview 也上线了(呃,之前忘了)。请戳:Bulletproof /GLantern 。
首先说一下原来是怎么画的:
DisplayObject .prototype .render = function (renderer, output ) { this .__render (renderer, this ._rawTarget ); this .__processFilters (renderer, this ._rawTarget , this ._bufferedTarget ); RenderHelper .copyTargetContent (renderer, this ._bufferedTarget , output); };DisplayObjectContainer .prototype .render = function (renderer, output ) { super .render (renderer, this ._containerTarget ); for (var i = 0 ; i < this ._children .length ; ++i) { this ._children [i].render (renderer, this ._containerTarget ); } RenderHelper .copyTargetContent (renderer, this ._containerTarget , output); };
这导致了什么问题呢?
大量借助 WebGLFramebuffer
的拷贝(通过 shader 拷贝),每个元素绘制都会有1到2次拷贝操作。
WebGLFramebuffer
是没有抗锯齿的,所以所有的图元绘制都带有锯齿。
一个潜在的问题:假设舞台大小为 100×100,元素A大小为 50×50,位于位置 (-200, -200) 处,其逻辑父B位于位置 (200, 200) 处,则A就画不出来了。
对于问题3的稍详细的解释:因为第一次变换的时候边界 (-200,-200,-150,-150) 就已经超出了视场 (0,0,100,100) 的范围,所以做 visiblility test 的时候会失败而不会绘制;但是,稍微计算一下的话就会发现A的实际位置在 (0,0,50,50),应该被画出来。这是一个刁钻的情况,但是可以发生。
现在的绘制流程就简单多了,直接向屏幕(相当于 gl.bindBuffer(gl.FRAMEBUFFER, null)
)绘制。对原先的 render()
方法,干脆去掉了 outputTarget
这个冗余的参数,因为 WebGLRenderer
已经有了一个公开属性 currentTarget
,同时出现两套同样意义的字段是不合适的。抽象调用:
render (renderer :WebGLRenderer ):void { if (this .visible && this .alpha > 0 ) { this .__preprocess (renderer); this .__render (renderer); this .__postprocess (renderer); } }
具体实现(这里取进行了最底层绘制操作的 SolidStrokeRenderer
为例子):
render (renderer :WebGLRenderer ):void { if (this ._vertices .length > 0 ) { var target = renderer.currentRenderTarget ; RenderHelper .renderPrimitives2 (renderer, target, this ._vertexBuffer , this ._colorBuffer , this ._indexBuffer , false , target.isRoot , false ); } }
虽然在 WebGL 1.0 中,FBO(frame buffer object)不支持抗锯齿,但是屏幕渲染器是支持的。对于直接绘制到屏幕上的元素(例如不带滤镜的 Shape
)就可以用上抗锯齿功能了。同时,拷贝操作的大量减少提高了绘制效率,现在小圆脸即使不缓存 Graphics
结果也可以稳定在60帧了(测试基准:NW.js v0.12.3 stable)。
请注意,这里为了能直接将最终位置反映到屏幕上,新建了一个 shader:Primitive2Shader
。在片元部分和 PrimitiveShader
是一样的,简单颜色赋值而已。但是在顶点处理上就复杂一点了:
precision mediump float ;attribute vec3 aVertexPosition;attribute vec4 aVertexColor;uniform mat4 uProjectionMatrix;uniform mat4 uTransformMatrix;uniform vec2 uOriginalSize;uniform vec2 uFitSize;uniform bool uFlipX;uniform bool uFlipY;varying vec4 vVertexColor;void main() { vec3 newVertexPostion = aVertexPosition; if (uFlipX) { newVertexPostion.x = uOriginalSize.x - newVertexPostion.x; } if (uFlipY) { newVertexPostion.y = uOriginalSize.y - newVertexPostion.y; } gl_Position = uProjectionMatrix * uTransformMatrix * vec4 (newVertexPostion.xyz, 1.0 ); vVertexColor = aVertexColor; }
第一步是要支持 uTransformMatrix
的计算,这个计算由 DisplayObject.__updateTransform()
完成,在必要的时候触发计算。最终的值会被传入 shader。
第二步就是其他处理。有没有想过,为什么会出现 X 轴和 Y 轴的翻转?答案是,OpenGL(数学)坐标系和 Flash(GDI)坐标系关于 Y 轴是相反的。为了正确地一次画出图形,需要在 shader 中立即执行 Y 轴翻转。而翻转的轴是 (0,H)(H
是图元高度),因此还需要一个额外参数,图元大小(uOriginalSize
),指示翻转轴位置。
解决了这些之后,就是代码兼容性调整了,耐心+细心将编译错误和运行错误解决了,三个测试样例通过,收工。