22

基于 HTML5 WebGL 的发动机 3D 可视化系统

 4 years ago
source link: https://segmentfault.com/a/1190000022064682
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

前言

工业机械产品大多体积庞大、运输成本高,在参加行业展会或向海外客户销售时,如果没有实物展示,仅凭静态、简单的图片说明书介绍,无法让客户全面了解产品,不仅工作人员制作麻烦,客户看得也费力。如果能在 Web 上做 3D 设备展示,销售人员可以不限平台随时给客户介绍演示。还可以不受现实条件限制,演示设备拆分和组装的过程,展示产品内部结构和动态运作时的效果,让客户更直观了解产品的部件组成,更准确、全面地了解产品的功能和特点,大大降低了沟通成本。为了解决这些行业痛点,本篇文章采用 Hightopo 的 HT for Web 产品实现了一个发动机设备 3D 可视化案例。

系统预览

预览地址:基于 HTML5 WebGL 的发动机 3D 可视化系统( http://www.hightopo.com/demo/engine/

maiyYfu.png!web

场景搭建

发动机模型是设计师通过 3ds Max 建模,然后导出 obj 与 mtl 文件,在 HT 中解析 obj 与 mtl 文件生成 3D 场景中可用的模型(可参考 obj 手册《 通过JSON加载 》章节),因为各个部件需要单独操作,所以设备模型拆分为多个 obj 文件后导入。

2D 面板部分则是通过 HT 的矢量绘制,我们需要创建 ht.graph.GraphView 和 ht.graph3d.Graph3dView 来呈现 2D 和 3D 的内容。相关代码如下:

var g2d = new ht.graph.GraphView();
var g3d = new ht.graph3d.Graph3dView();
// 将 3D 组件加入到 body 下
g3d.addToDOM();
// 将 2D 组件加入到 3D 组件的根 div 下
g2d.addToDOM(g3d.getView());

功能实现

设备拆解动画

bm636rI.gif

当我们点击“展开”按钮时,给各个动画设置不同的延迟,使动画错开执行,以达到更好的视觉效果,让2D图纸和3D场景更好地联动起来。

ueQBn2F.gif

如果我们的每个动画都匀速运行,那看起来难免有些单调。于是我给不同动画加上了不同的 Easing 函数,Easing 函数通过定义不同曲线的数据公式方式,来描述每一帧回调时需要改变图形参数属性的幅度,从而达到均匀、先快后慢、先慢后快,甚至先超出起始值和结束值再慢慢恢复的各种动画特效。这里还有个例子可以帮助我们更直观的感受不同 Easing 函数的效果:《 From Easing 》。

例如图纸中心的跟随部件拆解旋转放大的圆环,我给它设置了 Easing.backBoth 缓动效果,代码如下:

// 圆环动画
animCenter(data) {
    ht.Default.startAnim({
        duration: 4500,
        // 设置缓动函数
        easing: Easing.backBoth,
        // 延迟一秒执行动画
        delay: 1000,
        action: function (v, t) {
            // 修改图元缩放值
            data.setScale(1 + v \* 0.9, 1 + v \* 0.9);
            // 修改图元旋转角度
            data.setRotation(Math.PI \* v);
        },
    });
}

// 缓动函数
Easing.easeOutStrong = function (t) {
    var BACK\_CONST = 1.70158;
    if ((t \*= 2) < 1) {
        return .5 \* (t \* t \* (((BACK\_CONST \*= (1.525)) + 1) \* t - BACK\_CONST));
    }
    return .5 \* ((t -= 2) \* t \* (((BACK\_CONST \*= (1.525)) + 1) \* t + BACK\_CONST) + 2);
};

设备部件的拆解动画,是通过改变节点的坐标和旋转角度来实现的,代码如下:

// targetP3 为拆解后的坐标,p3为当前坐标
var p3 = node.p3();
var offset = [targetP3[0] - p3[0], targetP3[1] - p3[1], targetP3[2] - p3[2]];

// targetR3 为拆解后的旋转角度,r3为当前旋转角度
var r3 = node.r3();
var offset = [targetR3[0] - r3[0], targetR3[1] - r3[1], targetR3[2] - r3[2]];

// 拆解动画
ht.Default.startAnim({
    duration: 2000,
    // 设置缓动函数
    easing: Easing.easeOutStrong,
    delay: 1000,
    action: function (v, t) {
        // 修改节点坐标
        node.p3(p3[0] + offset[0] * v, p3[1] + offset[1] * v, p3[2] + offset[2] * v);
        // 修改节点旋转角度
        node.r3(r3[0] + offset[0] * v, r3[1] + offset[1] * v, r3[2] + offset[2] * v);
    },
});

// 缓动函数
Easing.easeOutStrong = function (t) {
    return 1 - (--t) * t * t * t;
};

还有图纸两侧的面板,可以通过设置它的裁剪方向和裁剪比例实现隐藏效果,代码如下:

// 设置图元裁剪方向为从右到左
node.s('clip.direction', 'left');

// 裁剪动画
ht.Default.startAnim({
    duration: 1800,
    easing: Easing.easeOutStrong,
    action: function (v, t) {
        // 修改图元裁剪比例
        node.s('clip.percentage', 1 - v);
    }
});

// 缓动函数
Easing.easeOutStrong = function (t) {
    return 1 - (--t) \* t \* t \* t;
};

设备内部运作动画

AnEBB3z.gif

接下来我们实现视角变化动画来观察设备不同部件构造,3D 场景中的视角是由 Graph3dView 提供的 center(目标点坐标)和 eye(摄像机坐标)两个参数决定的,所以视角动画只要动态改变这两个参数,这里用了 HT 提供的 moveCamera 方法实现,代码如下:

g3d.moveCamera([-466, 93, -280], [40, -40, -40], {
    duration: 2500,
    easing: function (t) {
        return 1 - (--t) * t * t * t;
    },
});

设备中一些小的部件吸附在大部件上,会跟随大部件移动旋转。比如这个液压杆,当我们要实现小部件的运动动画时,如果用修改坐标的方式计算起来比较麻烦,所以我们用修改锚点的方式来实现,锚点影响着节点的位置,锚点也是旋转和缩放的中心点。这里通过修改液压杆的 Y 轴锚点实现动画,效果如下:

U7v6buF.gif

相关代码如下:

ht.Default.startAnim({
        duration: 800,
        action: function (v, t) {
            // 修改节点 Y 轴锚点
            node.setAnchor3d(0.5,v,0.5);
        },
};

FF3IJ3m.gif

点击流动按钮后,我们可以看到管道内有液体流动的动画。要实现流动效果,首先我们需要一张二方连续贴图(左右或上下可以无缝衔接的贴图),然后我们再通过代码驱动 UV 向 U 轴的正值方向偏移一个象限,并无限循环这一动作,效果如下:

I3QjmeM.gif

代码如下:

ht.Default.startAnim({
    duration: 2000,
    action: function (v, t) {
        // 修改贴图uv值
        node.s('shape3d.uv.offset', [v, 0]);
    },
};

设备复原动画

ZrQJjia.gif

鼠标移入效果

feuiMjJ.gif

为了能透过外壳清楚的观察到设备内部结构,所以当鼠标悬停在部件上时,我调整了外壳模型透明度并设置模型高亮模式,相关代码如下:

// 设置高亮颜色
ht.Style['highlight.color'] = 'rgba(255,255,255,0.6)';
// 设置当前高亮的模式
g3d.setHighlightMode('mouseover');
// 节点启用高亮效果
data.s('highlight.visible', true);

// 节点设置为可交互
data.s('interactive', true);
// 节点开启交互后,不阻止场景上默认的交互行为
data.s('preventDefaultWhenInteractive', false);

// 监听交互事件
g3d.mi(function (e) {
    // 鼠标移入事件
    if (e.kind === 'onEnter') {
        // 节点开启透明
        data.s('shape3d.transparent', true);
        // 设置节点透明度
        data.s('shape3d.opacity', 0.25);
    }

    // 鼠标移出事件
    if (e.kind === 'onLeave') {
        data.s('shape3d.transparent', false);
    }
});

场景视角限制

最后,我们再对整个场景的视角范围做下限制,代码如下:

// 设置最大角度
ht.Default.graph3dViewMaxPhi = Math.PI \* 1 / 2;
// 设置最小角度
ht.Default.graph3dViewMinPhi = Math.PI \* 1 / 4;

总结

通过案例我们可以感受到,相较于传统方式,设备的三维展示具有更灵活的表现形式和更直观生动的效果,对于出口型企业,生动的演示动画能让外商更快了解产品的工作原理和优势,还能避免因语言误差而造成误解。而且比起普通的工业动画,Web 上的可视化系统展示内容更丰富、自由度更高,后续需求更改也更为灵活、成本更低。

本文使用的设备模型是设计师虚构的核动力发动机,更注重于模型的展示效果,如果应用于实际产品中,还可以制作更还原实际的设备拆解流程,通过线上的 3D 产品操作演练,对工作人员进行产品组装、拆分、维修培训。有兴趣可以参考我们另一个案例《 基于 HTML5 WebGL + WebVR 的 3D 虚拟现实可视化培训系统 》。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK