基于 HTML5 WebGL 的 3D 渲染引擎构建工厂运做系统

前言

今天为你们带来一个很酷的做品,依然运用了强大的 HT for Web 的 3D 图形组件,动做流畅性能好,你们能够先来欣赏一下效果!javascript

点我进入! java

在这里插入图片描述
总体风格为科技金属风,制做精良,因为上传 gif 大小有限制,因此务必打开连接查看细节演示!

代码实现

作完场景后,首先咱们要对它进行一些基本的设置,如:dom

// 设置 camera 的位置
gv.setEye([457, 9047, 434])
// 设置中心点位置
gv.setCenter([-4, -1, 0])
// 设置远端距离
gv.setFar(500000)
复制代码

设置后可让场景在反序列化后可以显示出咱们想要的展现角度,设置远端位置可以避免形成场景显示不彻底等问题。 函数

在这里插入图片描述

进入动画

为了使其看起来有一个进入的过程,咱们给场景增长一个入场的动画来增色:性能

ht.Default.startAnim({
    duration: 3000, // 动画周期毫秒数,默认采用`ht.Default.animDuration`
    action: function (v) { // action 函数必须提供,实现动画过程当中的属性变化。
        gv.setEye([gv.getEye()[0] + (1117 - gv.getEye()[0]) * (v / 5), gv.getEye()[1] + (450 - gv.getEye()[1]) * (v / 5), gv.getEye()[2] + (1139 - gv.getEye()[2]) * (v / 5)])
    },
    finishFunc: function () { // 动画结束后调用的函数。
        gv.scene = {
            eye: ht.Default.clone(gv.getEye()),
            center: ht.Default.clone(gv.getCenter()),
            far: ht.Default.clone(gv.getFar()),
            near: ht.Default.clone(gv.getNear())
        }
    }
})
复制代码

这个动画咱们的思路是经过改变 camera 的位置来的实现,使用动画函数咱们能够在指定的时间周期内完成动画,可理解为将某些属性由起始值逐渐变到目标值的过程。经过在 action 函数中咱们对 carmera 进行细致地调整,就能够实现完美的入场效果了。finishFunc 函数中咱们作了一个复制的操做,目的是要记住这个位置,以便于咱们后面的功能实现,这个稍后会提到。动画

视角控制

对了,咱们还要对整个场景的视角及范围作限制:ui

var mapInteractor = new ht.graph3d.MapInteractor(gv)
gv.setInteractors([
    mapInteractor
])
gv.mp(function (e) {
    if (e.property === "eye") {
        if (gv.getEye()[0] > 3500) {
            gv.getEye()[0] = 3500
        }
        if (gv.getEye()[0] < -3500) {
            gv.getEye()[0] = -3500
        }
        if (gv.getEye()[1] > 9000) {
            gv.getEye()[1] = 9000
        }
        if (gv.getEye()[2] > 3500) {
            gv.getEye()[2] = 3500
        }
        if (gv.getEye()[2] < -3500) {
            gv.getEye()[2] = -3500
        }
    }
})
复制代码

这样能够限制翻转到场景底面,而后再对 eye 作限制防止在拉远的时候超出天空球包裹的范围。spa

接下来咱们要加一个场景视角复位的功能:设计

gv.mi(function (e) {
    if (e.kind === 'doubleClickBackground') {
        gv.moveCamera(gv.scene.eye, gv.scene.center, true)
    }
    ...
})
复制代码

事件监听一下,在双击的时候经过 moveCamera() 来移动中心点的位置,坐标就是咱们在入场动画的操做中记录的位置。3d

为了增强性能及便利性,咱们在点击事件中再添加一个控制面板开关的的逻辑,这样能够简约化显示:

if (e.kind === 'clickData') {
   if (e.data.getTag() === '按钮') {
        var status = dm.getDataByTag('面板1').s('3d.visible')
        for (var i = 1; i <= 10; i++) {
            dm.getDataByTag('面板' + i).s('3d.visible', !status)
        }
    }
}
复制代码

经过对 2D 面板的属性改变来实现以下效果:

在这里插入图片描述
面板数值的变化也经过绑定的属性来修改,为了作演示,我用一些随机数来代替,这里就很少说了。

动画实现

而后咱们要把管道的流动、履带的运行、回转窑的运动以及磨轮转动等动画先实现出来:

function flow(name1, name2) {
    for (var i = 1; i <= 6; i++) {
        // uv 偏移
        if (name2) {
            dm.getDataByTag(name2 + i).s('shape3d.uv.offset', [dm.getDataByTag(name2 + i).s('shape3d.uv.offset')[0] + 0.005, dm.getDataByTag(name2 + i).s('shape3d.uv.offset')[1]])
        }
        // 设默认值
        else {
            dm.getDataByTag(name1 + i).s('shape3d.uv.offset', [0,0])
        }
    }
}
// 储料罐
function tank(name, num, v) {
    for (var i = 1; i <= 8; i++) {
        dm.getDataByTag(name + i).setScaleTall(dm.getDataByTag(name + i).getScaleTall() + (num[i - 1] - dm.getDataByTag(name + i).getScaleTall()) * v)
    }
}
// 滚轮
function roller(name) {
    for (var i = 1; i <= 12; i++) {
        if (i % 2 === 0) {
            value = -0.1
        }
        else {
            value = 0.1
        }
        if (i <= 8) {
            dm.getDataByTag(name + i).r3(dm.getDataByTag(name + i).r3()[0], dm.getDataByTag(name + i).r3()[1] + value, dm.getDataByTag(name + i).r3()[2])
        }
        else {
            dm.getDataByTag(name + i).r3(dm.getDataByTag(name + i).r3()[0] + value, dm.getDataByTag(name + i).r3()[1], dm.getDataByTag(name + i).r3()[2])
        }
    }
}
anim()
function anim() {
    var num = []
    for (var i = 1; i <= 8; i++) {
        num.push(Math.random() * 6)
    }
    ht.Default.startAnim({
        duration: 1000,
        action: function (v, t) {
            dm.getDataByTag('流动2').r3(dm.getDataByTag('流动2').r3()[0] - 0.1, dm.getDataByTag('流动2').r3()[1], dm.getDataByTag('流动2').r3()[2])
            flow('流动', '流动')
            tank('储料罐', num, v)
            roller('滚轮')
        },
        finishFunc: function () {
            anim()
        }
    })
}
复制代码

我把他们统一放在一个动画函数中循环播放,都是一些比较简单的动画,经过使高度、角度等属性的变化来实现相应的动画效果,如代码所示不一一细述。这里我稍微说一下关于这个管道和履带流动的实现思路,我是利用了调整 UV 贴图来完成的。

什么是 UV ?通俗的讲,UV 就是把三维立体模型的外表面剥离下来,展开铺平成二维平面状态,以便进行贴图绘制,就如同香烟盒上的包装图案实际上是在纸盒片状态下印刷完成的同样。

在这里插入图片描述
首先咱们须要绘制一张二方连续贴图(左右或上下能够无缝衔接的贴图),而且依照场景中管道和传送带流动的方向,将 UV 展成长条状,与贴图相匹配。
在这里插入图片描述
在这里插入图片描述
而后咱们在经过代码驱动 UV 向 U 轴的正值方向偏移一个象限,并没有限循环这一动做。回到三维场景中,你就会神奇的发现,管道和传送带在不间断的流动着!
在这里插入图片描述
最后咱们来完成卡车的运行动画,总体流程先设计好:
在这里插入图片描述

var truck1 = dm.getDataByTag('卡车1')
var truck2 = dm.getDataByTag('卡车2')
var truck3 = dm.getDataByTag('卡车3')
var cargo1 = dm.getDataByTag('货斗1')
var cargo2 = dm.getDataByTag('货斗2')
var coal = dm.getDataByTag('货1')
var limestone = dm.getDataByTag('货2')
var panel1 = dm.getDataByTag('面板8')
var panel2 = dm.getDataByTag('面板9')
anim1()
// 出发
function anim1() {
    ht.Default.startAnim({
        duration: 4000,
        easing: function (t) { return (t *= 2) < 1 ? 0.5 * t * t : 0.5 * (1 - (--t) * (t - 2)) },
        action: function (v, t) {
            truck1.p3(truck1.p3()[0], truck1.p3()[1], truck1.p3()[2] + (700 - truck1.p3()[2]) * (v / 10))
            truck2.p3(truck2.p3()[0], truck2.p3()[1], truck2.p3()[2] + (700 - truck2.p3()[2]) * (v / 5))
            truck3.p3(truck3.p3()[0] + (300 - truck3.p3()[0]) * (v / 20), truck3.p3()[1], truck3.p3()[2])
        },
        finishFunc: function () {
            anim2()
        }
    })
}
// 掉头
function anim2() {
    ht.Default.startAnim({
        duration: 1000,
        action: function (v, t) {
            truck1.r3(truck1.r3()[0], truck1.r3()[1] + (180 * Math.PI / 180 - truck1.r3()[1]) * v, truck1.r3()[2])
            truck2.r3(truck2.r3()[0], truck2.r3()[1] + (180 * Math.PI / 180 - truck2.r3()[1]) * v, truck2.r3()[2])
            truck3.r3(truck3.r3()[0], truck3.r3()[1] + (-90 * Math.PI / 180 - truck3.r3()[1]) *  v, truck3.r3()[2])
        },
        finishFunc: function () {
            anim3()
        }
    })
}
// 卸货
function anim3() {
    ht.Default.startAnim({
        duration: 2000,
        action: function (v, t) {
            cargo1.r3(cargo1.r3()[0] + (70 * Math.PI / 180 - cargo1.r3()[0]) * v, cargo1.r3()[1],  cargo1.r3()[2])
            cargo2.r3(cargo2.r3()[0] + (70 * Math.PI / 180 - cargo2.r3()[0]) * v, cargo2.r3()[1],  cargo2.r3()[2])
            panel1.a('进度值', panel1.a('进度值') + (0 - panel1.a('进度值')) * v)
            panel2.a('进度值', panel2.a('进度值') + (0 - panel2.a('进度值')) * v)
            panel1.a('重量', panel1.a('进度值').toFixed(1))
            panel2.a('重量', panel2.a('进度值').toFixed(1))
        },
        finishFunc: function () {
            coal.s('3d.visible', false)
            limestone.s('3d.visible', false)
            anim4()
        }
    })
}
// 卸货
function anim4() {
    ht.Default.startAnim({
        duration: 2000,
        action: function (v, t) {
            cargo1.r3(cargo1.r3()[0] + (0 * Math.PI / 180 - cargo1.r3()[0]) * v, cargo1.r3()[1],  cargo1.r3()[2])
            cargo2.r3(cargo2.r3()[0] + (0 * Math.PI / 180 - cargo2.r3()[0]) * v, cargo2.r3()[1],  cargo2.r3()[2])
        },
        finishFunc: function () {
            anim5()
        }
    })
}
// 返回
function anim5() {
    ht.Default.startAnim({
        duration: 4000,
        easing: function (t) { return (t *= 2) < 1 ? 0.5 * t * t : 0.5 * (1 - (--t) * (t - 2)) },
        action: function (v, t) {
            truck1.p3(truck1.p3()[0], truck1.p3()[1], truck1.p3()[2] + (1180 - truck1.p3()[2]) * (v / 10))
            truck2.p3(truck2.p3()[0], truck2.p3()[1], truck2.p3()[2] + (1180 - truck2.p3()[2]) * (v / 5))
            truck3.p3(truck3.p3()[0] + (1180 - truck3.p3()[0]) * (v / 20), truck3.p3()[1], truck3.p3()[2])
        },
        finishFunc: function () {
            anim6()
        }
    })
}
// 掉头
function anim6() {
    ht.Default.startAnim({
        duration: 1000,
        action: function (v, t) {
            truck1.r3(truck1.r3()[0], truck1.r3()[1] + (0 * Math.PI / 180 - truck1.r3()[1]) * v, truck1.r3()[2])
            truck2.r3(truck2.r3()[0], truck2.r3()[1] + (0 * Math.PI / 180 - truck2.r3()[1]) * v, truck2.r3()[2])
            truck3.r3(truck3.r3()[0], truck3.r3()[1] + (90 * Math.PI / 180 - truck3.r3()[1]) * v, truck3.r3()[2])
        },
        finishFunc: function () {
            panel1.a('进度值', 1)
            panel2.a('进度值', 1)
            panel1.a('重量', 10)
            panel2.a('重量', 10)
            coal.s('3d.visible', true)
            limestone.s('3d.visible', true)
            anim1()
        }
    })
}
复制代码

这个是我实现卡车的整个运做流程的完整代码,分别由几段动画协调组合而成,只要搞清楚顺序以及每个动做实现的逻辑并不难办到,无非就是方向、角度和距离的一些计算,还有面板进度条同步的设置。

在这里插入图片描述
通过咱们的努力后,一个炫酷专业的工厂流程系统的演示咱们就完成了!

总结

在互联网+ 概念飞速发展的今天,有太多的领域在等待着咱们去挖掘,HT for Web 很是适用于各类的智慧建筑,监控系统以及电力、燃气等工业自动化 ( HMI / SCADA ) 领域。但愿看了个人这篇文章,你们能有所启发,挑战更多的不可能!

相关文章
相关标签/搜索