数字孪生——thing.js和three.js

一、前言

数字孪生指的是采用虚拟仿真技术,将物理实体的全部或部分信息数字化并复制到虚拟世界中,使得物理实体对象的一切运动、行为及系统属性在虚拟空间中得以表现和模拟的技术。其以数据为驱动,构建孪生体模型,继而在数字空间模拟仿真物理系统的演绎过程,进一步在数字空间对孪生系统进行推演和预测。它主要基于遥感、传感器等技术实现,并运用大数据、人工智能、云计算等技术提高其精确度,为智能决策和高效操作提供支撑。本文以物流方向出发,主要从业务角度和应用角度,针对ThreeJS 和 ThingJS 做出技术调研。

二、数字孪生在物流领域的前景

1. 政策支持

近些年来,我国密集出台相关政策以支持数字孪生技术的应用发展,相关政策颁布时间及内容如下:数字孪生——thing.js和three.js_第1张图片

2. 行业发展需要

对于物流行业来说,中转分拣是其普遍面临的复杂场景之一,顺丰科技建立了高度逼真的物流自动化设备孪生体及数字孪生平台,为物流中转场提供了端到端的高度逼真的孪生验证环境,通过优化运营策略,提高了产能,降低了成本,解决了业务中的实际问题,实现了物流中转场的智能运维。通过构建高度真实的虚拟数字孪生体对场地的真实分拣情况进行仿真模拟,平台依靠数据与算法输出了更优的分拣计划,大大提升了中转场地的分拣验证效率。基于数字孪生的全新优化方法,可以在逼真度达99%以上的虚拟孪生体中每天验证数千次,既高效可靠又突破了优化的瓶颈,分拣效果更优。在现实中转场小件分拣机的多个作业班次上验证,可增加超8%的半圈落格的件量,且在相同件量下可缩短超10%的分拣时长,在固定分拣时长下可实际提升超8%的平均产能。
2022年9月顺丰科技还正式聘请了香港中文大学(深圳)的黄川教授作为数字孪生科学家,联合产学研各方的优势资源与专业能力,推动数字孪生技术能力建设往纵深方向发展,在仿真效率、模型逼真度、大规模仿真与优化联合求解等复杂课题上开展先行研究,致力于构建数字孪生优化物流与供应链行业运营效率的新范式。

3. 数字孪生是智慧物流的必经之路

数字孪生在物流领域的应用尚处在初期阶段,随着政策环境优化、技术演进以及行业标准和体系逐步成熟,数字孪生应用场景广度和深度将进一步拓展,推动行业规模迅速增长。根据IDC数据,预计2025年,全球数字孪生市场规模将增至264.6亿美元,2020-2025年CAGR高达38.35%。数字孪生是智慧物流的必经之路。

三、数字孪生在物流领域的应用

1. 数字孪生在供应链中的应用

供应链管理涉及供应商、制造商、经销商和零售商之间的物流活动,目的是确保产品和服务能够按时提供给消费者。通过数字孪生技术,物流从业者可以建立一个准确的模型,用于监控和管理整个供应链的运作。通过实时数据的监测和分析,可以及时发现和解决供应链中的问题,提高整体运营效率和灵活性。例如,当某个环节出现问题时,数字孪生系统可以快速定位问题所在,并提供相应的解决方案,从而减少供应链中断的时间和风险。

2. 数字孪生在运输和仓储管理中的应用

物流行业需要管理大量的运输活动。通过数字孪生技术,物流从业者可以通过虚拟模型来模拟不同运输的效果,从而优化物流运作。例如,可以模拟不同运输路线的成本和效益,选择最优的路线来降低运输成本;
此外,数字孪生技术还可以帮助企业建立一套仓库实时监控系统,监测采用热成像仪、光学传感器、气体传感器等多种传感器装置,可以实时采集仓库内的温度、湿度、气体、光线等信息,并通过数字孪生技术实时地将这些信息反映到数字模型中。这个模型可以反映出仓库内的各种情况,帮助管理者快速了解仓库的运作状态,并采取相应措施。比如,当仓库库存接近上限时,系统会给管理者发送警报,以便及时调整物流计划,或可监测仓库是否有长时间滞留的货物,以便管理者定期清理滞留货物,避免造成仓存空间浪费。

3. 数字孪生在物流行业进行预测和优化上的应用

通过对模型仓库的数据分析,可以预测出物流的需求趋势与变化。这样,管理者可以提前调整物流计划,合理安排仓储与运输资源,避免库存积压或库存闲置浪费的情况。同时,通过数字孪生仓库模型的模拟实验,还可以测试不同物流策略的效果,进而制定出最优的物流规划。。
通过虚拟模型,物流从业者可以结合历史数据和实时监测数据进行预测,以便及时调整运输、仓储、人力等各资源安排的计划。此外,数字孪生还可以结合物流大数据和人工智能技术,通过对数据的分析和挖掘,提供更准确的预测和决策支持。预测和优化能力的提升,可以帮助物流企业提前做好准备,降低库存和运输成本,提高客户满意度和市场竞争力。

四、数字孪生技术

1. 数字孪生十大技术

数字孪生是一个综合的概念,涉及到多个技术领域,以下是数字孪生中常用的十个技术:

  • 3D建模技术:数字孪生的基础是对实际物理系统的数字化建模。 3D建模技术可以将实际物理系统的形状、大小和结构等信息转换为数字化的模型。
  • 传感器技术:传感器技术可以实时采集物理系统中的各种数据,如温度、压力、湿度、振动等,并将其传输到数字孪生模型中进行模拟分析。
  • 数据分析技术:数据分析技术可以帮助数字孪生系统快速准确地分析大量数据。
  • 人工智能技术:用于模拟人类的智能行为,包括机器学习、深度学习、自然语言处理等。
  • 虚拟现实技术:用于帮助数字孪生系统实现实时的数字化模拟。
  • 云计算技术:用于帮助数字孪生系统快速处理大量数据和计算。
  • 区块链技术:可以帮助数字孪生系统实现数据的安全和可靠性。
  • 物联网技术:用于帮助数字孪生系统实时监测。
  • 器学习技术:可以帮助数字孪生系统自动学习和优化。
  • 自然语言处理技术:用于帮助数字孪生系统自动分析和理解数据。

2.数字孪生技术——前端技术框架

2.1 Three.js

Three.js是目前最流行的,基于WebGL的开源3D框架,它可以让开发者轻松地实现复杂的 3D 场景、动画和交互效果。Three.js广泛应用于游戏开发、虚拟现实(VR)和增强现实(AR)项目以及数据可视化等领域。

2.1.1 Three.js的特点和优劣势

Three.js 的特点和优势如下:

  • 轻量级:Three.js 是一个非常轻量级的 3D 库,它将所有功能集中在一个文件中,使得在浏览器中运行 3D 应用变得非常简单。相较于其他大型的 3D 引擎,它的资源占用较小,加载速度更快。
  • 跨平台:Three.js 支持多种操作系统,如 Windows、macOS 和 Linux,使得开发者可以在不同的平台上进行开发和部署。
  • 强大的功能:虽然 Three.js 体积较小,但它提供了许多强大的功能,如光照、阴影、纹理贴图、粒子系统等,可以满足大部分 3D 项目的需求。
  • 社区支持:Three.js 拥有庞大的社区支持,这意味着你可以找到大量的教程、示例代码和问题解答,帮助你解决遇到的问题。
  • 可扩展性:Three.js 允许开发者自定义和扩展其 API,以满足特定的需求。此外,还有许多第三方插件和扩展可以与 Three.js 结合使用,进一步增强其功能。
  • 实时渲染:Three.js 支持 WebGL 的硬件加速渲染,可以实现流畅的实时渲染效果。
  • 理引擎:Three.js 内置了基于物理引擎的碰撞检测和运动模拟功能,可以方便地为 3D 物体添加物理效果。

Three.js 的缺点如下:

  • 学习曲线较陡峭:Three.js是一个功能丰富的3D库,但它涉及了较多3D建模技术的知识。需要一定的时间和精力去学习和熟悉。
  • 性能问题:尽管Three.js在大多数情况下表现良好,但在处理大型场景或复杂模型时,可能会出现性能下降的情况。尤其是在低端设备上,渲染场景的速度可能会很慢。
  • 依赖于WebGL:Three.js是基于WebGL的,这意味着它依赖于浏览器的支持。如果用户使用的浏览器不支持WebGL,那么Three.js将无法正常工作。
  • 社区支持有限:相较于一些成熟的3D库,如Unity或Unreal Engine,Three.js的社区支持相对较少。这可能会导致在遇到问题时寻求帮助变得困难。
  • 缺乏某些功能:虽然Three.js提供了很多强大的功能,但它可能仍然缺少一些特定领域的功能,如物理引擎、动画系统等。对于这些特定需求,可能需要寻找其他更专业的库来满足。
  • 不支持所有类型的3D模型:Three.js 主要支持基于 JSON 的3D模型格式(如 GLTF、OBJ 等),对于其他类型的3D模型,可能需要额外的插件或工具进行导入。

2.1.2 Three.js的应用领域

  • 游戏开发:Three.js 可以用于开发 2D 和 3D 游戏,如第一人称射击游戏、策略游戏等。
  • 虚拟现实(VR)和增强现实(AR):Three.js 可以与 VR 和 AR 设备配合使用,为用户提供沉浸式的体验。
  • 数据可视化:Three.js 可以用于创建各种类型的数据可视化图表,如散点图、柱状图、饼图等。
  • 工程模拟:Three.js 可以用于模拟复杂的工程结构,如桥梁、建筑物等。
  • 术创作:Three.js 可以用于艺术家创建 3D 作品,如建筑模型、雕塑等。

2.2 Thing.js

2.2.1 Thing.js的特点和优劣势

Thing.js 的特点和优势如下:

  • 易用性:ThingJS 是一个简单易用的平台,用户无需具备复杂的技术知识即可上手使用。它提供了直观的界面和丰富的功能,帮助用户快速实现各种任务。
  • 集成性:ThingJS 支持多种设备和应用程序的集成,可以方便地与其他系统进行数据交换和通信。这使得用户可以在一个统一的平台上管理和控制多个设备和应用。
  • 可扩展性:ThingJS 具有很好的可扩展性,可以轻松地添加新设备并与现有系统集成。这使得企业和组织能够随着业务需求的增长而灵活地扩展其 IoT 基础设施。
  • 安全性:ThingJS 提供了多种安全机制,如加密、身份验证和访问控制,以确保数据的机密性、完整性和可用性。这对于处理敏感信息和保护用户隐私至关重要。
  • 跨平台支持:ThingJS 支持多种操作系统和设备,如 iOS、Android、Windows 和 macOS 等。这使得用户可以在不同的设备上使用 ThingJS,实现无缝的全息体验。
  • 物联网(IoT):ThingJS 是物联网的核心技术,它使各种设备能够相互连接并通过互联网交换数据。这使得设备能够实时收集和共享信息,从而提高效率和便利性。
  • 低功耗:ThingJS 通常具有低功耗特性,这有助于延长设备的使用寿命并降低能源消耗。这对于许多 IoT 应用来说非常重要,因为它们需要在电池供电的设备上运行。
  • 互操作性:ThingJS 支持多种通信协议和技术,如 HTTP/HTTPS、MQTT、CoAP 等。这使得不同厂商的设备可以相互通信,从而促进了整个行业的创新和发展。
  • 数据分析:ThingJS 可以收集大量的数据,这些数据可以通过分析和挖掘来提取有价值的洞察。这可以帮助企业做出更明智的决策,并优化其运营和产品设计。

Thing.js 的缺点如下:

  • 学习曲线较陡峭:由于 Thingjs 是一个基于 WebGL 的 3D 引擎,因此对于初学者来说,可能需要一定的时间来学习和掌握其使用方法。
  • 社区支持有限:虽然ThingJS是一个功能强大的3D物联网平台,但其社区规模和活跃度相对较小,这可能导致在遇到问题时寻求帮助变得困难。
  • 性能问题:Thingjs 在处理大规模场景和复杂模型时,可能会遇到性能瓶颈,导致渲染速度较慢。
  • 插件生态不完善:与一些成熟的 3D 引擎相比,Thingjs 的插件生态相对较弱,这可能会影响到开发者在项目中使用各种插件的能力。
  • 对硬件要求较高:Thingjs 需要较高的 CPU 和 GPU 性能来保证流畅的渲染效果,这可能会限制一些低性能设备的使用。
  • 定制性有限:ThingJS的API和功能相对固定,对于一些特定的定制需求可能无法满足。
  • 文档和教程不够完善:虽然ThingJS提供了一定的文档和教程,但对于一些高级功能和实际应用场景的讲解可能不够详细和全面。

2.2.2 Thing.js的应用领域

  1. 物联网(IoT):ThingJS 可以帮助企业和个人将各种设备和传感器连接到互联网,实现远程监控和管理。
  2. 智能家居:通过 ThingJS 系统,用户可以远程控制家中的电器、照明等设备,实现智能化生活。
  3. 工业自动化:ThingJS 可以应用于工厂、仓库等场景,实现设备的远程监控和控制,提高生产效率。
  4. 智能城市:ThingJS 可以帮助城市管理者实时监控城市的交通、环境等信息,提高城市管理水平。

Three.js 与 Thing.js的对比

数字孪生——thing.js和three.js_第2张图片

2.4 数字孪生实际案例

https://finemaxdemo.fanruan.com/
https://store.thingjs.com/zeroCode/getProjects

五、总结

数字孪生在未来物流领域有较大的潜力,物理实体的虚拟数字模型的前端技术实现,根据Three.js 和 Thing.js 的特性:Three.js 更适合创建一个复杂的3D场景或游戏,Thing.js 更适合构建一个适用于物联网设备的可交互3D模型和界面;且根据开发团队的人员配置,中小企业使用Thing.js或许更合适。

六、Thing.js的使用

1、说明

ThingJS 3D 可视化开发平台提供在线开发离线开发两种开发方式。其中离线开发又分离线开发 SDK 版(坐席版)和离线开发网络版。
在线开发VIP,每个VIP支持多名(上限为9人)开发人员共同进行项目开发
ThingJS 离线开发 SDK 版需要一个U盘形式的Key,每个Key仅供一台计算机使用,如果企业有多位 ThingJS 开发人员要投入开发,如果购买多个离线开发 SDK 版授权来支持,这在成本、便捷度两方面都不是特别合适。
ThingJS 离线开发网络版是一个可在局域网环境部署的开发服务器。它通过和开发版本管理工具 Git 结合,理论上可支持不限人数的 ThingJS 开发人员共同进行项目开发。离线开发网络版所支持的本机开发 IDE(VSCode)+Git 方式,在开发习惯跟大部分开发人员的日常工作习惯相同,可让开发人员在开发方式上完成无缝切换。
现因条件限制,下面用在线开发工具进行示例讲解。

2、使用示例讲解

2.1 在线开发工具界面介绍

官网:https://www.thingjs.com/guide/
数字孪生——thing.js和three.js_第3张图片
数字孪生——thing.js和three.js_第4张图片
点击“创建3D项目“进入如下界面,界面会有官方场景,可以选择官方场景进行修改使用,也可新建自己的项目。
数字孪生——thing.js和three.js_第5张图片

2.2 控制对象

下面以官方案例为基础,在上面进行修改,最终渲染出的结果如下图:
数字孪生——thing.js和three.js_第6张图片
可以通过点击界面上的按钮操作场景中的物体,代码中控制对象时通过id来获取的对象(也可通过其它属性来获取),在界面如下操作中可获取对象的id:
数字孪生——thing.js和three.js_第7张图片

对接数据

数据对接的方式有四种:
① Ajax:在 ThingJS 在线开发环境中,内置了 JQuery 库,可以直接使用 JQurey 封装的 Ajax 方法进行数据对接;
② JSONP:由于 JQuery 的 Ajax 请求对 JSONP 进行了封装,因此可以直接使用相关方法请求 JSONP 数据,使用 JQuery 的 Ajax 方法 发起多个 jsonp 请求时,回调函数名不要重复(即 jsonpCallback 的设置不要重复),否则可能会导致回调函数 undefined ;
③ WebSocket:WebSocket 的优点在于服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,真正实现了数据的实时双向通信。并且 WebSocket 通信不受同源策略的限制,即不存在跨域问题;
④ MQTT:MQTT是一个轻量级协议,使用MQTT协议的中心是broker(服务器/代理),客户端通过订阅消息和发布消息进行数据交互。使用MQTT方式的步骤如下:
1.直接连接MQTT服务器(需支持websocket访问,Mosquitto支持websocket的配置可自行百度)
2.引用第三方 mqtt库
3.MQTT数据对接
浏览器端的示例如下 (在客户端可调用 end() 方法主动关闭 Mosquitto 连接):

// 引用第三方 mqtt 库,详见 https://github.com/mqttjs/MQTT.jsTHING.Utils.dynamicLoad(['https://www.thingjs.com/static/lib/mqtt.js'], function () {
    init();
});
var client = null;
// 创建一个Mosquitto连接
client = mqtt.connect("wss:www.3dmmd.cn:8086");
client.subscribe("/public/TEST/dev1");
// 连接成功后发送数据
client.on("message", function (topic, payload) {
    console.log('data:' + payload);
    obj.setAttribute("monitorData/温度", payload);
    changeColor(obj);
});
// 关闭连接
client.end();       

下面我们用Ajax对接方式为例,实现当接口返回temper信息时,将场景中的旗帜变成红色,代码及预览效果如下图:
数字孪生——thing.js和three.js_第8张图片
上面讲了线上开发界面、控制对象、数据对接,完整代码如下:

// 第一步:加载场景代码----------如果这一步漏掉了, 场景预览界面会提示“脚本加载失败”
var app = new THING.App({
    url: 'https://www.thingjs.com/static/models/factory',  // 场景地址
    background: '#000000',
    env: 'Seaside',
});

// 创建Thing。可创建多个Thing,创建的Thing都会在场景中展示
var obj = app.create({
    name: '烟囱_02',
    url: '/api/models/9121e5476dd1496c9896f649853f042c/0/gltf/', 
    position: [20, 0, 0],
    angle: 0,
    complete: function () {  // 创建Thing完成后的回调
    }
});

/** 
 *   on事件用于通知App初始化完成或场景、物体加载完成
*/
app.on('load',function(){
    // 1.旗帜的显示与隐藏
    var flag = app.query('#6555')[0]   //通过id获取对象数组,取第一个为旗帜
    new THING.widget.Button('隐藏旗帜',function(){
        flag.visible = false
    })
    new THING.widget.Button('显示旗帜',function(){
        flag.visible = true
    })
    // 2.汽车的移动
    var car = app.query('#2271')[0]
    new THING.widget.Button('汽车移动',function(){
        var path = [[10,0,-10],[10,0,10]]
        car.movePath({
            orientToPath:true,
            path:path,
            time:1000
        })
    })
    // 3.对接数据
    $.ajax({
        type: "get",
        url: "https://3dmmd.cn/getMonitorDataById",
        data: { "id":1605 },
        dataType: "json",
        success: function (d) {
            if(d.data.temper){
                // 将旗帜的颜色变成红色
                flag.style.color='red'
            }
        }
    }); 
})

// 天空效果
var effect = {
    showHelper:true, // 显示光源位置
    turbidity:13.4, // 光源扩散大小
    rayleigh:1.84, // 大气散射
    time:16.093, // 时间 [0~24]
    beta:299, // 水平角度
};
app.skyEffect = effect;

2.4 项目部署

2.4.1 在线部署

部署步骤:

  1. 在顶部菜单栏中,选择项目 > 申请项目在线部署。

数字孪生——thing.js和three.js_第9张图片
2. 选择年度在线部署或季度在线部署后,选择下一步,即可进入支付页面,支付完成部署成功后显示如下:
数字孪生——thing.js和three.js_第10张图片
在线部署项目更新:
为方便开发人员对已部署的项目内容进行更新,ThingJS在线开发平台提供“项目更新”的功能。
点击项目-项目更新-在线部署项目更新-确定即可完成项目更新。

2.4.2 离线部署

开发人员可将所完成开发的项目离线部署到自己的私有服务器上。这时候就会使用到该项目的“ThingJS项目离线部署包”。
“ThingJS 项目离线部署包”可由已开通“VIP 商业开发者”或购买“离线部署永久授权”的账号从“在线开发”环境进行下载;或由ThingJS离线开发环境生成。
硬件配置
安装服务器推荐配置

数字孪生——thing.js和three.js_第11张图片
(注: (1)若需在CPU为ARM的Linux服务器上进行部署,请先咨询平台客服;
(2)不支持在Docker中进行部署。)
浏览器客户端推荐配置
数字孪生——thing.js和three.js_第12张图片
具体操作参考:https://support.thingjs.com/book/thingjs-lowcode10/63842fcfddc46115804148b9

七、 Three.js的使用

1、说明

threejs是一个js库,在项目的开发环境引入threejs,直接通过npm命令行安装就行,为了方便,下面的示例讲解中直接在.html文件中直接引入threejs。
three.js 依赖于ES module,因此任何引用它的script标签必须使用type=“module”。

2、使用示例讲解

2.1 创建场景

下面我们创建一个简单的场景,里面是一个绿色的立方体,具体操作步骤及说明均在代码里有注释,代码如下:

DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>My first three.js apptitle>
    <style>
      body { margin: 0; }
    style>
  head>
  <body>
    <script type="module">
      // 引入three.js
      import * as THREE from 'https://unpkg.com/three/build/three.module.js'; 

      //  1. 创建一个场景
      const scene = new THREE.Scene();
      /** 
       *  2. 创建一个透视摄像机
       *  第一个参数是视野角度(FOV)。视野角度就是无论在什么时候,你所能在显示器上看到的场景的范围,它的单位是角度(与弧度区分开)
       *  第二个参数是长宽比(aspect ratio)。 也就是你用一个物体的宽除以它的高的值。
       *  第三个参数和第四个参数分别是近截面(near)和远截面(far)。 当物体某些部分比摄像机的远截面远或者比近截面近的时候,该这些部分将不会被渲染到场景中。
       * 
      */
      const camera = new THREE.PerspectiveCamera( 90, window.innerWidth / window.innerHeight, 0.1, 1000 );
      //  3. 创建一个渲染器
      const renderer = new THREE.WebGLRenderer();
      //  4. 设置渲染器的尺寸
      renderer.setSize( window.innerWidth, window.innerHeight );
      //  5. 这一步非常重要:将renderer的dom元素添加到HTML文档的body中。这就是渲染器用来显示场景给我们看的元素。
      document.body.appendChild( renderer.domElement );
      //  6. 到这里canvas元素已经准备好了,我们可以往canvas中添加我们想要的模型了
      const geometry = new THREE.BoxGeometry( 1, 1, 1 );  // 创建一个立方体的形状,注意这里只是描述了形状,这里还没有物体
      const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } ); // 创建一个MeshBasicMaterial材质
      const cube = new THREE.Mesh( geometry, material ); // 创建一个Mesh表示一个虚拟的物体,参数就是上面创建的geometry立方体和material材质,这里才有了物体
      //  7. 把物体添加到场景中,调用scene.add()的时候,物体将会被添加到(0,0,0)坐标,此时摄像机和立方体重叠在一起的
      scene.add( cube );
      //  8. 调整摄像机的坐标,不然摄像机和物体重叠,看不见物体
      camera.position.set(3,-1,5)  // x,y,z
      //  9. 渲染场景
      renderer.render( scene, camera );
    script>
  body>
html>

2.2 控制对象

1. 通过控制器操作物体

通过three.js的控制器对物体模型进行操控,下面以拖放控制器(DragControls)作为例子来操作一下物体。
因为我这里下载three.js文件包太慢了,之前有个vue项目安装了three.js,所以下面改用vue项目中使用three.js做为例子。
DragControls 是一个附加组件,必须显式导入。

// 引入 DragControls
import { DragControls } from "three/addons/controls/DragControls.js";

/** 1.创建一个拖拽控制器
 *  第一个参数就是我们上面创建的立方体,该参数是一个数组;
 *  第二个参数就是我们上面创建的相机;
 *  第三个参数就是我们上面创建的渲染器的dom元素
 */
const controls = new DragControls([cube], camera, renderer.domElement); 
// 2.添加事件侦听器
controls.addEventListener("drag", function () {
      renderer.render(scene, camera);
});

如上就实现了一个非常简单的拖拽物体效果。

2. 通过点击物体操作

如果想要实现常规的通过点击物体来操作物体,那么我们就要需要先了解一下三维空间点击事件的原理
在三维空间内判断鼠标点击的是哪个模型,核心的原理是射线碰撞,即从相机(camera)的中心点到屏幕上鼠标点组成一条射线,计算三维场景内哪些模型被射线穿过。主要是涉及了三个坐标系的转换:

  1. 将鼠标点从屏幕坐标系(y正向向下,x轴向右)转换到三维空间里的可视窗口的二维坐标系(正常的二维坐标系,但xy的区间是-1~1);
  2. 将三维空间里的可视窗口的二维坐标系点转换为三维空间的xyz坐标系;
  3. 将2转换后的点和camera的中心点组成射线。
    要获取被点击的物体,我们需要用到两个类:
    Raycaster: 这个类用于进行raycasting(光线投射)。 光线投射用于进行鼠标拾取(在三维空间中计算出鼠标移过了什么物体)。
    Vector2: 这个类用于表示2D vector(二维向量)。 一个二维向量是一对有顺序的数字(标记为x和y),可用来表示很多事物,例如:
  • 一个位于二维空间中的点(例如一个在平面上的点)。
  • 一个在平面上的方向与长度的定义。在three.js中,长度总是从(0, 0)到(x, y)的 Euclidean distance(欧几里德距离,即直线距离), 方向也是从(0, 0)到(x, y)的方向。
  • 任意的、有顺序的一对数字。
    其他的一些事物也可以使用二维向量进行表示,比如说动量矢量、复数等等;但以上这些是它在three.js中的常用用途。
    对 Vector2 实例进行遍历将按相应的顺序生成它的分量 (x, y)。
    现在实现一个点击立方体,使之变为红色的效果,完整代码及说明如下:
<script>
import * as THREE from "three";
import { DragControls } from "three/addons/controls/DragControls.js";

export default {
  name: "threejs-test",
  data() {
    return {
      scene: null,
      camera: null,
    };
  },
  mounted() {
    // ----------------------  第一部分内容:创建场景  --------------------------

    //  1. 创建一个场景
    const scene = new THREE.Scene();
    this.scene = scene;
    /**
     *  2. 创建一个透视摄像机
     *  第一个参数是视野角度(FOV)。视野角度就是无论在什么时候,你所能在显示器上看到的场景的范围,它的单位是角度(与弧度区分开)
     *  第二个参数是长宽比(aspect ratio)。 也就是你用一个物体的宽除以它的高的值。
     *  第三个参数和第四个参数分别是近截面(near)和远截面(far)。 当物体某些部分比摄像机的远截面远或者比近截面近的时候,该这些部分将不会被渲染到场景中。
     *
     */
    const camera = new THREE.PerspectiveCamera(
      90,
      window.innerWidth / window.innerHeight,
      0.1,
      1000
    );
    this.camera = camera;
    //  3. 创建一个渲染器
    const renderer = new THREE.WebGLRenderer();
    //  4. 设置渲染器的尺寸
    renderer.setSize(window.innerWidth, window.innerHeight);
    //  5. 这一步非常重要:将renderer的dom元素添加到HTML文档的body中。这就是渲染器用来显示场景给我们看的元素。
    document.body.appendChild(renderer.domElement);
    //  6. 到这里canvas元素已经准备好了,我们可以往canvas中添加我们想要的模型了
    const geometry = new THREE.BoxGeometry(1, 1, 1); // 创建一个立方体的形状,注意这里只是描述了形状,这里还没有创建物体
    const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 }); // 创建一个MeshBasicMaterial材质
    const cube = new THREE.Mesh(geometry, material); // 创建一个Mesh表示一个虚拟的物体,参数就是上面创建的geometry立方体和material材质,这里才有了物体
    //  7. 把物体添加到场景中,调用scene.add()的时候,物体将会被添加到(0,0,0)坐标,此时摄像机和立方体彼此在一起
    scene.add(cube);
    //  8. 调整摄像机的坐标,不然摄像机和物体重叠,看不见物体
    camera.position.set(3, -1, 5); // x,y,z
    //  9. 渲染场景
    // renderer.render(scene, camera);
    (function animate() {
      requestAnimationFrame(animate);
      renderer.render(scene, camera);
    })();

    // ----------------------  第二部分内容: 控制对象  --------------------------
    // 1.利用控制器操控对象
    const controls = new DragControls([cube], camera, renderer.domElement); // 创建一个拖拽控制器
    // 添加事件侦听器
    controls.addEventListener("drag", function () {
      renderer.render(scene, camera);
    });
    // 2.通过点击操作对象
    const _this = this;
    document
      .getElementsByTagName("body")[0]
      .addEventListener("click", function (event) {
        const raycaster = new THREE.Raycaster();
        const mouse = new THREE.Vector2();
        //将鼠标点击位置的屏幕坐标转换成threejs中的标准坐标
        mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
        mouse.y = 1 - (event.clientY / window.innerHeight) * 2;
        // 通过鼠标点的位置和当前相机的矩阵计算出raycaster
        raycaster.setFromCamera(mouse, _this.camera);
        // 获取raycaster直线和所有模型相交的数组集合
        var intersects = raycaster.intersectObjects(_this.scene.children);
        //将所有的相交的模型(被点击的模型)的颜色设置为红色
        for (var i = 0; i < intersects.length; i++) {
          intersects[i].object.material.color.set(0xff0000);
        }
      });
  },
};
</script>

注意第49行——52行渲染场景的代码有所调整,应用了requestAnimationFrame()方法,它可以保证合适的时间触发renderer.render()方法,一般为 60次/秒,如果不应用requestAnimationFrame(),你可能会在点击物体后看不到任何变化。
因上面仅是示例代码,故所有功能都写到mounted生命周期中的,平时项目开发中,还是要进行函数的封装。

3、数据对接及项目部署

由于three.js是一个js库,所以它的数据对接及项目部署同一般项目一样,无需特殊操作。

你可能感兴趣的:(javascript,前端)