深入理解three.js对svg的支持(二):SVGRenderer

来源:互联网 发布:内存卡数据恢复软件 编辑:程序博客网 时间:2024/06/07 00:13

前言:SVG作为一种优秀的矢量图形格式在Web得到广泛应用,three.js作为知名的WebGL库自然也对其提供了支持。然而,官方文档中对此的说明十分单薄,网上与此相关的资源也不多。经过多次试验之后,在此分享我的一点理解,包括SVGLoader,SVGObject,SVGRenderer,svg和THREE对象的互相转化等内容。

2 SVGRenderer

上文说到了SVGRenderer的机制,那么这一节就来看看源码。源码不长,只有500多行。

2.1 THREE对象转svg

SVGRenderer代码的核心在this.render方法上。渲染的数据从投影得来:

_projector = new THREE.Projector(),..._renderData = _projector.projectScene( scene, camera, this.sortObjects, this.sortElements );_elements = _renderData.elements;_lights = _renderData.lights;

至于Projector.js就复杂多了,单单projectScene方法代码的行数就和整个SVGRenderer.js一样多,不再深究,有兴趣的自行研读。

_renderData包含了元素和光照信息。撇开光照不谈,数组_elements是核心数据,对其进行遍历渲染。这里将元素分为了3种类型,是根据Projector.js制定,但不完全对应。3种类型分别是THREE.RenderableSpriteTHREE.RenderableLineTHREE.RenderableFace,每个类型的判断中分别调用各自的render函数,如renderLine等,各自的render函数大同小异,核心流程和函数定义的伪代码如下:

//遍历元素将数据放入_svgfor e in elements:    if e是3种的某一种:        处理数据        调用自己的render方法flushPath()//将最后一条路径数据存入_svg,因为addPath机制的原因,最后一条路径不会调用flushPath,因此_currentPath中的数据不会保存到_svg中,得手动来一下//函数定义3render:    将数据按照svg path格式编码到path、处理style;    addPath(style,path);addPath:    if style == _currentStyle:        path放入_currentPath;    else:        flushPath();        清空_currentPath和_currentStyle;flushPath():    _currentPath和_currentStyle数据放入_svg;    清空_currentPath和_currentStyle;

注意到这里有一个_svg对象,它在一开始就被定义为DOM元素,所有的内容都在此对象下显示,其构建完成之后就是一个完整的svg图像。

SVGRenderer的功能还是很强大的,可以将视口中的三维物体转为二维svg显示(统一使用<path>编码)。如官方的例子就是将一组动态的THREE.Line转为了svg的path在SVGRenderer中显示。

2.2 SVGObject

等等,前面的这些都是三维对象转svg的例子,那么SVGObject是怎样在SVGRenderer中显示的呢?

其实SVGRenderer.js的源码首先就给出了SVGObject的定义:

THREE.SVGObject = function ( node ) {    THREE.Object3D.call( this );    this.node = node;};THREE.SVGObject.prototype = Object.create( THREE.Object3D.prototype );THREE.SVGObject.prototype.constructor = THREE.SVGObject;

可见SVGObject确实是继承自Object3D,但是注意构造函数,有个传入参数node,并且还动态扩展了一个属性this.node,这个就是SVG文件的根节点啊,有种不好的预感……

这里写图片描述

先不管,看它是如何渲染的呢?render的最后有这么一段代码:

scene.traverseVisible( function ( object ) {    if ( object instanceof THREE.SVGObject ) {        _vector3.setFromMatrixPosition( object.matrixWorld );        _vector3.applyMatrix4( _viewProjectionMatrix );        var x =   _vector3.x * _svgWidthHalf;        var y = - _vector3.y * _svgHeightHalf;        var node = object.node;        node.setAttribute( 'transform', 'translate(' + x + ',' + y + ')' );        _svg.appendChild( node );        }    } );

原来渲染流程的最后会通过遍历查找scene中的SVGObject对象,单独处理SVGObject!对,2.1小节部分的render代码你怎么改都和SVGObject的显示没关系……,读取其node——合着是把我们的svg图像放到了_svg里,再放到DOM里而已,纯粹是多套了一层啊——坑爹呢是!

这里写图片描述

怪不得不能用变换,因为数据都在node属性里,又没有提供任何操作node的方法,你变transform和这个扩展出来的node没有任何关系。想变的话。。。改源代码吧——但又回到svg蛋疼的变换上去了。(我想静静)

下篇中,将对svg于THREE对象的关系进行进一步的探索。

原创粉丝点击