2

如何用webgl(three.js)搭建一个3D库房,3D仓库3D码头,3D集装箱,车辆定位,叉车定位可视...

 1 year ago
source link: https://www.cnblogs.com/yeyunfei/p/16826146.html
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

如何用webgl(three.js)搭建一个3D库房,3D仓库3D码头,3D集装箱,车辆定位,叉车定位可视化孪生系统——第十五课

  又是快两个月没写随笔了,长时间不总结项目,不锻炼文笔,一开篇,多少都会有些生疏,不知道如何开篇,如何写下去。有点江郎才尽,黔驴技穷的感觉。

  写随笔,通常三步走,第一步,搭建框架,先把你要写的内容框架搭建出来;第二步,添砖,在框架基础上,填写各部分内容;第三步,加瓦,再写好的内容上进行修改,润湿。然后文章的质量,就因人而异了。但不管怎么说,得写,得练,得经受的起各路能人志士的批评指教,至于改不改,那也是写文章的人的事了(通常我是认真接受批评指教的)。

  你看,写道这里,我又不知道再序些啥了,索性就这样吧。

  闲话少序,切入正题

前言

  前面的课程有讲解过库房相关的,但都是密集架库房,档案室库房类的(《如何用webgl(three.js)搭建一个3D库房,3D密集架,3D档案室(升级版)》《如何用webgl(three.js)搭建一个3D库房,3D密集架,3D档案室,-第二课》《如何用webgl(three.js)搭建一个3D库房-第一课》)

  该篇主要讲解堆放箱体的库房,以及码头集装箱类似的库房场地解决方案。

  可视化孪生系统实现起来主要是数据源、业务系统、展示方案这三大部分。

  数据源:就是数据的来源,针对该篇文章,是如何对库房,库位的数据进行采集,录入。物联网厂家通常叫做前端采集模块。

      该项目,数据源主要通过 rfid+手动录入 的方式,项目中库位主要分了室内和室外两大部分,室内通过门口rfid门禁知道箱子的出入,再通过操作员手动录入箱子的位置(再库位上,详细划分了位置编号);室外部分直接通过操作员手动录入入库出库信息。

    这里还涉及到车辆定位(叉车),采用的是定位卡+基站以及分站的方式。

  业务系统:针对采集上来的数据,如何进行有效的处理与存储,如何有效符合客户功能需求,以及要综合考虑数据源结构,展示端需求数据结构,系统性能等。这些都是业务系统的主要功能要求。

       业务系统,也是我们程序员常说的后端服务。

  展示方案:争对客户需求,设计符合客户要求的交互三维可视化方案。

  该篇我们主要详细讲解展示端方案。

一、整体效果及功能

1.1、库房外部及周遭场景

800616-20221027200149155-295932622.gif

通过对园区进行建模,虚拟模拟周边道路环境,实现整体场景展示。

1.2、外部库位集装箱信息,以及车辆信息

鼠标滑动到集装箱,或者车辆上,显示货物,车辆信息。

800616-20221027200203816-458965803.gif

 具体实现滑动显示

ModelBussiness.prototype.mouseInCurrentObj = null;
ModelBussiness.prototype.lastMouseInCurrentObj = null;
//鼠标滑入事件
ModelBussiness.prototype.mouseOverInCallBack = function (_obj, face, objs) {
  console.log(_obj.name);
  var _this = modelBussiness;
  WT3DObj.controls.autoRotate = false;
  
  var color = 0xbfffea;
  modelBussiness.lastMouseInCurrentObj = _obj;
  modelBussiness.mouseInCurrentObj = _obj;
  if (_obj.name.indexOf("dev_car_") >= 0) {

        var _sobj = _obj;
        if (_obj.name.indexOf("OBJCREN") > 0) {
            _sobj = _obj.parent;
        }
        var id = (_sobj.name.split("_Model_")[1]);

        var name = id;
        modelBussiness.mouseInCurrentObj = _sobj;
        _sobj.visible = true;
        WT3DObj.commonFunc.setSkinColorByObj(_sobj, 0x00ffff);
        $("#MarkMessageHelper").remove();
        $("body").append("<div id='MarkMessageHelper' style='position:absolute;left:" + (window.event.pageX) + "px;top:" + (window.event.pageY - 10) + "px;height:2px;width:2px;z-index:1000;'></div>");
        showCarinfo(name,id);
    }

}
800616-20221027200322273-1804562141.gif
//展示货物信息
function showGoodInfo(name, id) {
    //显示结构部分
    var html = ' XXXXX';
    //弹窗
    layer.tips(html, "#MarkMessageHelper", {
        tips: [1, '#003333'],//弹窗类型与颜色
        time: 0,//弹窗自动关闭时长 0表示不自动关闭
        area: ["415px", "230px"],//弹窗大小
        success: function () {//弹窗显示后回调
            setTimeout(function () {
                //数据接口 根据id获取货物详细信息
                webapi.GetAllGoodsInfo(id, function (result) {
                    if (result) {
                        modelBussiness.cacheData = {
                            id: id,
                            result: result
                        };
                        for (var item in result) {//填充弹窗内结构的数据
                            $("#devParamValue3D" + id + "_" + item).html(result[item]);
                            if (item == "photo_urls") {
                                var _html = "";
                                $.each(result[item], function (_pindex, _pobj) {

                                    _html += ' <div style="float:left;cursor:pointer;margin-right:10px;" onclick="modelBussiness.showPics(\'' + _pobj.url + '\',\'' + _pobj.doctype + '\')">' + _pobj.doctype + '</div>';
                                })
                                $("#devParamValue3D" + id + "_photos").html(_html);
                            }
                        }
                    } else {
                        $("#devParamValue3D" + id + "_content").html("<font style='color:red;'>获取数据异常</font>");
                    }
                })
            }, 200);
        }
    });

}

 1.3、车辆出入管理

800616-20221027200610634-1735274978.gif

 对进入的车辆实时监控,卡口信息实时展示

实现代码如下:

            var carjp1_4_run = WT3DObj.commonFunc.findObject("car_sanka_3");
         
            carjp1_4_run.position.x = -858.739;
            carjp1_4_run.position.z = 19837.371;
 
            new TWEEN.Tween(carjp1_4_run.position).to({
               z: 14344.865
            }, 5000).onComplete(function () {
                new TWEEN.Tween(carjp1_4_run.rotation).to({
                    y: 0,
                }, 1000).onComplete(function () { }).start();
                new TWEEN.Tween(carjp1_4_run.position).to({
                    x: -225.796,
                    z: 13523.366
                }, 1000).onComplete(function () {


                    layer.tips("", "#MarkMessageHelper")
                    layer.msg(`
        车牌:<font color='#00ff00'>粤A17001</font><br/>
        类型:<font color='#00ff00'>内部车辆</font><br/>
        外出:<font color='#00ff00'>2022/10/22 15:36:45</font><br/>
        进入:<font color='#00ff00'>2022/10/22 17:56:12</font><br/>
        照片:<img src='../img/car_01.png' style="width:100px;height:100px;" />
        `)

                    setTimeout(function () {
                        var dz_3_lg = WT3DObj.commonFunc.findObject("dz_gz_1");
                        dz_3_lg.rotation.x = Math.PI / 2;
                        dz_3_lg.rotation.z = -Math.PI;
                        new TWEEN.Tween(dz_3_lg.rotation).to({
                            x: Math.PI
                        }, 2000).onComplete(function () {
                            new TWEEN.Tween(carjp1_4_run.position).to({
                                x: 1197.955,
                                z: 13308.873
                            }, 2000).onComplete(function () {

                            }).start();
                            new TWEEN.Tween(carjp1_4_run.rotation).to({
                                y: 11.617 / 180 * Math.PI,
                            }, 2000).onComplete(function () {
                                new TWEEN.Tween(carjp1_4_run.position).to({
                                    x: 10812.744,
                                    z: 12807.050
                                }, 6000).onComplete(function () {

                                }).start();
                                new TWEEN.Tween(carjp1_4_run.rotation).to({
                                    y: 0,
                                }, 2000).onComplete(function () {

                                }).start();

                            }).start();
                            setTimeout(function () {
                                new TWEEN.Tween(dz_3_lg.rotation).to({
                                    x: - Math.PI / 2
                                }, 2000).onComplete(function () {
                                    $("#doAnimationBtn").show();
                                }).start();
                            }, 2000);
                        }).start();
                    }, 2000);

                }).start();
            }).start();

1.4、内部仓库场景

 双击进入内部室内仓库

800616-20221027200358195-1288594129.gif

 绑定双击事件,实现跳转即可

实现展开楼层如下:

ModelBussiness.prototype.tempNameList = [];
ModelBussiness.prototype.tempDataList = [];
ModelBussiness.prototype.videoDataCache = {};
ModelBussiness.prototype.showFloorState = "close";
//显示楼层内部情况
ModelBussiness.prototype.showBuildFloors = function (buildnub, callBack) {
    var _this = this
    _this.showFloorState = "open";
    var builds = WT3DObj.commonFunc.findObjectsByNames(["ckbuild_486"]);
    //隐藏大楼
    WT3DObj.commonFunc.setSkinColorByname("ckbuild_486", 0x00ffff);
    WT3DObj.commonFunc.changeCameraPosition({ x: 3652.5144280174954, y: 990.805706980618, z: 5107.394022507952 }, { x: 1914.4771268074287, y: -723.8717024746979, z: 2181.6118222317314 }, 500,
        function () { });
    WT3DModel.commonFunc.changeObjsOpacity(builds, 1, 0.1, 500, function (obj) {
        var _obj = WT3DObj.commonFunc.findObject("ckbuild_486");
        if (typeof (_obj.oldPositionY) == 'undefined') {
            _obj.oldPositionY = _obj.position.y
        }
        _obj.position.y = 1000000;
        _obj.visible = false;

        WT3DObj.commonFunc.changeCameraPosition({ x: -1181.6606035933219, y: 7695.800119393643, z: 17124.216668774727 },{ x: 7526.409787213892, y: 2616.2148116624617, z: 7792.131296514065 }, 500,
            function () { });

        var names = ["zgx_102_f1", "zgx_102_f2", "zgx_102_f3", "zgx_102_f4"];
        var floors = WT3DObj.commonFunc.findObjectsByNames(names);
        modelBussiness.openFloors(floors, function () {
            if (callBack) {
                callBack();
            }
        });
    });

}//显示楼层
ModelBussiness.prototype.openFloors = function (floors, callBack) {
    //显示楼层
    $.each(floors, function (_index, _obj) {
        if (typeof (_obj.oldPositionY) == 'undefined') {
            _obj.oldPositionY = _obj.position.y
        }
        if (_obj.position.y > 100000) {
            _obj.position.y -= 1000000;
        }
        _obj.visible = true;
    });
    setTimeout(function () {
        $.each(floors, function (_index, _obj) {
            //展开楼层
            _obj.floorPosition = _obj.position.y;
            var floor = parseInt(_obj.name.split("_f")[1]);
            height = (floor - 1) * 1500 + 50;
            new TWEEN.Tween(_obj.position).to({
                y: height
            }, 500).start();
        });
        setTimeout(function () {

            if (callBack) {
                callBack()
            }
        }, 600);
    }, 500)

}

 1.5、分区块信息

 建模时,已经固定分区,所以直接将分区标题固定即可。

800616-20221027200450680-1733364168.gif
ModelBussiness.prototype.showAreaGoods = function (code, callBack) {
    var objs = [];
    var hideobjs = [];
    $.each(WT3DObj.scene.children, function (_index, _obj) {
        //遍历所有模型,找到对应的模型展示。非对应货物 隐藏
        if (_obj.name.indexOf("location2_") == 0) {
            _obj.visible = true;
            if (_obj.oldPositionY || _obj.oldPositionY == 0) {
                _obj.position.y = _obj.oldPositionY;
            }
        }
        if (_obj.name.indexOf("g_") == 0) {
            _obj.visible = true;
            if (code == "ALL") {
                _obj.visible = true;
            } else {
                if (_obj.name.indexOf("_Area_" + code) > 0) {
                    _obj.visible = true;
                } else {
                    _obj.visible = false;
                }
            }
        }
    });
  
}

 1.6、单独库位展示

 单独库位展示,采用iframe弹框方式,有效节约资源,降低逻辑复杂度。

 

800616-20221027200053255-1381433323.gif
//展示货物信息
function showGoodInfo(name, id) {
    //显示结构部分
    var html = CONTENT;
    //弹窗
    layer.tips(html, "#MarkMessageHelper", {
        tips: [1, '#003333'],//弹窗类型与颜色
        time: 0,//弹窗自动关闭时长 0表示不自动关闭
        area: ["415px", "230px"],//弹窗大小
        success: function () {//弹窗显示后回调
  
        }
    });

}

 1.7、货物搜索定

 实现货物快速定位与检索

ContractedBlock.gifExpandedBlockStart.gif

View Code

 1.8、叉车定位

800616-20221027200010101-1271859446.gif

根据定位信息,实现叉车位置实时跟踪

这里采用的摄像头定位卡加上基站的方案。

ContractedBlock.gifExpandedBlockStart.gif

View Code

二、实现逻辑

 2.1、建模

  2.1.1、创建园区整体模型

2.1.1.1、创建周边环境

800616-20221027195823481-743031919.jpg

ContractedBlock.gifExpandedBlockStart.gif

View Code

这里的道路直接用亮线画出道路框架即可,然后通过流动的光线模拟车流,这在前面的文章中有详细讲解。

2.1.1.2、创建大楼

 

800616-20221027195902365-63776836.jpg

ContractedBlock.gifExpandedBlockStart.gif

View Code

800616-20221027195829613-2144199051.png

 2.1.1.3、创建楼层

用于分解楼层展示

800616-20221027195908260-700109332.png
function createAreaModels(name,title, color,_points) {
    var points = [];
    var maxx = null;
    var maxy = null;
    var minx =null;
    var miny = null;
    var extra_x = 0;
    var extra_y = 0;
    if (window.location.href.indexOf("index.html") >= 0) {
        extra_x = config.basePoint.index.x;
        extra_y = config.basePoint.index.y;
    } else {
        extra_x = config.basePoint.room.x;
        extra_y = config.basePoint.room.y;
    }
    $.each(_points, function (_index, _obj) {
        if (_obj.x > maxx || maxx == null) {
            maxx = _obj.x;
        }
        if (_obj.y > maxy || maxy == null){
            maxy = _obj.y;
        }

        if (_obj.x < minx || minx == null) {
            minx = _obj.x;
        }
        if (_obj.y < miny || miny == null) {
            miny = _obj.y;
        }
        points.push({
            "x": _obj.x + extra_x,
            "y": _obj.y + extra_y,
            "type": "nomal"
        });
    });
    var titleimg = "";var modeljson = [{ "show": true, "uuid": "", "name": "area_" + name, "objType": "ExtrudeGeometry", "position": { "x": 0, "y": 0, "z": 0 }, "style": { "skinColor": 16711680, "skin": { "skin_up": { "skinColor": color, "materialType": "Phong", "side": 1, "opacity": 0.5 }, "skin_down": { "skinColor": color, "side": 1, "opacity": 1 }, "skin_side": { "skinColor": color, "opacity": 1 } } }, "scale": { "x": 1, "y": 1, "z": 1 }, "shapeParm": { "points": points, "holes": [] }, "extrudeSettings": { "amount": 120, "curveSegments": 1, "steps": 1, "bevelEnabled": false, "bevelThickness": 1, "bevelSize": 1, "bevelSegments": 1, "extrudePathPoints": [] }, "showSortNub": 40, "customType1": "", "customType2": "", "animation": null, "dbclickEvents": null, "rotation": [{ "direction": "x", "degree": 1.5707963267948966 }, { "direction": "y", "degree": 0 }, { "direction": "z", "degree": 0 }], "BindDevId": null, "BindDevName": null, "devInfo": null, "BindMeteId": "10001", "BindMeteName": "" }
return modeljson;
}

   2.1.2、创建室内库房模型

800616-20221027195432992-203660404.png

这里的模型通过代码实现,篇幅过长,不便展示。

   2.1.3、创建箱子模型

800616-20221025170505306-1359239413.png
 { "show": true, "uuid": "", "name": name, "objType": "ExtrudeGeometry", "position": { "x": position.x, "y": position.y, "z": position.z }, "style": { "skinColor": 16711680, "skin": { "skin_up": { "skinColor": color1, "side": 1, "opacity": 1, "imgurl": imgurl1, "repeatx": true, "width": 0.01, "repeaty": true, "height": 0.01 }, "skin_down": { "skinColor": 16777215, "side": 1, "opacity": 1 }, "skin_side": { "skinColor": color2, "opacity": 1, "imgurl": imgurl2, "repeatx": true, "width": 0.01, "repeaty": true, "height": 0.01 } } }, "scale": { "x": size.x / 100, "y": size.y / 100, "z": size.z / 100 }, "shapeParm": { "points": [{ "x": 0, "y": 0, "type": "nomal" }, { "x": 0, "y": 100, "type": "nomal" }, { "x": 100, "y": 100, "type": "nomal" }, { "x": 100, "y": 0, "type": "nomal" }], "holes": [] }, "extrudeSettings": { "amount": 100, "curveSegments": 1, "steps": 1, "bevelEnabled": false, "bevelThickness": 1, "bevelSize": 1, "bevelSegments": 1, "extrudePathPoints": [] }, "showSortNub": 100, "customType1": "", "customType2": "", "animation": null, "dbclickEvents": null, "rotation": [{ "direction": "x", "degree": 0 }, { "direction": "y", "degree": 0 }, { "direction": "z", "degree": 0 }], "BindDevId": null, "BindDevName": null, "devInfo": null, "BindMeteId": null, "BindMeteName": null }

  2.1.4、创建集装箱模型

800616-20221025170646652-945890051.png
[{"show":true,"uuid":"","name":"cube2_6","objType":"cube2","length":400,"width":200,"height":200,"x":0,"y":200,"z":0,"style":{"skinColor":16777215,"skin":{"skin_up":{"skinColor":2531071,"side":1,"opacity":1,"imgurl":"../img/3dImg/cbjysfk2.jpg"},"skin_down":{"skinColor":2531071,"side":1,"opacity":1,"imgurl":"../img/3dImg/cbjysfk2.jpg"},"skin_fore":{"skinColor":2531071,"side":1,"opacity":1,"imgurl":"../img/3dImg/cbjysfk2.jpg"},"skin_behind":{"skinColor":2531071,"side":1,"opacity":1,"imgurl":"../img/3dImg/cbjysfk2.jpg"},"skin_left":{"skinColor":2531071,"side":1,"opacity":1,"imgurl":"../img/3dImg/cbjysfk2.jpg"},"skin_right":{"skinColor":2531071,"side":1,"opacity":1,"imgurl":"../img/3dImg/cbjysfk2.jpg"}}},"showSortNub":6,"customType1":"","customType2":"","animation":null,"dbclickEvents":null,"rotation":[{"direction":"x","degree":0},{"direction":"y","degree":0},{"direction":"z","degree":0}],"thick":null,"scale":{"x":1,"y":1,"z":1},"BindDevId":null,"BindDevName":null,"devInfo":null,"BindMeteId":null,"BindMeteName":null}]

  2.1.5、车辆模型

800616-20221025170823547-1126615351.png
 { "name": _name, "objType": "objmodel", "position": _position, "scale": _scale, "visible": true, "rotation": [{ "direction": "x", "degree": _rotation.x }, { "direction": "y", "degree": _rotation.y-Math.PI/2 }, { "direction": "z", "degree": _rotation.z }], "filePath": "../js/models/car/", "mtlFileName": "car03.mtl", "objFileName": "car03.obj", "mtlIsPublic": false, "showSortNub": 7, "show": true, "customType1": "", "customType2": "", "animation": null, "dbclickEvents": null, "BindDevId": null, "BindDevName": null, "devInfo": null, "BindMeteId": null, "BindMeteName": null }

2.2、数据载入

  通过数据生成模型,画出库位,载入车辆等

 
/* type:
        1://集装箱
       2://箱子
        3://筒状
    color://颜色
   id :设备id 唯一    必填
   position :设备位置  必填 格式 { x: 0, y: 0, z: 0} 这里矢量单位
   size:尺寸 默认值 { x: 1, y: 1, z: 1 };
*/
function createModelJsonByType(type,color, id, position, size) {
    if (!scale) {
        scale = { x: 1, y: 1, z: 1 };
    }
    var modeljson = null;
    switch (type) {
        case 1:
            {

            modeljson = {
             ....
            };
            }
            break;
        case 2:
            modeljson = {
              ....。 };
            break;
        case 3:
            modeljson = {
               ....       };
            break;
       

             
    }

    modeljson.name = "dev_T_" + type + "_ID_" + id;
    if (config && config.name) {
        modeljson.name = config.name;
    }
    if (modeljson.children) {
        $.each(modeljson.children, function (_i, _o) {
            _o.name = "dev_T_" + type + "_ID_" + id + "OBJCREN" + _i;
        });
    }
    if (modeljson.position) {
        modeljson.position.x = position.x;
        if (position.y || position.y == 0) {
            modeljson.position.y = position.y;
        }
        modeljson.position.z = position.z;
    }

    return modeljson;
}
/*
创建车 
*/
function createCarModel(_name, _position, _rotation, _scale, carType) {


    var model = ...model;
    // 1.集卡(带集装箱的) 2.集卡(空车) 3.散卡(带箱的小货车) 4.正面吊 5.小铲车 6 板车
    if (carType) {
        switch (carType) {
            case 1: {
                model.filePath = "../js/models/jika/";
                model.mtlFileName = "jika.mtl";
                model.objFileName = "jika.obj";
                model.scale = {
                    x: 4.200,
                    y: 4.200,
                    z: 4.200
                }
            }
                break;
            case 2: {
                model.filePath = "../js/models/jika_nocube/";
                model.mtlFileName = "jika_nocube.mtl";
                model.objFileName = "jika_nocube.obj";
                model.scale = {
                    x: 4.200,
                    y: 4.200,
                    z: 4.200
                }
            }
                break;
            case 3: {
                model.filePath = "../js/models/sanka/";
                model.mtlFileName = "sanka.mtl";
                model.objFileName = "sanka.obj";
                model.scale = {
                    x: 0.080,
                    y: 0.080,
                    z: 0.080
                }
                model.rotation[1].degree -= Math.PI / 2;
            }
                break;
            case 4: {
                model.filePath = "../js/models/diaoche/";
                model.mtlFileName = "dc.mtl";
                model.objFileName = "dc.obj";
                model.scale = {
                    x: 1.150,
                    y: 1.150,
                    z: 1.150
                }
            }
                break;
            case 5: {
                model.filePath = "../js/models/canche/";
                model.mtlFileName = "canche.mtl";
                model.objFileName = "canche.obj";
                model.scale = {
                    x: 0.1,
                    y: 0.1,
                    z: 0.1
                }
            }
                break;
            case 6: {
                model.filePath = "../js/models/banche/";
                model.mtlFileName = "banche.mtl";
                model.objFileName = "banche.obj";
                model.scale = {
                    x: 4.200,
                    y: 4.200,
                    z: 4.200
                }
            }
                break;
        }
    }
    //model.scale.x *= 0.8;
    //model.scale.y *= 0.8;
    //model.scale.z *= 0.8;
    return model;

}

2.3、自动生成货物模型

 生成模型注意对于批量模型消耗浏览器性能,掉帧问题。这里后面我会用专门的篇幅讲解,如何优化加载大量货物且不掉帧的解决方案。

  //获取区域库位划分数据
        webapi.GetAllArea(1, function (result) {
            var models = [];
            if (result && result.length > 0) {
                $("#room_shelfNub").html(result.length);
                $.each(result, function (_index, _obj) {
                    var _color = _obj.color;
                    if (_color == "") {
                        _color = Math.random() * 16777215 + "";
                    } else {
                        _color = _color.replace("#", "0x")
                    }
                    _color = parseInt(_color)
                    //生成区域画线
                    var model = createAreaModels(_obj.code, _obj.name, _color, _obj.AreaPoints);
                    models = models.concat(model);
                })
            }
            console.log(models);
            
            WT3DObj.commonFunc.loadModelsByJsons(models, { x: 0, y: 0, z: 0 }, { x: 0, y: 0, z: 0 }, true, function () {

            });

        })

2.4、主要逻辑

  具体实现逻辑主要分为五个步骤

  1、创建模型

  2、校准坐标系,将模型的坐标系与数据坐标系校准对应。

  3、根据配置载入配置模型,如摄像头等

  4、生成库位、货物。根据动态数据,生成库位、车辆、货物等模型

  5、业务逻辑。实现滑动,双击,搜索等常规业务。

由于篇幅原因,本节先讲解到这。

技术交流 [email protected]

交流微信:

    800616-20190306111130020-1677299606.png

如果你有什么要交流的心得 可邮件我

其它相关文章:

webgl(three.js)实现室内三维定位,3D定位,3D楼宇bim、实时定位三维可视化解决方案——第十四课(定位升级版)

使用three.js(webgl)搭建智慧楼宇、设备检测、数字孪生——第十三课

如何用three.js(webgl)搭建3D粮仓、3D仓库、3D物联网设备监控-第十二课

如何用webgl(three.js)搭建处理3D隧道、3D桥梁、3D物联网设备、3D高速公路、三维隧道桥梁设备监控-第十一课

如何用three.js实现数字孪生、3D工厂、3D工业园区、智慧制造、智慧工业、智慧工厂-第十课

使用webgl(three.js)创建3D机房,3D机房微模块详细介绍(升级版二)

如何用webgl(three.js)搭建一个3D库房-第一课

如何用webgl(three.js)搭建一个3D库房,3D密集架,3D档案室,-第二课

使用webgl(three.js)搭建一个3D建筑,3D消防模拟——第三课

使用webgl(three.js)搭建一个3D智慧园区、3D建筑,3D消防模拟,web版3D,bim管理系统——第四课

如何用webgl(three.js)搭建不规则建筑模型,客流量热力图模拟

 使用webgl(three.js)搭建一个3D智慧园区、3D建筑,3D消防模拟,web版3D,bim管理系统——第四课(炫酷版一)

使用webgl(three.js)搭建3D智慧园区、3D大屏,3D楼宇,智慧灯杆三维展示,3D灯杆,web版3D,bim管理系统——第六课

如何用webgl(three.js)搭建处理3D园区、3D楼层、3D机房管线问题(机房升级版)-第九课(一)


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK