从零开始学习three.js(21):一文详解three.js中的矩阵Matrix和向量Vector

一、三维世界的数学基石

在Three.js的三维世界里,所有视觉效果的实现都建立在严密的数学基础之上。其中向量(Vector)矩阵(Matrix) 是最核心的数学工具,它们就像构建数字宇宙的原子与分子,支撑着物体的移动、旋转、缩放以及复杂的空间变换。本文一文详解向量(Vector)矩阵(Matrix)

1.1 向量:三维空间的基本元素

向量是描述空间方向和位置的数学实体,在Three.js中主要使用以下三种向量类型:

// 三维向量(最常用)
const position = new THREE.Vector3(1, 2, 3);// 二维向量(用于UV映射)
const uvCoord = new THREE.Vector2(0.5, 0.5);// 四维向量(颜色RGBA或特殊计算)
const color = new THREE.Vector4(1, 0, 0, 0.5);

核心操作示例

// 向量加法(物体位移)
const v1 = new THREE.Vector3(1, 2, 3);
const v2 = new THREE.Vector3(4, 5, 6);
v1.add(v2); // (5,7,9)// 点积计算(光照计算)
const lightDir = new THREE.Vector3(0, 1, 0).normalize();
const normal = new THREE.Vector3(0, 0, 1);
const dotProduct = lightDir.dot(normal); // 0// 叉乘应用(计算法线)
const tangent = new THREE.Vector3(1, 0, 0);
const bitangent = new THREE.Vector3(0, 1, 0);
const normal = tangent.cross(bitangent); // (0,0,1)

1.2 矩阵:空间变换

Three.js中的矩阵主要用于描述空间变换关系,以下是关键矩阵类型:

矩阵类型维度应用场景
Matrix33x3UV变换、法线矩阵
Matrix44x4模型视图投影矩阵(核心)
Matrix4数组-实例化渲染

二、矩阵运算的奥秘

2.1 基础矩阵操作

// 创建单位矩阵(所有矩阵变换的起点)
const identityMat = new THREE.Matrix4().identity();// 矩阵相乘(变换组合)
const rotateMat = new THREE.Matrix4().makeRotationX(Math.PI/2);
const translateMat = new THREE.Matrix4().makeTranslation(0, 5, 0);
const finalMat = translateMat.multiply(rotateMat); // 注意顺序!// 矩阵求逆(坐标系转换)
const viewMatrix = camera.matrixWorldInverse;

2.2 矩阵分解技巧

const matrix = new THREE.Matrix4();
const position = new THREE.Vector3();
const quaternion = new THREE.Quaternion();
const scale = new THREE.Vector3();matrix.decompose(position, quaternion, scale);
console.log('Position:', position);
console.log('Rotation:', quaternion);
console.log('Scale:', scale);

三、矩阵变换实战指南

3.1 变换组合原理

Three.js采用后乘的矩阵组合方式,理解执行顺序至关重要:

const mesh = new THREE.Mesh(geometry, material);// 正确的变换顺序:缩放 -> 旋转 -> 平移
mesh.scale.set(2, 2, 2);
mesh.rotation.x = Math.PI/4;
mesh.position.y = 10;// 等效矩阵计算:
const scaleMat = new THREE.Matrix4().makeScale(2, 2, 2);
const rotateMat = new THREE.Matrix4().makeRotationX(Math.PI/4);
const translateMat = new THREE.Matrix4().makeTranslation(0, 10, 0);// 矩阵组合顺序:T * R * S
const finalMatrix = translateMat.multiply(rotateMat).multiply(scaleMat);
mesh.matrix = finalMatrix;

3.2 矩阵堆栈管理

在复杂层级结构中,矩阵需要逐级传递:

function updateWorldMatrices(object, parentMatrix) {if (!parentMatrix) parentMatrix = new THREE.Matrix4();// 计算本地矩阵object.updateMatrix();// 组合世界矩阵object.matrixWorld.multiplyMatrices(parentMatrix, object.matrix);// 递归处理子对象for (let child of object.children) {updateWorldMatrices(child, object.matrixWorld);}
}

四、关键矩阵系统解析

4.1 模型视图投影矩阵(MVP)

// 获取三个关键矩阵
const modelMatrix = mesh.matrixWorld;
const viewMatrix = camera.matrixWorldInverse;
const projectionMatrix = camera.projectionMatrix;// 组合MVP矩阵
const mvpMatrix = new THREE.Matrix4().multiplyMatrices(projectionMatrix, viewMatrix).multiply(modelMatrix);

4.2 法线矩阵(Normal Matrix)

const normalMatrix = new THREE.Matrix3();
normalMatrix.getNormalMatrix(modelViewMatrix);// 在着色器中使用
material.onBeforeCompile = (shader) => {shader.uniforms.normalMatrix = { value: normalMatrix };shader.vertexShader = `uniform mat3 normalMatrix;${shader.vertexShader}`.replace('#include <beginnormal_vertex>', `objectNormal = normalMatrix * objectNormal;`);
};

五、性能优化策略

5.1 矩阵更新优化

// 禁用自动矩阵更新
mesh.matrixAutoUpdate = false;// 手动批量更新
function updateScene() {objects.forEach(obj => {obj.updateMatrix();obj.updateMatrixWorld(true); // 跳过子对象更新});
}

5.2 矩阵缓存重用

const _tempMatrix = new THREE.Matrix4();function calculateTransform(position, rotation, scale) {return _tempMatrix.compose(position, rotation, scale).clone();
}

六、常见问题诊断

6.1 变换顺序错误

症状:物体缩放导致旋转轴偏移
解决方案

// 错误方式:
mesh.position.set(0, 5, 0);
mesh.rotation.y = Math.PI/2;
mesh.scale.set(2, 2, 2);// 正确方式:
mesh.scale.set(2, 2, 2);
mesh.rotation.y = Math.PI/2;
mesh.position.set(0, 5, 0);

6.2 矩阵更新遗漏

症状:子对象未跟随父级移动
解决方案

parent.add(child);
parent.matrixWorldNeedsUpdate = true; // 强制更新世界矩阵

七、高阶应用实例

7.1 自定义矩阵动画

function matrixAnimation(mesh, duration) {const startMatrix = mesh.matrix.clone();const endMatrix = new THREE.Matrix4().makeRotationY(Math.PI).multiply(new THREE.Matrix4().makeTranslation(5, 0, 0));new TWEEN.Tween({ t: 0 }).to({ t: 1 }, duration).onUpdate(({ t }) => {mesh.matrix = startMatrix.clone().lerp(endMatrix, t);mesh.matrixWorldNeedsUpdate = true;}).start();
}

7.2 GPU矩阵计算

// 顶点着色器中使用自定义矩阵
const material = new THREE.ShaderMaterial({uniforms: {customMatrix: { value: new THREE.Matrix4() }},vertexShader: `uniform mat4 customMatrix;void main() {gl_Position = projectionMatrix * modelViewMatrix * customMatrix * vec4(position, 1.0);}`
});

八、调试与可视化工具

8.1 矩阵可视化

function printMatrix(label, matrix) {console.log(`${label}:`);const te = matrix.elements;for (let i = 0; i < 4; i++) {console.log(te[i*4].toFixed(2), te[i*4+1].toFixed(2),te[i*4+2].toFixed(2),te[i*4+3].toFixed(2));}
}

8.2 坐标系辅助显示

const axisHelper = new THREE.AxesHelper(5);
mesh.add(axisHelper);// 实时显示世界坐标系
function updateAxisHelper() {axisHelper.matrixWorld.copy(mesh.matrixWorld);axisHelper.matrixWorld.decompose(axisHelper.position,axisHelper.quaternion,axisHelper.scale);
}

九、最佳实践总结

  1. 优先使用高层API:尽量通过positionrotationscale属性操作对象
  2. 谨慎直接修改矩阵:仅在必要时直接操作矩阵元素
  3. 注意更新顺序:修改属性后及时调用updateMatrix()
  4. 重用矩阵对象:避免频繁创建新矩阵实例
  5. 理解空间转换链局部坐标 -> 世界坐标 -> 视图坐标 -> 裁剪坐标

通过掌握矩阵与向量的奥秘,开发者可以:
✅ 实现精准的物理碰撞检测
✅ 创建电影级动态光影效果
✅ 构建工业级数字孪生系统
✅ 开发复杂机械运动仿真

建议结合Three.js官方文档中的Matrix4和Vector3API参考进行实践,并利用浏览器开发者工具实时观察矩阵变化。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/pingmian/81506.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ArcGIS Pro 3.4 二次开发 - 内容

环境&#xff1a;ArcGIS Pro SDK 3.4 .NET 8 文章目录 内容1 工程1.1 创建一个空工程1.2 使用指定名称创建新工程1.3 使用Pro的默认设置创建新工程1.4 使用自定义模板文件创建新工程1.5 使用 ArcGIS Pro 提供的模板创建工程1.6 打开现有工程1.7 获取当前工程1.8 获取当前工程的…

【Python-Day 15】深入探索 Python 字典 (下):常用方法、遍历、推导式与嵌套实战

Langchain系列文章目录 01-玩转LangChain&#xff1a;从模型调用到Prompt模板与输出解析的完整指南 02-玩转 LangChain Memory 模块&#xff1a;四种记忆类型详解及应用场景全覆盖 03-全面掌握 LangChain&#xff1a;从核心链条构建到动态任务分配的实战指南 04-玩转 LangChai…

31、魔法生物图鉴——React 19 Web Workers

一、守护神协议&#xff08;核心原理&#xff09; 1. 灵魂分裂术&#xff08;线程架构&#xff09; // 主组件中初始化Workerconst workerRef useRef(null);​useEffect(() > {workerRef.current new Worker(new URL(./creatureWorker.js, import.meta.url));workerRef.…

Spark SQL 之 Antlr grammar 具体分析

src/main/antlr4/org/apache/spark/sql/catalyst/parser/SqlBaseLexer.g4 BACKQUOTED_IDENTIFIER: ` ( ~` | `` )* `;src/main/antlr4/org/apache/spark/sql/catalyst/parser/SqlBaseParser.g4 queryPrimary:

低功耗:XILINX FPGA如何优化功耗?

优化Xilinx FPGA及其外围电路的功耗需要从硬件设计、软件配置和系统级优化三个层面综合考虑。以下是具体的优化策略&#xff0c;涵盖硬件和软件方面&#xff1a; 一、硬件层面的功耗优化 选择低功耗FPGA型号 选择Xilinx低功耗系列芯片&#xff0c;如7系列中的Artix-7&#xff…

深入理解 ZAB:ZooKeeper 原子广播协议的工作原理

目录 ZAB 协议&#xff1a;ZooKeeper 如何做到高可用和强一致&#xff1f;&#x1f512;ZAB 协议的核心目标 &#x1f3af;ZAB 协议的关键概念 &#x1f4a1;ZAB 协议的运行阶段 &#x1f3ac;阶段一&#xff1a;Leader 选举 (Leader Election) &#x1f5f3;️阶段二&#xff…

OpenHarmony外设驱动使用 (五),Fingerprint_auth

OpenHarmony外设驱动使用 &#xff08;五&#xff09; Fingerprint_auth 概述 功能简介 指纹认证是端侧设备不可或缺的功能&#xff0c;为设备提供用户认证能力&#xff0c;可应用于设备解锁、支付、应用登录等身份认证场景。用户注册指纹后&#xff0c;指纹认证模块就可为设…

前端(vue)学习笔记(CLASS 6):路由进阶

1、路由的封装抽离 将之前写在main.js文件中的路由配置与规则抽离出来&#xff0c;放置在router/index.js文件中&#xff0c;再将其导入回main.js文件中&#xff0c;即可实现路由的封装抽离 例如 //index.js import { createMemoryHistory, createRouter } from vue-routerim…

前后端交互中的绝对路径和相对路径

前端 <form action"hello" method"post"> 1. 不加斜杠 &#xff08;相对路径&#xff0c;如 action"hello"&#xff09; 解析规则&#xff1a;基于当前页面的 URL 路径部分 进行拼接。 假设当前页面 URL 是 http://域名:端口/应用上下文…

在Odoo 18中创建进度条指南

在Odoo 18中创建进度条指南 一、创建进度条模板 首先在名为 progress_bar_widget.xml 的文件中定义一个名为 ProgressBarWidget 的新模板。该模板使用两个CSS类&#xff1a;progress-bar-inner 用于样式化进度条&#xff0c;progress_number 用于显示进度百分比。您可以根据需…

Linux grep 命令详解:常用选项、参数及实战场景

一、grep 命令简介 grep&#xff08;Global Regular Expression Print&#xff09;是 Linux 中用于文本搜索的核心工具&#xff0c;支持正则表达式&#xff0c;能快速定位文件中的目标内容。 二、常用选项&#xff08;Options&#xff09;及英文对照 | 选项 | 英文全称 | 作用 …

【Java-EE进阶】SpringBoot针对某个IP限流问题

目录 简介 1. 使用Guava的RateLimiter实现限流 添加Guava依赖 实现RateLimiter限流逻辑 限流管理类 控制器中应用限流逻辑 2. 使用计数器实现限流 限流管理类 控制器中应用限流逻辑 简介 针对某个IP进行限流以防止恶意点击是一种常见的反爬虫和防止DoS的措施。限流策…

Linux问题排查-找到偷偷写文件的进程

在 Linux 系统中&#xff0c;若要通过已修改的文件找到修改该文件的进程 PID&#xff0c;可以结合以下方法分析&#xff0c;具体取决于文件是否仍被进程打开或已被删除但句柄仍存在&#xff1a; 一、文件仍被进程打开&#xff08;未删除&#xff09; 如果文件当前正在被某个进…

More Effective C++:改善编程与设计(下)

目录 条款19:了解临时对象的来源 条款20:协助完成“返回值优化” 条款21:利用重载技术避免隐式类型转换 条款22:考虑以操作符复合形式&#xff08;op&#xff09;取代其独身形式&#xff08;op&#xff09; 条款23:考虑使用其他程序库 条款24:了解virtual functions、mul…

VTK|类似CloudCompare的比例尺实现2-vtk实现

文章目录 实现类头文件实现类源文件调用逻辑关键问题缩放限制问题投影模式项目git链接实现类头文件 以下是对你提供的 ScaleBarController.h 头文件添加详细注释后的版本,帮助你更清晰地理解每个成员和方法的用途,尤其是在 VTK 中的作用: #ifndef SCALEBARCONTROLLER_H #de…

PostgreSQL 联合索引生效条件

最近面试的时候&#xff0c;总会遇到一个问题 在 PostgreSQL 中&#xff0c;联合索引在什么条件下会生效&#xff1f; 特此记录~ 前置信息 数据库版本 PostgreSQL 14.13, compiled by Visual C build 1941, 64-bit 建表语句 CREATE TABLE people (id SERIAL PRIMARY KEY,c…

SpringBoot项目里面发起http请求的几种方法

在Spring Boot项目中发起HTTP请求的方法 在Spring Boot项目中&#xff0c;有几种常用的方式可以发起HTTP请求&#xff0c;以下是主要的几种方法&#xff1a; 1. 使用RestTemplate (Spring 5之前的主流方式) // 需要先注入RestTemplate Autowired private RestTemplate restT…

《Python星球日记》 第90天:微调的概念以及如何微调大模型?

名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 目录 一、微调原理1. 什么是大模型微调?2. 为什么需要微调?3. 微调的基本流程4. 微调策略分类二、LoRA(Low-Rank Adaptation)技术详解1. LoRA的核…

机器学习-人与机器生数据的区分模型测试 - 模型融合与检验

模型融合 # 先用普通Pipeline训练 from sklearn.pipeline import Pipeline#from sklearn2pmml.pipeline import PMMLPipeline train_pipe Pipeline([(scaler, StandardScaler()),(ensemble, VotingClassifier(estimators[(rf, RandomForestClassifier(n_estimators200, max_de…

怎样免费开发部署自己的网站?

要免费开发自己的网站&#xff0c;您可以根据自己的技术水平和需求选择以下两种主要方式&#xff1a; 零基础用户&#xff1a;建议使用如WordPress.com、Weebly、Strikingly等平台&#xff0c;快速搭建网站。 有一定技术基础的用户&#xff1a;可选择自行开发网站&#xff0c;…