Three.js基础知识
图
Three.js是一款运行在浏览器中的3D引擎,你可以用它创建各种三维场景,包括了摄影机、光影、材质等各种对象,在数据可视化、Github主页等网站都能看到它的身影,在未来,前端领域少不了3D技术

学习地址

官方网站,可切换中文查看

普通环境搭建

在HTML文件的中引入threejs的CDN即可:BootCDN

可以测试是否引入成功

<script src="https://cdn.bootcdn.net/ajax/libs/three.js/r134/three.min.js"></script>
console.log('当前使用Threejs版本:' + window.__THREE__);

NPM环境搭建

先对某文件夹进行npm init初始化,然后安装parceljs打包工具,命令为npm i parcel,然后创建html入口并指定为打包目录

"scripts": {
  "dev": "parcel src/index.html",
  "build": "parcel build src/index.html"
},

指定样式文件和js入口文件

<link rel="stylesheet" href="./assets/css/style.css">
<script src="./assets/js/main.js" type="module"></script>

环境就搭建好了

安装Three

npm i three

引入
import * as THREE from 'three'

Three基础流程

import * as THREE from 'three'
import {
  OrbitControls
} from 'three/examples/jsm/controls/OrbitControls'

// console.log(THREE);

// 创建场景
const scene = new THREE.Scene()
// 创建透视相机:视野角度、长宽比、近端面、远端面
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
// 调整相机位置
camera.position.set(5, 5, 5)
// 或者 camera.position.x = 5
// 把相机加入场景
scene.add(camera)

// 添加几何体
const geometry = new THREE.BoxGeometry(1, 1, 1);
// 材质
const material = new THREE.MeshBasicMaterial({
  color: 0x4194cd
});
// 根据几何体和材质创建网格
const cube = new THREE.Mesh(geometry, material);
// 设置角度
cube.rotation.set(0.5, 0.5, 0)
// 加入场景
scene.add(cube);

// 初始化渲染器
const renderer = new THREE.WebGLRenderer()
// 渲染的尺寸大小
renderer.setSize(window.innerWidth, window.innerHeight)
// 将内容添加到body
document.body.appendChild(renderer.domElement)
// 使用渲染器将相机、场景进行渲染
// renderer.render(scene, camera)

// 创建轨道控制器:指定相机和渲染器
const controls = new OrbitControls(camera, renderer.domElement)

// 每帧重渲染
function render() {
  renderer.render(scene, camera)
  requestAnimationFrame(render)
}

render()

图

坐标辅助器

能更方便开发时进行查看

// 添加坐标轴辅助器,指定长度
const axesHelper = new THREE.AxesHelper(5)
// 加入场景
scene.add(axesHelper)

轨道控制器

// @ts-ignore
import {OrbitControls} from 'three/examples/jsm/controls/OrbitControls'

// 创建轨道控制器
const controls = new OrbitControls(camera, renderer.domElement)
// 禁止缩放
controls.enableZoom = false
// 禁止旋转
controls.enableRotate = false

物体的位置和移动

// 添加几何体及其大小
const geometry = new THREE.BoxGeometry(1, 1, 1);
// 材质及颜色
const material = new THREE.MeshBasicMaterial({
  color: 0x4194cd
});
// 根据几何体和材质创建网格对象
const cube = new THREE.Mesh(geometry, material);
// 设置物体配置
cube.position.set(0, 0, 0)
// 或者直接设置单一位置
// cube.position.x = 1
// 加入场景
scene.add(cube);


// 每帧重渲染
function render() {

  cube.position.x += 0.01
  if (cube.position.x > 5) {
    cube.position.x = 0
  }

  renderer.render(scene, camera)
  requestAnimationFrame(render)
}

物体缩放和旋转

// 缩放
// cube.scale.set(2, 1, 1)
// 或者
cube.scale.x = 3

// 旋转
cube.rotation.set(Math.PI / 8, 0, 0)
// 一直旋转
cube.rotation.x += 0.01

requestAnimationFrame卡顿问题

requestAnimationFrame是尽可能的接近1帧的时间,但是受到电脑性能的影响,肯能出现跳帧的情况,所以在计算的时候最好使用帧的时间计算动画

// 时钟运行时长
let time = clock.getElapsedTime()
// 下一次获取间隔时间
let deltaTime = clock.getDelta()
console.log('运行时长' + time)
console.log('获取间隔' + deltaTime)

阻尼

解决控制器的晃动问题

// 创建轨道控制器:指定相机和渲染器
const controls = new OrbitControls(camera, renderer.domElement)
// 开启阻尼
controls.enableDamping = true

// 每帧重渲染
function render() {

  controls.update()

  renderer.render(scene, camera)
  requestAnimationFrame(render)
}

平面几何体及可反射光线的Lamber材质

// 平面几何体(指定高度、宽度)
let planeGeometry = new THREE.PlaneGeometry(10, 10)
// 指定为可反射光线的Lamber材质
let planeLambertMaterial = new THREE.MeshLambertMaterial({
  color: 0x888888
})
// 根据几何体和材质创建网格
let plane = new THREE.Mesh(planeGeometry, planeLambertMaterial)
// 设置位置
plane.rotation.x = -0.5 * Math.PI
// 加入场景
scene.add(plane)

AmbientLight光源

该光源应用到整个场景中,没有特殊来源方向,不会产生阴影

// 加入AmbientLight光源,该光源应用到整个场景中,没有特殊来源方向,不会产生阴影
let ambientLight = new THREE.AmbientLight(0xaaaaaa)
// 加入场景
scene.add(ambientLight)

图

聚光灯光源与投影

// 投影光源,聚光灯光源
let spotLight = new THREE.SpotLight(0xffffff)
// 设置聚光灯位置
spotLight.position.set(10, 10, -10)
// 开启阴影
spotLight.castShadow = true
// 设置阴影效果
spotLight.shadow.mapSize = new THREE.Vector2(1024, 1024)
spotLight.shadow.camera.far = 20
spotLight.shadow.camera.near = 10
// 加入场景
scene.add(spotLight)

然后给平面加上接受阴影属性

// 接受阴影
plane.receiveShadow = true

给渲染器加上阴影渲染

// 开启阴影渲染
renderer.shadowMap.enabled = true

给物体加上阴影属性,注意要使用MeshLambertMaterial材质

// 开启阴影
cube.castShadow = true

图

画面自适应和全屏

解决当容器大小变化后无法自适应问题

// 自适应
window.addEventListener('resize', () => {
  // 更新摄像头
  camera.aspect = window.innerWidth / window.innerHeight
  // 更新摄像头投影矩阵
  camera.updateProjectionMatrix()
  // 更新渲染器
  renderer.setSize(window.innerWidth, window.innerHeight)
  // 更新渲染器像素比
  renderer.setPixelRatio(window.devicePixelRatio)
})

全屏

// 全屏(非网页,而是指定元素)
window.addEventListener('dblclick', () => {
  if (document.fullscreenElement) {
    document.exitFullscreen()
  } else {
    renderer.domElement.requestFullscreen()
  }
})

物体移除

scene.remove(cube)

圆环

// 圆环 参数:半径 粗线 分段 圆润 
let torusGeometry = new THREE.TorusGeometry(0.5, 0.2, 70, 100)
// 材质
const torusGeometryMaterial = new THREE.MeshLambertMaterial({
  color: 0xff2288
});
// 根据几何体和材质创建网格
const torus = new THREE.Mesh(torusGeometry, torusGeometryMaterial);
// 设置位置
torus.position.set(3, 1, -2)
// 开启阴影
torus.castShadow = true
// 加入场景
scene.add(torus);

图

柱体

// 柱体 参数 顶部半径 底部半径 高度 分段数 高度分段数 地面是否开放
let cylinderGeometry = new THREE.CylinderGeometry(0.8, 0.8, 1.2, 100)
// 材质
const cylinderGeometryMaterial = new THREE.MeshLambertMaterial({
  color: 0x4194cd
});
// 根据几何体和材质创建网格
const cylinder = new THREE.Mesh(cylinderGeometry, cylinderGeometryMaterial);
// 设置位置
cylinder.position.set(3, 1, -2)
// 开启阴影
cylinder.castShadow = true
// 加入场景
scene.add(cylinder);

图

球体

const geometry = new THREE.SphereGeometry(1, 100, 100);

贴图

let textureLoader = new THREE.TextureLoader()
let texture = textureLoader.load('/zhuan.png')
material.map = texture

图

导入glb模型

import {
  GLTFLoader
} from 'three/examples/jsm/loaders/GLTFLoader'

// 加载模型
let loader = new GLTFLoader()
loader.load('/other.glb', function (gltf) {
  scene.add(gltf.scene)
  gltf.scene.position.set(0, 2, 0)
})

注意加载模型的位置

环境贴图

用户给物体的周围制造环境

// 加入环境贴图
  scene.background = new THREE.CubeTextureLoader().setPath('/').load(['2.png', '2.png', '2.png', '2.png', '2.png', '2.png'])

注意,图片的宽高要一样,如果需要物体根据贴图反光,需要给物体加上envMap

// 材质
const material = new THREE.MeshLambertMaterial({
  color: 0xffffff,
  envMap: scene.background
});