一、three.js核心概念
1.1 Scene (场景):包含灯光、相机、物体等其他一切需要交给three.js渲染的首层容器。
1.2 Camera (相机):相当于人眼,场景中的3D物体将以camera视角看到的2维形状进行展示。
常用类型 | 特点 |
OrthographicCamera | 正交投影相机,视线为一组平行线。 |
PerspectiveCamera | 透视投影相机,视线为从相机位置射出的一组同源射线。 |
1.3 Renderer (渲染器):与页面上canvas元素绑定,将scene中的内容绘制到canvas中。
二、three.js重要概念
2.1 物体:物体是由几何(Geometry)和材质(Material)两部分组成的,几何决定物体的形状,材质决定物体的颜色、光学特性等性质,物体在场景中也可以被渲染为点模型(Points)、线模型(Line)和网格模型(Mesh)三种。
绝大多数常见的几何形状,three.js都进行了封装,使用时只需要定义长宽高半径等基本要素即可。
常用的材质如下表所示。
材质 | 特点 | |
点材质(PointsMaterial) | 比较简单,只当需要绘制为点模型时使用,可以定义顶点的color、size等属性 | |
线材质 | LineBasicMaterial | 绘制线模型时使用 |
LineDahedMaterial | 产生虚线效果,可以设置虚线大小、间隔等参数 | |
网格材质 | MeshBasicMaterial | 不受光照影响,没有棱角感 |
MeshLambertMaterial | 会与光照产生漫反射,物体表面分界等地方会产生棱角感 | |
MeshPhongMaterial | 会与光照产生镜面反射,产生高光效果 | |
精灵材质(SpriteMaterial) | 本质上是一个与camara视线垂直的一个平面,当camera转动时,会自动保持垂直 | |
自定义着色器材质(ShaderMaterial) | 使用自定义shader渲染的材质,shader是用GLSL编写的小程序,运行于GPU。如果需要渲染内置Material之外的效果可以考虑使用这个。 |
2.2 光照:对自然界光照的模拟。常用的光照分为环境光、平行光、点光源和聚光灯。
灯光 | 特点 |
环境光(AmbientLight) | 没有光源,环境光RGB成分分别和物体材质颜色RGB成分相乘 |
点光源(PointLight) | 光线从光源位置向四周发散,光照强度会随着距离由近及远按衰减度衰减。 |
平行光(DirectionalLight) | 通过position和target两个参数确定平行光方向, |
聚光灯(SpotLight) | 沿着特定方向逐渐发散的光源,照射范围在空间形成一个圆锥体 |
平行光漫反射简单数学模型:
漫反射光的颜色 = 网格材质颜色 ✖️ 入射光的颜色 ✖️光线入射角的余弦值
阴影的产生:阴影的产生需要进行三部分的设置,即光源的设置(castShadow. shadow.camera)、产生阴影物体的设置(castShadow)、接受阴影物体的设置(receiveShadow)。
2.3 纹理贴图
几何(Geometry)对纹理UV坐标将纹理贴图映射到几何的每个顶点,建立几何顶点和纹理贴图的映射关系,即可将纹理贴图的像素值转为几何顶点的颜色值等属性。
常用纹理贴图
纹理 | 用途 |
颜色贴图(map) | 将贴图的每个像素点的GGB分量分别与该点的RGB分量相乘作为该点的颜色值。 |
凹凸贴图(bumpMap) | 用图片像素的灰度值表示几何表面高低的深度,另外可以配合bumpScale参数作为凹凸高度。 |
法线贴图(normalMap) | 从效果上讲和凹凸贴图的效果类似,但是法线贴图是以每个像素的RGB值影响每个像素片段的曲面法线,改变颜色照亮的方式,并不改变曲面的实际形状。配合normalScale(Vector2)参数使用。 |
透明贴图(alphaMap) | 使用纹理的绿色通道控制表面的透明度 |
三、three.js坐标系
3.1 层级模型: three.js中,可以将若干个对象(物体、灯光或者其他组)放入一个组(Group)中,就形成了层级模型。所有的物体、灯光或者组,都会被包含在scene中,所以scene(场景)则是最外层。
3.2 坐标概念: 在scene中有一个基础的右手系3维坐标系,在scene没有旋转时,屏幕横向向右为x轴,纵向向上为y轴,屏幕向外为z轴。
局部坐标系:当直接给物体或者灯光时,其坐标为本地局部坐标,即以物体所属组的位置为原点的坐标。当物体直接添加到scene中时,其坐标即为世界坐标。
世界坐标系:物体相对于scene坐标原点的位置坐标。
观察坐标系:简单理解就是以camera视野观察到的空间坐标系。
裁剪坐标系:屏幕上可见的片段坐标系。
标准设备坐标系:世界坐标转为观察坐标,需要乘上视图矩阵;观察空间坐标转为裁剪坐标系,需要乘上相机投影矩阵。three.js中提供了一个Vector3.project(camera)方法综合这两步,得到标准设备坐标。
屏幕坐标系:three.js中即为canvas坐标系。
经纬度转为球坐标的计算
function lglt2xyz(lng: number, lat: number, radius: number) {
const theta = (90 + lng) * (Math.PI / 180);
const phi = (90 - lat) * (Math.PI / 180);
return new THREE.Vector3().setFromSpherical( new THREE.Spherical(radius, phi, theta));
}
物体坐标转为屏幕像素坐标
局部坐标->世界坐标->标准设备坐标->屏幕坐标->像素坐标
局部坐标->世界坐标:物体坐标 + group坐标
世界坐标->标准设备坐标: Vector.project(camera)
标准设备坐标->屏幕坐标:((x + 1) / 2, ( 1 - y) / 2 )
屏幕坐标->像素坐标: 分别乘以canvas的宽高像素值。
function toScreenPosition( obj: THREE.Object3D, camera: THREE.Camera, width: number, height: number, ) {
const vector = new THREE.Vector3();
const widthHalf = 0.5 * width;
const heightHalf = 0.5 * height;
obj.updateMatrixWorld();
vector.setFromMatrixPosition(obj.matrixWorld);
vector.project(camera);
vector.x = (vector.x + 1) * widthHalf;
vector.y = -(vector.y - 1) * heightHalf;
return { x: vector.x, y: vector.y, z: vector.z, };
}
四、其他
4.1 轨道控制器(OrbitControls):使相机围绕目标进行轨道运动
function render() {
renderer.render(scene, camera);
}
render();
const controls = new THREE.OrbitControls(camera,renderer.domElement); controls.addEventListener('change', render);
function render() {
renderer.render(scene, camera);
requestAnimationFrame(render);
}
render();
4.2 Tween动画库
const box = document.createElement('div');
box.style.setProperty('background-color', '#008800');
box.style.setProperty('width', '100px');
box.style.setProperty('height', '100px');
document.body.appendChild(box);
function animate(time) {
requestAnimationFrame(animate);
TWEEN.update(time);
}
requestAnimationFrame(animate);
const coords = {x: 0, y: 0};
const tween = new TWEEN.Tween(coords).to({x: 300, y: 200}, 1000).easing(TWEEN.Easing.Quadratic.Out).onUpdate(() => {
box.style.setProperty('transform', `translate(${coords.x}px, ${coords.y}px)`)
});
tween.start()