场景Scene
、相机Camera
、渲染器Renderer
初始化完成之后,可以向场景中添加一个简单的模型进行展示。在此之前需要了解三个概念:几何体、材质、网格模型。
- 几何体:表示物体的几何形状。
- 材质:表示物体的外观效果。
- 网格模型:将几何体和材质组合起来,作为three.js的一个基本物体单位,可以添加进场景中。
几何体相关
几何体是指由点、线、面所构成的空间实体。其中,点、线、面是几何体的基本元素,几何体包括球体、立方体、圆柱体、矩形、圆形等。这些几何体都有自己的特定形状和特征,可以应用于各种数学、物理和工程领域。
常见的几何体
javascript
// BoxGeometry:长方体
const geometry = new THREE.BoxGeometry(100, 100, 100);
// SphereGeometry:球体
const geometry = new THREE.SphereGeometry(50);
// CylinderGeometry:圆柱
const geometry = new THREE.CylinderGeometry(50,50,100);
// PlaneGeometry:矩形平面
const geometry = new THREE.PlaneGeometry(100,50);
// CircleGeometry:圆形平面
const geometry = new THREE.CircleGeometry(50);
缓冲类型几何体BufferGeometry
threejs提供的长方体BoxGeometry
、球体SphereGeometry
等各种形状的几何体都是基于BufferGeometry
类构建的,BufferGeometry是一个没有任何形状的空几何体,你可以通过BufferGeometry自定义任何几何形状,具体一点说就是定义顶点数据。
javascript
// 创建一个空的几何体对象
const geometry = new THREE.BufferGeometry();
缓冲区对象BufferAttribute
BufferAttribute
类用于存储与BufferGeometry
相关联的 attribute(例如顶点位置向量,面片索引,法向量,颜色值,UV坐标以及任何自定义 attribute )。
BufferAttribute( array, itemSize, normalized )
- array :与
BufferGeometry
相关联的数组。必须是TypedArray类型。 - itemSize:与顶点相关的数据值的大小。如果 attribute 存储的是三元组(例如顶点空间坐标、法向量或颜色值)则itemSize的值应该是3。
- normalized:指示数据如何与GLSL代码中的数据进行对应。
javascript
// 类型化数组创建顶点数据
const vertices = new Float32Array([
0, 0, 0, // 顶点1坐标
80, 0, 0, // 顶点2坐标
80, 80, 0, // 顶点3坐标
0, 0, 0, // 顶点4坐标 和顶点1位置相同
80, 80, 0, // 顶点5坐标 和顶点3位置相同
0, 80, 0, // 顶点6坐标
]);
// 创建属性缓冲区对象
const attribue = new THREE.BufferAttribute(vertices, 3);
使用BufferGeometry
定义一个矩形几何体
一个矩形平面,可以至少通过两个三角形拼接而成。而且两个三角形有两个顶点的坐标是重合的。
注意三角形的正反面问题:保证矩形平面两个三角形的正面是一样的,也就是从一个方向观察,两个三角形都是逆时针或顺时针。
- 通过javascript类型化数组
Float32Array
创建一组xyz坐标数据用来表示几何体的顶点坐标。
javascript
// 类型化数组创建顶点数据
const vertices = new Float32Array([
0, 0, 0, // 顶点1坐标
80, 0, 0, // 顶点2坐标
80, 80, 0, // 顶点3坐标
0, 0, 0, // 顶点4坐标 和顶点1位置相同
80, 80, 0, // 顶点5坐标 和顶点3位置相同
0, 80, 0, // 顶点6坐标
]);
- 通过threejs的属性缓冲区对象
BufferAttribute
表示threejs几何体顶点数据。
javascript
// 创建属性缓冲区对象
// 3个为一组,表示一个顶点的xyz坐标
const attribue = new THREE.BufferAttribute(vertices, 3);
- 设置几何体的顶点
javascript
// 设置几何体attribue属性的位置属性
// geometry.setAttribute( 'position', attribue);
geometry.attributes.position = attribue;
网格模型Mesh对应的几何体BufferGeometry,拆分为多个三角后,很多三角形重合的顶点位置坐标是相同的,这时候如果你想减少顶点坐标数据量,可以借助几何体顶点索引geometry.index
来实现。
在上面的讲述中可以看出,每个三角形3个顶点坐标,矩形平面可以拆分为两个三角形,也就是6个顶点坐标。如果几何体有顶点索引geometry.index
,你就可以把三角形重复的顶点位置坐标删除。
javascript
const vertices = new Float32Array([
0, 0, 0, // 顶点1坐标
80, 0, 0, // 顶点2坐标
80, 80, 0, // 顶点3坐标
0, 80, 0, // 顶点4坐标
]);
- 通过javascript类型化数组
Uint16Array
创建顶点索引.index
数据。
javascript
// Uint16Array类型数组创建顶点索引数据
const indexes = new Uint16Array([
// 下面索引值对应顶点位置数据中的顶点坐标
0, 1, 2, 0, 2, 3,
])
- 通过threejs的属性缓冲区对象BufferAttribute表示几何体顶点索引
.index
数据。
javascript
// 索引数据赋值给几何体的index属性
geometry.index = new THREE.BufferAttribute(indexes, 1); // 1个为一组
完整代码
javascript
// 创建一个空的几何体对象
const geometry = new THREE.BufferGeometry();
// 类型化数组创建顶点数据
const vertices = new Float32Array([
0, 0, 0, // 顶点1坐标
80, 0, 0, // 顶点2坐标
80, 80, 0, // 顶点3坐标
0, 0, 0, // 顶点4坐标 和顶点1位置相同
80, 80, 0, // 顶点5坐标 和顶点3位置相同
0, 80, 0, // 顶点6坐标
] );
// 创建属性缓冲区对象 itemSize = 3 因为每个顶点都是一个三元组。
const attribue = new THREE.BufferAttribute(vertices, 3);
// 设置几何体attribue属性的位置属性
geometry.attributes.position = attribue;
// 创建索引
const indexes = new Uint16Array([0, 1, 2, 2, 3, 0])
// 索引数据,1个为一组
const index = THREE.BufferAttribute(indexes, 1);
// 索引数据赋值给几何体的index属性
geometry.index = index;
const material = new THREE.MeshBasicMaterial( { color: 0xff0000 } );
const mesh = new THREE.Mesh( geometry, material );
几何体方法
BufferGeometry通过.scale()
、.translate()
、.rotateX()
、.rotateY()
等方法可以对几何体本身进行缩放、平移、旋转,这些方法本质上都是改变几何体的顶点数据。
缩放.scale()
javascript
// 几何体xyz三个方向都放大2倍
geometry.scale(2, 2, 2);
平移.translate()
javascript
// 几何体沿着x轴平移50
geometry.translate(50, 0, 0);
旋转.rotateX()
、.rotateY()
、.rotateZ()
javascript
// 几何体绕着x轴旋转45度
geometry.rotateX(Math.PI / 4);
居中.center()
javascript
geometry.translate(50, 0, 0);//偏移
// 居中:已经偏移的几何体居中,执行.center(),你可以看到几何体重新与坐标原点重合
geometry.center();
材质Material
在Three.js中,Material
是所有材质的基类,它包含了一系列属性和方法,用于控制材质的透明度、深度测试、颜色、渲染面(正面、背面或双面)、透明度设置等。
MeshBasicMaterial、MeshLambertMaterial、MeshPhongMaterial等子类网格材质会从父类Material
继承一些属性和方法,比如透明度属性.opacity
、面属性.side
、是否透明属性.transparent
等等。
主要的材质属性:
- color :基本颜色,通常是一个
THREE.Color
对象,表示材质的漫反射颜色。 - map :纹理贴图,是一个
THREE.Texture
对象,用于为材质添加纹理。 - opacity:透明度,表示材质的不透明程度,取值范围为 0(完全透明)到 1(完全不透明)。
- transparent :指示材质是否透明。如果设置为
true
,则材质将考虑透明度(opacity)的影响。 - alphaMap :透明度贴图,是一个
THREE.Texture
对象,用于根据纹理图像的灰度值控制材质的透明度。 - side :渲染面的方向,可以是
THREE.FrontSide
、THREE.BackSide
或THREE.DoubleSide
。默认值是THREE.FrontSide
,只渲染正面。 - emissive :自发光颜色,通常是一个
THREE.Color
对象,表示材质的自发光颜色。 - emissiveMap :自发光贴图,是一个
THREE.Texture
对象,用于为材质添加自发光效果。 - specular :镜面反射颜色,通常是一个
THREE.Color
对象,表示材质的镜面反射颜色。这个属性主要应用于具有镜面反射效果的材质,如THREE.MeshPhongMaterial
。 - shininess :光泽度,表示材质的光泽程度。这个属性主要应用于具有镜面反射效果的材质,如
THREE.MeshPhongMaterial
。 - wireframe :布尔值,指示是否以线框模式渲染物体。如果设置为
true
,则物体将以线框模式显示。 - bumpMap :凹凸贴图,是一个
THREE.Texture
对象,用于为材质添加凹凸效果,以模拟表面的细微凹凸。 - normalMap :法线贴图,是一个
THREE.Texture
对象,用于为材质添加法线贴图效果,以模拟表面的细节。 - displacementMap :位移贴图,是一个
THREE.Texture
对象,用于根据纹理图像的灰度值改变物体表面的高度。 - roughness :粗糙度,表示材质表面的粗糙程度。这个属性主要应用于基于物理的渲染(PBR)材质,如
THREE.MeshStandardMaterial
和THREE.MeshPhysicalMaterial
。 - metalness :金属度,表示材质表面的金属质感。这个属性主要应用于基于物理的渲染(PBR)材质,如
THREE.MeshStandardMaterial
和THREE.MeshPhysicalMaterial
。 - roughnessMap :粗糙度贴图,是一个
THREE.Texture
对象,用于根据纹理图像的灰度值控制材质的粗糙度。这个属性主要应用于基于物理的渲染(PBR)材质。 - metalnessMap :金属度贴图,是一个
THREE.Texture
对象,用于根据纹理图像的灰度值控制材质的金属度。这个属性主要应用于基于物理的渲染(PBR)材质。 - envMap :环境贴图,是一个
THREE.Texture
对象,用于为材质添加反射和折射效果。 - refractionRatio :折射率,表示材质的折射程度。这个属性主要应用于具有折射效果的材质,如
THREE.MeshPhysicalMaterial
。
javascript
material.color.set(0x00ffff);
material.transparent = true; // 开启透明
material.opacity = 0.5; // 设置透明度
material.side = THREE.DoubleSide; // 双面可见
受光照影响材质
threejs提供的网格材质,有的受光照影响,有的不受光照影响。Three.js会提供一些的光照模型来模拟物体表面的光照,光照模型就一种模拟光照的计算方法。MeshPhysicalMaterial
和MeshLambertMaterial
一样都是渲染网格模型的材质,但是他们用的光照模型不同,具体点说就是材质模拟Mesh反射光照的代码算法不同,算法不同,自然模拟光照的真实程度也不同。
- MeshLambertMaterial:Lambert光照模型(漫反射)光线向四周反射。
- MeshPhongMaterial:Phong光照模型(漫反射、高光反射),可以提供一个镜面反射效果。
- MeshStandardMaterial 和 MeshPhysicalMaterial:基于物理的光照模型(微平面理论、能量守恒、菲涅尔反射...)
javascript
// 模拟镜面反射,产生一个高光效果
const material = new THREE.MeshPhongMaterial({
color: 0xff0000,
shininess: 20, // 高光部分的亮度,默认30
specular: 0x444444, // 高光部分的颜色
});
网格模型Mesh
网格模型Mesh
其实就一个一个三角形(面)拼接构成。使用网格模型Mesh
渲染几何体geometry
,就是几何体所有顶点坐标三个为一组,构成一个三角形,多组顶点构成多个三角形,就可以用来模拟表示物体的表面。
三个点可以构成一个三角形,从第一个点往第三个点连接。空间中一个三角形有正反两面,你的眼睛(相机)对着三角形的一个面,如果三个顶点的顺序是逆时针 方向,该面视为正面 ,如果三个顶点的顺序是顺时针 方向,该面视为反面。
Mesh(geometry, material)
- geometry :物体的结构。
BufferGeometry
的实例,默认值是一个新的BufferGeometry
。 - material :物体的外观。一个
Material
,或是一个包含有Material
的数组,默认是一个新的MeshBasicMaterial
。 - isMesh:当前对象是否是网格模型。
javascript
const mesh = new THREE.Mesh(geometry, material);
// 获取模型的几何体
console.log('mesh.geometry', mesh.geometry);
// 获取模型的材质
console.log('mesh.material', mesh.material);
访问改变模型材质属性
javascript
// 访问模型材质,并设置材质的颜色属性
mesh.material.color.set(0xffff00);
访问改变模型几何体属性
javascript
// 访问模型几何体,并平移几何体顶点数据
mesh.geometry.translate(0, 100, 0);
材质或几何体共享
如果两个mesh
使用同一个材质material
,改变其中一个mesh
另一个也会随之改变。
javascript
const mesh = new THREE.Mesh(geometry, material);
const mesh2 = new THREE.Mesh(geometry, material);
mesh2.position.x = 100;
// 两个mesh共享一个材质,改变一个mesh的颜色,另一个mesh2的颜色也会跟着改变
// mesh.material和mesh2.material都指向同一个material
// 三者等价:mesh.material、mesh2.material、material
mesh.material.color.set(0xffff00);
// 三者等价:mesh.geometry、mesh2.geometry、geometry
mesh.geometry.translate(0,100,0);
克隆.clone()
通过克隆.clone()
获得的新模型和原来的模型共享材质和几何体。改变mesh2
的颜色mesh
不会被改变。克隆.clone()
简单说就是复制一个和原对象一样的新对象。
javascript
const mesh2 = mesh.clone();
// 克隆几何体和材质,重新设置mesh2的材质和几何体属性
mesh2.geometry = mesh.geometry.clone();
mesh2.material = mesh.material.clone();
// 改变mesh2颜色,不会改变mesh的颜色
mesh2.material.color.set(0xff0000);
复制.copy()
通过复制.copy()
简单说就是把一个对象属性的属性值赋值给另一个对象。也不会随之改变另一个mesh
。
javascript
// 改变mesh的位置,使之位于mesh2的正上方(y),距离100。
mesh.position.copy(mesh2.position); // 复制mesh2的位置属性给mesh
mesh.position.y += 100;// mesh在原来y的基础上增加100
也可使用.copy()
让两个模型的姿态角度始终保持一样。
javascript
// 渲染循环
function render() {
mesh.rotateY(0.01);// mesh旋转动画
// 同步mesh2和mesh的姿态角度一样,不管mesh姿态角度怎么变化,mesh2始终保持同步
mesh2.rotation.copy(mesh.rotation);
renderer.render(scene, camera);
requestAnimationFrame(render);
}
render();
点模型Points
点模型Points
用于显示点的类。
Points(geometry, material)
- geometry :物体的结构。
BufferGeometry
的实例,默认值是一个新的BufferGeometry
。 - material :点的材质。默认是
PointMaterial
。 - isPoints:当前对象是否是点模型。
javascript
// 点渲染模式
const material = new THREE.PointsMaterial({
color: 0xffff00,
size: 10.0 // 点对象像素尺寸
});
// 点模型对象
const points = new THREE.Points(geometry, material);
线模型Line
线模型Line
一条连续的线。
Line(geometry, material)
- geometry :表示线段的顶点,默认值是一个新的
BufferGeometry
。 - material :线的材质。默认是
LineBasicMaterial
。 - isLine:当前对象是否是线模型。
javascript
// 线材质对象
const material = new THREE.LineBasicMaterial({
color: 0xff0000 // 线条颜色
});
// 创建线模型对象
const line = new THREE.Line(geometry, material);
threejs线模型除了Line,还提供了LineLoop
、LineSegments
,区别在于绘制线条的规则不同。
javascript
// 闭合线条
const lineLoop = new THREE.LineLoop(geometry, material);
// 非连续的线条
const lineSegments = new THREE.LineSegments(geometry, material);
基础案例使用
创建一个立方体,使其围绕Y轴旋转。
javascript
// 创建场景
const scene = new THREE.Scene();
let width = window.innerWidth; // 窗体宽度
let height = window.innerHeight; // 窗体高度
// 创建透视投影相机,视角45度,画幅比例 宽比高,近平面距离0.1,远平面1000
const camera = new THREE.PerspectiveCamera(60, width / height, 0.1, 1000);
// 创建渲染器
const renderer = new THREE.WebGLRenderer();
// 渲染器canvas宽高设为与窗口一致
renderer.setSize(width, height);
renderer.setClearColor(0xffffff, 1) // 设置背景颜色
// 将渲染器对应的dom元素添加到body中
document.body.appendChild(renderer.domElement);
// 定义一个几何体
const geometry = new THREE.BoxGeometry(30, 30, 30);
// 定义一种材质,显示为线框
const material = new THREE.MeshBasicMaterial({ color: 0x000000, wireframe: true });
// 网孔(Mesh)是用来承载几何模型的一个对象,可以把材料应用到它上面
const mesh = new THREE.Mesh(geometry, material);
// 把几何模型添加到场景中,对象被添加到原点(0,0,0)坐标。
scene.add(mesh);
camera.position.set(0, 200, 300); // 设置相机位置
camera.lookAt(scene.position); // 设置相机方向(指向的场景对象)
function render() {
// 渲染循环,以每秒60次的频率来绘制场景
requestAnimationFrame(render);
// 设置立方体绕y轴旋转
mesh.rotation.y += 0.005;
renderer.render(scene, camera);
}
render();