0%

Three.js

Renderer

init scene

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
const canvas = document.createElement('canvas')
const sizes = {
width: this.$refs.container.clientWidth,
height: this.$refs.container.clientHeight
}
canvas.width = sizes.width;
canvas.height = sizes.height;
this.$refs.container.appendChild(canvas);
// 创建3D场景对象Scene
const scene = new THREE.Scene();
console.log(scene);
// Geometry
const geometry = new THREE.SphereGeometry(5, 32, 32);
const material = new THREE.MeshBasicMaterial({color: '#aaa', wireframe:true})
// Mesh
const mesh = new THREE.Mesh(geometry, material)
mesh.position.set(0, 0, -10);
scene.add(mesh)

// 创建相机对象Camera
// 45-75 是透视投影相机建议的视角范围fov
// 视角比例aspect w/h
// 投影近平面
// 投影远平面
const camera = new THREE.PerspectiveCamera(75, sizes.width/sizes.height, 0.1, 1000);
scene.add(camera);

const renderer = new THREE.WebGLRenderer({canvas: canvas});
renderer.render(scene, camera);

Caution! 虽设置canvas尺寸 但canvas初始化renderer时 其尺寸会受到影响 其结果仍使canvas超出父容器 出现滚动条
应使用renderer.setSize

1
2
3
const renderer = new THREE.WebGLRenderer()
renderer.setSize(container.current.clientWidth, container.current.clientHeight)
container.current.appendChild(renderer.domElement)

另外onResize要加防抖 overflow hidden该加还是得加

Controls

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
...
// Add OrbitControls
const controls = new OrbitControls(camera, renderer.domElement)

// controls.update() must be called after any manual changes to the camera's transform
camera.position.set( 0, 20, 100 );
controls.update();

// camera 移动是重复的render过程 需要requestAnimationFrame
function animate() {

requestAnimationFrame( animate );

// required if controls.enableDamping or controls.autoRotate are set to true
controls.update();

renderer.render( scene, camera );
}

Mesh和Geometry

两者都可以rotate, Geometry是”是什么(what)” Mesh是”如何(how)”

Animation

1
2
3
4
5
tick(){
mesh.rotaion.y += 0.05
console.log("tick tack")
requestAnimationFrame(tick)
}

tick 调用频率是帧速率相关的 可以用Date或者THREE.Clock

1
2
3
4
5
6
7
8
9
const clock = new THREE.Clock()
const animate = () => {
const t = clock.getElapsedTime()
group.rotation.y=0.5*Math.PI*t
requestAnimationFrame(animate);
renderer.render(scene, camera);
};

animate();

Issue: 单纯调用gsap无效 需要配合requestAnimationFrame和render方法才能使模型运动生效

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
gsap.to(group.position, {
duration:1,
delay:1,
x:20
})
gsap.to(group.position, {
duration:1,
delay:2,
x:0
})

const animate = () => {
requestAnimationFrame(animate);
renderer.render(scene, camera);
};

animate();

Camera

正交相机

1
2
3
4
5
6
7
8
const camera = new THREE.OrthographicCamera(
-20*container.current.clientWidth/container.current.clientHeight, /*左边界*/
20*container.current.clientWidth/container.current.clientHeight,/*右边界*/
20,/*上边界*/
-20,/*下边界*/
0.1,
1000
)

相机环绕

1
2
3
4
5
6
7
8
9
10
11
12
 // mouse rotate
document.addEventListener('mousemove', (e)=>{
if(!container.current) return
const x = (e.x - container.current.offsetLeft)/container.current.clientWidth - 0.5;
const y = -(e.x - container.current.offsetTop)/container.current.clientHeight + 0.5;
// 圆形轨迹的坐标呈正弦/余弦往复
camera.position.z = Math.cos(x*Math.PI*10)*20
camera.position.x = Math.sin(x*Math.PI*10)*20
// 注意! 相机位置移动时 会从原目标移开 所以重新设置lookAt
camera.lookAt(scene.position)
renderer.render(scene, camera)
})

camera controls

  • OrbitControl
  • DeviceOrientationControls
  • FirstPersionControls
  • FlyControls
  • PointLockControls
  • TrackballControls

debug tool:
dat.gui (out of date) -> lil-gui
control panel
controlkit
Guify
Oui

纹理映射

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
const loader = new THREE.TextureLoader()
const texture = loader.load('./assets/images/cover/cover-6.webp',
(txt)=>{
console.log(txt)
},
()=>{
console.log('loading')
},
(e)=>{
console.error(e)
}
)
// 设置纹理在U轴(水平方向)和V轴(垂直方向)上的重复次数。
texture.repeat.x = 2
texture.repeat.y = 2
// 设置纹理在U轴(S方向)和V轴(T方向)上的包裹模式(wrap mode)。
// THREE.MirroredRepeatWrapping接缝处镜像翻转
texture.wrapS = THREE.MirroredRepeatWrapping
texture.wrapT = THREE.MirroredRepeatWrapping
texture.offset.x = 0.5
// 素材逆时针旋转Π/2
texture.rotation = Math.PI * 0.5
texture.center.x = 1
texture.center.y = 1
// 放大时 线性平滑
texture.magFilter = THREE.LinearFilter
// 缩小时 取相邻两个mip层做线性平滑
texture.minFilter = THREE.LinearMipmapLinearFilter
const cubeMaterial = new THREE.MeshBasicMaterial({
// color: 0xff0000,
// wireframe:true
map: texture
})

const sphereGeometry = new THREE.SphereGeometry(5, 32, 32)
const ball = new THREE.Mesh(sphereGeometry, cubeMaterial)

material properties:

  • transparent true/false
  • opacity 透明度0~1
  • wireframe 显示线框
  • side 材质应用到外侧(THREE.FrontSide)或者内侧(THREE.BackSide)
  • flatshading

matcap

environmentMap and HDRI(High Dynamic Range Imaging 高动态范围成像)

在宏大场景中,计算周围环境在物体上的倒影是巨大的运算负担,于是聪明的图形工作者想到了将环境贴图直接贴在反光物体上的想法

在Three.js中支持将立方体环境的图像映射到物体上 常用HDRI生成立方环境贴图的免费网站:https://polyhaven.com/hdris 原HDRIHeaven

或使用blender工具

3D Text

TextBufferGeometry

facetype.js 转换.ttf字体成json
QQs: 包含中文的字体转换后出现问题,json中以‘字-坐标’作key-value,中文(甚至字母和数字)转换失败在json中显示为方框,于是输入的字无法找到映射。loader导入这样的字体会报 character “xxx” does not exists in font family XXX

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import { FontLoader } from 'three/examples/jsm/loaders/FontLoader.js';
import { TextGeometry } from 'three/examples/jsm/geometries/TextGeometry';

// text
const fontLoader = new FontLoader()
fontLoader.load('./assets/font/Kristen ITC_Regular.json', (font) => {
const textGeometry = new TextGeometry(
'Hello 3D',
{
font,
size: 2,
depth:0.2, /** 原height属性 */
curveSegments: 12,
bevelEnabled: true, /**斜角 */
bevelThickness: 0.3,
bevelSize: 0.2,
bevelOffset: 0,
bevelSegments: 5
}
)

const textMaterial = new THREE.MeshBasicMaterial({ color: 'blue', wireframe:true })
const textMesh = new THREE.Mesh(textGeometry, textMaterial)
//textMesh.position.set(0,0,0)
scene.add(textMesh)
})

```js

3D Text默认position位于文字起点 移动到中心

```js
textGeometry.translate(
- (textGeometry.boundingBox.x - 0.2)*0.5, /** 减去倒角 */
- (textGeometry.boundingBox.y - 0.2)*0.5,
- (textGeometry.boundingBox.z - 0.3)*0.5
)

阴影

粒子

可视化优化

Three.js常见性能问题和内存泄漏

  • 及时dispose释放资源
  • 减少segment
  • 复用geometry实例
  • 优化requestAnimationFrame 降低帧率和简化回调

webGPU

Three.js 支持webGPU,使用webGPU需要浏览器对该功能的支持 见openGL

对于没有原生支持的浏览器可以通过Polyfill实现