喜欢做动画的我很早之前就已经开始关注Threejs这个框架了,觉得这个框架老酷炫了,可以在浏览器中展示一个3D场景,奈何时间与精力关系(主要是懒)一直都没有真正花功夫去学,好在最近终于开始简单学了一点Threejs的皮毛,并且写了个安卓机器人的小demo,涉及到的知识点是一些简单的几何模型的使用,有兴趣的就来一起看下吧。
Threejs可以用在不同项目中,可以用纯js写,也可以用Vue来写,我这里使用的是React+ts,第一步咱先来创建个项目,使用cra创建
等待一段时间的项目初始化之后,在vscode中打开这个叫robot
的工程,然后在终端里面分别执行以下命令
第一个命令是安装threejs
这个库,第二个命令是安装@types/three
这个库,后者是一个TypeScript的类型定义库,用于为 Three.js 库提供类型定义,库都安装好了之后我们打开项目为我们默认创建好的App.tsx
文件,这个就是工程的示例页面,我们将在里面做改动
首先把App.tsx
里面的代码都删除,换成如下代码
这里创建了一个div的Html元素containerRef
,并且在div
标签中引用它,而useEffect
内部就是用来写我们Three的代码,在任何一个Three的工程中,有三个要素是必不可少的,那就是渲染器,相机以及场景,所以我们第一步就是将三个要素创建出来
一般都是使用透视相机PerspectiveCamera
,第一个参数是fov
,表示相机所成的一个四棱台远面与近面之间的夹角,夹角越小,看见的东西越少,夹角越大,看见的东西就越多,但是周围会显的比较模糊,一般取值以45~75最佳,第二个参数aspect
是近裁面的一个宽高比,我们用窗口的宽除以窗口的高就可以了,第三个值near
与第四个值far
分别是与近面和远面的距离,这里设置的值分别为0.1与1000,调用camera.positon.set
表示设置相机的位置,默认都是在(0,0,0)的位置,我们这里给相机设置的位置为(15,12,8),并且让相机正对(0,0,0)的位置,了解完了一些概念后,开始创建机器人相关的物体了
整个机器人是使用若干个几何体拼接起来的,所以我们从下往上先将双腿做出来
首先创建了一个Object3D
对象robot
对象,Object3D
是一个基类,所有3D对象都是Object3D
的子类或者衍生类,我们后面所创建的机器人身上的部位最终都会add
到robot
里面去,generateLegs
是用来创建腿的函数,调用了两次,分别在(0,0,2)和(0,0,-2)位置创建了两个绿色的胶囊型物体,材质使用的是MeshStandardMaterial
,并且设置了roughness
和metalness
,让双腿有金属质感,然后用创建好的胶囊与材质生成Mesh
后,腿就制作好了,添加到robot
里去,并且将robot
添加到场景scene
里面,腿的工作就完成了,但是这个时候如果想去浏览器看下效果的话,会发现是漆黑一片的
原因是我们的材质是MeshStandardMaterial
,这种材质是感光的,需要有光源照在上面才会看见,所以我们还需要在场景里面添加进去光源,这里使用平行光DirectionalLight
平行光的颜色是白色,强度设置的是5,这些可以根据自己的喜好去定义,光源所在的位置是(5,5,10),有了光源后,这样子就能看见腿了
腿往上就是身体,身体是一个圆柱体,所以这里使用的是CylinderGeometry
,并且在y轴方向上往上平移4个单位,看代码
CylinderGeometry
第一个参数是上部分圆的半径,第二个参数是下部分圆的半径,第三个参数是高度,材质使用的跟腿一样,然后身体也出来了
两侧的手臂跟腿是差不多的做法,唯一区别就是位置不一样
脑袋是个半球体,这里使用SphereGeometry
创建个球体,半径设置为4,widthSegment
与heightSegment
都使用默认值,phiStart
与phiLength
分别是水平方向上的起始角度与跨度,由于水平方向上是整个圆,所以phiLength
设置为Math.PI * 2
,相对的垂直方向上的跨度就是90度,这里给thetaLength
设置为Math.PI * 0.5
脑袋在y轴方向上平移了6.5个单位,这个时候浏览器上面机器人的脑袋就已经出来了
触角的位置要比脑袋还要高,同时它也要有点角度,所以创建个generateHorn
函数,除了接受位置参数之外,还要接受角度参数,代码如下
眼睛是两个黑色的球体,所以还是使用SphereGeometry
,位于脑袋的内部,从脑袋里面露出来个半圆作为眼睛,代码如下
我们看到整个机器人都已经出来了,但是目前页面还是不能够操作的,比如现在我们只看到机器人的一侧,另一侧是看不到的,当然这与我们相机摆放的位置有关,调整下相机的位置就好了,不过也可以使用轨道控制器OrbitControls
,让页面可以跟着鼠标的操作360度移动,代码很简单,生成个轨道对象,再调用update
方法
不过加了上述代码后,我们会发现页面还是动不了,因为我们还必须刷新页面,创建个update
函数,内部调用requestAnimationFrame
,这个函数的执行时机与浏览器的刷新频率是一致的,所以只需要在浏览器刷新的时候,再渲染一次页面,就能完成页面上的动画效果了,代码如下
现在我们的页面就可以操作了,来看下
我们还可以在update
方法中,让机器人自己可以转动起来,比如让它绕着y轴转动,就可以这样写
可以看到只需要给robot
在y轴上不断减少0.005的角度,机器人就可以自己转动起来了,效果如下
我们还可以让机器人变小一些,只需要在robot
的scale
属性上,分别给x,y,z轴方向都设置上缩放比例,机器人的大小就改变了,这里设置了0.3的缩放比例
我们看下现在机器人的大小是不是改变了
的确是变小了,下面来给机器人周围加上一些粒子
这里粒子的原型其实就是一个个小球体,所以几何模型依然使用SphereGeometry
,然后由于我们需要制作多个球体,所以还要创建一个Object3D
,将所有粒子都放在Object3D
里面,至于如何创建多个粒子,这里就用到了copy
函数,看代码
给球体设置了widthSegment
和heightSegment
让球体带点棱角,其他的同上面设置的一样,最终生成一个物体mesh
,在for循环内每次都会新生成一个target
的Mesh
对象,通过调用copy
函数,将mesh
复制给target
,那么target
就拥有mesh
的所有属性了,后面只需要给新的target
设置新的位置就可以了,编译完之后,我们的机器人周围已经都是小粒子了
现在这些粒子看起来还不太美观,这里再稍作修改,把上面的金属质感metalness
设置为5,粗糙程度roughness
设置为0.1,改变一下粒子受光照的效果
再看下效果,粒子就变得好看些了
现在再让粒子360无死角转动起来,给stars
的x,y,z上都加上旋转增量,更新一下update
函数内的代码
最终效果就做好了
文章到这里就结束了,用的东西都是比较入门的,诸如一些纹理图的加载,GLTF模型的加载等这样的知识点,我仍旧在学习摸索的过程中,后面也会通过一个个小的demo分享出来。