3D機房前端學習筆記

JamiePlur發表於2017-03-29

本文原始碼地址:http://download.csdn.net/detail/jamesplur/9797919

實現任務:能夠在前端網頁中嵌入3D機房模型並實現基本的互動


3D基本原理

為了實現3D影像的動態展示有三個要點:

  1. 建立三維場景

    三維場景包括三維模型、光源、相機三個部分構成。

    從最簡單的情形來說,三維模型由網格(mesh)和材質(material)組成。網格則一般由若干個圖元(一般是空間中的三角形)拼接而成。網格表面還要考慮本身的紋理和對光源的反射情況。所以3D模型的資料主要是頂點資訊和網格表面材質資訊。

    光源就是在空間中人為新增光照資訊,例如方向,強度,光色等。沒有光源,渲染出來的3D場景將一片漆黑。

    相機相當於場景的一個觀察點。相機在建立時就要確定,視角,遠平面,近平面,縱橫比四個引數。根據這些引數可以確定一個可視區域。3D模型必須在可視區域(view frustum)內。否則將不會被螢幕捕捉。

圖片來源網路

  1. 將場景以2D的形式顯示

    要想將場景以2D的形式顯示出來,實際就是將空間中的點對映為平面的畫素值。這個過程需要考慮兩個關係:

    模型和相機之間的關係:這個變換關係由兩個矩陣控制,其中一個矩陣表示了相機所在位置和方向,另一個矩陣控制了從3D到2D的投影引數。模型和相機的關係決定空間中的某一點是否會被渲染。

    光源和材質的關係: 光源和材質的關係決定這一點的畫素值會是多少。著色器將會結合該點的材質(包括對光的反射率)和光照強度,判斷該點的顏色。

  2. 當3D資料發生改變時,可以近乎無延遲的對2D影像更新渲染

    上述複雜的對映過程,實際是交給著色器來完成的,著色器往往是一種基於gpu的程式(所謂硬體加速)。保證了渲染的低延遲。

瀏覽器對3D的支援

WEBGL標準的實現,使利用瀏覽器進行3D繪圖成為可能
這裡引用wiki百科的一段介紹

WebGL (Web Graphics Library) is a JavaScript API for rendering 3D graphics within any compatible web browser without the use of plug-ins.[2] WebGL is integrated completely into all the web standards of the browser allowing GPU accelerated usage of physics and image processing and effects as part of the web page canvas. WebGL elements can be mixed with other HTML elements and composited with other parts of the page or page background.[3] WebGL programs consist of control code written in JavaScript and shader code that is written in OpenGL Shading Language (GLSL), a language similar to C or C++, and is executed on a computer’s graphics processing unit (GPU). WebGL is designed and maintained by the non-profit Khronos Group.[4]

總結一下要點:

  • 部分瀏覽器支援(火狐、谷歌),不需要外掛
  • OPENGL的web版,提供了js介面,可將3d場景直接繪製到頁面元素中(canvas元素)
  • 提供了硬體加速手段,也就是之前所提的著色器,這段著色器由GLSL語言所編寫,可以操控顯示卡使網頁的顯示更加流暢

實際上利用WEBGL介面進行開發是非常非常吃力的,具體有多吃力可以參考附件程式碼。
可以看到編寫一個旋轉的正方體就用了三百行程式碼。這是開發所不允許的。

VIZI開發引擎簡介

提高開發效率有兩個要點:

  • 選用一個合適的3D開發引擎:
    由於目前3D開發還沒有一個功能全面的的而且被廣泛相容的(相容這一點更加重要)框架,所以很多公司都給出了自己的解決方案,大多都是收費的。在免費的框架中,three.js是其中最為出色的,它合理的封裝了WEBGL的主要部件,使得初學者學習3D開發變成了可能。tween.js則是three.js有力的補充,利用tween.js可以實現一些簡單的補間動畫。

  • 利用模型軟體進行建模:
    用js進行建模可真的是想多了。最合理的方式是利用3Dmaxs或者maya進行建模和一些複雜動畫的編輯。再匯出到合適的格式中去。這些格式包括obj,dae等。其中dae是功能最為強大的通用場景格式檔案,包含了相機、光源、幾何體、材質、動畫等資訊。three.js中提供了這些檔案的解析器,將會把這些檔案解析為json格式,再進一步轉化為three.scene物件。

    這裡注意:
    WebGL預設情況下不允許使用本機上的紋理、模型檔案的。若想載入外部模型和紋理檔案,
    由於同源策略的安全限制從檔案系統載入檔案會因為安全異常而失敗。不過有兩個辦法可以解決:
    降低瀏覽器的安全級別
    在本機上建立一個伺服器,把外部檔案放到該伺服器作為網路檔案訪問   
    

所謂開發引擎,便是將介面進行高度的封裝抽象,以實現快速的開發。

Vizi是由美國tony parisi編寫的3d開發引擎,該引擎基於three.js和tween.js,可以快速實現3d開發所需的基本功能。之後給出例項也是基於tony parisi所編寫的例程。

這裡簡要介紹下VIZI的類所完成的工作:

Viewer類:

在例項化viewer時,會將viewer的容器(一個塊狀dom元素)傳遞給它作為顯示3d內容的畫布。

viewerrun()方法會初始化渲染器(一個封裝了webgl著色器的類)並開啟迴圈。

viewerreplacescene()方法會將讀取到的場景資料載入到畫布中,如果場景資料沒有相機和光源的話,該函式還會建立預設的光源和相機。

Object類:

vizi中模型儲存的基本資料型別

loader類:

loaderloadscene()方法會根據載入檔案的型別自動選擇three.js的載入器

picker類:

picker物件可以新增到模型物件的屬性中去,並可由addEventListener()方法新增互動事件,如滑鼠經過,滑鼠點選等

Component類:

component物件可以新增到模型物件的屬性中去,並通過tween.js為物件新增簡單的補間動畫

附錄程式使用文件

這裡利用vizi引擎,開發了3D機房的一個演示介面

3d機房的模型檔案是由maya匯出的dae檔案,匯出外掛為opencollada(不知道為什麼,3dsmax匯出的dae檔案是不能用的)

該程式將vizi的一些類都封裝起來,提供了所需要的一些介面函式和回撥函式。下面是程式詳細解釋:

1. 檔案結構

  • 3d 是模型測試頁面
  • Src中存放3d機房的原始檔3d.js
  • Libs中存放相關庫檔案
  • Images中存放圖片
  • Css中存放樣式表

2. 建立機房

首先建立一個容器:

    <div id="container"></div>

然後例項化threedroom類,並將container元素作為引數傳遞。其餘的引數為可能用到的回撥函式

var container = document.getElementById("container");
    threedroom = new ThreeDroom({ container : container,
            loadCallback : onLoadComplete,
            loadProgressCallback : onLoadProgress,
            mouseOverCallback : onMouseOver,
            mouseOutCallback : onMouseOut,
            mouseUpCallback : onMouseUp,
         });

    threedroom.go();

threedroom.go()函式例項化了viewer類,載入了場景檔案,並開始了渲染迴圈

ThreeDroom.prototype.go = function() {
    this.viewer = new Vizi.Viewer({ container : this.container, showGrid : true});
    this.loadURL(ThreeDroom.URL);
    this.viewer.run();
}

其中loadURL為載入模型的函式,同時也為新增了兩個監聽事件,當載入完成之後會呼叫onLoadComplete()這個函式(onLoadProgress負責顯示載入的進度)

ThreeDroom.prototype.loadURL = function(url) {

    var that = this;

    var loader = new Vizi.Loader;
    loader.addEventListener("loaded", function(data) { that.onLoadComplete(data, loadStartTime); }); 
    loader.addEventListener("progress", function(progress) { that.onLoadProgress(progress); }); 

    var loadStartTime = Date.now();
    loader.loadScene(url);  
}

3. 新增滑鼠互動

onLoadComplete()除了將場景資料交給viewer之外,還為場景中的物件新增了互動事件:

ThreeDroom.prototype.onLoadComplete = function(data, loadStartTime)
{
     var that = this;
    ThreeDroom.Scene = data.scene;
     this.viewer.replaceScene(data);
    ThreeDroom.Scene.map(/^bottom_Mesh(.*)/,function(o){
         console.log("found node!");
         var highlight=new Vizi.HighlightBehavior({highlightColor:0x88eeff});
         o.addComponent(highlight);
         var picker = new Vizi.Picker;
         picker.addEventListener("mouseover", function (event) {
             that.onMouseOver("over", event);
             if(!that.warningstatus)
             {highlight.on();}
         });
         picker.addEventListener("mouseout", function (event) {
             that.onMouseOut("out", event);
              if(!that.warningstatus)
              {highlight.off();}
         });
         picker.addEventListener("mouseup", function (event) {
             that.onMouseUp("up", event);
         });
         o.addComponent(picker);
     });

首先,新增場景資料之後,所有的3d物件資料都儲存在ThreeDroom.Scene中。

map()是一個遍歷函式,它會找到物件名稱符合正規表示式的物件,並新增滑鼠的互動事件。

 ThreeDroom.Scene.map(/這裡新增正規表示式/,function(這裡新增響應事件))

滑鼠互動事件會觸發對應的響應函式,這個響應函式定義在3d.js裡:

ThreeDroom.prototype.onMouseOver = function(what, event) {
    if (this.mouseOverCallback)
    this.mouseOverCallback(what, event);

}

ThreeDroom.prototype.onMouseOut = function(what, event) {
    if (this.mouseOutCallback)
        this.mouseOutCallback(what, event);
}

ThreeDroom.prototype.onMouseUp = function(what, event) {
    if (this.mouseUpCallback)
        this.mouseUpCallback(what, event);
}

該函式的作用是回撥用主頁面的處理函式,應在此新增事件
其中可通過what引數來區分是由那個元素觸發了事件,event是jquery所定義的事件標記

function onMouseOver(what, event) {
    console.log("Mouse over");
    alert("在這裡填充響應事件!");
}

function onMouseOut(what, event) {
    console.log("Mouse out");
    alert("在這裡填充響應事件!");

}
function onMouseUp(what, event) {
    console.log("Mouse up");
    alert("在這裡填充響應事件!");
}

總結來說,要為新增滑鼠互動事件,首先要在onloadcomplete函式中,為指定的3d物件新增picker

然後在頁面中的回撥函式中利用what引數分辨是哪個元素產生的事件

4. 新增報警動畫

警報動畫的介面函式為warningon與warningoff,引數為target。也就是所需要新增警報動畫的物件的名稱的正規表示式,warningon代表開啟警報,warningoff代表關閉警報。需要注意只有有材質的子物件才可以新增報警動畫。組物件是不能新增的。

function warningon() {
   threedroom.warningon(/^bottom_Mesh(.*)/);
}

function warningoff() {
   threedroom.warningoff(/^bottom_Mesh(.*)/);
}

相關文章