几何体对象(三维建模)

  three.js封装了很多创建几何体的构造函数,例如课程第一章展示过的长方体构造函数BoxGeometry()和球体的构造函数SphereGeometry(), 不论你有没有图形学基础,使用这些构造函数创建几何体对象没有太大的难度,如果你有WebGL的基础,从WebGL的角度看, 几何体对象实质就是顶点数据的封装,比如顶点位置坐标、顶点颜色、顶点法向量,从事互联网技术开发的人群有来自各个专业的,每个人的基础差异较大, 如果你想深入学习three.js,肯定要去了解一些WebGL、图形学的知识,如果仅仅是给一个企业官网添加我一个3D效果,就没有专门学习WebGL和图形学的必要。 只要你有编程基础,无论是否有WebGL基础,下面的课程中涉及到的代码案例你都可以正常学习,对于文字解析部分会从使用的角度描述构造函数、方法和属性, 也会从WebGL的角度简单阐述three.js的构造函数封装了哪些内容,一个是从黑箱使用的角度学习,一个是从探索黑箱构造角度学习。

常见三维几何体构造函数

  树状图列举了three.js引擎提供的常用规则三维几何对应的构造函数,它们的使用非常简单,只要你知道长度、半径几何概念就可以, 不需要了解WebGL的顶点数据和图元知识。下面会简单讲解几个几何体构造函数,展示几何体的创建方法, 有个印象,不需要记忆具体格式,用到时候时候查找three.js文档即可。

三维几何体立方体球体圆柱正四面体多面体正八面体正二十面体圆环环面纽结CubeGeometry——老版BoxGeometrySphereGeometryTetrahedronGeometryCylinderGeometryOctahedronGeometryIcosahedronGeometryTorusGeometryTorusKnotGeometryPolyhedronGeometry

  

立方体创建

构造函数完整格式:

BoxGeometry(width, height, depth, widthSegments, heightSegments, depthSegments)

  该构造函数的前三个参数表示立方体的长宽高尺寸,后三个参数表示xyz三个方向上的细分数量,立方体实质上都是由三角面构成,最少细分的情况下,每个面由两个三角面组成,一个三角面是通过三个顶点创建, 细分你可以理解为立方体的一个平面包含多个三角面,也就是多个顶点。

  几何体构造函数的每一个参数都有一个默认的值,使用构造函数的时候如果有些参数没有定义,系统会使用默认值。 比如下面立方体的创建语句构造函数BoxGeometry没有填写后三个参数

    //立方体(长宽高均为100)
    var box=new THREE.BoxGeometry(100,100,100);

创建球体

    //球体(半径60,细分数40,40)
    var box=new THREE.SphereGeometry(60,40,40);

创建圆柱、圆台

    //圆台(顶部半径20,底部半径40,高度100,圆周细分数40)
    var box=new THREE.CylinderGeometry(20,40,100,40);
    
    //圆柱(顶部、底部半径均为40,高度100,圆周细分数40)
    var box=new THREE.CylinderGeometry(40,40,100,40);

多面体PolyhedronGeometry

  如果你有WebGL基础,对顶点位置坐标和顶点索引有概念,理解构造函数PolyhedronGeometry非常容易,如果没有WebGL基础也不影响使用该构造函数创建多面体, 下面就通过PolyhedronGeometry构造函数创建一个立方体形式的六面体。注意思考PolyhedronGeometry和BoxGeometry创建立方体的方式有什么不同,如果你想使用three.js开发一个在线三维建模软件,肯定不能仅仅学习一些简单的常用的三维几何体构造函数,要学习更底层的东西。

three.js多面体——顶点索引
索引 顶点
0 (1, 1, 1)
1 (-1, 1, 1)
2 (-1, -1, 1)
3 (1, -1, 1)
5 (1, 1, -1)
6 (-1, 1, -1)
7 (-1, -1, -1)
8 (1, -1, -1)

构造函数格式:PolyhedronGeometry(vertices, faces, radius, detail)

参数 含义
vertices 几何体顶点坐标数组
indices 几何体顶点索引数组,三个顶点确定一个三角面
radius 几何体半径,可以理解为顶点位置坐标的缩放系数
detail 几何体表面细分,创建曲面的时候,细分程度越大越光滑
    //立方体顶点位置坐标
    var vertices = [
        -1,-1,-1,    1,-1,-1,    1, 1,-1,    -1, 1,-1,
        -1,-1, 1,    1,-1, 1,    1, 1, 1,    -1, 1, 1,
    ];
    //立方体顶点索引,三个顶点定义一个三角面
    var indices = [
        2,1,0,    0,3,2,
        0,4,7,    7,3,0,
        0,1,5,    5,4,0,
        1,2,6,    6,5,1,
        2,3,7,    7,6,2,
        4,5,6,    6,7,4
    ];
    var box=new THREE.PolyhedronGeometry(vertices,indices,60);

  顶点索引存在的好处是可以重用数据,可以简单类比数据库,以立方体为例三个相邻的三个三角面会共用同一个顶点位置坐标。

  PolyhedronGeometry和BoxGeometry创建立方体的结果是一样的,区别在于顶点的生成方式,PolyhedronGeometry构造函数的顶点通过手动创建, BoxGeometry通过函数生成,具体函数细节都被封装在three.js引擎中,有兴趣可以阅读three.js源码。

平面构造函数

平面矩形平面圆平面PlaneGeometryCircleGeometry

创建矩形平面

构造函数格式:PlaneGeometry(width, height, widthSegments, heightSegments)

  前两个参数定义个是矩形三角面的宽高尺寸,后两个参数定义矩形面的细分数,默认值都是1,从WebGL原生代码角度看三个顶点确定一个三角面,一个矩形面至少需要两个三角面组成, 换句话就是说细分数是1,细分数越大你可以理解为多个共平面的三角面组成矩形面,本质上是顶点数量的增加,当然这些都已被three.js引擎隐藏实现。

    //矩形平面,宽度30,高度50
    var box=new THREE.PlaneGeometry(30,50);
    //THREE.DoubleSide设置两面均显示
    var material=new THREE.MeshLambertMaterial({
        color:0x0000ff,
        side: THREE.DoubleSide
    });//材质对象

  从使用的角度看记住THREE.DoubleSide即可,如果在材质不中不设置side属性的值为THREE.DoubleSide,你可以用鼠标旋转平面,你会发现只能看到它的一面,另一面看不到,从WebGL角度来看很容易明白,生活中物体之所以能看到是因为有光照的存在,程序中模仿了光照,程序引入光照,顶点的法向量会参与顶点的颜色计算,法向量与光线向量的点积如果是负数,颜色值就会被舍弃,通常一个顶点只有一个法向量, THREE.DoubleSide本质上相当于绘制了两个平面,两个平面顶点的法向量方向相反,位置大小一样。如果你不太理解,记住就可以,想理解,就去研究WebGL。

  使用构造函数BoxGeometry创建立方体,就相当于使用构造函数PlaneGeometry创建六个矩形平面拼合成一个立方体。

创建圆平面

构造函数格式:CircleGeometry(radius, segments, thetaStart, thetaLength)

  radius表示圆弧半径,segments表示圆的细分数量,如果是5,绘制出来的效果就是正五边形平面,绘制圆平面是通过增大segments的值来模拟, 后两个参数表示绘制的起始角度,默认的情况下一个是0,另一个是2π,换句话说对应的是圆平面,第四个参数如果像下面代码中设置为0.5*Math.PI相当于绘制一个扇形区域。 圆柱、球体的构造函数也同样具有起始角度参数,可以类比学习,降低记忆量。

    //正五边形
    var box=new THREE.CircleGeometry(50,5);
    //正40边形(圆平面)
    var box=new THREE.CircleGeometry(50,40);
    //四分之一扇形
    var box=new THREE.CircleGeometry(50,40,0,0.5*Math.PI);

二维轮廓线

  使用三维软件进行建模的时候,往往会利用二维线条轮廓生成三维几何体或平面,本节课主要内容就是为大家讲解three.js引擎提供的相关API。

  执行构造函数ShapeGeometry()和立方体BoxGeometry()、空几何体Geometry()一样都是返回一个几何体对象,可以作为点线面构造函数的几何体参数, 不同的是ShapeGeometry()的封装程序介于BoxGeometry()和Geometry()之间,构造函数BoxGeometry()创建立方体的顶点数据直接定义长宽高和细分参数, 生成顶点数据的工作交给three.js程序自动完成,Geometry()创建的是一个没有顶点数据的空几何体,需要工程师自定义顶点相关数据, ShapeGeometry()的参数是构造函数Shape()返回的对象,Shape对象具有一系列直线、圆弧、样条曲线创建方法,比如创建圆弧的方法absarc(), 执行absarc()方法会返回一个圆弧,这里说的圆弧其实是一种借用数学里面的概念对顶点数据进行抽象描述, absarc()方法创建的圆弧本质上可以理解为一系列沿着圆弧分布的顶点数据,至于显示效果取决于你使用点、线、面三种渲染模式中的哪一种。

圆弧——点模式渲染

    /**
     * 创建点模型
     */
    var shape = new THREE.Shape();
    shape.absarc(0,0,100, 0, 0.5*Math.PI);
    var geometry = new THREE.ShapeGeometry(shape);
    var material=new THREE.PointsMaterial({
        color:0x0000ff,
        size:10.0//点对象像素尺寸
    });//材质对象
    var line=new THREE.Points(geometry,material);//点模型对象
    scene.add(line);//点模型添加到场景中

圆弧——线模式渲染

    /**
     * 创建线模型
     */
    var shape = new THREE.Shape();
    shape.absarc(0,0,100, 0, 0.5*Math.PI);
    var geometry = new THREE.ShapeGeometry(shape);
    var material=new THREE.LineBasicMaterial({color:0x0000ff});//材质对象
    var line=new THREE.Line(geometry,material);//线模型对象
    scene.add(line);//线模型添加到场景中

圆弧——面模式渲染

  three.js使用Mesh创建网格模型的时候,非闭合轮廓线自动闭合,也就是几何体首尾两顶点相连。

    /**
     * 创建网格模型
     */
    var shape = new THREE.Shape();
    shape.absarc(0,0,100, 0, 0.5*Math.PI);
    var geometry = new THREE.ShapeGeometry(shape);
    var material=new THREE.MeshLambertMaterial({
        color:0x0000ff,//三角面颜色
        side:THREE.DoubleSide//两面可见
    });//材质对象
    var mesh=new THREE.Mesh(geometry,material);//网格模型对象
    scene.add(mesh);//网格模型添加到场景中

shape()对象

Shape方法线条绘制几何体对象moveTolineTo直线arcabsarc 圆弧ellipse absellipse 椭圆quadraticCurveTo bezierCurveToQuadraticBezierCurve贝赛尔曲线...返回ShapeGeometry对象makeGeometry返回几何体Geometry对象,支持自定义细分createPointsGeometry返回几何体Geometry对象,支持自定义细分createSpacedPointsGeometry

  Shape对象创建的轮廓线要想渲染出来,首先要转化为为几何体对象Geometry,Shape对象生成几何体对象除了借助构造函数ShapeGeometry, 也可以使用makeGeometry、createPointsGeometry等方法,shape对象执行makeGeometry方法返回的结果是ShapeGeometry几何体对象, 说明makeGeometry方法和构造函数ShapeGeometry是等价的,ShapeGeometry对象有自己默认的曲线细分精度。 shape对象执行createPointsGeometry方法返回的结果同样是几何体对象,与makeGeometry方法不同之处是createPointsGeometry方法细分程序支持自定义, 细分数越大,相同长度的曲线,顶点数量越多,Shape对象本身并不是顶点数据,可以理解为一种绘图方法,就像数学中的中曲线函数, Shape对象转化为为几何体对象,本质上就是利用Shape定义的曲线函数生成顶点数据供GPU调用。

  使用shape对象的方法把Shape对象生成几何体对象。

    /**
     * 创建点模型
     */
    var shape = new THREE.Shape();
    shape.absarc(0,0,100, 0, 0.5*Math.PI);
    var geometry = shape.makeGeometry();//等价于ShapeGeometry,默认细分数
    var material=new THREE.PointsMaterial({
        color:0x0000ff,
        size:10.0//点对象像素尺寸
    });//材质对象
    var line=new THREE.Points(geometry,material);//模型对象
    scene.add(line);//点模型添加到场景中

ShapeGeometry构造函数


    var geometry = new THREE.ShapeGeometry(shape);//默认细分数

makeGeometry()方法


    var geometry = shape.makeGeometry();//等价于ShapeGeometry,默认细分数

createPointsGeometry()方法


    var geometry = shape.createPointsGeometry(20);//细分数20

createSpacedPointsGeometry()方法


    var geometry = shape.createSpacedPointsGeometry(15);//细分数15

拉伸和扫描成型

three.js拉伸成型ExtrudeGeometry

  构造函数ExtrudeGeometry()和ShapeGeometry()一样是利用Shape对象生成几何体对象,区别在于ExtrudeGeometry()可是利用2D轮廓生成3D模型, 如果你使用任何三维软件都知道可以先绘制一个二维的轮廓图,然后拉伸成型得到三维模型。ExtrudeGeometry()第二个参数是拉伸参数,数据类型是对象, 属性amount表示拉伸长度,bevelEnabled表示拉伸是否产生倒角,其它参数见下表。

拉伸参数

amount 拉伸长度,默认100
bevelEnabled 是否使用倒角
bevelSegments 倒角细分数,默认3
bevelThickness 倒角尺寸(经向)
curveSegments 拉伸轮廓细分数
steps 拉伸方向细分数
extrudePath 扫描路径THREE.CurvePath,默认Z轴方向
material 前后面材质索引号
extrudeMaterial 拉伸面、倒角面材质索引号
bevelSize 倒角尺寸(拉伸方向)
    /**
     * 创建拉伸网格模型
     */
    var shape = new THREE.Shape();
    /**四条直线绘制一个矩形轮廓*/
    shape.moveTo(0,0);//起点
    shape.lineTo(0,100);//第2点
    shape.lineTo(100,100);//第3点
    shape.lineTo(100,0);//第4点
    shape.lineTo(0,0);//第5点
    var geometry = new THREE.ExtrudeGeometry(//拉伸造型
        shape,//二维轮廓
        //拉伸参数
        {
            amount:120,//拉伸长度
            bevelEnabled:false//无倒角
        }
        );
    var material=new THREE.MeshPhongMaterial({color:0x0000ff});//材质对象
    var mesh=new THREE.Mesh(geometry,material);//网格模型对象
    scene.add(mesh);//网格模型添加到场景中

  通过使用点模式渲染上面的几何体,可以看出几何体拉伸的本质效果就是空间分布顶点数据的产生。

    var material=new THREE.PointsMaterial({
        color:0x0000ff,
        size:5.0//点对象像素尺寸
    });//材质对象
    var mesh=new THREE.Points(geometry,material);//点模型对象
    scene.add(mesh);//点模型添加到场景中

扫描

three.js扫描成型ExtrudeGeometry

  拉伸和扫描都是一种三维造型建模方法,three.js提供了一个共同的构造函数来实现扫描和拉伸,对于扫描而言不需要定义amount属性设置拉伸距离,设置扫描路径即可, 定义属性extrudePath,extrudePath的值是路径THREE.CurvePath,可以通过样条曲线、贝赛尔曲线构造函数创建不规则曲线扫描轨迹。

     /**
     * 创建扫描网格模型
     */
    var shape = new THREE.Shape();
    /**四条直线绘制一个矩形轮廓*/
    shape.moveTo(0,0);//起点
    shape.lineTo(0,10);//第2点
    shape.lineTo(10,10);//第3点
    shape.lineTo(10,0);//第4点
    shape.lineTo(0,0);//第5点
    /**创建轮廓的扫描轨迹(3D样条曲线)*/
    var curve = new THREE.SplineCurve3([
        new THREE.Vector3( -10, -50, -50 ),
        new THREE.Vector3( 10, 0, 0 ),
        new THREE.Vector3( 8, 50, 50 ),
        new THREE.Vector3( -5, 0, 100)
    ]);
    var geometry = new THREE.ExtrudeGeometry(//拉伸造型
        shape,//二维轮廓
        //拉伸参数
        {
            bevelEnabled:false,//无倒角
            extrudePath:curve,//选择扫描轨迹
            steps:50//扫描方向细分数
        }
    );
    var material=new THREE.MeshPhongMaterial({color:0x0000ff});//材质对象
    var mesh=new THREE.Mesh(geometry,material);/扫描网格模型对象
    scene.add(mesh);//扫描网格模型添加到场景中

3D样条曲线生成管道

  在实际生活大家应该都会见到圆形截面电线、软管等呈现样条状的几何体,对于这些几何体可以通过管道构造函数TubeGeometry(),3D样条曲线作为参数实现,它的本质就是以3D样条为基准,生成一系列环绕样条曲线等径分布的顶点数据, 具体计算方法如何实现的可以查看three.js引擎源码。管道构造函数TubeGeometry()和拉伸扫描构造函数ExtrudeGeometry()一样是利用已有的2D或3D线条生成相关顶点数据, 也就是一个几何体对象。

    /**
     * 创建管道网格模型
     */
    var path = new THREE.SplineCurve3([//创建轮廓的扫描轨迹(3D样条曲线)
        new THREE.Vector3( -10, -50, -50 ),
        new THREE.Vector3( 10, 0, 0 ),
        new THREE.Vector3( 8, 50, 50 ),
        new THREE.Vector3( -5, 0, 100)
    ]);
    var geometry = new THREE.TubeGeometry( path, 40, 2, 8, false );
    var material=new THREE.MeshPhongMaterial({
        color:0x0000ff,
        side:THREE.DoubleSide//两面可见
    });//材质对象
    var mesh=new THREE.Mesh(geometry,material);//管道网格模型对象
    scene.add(mesh);//管道网格模型添加到场景中

构造函数格式:TubeGeometry(path, tubularSegments, radius, radiusSegments, closed)

参数
path 扫描路径,基本类是Curve的路径构造函数
tubularSegments 路径方向细分数,默认64
radius 管道半径,默认1
radiusSegments 管道圆弧细分数,默认8
closed Boolean值,管道是否闭合

参数化曲面

  参数化曲面简单的说就是数学函数插值计算后生成一个曲面的顶点数据,这需要借助构造函数ParametricGeometry()实现,立方体、球体等规则几何体都是该构造函数的进一步分装, SphereGeometry构造函数封装了球体顶点数据生成的算法,使用ParametricGeometry()的时候相当于自己写几何体顶点生成算法。

格式:ParametricGeometry(func, slices, stacks)

参数
func 几何体生成顶点算法函数名
slices u方向细分数
stacks v方向细分数

平面

    /**
     * 创建平面网格模型
     */
    function plane(u,v) {
        var width = 50,height = 100;//平面宽高尺寸
        var x = u*width;//等比例运算
        var y = v*height;//等比例运算
        var z = 0;
        return new THREE.Vector3(x,y,z)
    }
    var geometry = new THREE.ParametricGeometry(plane,10,10);
    var material=new THREE.MeshPhongMaterial({
        color:0x0000ff,//三角面颜色
        side:THREE.DoubleSide//两面可见
    });//材质对象
    material.wireframe = true;//线条模式渲染(查看细分数)
    var mesh=new THREE.Mesh(geometry,material);//线模型对象
    scene.add(mesh);//线模型添加到场景中

  函数u、v参数的值区间是[0,1],u、v步长是u/slices、u/stacks,构造函数后两个参数slices、stacks的作用就是设置一个曲面在UV两个方向上的细分数,UV方格方向可以是xyz坐标轴的任意两个。

旋转抛物面:z = a2*(x2+y2

        
    function paraboloid(u,v) {
        var k = 100;//x、y取值范围
        var a = 0.2;//旋转抛物面焦点
        var x = (u-0.5)*k;
        var y = (v-0.5)*k;
        var z = Math.pow(a,2)*(Math.pow(x,2)+Math.pow(y,2));
        return new THREE.Vector3(x,y,z)
    }
        

旋转造型

three.js旋转成型LatheGeometry

  生活中有很多的几何体具备旋转特征,比如球体,常见杯子, three.js提供了一个构造函数LatheGeometry(), LatheGeometry可以利用已有的二维数据生成三维顶点数据,二维数据可以通过Vector2自定义,也可以通过3D曲线或2D线条轮廓生成。 LatheGeometry的二维坐标数据默认绕y轴旋转。

格式:LatheGeometry(points, segments, phiStart, phiLength)

参数
points Vector2表示的坐标数据组成的数组
segments 圆周方向细分数,默认12
phiStart 开始角度,默认0
phiLength 旋转角度,默认2π
    /**
     * 创建旋转网格模型
     */
    var points = [
        new THREE.Vector2(50,60),
        new THREE.Vector2(25,0),
        new THREE.Vector2(50,-60)
    ];
    var geometry = new THREE.LatheGeometry(points,30);
    var material=new THREE.MeshPhongMaterial({
        color:0x0000ff,//三角面颜色
        side:THREE.DoubleSide//两面可见
    });//材质对象
    material.wireframe = true;//线条模式渲染(查看细分数)
    var mesh=new THREE.Mesh(geometry,material);//旋转网格模型对象
    scene.add(mesh);//旋转网格模型添加到场景中

样条曲线插值计算

  借助Shape对象的方法.splineThru(),把上面的三个顶点进行样条插值计算, 可以得到一个光滑的旋转曲面。

    /**
     * 创建旋转网格模型
     */
    var shape = new THREE.Shape();//创建Shape对象
    var points = [//定位定点
        new THREE.Vector2(50,60),
        new THREE.Vector2(25,0),
        new THREE.Vector2(50,-60)
    ];
    shape.splineThru(points);//顶点带入样条插值计算函数
    var splinePoints = shape.getPoints(20);//插值计算细分数20
    var geometry = new THREE.LatheGeometry(splinePoints,30);//旋转造型
    var material=new THREE.MeshPhongMaterial({
        color:0x0000ff,//三角面颜色
        side:THREE.DoubleSide//两面可见
    });//材质对象
    material.wireframe = true;//线条模式渲染(查看细分数)
    var mesh=new THREE.Mesh(geometry,material);//旋转网格模型对象
    scene.add(mesh);//旋转网格模型添加到场景中

  shape.getPoints(20)的作用是利用已有的顶点插值计算出新的顶点,两个顶点之间插值计算出20个顶点,如果细分数是1不是20,相当于不进行插值计算, 插值计算的规则通过Shape对象的方法.splineThru()定义,几何曲线的角度描述,splineThru的作用就是创建一个样条曲线,除了样条曲线还可以使用贝赛尔等曲线进行插值计算。