1. 程式人生 > >基於 HTML5 Canvas 的 3D 渲染引擎介面以及吸附等效果的運用

基於 HTML5 Canvas 的 3D 渲染引擎介面以及吸附等效果的運用

前言

HT 是啥:Everything you need to create cutting-edge 2D and 3D visualization.

這口號是當年心目中的產品方向,接著就朝這個方向慢慢打磨,如今 HT 算是達到了這樣的效果,談不上用盡洪荒之力,但我們對產品結果很滿意,特別是 HT 的使用者手冊,將例子和文件無縫融合一體,小小 10 來兆開發包居然包含了四十五份手冊,數百個活生生的 HTML5 例子,還沒體驗過的同學可以點選 http://www.hightopo.com/guide/readme.html HT 手冊入口玩一玩。

這樣綜合並可搜尋的手冊入口居然還常被問及 HT 的 Demo 在哪裡?只能怪這些年深入人心的極致使用者體驗理念,把人慣得包括很多程式設計師都如此之“懶”,當然也怪我們沒把使用者體驗的最後一公里做到位,將數百個手冊例子進行了歸類整理,最終形成了方便大家直觀查詢所有 HT 例子的頁面:

http://www.hightopo.com/demos/index.html 。很明顯,對於 HT 老使用者包括我們自己做技術支援,都感覺查詢例子更直觀方便,但對於 HT 初學者,面對這一堆數百個涵括通用元件、網路拓撲圖元件、3D 元件、向量圖形、各種編輯器等等五法八門的 HTML5 例子盛宴,往往無從下手,為此我打算為像我一樣喜歡這方面的新手朋友多寫幾篇這樣的部落格,慢慢的給大家講述各種各樣的越來越多的有趣的小功能!

效果圖

程式碼實現

HT 提供了基於 WebGL 的 3D 技術的圖形元件 ht.graph3d.Graph3dView,WebGL 基於 OpenGL ES 2.0 圖形介面,因此 WebGL 屬於底層的圖形 API 介面,二次開發還是有很高的門檻,HT 的 Graph3dView 元件通過對 WebGL 底層技術的封裝,與 HT 其他元件一樣,基於 HT 統一的 DataModel 資料模型來驅動圖形顯示,極大降低了 3D 圖形技術開發的門檻。同時 HT 提供了強大的完全基於 HTML5 技術 3D 圖形建模設計器,使用者無需編碼即可快速視覺化搭建各種 3D 場景,可以說 HT 的 3D 開發模式完全打破了傳統 3D 開發模式,絕大部分應用不再需要依賴精通 3ds Max 或 Maya 的專業 3D 設計師來建模,也不需要整合 Unity3d 等引擎做圖形渲染,HT 一站式的提供了從建模到渲染,包括和 2D 元件呈現和資料融合的一站式解決方案。

我本次講解的就是這個 3D 的介面,所以我們首先要建立 3D 渲染引擎元件,視覺化呈現資料模型的三維環境場景。

dataModel = new ht.DataModel();
g3d = new ht.graph3d.Graph3dView(dataModel);
g3d.addToDOM();
window.addEventListener('resize', function (e) {
  g3d.invalidate();
}, false);

我們還要設定眼睛(或Camera)所在位置,預設值為 [0, 300, 1000] ,格式為 [x, y, z] 。

g3d.setEye([0, 300, 600]);

然後我們再給它加上一些選中效果。Graph3dView 中被選中的圖元會顯示為較暗的狀態,變暗係數是由圖元 style 的 brightness 和 select.brightness 屬性決定,select.brightness 屬性預設值為 0.7,最終返回值大於 1 變亮,小於 1 變暗,等於 1 或為空則不變化。Graph3dView#getBrightness 函式控制最終圖元亮度,因此也可以通過過載覆蓋該函式自定義選中圖元亮度。

g3d.getBrightness = function (data) {
  if (data.s('isFocused')) {
     return 0.7;
   }
  return null;
};
lastFocusData = null;
g3d.getView().addEventListener('mousemove', function (e) {
  // 傳入邏輯座標點或者互動 event 事件引數,返回當前點下的圖元
   var data = g3d.getDataAt(e);
   if (data !== lastFocusData) {
     if (lastFocusData) {
        astFocusData.s('isFocused', false);
      }
      if (data) {
         data.s('isFocused', true);
      }
      astFocusData = data;
  }
});

接下來我們寫個函式來方便繪製每個部分的模型:

function createNode (p3, s3, host) {
  // 拓撲圖元型別
  var node = new ht.Node();
  // 獲取或設定圖元中心點的三維座標 有三個引數時相當於 setPosition3d 沒有相當於 get
  node.p3(p3);
  // 獲取或設定圖元的尺寸 有三個引數時相當於 setSize3d 沒有相當於 get
  node.s3(s3);
  // 設定宿主圖元,當圖元吸附上宿主圖元(host)時,宿主移動或旋轉時會帶動所有吸附者
  node.setHost(host);
  dataModel.add(node);
  return node;
}

講到了這,我們來說說吸附,吸附功能對於設計有層次關係的模型非常方便,例如裝置面板吸附上裝置機框,裝置埠吸附上裝置面板,這樣從機框 - 面板 - 埠的層次關係吸附,使得使用者拖動整體機框時所有這個層次下的圖元都會跟隨移動。對於 3D 的場景下,吸附的概念更進一步延伸,當機框在三維空間進行任意位置偏移以及任意角度旋轉時,所有吸附的相關圖元都會正確的跟隨平移,並做出相應位置對應的旋轉,以達到整體裝置各個圖形部分保持物理相對位置一致。

下面我們來一起建立模型吧!分別是地板 floor,桌面 table,四個桌腿以及盒子:

// 地板
floor = createNode([0, 0, 0], [600, 5, 400]).s({
  'all.color': '#A0A0A0',// 六面顏色
  'label': '地板',// 圖元文字內容
  'label.face': 'top',// 文字在3d下的朝向,可取值(left|right|top|bottom|front|back|center)
  'label.background': 'yellow',// 圖元文字背景
  'label.position': 22,// 圖元文字位置
  'label.t3': [10, 0, -10],// 文字在3d下的偏移,格式為 [x,y,z]
  'label.font': '28px arial, sans-serif'// 圖元文字字型
});
// 桌面
table = createNode([0, 120, 0], [400, 10, 280], floor).s({
  'shape3d': 'cylinder',// 為空時顯示為六面立方體,cylinder 圓柱
  'shape3d.side': 60,// 決定 3d 圖形顯示為幾邊型,為 0 時顯示為平滑的曲面效果
  'shape3d.color': '#E5BB77',// 3d 圖形整體顏色
  'label': '桌子',
  'label.face': 'top',
  'label.background': 'yellow',
  'label.position': 23,
  'label.t3': [0, 0, -10],
  'label.font': '20px arial, sans-serif'
});
// 四個桌腿
foot1 = createNode([100, 60, 80], [20, 110, 20], table).s({
  'shape3d': 'cylinder',
  'shape3d.color': '#E5BB77',
});
foot2 = createNode([-100, 60, 80], [20, 110, 20], table).s({
  'shape3d': 'cylinder',
  'shape3d.color': '#E5BB77',
});
foot3 = createNode([100, 60, -80], [20, 110, 20], table).s({
  'shape3d': 'cylinder',
  'shape3d.color': '#E5BB77',
});
foot4 = createNode([-100, 60, -80], [20, 110, 20], table).s({
  'shape3d': 'cylinder',
  'shape3d.color': '#E5BB77',
});
// 盒子
box = createNode([0, 150, 0], [100, 50, 60], table).s({
  'all.color': '#2e2f32',
  'front.color': '#BDC3C7',// 前面顏色
  'note': '盯著你看', // 圖元冒泡標註
  'note.face': 'top',
  'note.position': 7,
  'note.t3': [0, 0, 10],
  'note.autorotate': true// 圖示在 3D 下是否自動朝向眼睛的方向
});

程式碼中有一些屬性,我已經幫大家寫好了詳細的註釋。在此獻上各種關於 'shape3d' 的圖形的值,方便大家玩耍:

總結

我每週儘量都寫一些技術隨筆,既幫助自己整理知識,也能夠跟大家一起學習,我們由淺至深,循序漸進。希望看了我的文章能得你們帶來幫助,同時也希望大家能多多支援和鼓勵!