基于 WebGL 的 HTML5 网络拓扑结构 3D 图

2024-08-30 11:18

本文主要是介绍基于 WebGL 的 HTML5 网络拓扑结构 3D 图,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

现在,3D 模型已经用于各种不同的领域。在医疗行业使用它们制作器官的精确模型;电影行业将它们用于活动的人物、物体以及现实电影;视频游戏产业将它们作为计算机与视频游戏中的资源;在科学领域将它们作为化合物的精确模型;建筑业将它们用来展示提议的建筑物或者风景表现;工程界将它们用于设计新设备、交通工具、结构以及其它应用领域;在最近几十年,地球科学领域开始构建三维地质模型,而且 3D 模型经常做成动画,例如,在故事片电影以及计算机与视频游戏中大量地应用三维模型。它们可以在三维建模工具中使用或者单独使用。为了容易形成动画,通常在模型中加入一些额外的数据,例如,一些人类或者动物的三维模型中有完整的骨骼系统,这样运动时看起来会更加真实,并且可以通过关节与骨骼控制运动。

这些种种都让我们前端开发者觉得如果我们可以不用学习 unity3d 或者其他游戏开发工具就能实现 3D 效果,而且能够精准的靠代码来控制移动或者方向就好了。于是我利用 HT For Web 中的 3D组件 来实现了一个小例子,用了 HT 中 3D组件 的大部分功能,做这个例子就是想把 3D 组件好好的掌握,尽量放进一个例子中,到时候别人有需要就可以参考了。

先来看看整体实现的效果图:
图片描述

创建 3D 场景

用 HT for Web,现有的 3D 模板创建三层底板不是问题,问题是要如何将图中第一层的“电脑”和“机柜组件”放上去?我是在网上 down 下来的 obj 格式的文件,然后我利用 HT 中的 ht.Default.loadObj(objUrl, mtlUrl, params) 函数将模型加载进去,其中的 params 部分可以参考 http://www.hightopo.com/guide/guide/plugin/obj/ht-obj-guide.html
代码如下:

ht.Default.loadObj('obj/机柜组件1.obj', 'obj/机柜组件1.mtl', {  // 加载 obj 文件cube: true,  // 是否将模型缩放到单位1的尺寸范围内,默认为falsecenter: true,  // 模型是否居中,默认为false,设置为true则会移动模型位置使其内容居中shape3d: 'box',  // 如果指定了shape3d名称,则HT将自动将加载解析后的所有材质模型构建成数组的方式,以该名称进行注册finishFunc: function(modelMap, array, rawS3){  // 用于加载后的回调处理 if(modelMap){  device2 = createNode('box', floor1);  // 创建一个节点,在第一层“地板”上device2.p3([x1-120, y1+13, z1+60]);  // 设置这个节点坐标device2.s3(rawS3);  // 设置这个节点大小createEdge(device1, device2);  // 创建连线device3 = createNode('box', floor1);  device3.s3(rawS3);  device3.p3([x1+120, y1+13, z1+60]);  createEdge(device1, device3);  }  }  
}); 

其中 finishiFunc 函数中的三个参数定义如下:

  • modelMap:调用 ht.Default.parseObj 解析后的返回值,若加载或解析失败则返回值为空
  • array:所有材质模型组成的数组
  • rawS3:包含所有模型的原始尺寸

一般在实际应用中我们都会将图元的大小设置为模型的原始尺寸。

警告模型建模

“电脑”上方有个红色的立体能旋转的“警告”,是依靠 ht.Default.setShape3dModel 函数(HT for Web 建模手册)注册的一个 3D 模型,在 ht 中,封装好的建模函数有很多,比较基础的就是球体,圆柱,立方体等等,这边我用的是构造环形的方法 createRingModel 来生成“警告”最外面的环,感叹号的上部分就是用的 createSmoothSphereModel 构造的球体,感叹号的下部分就是用 createSmoothCylinderModel 来构造的圆柱。我一开始直接使用了 3D 模型中封装好的函数,导致后来根本不知道函数中使用的参数是做什么用的,而且也不明白 3D 模型是怎么构成的,然后自己又重新看了前面的“模型基础”,才知道原来 3D 模型采用的一个面,最基础的是三角面,之后复杂的面也是由多个三角面来形成的,然后绕着一根特定的轴旋转之后形成的,当然,这个轴是你来决定的,不同的轴可以生成不同的形状,对于颜色等风格方面的设置可以参考 HT for Web 风格手册。至于如何让这个 3D 模型旋转起来,ht 中封装了 addScheduleTask(Task) 方法,我在第三层 Task 中调用了 ht 封装的一个旋转函数 setRotation 来设置旋转的顺序和方向,并且指定了旋转的对象。以下是自定义“警告”的 3D 模型的方法(注意:因为本例的模型是自定义组合的,如果要设置整体模型的颜色要用 “all.blend” style 属性):

function createAlarm(device, formPane) {var ringModel = ht.Default.createRingModel([ 8, 1, 10, 1, 10, -1, 8, -1, 8, 1 ], null, null, false, false, 100); // 根据xy平面的曲线,环绕一周形成3D模型。var sphereModel = ht.Default.createSmoothSphereModel(8, 8, 0, Math.PI*2, 0, Math.PI, 2); // 构建光滑球体模型 var cylinderModel = ht.Default.createSmoothCylinderModel(8, true, true, 1, 2, 0, Math.PI*2, 8); // 构建光滑圆柱体模型var alarmArr = [ // 组合模型 由三个模型ringModel、sphereModel、cylinderModel组合而成{shape3d: ringModel, // 定义模型类型r3: [Math.PI/2, 0, 0], // 设置旋转角度color: { // 设置模型颜色func: 'style@all.blend', // 数据绑定style样式中的all.blend属性,可通过data.s()获取和设置这个属性}},{shape3d: sphereModel,t3: [0, 4, 0],color: {func: 'style@all.blend',}},{shape3d: cylinderModel,t3: [0, -3, 0],color: {func: 'style@all.blend',}}];ht.Default.setShape3dModel('alarm', { // 注册自定义3D模型shape3d: alarmArr});var alarmTip = createNode('alarm', device); // 创建shape3d为alarm的节点alarmTip.s3([2, 2, 2]); // 设置节点大小alarmTip.p3(device.p3()[0], device.p3()[1]+60, device.p3()[2]);alarmTip.s('all.blend', 'red'); // 改变此属性可改变模型的颜色,因为模型创建的时候已经数据绑定了return alarmTip;
}

接下来看看怎么让这个“告警”节点“闪烁”,我是直接将这个动画跟节点绑定,这样可以直接通过节点来控制动画。所以在上面我们创建 alarm 的模型时就可以直接将动画绑在节点上:

if(formPane){alarmNode.scaleFunc = function() { //设置大小变化动画var size = alarmNode.s3(); //获取节点的大小if (size[0] === 2 && size[1] === 2 && size[2] === 2) alarmNode.s3([1, 1, 1]);else alarmNode.s3([2, 2, 2]);alarmNode.scaleTimer = setTimeout(alarmNode.scaleFunc, formPane.v('scaleInterval')); //设置动画}alarmNode.blinkFunc = function(){ //设置闪烁的动画var color = alarmNode.s('all.blend'); //获取节点的style样式if (color === 'red') alarmNode.s({'all.blend': 'yellow'}); //如果节点颜色为红色,那么设置为黄色else alarmNode.s({'all.blend': 'red'});alarmNode.blinkTimer = setTimeout(alarmNode.blinkFunc, formPane.v('blinkInterval'));}alarmNode.rotateFunc = function() { //设置旋转动画alarmNode.setRotation(alarmNode.getRotation() + Math.PI/20); //获取节点当前的旋转角度,在这个旋转角度之上添加 Math.PI/20 个角度alarmNode.rotateTimer = setTimeout(alarmNode.rotateFunc, formPane.v('rotInterval'));}
}

上面的动画我设置了可以通过 form 表单面板上的属性来控制节点闪烁的速度,以及闪烁节点的动画等等,主要说一下这个功能在 form 表单上的实现:

formPane.addRow([ // 向form表单面板上添加一行元素{checkBox: { // 复选框label: 'Enable Blink', // 复选框对应的文本内容selected: true, // 设置选中复选框onValueChanged: function(){ // 复选框值变化时回调的函数var data = dataModel.getDataByTag('colorAlarm'); // 通过tag标签获取节点if (this.getValue()) { // 获取复选框当前值true/falsedata.blinkTimer = setTimeout(data.blinkFunc, formPane.v('blinkInterval')); // 直接通过设置节点的blinkTimer来设置动画}else {clearTimeout(data.blinkTimer); // 清除动画}}}},{id: 'blinkInterval', // form可以通过getValue(简写为v)来获取这个item的值slider: { // 设置了该属性后HT将根据属性值自动构建ht.widget.Slider对象,并保存在element属性上min: 0, // 滑动条最小值max: 1000, // 滑动条最大值step: 50, // 滑动条步进value: 500, // 当前滑动条的值}}
], [0.1, 0.1]); // 设置这行的两个item元素的宽度小于1的值为比例

管线上小球的流动

最后来说说 3D 管线上的小球流动的部分,这个功能确实非常实用,而且做出来的效果也确实不错,跟大家分享~

首先,创建一条连线连接起始节点和结束节点并设置这个连线的样式,用 ht.Edge 可以将连线吸附在起始节点和结束节点上,这样移动这两个节点中的任意一个节点连线都会跟着节点移动的位置变化,非常方便:

var polyline = new ht.Edge(source, target); // 创建连线
dataModel.add(polyline); // 将连线添加进数据容器中
polyline.s({'edge.width': 5, // 连线宽度'edge.type': 'points', // 连线类型 为points时连线走向将由edge.points属性决定,用于绘制折线'edge.points': [ // 可设置类型为ht.List的{x:100, y:100}格式的点对象数组,当edge.type为points时起作用{x: source.getPosition3d()[0]+200, y: source.getPosition3d()[2], e: source.getPosition3d()[1]},{x: target.getPosition3d()[0]+400, y: target.getPosition3d()[2], e: target.getPosition3d()[1]}],'edge.segments': [1, 4], // 用于描述点连接样式,数组元素为整型值'shape3d': 'cylinder', // 圆柱'shape3d.color': 'rgba(242, 200, 40, 0.4)','shape3d.resolution': 30, // 微分段数,可以决定曲线的平滑度'edge.source.t3': [20, 0, 0], // 连线source端偏移,[tx, ty, tz]格式,默认为空'edge.target.t3': [20, 0, 0] // 连线target端偏移,[tx, ty, tz]格式,默认为空
});

因为我们在创建连线的时候设置的 points 仅为曲线上的两个点,所以如果要获取曲线目前形成的点,是缺少 source 和 target 两个点的,我们重新设置一个数组,将这两个点添加进去,后面获取曲线上所有点时会用上:

var list = new ht.List();
list.push({x: source.getPosition3d()[0], y: source.getPosition3d()[2], e: source.getPosition3d()[1]}); // 向数组中添加source点
polyline.s('edge.points').each(function(item){ // 添加style属性中已设置的两个点list.push(item);
});
list.push({x: target.getPosition3d()[0], y: target.getPosition3d()[2], e: target.getPosition3d()[1]}); // 添加target点

然后创建一个在管线上滑动的小球节点,这是仅是设置节点,真正添加进数据容器 dataModel 中需要设置完小球的坐标时再添加,如果没有给节点设置位置就将节点添加进数据容器中,节点的初始位置就是 3D 场景的正中心 [0, 0, 0] 的位置。小球滑动的动画代码如下:

var ball = new ht.Node(); // 创建小球节点
ball.s({ // 设置小球节点的样式'shape3d': 'sphere', // 设置小球的3d模型为球形'shape3d.color': 'rgba(40, 90, 240, 0.4)' // 设置3d模型的颜色
});var delta = 10, flag = 0;
setInterval(function(){flag++;var length = (polyline.a('total') || 0) % polyline.a('length') + delta; // 小球当前走过的曲线长度var cache = ht.Default.getLineCacheInfo(list, polyline.s('edge.segments')); // 获取曲线上的点的信息var lineLength = ht.Default.getLineLength(cache); // 获取曲线的总长度polyline.a('length', lineLength - 50); // 因为我设置了edge的t3(相当于2d中的offset),所以线段长度实际没有那么长var offset = ht.Default.getLineOffset(cache, length); // 曲线根据曲线上点的信息的偏移量ball.setPosition3d(offset.point.x + 10, offset.point.y, offset.point.z); // 设置节点的坐标polyline.a('total', length);if(flag === 1) dataModel.add(ball); // 这时候节点已经有了坐标了,可以添加进数据容器中了
}, 10);

特殊的多边形

我们还可以看到第二层上有两个特殊的多边形“平行四边形”和“梯形”,平行四边形是靠 createParallelogramModel 模型函数,这个函数比较简单,createExtrusionModel(array, segments, top, bottom, resolution, repeatUVLength, tall, elevation),array 是你要形成的图形的坐标点,这边只是针对于 xz 轴上画的平面图形,segments 指的是如何连接这几个坐标点,可参考 HT for Web 形状手册,top 和 bottom 就是让你选择是否有顶部或者底部,resolution 微分段数,我们描绘一段曲线的时候可能只要确认几个个别的点然后在每两个点之间的连线上把它分成多个段,这样这条线段就会变得平滑,ht 为了用户能够轻松操作这些线段,就封装了这一个参数,repeatUVLength 默认为空,设置值后顶部和底部的贴图将根据制定长度值进行重复,tall 模型的高度,默认为 5,elevation 模型中心的 y 轴位置,默认值为 0,设置这个值可以使 xz 上的平面绕着 y 轴旋转。

设备的环状摆置

底层的一个环形的效果是通过一个算法来实现的,环形得确认这个环形上有多少个元素,然后算每两个之间的角度,在通过 sin、cos 来计算每一个元素的位置,得出了如下代码:

names = ['设备2', '设备3', '设备4', '设备5', '设备6', '设备7', '设备8', '设备9'];  
names.forEach(function(name, index) {  x = 400, y = 200, angle = 45, r = 120;  x = x3 + Math.sin((2 * Math.PI / 360) * angle * index) * r;  y = z3 + Math.cos((2 * Math.PI / 360) * angle * index) * r;  device = createRect([x, y3 + 15, y], [w * 0.1, 15, h * 0.1], '', '', floor3);  createEdge(device5, device);  
}); 

其他如果还有不懂的部分可以去官网(http://hightopo.com)查看对应的手册,或者留言私信都可以。

最后附上本文例子:http://www.hightopo.com/demo/3DTopology/index.html

这篇关于基于 WebGL 的 HTML5 网络拓扑结构 3D 图的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


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

相关文章

使用Java实现通用树形结构构建工具类

《使用Java实现通用树形结构构建工具类》这篇文章主要为大家详细介绍了如何使用Java实现通用树形结构构建工具类,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录完整代码一、设计思想与核心功能二、核心实现原理1. 数据结构准备阶段2. 循环依赖检测算法3. 树形结构构建4. 搜索子

利用Python开发Markdown表格结构转换为Excel工具

《利用Python开发Markdown表格结构转换为Excel工具》在数据管理和文档编写过程中,我们经常使用Markdown来记录表格数据,但它没有Excel使用方便,所以本文将使用Python编写一... 目录1.完整代码2. 项目概述3. 代码解析3.1 依赖库3.2 GUI 设计3.3 解析 Mark

Vue中组件之间传值的六种方式(完整版)

《Vue中组件之间传值的六种方式(完整版)》组件是vue.js最强大的功能之一,而组件实例的作用域是相互独立的,这就意味着不同组件之间的数据无法相互引用,针对不同的使用场景,如何选择行之有效的通信方式... 目录前言方法一、props/$emit1.父组件向子组件传值2.子组件向父组件传值(通过事件形式)方

css中的 vertical-align与line-height作用详解

《css中的vertical-align与line-height作用详解》:本文主要介绍了CSS中的`vertical-align`和`line-height`属性,包括它们的作用、适用元素、属性值、常见使用场景、常见问题及解决方案,详细内容请阅读本文,希望能对你有所帮助... 目录vertical-ali

浅析CSS 中z - index属性的作用及在什么情况下会失效

《浅析CSS中z-index属性的作用及在什么情况下会失效》z-index属性用于控制元素的堆叠顺序,值越大,元素越显示在上层,它需要元素具有定位属性(如relative、absolute、fi... 目录1. z-index 属性的作用2. z-index 失效的情况2.1 元素没有定位属性2.2 元素处

Python实现html转png的完美方案介绍

《Python实现html转png的完美方案介绍》这篇文章主要为大家详细介绍了如何使用Python实现html转png功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 1.增强稳定性与错误处理建议使用三层异常捕获结构:try: with sync_playwright(

Vue 调用摄像头扫描条码功能实现代码

《Vue调用摄像头扫描条码功能实现代码》本文介绍了如何使用Vue.js和jsQR库来实现调用摄像头并扫描条码的功能,通过安装依赖、获取摄像头视频流、解析条码等步骤,实现了从开始扫描到停止扫描的完整流... 目录实现步骤:代码实现1. 安装依赖2. vue 页面代码功能说明注意事项以下是一个基于 Vue.js

CSS @media print 使用详解

《CSS@mediaprint使用详解》:本文主要介绍了CSS中的打印媒体查询@mediaprint包括基本语法、常见使用场景和代码示例,如隐藏非必要元素、调整字体和颜色、处理链接的URL显示、分页控制、调整边距和背景等,还提供了测试方法和关键注意事项,并分享了进阶技巧,详细内容请阅读本文,希望能对你有所帮助...

Nginx实现前端灰度发布

《Nginx实现前端灰度发布》灰度发布是一种重要的策略,它允许我们在不影响所有用户的情况下,逐步推出新功能或更新,通过灰度发布,我们可以测试新版本的稳定性和性能,下面就来介绍一下前端灰度发布的使用,感... 目录前言一、基于权重的流量分配二、基于 Cookie 的分流三、基于请求头的分流四、基于请求参数的分

基于Canvas的Html5多时区动态时钟实战代码

《基于Canvas的Html5多时区动态时钟实战代码》:本文主要介绍了如何使用Canvas在HTML5上实现一个多时区动态时钟的web展示,通过Canvas的API,可以绘制出6个不同城市的时钟,并且这些时钟可以动态转动,每个时钟上都会标注出对应的24小时制时间,详细内容请阅读本文,希望能对你有所帮助...