Three.js-硬要自学系列8 (三维向量、欧拉与角度,模型材质颜色、克隆和复制)

本章主要学习知识点

  • 了解三维向量概念,并实现使用向量归一化移动模型
  • 了解欧拉对象
  • 练习修改材质的颜色
  • 掌握如何复制克隆模型

三维向量Vector3

三维向量Vector3有xyz三个分量,threejs中会用三维向量Vector3表示很多种数据,想象你去快递站取件,需要三个关键信息: 楼层、房间号、货架位置,

在three.js中用代码表示

js 复制代码
const position = new THREE.Vector3(3,4,10)

这里的(3,5,10)对应为包裹位于 3楼、4号房间,第10个货架

如果要用来描述物体位置则为

js 复制代码
mesh.position.set(x,y,z)

三维向量的实际应用

思考一个问题,假设我们需要前往一个目标地,那该如何描述怎么前往呢?通常会以实际的距离来进行表达,如

向东走100米,在向北走30米,再向西走300米

这就是一个三维方向向量,用代码表示

js 复制代码
const direction = new THREE.Vector3(100,30,300)

我们可以让物体沿该向量所指方向移动

js 复制代码
mesh.position.add(direction)

三维向量数学运算

三维向量支持常见数学操作,就像用工具测量空间关系

操作类型 生活类比 Three.js 方法示例
加法 两段路程叠加 v1.add(v2)
减法 计算两个地点的间距 v1.sub(v2); v1.length()
缩放 按比例放大/缩小距离 v1.multiplyScalar(0.5)
单位化 提取纯方向(忽略长度) v1.normalize()

向量归一化

创建一个立方体,并设置模型平移旋转缩放

js 复制代码
// 设置模型位置
cube.position.set(1,0,0)
// 设置平移
cube.translateY(3)
// 缩放
cube.scale.set(2,2,2)
// 旋转
cube.rotateX(45)
cube.rotateY(45)
cube.rotateZ(45)

模型当前的状态

设置三维向量(1,1,1),并通过使用normalize()单位化向量,也就是向量归一化,并设置立方体沿该向量所指方向移动5个单位

欧拉Euler与角度

欧拉使用来描述物体绕三个轴XYZ的旋转角度,想象我们在操作无人机时有三个控制杆:

  • 俯仰角Pitch: 前后倾斜(类似点头)→对应绕X轴旋转
  • 偏航角Yaw: 左右转向(类似摇头)→对应绕Y轴旋转
  • 滚转角Roll: 侧身翻转(类似翻跟头)→对应绕Z轴旋转

在Three.js 中,用THREE.Euler(x,y,z,order)表示这三个旋转角度

js 复制代码
// 创建一个Euler对象,参数为弧度值,分别表示绕x轴、y轴、z轴的旋转角度,旋转顺序为xyz
const Euler = new THREE.Euler(Math.PI/4, 0, Math.PI/2,'XYZ')

旋转顺序

旋转顺序就像做菜的步骤顺序不同会影响最终味道:

  • 顺序XYZ:先调俯仰→再调方向→最后侧翻
  • 顺序ZYX:先侧翻→再调方向→最后调俯仰

在实际开发中需要注意,同一组角度(30°,45°,0°),不同顺序会导致物体朝向完全不同的方向

使用欧拉角度旋转时存在的问题,万向锁

万向锁;旋转bug

当俯仰角达到90度时,偏航和滚转轴会重合,导致失去一个旋转自由度。这就好比使用手机指南针时,若手机完全竖直(俯仰90度),左右旋转和前后倾斜会变得难以区分

面对这种问题,three.js中提供了 四元数用来避免万向锁问题的出现,目前只需要了解欧拉就好了

设置模型在Y轴上的角度,并使其沿X轴和Y轴旋转看看

js 复制代码
cube.rotation.y = Math.PI/4;

cube.rotation.y += 0.01;
cube.rotation.x += 0.01;
// 渲染场景和相机
renderer.render( scene, camera );
requestAnimationFrame( animation );

模型材质颜色

材质颜色的两种类型

  • 整体材质颜色
    • 通过材质对象的.color属性设置(如material.color = new THREE.Color('red')),类似给物体涂上统一颜色的油漆
    • 不受顶点颜色影响,适合单一颜色的物体(如纯色塑料玩具)
  • 顶点颜色
    • 给几何体每个顶点单独设置颜色(如geometry.colors.push(new THREE.Color('blue'))),类似用彩色马克笔给模型的不同部位上色
    • 常常用于温度云图、渐变效果等需要局部变色的场景

敲黑板,重点哈,材质类型对颜色的影响

  • 基础材质(MeshBasicMaterial)
    • 最简单的材质,颜色不受光照影响
    • 场景:白炽灯下的彩色纸片,始终保持固定颜色
  • 高光材质(MeshPhongMaterial)
    • 颜色会随光照角度变化,产生镜面反光
    • 通过.specular设置高光颜色,类似金属表面反光效果
    • 场景:阳光下旋转的金属水杯,会有亮斑移动
  • 物理材质(MeshStandardMaterial)
    • 通过roughness(粗糙度)和metalness(金属感)参数精细控制颜色表现
    • 场景:粗糙的铁锈表面 vs 光滑的不锈钢表面

光照对颜色的影响

  • 环境光:均匀染色(如阴天时的整体色调)
  • 平行光:产生明暗分界(如正午阳光下的物体投影)
  • 点光源:形成渐变光晕(如灯泡周围的亮度衰减)

修改材质颜色

js 复制代码
// 兰伯特材质
const material = new THREE.MeshLambertMaterial({color: 'deepskyblue'})
// 修改材质颜色
material.color.set('deeppink')

材质颜色将被改为deeppink深红粉

克隆和复制

克隆和复制两者的核心区别在于操作对象的状态和引用关系,我简单的对比下

  • 克隆 clone
    • 行为 :像复印机一样,生成一个独立的新对象,内容和原对象完全相同。
    • 特性 :新对象与原对象无引用关联(除非共享子属性,如材质和几何体)。
  • 复制 copy
    • 行为 :像用模板覆盖白纸,将原对象属性值赋给已存在的目标对象,目标对象原有数据被替换。
    • 特性 :操作后,目标对象与原对象无关联

创建一个立方体,尝试克隆一个相同的立方体

js 复制代码
const material = new THREE.MeshLambertMaterial({color: 'deepskyblue'})
// 网格模型
cube = new THREE.Mesh( geometry, material );
// 克隆
cube2 = cube.clone();
cube2.position.x = 5;

我们克隆出了一个与原立方体一摸一样的立方体,那如何知道克隆出来的立方体与原立方体是无引用关联的呢?我们可以设置原立方体旋转

js 复制代码
cube.rotation.y += 0.01;

可以看到新的模型并未旋转,我们尝试修改原立方体的材质颜色,看看新立方体是否共享了材质

js 复制代码
cube.material.color.set('deeppink')

嗯,如上图,新立方体的材质颜色也同步更新了,所以在使用克隆时,共享子属性是有引用关联的这点要注意

接下来尝试复制原立方体的旋转动作,看看结果如何

js 复制代码
cube2.rotation.copy(cube.rotation)

两者皆以同样的节奏旋转起来了

一些值得注意的细节

  • 克隆几何体Geometry: 生成完全独立的几何体数据,修改原几何体不影响克隆体。
  • 克隆网格模型Mesh
    • 默认克隆: 新模型与原模型共享几何体和材质。修改原模型的材质颜色,克隆体也会变化
    • 完全独立克隆:需额外克隆几何体和材质
js 复制代码
const mesh2 = mesh.clone(); 
mesh2.geometry = mesh.geometry.clone(); // 独立几何体
mesh2.material = mesh.material.clone(); // 独立材质

以上案例均可在案例中心查看体验

THREE 案例中心

相关推荐
抹茶san9 分钟前
el-tabs频繁切换tab引发的数据渲染混淆
前端·vue.js·element
Captaincc14 分钟前
关于MCP最值得看的一篇:MCP创造者聊MCP的起源、架构优势和未来
前端·mcp
小小小小宇18 分钟前
记录老项目Vue 2使用VueUse
前端
vvilkim18 分钟前
React Server Components 深度解析:下一代 React 渲染模式
前端·react.js·前端框架
HBR666_28 分钟前
vue3 excel文件导入
前端·excel
天天扭码32 分钟前
偶遇天才算法题 | 拼劲全力,无法战胜 😓
前端·算法·面试
小菜刀刀36 分钟前
文件包含漏洞,目录遍历漏洞,CSRF,SSRF
前端·csrf
anyup_前端梦工厂1 小时前
React 单一职责原则:优化组件设计与提高可维护性
前端·javascript·react.js
天天扭码1 小时前
面试官:算法题”除自身以外数组的乘积“ 我:😄 面试官:不能用除法 我:😓
前端·算法·面试
小小小小宇1 小时前
十万字JS不良实践总结(逼疯审核版)
前端