基於 HTML5 + WebGL 的無人機 3D 視覺化系統

圖撲軟體發表於2020-04-06

前言

近年來,無人機的發展越發迅速,既可民用於航拍,又可軍用於偵察,涉及行業廣泛,也被稱為“會飛的照相機”。但作為軍事使用,無人機的各項效能要求更加嚴格、重要。本系統則是通過 Hightopo 的  HT for Web  產品來搭建的一款 無人機 3D 視覺化系統,通過對無人機及其資訊的全景展示來模擬無人機狀態的監控。

系統中包含 4 種展示模式:實體模式 、熱力模式、線框模式和內部模式,通過飛機下方操作按鈕即可進行模式切換。

預覽地址:http://www.hightopo.com/demo/Drones/

實現過程

  • 載入介面採用 2D 拓撲元件進行繪製,全向量化圖示,與傳統的 png、jpg 等格式的圖片相比,完美適配移動端、PC 端、大屏等各種尺寸及解析度螢幕,不會出現失真情況。
  • 無人機及周邊資訊皮膚採用 3D 引擎進行場景搭建,使用者可從場景任意位置對無人機進行檢視。
  • 動畫過程採用產品提供的動畫函式 ht.Default.startAnim 來驅動圖形屬性值的改變,應用其 Time-Based 的方式,只需要指定動畫週期 duration 的毫秒數,由系統去計算幀數或 action 函式被呼叫的次數,以保證更加高效、平滑的進行動畫過程。

介面

 

載入介面中通過動態改變圖形的屬性值來展現載入進度,載入完畢後通過動畫的 finishFunc 呼叫 hidden2d 方法來改變圖形的透明度,在此之後通過 moveCamera 將場景內視角拉近,從而實現淡出到淡入的效果(即離開載入介面進入到 3D 場景中)。與此同時改變圖形在 3D 場景中位置來實現各形態的無人機合為一體以及將對應按鈕分離。

// 載入進度
function loadAnim(){
  ht.Default.startAnim({
    duration: 6000,
    easing: t => {
      return 1 - (--t) * t * t * t;
    },
    action: (v, t) => { 
      loge.s('clip.percentage', v);
      percentText.s('text', Math.floor(v * 100));
    },
    finishFunc: () => {
      hidden2d()
    }
  })
}

// 隱藏 2d 圖紙
function hidden2d(){
  ht.Default.startAnim({
    duration: 500,
    easing: t => {
      return t;
    },
    action: (v, t) => { 
      dm2d.each(e => {
        e.s('opacity', 1 - v);
      })
    },
    finishFunc: () => {
      dm2d.setBackground('');
      g3d.moveCamera(eye, center, {duration: 3000, easing: t => {return 1 - (--t) * t * t * t;}});
      planeFit();
      buttonSeparate();
    }
  })
}

 2D 介面的製作是繪製了一張圖紙,而 logo 則是製作了一個圖示,一個圖示可以在圖紙中進行多次使用,並可展示不同的樣子。下圖中右側的四個 logo 就是同一個圖示,分別展示了不同的裁切方式以及透明度,系統中 logo 的進度條效果就是動態的去改變圖示的裁切比例來實現,而介面的淡出效果則是改變透明度。整個圖示的製作是繪製不同的圖形組合而成,這些圖形我們也可以去改變顏色,形成左側的 logo 樣式。

無人機形態切換

無人機主體形態分為三種:實體模式、線框模式和熱力模式。通過點選下方按鈕,可切換至按鈕所對應的形態。切換的過程中,將目標形態進行顯示,並分別上下移動目標形態和原形態,使使用者可以短暫的進行同時檢視,之後再回歸原位並將原形態進行隱藏。隱藏的方式則有所不用,線框模式是改變線框顏色,其餘兩種模式則是調整模型的透明度。這裡的線框是根據模型的輪廓生成的,通過 3D 引擎自動計算描繪,非常便捷。

// 選擇展示機型
function select(data){
  const name = data.getDisplayName();
  const moveData = dm3d.getDataByTag(name);
  const normalP = normalPlane.p3();

  ht.Default.startAnim({
    duration: 1000,
    easing: t => {
      return 1 - (--t) * t * t * t;
    },
    action: (v, t) => {
      if(name === 'linePlane'){
        moveData.s('wf.color', 'rgba(64,173,152,' + v + ')');
      }
      else{
        moveData.s('shape3d.opacity', v);
      }
      moveData.p3(normalP[0], normalP[1] + (4500 - normalP[1]) * v, normalP[2]);
      isShow.p3(normalP[0], normalP[1] + (2500 - normalP[1]) * v, normalP[2]);

      hiddenRing(v);
    },
    finishFunc: () => {
      isShow.s('shape3d.transparent', true);

      ht.Default.startAnim({
        duration: 1000,
        easing: t => {
          return 1 - (--t) * t * t * t;
        },
        action: (v, t) => { 
          if(isShow.getTag() === 'linePlane'){
            isShow.s('wf.color', 'rgba(64,173,152,' + (1 - v) + ')');
          }
          else{
            isShow.s('shape3d.opacity', 1 - v);
          }
          moveData.p3(normalP[0], 4500 + (normalP[1] - 4500) * v, normalP[2]);
          isShow.p3(normalP[0], 2500 + (normalP[1] - 2500) * v, normalP[2]);

          showRing(v);
        },
        finishFunc: () => {
          isShow = moveData;
          if(moveData.getTag() === 'normalPlane'){
            moveData.s('shape3d.transparent', false);
          }
          isAnim = false;
        }
      });
    }
  });
}

通過 3D 引擎,我們可以生成立體圖形,也可以新增匯入的模型,圖形的位置由 x、y、z 三個方向的座標來確認,而座標軸匯聚的原點則是圖形的錨點,無人機前方旋轉的圓環則是將錨點調整到圓環中心後,操縱 rotation 屬性進行轉動 。在系統中線框狀態的無人機則是像圖中左側的球體這樣生成的,如果我們將圖形的透明度調為 0,則只顯示線框的樣式。

內部結構

線上框模式下,大家會發現按鈕的上方出現了一個小按鈕,點選它就可以進入到無人機的另一個狀態,在這裡我們除了可以看到線框,還能夠接觸到無人機的內部結構,檢視它的每一個部件。進入的過程會將場景內的其它圖形隱藏,將內部結構顯示出來。

// 內部模式
function moveToInternal(){
  const width = background.getWidth();   // 獲取背景當前寬度
  const beginLeft = -(width / 2 - 960);   // 計算兩側節點起始位置
  const beginRight = width / 2 + 960;

  ht.Default.startAnim({
    duration: 2000,
    easing: t => {
      return t;
    },
    action: (v, t) => { 
      linePlane.s('wf.color', 'rgba(64,173,152,' + (1 - v) + ')');
      hiddenRing(v);
    },
    finishFunc: () => {
      dm3d.each(e => {
          e.s('3d.visible', false)
      })

      linePlane_internal.each(e => {
        e.s('3d.visible', true);
      })

      ht.Default.startAnim({
        duration: 1000,
        easing: t => {
          return 1 - (--t) * t * t * t;
        },
        action: (v, t) => { 
          title.setY(-50 + (70 + 50) * v);
          returnButton.setY(1095 + (1020 - 1095) * v);
          leftShape.setX(beginLeft + 130 * v);
          rightShape.setX(beginRight - 130 * v);
        }
      })
    }
  })
}

接下來,我們探索一下進入內部模式時,從四周緩緩移入視窗的圖形又是怎麼實現的

現如今,終端的螢幕解析度各式各樣,很多網頁在開發前期都會選擇響應式佈局或自適應式佈局,而 HT for Web 的 2D 拓撲元件除了提供向量繪製,還提供了一套相契合的佈局方式。從上圖中我們可以看到,紅框中的部分是最開始的載入介面,而紅框四周的部分則是內部模式中移入的部分,系統中的載入頁面與此不同,正是因為新增了佈局方式。

首先我們將紅框中背景圖片選為整個頁面根節點,修改它的 fullscreen 屬性為 fill,併為其新增全屏鎖定的方式。其次將根節點設定為其餘節點的吸附節點,併為其餘節點修改合適的佈局方式。這樣背景圖片就會填充整個介面,而四周節點的位置則始終保持在背景圖片的外側,在載入介面中就不會顯示出來了。內部模式將四周節點移動至介面內,也是通過修改位置來實現的,但是因為全屏鎖定方式設定為垂直,所以背景的寬度被改變,左右兩側節點的移動則需要在獲取到當前介面顯示寬度後去計算移動位置。

總結

現如今,資訊化快速發展,智慧化工具成為了新生產力出現在我們的生活中。與此同時,工業網際網路的概念也隨之誕生,將人、資料、裝置聯絡到了一起,而視覺化介面則可以很好的將資料和裝置進行展示,方便管理的同時也更加安全、高效。

HT for Web 提供了 2D、3D 兩種模式的視覺化方式,它功能強大的同時也易於學習使用,有興趣的同學也可以來體驗一下。

相關文章