问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

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

发布网友 发布时间:2024-09-17 05:25

我来回答

1个回答

热心网友 时间:2024-09-20 10:01

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

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

代码地址:https://github.com/QuarkGluonPlasma/threejs-exercize

思路分析

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

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

具体有这么些几何体:

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

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

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

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

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

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

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

那么床呢?

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

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

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

看起来还有雾?

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

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

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

我们简单小结下:

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

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

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

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

代码实现

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

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

functioncreateGrass(){constgeometry=newTHREE.PlaneGeometry(10000,10000);consttexture=newTHREE.TextureLoader().load('img/grass.jpg');texture.wrapS=THREE.RepeatWrapping;texture.wrapT=THREE.RepeatWrapping;texture.repeat.set(100,100);constgrassMaterial=newTHREE.MeshBasicMaterial({map:texture});constgrass=newTHREE.Mesh(geometry,grassMaterial);grass.rotation.x=-0.5*Math.PI;scene.add(grass);}

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

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

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

scene.fog=newTHREE.Fog(0xffffff,10,1500);

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

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

consthouse=newTHREE.Group();functioncreateHouse(){createFloor();constsideWall=createSideWall();constsideWall2=createSideWall();sideWall2.position.z=300;createFrontWall();createBackWall();constroof=createRoof();constroof2=createRoof();roof2.rotation.x=Math.PI/2;roof2.rotation.y=Math.PI/4*0.6;roof2.position.y=130;roof2.position.x=-50;roof2.position.z=155;createWindow();createDoor();createBed();}

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

functioncreateFloor(){constgeometry=newTHREE.PlaneGeometry(200,300);consttexture=newTHREE.TextureLoader().load('img/wood.jpg');texture.wrapS=THREE.RepeatWrapping;texture.wrapT=THREE.RepeatWrapping;texture.repeat.set(2,2);constmaterial=newTHREE.MeshBasicMaterial({map:texture});constfloor=newTHREE.Mesh(geometry,material);floor.rotation.x=-0.5*Math.PI;floor.position.y=1;floor.position.z=150;house.add(floor);}

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

functioncreateSideWall(){constshape=newTHREE.Shape();shape.moveTo(-100,0);shape.lineTo(100,0);shape.lineTo(100,100);shape.lineTo(0,150);shape.lineTo(-100,100);shape.lineTo(-100,0);constextrudeGeometry=newTHREE.ExtrudeGeometry(shape);consttexture=newTHREE.TextureLoader().load('./img/wall.jpg');texture.wrapS=texture.wrapT=THREE.RepeatWrapping;texture.repeat.set(0.01,0.005);varmaterial=newTHREE.MeshBasicMaterial({map:texture});constsideWall=newTHREE.Mesh(extrudeGeometry,material);house.add(sideWall);returnsideWall;}

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

constsideWall=createSideWall();constsideWall2=createSideWall();sideWall2.position.z=300;

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

constaxisHelper=newTHREE.AxisHelper(2000);scene.add(axisHelper);

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

functioncreateBackWall(){constshape=newTHREE.Shape();shape.moveTo(-150,0)shape.lineTo(150,0)shape.lineTo(150,100)shape.lineTo(-150,100);constextrudeGeometry=newTHREE.ExtrudeGeometry(shape)consttexture=newTHREE.TextureLoader().load('./img/wall.jpg');texture.wrapS=texture.wrapT=THREE.RepeatWrapping;texture.repeat.set(0.01,0.005);constmaterial=newTHREE.MeshBasicMaterial({map:texture});constbackWall=newTHREE.Mesh(extrudeGeometry,material);backWall.position.z=150;backWall.position.x=-100;backWall.rotation.y=Math.PI*0.5;house.add(backWall);}

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

functioncreateFrontWall(){constshape=newTHREE.Shape();shape.moveTo(-150,0);shape.lineTo(150,0);shape.lineTo(150,100);shape.lineTo(-150,100);shape.lineTo(-150,0);constwindow=newTHREE.Path();window.moveTo(30,30)window.lineTo(80,30)window.lineTo(80,80)window.lineTo(30,80);window.lineTo(30,30);shape.holes.push(window);constdoor=newTHREE.Path();door.moveTo(-30,0)door.lineTo(-30,80)door.lineTo(-80,80)door.lineTo(-80,0);door.lineTo(-30,0);shape.holes.push(door);constextrudeGeometry=newTHREE.ExtrudeGeometry(shape)consttexture=newTHREE.TextureLoader().load('./img/wall.jpg');texture.wrapS=texture.wrapT=THREE.RepeatWrapping;texture.repeat.set(0.01,0.005);constmaterial=newTHREE.MeshBasicMaterial({map:texture});constfrontWall=newTHREE.Mesh(extrudeGeometry,material);frontWall.position.z=150;frontWall.position.x=100;frontWall.rotation.y=Math.PI*0.5;house.add(frontWall);}

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

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

functioncreateWindow(){constshape=newTHREE.Shape();shape.moveTo(0,0);shape.lineTo(0,50)shape.lineTo(50,50)shape.lineTo(50,0);shape.lineTo(0,0);consthole=newTHREE.Path();hole.moveTo(5,5)hole.lineTo(5,45)hole.lineTo(45,45)hole.lineTo(45,5);hole.lineTo(5,5);shape.holes.push(hole);constextrudeGeometry=newTHREE.ExtrudeGeometry(shape);varextrudeMaterial=newTHREE.MeshBasicMaterial({color:'silver'});varwindow=newTHREE.Mesh(extrudeGeometry,extrudeMaterial);window.rotation.y=Math.PI/2;window.position.y=30;window.position.x=100;window.position.z=120;house.add(window);returnwindow;}

颜色设置为银白色。

门框也是一样:

scene.fog=newTHREE.Fog(0xffffff,10,1500);0

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

scene.fog=newTHREE.Fog(0xffffff,10,1500);1

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

scene.fog=newTHREE.Fog(0xffffff,10,1500);2

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

scene.fog=newTHREE.Fog(0xffffff,10,1500);3

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

scene.fog=newTHREE.Fog(0xffffff,10,1500);4

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

scene.fog=newTHREE.Fog(0xffffff,10,1500);5

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

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

scene.fog=newTHREE.Fog(0xffffff,10,1500);6

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

scene.fog=newTHREE.Fog(0xffffff,10,1500);7

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

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

scene.fog=newTHREE.Fog(0xffffff,10,1500);8

看下最终的效果:

全部代码上传到了github:

代码地址:https://github.com/QuarkGluonPlasma/threejs-exercize

总结

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

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

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

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

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

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

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

声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
求助:补全成语. _然而止、_然不屈、_然四顾、_然若失、_然而至、_然拒绝、_然无声... 我姑妈(姑妈去世),现奶奶的房产 姑妈的女儿有继承权吗? 丈夫去世遗产应该能怎么分配 《人世间》骆士宾遗产股权归谁所有 为什么说曹珊不应该得罪水自流_百 ... 有谁知我国十大元帅及十大上将? 解放中国的10大元帅是那几个? 对中国有何影响? 中国历史上的十大元帅 为什么中国过去有元帅现在没有了 ...想看中国80年代、90年代热播的电视剧,《渴望》、《蛙女》等,越多... 浅谈THREE.js的基础用法 《学习Three.js》速读——(1) 使用Three.js创建您的第一个3D场景 注会科目搭配:一年报考两科,三种参考方案! ...是门风窗户风空调风电扇风捎到身上我的左边脸胳膊腿就发 左胳膊突然没力气怎么回事啊? 我最近的左胳膊老是发麻无力,尤其是开空调时候,酸胀无力,我这是什么病... 洗衣机洗衣桶弹簧是正簧还是反簧 猪脚和什么一起炖最补 CAD字体库存放在哪个文件夹里面? cad2014字体在哪 在麦当劳点单的时候,为什么我会去买自己根本吃不完的加大套餐? 楚乔传宇文钥身世揭露楚乔传宇文钥身世 小白鼠跑了,怎么找回来 养了两只小白鼠十一家里没人怎么办? 在家里用什么最简单的方法知道猪骨头是否有毒? 宠物-小白鼠 关于饲养小白鼠 air pods序列号在哪里看 python海龟作图20秒完成小猪佩奇,附源码! 雎鸠哪个是公哪个是母 安全较好的电热水器品牌有哪些 十大名牌电热水器排名榜 不要再遗憾地说,道理我都懂可我做不到啊 怎样查电话黑名单里有谁的号码? 华为手机怎么看黑名单的号码? 电脑更改网页的大小怎么设置? 怎么让网页变回原来大小? 宝马7系后雾灯报码如何屏蔽 嘎哈加偏旁组词 橄榄调和油好不好 橄榄调和油介绍 索尼xga如何使用 政治机关有哪些 我国的行政机关是什么政治 方青卓相关内容 方青卓个人简历 自古以来我国重要的政治制度有哪些 美的空调冷静星二代怎么拆洗 智齿一直不拔会怎样 智齿不拔有什么危害 智齿不疼需要拔吗 女生智齿不拔的危害