Three.js——tween动画、光线投射拾取、加载.obj/.mtl外部文件、使用相机控制器
个人简介
👀个人主页: 前端杂货铺
⚡开源项目: rich-vue3 (基于 Vue3 + TS + Pinia + Element Plus + Spring全家桶 + MySQL)
🙋♂️学习方向: 主攻前端方向,正逐渐往全干发展
📃个人状态: 研发工程师,现效力于中国工业软件事业
🚀人生格言: 积跬步至千里,积小流成江海
🥇推荐学习:🍖开源 rich-vue3 🍍前端面试宝典 🍉Vue2 🍋Vue3 🍓Vue2/3项目实战 🥝Node.js实战 🍒Three.js🌕个人推广:每篇文章最下方都有加入方式,旨在交流学习&资源分享,快加入进来吧
| 内容 | 参考链接 |
|---|---|
| WebGL专栏 | WebGL 入门 |
| Three.js(一) | 创建场景、渲染三维对象、添加灯光、添加阴影、添加雾化 |
| Three.js(二) | scene场景、几何体位置旋转缩放、正射投影相机、透视投影相机 |
| Three.js(三) | 聚光灯、环境光、点光源、平行光、半球光 |
| Three.js(四) | 基础材质、深度材质、法向材质、面材质、朗伯材质、Phong材质、着色器材质、直线和虚线、联合材质 |
| Three.js(五) | Three.js——二维平面、二维圆、自定义二维图形、立方体、球体、圆柱体、圆环、扭结、多面体、文字 |
文章目录
- 前言
- 一、Tween动画
- 二、点击选取对象
- 三、加载外部文件
- 四、使用相机控制器
- 总结
前言
大家好,这里是前端杂货铺。
上篇文章我们学习了 二维平面、二维圆、自定义二维图形、立方体、球体、圆柱体、圆环、扭结、多面体、文字。接下来,我们继续我们 three.js 的学习!
在学习的过程中,如若需要深入了解或扩展某些知识,可以自行查阅 => three.js官方文档。
一、Tween动画
Tween.js是一个轻量级的 JavaScript 库,可以创建平滑的动画和过渡效果。
下面,我们设置一个不间断重复的,每两秒进行重新动画的场景。
核心代码:
new TWEEN.Tween(cube.rotation).to({x: cube.rotation.x + 2,y: cube.rotation.y + 2,z: cube.rotation.z + 2,
}, 2000).start().repeat(Infinity);TWEEN.update();
完整代码:
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>*{ margin: 0; padding: 0;}</style><script src="../lib/three/three.js"></script><script src="../lib/three/tween.min.js"></script>
</head><body>
<script>// 创建场景const scene = new THREE.Scene();// 创建相机 视野角度FOV、长宽比、近截面、远截面const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);// 设置相机位置camera.position.set(0, 0, 20);// 创建渲染器const renderer = new THREE.WebGLRenderer();// 设置渲染器尺寸renderer.setSize(window.innerWidth, window.innerHeight);document.body.appendChild(renderer.domElement);// 添加立方体const cubeGeometry = new THREE.BoxGeometry(3, 3, 3);// 创建立方体材质const lambert = new THREE.MeshLambertMaterial({color: 0xff0000});const basic = new THREE.MeshBasicMaterial({wireframe: true});const cube = new THREE.SceneUtils.createMultiMaterialObject(cubeGeometry, [lambert, basic]);// 添加到场景scene.add(cube);// 添加灯光const spotLight = new THREE.SpotLight(0xffffff);spotLight.position.set(-10, 10, 90);scene.add(spotLight);new TWEEN.Tween(cube.rotation).to({x: cube.rotation.x + 2,y: cube.rotation.y + 2,z: cube.rotation.z + 2,}, 2000).start().repeat(Infinity);const animation = () => {TWEEN.update();// 渲染renderer.render(scene, camera);requestAnimationFrame(animation);}animation();
</script>
</body></html>
tween动画
二、点击选取对象
通过鼠标点击获取x, y坐标,进而计算出归一化坐标。之后通过光线投射进行物体的拾取。
Raycaster 这个类用于进行光线投射。光线投射用于进行鼠标拾取(在三维空间中计算出鼠标移过了什么物体)。
new THREE.Raycaster( origin : Vector3, direction : Vector3, near : Float, far : Float )
| 参数名称 | 描述 |
|---|---|
| origin | 光线投射的原点向量 |
| direction | 向射线提供方向的方向向量,应当被标准化 |
| near | 返回的所有结果比 near 远。near 不能为负值,其默认值为 0 |
| far | 返回的所有结果都比far近。far不能小于near,其默认值为Infinity(正无穷) |
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>*{ margin: 0; padding: 0;}</style><script src="../lib/three/three.js"></script><script src="../lib/three/tween.min.js"></script>
</head><body>
<script>// 创建场景const scene = new THREE.Scene();// 创建相机 视野角度FOV、长宽比、近截面、远截面const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);// 设置相机位置camera.position.set(0, 0, 20);// 创建渲染器const renderer = new THREE.WebGLRenderer();// 设置渲染器尺寸renderer.setSize(window.innerWidth, window.innerHeight);document.body.appendChild(renderer.domElement);// 添加立方体const cubeGeometry = new THREE.BoxGeometry(2, 2, 2);// 创建立方体材质const lambert = new THREE.MeshLambertMaterial({color: 0xff0000});const cube = new THREE.Mesh(cubeGeometry, lambert);cube.rotation.set(0.4, 0.4, 0);cube.position.x = -2;// 添加到场景cube.name = 'cube';scene.add(cube);const sphereGeometry = new THREE.SphereGeometry(2, 10, 10);const sphere = new THREE.Mesh(sphereGeometry, lambert);sphere.position.x = 4;sphere.name = 'sphere';scene.add(sphere);// 添加灯光const spotLight = new THREE.SpotLight(0xffffff);spotLight.position.set(-10, 10, 90);scene.add(spotLight);document.onclick = function (event) {// 归一化坐标(将鼠标位置归一化为设备坐标。x 和 y 方向的取值范围是 (-1 to +1))const x = (event.clientX / window.innerWidth) * 2 - 1;const y = -(event.clientY / window.innerHeight) * 2 + 1;// 创建设备坐标(三维)const standardVector = new THREE.Vector3(x, y, 0.5);// 转化为世界坐标 (将此向量 (坐标) 从相机的标准化设备坐标 (NDC) 空间投影到世界空间)const worldVector = standardVector.unproject(camera);// 做序列化const ray = worldVector.sub(camera.position).normalize();// 实现点击选中// 创建一个射线发射器,用来发射一条射线const raycaster = new THREE.Raycaster(camera.position, ray);// 返回射线碰撞到的物体const intersects = raycaster.intersectObjects(scene.children, true);let point3d = null;if (intersects.length) {point3d = intersects[0];}if (point3d) {console.log(point3d.object.name);}}const animation = () => {// 渲染renderer.render(scene, camera);requestAnimationFrame(animation);}animation();
</script>
</body></html>
raycaster光线投射
三、加载外部文件
加载外部文件,可以使用 MTL 加载器来实现。
MTLLoader 一个用于加载 .mtl 资源的加载器,由 OBJLoader 在内部使用。
材质模版库(MTL)或 .MTL 文件格式是 .OBJ 的配套文件格式, 用于描述一个或多个 .OBJ 文件中物体表面着色(材质)属性。
MTLLoader( loadingManager : LoadingManager )
接下来,我们加载一个城市模型
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>*{ margin: 0; padding: 0;}</style><script src="../lib/three/three.js"></script><script src="../lib/three/OBJLoader.js"></script><script src="../lib/three/MTLLoader.js"></script><script src="../lib/three/OBJMTLLoader.js"></script>
</head><body>
<script>// 创建场景const scene = new THREE.Scene();// 创建相机 视野角度FOV、长宽比、近截面、远截面const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);// 设置相机位置camera.position.set(0, 300, 400);camera.lookAt(new THREE.Vector3(0, 0, 0));// 创建渲染器const renderer = new THREE.WebGLRenderer();// 设置渲染器尺寸renderer.setSize(window.innerWidth, window.innerHeight);document.body.appendChild(renderer.domElement);// 添加灯光const spotLight = new THREE.SpotLight(0xffffff);spotLight.position.set(2000, 8000, 4000);scene.add(spotLight);const loader = new THREE.OBJMTLLoader();// PS:想要文件的加群获取...loader.load('../assets/models/city.obj', '../assets/models/city.mtl', (mesh) => {scene.add(mesh);})const animation = () => {// 渲染renderer.render(scene, camera);requestAnimationFrame(animation);}animation();
</script>
</body></html>

四、使用相机控制器
相机控制器有很多种,它的作用是使得相机围绕目标进行不同类型的运动。
接下来,我们使用 轨迹球控制器、第一人称控制器、飞行控制器、翻滚控制器和轨道控制器 查看一下效果。
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>*{ margin: 0; padding: 0;}</style><script src="../lib/three/three.js"></script><script src="../lib/three/OBJLoader.js"></script><script src="../lib/three/MTLLoader.js"></script><script src="../lib/three/OBJMTLLoader.js"></script><!-- 轨迹球控件 --><script src="../lib/three/TrackballControls.js"></script><!-- 第一人称控件 --><script src="../lib/three/FirstPersonControls.js"></script><!-- 飞行控件 --><script src="../lib/three/FlyControls.js"></script><!-- 翻滚控件 --><script src="../lib/three/RollControls.js"></script><!-- 轨道控件 --><script src="../lib/three/OrbitControls.js"></script>
</head><body>
<script>const clock = new THREE.Clock();// 创建场景const scene = new THREE.Scene();// 创建相机 视野角度FOV、长宽比、近截面、远截面const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);// 设置相机位置// camera.position.set(0, 300, 400);// camera.position.set(100, 30, 0);camera.position.set(100, 100, 0);camera.lookAt(new THREE.Vector3(0, 0, 0));// 轨迹球控件// const trackball = new THREE.TrackballControls(camera);// 第一人称控件// const controls = new THREE.FirstPersonControls(camera);// controls.lookSpeed = 0.2;// 飞行控件// const controls = new THREE.FlyControls(camera);// controls.rollSpeed = 0.5;// 翻滚控件// const controls = new THREE.RollControls(camera);// 轨道控件const controls = new THREE.OrbitControls(camera);// 创建渲染器const renderer = new THREE.WebGLRenderer();// 设置渲染器尺寸renderer.setSize(window.innerWidth, window.innerHeight);document.body.appendChild(renderer.domElement);// 添加灯光const spotLight = new THREE.SpotLight(0xffffff);spotLight.position.set(2000, 8000, 4000);scene.add(spotLight);const loader = new THREE.OBJMTLLoader();loader.load('../assets/models/city.obj', '../assets/models/city.mtl', (mesh) => {scene.add(mesh);})const animation = () => {// trackball.update();controls.update(clock.getDelta());// 渲染renderer.render(scene, camera);requestAnimationFrame(animation);}animation();
</script>
</body></html>
下面,我们以轨道控制器为例,看一下具体的呈现效果:
three.js 使用相机控制器
总结
本篇文章我们讲解了几种常见几何体的基本使用,包括 tween动画、光线投射拾取、加载.obj/.mtl外部文件、使用相机控制器。
更多内容扩展请大家自行查阅 => three.js官方文档,真心推荐读一读!!
好啦,本篇文章到这里就要和大家说再见啦,祝你这篇文章阅读愉快,你下篇文章的阅读愉快留着我下篇文章再祝!
参考资料:
- Three.js 官方文档
- WebGL+Three.js 入门与实战【作者:慕课网_yancy】

相关文章:
Three.js——tween动画、光线投射拾取、加载.obj/.mtl外部文件、使用相机控制器
个人简介 👀个人主页: 前端杂货铺 ⚡开源项目: rich-vue3 (基于 Vue3 TS Pinia Element Plus Spring全家桶 MySQL) 🙋♂️学习方向: 主攻前端方向,正逐渐往全干发展 …...
内网渗透-在HTTP协议层面绕过WAF
进入正题,随着安全意思增强,各企业对自己的网站也更加注重安全性。但很多web应用因为老旧,或贪图方便想以最小代价保证应用安全,就只仅仅给服务器安装waf。 本次从协议层面绕过waf实验用sql注入演示,但不限于实际应用…...
qt QGroupBox radiobutton
QGroupBox 显示文本:属性 title 加载radiobutton if (jsonObject.contains("startEndTogether") && jsonObject["startEndTogether"].isString()) {QString selectedButton jsonObject["startEndTogether"].toString();//…...
jetson nano onnxruntime 安装
安装说明: onnxruntime 依赖cuda、cudnn版本,可onnxruntime查找对应关系。但可能会出现jetpack中的cuda和cudnn的版本无法查找到对应版本的onnxruntime的问题。 解决方法: 通过Jetson Zoo下载相应的whl包直接安装。...
图形学初识--屏幕空间变换
文章目录 前言正文为什么需要屏幕空间变换?什么是屏幕空间变换?屏幕空间变换矩阵如何推导?问题描述步骤描述 结尾:喜欢的小伙伴点点关注赞哦! 前言 前面章节主要讲解了视图变换和投影变换,此时距离在屏幕空间显示也就…...
爬楼梯 - LeetCode 热题 81
大家好!我是曾续缘😇 今天是《LeetCode 热题 100》系列 发车第 81 天 动态规划第 1 题 ❤️点赞 👍 收藏 ⭐再看,养成习惯 爬楼梯 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法…...
详解 Spark 核心编程之 RDD 分区器
一、RDD 分区器简介 Spark 分区器的父类是 Partitioner 抽象类分区器直接决定了 RDD 中分区的个数、RDD 中每条数据经过 Shuffle 后进入哪个分区,进而决定了 Reduce 的个数只有 Key-Value 类型的 RDD 才有分区器,非 Key-Value 类型的 RDD 分区的值是 No…...
Selenium番外篇文本查找、元素高亮、截图、无头运行
Selenium根据文本查找元素 python def find_element_with_text(self, loc, attribute, text):try:WebDriverWait(self.driver, 5).until(EC.all_of(EC.text_to_be_present_in_element_attribute(loc, attribute, text)))element self.driver.find_element(*loc)if isinsta…...
Java 22的FFM API,比起Java 21的虚拟线程
哪个对Java未来的发展影响更大?两个 Java 版本中的重要特性:Java 21 的虚拟线程和 Java 22 的 FFM API。我这里有一套编程入门教程,不仅包含了详细的视频讲解,项目实战。如果你渴望学习编程,不妨点个关注,给…...
用c语言实现简易三子棋
本篇适用于C语言初学者。 目录 完整代码: 分步介绍: 声明: 代码主体部分: 模块功能实现: 完整代码: #include<stdio.h> #include <stdlib.h> #include <time.h>#define ROW 3 #d…...
2024年华为OD机试真题-执行时长-Python-OD统一考试(C卷D卷)
2024年OD统一考试(D卷)完整题库:华为OD机试2024年最新题库(Python、JAVA、C++合集) 题目描述: 为了充分发挥GPU算力,需要尽可能多的将任务交给GPU执行,现在有一个任务数组,数组元素表示在这1秒内新增的任务个数且每秒都有新增任务,假设GPU最多一次执行n个任务,一次执…...
对未知程序所创建的 PDF 文档的折叠书签层级全展开导致丢签的一种解决方法
对需要经常查阅、或连续长时间阅读的带有折叠书签的 PDF 文档展开书签层级,提高阅览导航快捷是非常有必要的。 下面是两种常用书签层级全展开的方法 1、 FreePic2Pdf 1 - 2 - 3 - 4 - 5 - 6,先提取后回挂 2、PdgCntEditor 载入后,直接保存…...
计算机系统结构之FORK和JOIN
程序语言中用FORK语句派生并行任务,用JOIN语句对多个并发任务汇合。 FORK语句的形式为FORK m,其中m为新领程开始的标号。 JOIN语句的形式为JOIN n,其中n为并发进程的个数。 例1:给定算术表达式ZEA*B*C/DF经并行编译得到如下程序…...
Yocto - virtual/kernel介绍
在 Yocto 项目中,"virtual/kernel "是一个虚拟目标,作为 Linux 内核的抽象层。它是一种以灵活方式指定内核依赖关系的方法,允许实际的内核配方由特定构建中使用的机器配置和层决定。 下面是关于 "virtual/kerne"的含义和…...
如何在 DigitalOcean 云服务器上创建自定义品牌名称服务器
前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。 介绍 对于托管提供商或转售商来说,拥有自定义的名称服务器可以为客户提供更专业的外观。这消除了要求客户将其域名指向另一…...
心链6----开发主页以及后端数据插入(多线程并发)定时任务
心链 — 伙伴匹配系统 开发主页 信息搜索页修改 主页开发(直接list用户) 在后端controller层编写接口去实现显示推荐页面的功能 /*** 推荐页面* param request* return*/GetMapping("/recommend")public BaseResponse<List<User>&…...
【Linux】日志管理
一、日志进程 1、处理日志的进程 rsyslogd:系统专职日志程序 观察rsyslogd程序: ps aux | grep rsyslogd 2、常见的日志文件 1、系统主日志文件: /var/log/messages 动态查看日志文件尾部: tail -f /var/log/messages 2、安全…...
AI 绘画爆火背后:扩散模型原理及实现
节前,我们星球组织了一场算法岗技术&面试讨论会,邀请了一些互联网大厂朋友、参加社招和校招面试的同学。 针对算法岗技术趋势、大模型落地项目经验分享、新手如何入门算法岗、该如何准备、面试常考点分享等热门话题进行了深入的讨论。 合集&#x…...
详解智慧互联网医院系统源码:开发医院小程序教学
本篇文章,笔者将详细介绍智慧互联网医院系统的源码结构,并提供开发医院小程序的详细教学。 一、智慧互联网医院系统概述 智慧互联网医院系统涵盖了预约挂号、在线咨询、电子病历、药品管理等多个模块。 二、系统源码结构解析 智慧互联网医院系统的源码…...
【技术实操】银河高级服务器操作系统实例分享,数据库日志文件属主不对问题分析
1. 问题现象描述 2023 年 06 月 30 日在迁移数据库过程中,遇到数据库 crash 的缺陷,原因如下:在数据库启动时候生成的一组临时文件中,有 owner 为 root 的文件, 文件权限默认为 640, 当数据库需要使用的时…...
别再依赖SDK了!手把手教你用OpenCV和Eigen从零实现RGB-D相机对齐(附完整C++代码)
从零实现RGB-D相机对齐:OpenCV与Eigen实战指南 在计算机视觉领域,RGB-D相机的深度与彩色图像对齐(D2C)是一个基础但至关重要的技术环节。虽然市面上大多数商用RGB-D相机都提供了现成的SDK和API来实现这一功能,但对于真…...
轻量级工作流编排引擎:从脚本管理到自动化流程的实践指南
1. 项目概述:从单体脚本到流程编排的进化 如果你和我一样,在数据工程、自动化运维或者机器学习模型训练这些领域摸爬滚打过几年,大概率会遇到一个相似的困境:手头的任务脚本越来越多,它们之间有的有依赖关系࿰…...
用Logisim搞定Educoder交通灯实训:从数码管驱动到状态机集成的保姆级避坑指南
用Logisim征服Educoder交通灯实训:从零搭建到联调的全链路实战手册 第一次打开Educoder平台的交通灯实训项目时,我盯着那些闪烁的数码管和错综复杂的线路图,感觉像在破解某种外星密码。三小时后,当我的第一个状态机模块终于通过测…...
5秒无损转换B站缓存视频:m4s-converter完整使用指南
5秒无损转换B站缓存视频:m4s-converter完整使用指南 【免费下载链接】m4s-converter 一个跨平台小工具,将bilibili缓存的m4s格式音视频文件合并成mp4 项目地址: https://gitcode.com/gh_mirrors/m4/m4s-converter 你是否曾经在B站缓存了珍贵的学习…...
Flutter GetX实战:从Provider迁移到GetX,我的开发效率提升了多少?
Flutter GetX实战:从Provider迁移到GetX的效率革命 当Flutter开发团队面临状态管理方案的选择时,往往会陷入一种甜蜜的烦恼——官方推荐的Provider虽然稳定可靠,但第三方库GetX却以"全家桶"式的解决方案不断吸引开发者的目光。作为…...
深部空间专属孪生,打造密闭硐室独有不可替代透明体系技术白皮书
深部空间专属孪生,打造密闭硐室独有不可替代透明体系技术白皮书副标题:井下专用暗光算法实现三维实时重建,搭配地下专属无感定位、多盲区跨镜穿透追踪、身体指纹特征识别,场景适配独一无二,行业无同类对标方案前言矿山…...
技能工程化框架:从标准化定义到编排实战
1. 项目概述:从“技能”到“智能”的工程化桥梁在当今的软件开发领域,尤其是涉及复杂交互和自动化流程的场景,我们常常会听到“技能”这个词。它听起来很抽象,但如果你拆解过任何一款智能助手、自动化机器人或者一个大型的业务流程…...
终极显卡调校指南:如何用NVIDIA Profile Inspector释放游戏性能
终极显卡调校指南:如何用NVIDIA Profile Inspector释放游戏性能 【免费下载链接】nvidiaProfileInspector 项目地址: https://gitcode.com/gh_mirrors/nv/nvidiaProfileInspector NVIDIA Profile Inspector是一款专为NVIDIA显卡用户设计的免费优化工具&…...
Arm Morello平台模型与CHERI安全扩展开发指南
1. Arm Morello平台模型概述Morello是Arm公司推出的实验性处理器架构,基于CHERI(Capability Hardware Enhanced RISC Instructions)安全扩展技术。这个平台模型本质上是一个功能准确的虚拟硬件环境,允许开发者在物理芯片问世前18-…...
DS3502 I2C数字电位器:从原理到Arduino/Python实战应用
1. 项目概述:告别手动旋钮,拥抱数字控制如果你和我一样,厌倦了在面包板上反复拧动电位器旋钮来调试电路,或者正在寻找一种能够通过程序精确控制电阻值的方法,那么DS3502这类I2C数字电位器绝对是你的“梦中情芯”。它本…...
