3D引擎的基本知识
本文的目标是让大家看完之后可以立刻上手用起来,既然要用3d引擎,那我们理解了一些3d的基本知识后,再看threejs
的API文档效率就会很高。无论什么3d引擎,都不外乎由以下几种基本元素构成
场景(scene
)
一个容器,容纳着除渲染器以外的三维世界里的一切。场景的元素采用右手笛卡尔坐标系,x轴正方向向右,y轴正方向向上,z轴由屏幕从里向外
摄像机(camera
)
就像人的眼睛,在一个空间里可以看向任意方向,可以通过参数调节可视角度和可视距离。
一般我们使用符合物理世界近大远小真实情况的透视相机PerspectiveCamera
,还有一些特殊情况,需要远近大小是一样的,那就要用正交相机OrthographicCamera
PerspectiveCamera( fov : Number, aspect : Number, near : Number, far : Number ) //构造函数参数 //fov:视场角 //aspect:视场宽高比(一般用 画布宽/画布 高即可) //near:能看多近 //far:能看多远 //这几个参数决定了哪些scene里的三维顶点会被渲染/绘制出来
渲染器(renderer
)
将
camera
在scene
里看到的内容渲染/绘制到画布上
几何体(geometry
)
3D世界里的所有物体都是
点组成面
,面组成几何体
。相信大家对以下标准的几何体比较熟悉
- 球体
- 立方体
- 圆锥体
- 圆柱体
面
是由点构成的,面
又可以组成各式各样的几何体。以球体举例,球体面上的点越多,球就越圆。但点越多,运算量也会越大...
另外我们一般说的3d模型
就是一个或多个几何体,只是有的3d模型文件里除了包含几何体还可以包含一些额外的信息,比如贴图,材质等...需要在读取模型文件时解析出来
灯光(light
)
3d引擎在没有手动创建光的情况下会默认有个
环境光
,不然你什么都看不到。常见的灯光有以下几种类型
- AmbientLight(环境光,没有方向全局打亮,不会产生明暗)
- DirectionLight(平行光,参考日光来理解)
- PointLight(点光源,参考灯泡来理解)
- SpotLight(聚光灯,参考舞台聚光灯)
贴图(texture
)
想象一下你手里有一个立方体,你用一张A4纸包裹上立方体的所有面,并在上面画画。你画的内容就是
贴图
。
有一些类型的贴图会和光照发生反应...后面我们用到的时候再说
材质(material
)
延续贴图里的想象,你用白卡纸画画,还是用油纸画画,呈现出来的质感是不同的对不对,这就是
材质
!下面五个球的颜色都是一样的,而材质从左至右分别是
- MeshBasicMaterial(基础材质,不受光照影响)
- MeshStandardMaterial(PBR标准材质)
- MeshPhongMaterial(高光材质,适用于陶瓷,烤漆类质感)
- MeshToonMaterial(卡通材质,俗称三渲二)
- MeshStandardMaterial(PBR标准材质模拟金属反射)
来实战吧!
有了这些基础知识,再来使用threejs就很容易上手了。可以说在3dmax等软件中调出来的90%的效果,用threejs都能找到对应的配置参数。
搭建基础场景
//<div id='container' style="width:100%;height: 100%;"></div> var scene, camera, renderer; function init(){ scene = new THREE.Scene(); //这里参数不懂的同学回去看基本知识里的camera部分 camera = new THREE.PerspectiveCamera(90, document.body.clientWidth / document.body.clientHeight, 0.1, 100); //camera的位置在x0,y0,z3,还记得迪尔卡右手坐标系吗? camera.position.set(0, 0, 3); renderer = new THREE.WebGLRenderer(); renderer.setSize(document.body.clientWidth, document.body.clientHeight); document.getElementById("container").appendChild(renderer.domElement); var controls = new THREE.OrbitControls(camera, renderer.domElement); //等待添加模型 loop(); } function loop() { requestAnimationFrame(loop); renderer.render(scene, camera); } window.onload = init;
现在我们可以先添加一个标准几何体来试试看,比如我们添加一个立方体来试试看
const geometry = new THREE.BoxGeometry( 1, 1, 1 ); const material = new THREE.MeshBasicMaterial( {color: 0x00ff00} ); const cube = new THREE.Mesh( geometry, material ); scene.add( cube ); 复制代码
很显然,场景是生效的...大家注意看源码块中的注释
汽车模型
回到咱们的项目上来,品牌方给的是一个非常精细的模型,文件量有好几百兆,数百万面(triangles
)。
我说这可用不了,你得减面
还得给我转成引擎能支持的格式gltf
或obj
根据我的评估,要想在移动端网页里流畅运行,最多不能超过10万面
外包工头老杨说,你也别让客户给你弄了,他们都不会
我知道你懂,你就给弄了算了,我给你加【5K】
加5K你让我怎么好意思拒绝呢...
然后,我花25美刀
巨资在sketchfab
上购买了一个模型
再稍微改改就能满足要求,当然sketchfab也有免费模型
但毕竟收了老杨5K,不花点钱我心里略感不安
呐 :p
优化模型结构
根据实际的需求,比如车窗要透明可以看到内饰
,所以车窗就得单独给有透明属性的材质。车轮,灯罩,车网,车架,车身等等都要拆成独立的几何体
才能独立配置材质
。
梳理好模型结构后,我们就要准备模型文件了