安装
在项目文件夹中打开终端并执行以下命令
npm install --save three
将 three.js 导入项目,导入模块有两种形式:
// 导入整个 three 核心库
import * as THREE from 'three'
// 仅导入所需部分模块
import { Scene } from 'three'
基本用法
three.js 创建的每个 canvas 都包含以下四种类型元素:
- 场景 Scene
- 物体 Objects
- 摄像机 Camera
- 渲染 Renderer
创建一个场景
const scene = new THREE.Scene();
添加物体
在 three.js 中物体也称之为网格(Mesh),每个网格必须包含两个属性,分别是形状和材质。
添加物体的基本步骤
- 创建模型
- 添加材质
- 添加物体网格
- 将物体加入场景
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshBasicMaterial({ color: 0xff0000 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
添加摄像机
在 three.js 中包含了不同种类的摄像机类型,具体类型可参考下方链接的官方文档
const aspect = {
width: window.innerWidth,
height: window.innerHeight,
};
const camera = new THREE.PerspectiveCamera(75, aspect.width / aspect.height, 0.1, 100);
camera.position.x = 1;
camera.position.z = 3;
scene.add(camera);
渲染场景
现在,如果将之前写好的代码复制到HTML文件中,你不会在页面中看到任何东西。这是因为我们还没有对它进行真正的渲染。为此,我们需要使用一个被叫做“渲染循环”(render loop)或者“动画循环”
渲染场景的基本步骤
- 获取 DOM 元素
- 添加渲染器
- 设置渲染尺寸
- 渲染图像
const canvas = document.querySelector('canvas'); // 获取 DOM 元素
const renderer = new THREE.WebGLRenderer({ canvas }); // 添加 WebGLRenderer
renderer.setPixelRatio(window.devicePixelRatio); // 设置像素比例,针对视网膜屏
renderer.setSize(aspect.width, aspect.height); // 设置渲染尺寸
renderer.render(scene, camera); // 渲染图像
在这里我们创建了一个使渲染器能够在每次屏幕刷新时对场景进行绘制的循环(在大多数屏幕上,刷新率一般是60次/秒)。如果你是一个浏览器游戏开发的新手,你或许会说“为什么我们不直接用setInterval来实现刷新的功能呢?”当然啦,我们的确可以用setInterval,但是,requestAnimationFrame有很多的优点。最重要的一点或许就是当用户切换到其它的标签页时,它会暂停,因此不会浪费用户宝贵的处理器资源,也不会损耗电池的使用寿命。
完整代码
// 创建场景
const scene = new THREE.Scene();
// 添加物体
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshBasicMaterial({ color: 0xff0000 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
// 添加摄像机
const aspect = {
width: window.innerWidth,
height: window.innerHeight,
};
const camera = new THREE.PerspectiveCamera(75, aspect.width / aspect.height, 0.1, 100);
camera.position.x = 1;
camera.position.z = 3;
scene.add(camera);
// 渲染场景
const canvas = document.querySelector('canvas');
const renderer = new THREE.WebGLRenderer({ canvas });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(aspect.width, aspect.height);
renderer.render(scene, camera);
// 窗口变化后更新设置
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.render(scene, camera);
});
常用操作
变形 Transform
通过对物体中的属性设置实现变形效果。可参考 Object3D 中提供的属性或方法。所有继承自 Object3D 的元素都包含变形属性。
常用的变形属性
- position 设置位置坐标 (局部位置)
- scale 缩放 (局部缩放)
- rotation 旋转 (局部旋转)
- quaternion 旋转的四元数
// 修改物体的位置坐标
mesh.position.x = 1
mesh.position.y = 1
mesh.position.z = 3
// 修改物体缩放比例
mesh.scale.x = 1
mesh.scale.y = 2
mesh.scale.z = 3
// 修改物体旋转角度,旋转180度则是 3.14
mesh.rotation.x = Math.PI * 0.25 // 45deg
mesh.rotation.y = Math.PI // 180deg
mesh.rotation.z = Math.PI * 1.25 // 225deg
同时,也可使用set方法来修改对应的属性
mesh.position.set( 1, 1, 3 )
mesh.scale.set( 1, 2, 3 )
const a = new THREE.Euler( 0, 1, 1.57, 'XYZ' )
const b = new THREE.Vector3( 1, 0, 1 )
b.applyEuler( a )
创建组合
可将多个物体组合在同一个组中,修改此组的属性即可统一修改组中所有元素的变换
// 创建组合
const group = new THREE.Group()
// 创建第一个物体
const geometry1 = new THREE.BoxGeometry( 1, 1, 1 )
const material1 = new THREE.MeshBasicMaterial({ color: "purple" })
const mesh1 = new THREE.Mesh( geometry, material )
mesh1.position.z = 1
// 创建第二个物体
const geometry2 = new THREE.BoxGeometry( 1, 1, 1 )
const material2 = new THREE.MeshBasicMaterial({ color: "green" })
const mesh2 = new THREE.Mesh( geometry, material )
mesh2.position.y = 2
group.add( mesh1, mesh2 )
group.position.x = 1
scene.add( group )
创建父元素
const geometry = new THREE.BoxGeometry( 1, 1, 1 )
const material = new THREE.MeshBasicMaterial({ color: "purple" })
const parentMesh = new THREE.Mesh( geometry, material )
const childMesh = new THREE.Mesh( geometry, material )
childMesh.position.set( -3, 0, 0 )
childMesh.position.set( 3, 0, 0 )
parentMesh.add( childMesh )
scene.add( parentMesh )
导入模型
在网络中的 3d 模型,推荐使用 glTF 格式,.glb 和 .gltf 是这种格式的这两种不同版本, 都可以被很好地支持。由于glTF这种格式是专注于在程序运行时呈现三维物体的,所以它的传输效率非常高,且加载速度非常快。该文件以 JSON (.gltf) 格式或二进制 (.glb) 格式提供,外部文件存储贴图 (.jpg, .png) 和额外的二进制数据 (.bin) 。一个 glTF 组件可传输一个或多个场景,包括网格、材质、纹理、蒙皮、骨骼、变形目标、动画、灯光以及摄像机。
首先加载 GLTFLoader 模块,同时需要确保包含 three.module.js 文件,并确保 GLTFLoader.js 文件里指向的 three.module.js 地址正确。
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
const loader = new GLTFLoader()
loader.load('./rocket/rocket-gltf.glb', (gltf) => {
scene.add(gltf.scene);
});
遍历元素
gltfLoader.load('./assets/models/gltf/LittlestTokyo.glb', (gltf) => {
model = gltf.scene;
model.traverse((obj) => {
if (obj.isMesh) {
obj.castShadow = true;
}
});
scene.add(model);
});
如果模型有被压缩,需使用一个解压工具,draco提供更大的压缩率,减小文件大小。
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
const dracoLoader = new DRACOLoader();
// 映射的路径需为网站的相对路径
dracoLoader.setDecoderPath('/assets/draco/gltf/');
const gltfLoader = new GLTFLoader();
gltfLoader.setDRACOLoader(dracoLoader);
gltfLoader.load('./rocket/rocket-gltf.glb', (gltf) => {
scene.add(gltf.scene);
} )
提取模型中的动画
let mixer;
const clock = new THREE.Clock();
const loader = new GLTFLoader();
loader.load('./assets/gltf/Soldier.glb', (gltf) => {
scene.add(gltf.scene);
const clip = gltf.animations[0];
mixer = new THREE.AnimationMixer(gltf.scene);
const action = mixer.clipAction(clip);
action.play();
});
const animate = () => {
requestAnimationFrame(animate);
const delta = clock.getDelta();
mixer?.update(delta);
renderer.render(scene, camera);
};
animate();
动画
canvas 中动画的实现原理同视频的原理一致,即每秒以不同状态的多张图片切换显示实现视觉上的流畅,在 three.js 即是每秒渲染多张图片以序列方式显示。
通过 window 提供的方法 requestAnimationFrame() 来实现重复请求,该桢率取决于设备刷新的帧率。
渲染动画
- 设置动画刷新帧率
- 设置动画属性
- 渲染场景
const animate = () => {
mesh.rotation.x += 0.01
mesh.rotation.y += 0.01
renderer.render( scene, camera )
requestAnimationFrame( animate ) // 每桢执行一次 animate 函数
}
animate()
以上代码通过每帧执行一次函数,更新物体的旋转值并重新渲染图像来实现动画的播放,但这样的方法有一个问题,即设备的帧率不同可能会导致动画播放的速度也不相同,如60帧的设备与120帧的设备对比,60帧设备每秒会旋转 0.01 * 60 个值,而120帧设备每秒会旋转 0.01 * 120 个值。
可通过 three.js 提供的 Clock() 方法来配置。
const clock = new.THREE.Clock()
const animate = () => {
const elapsedTime = clock.getElapsedTime()
mesh.rotation.x = elapsedTime * Math.PI // 每秒转半圈
mesh.rotation.y = elapsedTime
renderer.render( scene, camera )
requestAnimationFrame( animate ) // 每桢执行一次 animate 函数
}
animate()
补间动画
VectorKeyframeTrack
AnimationClip
const clock = new THREE.Clock();
const positionKF = new THREE.VectorKeyframeTrack(
'.position',
[0, 1, 2],
[
0, 0, 0,
10, 10, 0,
0, 0, 0,
],
);
const clip = new THREE.AnimationClip(
'Action',
4,
[positionKF],
);
const mixer = new THREE.AnimationMixer(cube);
const clipAction = mixer.clipAction(clip);
clipAction.play();
const animate = () => {
requestAnimationFrame(animate);
const delta = clock.getDelta();
mixer.update(delta);
renderer.render(scene, camera);
};
animate();
使用 GSAP
gsap.to(mesh.position, {x: 2});
const animate = () => {
renderer.render( scene, camera )
requestAnimationFrame( animate ) // 每桢执行一次 animate 函数
}
animate()
交互
Bilibili 视频教程:20-光线投射实现3D场景交互事件
光线投射用于进行鼠标拾取(在三维空间中计算出鼠标移过了什么物体从而可实现对三维空间中物体的交互)。
const raycaster = new THREE.Raycaster();
// 创建鼠标向量
const mouse = new THREE.Vector2();
window.addEventsListener( 'click', ( event ) => {
// 转换数值
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
// 通过摄像机和鼠标位置更新射线
raycaster.setFromCamera(mouse, camera);
// 计算物体与射线的焦点
const intersects = raycaster.intersectObjects([mesh1, mesh2, mesh3])
if (intersects.length) {
intersects.forEach((item) => console.log(item));
}
} )
设置响应式画布
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight; // 重设相机比例
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight); // 重设渲染尺寸
renderer.render(scene, camera); // 重新绘制
});
创建矩阵
const amount = 10;
const count = amount ** 3;
const whiteColor = new THREE.Color().setHex(0xffffff);
const geometry = new THREE.IcosahedronGeometry(0.5, 2);
const material = new THREE.MeshPhongMaterial({ color: 0xffffff });
const meshes = new THREE.InstancedMesh(geometry, material, count);
let index = 0;
const offset = (amount - 1) / 2;
const matrix = new THREE.Matrix4();
for (let i = 0; i < amount; i++) {
for (let j = 0; j < amount; j++) {
for (let k = 0; k < amount; k++) {
matrix.setPosition(offset - i, offset - j, offset - k);
meshes.setMatrixAt(index, matrix);
meshes.setColorAt(index, whiteColor);
index++;
}
}
}
scene.add(meshes);
场景 Scene
background 设置场景的背景,如不设置则为透明
scene.background = new THREE.Color(0x999999);
CubeTexture
纹理盒子,类似于 HDR 的显示方式,场景会显示一个六边形盒子,其中包含了6个贴图 nx (负X),ny (负Y),nz (负Z),px (正X),ny (正Y),nz (正Z),作为场景的背景,可以将场景纹理映射给材质,也可为物体提供照明。
const urls = [
'./assets/textures/cube/pisa/px.png',
'./assets/textures/cube/pisa/nx.png',
'./assets/textures/cube/pisa/py.png',
'./assets/textures/cube/pisa/ny.png',
'./assets/textures/cube/pisa/pz.png',
'./assets/textures/cube/pisa/nz.png',
];
const environmentMap= new THREE.CubeTextureLoader().load(urls);
scene.background = environmentMap;
// 映射到网格
const material = new THREE.MeshBasicMaterial({ color: 0xffffff, envMap: textureCube });
几何体 Geometry
BufferGeometry()
Bilibili 视频教程:11-几何体,顶点,索引,面之BufferGeometry
最基础的几何体类型,在three.js中提供的所有其他几何体都是继承该基础集合体而创建。
是面片、线或点几何体的有效表述。包括顶点位置,面片索引、法相量、颜色值、UV 坐标和自定义缓存属性值。使用 BufferGeometry 可以有效减少向 GPU 传输上述数据所需的开销。
// 创建一个四边形面
const plane = new THREE.BufferGeometry()
const vertices = new Float32Array( [
-1, -1, 0,
1, -1, 0,
1, 1, 0,
-1, 1, 0,
] )
const indices = new Unit16Array( [ 0, 1, 2, 2, 3, 0 ] ) // 通过顶点索引的方式来使不同面共用顶点
plane.setAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) )
plane.setIndex( new THREE.BufferAttribute( indices, 1 ) )
const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } )
scene.add( plane )
常用普通几何体
- BoxGeometry 立方体
- CapsuleGeometry 胶囊
- CircleGeometry 圆形
- ConeGeometry 圆锥
- CylinderGeometry 圆柱
- PlaneGeometry 平面
- SphereGeometry 圆球
- TorusGeometry 圆环
- ExtrudeGeometry 挤压
属性与方法
dispose() 释放内存
材质 Materials
材质类型
MeshBasicMaterial
基础网格材质,该材质不受光影影响
MeshNormalMaterial
法线网格材质,一种把法向量映射到RGB颜色的材质。
MeshDepthMaterial
深度网格材质,按深度绘制几何体的材质。深度基于相机远近平面。白色最近,黑色最远
MeshMatcapMaterial
由一个材质捕捉(MatCap,或光照球(Lit Sphere))纹理所定义,其编码了材质的颜色与明暗基于法线来模拟材质
https://github.com/nidorx/matcaps
const material = new THREE.MeshMatcapMaterial();
material.matcap = matcapTexture;
以下材质需要结合灯光使用:
MeshLambertMaterial
漫射材质,非光泽表面的材质,没有镜面高光
MeshPhongMaterial
镜面材质,用于具有镜面高光的光泽表面的材质,调整 shininess 数值可设置光泽度,默认为30,specular可设置高光色
- shininess
Number
光泽度 - specular
Color
高光色
MeshToonMaterial
卡通材质
// 自定义过渡色纹理
const gradientTexture = new THREE.TextureLoader().load('./gradient.jpg');
gradientTexture.minFilter = THREE.NearestFilter;
gradientTexture.magFilter = THREE.NearestFilter;
const material = new THREE.MeshToonMaterial();
material.gradientMap = gradientTexture;
MeshStandardMaterial (重要)
标准网格材质,一种基于物理的标准材质,常用属性 metalness, roughness
MeshPhysicalMaterial
物理网格材质类似标准网格材质,但增加了清漆涂层
PointMaterial
粒子材质
ShadowMaterial
用于接收投影的材质,如果没有投影不会显示
ShaderMaterial
RawShaderMaterial
// MeshMatcapMaterial
const textureLoader = new THREE.TextureLoader()
const matcapTexture = textureLoader.load( "/texture/matcap.jpg" )
const material = new THREE.MeshMatcapMaterial()
material.matcap = matcapTexture
常用的材质属性
- color
color
颜色 - wireframe
boolean
线框 - transparent
boolean
透明 - opacity
number
不透明度 0 ~ 1之间的数值,需将 transparent 设置为 true - side 设置材质的面向 material.side = THREE.DoubleSide。还有另外两个选项 FrontSide, BackSide
- visible
boolean
是否可见 - flatShading
boolean
每个平面将用扁平的颜色来着色
常用的贴图属性
- map 颜色贴图
- alphaMap 透明贴图
- aoMap AO贴图 ( aoMapIntensity 设置强度 )
- lightMap 光照贴图 ( lightMapIntensity 设置强度 )
- specularMap 高光贴图
- envMap 环境贴图
属性与方法
dispose() 释放内存
网格 Mesh
Mesh
const mesh = new THREE.Mesh( geometry, material );
InstancedMesh
纹理 Textures
Bilibili 视频教程:14-基础材质,贴图,高光,透明,环境,光照
定义纹理贴图的映射模式。
const texture = new.THREE.TextureLoader().load('/texture/color.jpg');
const material = new.THREE.MeshBasicMaterial({ map: texture })
// 可以使用属性赋值方式
material.map = texture;
导入多个纹理素材
const textureLoader = new.THREE.TextureLoader();
const colorTexture = textureLoader.load('/texture/color.jpg');
const aoTexture = textureLoader.load('/texture/ao.jpg');
const material = new.THREE.MeshBasicMaterial({
map: colorTexture,
aoMap: aoTexture,
});
原生 JS 导入(不推荐)
const image = new Image();
image.onload = () => {
const texture = new THREE.Texture(image);
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshBasicMaterial({ color: 0xff0000 });
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
}
纹理类型
- 色彩纹理 Color 应用在物体上的颜色
map
- Alpha 纹理 Alpha 在物体上实现镂空效果,黑透白不透
alphaMap
- 置换纹理 Displacement ,也叫高度 Height,可以对物体的顶点进行调整,白色表示上升,黑色下降,前提是有足够的细分
displacementMap
- 法线贴图 Normal 给物体增加更多细节,主要与照明有关,不受细分数的影响
normalMap
- 环境遮蔽 Ambient Occlusion 添加伪阴影,如夹缝,黑色表示添加的部分,白色则不添加
aoMap
- 金属度 Metalness,白色为金属,黑色非金属,用于创建放射效果
metalnessMap
- 粗糙度 Roughness,白色粗糙,黑色光滑
roughnessMap
- 环境贴图, 可用于制作反射或折射,也可以用于网格的照明。可使用 cubeTexture
envMap
https://matheowis.github.io/HDRI-to-CubeMap
属性与方法
- repeat
- wrapS
- wrapT
- offset
- rotation
- center 设置变形中心点
- minFilter
- magFilter
* Mipmapping
* 色彩空间
// SRGB
texture.colorSpace = THREE.SRGBColorSpace
// 线性(默认)
texture.colorSpace = THREE.linearColorSpace
灯光 Light
类型
→ AmbientLight 环境光
环境光会均匀的照亮场景中的所有物体。环境光不能用来投射阴影,因为它没有方向。
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
scene.add(ambientLight);
→ DirectionalLight 平行光
平行光是沿着特定方向发射的光。这种光的表现像是无限远,从它发出的光线都是平行的。常常用平行光来模拟太阳光 的效果; 太阳足够远,因此我们可以认为太阳的位置是无限远,所以我们认为从太阳发出的光线也都是平行的。定义平行光的位置,只需确认平行光的方向,与远近无关。
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
scene.add(directionalLight);
→ HemisphereLight 半球光
光源直接放置于场景之上,光照颜色从天空光线颜色渐变到地面光线颜色。
const light = new THREE.HemisphereLight(0xffffff, 0x888888);
→ PointLight 点光源
从一个点向各个方向发射的光源。一个常见的例子是模拟一个灯泡发出的光。
// 接收四个参数 颜色,强度,距离,衰减
const light = new THREE.PointLight( 0xff0000, 1, 100 );
light.position.set( 50, 50, 50 );
scene.add( light );
→ RectAreaLight 平面光光源
从一个矩形平面上均匀地发射光线。这种光源可以用来模拟像明亮的窗户或者条状灯光光源。
const rectLightHelper = new RectAreaLightHelper( rectLight );
scene.add( rectLightHelper );
→ SpotLight 聚光灯
光线从一个点沿一个方向射出,随着光线照射的变远,光线圆锥体的尺寸也逐渐增大。
const spotLight = new THREE.SpotLight(0xffffff, 1);
scene.add(spotLight);
属性与方法
position 位置
light.position.set(0, 1, 0);
intensity 强度
light.intensity = 1;
性能
在场景中,灯光的使用对于性能消耗较大。以下按照性能消耗从低到高排列
低消耗:
- AmbientLight
- HemisphereLight
中等消耗:
- DirectionalLight
- PointLight
高消耗:
- SpotLight
- ReacAreaLight
投影 Shadow
要在环境中产生投影,需进行一系列的配置
cylinder.castShadow = true; // 产生投影的物体
plane.receiveShadow = true; // 接收投影的物体
spotLight.castShadow = true; // 灯光
renderer.shadowMap.enabled = true; // 渲染
// 优化投影
// 设置投影贴图尺寸,可以结合 mipmapping
spotLight.shadow.mapSize.width = 1024 * 2;
spotLight.shadow.mapSize.height = 1024 * 2;
// 优化精度,设置 near far
spotLight.shadow.camera.near = 1;
spotLight.shadow.camera.far = 6;
// 模糊
spotLight.shadow.radius = 10;
支持投影的灯光类型
- PointLight
- DirectionalLight
- SpotLight
投影类型
// 修改投影类型
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
BasicShadowMap
性能更高,但会损失质量
PCFShadowMap
默认类型,性能低,但边缘模糊
PCFSoftShadowMap
性能低,比默认类型效果更好,radius属性失效。
VSMShadowMap
性能低,质量高
摄像机 Camera
摄像机的抽象基类。在构建新摄像机时,应始终继承此类。
类型
PerspectiveCamera 透视相机
这一摄像机使用perspective projection(透视投影)来进行投影。
PerspectiveCamera( fov : Number, aspect : Number, near : Number, far : Number )
const camera = new THREE.PerspectiveCamera( 45, width / height, 1, 1000 )
scene.add( camera )
- fov —— 摄像机视锥体垂直视野角度
- aspect —— 摄像机视锥体长宽比
- near —— 摄像机视锥体近端面
- far —— 摄像机视锥体远端面
OrthographicCamera
这一摄像机使用orthographic projection(正交投影)来进行投影。没有透视。
OrthographicCamera(left, right, top, bottom, near, far );
const camera = new THREE.OrthographicCamera( width / - 2, width / 2, height / 2, height / - 2, 1, 1000 );
scene.add( camera );
StereoCamera
双透视摄像机(立体相机)常被用于创建3D Anaglyph(3D立体影像) 或者Parallax Barrier(视差屏障)。
CubeCamera
创建6个渲染到WebGLCubeRenderTarget的摄像机。
ArrayCamera
摄像机阵列 ArrayCamera 用于更加高效地使用一组已经预定义的摄像机来渲染一个场景。这将能够更好地提升VR场景的渲染性能。
一个 ArrayCamera 的实例中总是包含着一组子摄像机,应当为每一个子摄像机定义viewport(视口)这个属性,这一属性决定了由该子摄像机所渲染的视口区域的大小。
方法
position
camera.position.x = 1;
camera.position.z = 3;
lookAt()
http://www.yanhuangxueyuan.com/doc/three.js/lookat.html
设置摄像机对准的坐标
// 将摄像机对准到场景中心
camera.lookAt( scene.position )
渲染器 Renderer
类型
WebGLRenderer
WebGL1Renderer
WebGLRenderTarget
WebGLCubeRenderTarget
WebGLMultipleRenderTarget
方法
setSize()
renderer.setSize(w, h);
*tongMapping(色调映射)
- THREE.NoToneMapping
- THREE.LinearToneMapping
- THREE.ReinhardToneMapping
- THREE.CineonToneMapping
- THREE.ACESFilmicToneMapping
雾 Fog
// 线性雾
scene.fog = new THREE.Fog( 0xcccccc, 10, 15 ) // 颜色,开始距离,消失距离
// 指数雾
scene.fog = new THREE.FogExp2( 0xcccccc, 0.1 ) // 颜色与密度
粒子 Particles
基础形状
const particlesGeometry = new THREE.SphereGeometry(1, 32, 32);
const particlesMaterial = new THREE.PointsMaterial({
size: 0.02,
});
const particles = new THREE.Points(particlesGeometry,particlesMaterial);
scene.add(particles);
随机
const particlesGeometry = new THREE.BufferGeometry();
const verticesAmout = 1000;
const positionArray = new Float32Array(verticesAmout * 3);
const colorArray = new Float32Array(verticesAmout * 3);
for (let i = 0; i < verticesAmout * 3; i++) {
positionArray[i] = (Math.random() - 0.5) * 10;
colorArray[i] = Math.random();
}
particlesGeometry.setAttribute('position', new THREE.BufferAttribute(positionArray, 3));
particlesGeometry.setAttribute('color', new THREE.BufferAttribute(colorArray, 3));
const particlesMaterial = new THREE.PointsMaterial({
size: 0.02,
});
particlesMaterial.vertexColors = true;
const particles = new THREE.Points(particlesGeometry, particlesMaterial);
scene.add(particles);
Points 粒子
类似与网格,粒子专用。
PointsMaterial 粒子材质
当使用透明通道纹理时,偶尔遇见透明背景失效,
解决方案1: alphaTest
particlesMaterial.alphaTest = 0.01;
解决方案2:depthTest
如果场景中有其他对象或粒子有不同的颜色,停用可能会导致错误。因为物体将不会遮挡后面的物体
particlesMaterial.depthTest= false;
解决方案3:depthWrite(最佳)
particlesMaterial.depthWrite= false;
解决方案4:blending
性能消耗最高,类似photoshop中的混合模式
particlesMaterial.blending= THREE.AdditiveBlending;
物理 Physics
3D库
- ammo.js
- cannon.js
- oimo.js
2D库
- Matter.js
- P2.js
- Planck.js
- Box2D.js
Cannon
npm install cannon --save
import CANNON from 'cannon';
const clock = new THREE.Clock();
const world = new CANNON.World();
world.gravity.set(0, -9.82, 0);
const sphereShape = new CANNON.Sphere(0.5);
const sphereBody = new CANNON.Body({
mass: 1,
position: new CANNON.Vec3(0, 3, 0),
shape: sphereShape,
});
world.addBody(sphereBody);
const sphere = new THREE.Mesh(
new THREE.SphereGeometry(0.5, 32, 32),
new THREE.MeshPhongMaterial(),
);
scene.add(plane, sphere);
const animate = () => {
const deltaTime = clock.getDelta();
world.step(1 / 60, deltaTime, 3);
sphere.position.copy(sphereBody.position);
renderer.render(scene, camera);
requestAnimationFrame(animate);
};
animate();
gravity 重力 vec3
环境纹理
参考资料:https://blog.csdn.net/moxiaomomo/article/details/107612292
PMREMGenerator
资源加载
TextureLoader 加载纹理
TextureLoader.load()支持设置4个参数,第一个是文件路径,后面三个都是回调函数,分别是加载完成,加载进度与加载错误
RGBELoader 加载 hdr
const rgbeLoader = newRGBELoader();
rgbeLoader.load( 'env.hdr', ( envMap ) => {
// 设置球形映射
envMap.mapping = THREE.EquirectangularReflectionMapping
// 设置背景
scene.background = envMap
// 设置环境
scene.environment = envMap
// 设置几何体的环境贴图
meshMaterial.envMap = envMap
} )
GLTFLoader GLTF加载器
加载模型
LoadingManager 加载管理
监听所有资源的加载情况,包括纹理、模型、字体等。
- onStart 开始加载
- onProgress 加载中
- onLoad 加载完成
- onError 错误
const loadingManager = new THREE.LoadingManager();
const textureLoader = new THREE.TextureLoader(loadingManager);
loadingManager.onStart = () => {}
曲线 Curve
CatmullRomCurve3
const closedSpline = new THREE.CatmullRomCurve3([
new THREE.Vector3(-60, -100, 60),
new THREE.Vector3(-60, 20, 60),
new THREE.Vector3(-60, 120, 60),
new THREE.Vector3(-60, 20, -60),
new THREE.Vector3(60, 20, -60),
]);
closedSpline.curveType = 'catmullrom';
closedSpline.closed = true;
辅助功能
调试工具
Bilibili 视频教程:10-应用 lil-GUI 调试开发3D效果
import { GUI } from 'three/examples/jsm/libs/lil-gui.module.min';
const gui = new GUI();
const spotLightFoler = gui.addFolder('SpotLight');
spotLightFoler.add(spotLight.position, 'x', -200, 200, 1).name('Postion X');
spotLightFoler.add(spotLight, 'angle', 0, Math.PI).onChange(() => {
animation();
spotlightHelper.update();
});
spotLightFoler.addColor(spotLight, 'color').onChange(() => {
animation();
});
// 布尔值
gui.add( mesh, "wireframe").( 'Is Wireframe' )
网格辅助器
const grid = new THREE.GirdHelper(100, 40, 0, 0);
scene.add(grid);
坐标轴辅助器
Udemy 视频教程:2.2 – Transformation
红色表示 X 轴,绿色 Y 轴,蓝色 Z 轴。
const axesHelper = new THREE.AxesHelper(2); // 参数越大,显示的轴长度越长
scene.add(axesHelper);
灯光辅助器
相机辅助器
控制器 Controls
OrbitControls 轨道控制器
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
const controls = new OrbitControls(camera, renderer.domElement);
// 当控制器发生改变后需重新渲染画面
controls.addEventListener('change', animation);
const animation = () => {
renderer.render(scene, camera);
};
// 设置目标点
controls.target.set(0, 1, 0);
controls.update();
TrackballControls 轨迹球控制器
import { TrackballControls } from 'three/examples/jsm/controls/TrackballControls';
const controls = new TrackballControls(camera, renderer.domElement);
const animate = () => {
requestAnimationFrame(animate);
controls.update(); // 需在动画执行时进行更新
renderer.render(scene, camera);
};
animate();
TransformControls 变形控制器
用于物体的变形操作,如位置移动
字体转换
参考链接
中文文档
http://www.yanhuangxueyuan.com/threejs/docs/index.html
Bilibili 视频教程
https://www.bilibili.com/video/BV1g44y1L7np
https://www.bilibili.com/video/BV1p34y1J7Nc/
http://www.yanhuangxueyuan.com/doc/Three.js/removeDispose.html