Three.js入门(二)

摘自Three.js入门指南 张雯莉 (作者)

官网文档对每个几何形状都给出了效果网址,可以点击右上角的小三角形展开参数菜单,调节各参数将实时渲染几何形状,十分适合新手来熟悉几何形状,每个形状后面都会给出一个链接,没有介绍的可以自行到官网查看介绍

4、几何形状(Geometry)

在创建物体时,需要传入两个参数,一个是几何形状(Geometry),另一个是材质(Material)。

几何形状(Geometry)最主要的功能是储存了一个物体的顶点信息。WebGL需要程序员指定每个顶点的位置,而在Three.js中,可以通过指定一些特征来创建几何形状,例如使用半径创建一个球体,从而省去程序员一个个指定顶点的工作量。

基本几何形状

  • 立方体

    虽然这一形状的名字叫盒子模型(BoxGeometry),但它其实是长方体,也就是长宽高可以设置为不同的值,它包含了长方体所有顶点和填充面的对象。其构造函数是:

THREE.CubeGeometry(width, height, depth, widthSegments, heightSegments, depthSegments)

这里,width是x方向上的长度;height是y方向上的长度;depth是z方向上的长度;后三个参数分别是在三个方向上的分段数,如widthSegments为3的话,代表x方向上水平分为三份。一般情况下不需要分段的话,可以不设置后三个参数,后三个参数的缺省值为1。其他几何形状中的分段也是类似的。分段只对面进行分段,不对体内部分段。

立方体实时效果网址

  • 平面

    这里的平面(PlaneGeometry)其实是一个长方形,而不是数学意义上无限大小的平面。其构造函数为:

THREE.PlaneGeometry(width, height, widthSegments, heightSegments)

其中,width是x方向上的长度;height是y方向上的长度;后两个参数同样表示分段。
如果需要创建的平面在x轴和z轴所在的平面内,可以通过物体的旋转来实现。

平面实时效果网址

  • 球体

球体(SphereGeometry)的构造函数是:

THREE.SphereGeometry(radius, segmentsWidth, segmentsHeight, 
                        phiStart, phiLength, thetaStart, thetaLength)

其中,radius是半径;segmentsWidth表示经度上的切片数;segmentsHeight表示纬度上的切片数;phiStart表示经度开始的弧度;phiLength表示经度跨过的弧度;thetaStart表示纬度开始的弧度;thetaLength表示纬度跨过的弧度。

关于球的这些参数的设置可以多修改参数看看运行结果来理解。

球形实时效果网址

  • 圆形

    圆形(CircleGeometry)可以创建圆形或者扇形,其构造函数是:

THREE.CircleGeometry(radius, segments, thetaStart, thetaLength)

这四个参数都是球体中介绍过的,可以参考球体部分。

圆形实时效果网址

  • 圆柱体

    圆柱体(CylinderGeometry)的构造函数是:

THREE.CylinderGeometry(radiusTop, radiusBottom, height, radiusSegments, heightSegments, openEnded)

其中,radiusTopradiusBottom分别是顶面和底面的半径,由此可知,当这两个参数设置为不同的值时,实际上创建的是一个圆台;height是圆柱体的高度;radiusSegmentsheightSegments可类比球体中的分段;openEnded是一个布尔值,表示是否没有顶面和底面,缺省值为false,表示有顶面和底面。

圆柱体实时效果网址

  • 正四面体、正八面体、正二十面体

    正四面体(TetrahedronGeometry)、正八面体(OctahedronGeometry)、正二十面体(IcosahedronGeometry)的构造函数较为类似,分别为:

THREE.TetrahedronGeometry(radius, detail)
THREE.OctahedronGeometry(radius, detail)
THREE.IcosahedronGeometry(radius, detail)

其中,radius是半径;detail是细节层次(Level of Detail)的层数,对于大面片数模型,可以控制在视角靠近物体时,显示面片数多的精细模型,而在离物体较远时,显示面片数较少的粗略模型。这里我们不对detail多作展开,一般可以对这个值缺省。

正四面体实时效果网址
正八面体实时效果网址
正二十面体实时效果网址

  • 圆环面

    圆环面(TorusGeometry)就是甜甜圈的形状,其构造函数是:

THREE.TorusGeometry(radius, tube, radialSegments, tubularSegments, arc)

这里写图片描述
其中,radius是圆环半径;tube是管道半径;radialSegmentstubularSegments分别是两个分段数,详见上图;arc是圆环面的弧度,缺省值为Math.PI * 2

圆环面实时效果网址

  • 圆环结

    如果说圆环面是甜甜圈,那么圆环结(TorusKnotGeometry)就是打了结的甜甜圈,其构造参数为:

THREE.TorusKnotGeometry(radius, tube, radialSegments, tubularSegments, p, q, heightScale)

前四个参数在圆环面中已经有所介绍,pq是控制其样式的参数,一般可以缺省;heightScale是在z轴方向上的缩放。

圆环结实时效果网址

关于文字形状自定义形状以及其他几何形状此处就不展开介绍,可以自行查阅有关资料。

5、材质

材质(Material)是独立于物体顶点信息之外的与渲染效果相关的属性。通过设置材质可以改变物体的颜色、纹理贴图、光照模式等。

基本材质

使用基本材质(BasicMaterial)的物体,渲染后物体的颜色始终为该材质的颜色,而不会由于光照产生明暗、阴影效果。如果没有指定材质的颜色,则颜色是随机的。其构造函数是:

THREE.MeshBasicMaterial(opt)

其中,opt可以缺省,或者为包含各属性的值。如新建一个不透明度为0.75的黄色材质

new THREE.MeshBasicMaterial({
    color: 0xffff00,
    opacity: 0.75
})

其他常见属性:

  • visible:是否可见,默认为true

  • side:渲染面片正面或是反面,默认为正面THREE.FrontSide,可设置为反面THREE.BackSide,或双面THREE.DoubleSide

  • wireframe:是否渲染线而非面,默认为false

  • color:十六进制RGB颜色,如红色表示为0xff0000

  • map:使用纹理贴图

对于基本材质,即使改变场景中的光源,使用该材质的物体也始终为颜色处处相同的效果。

Lambert材质

Lambert材质(MeshLambertMaterial)是符合Lambert光照模型的材质。Lambert光照模型的主要特点是只考虑漫反射而不考虑镜面反射的效果,因而对于金属、镜子等需要镜面反射效果的物体就不适应,对于其他大部分物体的漫反射效果都是适用的。
其光照模型公式为:

Idiffuse = Kd * Id * cos(theta)

其中,Idiffuse是漫反射光强,Kd是物体表面的漫反射属性,Id是光强,theta是光的入射角弧度。

创建一个黄色的Lambert材质的方法为:

new THREE.MeshLambertMaterial({
    color: 0xffff00
})

color是用来表现材质对散射光的反射能力,也是最常用来设置材质颜色的属性。除此之外,还可以用ambientemissive控制材质的颜色。

ambient表示对环境光的反射能力,只有当设置了AmbientLight后,该值才是有效的,材质对环境光的反射能力与环境光强相乘后得到材质实际表现的颜色。

emissive是材质的自发光颜色,可以用来表现光源的颜色。单独使用红色的自发光:

new THREE.MeshLambertMaterial({
    emissive: 0xff0000
})

Phong材质

Phong材质(MeshPhongMaterial)是符合Phong光照模型的材质。和Lambert不同的是,Phong模型考虑了镜面反射的效果,因此对于金属、镜面的表现尤为适合。

漫反射部分和Lambert光照模型是相同的,镜面反射部分的模型为:

Ispecular = Ks * Is * (cos(alpha)) ^ n

其中,Ispecular是镜面反射的光强,Ks是材质表面镜面反射系数,Is是光源强度,alpha是反射光与视线的夹角,n是高光指数,越大则高光光斑越小。

由于漫反射部分与Lambert模型是一致的,因此,如果不指定镜面反射系数,而只设定漫反射,其效果与Lambert是相同的。下面的例子可以画出一个红色的球,高光部分为黄色:

var material = new THREE.MeshPhongMaterial({
    color: 0xff0000,
    specular: 0xffff00,
    shininess: 1000
});
var sphere = new THREE.Mesh(new THREE.SphereGeometry(3, 20, 8), material);

//注:需要加光照
var light = new THREE.PointLight(0xffffff, 1, 200);
light.position.set(10, 15, 50);
scene.add(light);

材质的纹理贴图

有时候,我们希望使用图像作为材质。这时候就需要导入图像作为纹理贴图,并添加到相应的材质中。

然后加载贴图

var texture = THREE.ImageUtils.loadTexture('http://wow.techbrood.com/uploads/1702/crate.jpg');

将材质的map属性设置为texture:

 var material = new THREE.MeshLambertMaterial({
    map: texture
});

完整代码见此处

  • 六张图像应用于长方体

    有时候,我们希望长方体的六面各种的贴图都不同。因此首先准备了六张不同的图像,然后分别导入图像到六个纹理,并设置到六个材质中:

var material1 = new THREE.MeshPhongMaterial( { 
        map: THREE.ImageUtils.loadTexture('http://wow.techbrood.com/uploads/1702/crate.jpg') } );

var material2 = new THREE.MeshPhongMaterial( { 
     map: THREE.ImageUtils.loadTexture('http://wow.techbrood.com/uploads/1702/bricks.jpg') } );

var material3 = new THREE.MeshPhongMaterial( { 
     map: THREE.ImageUtils.loadTexture('http://wow.techbrood.com/uploads/1702/clouds.jpg') } );

var material4 = new THREE.MeshPhongMaterial( { 
     map: THREE.ImageUtils.loadTexture('http://wow.techbrood.com/uploads/1702/stone-wall.jpg') } );

var material5 = new THREE.MeshPhongMaterial( { 
     map: THREE.ImageUtils.loadTexture('http://wow.techbrood.com/uploads/1702/water.jpg') } );

var material6 = new THREE.MeshPhongMaterial( { 
     map: THREE.ImageUtils.loadTexture('http://wow.techbrood.com/uploads/1702/wood-floor.jpg') } );

var materials = [material1, material2, material3, material4, material5, material6];

var meshFaceMaterial = new THREE.MeshFaceMaterial( materials );

上述代码,我们先分别创建了6个材料,组成了一个材料数组,并使用这个数组创建一个MeshFaceMaterial对象。
最后,我们需要告诉我们的3D模型来使用这个新的组合“面材料”。

cube = new THREE.Mesh(new THREE.BoxGeometry(10, 10, 10),
                      meshFaceMaterial
           );

在做天空盒时,加载图片时还可以用CubeTextureLoader函数来加载图片贴图,例

scene.background = new THREE.CubeTextureLoader()
         .setPath( 'http://wow.techbrood.com/uploads/1702/' )
         .load( [ 'crate.jpg', 'bricks.jpg', 'clouds.jpg', 'stone-wall.jpg', 'water.jpg', 'wood-floor.jpg' ] );
  • 贴图的重复

    有时候我们不希望使用拉伸的方式贴图,而是平铺方式。可以通过指定重复方式来实现:
    设置两个方向(wrapS和wrapT)都重复:

texture.wrapS = texture.wrapT = THREE.RepeatWrapping;

设置两个方向上都重复4次,由于我们的图像本来是有2行2列,所以重复4次即为8行8列:

texture.repeat.set(4, 4);
  • UV映射

    UV贴图是将2D纹理映射到3D物体的一种方式,它将图像上每一个点映射到模型物体的表面。在这个过程中三维曲面网络(“mesh”)的X Y Z被展平到一副二维(U V)图片中,这样图片中的颜色就被映射到曲面网络(“mesh”)中。
    我们使用U V代表”纹理坐标系”来代替通常在三维空间使用的 X Y。

    将前面的六张图拼在一起,省去了为每个面创建贴图的工作,下面把每个贴图对应的点位置信息保存下来。

//载入图片
var material = new THREE.MeshPhongMaterial({ 
                    map: THREE.ImageUtils.loadTexture('http://wow.techbrood.com/uploads/160801/texture-atlas.jpg') 
                });
//子图像在贴图中的位置边界信息
var bricks = [new THREE.Vector2(0, .666), new THREE.Vector2(.5, .666), new THREE.Vector2(.5, 1), new THREE.Vector2(0, 1)];
var clouds = [new THREE.Vector2(.5, .666), new THREE.Vector2(1, .666), new THREE.Vector2(1, 1), new THREE.Vector2(.5, 1)];
var crate = [new THREE.Vector2(0, .333), new THREE.Vector2(.5, .333), new THREE.Vector2(.5, .666), new THREE.Vector2(0, .666)];
var stone = [new THREE.Vector2(.5, .333), new THREE.Vector2(1, .333), new THREE.Vector2(1, .666), new THREE.Vector2(.5, .666)];
var water = [new THREE.Vector2(0, 0), new THREE.Vector2(.5, 0), new THREE.Vector2(.5, .333), new THREE.Vector2(0, .333)];
var wood = [new THREE.Vector2(.5, 0), new THREE.Vector2(1, 0), new THREE.Vector2(1, .333), new THREE.Vector2(.5, .333)];

UV映射方式如下:坐标的范围值是0到1,(0,0)表示左下角,(1,1)表示右上角。

子图像的坐标是根据贴图中百分比来定义,以逆时针方向来定义顶点坐标,从该子图像左下角开始定义,即:

 //            [左下角,                    右下角,                       右上角,                   左上角]
 var bricks = [new THREE.Vector2(0, .666), new THREE.Vector2(.5, .666), new THREE.Vector2(.5, 1), new THREE.Vector2(0, 1)];

定义好子图像后,我们现在需要把它们映射到立方体的各个面上去。

//清除先前的映射
geometry.faceVertexUvs[0] = [];
//
geometry.faceVertexUvs[0][0] = [ bricks[0], bricks[1], bricks[3] ];
geometry.faceVertexUvs[0][1] = [ bricks[1], bricks[2], bricks[3] ];

geometry.faceVertexUvs[0][2] = [ clouds[0], clouds[1], clouds[3] ];
geometry.faceVertexUvs[0][3] = [ clouds[1], clouds[2], clouds[3] ];

geometry.faceVertexUvs[0][4] = [ crate[0], crate[1], crate[3] ];
geometry.faceVertexUvs[0][5] = [ crate[1], crate[2], crate[3] ];

geometry.faceVertexUvs[0][6] = [ stone[0], stone[1], stone[3] ];
geometry.faceVertexUvs[0][7] = [ stone[1], stone[2], stone[3] ];

geometry.faceVertexUvs[0][8] = [ water[0], water[1], water[3] ];
geometry.faceVertexUvs[0][9] = [ water[1], water[2], water[3] ];

geometry.faceVertexUvs[0][10] = [ wood[0], wood[1], wood[3] ];
geometry.faceVertexUvs[0][11] = [ wood[1], wood[2], wood[3] ];
//创建物体
mesh = new THREE.Mesh(geometry,  material);

geometry对象的属性包括顶点位置、面索引、法向量、颜色、UVs以及自定义属性。faceVertexUvs属性包含该geometry各个面的坐标映射。在ThreeJS模型中,立方体的每个面实际上是由2个三角形组成的。所以我们必须单独映射每个三角形。上述场景中,ThreeJS将为我们加载单一材料贴图,自动分拆成三角形并映射到每个面。
这里要注意每个面的顶点坐标的定义顺序必须遵循逆时针方向。为了映射底部三角形,我们需要使用的顶点指数0,1和3,而要映射顶部三角形,我们需要使用索引1,2,和顶点的3。

猜你喜欢

转载自blog.csdn.net/unirrrrr/article/details/80549296