第六章 高级几何体
一、ConvexGeometry:凸面体
在一组点外面建立一个凸包。
function generatePoints(){
var points = [];
for(var i = 0; i<20;i++){
var randomX = -15 + Math.round(Math.random() * 30);
var randomY = -15 + Math.round(Math.random() * 30);
var randomZ = -15 + Math.round(Math.random() * 30);
points.push(new THREE.Vector3(randomX,randomY,randomZ));
}
spGroup = new THREE.Object3D();
var material = new THREE.MeshBasicMaterial({color:0xff0000,transparent:false});
points.forEach(function(point){
var spGeom = new THREE.SphereGeometry(0.2);
var spMesh = new THREE.Mesh(spGeom,material);
spMesh.position = point;
spGroup.add(spMesh);
});
scene.add(spGroup);
}
接下来从这些点中创建一个凸包:
var convecGeometry = new THREE.ConvexGeometry(poitns);
convexMesh = createMesh(convecGeometry);
scene.add(convexMesh);
二、LatheGeometry:扫描体
从一条光滑的曲线开始创建图形。该曲线通过指定一些点(节点)来指定,该曲线通常被称为样条曲线。
该曲线绕一个固定的点旋转,就形成一个类似花瓶或铃铛的图形。
function generatePoints(segments,phiStart,phiLength){
var points = [];
var height = 5;
var count = 30;
for(var i = 0 ; i < count ; i++){
points.push(new THREE.Vector3((Math.sin(i*0.2)+Math.cos(i*0.3))*height+12,0,(i-count)+count/2));
}
...
var latheGeometry = new THREE.LatheGeometry(points,segments,phiStart,phiLength);
latheMesh = createMesh(latheGeometry);
scene.add(latheMesh);
}
参数:
points:构成样条曲线的点集。
segments:指定创建图形时所用的分段数。默认12
phiStart:创建图形时从圆的何处开始。取值0-2*PI。默认0.
phiLength:指定创建的图形有多完整。默认2*PI。
三、通过拉伸创建几何体
<1> ExtrudeGeometry
从一个二维图形创建一个三维图形,沿z轴拉伸。
var options = {
amount:10,
bevelThickness:2,
bevelSize:1,
bevelSegments:3,
bevelEnabled:true,
curveSegments:12,
steps:1
};
shape = createMesh(new THREE.ExtrudeGeometry(drawShape(),options));
参数:
amount:指定图形可以拉多高。
bevelThickness:指定斜角的深度。斜角指前后面和拉伸体之间的倒角。默认是6。
bevelSize:斜角的高度。默认是bevelThickness - 2
bevelSegments:斜角的分段数。默认是3.
bevelEnabled:为true,就会有斜角,默认为true。
curveSegments:指定拉伸图形时曲线分成多少段。默认为12.
steps:定义拉伸体被分成多少段。默认为1.
extrudePath:指定图形沿着什么路径拉伸,若没有指定,则沿着z轴拉伸。
material:定义前后面所用材质的索引。用函数THREE.SceneUtils.createMultiMaterialObject创建网格。
extrudeMaterial:指定斜角和拉伸体所使用的材质。用函数THREE.SceneUtils.createMultiMaterialObject创建网格。
<2> TubeGeometry
沿着一条三维样条曲线拉伸出一根管子。可以通过定义顶点来定义路径,然后使用TubeGeometry创建这根管子。
var points = [];
for(var i = 0;ivar randomX = -20 + Math.round(Math.random() * 50);
var randomY = -15 + Math.round(Math.random() * 40);
var randomZ = -20 + Math.round(Math.random() * 40);
points.push(new THREE.Vector3(randomX,randomY,randomZ));
}
var tubeGeometry = new THREE.TubeGeometry(
new THREE.SplineCurve3(points),
segments,radius,radiusSegments,closed
);
var tubeMesh = createMesh(tubeGeometry);
scene.add(tubeMesh);
我们先定义一些THREE.Vector3顶点,然后将这些点转换为THREE.SplineCurve3类,即用这些点定义一条光滑的曲线(样条曲线)。
参数:
path:必须。该属性用一个THREE.SplineCurve3对象来指定管道应遵循的路径。
segments:指定构造这个管道所用的分段数。默认是64.
radius:管道的半径。默认为1。
radiusSegments:指定管道圆周的分段数。默认为8.
closed:设置为true,管道首尾连接。默认为false。
debug:设置为true,额外的调试信息会添加到管道上。
<3>从SVG拉伸
SVG:可缩放矢量图。基于xml的标准,用来在网页上创建二维矢量图。开放标准,大部分现代浏览器都支持。但是直接使用SVG,通过js操作它并不直观。有几个开源js库使得使用SVG简单很多。
其中D3.js和Raphael.js就是其中最好的两个。
d3-threeD.js 是同D3.js一同开发的,但是作了些调整,使其可以单独使用其中部分功能。
function drawShape(){
//SVG元素中的属性d包含的就是用来绘制图形的路径表达式。
var svgString = $('batman-path').attr('d');
var shape = transformSVGPathExposed(svgString);//将SVG字符串转换为three.js可以识别的格式
return shape;
}
var options = {
amount:10,
bevelThickness:2,
bevelSize:1,
bevelSegments:3,
bevelEnabled:true,
curveSegments:12,
steps:1
};
shape = createMesh(new THREE.ExtrudeGeometry(drawShape(),options));
<4>ParametricGeometry
可以创建基于等式的几何体。
在three.js发布包中,example/js/ParametricGeometries.js。其中定义了几个公式的例子,并在ParametricGeometry中使用它们。
//创建平面的函数
function plane (u,v){
var x = u * width;
var y = 0;
var z = v * depth;
return new THREE.Vector3(x,y,z);
}
ParametricGeometry会调用这个函数。
u、v的取值范围是0-1,而且针对0-1之间的所有值该函数还会被调用很多次。
当这个函数被调用的时候就会得到一个宽为width、深度为depth的基础平面。
下面是创建一个波浪曲面的例子:
radialware = function(u,v){
var r = 50;
var x = Math.sin(u) * r;
var z = Math.sin(v/2) * 2 * r;
var y = (Math.sin(u * 4 * Math.PI) + Math.cos(v * 2 * Math.PI)) * 2.8;
return new THREE.Vector3(x,y,z);
}
var mesh = createMesh(new THREE.ParametricGeometry(radialware,120,120,false));
传递给ParametricGeometry对象的参数:
function:必须。该参数是一个函数,以u、v为参数,返回值是一个Vector3对象,作为图形上的点坐标。
slices:必须。定义u值应该分成多少份。
stacks:必须。定义v值应该分成多少份。
useTriks:默认为false。如果设置为true,则该几何体创建时会将使用三角面片。false则表示使用四边形。
u、v会作为参数传递给function指向的函数,该函数的返回值是一个Vector3对象。那么该函数会被调用多少次呢?其实是由slices和stacks参数决定的。
若:
slices = 5
stacks = 4
则在调用function函数时,使用的参数如下:
u : 0/5 v: 0/4
u : 1/5 v: 0/4
u : 2/5 v: 0/4
…
u : 5/5 v: 0/4
u : 0/5 v: 1/4
u : 1/5 v: 1/4
…
这两个值越大,则生成的向量就越多,创建出来的图形就越光滑。
ParametricGeometry.js
该文件包含的函数可以创建如下图形:
克莱因瓶
平面
二维莫比乌斯带
三维莫比乌斯带
管道
环面扭结
球体
创建三维文本
var options = {
size:90,
height:90,
weight:'normal',
font:'helvetiker',
style:'normal',
bevelThickness:2,
bevelSize:4,
bevelSegments:3,
bevelEnabled:true,
curveSegments:12,
steps:1
};
text1 = createMesh(new THREE.TextGeometry('Learning',options));
text1.position.z = -100;
text1.position.y = 100;
scene.add(text1);
text2 = createMesh(new THREE.TextGeometry('Three.js',options));
scene.add(text2);
TextGeometry的基本属性:
size:文本大小。默认100.
height:指定拉伸的长度。默认50
weight:指定字体的权重。默认normal。可选值有 normal 和 bold。
font:字体名。默认helvetiker
style:指定字体的样式。默认normal。可选值有 normal 和 italic。
bevelThickness:指定斜角的深度。默认10.
bevelSize:斜角的高度。默认是8
bevelSegments:斜角的分段数。默认是3
bevelEnabled:为true,就会有斜角,默认为false。
curveSegments:指定拉伸图形时曲线分成多少段。默认为4
steps:定义拉伸体被分成多少段。默认为1
extrudePath:指定图形沿着什么路径拉伸,若没有指定,则沿着z轴拉伸。
material:定义前后面所用材质的索引。用函数THREE.SceneUtils.createMultiMaterialObject创建网格。
extrudeMaterial:指定斜角和拉伸体所使用的材质索引。用函数THREE.SceneUtils.createMultiMaterialObject创建网格。
注意:要想使用three.js中内置的字体,需要将字体加载到页面中。
如果你想渲染一组二维字体,例如用作材质的纹理。那么你不应该使用TextGeometry,而应该使用HTML5画布。
context.font:设置字体
context.fillText:将文本输出到画布上。
添加自定义的字体:
typeface.js中提供了几种加载到three.js场景中的字体。其是一个将TrueType和OpenType字体转换为js的库。转换出来的文件可以包含在你的页面中,然后即可以在three.js中使用。
要想转换已有的OpenType字体或者TrueType字体,可以使用http://typeface.neocracy.org/fonts.html,上传你的字体,然后该网页就会帮你把该字体转换为js文件。
然后就可以在网页中包含你的字体:
例子:
<script type="text/javascript" src='../assets/fonts/bitstream_vera_sans_mono_roman.typeface.js'>script>
//输出字体的名字
console.log(THREE.FontUtils.faces);
或者查看该字体的js源码,在文件的结尾处:
‘familyName’:”Bitstream Vera Sans Mono”
使用二元操作组合网格
Three.js的扩展库:THREEBSP
http://github.com/skalnik/ThreeBsp
该扩展库提供以下三个函数:
intersect:在两个几何体的交集上创建出新的几何体。两个几何体相交的地方就是新的几何体。
union:将两个几何体联合为一个几何体。
subtract:在第一个几何体中减去与第二个几何体相交的部分,从而创建新的几何体。
该库是使用coffee-script(一种js脚本的变体)写的,要使用这个库我们有两个选择。
1.添加coffee-script,并在运行时编译
<script type='text/javascript' src='../libs/coffee-script.js'>script>
<script type='text/coffeescript' src='../libs/ThreeBSP.coffee'>script>
coffee-script是解析ThreeBSP.coffee文件的。
为保证我们在使用ThreeBSP功能之前,ThreeBSP文件已经被解析完毕,我们需要在页面底部加上:
2.将它预编译成js文件,然后直接包含编译后的js文件。
使用coffee-script的命令行,先将coffee-script转换为js文件。
coffee –compile ThreeBSP.coffee
我们倾向于使用第二种方式,因为速度快。
注意:这三个函数在计算时,使用的是Mesh的绝对位置。所以,如果你使用组合过的Mesh你可能会得到一些莫名奇怪的结果。为了得到最好的。可预测的结果,应确保使用未经组合的Mesh。
一、subtract函数
function redrawResult (){
scene.remove(result);
var sphere1BSP = new ThreeBSP(sphere1);
var sphere2BSP = new ThreeBSP(sphere2);
var cube2BP = new ThreeBSP(cube);
var resultBSP ;
switch(controls.actionSphere){
case 'subtract':
resultBSP = sphere1BSP.subtract(sphere2BSP);
break;
case 'interct':
resultBSP = sphere1BSP.interct(sphere2BSP);
break;
case 'union':
resultBSP = sphere1BSP.union(sphere2BSP);
break;
}
if(!resultBSP)
resultBSP = sphere1BSP;
switch(controls.actionCube){
case 'subtract':
resultBSP = resultBSP.subtract(cube2BP);
break;
case 'interct':
resultBSP = resultBSP.interct(cube2BP);
break;
case 'union':
resultBSP = resultBSP.union(cube2BP);
break;
}
if(controls.actionCube=='none' && controls.actionSphere=='none'){
}else{
result = resultBSP.toMesh();
result.geometry.computeFaceNormals();
result.geometry.computeVertexNormals();
scene.add(result);
}
}
使用步骤:
1.将Mesh包装成ThreeBSP对象
2.在ThreeBSP对象上调用intersect、union、subtract方法,该函数的结果包含了所有创建网格所需要的信息,我们可以调用toMes()将其转换为Mesh
3.将结果Mesh调用computeFaceNormals() 和 computeVertexNormals(),确保所有的法向量可以正确的计算出来。(在执行二元操作,顶点和面的法向量可能会发生变化)
二、intersect函数
用法与上类似。
我们可以将这三种方法应用在我们创建的每个Mesh上。
三、union函数
用法与上类似。
Three.js中有该函数替代方案,且性能更好。
THREE.GeometryUtils.merge