06.ThreeJs开发指南-第六章-高级几何体

第六章 高级几何体

一、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

你可能感兴趣的:(Three.js)