Three.js 几个重要概念
首先我们简单地了解一下一个3D场景必不可少的三个元素,在后面的章节再对各个元素中再做比较详细的介绍。

Scene
场景,可以把scene看作一个沙盘,你所要呈现的物体需要把它摆放在场景中。
Camera
摄像机,最终呈现在屏幕上的影像,实际就是站在摄像机的角度去看场景中的物体,可以把它看作是我们的眼睛。
Renderer
渲染器,我们有了一个场景,并且通过摄像机捕获到了这个画面,如何将这个画面呈现到屏幕,这就是渲染器的工作了。形象一点说,比如我看到一个物体,把它画出来给你看,这个画出来就相当于一个渲染的过程。
坐标系
在three.js 中使用的是右手坐标系,何谓右手坐标系
 在场景中摆放物品的位置、设置摄像机的位置等一切与位置有关的,都要参考这个坐标系。
创建一个简单的3D场景
1 引入Three.js
1.首先在项目中安装依赖
npm install --save three
2.在你需要创建3D场景的文件中引入
import * as THREE from 'three'
这个引入不是一劳永逸的,有一些组件需要特殊引入,我们在用到的时候再说
2.初始化
SimpleScene.js (接下来所有代码都写在这个文件)
import React, { Component } from 'react'
import * as THREE from 'three'
export default class SimpleScene extends Component {
componentDidMount(){
this.init()
}
init = () => {
const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 1000)
const renderer = new THREE.WebGLRenderer()
renderer.setSize(window.innerWidth, window.innerHeight)
renderer.render(scene, camera)
document.getElementById('stage').appendChild(this.renderer.domElement)
}
render() {
return (
<div id="stage">
</div>
)
}
}
首先说明一下初始化中我们做了哪些工作: 1.创建一个场景实例 scene,它是我们后面放置物体的空间 2.创建一个透视摄像机实例 camera,不同摄像机特性有所不同,3D场景中比较常用的就是透视摄像机,四个参数分别为:
- fov(视野角度): 摄像机的视野角度,在相同的距离下,视野角度越大,看见的东西越多,物体在显示器上就越小
- aspect(宽高比): 渲染窗口的宽高比
- near(近截面距离): 在摄像机与近截面直接的物体不会被渲染,也就是不可见
- far(远截面距离): 远截面后的物体不会被渲染
 3.创建一个渲染器实例,并设置宽高,这里的宽高指的是显示区域的大小,我们这里使它填充整个屏幕,然后设置它要渲染的场景和摄像机,最后将渲染器放入页面的容器中 至此,我们完成了一个3D场景必需三元素的初始化,运行程序
 我们就得到了一片黑暗的空间,接下来我们开始往里面放东西
放置网格模型
网格模型,也就前面所说的物体,一个网格模型由几何体和材质组成
const geometry = new THREE.BoxGeometry(1,1,1)
const material = new THREE.MeshBasicMaterial({color:'red'})
const mesh = new THREE.Mesh(geometry, material)
scene.add(mesh)
首先我们创建一个正方体的几何体,这里的几何体可以理解为只是一个骨架,接下来我们要为这个骨架贴上皮,即创建一个材质,这里我们创建的是一个红色的基本材质,最后把骨架和皮组合起来,创建了一个正方体模型,然后就可以把这个模型放进场景中。这时候运行程序,会发现还是一片黑暗。这是因为摄像机默认位置是(0,0,0),模型放置的默认中心位置也是(0,0,0),就是说现在摄像机在模型内部,我们现在把摄像机拉出来
camera.position.z = 3
就可以看到一个红色正方形
 但是这看起来一点都不3D,这是因为我们现在看到的是正方体的正面,调整一下摄像机的位置,并让它看向正方体
camera.position.set(3,3,3)
camera.lookAt(mesh.position)
就可以看到
 有点3D的感觉了,但还是像2D的样子,差了阴影,要产生阴影,就必须有光。
设置灯光
const pointLight = new THREE.PointLight(0xffffff)
pointLight.position.set(3,2,1)
scene.add(pointLight)
为场景添加一个点光源,光的颜色是白色,刷新一下,发现还是这样,没有阴影
 这是材质的问题,MeshBasicMaterial这种材质对光无感,这也是为啥刚才没有设置灯光的情况下,我们仍可以看到这个正方体,现在我们给正方体换一种皮
const material = new THREE.MeshPhongMaterial({color:'red'})
 现在终于有3D的感觉了,画面偏暗,我们可以增加灯光的强度,或者增加一个环境光
const ambientLight = new THREE.AmbientLight(0xcccccc, 0.5)
scene.add(ambientLight)

让正方体动起来
光看个图片也不够3D,下一步我们让正方体转起来
animate = () => {
requestAnimationFrame(this.animate)
this.mesh.rotation.y += 0.02
this.renderer.render(this.scene, this.camera)
}
然后在初始化后调用,就可以看到正方体以y轴为轴心转起来了 ??????
?
requestAnimationFrame(this.animate)
这一行代码的作用是使屏幕刷新时调用括号中的函数,这里的刷新指的是指屏幕刷新率的那个刷新,所以每次执行animate,正方体就会沿y轴旋转0.02个单位,如果电脑的刷新频率是60hz的话,一秒内这个函数执行60次,看起来正方体就转了起来,记得在旋转后调用
this.renderer.render(this.scene, this.camera)
不然只是修改了属性,没有渲染。
控制摄像机
光看着小正方体旋转还是不够3D,接下来我们将控制摄像机,全方位观察小正方体
首先需要引入轨道控制器,有了它我们就可以控制摄像机
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
在初始化的代码中,加上
const controls = new OrbitControls(this.camera, this.renderer.domElement)
controls.addEventListener('change', this.handleControl)
this.controls = controls
第一行,声明这个控制器是控制哪个摄像机,在哪个渲染器节点控制摄像机,第二行给控制器添加监听事件,每当我们使用鼠标拖拽或者滚轮时都会触发回调函数,回调函数相当简单:
handleControl = () => {
this.renderer.render(this.scene, this.camera)
}
这样我们就可以控制摄像机了,来看看效果

?
至此,我们的一个非常简单的3D场景就搭建完毕了。
全部代码
import React, { Component } from 'react'
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
export default class SimpleScene extends Component {
componentDidMount(){
this.init()
this.animate()
}
init = () => {
const scene = new THREE.Scene()
this.scene = scene
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 1000)
this.camera = camera
const geometry = new THREE.BoxGeometry(1,1,1)
const material = new THREE.MeshPhongMaterial({color:'red'})
const mesh = new THREE.Mesh(geometry, material)
this.mesh = mesh
scene.add(mesh)
camera.position.set(3,3,3)
camera.lookAt(mesh.position)
const pointLight = new THREE.PointLight(0xffffff,1)
pointLight.position.set(3,2,1)
scene.add(pointLight)
const ambientLight = new THREE.AmbientLight(0xcccccc, 0.5)
scene.add(ambientLight)
const renderer = new THREE.WebGLRenderer()
renderer.setSize(window.innerWidth, window.innerHeight)
renderer.render(scene, camera)
this.renderer = renderer
document.getElementById('stage').appendChild(renderer.domElement)
const controls = new OrbitControls(this.camera, this.renderer.domElement)
controls.addEventListener('change', this.handleControl)
this.controls = controls
}
handleControl = () => {
this.renderer.render(this.scene, this.camera)
}
animate = () => {
requestAnimationFrame(this.animate)
this.mesh.rotation.y += 0.02
this.renderer.render(this.scene, this.camera)
}
render() {
return (
<div id="stage">
</div>
)
}
}
|