three.js 入门(一)

352 阅读7分钟

1. 简介

three.js是一个运行在浏览器中的3D引擎,可以用于创建各种3D场景,包括摄像机、常用的灯光、材质、贴图以及一些组合运算方法,项目中自带大量示例但文档说明比较简单且汉化不完全,不是很方便学习,这里简单对目前了解到的内容做一下总结,希望可以对你有所帮助(部分图片来自网络,如有侵权还请告知)。

2. 三维建模基础概念

在正式学习three.js的使用之前,需要先简单了解一些基础概念(这里只有一部分)。

2.1 三维坐标系

即xyz三个相互垂直的轴线组成的坐标系。

2.2 网格

用于表示三维模型的表面,一般情况下一网格由众多三角形或者四边形组成

2.3 材质

用于确定三维模型表面的物理特性和视觉效果,通过使用不同的材质可以展示出丰富的模型效果

2.4 光照

通过设置不同的光源以及光源位置,形成各种光照效果,配合模型材质,决定场景中各物体的亮度,阴影和反射效果等,不设置光源相当于处于暗室,所有物体都为黑色(除部分不受光照影响的特殊材质外)

2.5 渲染

将三维模型转换为图像,使人可以在二维屏幕上看到三维物体的真实效果。

3. 简易示例

接下来使用three.js一步一步实现一个简单示例:

3.1 创建基础场景

// 创建场景
const scene = new THREE.Scene();
// 创建相机,下列参数分别为:相机垂直视角角度,相机长宽比,近截面和远截面
// 通过这些参数形成一个 视锥体*
const camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.1, 2000 );
// 设定相机位置并使相机看向原点
camera.position.set( 200, 200, 200 );
camera.lookAt( 0, 0, 0 );
// 创建渲染器并设置渲染尺寸
const renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
// 将渲染器的dom元素放入页面中,这个dom元素也就是渲染器用于显示场景内容的canvas
document.body.appendChild( renderer.domElement );

// 最后渲染器需要执行渲染方法来将模型真正的渲染出来
// 参数分别为场景示例和相机实例
renderer.render( scene, camera );
// 需要注意的是,该方法每次执行时都会重新渲染当内容
// 如果需要做动画效果或平滑过渡,则需要循环调用渲染方法

// 那么!
// 此时推荐使用 requestAnimationFrame*
// 创建一个渲染循环函数 
function randerLoop(){
  requestAnimationFrame( randerLoop )
  // 调用渲染器渲染方法
  renderer.render( scene, camera );
}
// 执行渲染循环函数
randerLoop()
// requestAnimationFrame会返回一个id,如果需要画面一直保持动态可以不做处理
// 如果需要停止循环,可以创建变量保存这个id,执行cancelAnimationFrame(id)就可以了。

注:

  • 视锥体:

  • requestAnimationFrame:

也许是一种更适合用来实现动画的计时器,相比setInterval,该方法的主要区别在于:

  1. requestAnimationFrame方法只需要传入一个回调函数,该函数会在浏览器进行下一次重绘之前执行。
  2. 该方法每秒执行的次数取决于浏览器刷新频率(一般为60次每秒)。
  3. 在元素被隐藏或页面不活动时,requestAnimationFrame方法不会进行重绘,意味着更少的性能消耗。

 

完成以上步骤后我们就得到了一个基础场景。

3.2 创建几何体

可以通过调用不同的api创建各种不同几何体,这些几何体包含顶点位置,片面索引等基础信息,不能直接用于展示,他们需要的参数不尽相同,这里列举几种简单几何体。

// 方块,参数即为长宽高
// 可选参数这里先不说
const geometryBox = new THREE.BoxGeometry( 1, 1, 1 );
// 球,参数为半径、水平分段数,垂直分段数。分段数决定了几何体的面数,平面越多几何体越平滑。
// 可选参数这里先不说
const geometryBall = new THREE.SphereGeometry( 15, 32, 16 );
// 圆柱,参数为顶部半径,底部半径,高度,侧面分段数,同样决定了几何体是否圆滑。
// 可选参数这里先不说
const geometryCylinder = new THREE.CylinderGeometry( 5, 5, 20, 32 );

3.3 创建材质

材质将用于描述上述几何体的外观,创建之后只需要调用即可,即便使用不同的渲染器,也不需要重新创建材质。这里列举几种常用材质。

// 基础材质,不受到光照影响的特殊材质
const basicMaterial = new THREE.MeshBasicMaterial( { color: 0xaa77ff } );
// 标准材质,一种基于物理的标准材质,成为许多3D应用的标准。
const standardMaterial = new THREE.MeshStandardMaterial( { color: 0x666666 } );
// Lambert网格材质,非光泽表面材质,没有镜面高光效果,通常用于表现木材或石材等非光滑材料。
const lambertMaterial = new THREE.MeshLambertMaterial( { color: 0xdd0000 } );
// toon材质,一种实现类似卡通一样明暗处理的材质,主要特征为明暗渐变不平滑。
const toonMaterial = new THREE.MeshToonMaterial( { color: 0x00ccff } );

3.4 创建网格

mesh方法接收两个参数,第一个参数为几何体实例,第二个参数为材质实例,得到一个3D对象。

举个例子:

// 这里传入上面创建的方块几何体和基础材质。
const mesh = new THREE.Mesh( geometryBox, basicMaterial );
// 将形成的网格体放入场景
scene.add( mesh );

得到了这样的效果:

上面提到过基础材质并不受到光照影响,所以无论有没有光源都可以正确显示,且没有明暗的区分也没有反射。

此时我们换一种材质:

// 切换为Lambert网格材质
const mesh = new THREE.Mesh( geometryBox, lambertMaterial );
scene.add( mesh );

定义lambert材质时已经将颜色设置为了红色,但模型却一片漆黑,只能看到之前场景设定的深灰色背景,正是因为没有光源,无法模拟光照效果,相当于处于暗室。

3.5 创建光源

three.js提供的光源已经足够日常使用,这里只举一个例子:半球形光源(HemisphereLight),其他的可以参考官方文档。

// 创建半球形光源,模拟光线从天空光线颜色渐变到地面光线颜色
// 需要三个参数:天空中发出光线的颜色,地面发出光线的颜色,光照强度
const light = new THREE.HemisphereLight( 0xffffee, 0x080820, 1.2 );
// 设定光源的位置,这里设定到远点上方略微偏离原点
light.position.set( 20, 100, 60 );
// 将光源加入场景
scene.add( light );

添加光源后,模型就能根据光源和材质进行正确的展示,同时因为光源的x,z轴偏移量不同,我们可以直观的看到模型三个面的颜色都不同。

4. 总结

基于上面提到的内容,我们成功的创建了一个完整的三维模型,并将其渲染在了浏览器上,但three.js的内容远不止于此,我也还在继续学习中,后续如果有更多收获,再整理文档进行分享,这次就到这里吧。

实现上图红色方块的效果所需的完整js代码:

// 创建场景
const scene = new THREE.Scene();
scene.background = new THREE.Color( 0x666666 );
// 创建相机,通过参数形成视锥体
const camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.1, 2000 );
// 设定相机位置
camera.position.set( 200, 200, 200 );
// 使相机看向原点
camera.lookAt( 0, 0, 0 );
// 创建渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
// 将渲染器dom(canvas)加入页面
document.body.appendChild( renderer.domElement );
// 创建光源
const light = new THREE.HemisphereLight( 0xffffee, 0x080820, 1.2 );
light.position.set( 20, 100, 60 );
// 将光源加入场景
scene.add( light );
// 创建Lambert网格材质
const lambertMaterial = new THREE.MeshLambertMaterial( { color: 0xdd0000 } );
// 创建方块
const geometryCube = new THREE.BoxGeometry( 40, 40, 40 );
// 执行mesh方法
const cube = new THREE.Mesh( geometryCube, lambertMaterial );
cube.position.set( 30, 20, 30 );
// 将cube加入场景
scene.add( cube );
// 创建渲染循环,没有动画也可以直接执行渲染方法renderer.render( scene, camera )
function animate() {
	requestAnimationFrame( animate );
	renderer.render( scene, camera );
}	
// 执行渲染循环
animate();