编程开源技术交流,分享技术与知识

网站首页 > 开源技术 正文

Web3D|基于WebGL的Three.js框架|SVG拉伸篇

wxchong 2024-07-19 05:40:41 开源技术 10 ℃ 0 评论

通过Web3D|基于WebGL的Three.js框架|入门篇Web3D|基于WebGL的Three.js框架|Mesh基础篇等一系列的文章,我们已经了解了用Three.js开发3D的基础知识,那么你有没有想过

怎么将一张SVG的矢量图拉伸,显示成3D效果呢?

探索

还记得Web3D|基于WebGL的Three.js框架|Mesh基础篇中最后提到的ExtrudeGeometryExtrudeBufferGeometry吗?没错,我们就是需要用它。

知道了核心点之后,我们需要理清楚,怎么将SVG中的元素,一步一步的转成Three中的元素。

  • 首先,SVG是一个标准的XML文件,所以我们可以很容易地解析其中的元素。
  • 其次,我们需要用到Three中专门用来定制2D图形的一些元素,ShapePath和Shape。也就是说,我们需要将SVG的元素,转成Three中的ShapePath,然后再切成Shape。
  • 最后,我们可以通过ExtrudeGeometryExtrudeBufferGeometry将我们的2D图形拉伸成3D图形。

具体步骤

一,加载SVG文件,并转成ShapePath

加载SVG并转成ShapePath,可以用到THREE.SVGLoader来实现:

var loader = new THREE.SVGLoader();
loader.load(
 SVG_URL, //SVG文件的URL
 function (shapePaths) {
 // 处理加载的ShapePath,转Shape,创建Mesh等。
 },
 // 加载进度
 function (xhr) {
 console.log((xhr.loaded / xhr.total * 100) + '% loaded');
 },
 // 异常处理
 function (error) {
 console.log('An error happened' + error);
 }
);

SVGLoader做了哪些事情:

  • 遍历SVG DOM。
  • 识别的SVG标签:path,rect,polygon,polyline,circle,ellipse和line。 SVG标签加载限制:首先会从各个节点中读取fill值,只有fill值不为空或不透明才会加载。
function isVisible( style ) {
 return style.fill !== 'none' && style.fill !== 'transparent';
}
  • 只有这些标签可识别,并且可见,SVGLoader才会创建完全对应的ShapePath。

重要的一点:由于这个SVGLoader只能读取标准的SVG文件。 通常情况下,在Adobe Illustrator中创建的SVG文件不能直接被加载,需要做一下转换。File -> Export -> Export As… -> 在打开的对话框中选择SVG格式 -> Export。

二,创建ExtrudeBufferGeometry

通过THREE.SVGLoader加载SVG格式的图片,会自动将一些标准格式的SVG图形转成ShapePath。接下来就要将这些ShapePath转成Three的Shape然后加到Scene中显示。

1. ShapePath转Shape

var shapes = path.toShapes(true, false);

2. 拉伸Shape,创建ExtrudeBufferGeometry

var bufferGeometry = new THREE.ExtrudeBufferGeometry(shapes, {depth: depth});

注意这里的参数depth就是你要拉伸的值,也就是Z轴方向的高度。

3. 通过ShapePath中加载的SVG图形的颜色创建Material

var material = new THREE.MeshBasicMaterial({color: color});

简单情况下,我们可以创建一个基础的Material去加载SVG中的颜色属性。

4. 创建Mesh加入Scene中显示

var mesh = new THREE.Mesh(geometry, material);

三,一些小技巧以及特殊处理

1. 文本处理

文本处理在3D中特别麻烦,尤其是中文的处理。虽然在设计SVG的时候可以将文本转成Path,这些在浏览器中显示不会有什么问题。但是在Three中,万一一个文本所对应的path生成的Shape中,有一些的depth(拉伸)值设置的不一样,文本就会显示成很诡异的样子。 所以可以建议将所有的文本值过滤掉。参考使用Sprite去实现。

2. 拉伸高度

SVG中有些图会默认先在底层创建一个rect,然后将所有的图再画在这个rect上。就像我们的室内地图的地板一样。 在做拉伸的时候,必须要用很小的depth值去渲染这些大面积的图形,或者直接过滤掉。

3. Shape合并处理

  • 一个ShapePath通常可以生成多个Shape,我们可以遍历这个Shape数组,将Shape一个一个创建ExtrudeBufferGeometry和Mesh加入到Scene中,也可以将这些所有的Shape作为一个整体去创建ExtrudeBufferGeometry和Mesh加入到Scene中。当然了,创建的Mesh越多,WebGL渲染的性能就越差。为了提高性能,通常将一个ShapePath对应的所有的Shape创建成一个ExtrudeBufferGeometry去实现。
  • 通过Geometry类可以取到BBox,进而取得center和size值。

4. 中心点设置

  • Scene加入的所有图形,其位置(positions)默认都是在(0,0,0)点,也就是Renderer的中心。
  • 我们通常会使用地板(或SVG中最底层能包含所有图形的那个图形)来确定移动的多少。详细请参考下一小节。

5. 和原始的SVG图片方向保持一致。

由于SVG中的坐标点事基于计算机坐标系统(自上而下,从左往右)的原则,而3D中(Three也是一样)使用的是前面提到过的右手坐标系统,由此可见,X轴基本一致,但Y轴是完全相反的。所以首先要做的,就是把X轴旋转180°。

将X轴旋转180°,使得Y坐标的值能够保持一致。

mesh.rotation.x = Math.PI;

由于我们将X轴旋转了180°,导致Z坐标的值反了过去,Z坐标的值,也就是我们设定的拉伸值(depth),所以我们需要将Z坐标的值向相反的方向拉回来。

mesh.translateZ(-depth - 1);

移动X坐标的值使得整个SVG渲染图到原点。

mesh.translateX(offsetX);	

移动Y坐标的值使得整个SVG渲染图到原点。

mesh.translateY(offsetY);

计算X轴和Y轴的偏移量。 X轴和Y轴的偏移量,可以通过原始SVG图片的ViewBox来确定,但这种方式作出来不太精确。我暂时使用的方法是遍历所有的ShapePath(这些ShapePath是SVGLoader中加载得来的)中的Shape,计算出一个最大的能包含所有图形的矩形区域,然后再进行X轴和Y轴的位移。

6. 使用Group来批量设置

	var box = new THREE.Box3();
	box.setFromObject(group);
	var center = box.getCenter();
	for(var i=0;i<group.children.length;i++) {
		group.children[i].rotation.z = Math.PI;
		group.children[i].rotation.y = Math.PI;
		group.children[i].position.x = -center.x;
		group.children[i].position.y = center.y;
	}

因为所有的模型都加到了一个Group中,我们可以通过Group的中心点来设置每一个模型的旋转和位移,但这段代码还不太完善。需要将所有图形的depth通过Z轴拉回来。

7. 通过Sprite来添加浮标或装饰

var iconSprite = new THREE.Sprite(new THREE.SpriteMaterial({map: new THREE.TextureLoader().load(path)}));
iconSprite.position.copy(intersected.point);
iconSprite.positionZ = iconSprite.positionZ + 150;
iconSprite.scale.x = iconSprite.scale.y = 20;//100;
scene.add(iconSprite);
  • Path是用来在Sprite上显示的图片的路径。
  • 位置设置中interescted是通过raycaster找到的鼠标点击时的模型。
  • 其它的设置和Geometry Mesh的设置一样。

总结

  1. SVG虽说是标准的XML,但也有很多自定义的格式在里面,如果有解析不到的元素,或需要扩展属性,我们需要去重写SVGLoader实现。
  2. SVG中可能会有一些元素通过图片是看不到的,或是重叠的,我们在加载的时候,最好是能够过滤掉一些没有必要的东西。
  3. SVG在设计的时候,中文为了能够正确的显示字体,一般都会转成path,这在通过Three拉伸的时候效果会很差,建议过滤掉,然后用Sprite来显示。
  4. 具体开发中,一定要注意SVG的坐标和Three的坐标转换,更多的信息可以参考我的系列文章:Web3D|基于WebGL的Three.js框架|坐标转换篇

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表