溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

什么是HTML5中WebG 的3D網(wǎng)絡(luò)拓?fù)浣Y(jié)構(gòu)圖

發(fā)布時(shí)間:2020-07-20 10:45:32 來源:億速云 閱讀:184 作者:Leah 欄目:web開發(fā)

這篇文章將為大家詳細(xì)講解有關(guān)什么是HTML5中WebG 的3D網(wǎng)絡(luò)拓?fù)浣Y(jié)構(gòu)圖,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個(gè)參考,希望大家閱讀完這篇文章后對(duì)相關(guān)知識(shí)有一定的了解。

現(xiàn)在,3D 模型已經(jīng)用于各種不同的領(lǐng)域。在醫(yī)療行業(yè)使用它們制作器官的精確模型;電影行業(yè)將它們用于活動(dòng)的人物、物體以及現(xiàn)實(shí)電影;視頻游戲產(chǎn)業(yè)將它們作為計(jì)算機(jī)與視頻游戲中的資源;在科學(xué)領(lǐng)域?qū)⑺鼈冏鳛榛衔锏木_模型;建筑業(yè)將它們用來展示提議的建筑物或者風(fēng)景表現(xiàn);工程界將它們用于設(shè)計(jì)新設(shè)備、交通工具、結(jié)構(gòu)以及其它應(yīng)用領(lǐng)域;在最近幾十年,地球科學(xué)領(lǐng)域開始構(gòu)建三維地質(zhì)模型,而且 3D 模型經(jīng)常做成動(dòng)畫,例如,在故事片電影以及計(jì)算機(jī)與視頻游戲中大量地應(yīng)用三維模型。它們可以在三維建模工具中使用或者單獨(dú)使用。為了容易形成動(dòng)畫,通常在模型中加入一些額外的數(shù)據(jù),例如,一些人類或者動(dòng)物的三維模型中有完整的骨骼系統(tǒng),這樣運(yùn)動(dòng)時(shí)看起來會(huì)更加真實(shí),并且可以通過關(guān)節(jié)與骨骼控制運(yùn)動(dòng)。

這些種種都讓我們前端開發(fā)者覺得如果我們可以不用學(xué)習(xí) unity3d 或者其他游戲開發(fā)工具就能實(shí)現(xiàn) 3D 效果,而且能夠精準(zhǔn)的靠代碼來控制移動(dòng)或者方向就好了。。。于是我利用 HT For Web 中的 3D組件 來實(shí)現(xiàn)了一個(gè)小例子,用了 HT 中 3D組件 的大部分功能,做這個(gè)例子就是想把 3D 組件好好的掌握,盡量放進(jìn)一個(gè)例子中,到時(shí)候別人有需要就可以參考了。

先來看看整體實(shí)現(xiàn)的效果圖:

什么是HTML5中WebG 的3D網(wǎng)絡(luò)拓?fù)浣Y(jié)構(gòu)圖

用 HT for Web,現(xiàn)有的 3d 模板創(chuàng)建三層底板不是問題,問題是要如何將圖中第一層的“電腦”和“機(jī)柜組件”放上去?我是在網(wǎng)上 down 下來的 obj 格式的文件,然后我利用 HT 中的 ht.Default.loadObj(objUrl, mtlUrl, params) 函數(shù)將模型加載進(jìn)去,其中的 params 部分可以參考 http://www.hightopo.com/guide...,代碼如下:

ht.Default.loadObj('obj/機(jī)柜組件1.obj', 'obj/機(jī)柜組件1.mtl', {  //加載 obj 文件
    cube: true,  //是否將模型縮放到單位1的尺寸范圍內(nèi),默認(rèn)為false
    center: true,  //模型是否居中,默認(rèn)為false,設(shè)置為true則會(huì)移動(dòng)模型位置使其內(nèi)容居中
    shape3d: 'box',  //如果指定了shape3d名稱,則HT將自動(dòng)將加載解析后的所有材質(zhì)模型構(gòu)建成數(shù)組的方式,以該名稱進(jìn)行注冊(cè)
    finishFunc: function(modelMap, array, rawS3){  //用于加載后的回調(diào)處理 
      if(modelMap){  
        device2 = createNode('box', floor1);  //創(chuàng)建一個(gè)節(jié)點(diǎn),在第一層“地板”上
    device2.p3([x1-120, y1+13, z1+60]);  //設(shè)置這個(gè)節(jié)點(diǎn)坐標(biāo)
    device2.s3(rawS3);  //設(shè)置這個(gè)節(jié)點(diǎn)大小
    createEdge(device1, device2);  //創(chuàng)建連線
    device3 = createNode('box', floor1);  
    device3.s3(rawS3);  
    device3.p3([x1+120, y1+13, z1+60]);  
    createEdge(device1, device3);  
      }  
    }  
});

其中 finishiFunc 函數(shù)中的三個(gè)參數(shù)定義如下:

  • modelMap:調(diào)用 ht.Default.parseObj 解析后的返回值,若加載或解析失敗則返回值為空

  • array:所有材質(zhì)模型組成的數(shù)組

  • rawS3:包含所有模型的原始尺寸

一般在實(shí)際應(yīng)用中我們都會(huì)將圖元的大小設(shè)置為模型的原始尺寸。

“電腦”上方有個(gè)紅色的立體能旋轉(zhuǎn)的“警告”,是依靠 ht.Default.setShape3dModel 函數(shù)(HT for Web 建模手冊(cè) http://www.hightopo.com/guide... 3d 模型,在 ht 中,封裝好的建模函數(shù)有很多,比較基礎(chǔ)的就是球體,圓柱,立方體等等,這邊我用的是構(gòu)造環(huán)形的方法 createRingModel 來生成“警告”最外面的環(huán),感嘆號(hào)的上部分就是用的 createSmoothSphereModel 構(gòu)造的球體,感嘆號(hào)的下部分就是用 createSmoothCylinderModel 來構(gòu)造的圓柱。我一開始直接使用了 3d 模型中封裝好的函數(shù),導(dǎo)致后來根本不知道函數(shù)中使用的參數(shù)是做什么用的,而且也不明白 3d 模型是怎么構(gòu)成的,然后自己又重新看了前面的“模型基礎(chǔ)”,才知道原來 3d 模型采用的一個(gè)面,最基礎(chǔ)的是三角面,之后復(fù)雜的面也是由多個(gè)三角面來形成的,然后繞著一根特定的軸旋轉(zhuǎn)之后形成的,當(dāng)然,這個(gè)軸是你來決定的,不同的軸可以生成不同的形狀,對(duì)于顏色等風(fēng)格方面的設(shè)置可以參考 HT for Web 風(fēng)格手冊(cè)(http://www.hightopo.com/guide...。至于如何讓這個(gè) 3d 模型旋轉(zhuǎn)起來,ht 中封裝了 addScheduleTask(Task) 方法,我在第三層 Task 中調(diào)用了 ht 封裝的一個(gè)旋轉(zhuǎn)函數(shù) setRotation 來設(shè)置旋轉(zhuǎn)的順序和方向,并且指定了旋轉(zhuǎn)的對(duì)象。以下是自定義“警告”的 3d 模型的方法(注意:因?yàn)楸纠哪P褪亲远x組合的,如果要設(shè)置整體模型的顏色要用 “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);//根據(jù)xy平面的曲線,環(huán)繞一周形成3D模型。
    var sphereModel = ht.Default.createSmoothSphereModel(8, 8, 0, Math.PI*2, 0, Math.PI, 2);//構(gòu)建光滑球體模型 
    var cylinderModel = ht.Default.createSmoothCylinderModel(8, true, true, 1, 2, 0, Math.PI*2, 8);//構(gòu)建光滑圓柱體模型
    
    var alarmArr = [//組合模型 由三個(gè)模型ringModel、sphereModel、cylinderModel組合而成
        {
          shape3d: ringModel,//定義模型類型
          r3: [Math.PI/2, 0, 0],//設(shè)置旋轉(zhuǎn)角度
          color: {//設(shè)置模型顏色
            func: 'style@all.blend',//數(shù)據(jù)綁定style樣式中的all.blend屬性,可通過data.s()獲取和設(shè)置這個(gè)屬性
          }
        },{
          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', {//注冊(cè)自定義3D模型
      shape3d: alarmArr
    });

    var alarmTip = createNode('alarm', device);//創(chuàng)建shape3d為alarm的節(jié)點(diǎn)
    alarmTip.s3([2, 2, 2]);//設(shè)置節(jié)點(diǎn)大小
    alarmTip.p3(device.p3()[0], device.p3()[1]+60, device.p3()[2]);
    alarmTip.s('all.blend', 'red');//改變此屬性可改變模型的顏色,因?yàn)槟P蛣?chuàng)建的時(shí)候已經(jīng)數(shù)據(jù)綁定了

    return alarmTip;
}

接下來看看怎么讓這個(gè)“告警”節(jié)點(diǎn)“閃爍”,我是直接將這個(gè)動(dòng)畫跟節(jié)點(diǎn)綁定,這樣可以直接通過節(jié)點(diǎn)來控制動(dòng)畫。所以在上面我們創(chuàng)建 alarm 的模型時(shí)就可以直接將動(dòng)畫綁在節(jié)點(diǎn)上:

if(formPane){
    alarmNode.scaleFunc = function() {//設(shè)置大小變化動(dòng)畫
        var size = alarmNode.s3();//獲取節(jié)點(diǎn)的大小
        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'));//設(shè)置動(dòng)畫
    }
    alarmNode.blinkFunc = function(){//設(shè)置閃爍的動(dòng)畫
        var color = alarmNode.s('all.blend');//獲取節(jié)點(diǎn)的style樣式
        if (color === 'red') alarmNode.s({'all.blend': 'yellow'});//如果節(jié)點(diǎn)顏色為紅色,那么設(shè)置為黃色
        else alarmNode.s({'all.blend': 'red'});
        alarmNode.blinkTimer = setTimeout(alarmNode.blinkFunc, formPane.v('blinkInterval'));
    }
    alarmNode.rotateFunc = function() {//設(shè)置旋轉(zhuǎn)動(dòng)畫
        alarmNode.setRotation(alarmNode.getRotation() + Math.PI/20);//獲取節(jié)點(diǎn)當(dāng)前的旋轉(zhuǎn)角度,在這個(gè)旋轉(zhuǎn)角度之上添加 Math.PI/20 個(gè)角度
        alarmNode.rotateTimer = setTimeout(alarmNode.rotateFunc, formPane.v('rotInterval'));
    }
}

上面的動(dòng)畫我設(shè)置了可以通過 form 表單面板上的屬性來控制節(jié)點(diǎn)閃爍的速度,以及閃爍節(jié)點(diǎn)的動(dòng)畫等等,主要說一下這個(gè)功能在 form 表單上的實(shí)現(xiàn):

formPane.addRow([//向form表單面板上添加一行元素
    {
        checkBox: {//復(fù)選框
            label: 'Enable Blink',//復(fù)選框?qū)?yīng)的文本內(nèi)容
            selected: true,//設(shè)置選中復(fù)選框
            onValueChanged: function(){//復(fù)選框值變化時(shí)回調(diào)的函數(shù)
                var data = dataModel.getDataByTag('colorAlarm');//通過tag標(biāo)簽獲取節(jié)點(diǎn)
                if (this.getValue()) {//獲取復(fù)選框當(dāng)前值true/false
                    data.blinkTimer = setTimeout(data.blinkFunc, formPane.v('blinkInterval'));//直接通過設(shè)置節(jié)點(diǎn)的blinkTimer來設(shè)置動(dòng)畫
                }
                else {
                    clearTimeout(data.blinkTimer);//清除動(dòng)畫
                }
            }
        }
    },
    {
        id: 'blinkInterval',//form可以通過getValue(簡(jiǎn)寫為v)來獲取這個(gè)item的值
        slider: {//設(shè)置了該屬性后HT將根據(jù)屬性值自動(dòng)構(gòu)建ht.widget.Slider對(duì)象,并保存在element屬性上
            min: 0,//滑動(dòng)條最小值
            max: 1000,//滑動(dòng)條最大值
            step: 50,//滑動(dòng)條步進(jìn)
            value: 500,//當(dāng)前滑動(dòng)條的值
        }
    }
], [0.1, 0.1]);//設(shè)置這行的兩個(gè)item元素的寬度小于1的值為比例

最后來說說 3D 管線上的小球流動(dòng)的部分,這個(gè)功能確實(shí)非常實(shí)用,而且做出來的效果也確實(shí)不錯(cuò),跟大家分享~

首先,創(chuàng)建一條連線連接起始節(jié)點(diǎn)和結(jié)束節(jié)點(diǎn)并設(shè)置這個(gè)連線的樣式,用 ht.Edge 可以將連線吸附在起始節(jié)點(diǎn)和結(jié)束節(jié)點(diǎn)上,這樣移動(dòng)這兩個(gè)節(jié)點(diǎn)中的任意一個(gè)節(jié)點(diǎn)連線都會(huì)跟著節(jié)點(diǎn)移動(dòng)的位置變化,非常方便:

var polyline = new ht.Edge(source, target);//創(chuàng)建連線
dataModel.add(polyline);//將連線添加進(jìn)數(shù)據(jù)容器中
polyline.s({
    'edge.width': 5,//連線寬度
    'edge.type': 'points',//連線類型 為points時(shí)連線走向?qū)⒂蒭dge.points屬性決定,用于繪制折線
    'edge.points': [//可設(shè)置類型為ht.List的{x:100, y:100}格式的點(diǎn)對(duì)象數(shù)組,當(dāng)edge.type為points時(shí)起作用
        {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],//用于描述點(diǎn)連接樣式,數(shù)組元素為整型值
    'shape3d': 'cylinder',//圓柱
    'shape3d.color': 'rgba(242, 200, 40, 0.4)',
    'shape3d.resolution': 30,//微分段數(shù),可以決定曲線的平滑度
    'edge.source.t3': [20, 0, 0],//連線source端偏移,[tx, ty, tz]格式,默認(rèn)為空
    'edge.target.t3': [20, 0, 0]//連線target端偏移,[tx, ty, tz]格式,默認(rèn)為空
});

因?yàn)槲覀冊(cè)趧?chuàng)建連線的時(shí)候設(shè)置的 points 僅為曲線上的兩個(gè)點(diǎn),所以如果要獲取曲線目前形成的點(diǎn),是缺少 source 和 target 兩個(gè)點(diǎn)的,我們重新設(shè)置一個(gè)數(shù)組,將這兩個(gè)點(diǎn)添加進(jìn)去,后面獲取曲線上所有點(diǎn)時(shí)會(huì)用上:

var list = new ht.List();
list.push({x: source.getPosition3d()[0], y: source.getPosition3d()[2], e: source.getPosition3d()[1]});//向數(shù)組中添加source點(diǎn)
polyline.s('edge.points').each(function(item){//添加style屬性中已設(shè)置的兩個(gè)點(diǎn)
    list.push(item);
});
list.push({x: target.getPosition3d()[0], y: target.getPosition3d()[2], e: target.getPosition3d()[1]});//添加target點(diǎn)

然后創(chuàng)建一個(gè)在管線上滑動(dòng)的小球節(jié)點(diǎn),這是僅是設(shè)置節(jié)點(diǎn),真正添加進(jìn)數(shù)據(jù)容器 dataModel 中需要設(shè)置完小球的坐標(biāo)時(shí)再添加,如果沒有給節(jié)點(diǎn)設(shè)置位置就將節(jié)點(diǎn)添加進(jìn)數(shù)據(jù)容器中,節(jié)點(diǎn)的初始位置就是 3D 場(chǎng)景的正中心 [0, 0, 0] 的位置。小球滑動(dòng)的動(dòng)畫代碼如下:

var ball = new ht.Node();//創(chuàng)建小球節(jié)點(diǎn)
ball.s({//設(shè)置小球節(jié)點(diǎn)的樣式
    'shape3d': 'sphere',//設(shè)置小球的3d模型為球形
    'shape3d.color': 'rgba(40, 90, 240, 0.4)'//設(shè)置3d模型的顏色
});

var delta = 10, flag = 0;
setInterval(function(){
    flag++;
    var length = (polyline.a('total') || 0) % polyline.a('length') + delta;//小球當(dāng)前走過的曲線長(zhǎng)度
    var cache = ht.Default.getLineCacheInfo(list, polyline.s('edge.segments'));//獲取曲線上的點(diǎn)的信息
    var lineLength = ht.Default.getLineLength(cache);//獲取曲線的總長(zhǎng)度
    polyline.a('length', lineLength - 50);//因?yàn)槲以O(shè)置了edge的t3(相當(dāng)于2d中的offset),所以線段長(zhǎng)度實(shí)際沒有那么長(zhǎng)
    var offset = ht.Default.getLineOffset(cache, length);//曲線根據(jù)曲線上點(diǎn)的信息的偏移量
    ball.setPosition3d(offset.point.x + 10, offset.point.y, offset.point.z);//設(shè)置節(jié)點(diǎn)的坐標(biāo)
    polyline.a('total', length);
    if(flag === 1) dataModel.add(ball);//這時(shí)候節(jié)點(diǎn)已經(jīng)有了坐標(biāo)了,可以添加進(jìn)數(shù)據(jù)容器中了
}, 10);

我們還可以看到第二層上有兩個(gè)特殊的多邊形“平行四邊形”和“梯形”,平行四邊形是靠 createParallelogramModel 模型函數(shù),這個(gè)函數(shù)比較簡(jiǎn)單,createExtrusionModel(array, segments, top, bottom, resolution, repeatUVLength, tall, elevation),array 是你要形成的圖形的坐標(biāo)點(diǎn),這邊只是針對(duì)于 xz 軸上畫的平面圖形,segments 指的是如何連接這幾個(gè)坐標(biāo)點(diǎn),可參考 HT for Web 形狀手冊(cè)(http://hightopo.com/guide/gui...),top 和 bottom 就是讓你選擇是否有頂部或者底部,resolution 微分段數(shù),我們描繪一段曲線的時(shí)候可能只要確認(rèn)幾個(gè)個(gè)別的點(diǎn)然后在每?jī)蓚€(gè)點(diǎn)之間的連線上把它分成多個(gè)段,這樣這條線段就會(huì)變得平滑,ht 為了用戶能夠輕松操作這些線段,就封裝了這一個(gè)參數(shù),repeatUVLength 默認(rèn)為空,設(shè)置值后頂部和底部的貼圖將根據(jù)制定長(zhǎng)度值進(jìn)行重復(fù),tall 模型的高度,默認(rèn)為 5,elevation 模型中心的 y 軸位置,默認(rèn)值為 0,設(shè)置這個(gè)值可以使 xz 上的平面繞著 y 軸旋轉(zhuǎn)。

底層的一個(gè)環(huán)形的效果是通過一個(gè)算法來實(shí)現(xiàn)的,環(huán)形得確認(rèn)這個(gè)環(huán)形上有多少個(gè)元素,然后算每?jī)蓚€(gè)之間的角度,在通過 sin、cos 來計(jì)算每一個(gè)元素的位置,得出了如下代碼:

names = ['設(shè)備2', '設(shè)備3', '設(shè)備4', '設(shè)備5', '設(shè)備6', '設(shè)備7', '設(shè)備8', '設(shè)備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);  
});

關(guān)于什么是HTML5中WebG 的3D網(wǎng)絡(luò)拓?fù)浣Y(jié)構(gòu)圖就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI