当前位置:首页 > 前沿科技 > 正文

我是如何用 Three.js 在三维世界建房子的(详细教程

这两天用画了一个3D的房子,放了一个床进去,可以用鼠标和键盘控制移动,有种3D游戏的即视感。这篇文章就来讲下实现原理。代码地址:思路分析我们先不着急写代码,先来分析下思路。这样一个房子,其实也是由几个几何体堆起来的:具体有这么些几何体:地板就是个平面,用PlaneGeometry(平面几何体)就可以...

这两天用画了一个3D的房子,放了一个床进去,可以用鼠标和键盘控制移动,有种3D游戏的即视感。这篇文章就来讲下实现原理。代码地址:思路分析我们先不着急写代码,先来分析下思路。这样一个房子,其实也是由几个......

这两天用画了一个3D的房子,放了一个床进去,可以用鼠标和键盘控制移动,有种3D游戏的即视感。

这篇文章就来讲下实现原理。

代码地址:

思路分析

我们先不着急写代码,先来分析下思路。

这样一个房子,其实也是由几个几何体堆起来的:

具体有这么些几何体:

地板就是个平面,用PlaneGeometry(平面几何体)就可以画,贴上个纹理贴图就行。

两个侧面的墙,是一个不规则的形状,这个可以用ExtrudeGeometry(挤压几何体),它支持用画笔画一个2D的路径,然后加厚变成3D的。

同理,后面的墙也很简单,可以是BoxGeometry(立方体)来画,也可以是ExtrudeGeometry(挤压结合体)先画个形状,然后变成3D的。

前面的墙稍微复杂些,它也是不规则的,可以用ExtrudeGeometry(挤压几何体)来画出形状,然后变成3D的,只不过它多了两个洞,需要画两个洞加到形状里面去。

门框、窗框也是形状里扣个洞,用ExtrudeGeometry变成3D的。

那房顶呢?房顶也没什么特殊的,只是立方体旋转一定的角度就行,用BoxGeometry(立方体)就可以画。

接下来,给墙和房顶、地板贴上不同的图,设置好不同的位置,就可以组装成一个房子了。

那么床呢?

提供了很多的几何体,可以画一些简单的物体,但复杂的物体就很难画出来了,这类物体一般会用专业的3D建模软件来画,导出FPX或者OBJ格式的文件由加载并渲染出来。

我们在网上找一个床的3D模型,我找了一个FBX格式的,然后用的FBXLoader加载就行。

还剩下一个草地,这个也是一个平面,用PlaneGeometry(平面几何体)画,只不过就是长宽比较大,看不到尽头而已。

看起来还有雾?

没错,确实设置了雾(Fog),在场景中设置雾的效果,指定颜色和雾的远近范围就行。为了有种模糊的感觉,我就在场景中加入了雾。

全部的物体都画完了,接下来就可以在3D场景中漫游了,通过鼠标和键盘可以改变方向和前后左右移动,这种交互使用FirstPersonControls(第一人称控制器)来实现。

一般我们常用的是OrbitsControls(轨道控制器),它支持围绕物体转动相机,就像卫星一样。但我们这里不是想绕着转,而是想键盘和鼠标控制的前后左右的随意移动。

我们简单小结下:

是在三维的坐标系中添加各种物体,组装成不同的3D场景。其中简单的物体可以画,复杂的物体会用建模软件画,然后加载到场景中。我们可以用不同的控制器来控制相机移动,达到不同的交互效果,比如轨道控制器、第一人称控制器等。

房子的墙、地板、房顶都可以用BoxGeometry(立方体)、ExtrudeGeometry(挤压几何体)画出来,但是床这种复杂的就不行了,会直接加载模型文件。

通过FistPersonControls(第一人称控制器)来控制交互,就能达到3D游戏的那种感觉。

思路理清了,接下来我们具体写下代码:

代码实现

先画草地,也就是一个大的平面,贴上草地的贴图。

三维的物体(Mesh)是由几何体(Geometry),加上材质(Material)构成的。我们创建平面几何体(PlaneGeometry),长和宽制定一个很大的值,比如10000,然后加载草地的图片作为纹理(Texture),构成材质。之后就可以创建出草地了。

functioncreateGrass(){

constgeometry=(10000,10000);

consttexture=().load('img/');

=;

=;

(100,100);

constgrassMaterial=({map:texture});

constgrass=(geometry,grassMaterial);

=-0.5*;

(grass);

}

纹理贴图要设置两个方向都重复,重复的次数是100次。

然后草地的平面要旋转一下。

加点雾,让天际模糊一些:

=(0xffffff,10,1500);

分别指定颜色为白色,雾的远近范围为10到1500。

接下来是创建房子,房子由地板、两侧的墙、前面的墙、后面的墙、门框窗框、房顶、床构成,要分别创建每一部分,我们把它们放到单独的Group(分组)里。

consthouse=();

functioncreateHouse(){

createFloor();

constsideWall=createSideWall();

constsideWall2=createSideWall();

=300;

createFrontWall();

createBackWall();

constroof=createRoof();

constroof2=createRoof();

=/2;

=/4*0.6;

=130;

=-50;

=155;

createWindow();

createDoor();

createBed();

}

创建地板也是平面几何体(PlaneGeometry),贴上木材的图就行,然后设置下位置:

functioncreateFloor(){

constgeometry=(200,300);

consttexture=().load('img/');

=;

=;

(2,2);

constmaterial=({map:texture});

constfloor=(geometry,material);

=-0.5*;

=1;

=150;

(floor);

}

创建侧面的墙,要用ExtrudeGeometry(挤压几何体)来画,也就是先画出一个2D的形状,然后挤压成3D。还要贴上墙的纹理贴图。

functioncreateSideWall(){

constshape=();

(-100,0);

(100,0);

(100,100);

(0,150);

(-100,100);

(-100,0);

constextrudeGeometry=(shape);

consttexture=().load('./img/');

==;

(0.01,0.005);

varmaterial=({map:texture});

constsideWall=(extrudeGeometry,material);

(sideWall);

returnsideWall;

}

两个侧墙只是位置不同,修改下z轴位置就行:

constsideWall=createSideWall();

constsideWall2=createSideWall();

=300;

对了,如果对位置拿不准,可以在场景中加个坐标系辅助工具(AxisHelper)。

constaxisHelper=(2000);

(axisHelper);

然后是后面的墙,这个形状简单一些,就是个矩形:

functioncreateBackWall(){

constshape=();

(-150,0)

(150,0)

(150,100)

(-150,100);

constextrudeGeometry=(shape)

consttexture=().load('./img/');

==;

(0.01,0.005);

constmaterial=({map:texture});

constbackWall=(extrudeGeometry,material);

=150;

=-100;

=*0.5;

(backWall);

}

接下来是前面的墙,这个除了要画出形状外,还要抠出两个洞:

functioncreateFrontWall(){

constshape=();

(-150,0);

(150,0);

(150,100);

(-150,100);

(-150,0);

constwindow=();

(30,30)

(80,30)

(80,80)

(30,80);

(30,30);

(window);

constdoor=();

(-30,0)

(-30,80)

(-80,80)

(-80,0);

(-30,0);

(door);

constextrudeGeometry=(shape)

consttexture=().load('./img/');

==;

(0.01,0.005);

constmaterial=({map:texture});

constfrontWall=(extrudeGeometry,material);

=150;

=100;

=*0.5;

(frontWall);

}

只是形状上多了两个洞,画起来复杂些,其余的纹理、材质,还有位置等设置方式都一样。

门窗也是画一个形状,抠一个洞,然后加点厚度变成3D的:

functioncreateWindow(){

constshape=();

(0,0);

(0,50)

(50,50)

(50,0);

(0,0);

consthole=();

(5,5)

(5,45)

(45,45)

(45,5);

(5,5);

(hole);

constextrudeGeometry=(shape);

varextrudeMaterial=({color:'silver'});

varwindow=(extrudeGeometry,extrudeMaterial);

=/2;

=30;

=100;

=120;

(window);

returnwindow;

}

颜色设置为银白色。

门框也是一样:

functioncreateDoor(){

constshape=();

(0,0);

(0,80);

(50,80);

(50,0);

(0,0);

consthole=();

(5,5);

(5,75);

(45,75);

(45,5);

(5,5);

(hole);

constextrudeGeometry=(shape);

constmaterial=({color:'silver'});

constdoor=(extrudeGeometry,material);

=/2;

=0;

=100;

=230;

(door);

}

接下来是房顶,就是两个立方体(BoxGeometry),做下旋转:

constroof=createRoof();

constroof2=createRoof();

=/2;

=/4*0.6;

=130;

=-50;

=155;

房顶的六个面的材质不同,一个面放瓦片的贴图,其余的面设置成灰色就行,模拟水泥的效果。其中,瓦片的纹理要做下旋转,设置下两个方向的重复次数。

functioncreateRoof(){

constgeometry=(120,320,10);

consttexture=().load('./img/');

==;

(5,1);

=/2;

consttextureMaterial=({map:texture});

constcolorMaterial=({color:'grey'});

constmaterials=[

colorMaterial,

colorMaterial,

colorMaterial,

colorMaterial,

colorMaterial,

textureMaterial

];

constroof=(geometry,materials);

(roof);

=/2;

=-/4*0.6;

=130;

=50;

=155;

returnroof;

}

接下来的床就简单了,因为不用自己画,直接加载一个已有的模型就行,这种复杂的模型一般都是专业建模软件画的。

functioncreateBed(){

varloader=();

('./obj/',function(object){

=40;

=80;

=20;

(object);

});

}

再就是灯光设置为环境光,也就是每个方向的光照强度都一样。

constlight=(0xCCCCCC);

(light);

创建相机,使用透视相机,也就是近大远小的那种透视效果:

constwidth=;

constheight=;

constcamera=(60,width/height,0.1,1000);

指定看的角度为60度,宽高比,远近范围0.1到1000。

创建渲染器,并用requestAnimationFrame一帧帧渲染就行了:

constrerer=();

functionrer(){

(scene,camera);

requestAnimationFrame(rer)

}

接下来还要支持在3D场景中漫游,这个也不用自己做,贴心的提供了很多控制器,各自有不同的交互效果,其中有个第一人称控制器(FirstPersonControls),就是玩游戏时那种交互,通过W、S、A、D键控制前后左右,通过鼠标控制方向。

constcontrols=(camera);

=0.05;

=100;

=false;

我们指定了转换方向的速度lookSpeed,移动的速度movementSpeed,禁止了纵向的转动。

然后每一帧都要更新一下看到的画面,通过时钟Clock获取到过去了多久,然后更新下控制器。

constclock=();

functionrer(){

constdelta=();

(delta);

(scene,camera);

requestAnimationFrame(rer)

}

看下最终的效果:

全部代码上传到了github:

代码地址:

总结

本文写了画3D房子的实现原理。

通过场景Scene管理各种物体,物体之间可以分组。物体由几何体(Geometry)和材质(Material)两部分构成,房子就是由立方体(BoxGeometry)、挤压几何体(ExtrudeGeometry)等各种几何体构成的,设置不同的贴图纹理,还有位置、旋转角度。

其中比较特殊的是ExtrudeGeometry(挤压几何体),它是通过在二维平面画一个形状,然后“挤压”成三维的形式,形状中还可以扣个洞。

房子中放了一张床,这种复杂的物体用手画就比较难了,这种一般都是由专业建模软件,比如bler来画好,然后用加载并渲染的。

视角的改变其实就是相机位置和朝向的改变,提供了各种控制器,比如OrbitsControls(轨道控制器)、FirstPersonControls(第一人称控制器)等。

我们这里要的通过键盘控制前后左右,通过鼠标控制转向的交互就可以用FirstPersonControls。

还是挺好玩的,业务上可能主要用于可视化、游戏,但工作之余也可以用它来做些有趣的东西。

最新文章