three.js 几何体、材质和网格模型

场景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定义一个矩形几何体

一个矩形平面,可以至少通过两个三角形拼接而成。而且两个三角形有两个顶点的坐标是重合的。

注意三角形的正反面问题:保证矩形平面两个三角形的正面是一样的,也就是从一个方向观察,两个三角形都是逆时针或顺时针。

  1. 通过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坐标
]);
  1. 通过threejs的属性缓冲区对象BufferAttribute表示threejs几何体顶点数据。
javascript 复制代码
// 创建属性缓冲区对象
// 3个为一组,表示一个顶点的xyz坐标
const attribue = new THREE.BufferAttribute(vertices, 3); 
  1. 设置几何体的顶点
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坐标
]);
  1. 通过javascript类型化数组Uint16Array创建顶点索引.index数据。
javascript 复制代码
// Uint16Array类型数组创建顶点索引数据
const indexes = new Uint16Array([
    // 下面索引值对应顶点位置数据中的顶点坐标
    0, 1, 2, 0, 2, 3,
])
  1. 通过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.FrontSideTHREE.BackSideTHREE.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.MeshStandardMaterialTHREE.MeshPhysicalMaterial
  • metalness :金属度,表示材质表面的金属质感。这个属性主要应用于基于物理的渲染(PBR)材质,如THREE.MeshStandardMaterialTHREE.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会提供一些的光照模型来模拟物体表面的光照,光照模型就一种模拟光照的计算方法。MeshPhysicalMaterialMeshLambertMaterial一样都是渲染网格模型的材质,但是他们用的光照模型不同,具体点说就是材质模拟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,还提供了LineLoopLineSegments,区别在于绘制线条的规则不同。

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();
相关推荐
小白菜学前端19 小时前
Threejs 材质贴图、光照和投影详解
前端·3d·three.js
破浪前行·吴2 天前
使用@react-three/fiber,@mkkellogg/gaussian-splats-3d加载.splat,.ply,.ksplat文件
前端·react.js·three.js
格瑞@_@9 天前
11.Three.js使用indexeddb前端缓存模型优化前端加载效率
前端·javascript·缓存·three.js·indexeddb缓存
谢小飞9 天前
我做了三把椅子原来纹理这样加载切换
前端·three.js
小白菜学前端10 天前
ThreeJS创建一个3D物体的基本流程
3d·three.js
茶老翁11 天前
1-初识Three.js
前端·three.js
莫石12 天前
搓绳子(直)
前端·数学·three.js
小白菜学前端13 天前
3d 添加辅助坐标器和轨道控制器
3d·three.js
孙_华鹏15 天前
threejs——实战中材质的应用
前端·three.js·数据可视化
天涯学馆18 天前
Three.js灯光阴影与动画交互
前端·unity3d·three.js