溫馨提示×

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

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

如何理解HTML5的WebGL自定義3D攝像頭監(jiān)控模型

發(fā)布時(shí)間:2021-10-12 16:05:42 來源:億速云 閱讀:166 作者:柒染 欄目:云計(jì)算

本篇文章給大家分享的是有關(guān)如何理解HTML5的WebGL自定義3D攝像頭監(jiān)控模型,小編覺得挺實(shí)用的,因此分享給大家學(xué)習(xí),希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。

前言

隨著視頻監(jiān)控聯(lián)網(wǎng)系統(tǒng)的不斷普及和發(fā)展, 網(wǎng)絡(luò)攝像機(jī)更多的應(yīng)用于監(jiān)控系統(tǒng)中,尤其是高清時(shí)代的來臨,更加快了網(wǎng)絡(luò)攝像機(jī)的發(fā)展和應(yīng)用。

在監(jiān)控?cái)z像機(jī)數(shù)量的不斷龐大的同時(shí),在監(jiān)控系統(tǒng)中面臨著嚴(yán)峻的現(xiàn)狀問題:海量視頻分散、孤立、視角不完整、位置不明確等問題,始終圍繞著使用者。因此,如何更直觀、更明確的管理攝像機(jī)和掌控視頻動(dòng)態(tài),已成為提升視頻應(yīng)用價(jià)值的重要話題。所以當(dāng)前項(xiàng)目正是從解決此現(xiàn)狀問題的角度,應(yīng)運(yùn)而生。圍繞如何提高、管理和有效利用前端設(shè)備采集的海量信息為公共安全服務(wù),特別是在技術(shù)融合大趨勢下,如何結(jié)合當(dāng)前先進(jìn)的視頻融合,虛實(shí)融合、三維動(dòng)態(tài)等技術(shù),實(shí)現(xiàn)三維場景實(shí)時(shí)動(dòng)態(tài)可視化監(jiān)控,更有效的識(shí)別、分析、挖掘海量數(shù)據(jù)的有效信息服務(wù)公共應(yīng)用,已成為視頻監(jiān)控平臺(tái)可視化發(fā)展的趨勢和方向。目前,在監(jiān)控行業(yè)中,??怠⒋笕A等做監(jiān)控行業(yè)領(lǐng)導(dǎo)者可基于這樣的方式規(guī)劃公共場所園區(qū)等的攝像頭規(guī)劃安放布局,可以通過??怠⒋笕A等攝像頭品牌的攝像頭參數(shù),調(diào)整系統(tǒng)中攝像頭模型的可視范圍,監(jiān)控方向等,更方便的讓人們直觀的了解攝像頭的監(jiān)控區(qū)域,監(jiān)控角度等。

以下是項(xiàng)目地址:基于 HTML5 的 WebGL 自定義 3D 攝像頭監(jiān)控模型

效果預(yù)覽

整體場景-攝像頭效果圖

如何理解HTML5的WebGL自定義3D攝像頭監(jiān)控模型

局部場景-攝像頭效果圖

如何理解HTML5的WebGL自定義3D攝像頭監(jiān)控模型

代碼生成
攝像頭模型及場景

項(xiàng)目中使用的攝像頭模型是通過 3dMax 建模生成的,該建模工具可以導(dǎo)出 obj 與 mtl 文件,在 HT 中可以通過解析 obj 與 mtl 文件來生成 3d 場景中的攝像頭模型。

項(xiàng)目中場景通過 HT 的 3d 編輯器進(jìn)行搭建,場景中的模型有些是通過 HT 建模,有些通過 3dMax 建模,之后導(dǎo)入 HT 中,場景中的地面白色的燈光,是通過 HT 的 3d 編輯器進(jìn)行地面貼圖呈現(xiàn)出來的效果。

錐體建模

3D 模型是由最基礎(chǔ)的三角形面拼接合成,例如 1 個(gè)矩形可以由 2 個(gè)三角形構(gòu)成,1 個(gè)立方體由 6 個(gè)面即 12 個(gè)三角形構(gòu)成, 以此類推更復(fù)雜的模型可以由許多的小三角形組合合成。因此 3D 模型定義即為對(duì)構(gòu)造模型的所有三角形的描述, 而每個(gè)三角形由三個(gè)頂點(diǎn) vertex 構(gòu)成, 每個(gè)頂點(diǎn) vertex 由 x, y, z 三維空間坐標(biāo)決定,HT 采用右手螺旋定則來確定三個(gè)頂點(diǎn)構(gòu)造三角形面的正面。

HT 中通過 ht.Default.setShape3dModel(name, model) 函數(shù),可注冊(cè)自定義 3D 模型,攝像頭前方生成的錐體便是通過該方法生成??梢詫⒃撳F體看成由 5 個(gè)頂點(diǎn),6 個(gè)三角形組成,具體圖如下:

如何理解HTML5的WebGL自定義3D攝像頭監(jiān)控模型

ht.Default.setShape3dModel(name, model)

1. name 為模型名稱,如果名稱與預(yù)定義的一樣,則會(huì)替換預(yù)定義的模型
2. model 為JSON類型對(duì)象,其中 vs 表示頂點(diǎn)坐標(biāo)數(shù)組,is 表示索引數(shù)組,uv 表示貼圖坐標(biāo)數(shù)組,如果想要單獨(dú)定義某個(gè)面,可以通過 bottom_vs,bottom_is,bottom_uv,top_vs,top_is, top_uv 等來定義,之后便可以通過 shape3d.top.*, shape3d.bottom.*  等單獨(dú)控制某個(gè)面

以下是我定義模型的代碼:

// camera 是當(dāng)前的攝像頭圖元
// fovy 為攝像頭的張角的一半的 tan 值
var setRangeModel = function(camera, fovy) {
    var fovyVal = 0.5 * fovy;
    var pointArr = [0, 0, 0, -fovyVal, fovyVal, 0.5, fovyVal, fovyVal, 0.5, fovyVal, -fovyVal, 0.5, -fovyVal, -fovyVal, 0.5];
    ht.Default.setShape3dModel(camera.getTag(), [{
        vs: pointArr,
        is: [2, 1, 0, 4, 1, 0, 4, 3, 0, 3, 2, 0],
        from_vs: pointArr.slice(3, 15),
        from_is: [3, 1, 0, 3, 2, 1],
        from_uv: [0, 0, 1, 0, 1, 1, 0, 1]
    }]);
}

我將當(dāng)前攝像頭的 tag 標(biāo)簽值作為模型的名稱,tag 標(biāo)簽在 HT 中用于唯一標(biāo)識(shí)一個(gè)圖元,用戶可以自定義 tag 的值。通過 pointArr 記錄當(dāng)前五面體的五個(gè)頂點(diǎn)坐標(biāo)信息,代碼中通過 from_vs, from_is, from_uv 單獨(dú)構(gòu)建五面體底面,底面用于顯示當(dāng)前攝像頭呈現(xiàn)的圖像。

代碼中設(shè)置了錐體 style 對(duì)象的 wf.geometry 屬性,通過該屬性可以為錐體添加模型的線框,增強(qiáng)模型的立體效果,并且通過 wf.color,wf.width 等參數(shù)調(diào)節(jié)線框的顏色,粗細(xì)等。

相關(guān)模型 style 屬性的設(shè)置代碼如下:

1 rangeNode.s({
 2     'shape3d': cameraName,
 3     // 攝像頭模型名稱
 4     'shape3d.color': 'rgba(52, 148, 252, 0.3)',
 5     // 錐體模型顏色
 6     'shape3d.reverse.flip': true,
 7     // 錐體模型的反面是否顯示正面的內(nèi)容
 8     'shape3d.light': false,
 9     // 錐體模型是否受光線影響
10     'shape3d.transparent': true,
11     // 錐體模型是否透明
12     '3d.movable': false,
13     // 錐體模型是否可移動(dòng)
14     'wf.geometry': true // 是否顯示錐體模型線框
15 });

攝像頭圖像生成原理

透視投影

透視投影是為了獲得接近真實(shí)三維物體的視覺效果而在二維的紙或者畫布平面上繪圖或者渲染的一種方法,它也稱為透視圖。 透視使得遠(yuǎn)的對(duì)象變小,近的對(duì)象變大,平行線會(huì)出現(xiàn)先交等更更接近人眼觀察的視覺效果。

如何理解HTML5的WebGL自定義3D攝像頭監(jiān)控模型

如上圖所示,透視投影最終顯示到屏幕上的內(nèi)容只有截頭錐體( View Frustum )部分的內(nèi)容, 因此 Graph4dView 提供了 eye, center, up, far,near,fovy 和 aspect 參數(shù)來控制截頭錐體的具體范圍。具體的透視投影可以參考 HT for Web 的 3D 手冊(cè)。

根據(jù)上圖的描述,在本項(xiàng)目中可以在攝像頭初始化之后,緩存當(dāng)前 3d 場景 eyes 眼睛的位置,以及 center 中心的位置,之后將 3d 場景 eyes 眼睛和 center 中心設(shè)置成攝像頭中心點(diǎn)的位置,然后在這個(gè)時(shí)刻獲取當(dāng)前 3d 場景的截圖,該截圖即為當(dāng)前攝像頭的監(jiān)控圖像,之后再將 3d 場景的 center 與 eyes 設(shè)置成開始時(shí)緩存的 eyes 與 center 位置,通過該方法即可實(shí)現(xiàn) 3d 場景中任意位置的快照,從而實(shí)現(xiàn)攝像頭監(jiān)控圖像實(shí)時(shí)生成。

相關(guān)偽代碼如下:

1 function getFrontImg(camera, rangeNode) {
 2     var oldEye = g3d.getEye();
 3     var oldCenter = g3d.getCenter();
 4     var oldFovy = g3d.getFovy();
 5     g3d.setEye(攝像頭位置);
 6     g3d.setCenter(攝像頭朝向);
 7     g3d.setFovy(攝像頭張角);
 8     g3d.setAspect(攝像頭寬高比);
 9     g3d.validateImp();
10     g3d.toDataURL();
11     g3d.setEye(oldEye);;
12     g3d.setCenter(oldCenter);
13     g3d.setFovy(oldFovy);
14     g3d.setAspect(undefined);
15     g3d.validateImp();
16 }

經(jīng)過測試之后,通過該方法進(jìn)行圖像的獲取會(huì)導(dǎo)致頁面有所卡頓,因?yàn)槭谦@取當(dāng)前 3d 場景的整體截圖,由于當(dāng)前3d場景是比較大的,所以 toDataURL 獲取圖像信息是非常慢的,因此我采取了離屏的方式來獲取圖像,具體方式如下:
   1. 創(chuàng)建一個(gè)新的 3d 場景,將當(dāng)前場景的寬度與高度都設(shè)置為 200px 的大小,并且當(dāng)前 3d 場景的內(nèi)容與主屏的場景是一樣的,HT中通過 new ht.graph4d.Graph4dView(dataModel) 來新建場景,其中的 dataModel 為當(dāng)前場景的所有圖元,所以主屏與離屏的 3d 場景都共用同一個(gè) dataModel,保證了場景的一致。
   2. 將新創(chuàng)建的場景位置設(shè)置成屏幕看不到的地方,并且添加進(jìn) dom 中。
   3. 將之前對(duì)主屏獲取圖像的操作變成對(duì)離屏獲取圖像的操作,此時(shí)離屏圖像的大小相對(duì)之前主屏獲取圖像的大小小很多,并且離屏獲取不需要保存原來的眼睛 eyes 的位置以及 center 中心的位置,因?yàn)槲覀儧]有改變主屏的 eyes 與 center 的位置, 所以也減少的切換帶來的開銷,大大提高了攝像頭獲取圖像的速度。

以下是該方法實(shí)現(xiàn)的代碼:

1 function getFrontImg(camera, rangeNode) {
 2     // 截取當(dāng)前圖像時(shí)將該攝像頭所屬的五面體隱藏
 3     rangeNode.s('shape3d.from.visible', false);
 4     rangeNode.s('shape3d.visible', false);
 5     rangeNode.s('wf.geometry', false);
 6     var cameraP3 = camera.p3();
 7     var cameraR3 = camera.r3();
 8     var cameraS3 = camera.s3();
 9     var updateScreen = function() {
10         demoUtil.Canvas2dRender(camera, outScreenG3d.getCanvas());
11         rangeNode.s({
12             'shape3d.from.image': camera.a('canvas')
13         });
14         rangeNode.s('shape3d.from.visible', true);
15         rangeNode.s('shape3d.visible', true);
16         rangeNode.s('wf.geometry', true);
17     };
18 
19     // 當(dāng)前錐體起始位置
20     var realP3 = [cameraP3[0], cameraP3[1] + cameraS3[1] / 2, cameraP3[2] + cameraS3[2] / 2];
21     // 將當(dāng)前眼睛位置繞著攝像頭起始位置旋轉(zhuǎn)得到正確眼睛位置
22     var realEye = demoUtil.getCenter(cameraP3, realP3, cameraR3);
23 
24     outScreenG3d.setEye(realEye);
25     outScreenG3d.setCenter(demoUtil.getCenter(realEye, [realEye[0], realEye[1], realEye[2] + 5], cameraR3));
26     outScreenG3d.setFovy(camera.a('fovy'));
27     outScreenG3d.validate();
28     updateScreen();
29 }

上面代碼中有一個(gè) getCenter 方法是用于獲取 3d 場景中點(diǎn) A 繞著點(diǎn) B 旋轉(zhuǎn) angle 角度之后得到的點(diǎn) A 在 3d 場景中的位置,方法中采用了 HT 封裝的 ht.Math 下面的方法,以下為代碼:

1 // pointA 為 pointB 圍繞的旋轉(zhuǎn)點(diǎn)
 2 // pointB 為需要旋轉(zhuǎn)的點(diǎn)
 3 // r3 為旋轉(zhuǎn)的角度數(shù)組 [xAngle, yAngle, zAngle] 為繞著 x, y, z 軸分別旋轉(zhuǎn)的角度 
 4 var getCenter = function(pointA, pointB, r3) {
 5     var mtrx = new ht.Math.Matrix4();
 6     var euler = new ht.Math.Euler();
 7     var v1 = new ht.Math.Vector3();
 8     var v2 = new ht.Math.Vector3();
 9 
10     mtrx.makeRotationFromEuler(euler.set(r3[0], r3[1], r3[2]));
11 
12     v1.fromArray(pointB).sub(v2.fromArray(pointA));
13     v2.copy(v1).applyMatrix4(mtrx);
14     v2.sub(v1);
15 
16     return [pointB[0] + v2.x, pointB[1] + v2.y, pointB[2] + v2.z];
17 };

這里應(yīng)用到向量的部分知識(shí),具體如下:

OA + OB = OC

如何理解HTML5的WebGL自定義3D攝像頭監(jiān)控模型

方法分為以下幾個(gè)步驟求解:

   1.  var mtrx = new ht.Math.Matrix4() 創(chuàng)建一個(gè)轉(zhuǎn)換矩陣,通過 mtrx.makeRotationFromEuler(euler.set(r3[0], r3[1], r3[2])) 獲取繞著 r3[0],r3[1],r3[2] 即 x 軸,y 軸,z 軸旋轉(zhuǎn)的旋轉(zhuǎn)矩陣。
   2. 通過 new ht.Math.Vector3() 創(chuàng)建 v1,v2 兩個(gè)向量。
   3. v1.fromArray(pointB) 為建立一個(gè)從原點(diǎn)到 pointB 的一個(gè)向量。
   4. v2.fromArray(pointA) 為建立一個(gè)從原點(diǎn)到 pointA 的一個(gè)向量。
   5. v1.fromArray(pointB).sub(v2.fromArray(pointA)) 即向量 OB - OA 此時(shí)得到的向量為 AB,此時(shí) v1 變?yōu)橄蛄?AB。
   6. v2.copy(v1) v2 向量拷貝 v1 向量,之后通過 v2.copy(v1).applyMatrix4(mtrx) 對(duì) v2 向量應(yīng)用旋轉(zhuǎn)矩陣,變換之后即為 v1向量繞著 pointA 旋轉(zhuǎn)之后的的向量 v2。
   7. 此時(shí)通過 v2.sub(v1) 就獲取了起始點(diǎn)為 pointB,終點(diǎn)為 pointB 旋轉(zhuǎn)之后點(diǎn)構(gòu)成的向量,該向量此時(shí)即為 v2。
   8. 通過向量公式得到旋轉(zhuǎn)之后的點(diǎn)為 [pointB[0] + v2.x, pointB[1] + v2.y, pointB[2] + v2.z]。

項(xiàng)目中的 3D 場景例子其實(shí)是 Hightopo 最近貴州數(shù)博會(huì),HT 上工業(yè)互聯(lián)網(wǎng)展臺(tái)的 VR 示例,大眾對(duì) VR/AR 的期待很高,但路還是得一步步走,即使融資了 23 億美金的 Magic Leap 的第一款產(chǎn)品也只能是 Full of Shit,這話題以后再展開,這里就上段當(dāng)時(shí)現(xiàn)場的視頻照片:

如何理解HTML5的WebGL自定義3D攝像頭監(jiān)控模型

2d 圖像貼到 3d 模型

通過上一步的介紹我們可以獲取當(dāng)前攝像機(jī)位置的截屏圖像,那么如何將當(dāng)前圖像貼到前面所構(gòu)建的五面體底部呢?前面通過 from_vs, from_is 來構(gòu)建底部的長方形,所以在 HT 中可以通過將五面體的 style 中 shape3d.from.image 屬性設(shè)置成當(dāng)前圖像,其中 from_uv 數(shù)組用來定義貼圖的位置,具體如下圖:

如何理解HTML5的WebGL自定義3D攝像頭監(jiān)控模型

以下為定義貼圖位置 from_uv 的代碼:

 1 from_uv: [0, 0, 1, 0, 1, 1, 0, 1] 

from_uv 就是定義貼圖的位置數(shù)組,根據(jù)上圖的解釋,可以將 2d 圖像貼到 3d 模型的 from 面。

控制面板

HT 中通過 new ht.widget.Panel() 來生成如下圖的面板:

如何理解HTML5的WebGL自定義3D攝像頭監(jiān)控模型

面板中每個(gè)攝像頭都有一個(gè)模塊來呈現(xiàn)當(dāng)前監(jiān)控圖像,其實(shí)這個(gè)地方也是一個(gè) canvas,該 canvas 與場景中錐體前面的監(jiān)控圖像是同一個(gè) canvas,每一個(gè)攝像頭都有一個(gè)自己的 canvas 用來保存當(dāng)前攝像頭的實(shí)時(shí)監(jiān)控畫面,這樣就可以將該 canvas 貼到任何地方,將該 canvas 添加進(jìn)面板的代碼如下:

 1 formPane.addRow([{ 2 element: camera.a('canvas') 3 }], 240, 240); 

代碼中將 canvas 節(jié)點(diǎn)存儲(chǔ)在攝像頭圖元的 attr 屬性下面,之后便可以通過 camera.a('canvas') 來獲取當(dāng)前攝像頭的畫面。

在面板中的每一個(gè)控制節(jié)點(diǎn)都是通過 formPane.addRow 來進(jìn)行添加,具體可參考 HT for Web 的表單手冊(cè)。之后通過 ht.widget.Panel 將表單面板 formPane 添加進(jìn) panel 面板中,具體可參考 HT for Web 的面板手冊(cè)。

部分控制代碼如下:

1 formPane.addRow(['rotateY', {
 2     slider: {
 3         min: -Math.PI,
 4         max: Math.PI,
 5         value: r3[1],
 6         onValueChanged: function() {
 7             var cameraR3 = camera.r3();
 8             camera.r3([cameraR3[0], this.getValue(), cameraR3[2]]);
 9             rangeNode.r3([cameraR3[0], this.getValue(), cameraR3[2]]);
10             getFrontImg(camera, rangeNode);
11         }
12     }
13 }], [0.1, 0.15]);

控制面板通過 addRow 來添加控制元素,以上代碼為添加攝像頭繞著 y 軸進(jìn)行旋轉(zhuǎn)的控制,onValueChanged 在 slider 的數(shù)值改變的時(shí)候調(diào)用,此時(shí)通過 camera.r3() 獲取當(dāng)前攝像頭的旋轉(zhuǎn)參數(shù), 由于是繞著 y 軸旋轉(zhuǎn)所以 x 軸與 z 軸的角度是不變的,變的是 y 軸的旋轉(zhuǎn)角度,所以通過 camera.r3([cameraR3[0], this.getValue(), cameraR3[2]]) 來調(diào)整攝像頭的旋轉(zhuǎn)角度以及通過 rangeNode.r3([cameraR3[0], this.getValue(), cameraR3[2]]) 來設(shè)置攝像頭前方錐體的旋轉(zhuǎn)角度,然后調(diào)用之前封裝好的 getFrontImg 函數(shù)來獲取此時(shí)旋轉(zhuǎn)角度下面的實(shí)時(shí)圖像信息。

項(xiàng)目中通過 Panel 面板的配置參數(shù) titleBackground: rgba(230, 230, 230, 0.4) 即可將標(biāo)題背景設(shè)置為具有透明度的背景,其它類似的 titleColor, titleHeight 等標(biāo)題參數(shù)都可以配置,通過 separatorColor,separatorWidth 等分割參數(shù)可以設(shè)置內(nèi)部面板之間分割線的顏色,寬度等。最后面板通過 panel.setPositionRelativeTo('rightTop') 將面板的位置設(shè)置成右上角,并且通過 document.body.appendChild(panel.getView()) 將面板最外層的 div 添加進(jìn)頁面中, panel.getView() 用來獲取面板的最外層 dom 節(jié)點(diǎn)。

具體初始化面板代碼如下:

1 function initPanel() {
 2     var panel = new ht.widget.Panel();
 3     var config = {
 4         title: "攝像頭控制面板",
 5         titleBackground: 'rgba(230, 230, 230, 0.4)',
 6         titleColor: 'rgb(0, 0, 0)',
 7         titleHeight: 30,
 8         separatorColor: 'rgb(67, 175, 241)',
 9         separatorWidth: 1,
10         exclusive: true,
11         items: []
12     };
13     cameraArr.forEach(function(data, num) {
14         var camera = data['camera'];
15         var rangeNode = data['rangeNode'];
16         var formPane = new ht.widget.FormPane();
17         initFormPane(formPane, camera, rangeNode);
18         config.items.push({
19             title: "攝像頭" + (num + 1),
20             titleBackground: 'rgba(230, 230, 230, 0.4)',
21             titleColor: 'rgb(0, 0, 0)',
22             titleHeight: 30,
23             separatorColor: 'rgb(67, 175, 241)',
24             separatorWidth: 1,
25             content: formPane,
26             flowLayout: true,
27             contentHeight: 400,
28             width: 250,
29             expanded: num === 0
30         });
31     });
32     panel.setConfig(config);
33     panel.setPositionRelativeTo('rightTop');
34     document.body.appendChild(panel.getView());
35     window.addEventListener("resize",
36     function() {
37         panel.invalidate();
38     });
39 }

在控制面板中可以調(diào)整攝像頭的方向,攝像頭監(jiān)控的輻射范圍,攝像頭前方錐體的長度等等,并且攝像頭的圖像是實(shí)時(shí)生成,以下為運(yùn)行截圖:

如何理解HTML5的WebGL自定義3D攝像頭監(jiān)控模型

以下是本項(xiàng)目采用的 3D 場景結(jié)合 VR 技術(shù)實(shí)現(xiàn)的操作:

如何理解HTML5的WebGL自定義3D攝像頭監(jiān)控模型

以上就是如何理解HTML5的WebGL自定義3D攝像頭監(jiān)控模型,小編相信有部分知識(shí)點(diǎn)可能是我們?nèi)粘9ぷ鲿?huì)見到或用到的。希望你能通過這篇文章學(xué)到更多知識(shí)。更多詳情敬請(qǐng)關(guān)注億速云行業(yè)資訊頻道。

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

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

AI