X3D Cover

不太懂程式的你想要做 3D 視覺化,卻覺得 3D 離你太遙遠嗎?事實上,我們完全可以用平常寫網頁的方式來製作 3D 內容!網頁上達成 3D 效果的方式很多,今天就讓我們從標籤語言的角度來看看,如何使用 X3DOM 實現 3D 視覺化。

如果我們稍微對網頁有些概念,會知道網頁是由一堆元素組成的,包含超連結、圖片、列表 … 而從這些元素轉換到原始碼,我們會看到一對對像這樣的標籤:

  <a href="http://blog.infographics.tw"> link </a>

上例是一個超連結的例子,由左右成對的開閉標籤「 <a>」、「</a>」與包含要顯示的文字「link」組成,內含「href」屬性值為「http://blog.infographics.tw」。

下面則是另外一個例子,單獨一個標籤「 <img/> 」代表圖片,「src」屬性值為「http://blog.infographics.tw/img/default.png」指向要顯示的圖片網址:

  <img src="http://blog.infographics.tw/img/default.png"/>

我們在寫網頁時,大多都依照這樣的規則在編寫,包括插入圖片 ( 使用 <img> ) ,強調文字 ( 使用 <em> ) 、製作列表 ( 使用 <li> ) 等等,整個網頁編輯的標籤語言我們則稱之為 HTML; HTML 所包含的標籤集主要都是在文字處理與排版,我們可以想像成是在製作 Microsoft Word 或 PDF 等文件的概念,圖形處理也就比較弱了。

因此,大約在 1999 年前後,用來繪制 2D 圖形的 SVG 標準被制訂了,而 1994 年也出現了為 3D 繪圖而制訂 VRML 標準,經過若干年的演進,VRML 目前已升級為稱做「X3D」的標準。

讓我們簡單的複習一下:

  • HTML 在網頁中排版文件
  • SVG 在網頁中做 2D 繪圖 ( 關於 SVG,可以參考我們先前的文章:可縮放向量圖 SVG 入門 )
  • X3D 在網頁做 3D 繪圖

(* SVG, X3D 不光只是用來做網頁繪圖,但我們在這裡先當成是這樣吧 )

X3DOM - Web 3D Framework

雖然 X3D 以標籤語言的形式定義了 3D 物件的描繪,但這不代表瀏覽器就支援了;為了讓我們可以在瀏覽器中展示與使用 X3D ,我們可以使用 X3DOM  這個 Framework ,他透過 WebGL 為我們實現了 X3D 的瀏覽器支援,讓我們不需要學習 WebGL 也可以輕易的製作 3D 內容!

前置作業

要使用 X3DOM ,我們只要在網頁中引入兩個 X3DOM 的檔案, x3dom.css 與 x3dom.js :

<link rel=”stylesheet” type=”text/css” href=”x3dom.css”/>
<script type=”text/javascript” src=”x3dom.js”></script>

這兩個檔案可以在 X3DOM 網站的下載頁面取得。

我的 3D 第一步

接著,試著在我們的網頁中加入下面的標籤:

  <x3d>
    <scene>
      <shape><box></box></shape>
    </scene>
  </x3d>

你可以看到一個類似下面的結果,畫面中間一個很不容易辨識但是的確存在的正方體,我們甚至可以用滑鼠轉動它:


由於 X3D 是一種標籤語言,我們很容易可以看出這些標籤在幹嘛:

  • <x3d>:如同 <svg>,告知瀏覽器接下來的內容屬於 X3D
  • <scene>:標示一個場景
  • <shape>:標示一個物體
  • <box>:一個立方體形狀

<shape> 告訴瀏覽器我們想要顯示一個物體,而 <box> 則指定這個物體的形狀為立方體。光是形狀還不夠,我們可以在 <shape> 標籤下面使用 <appearance> 指定這個物體呈現的樣貌,例如我們使用 <material> 標籤在 appearance 指定這個物體的顏色:

  <shape>
    <appearance>
      <material diffuseColor="1 1 0"/>
    </appearance>
    <box></box>
  </shape>

上例將會在網頁中畫出一個黃色的方塊:

或者是使用圖片做為其外觀:

  <shape>
    <appearance>
      <ImageTexture url="brick.png"/>
    </appearance>
    <box></box>
  </shape>

馬上我們就可以做出一個瑪莉歐裡的3D問號磚:

製作不同形狀

看到這邊我想大家都很興奮吧,「我也會 3D 了!」沒錯,使用標記語言的話真的很容易,不過我們現在只會畫方塊而已,要做遊戲的話大概只做得出來炸彈超人,炸彈還得是方的。

所以我們就來做不同形狀吧!剛剛我們使用了 <box> 標籤,我想大家應該也猜得到,就跟 SVG 一樣我們只要換個標籤就能有不同形狀了:

  • <sphere> : 畫球的標籤
  • <cone> : 畫圓錐的標籤
  • <torus> : 畫圓環 ( 甜甜圈 ) 的標籤
  • <cylinder> : 畫圓柱的標籤

我們利用兩個 <shape> 標籤放置不同的形狀後產生如下的結果:

雖然多個基本形狀可以組成很多有趣的模型,但這邊還是有兩個問題:

  • 要怎麼設定物體的位置?
  • 更複雜形狀要怎麼繪製?

如果大家有看過先前的「 可縮放向量圖 SVG 入門 」一文,大概也可以猜到了:物件的位置利用特殊屬性設定 (比方說 SVG 中圓形中心的 (cx,cy) 屬性 ),複雜形狀則透過設定形狀的端點來製作 ( 類似 SVG 的 Path 標籤 ) 。

<transform> - 物體位移、旋轉等「線性轉換」

我們可以使用 <transform> 來移動物體的位置。例如,我們利用兩個球體與一個圓柱來製作啞鈴:

  <transform translation="0 -1 0"> <!-- 沿 Y 軸移動 -1.5 單位 -->
    <shape><sphere/></shape>
  </transform>
  <transform translation="0 0 0">
    <shape><cylinder/></shape>
  </transform>
  <transform translation="0 1 0"> <!-- 沿 Y 軸移動 -1.5 單位 -->
    <shape><sphere/></shape>
  </transform>

這裡我們用 <transform> 將球體移至圓柱的兩端,但是因為我們沒有控制物體的大小所以變成了一個看起來像膠囊的東西:

不過沒關係,就像 SVG 的轉換一樣, X3D 的 也提供了位移、旋轉與縮放的機能。我們試著讓圓柱體縮小,再適當的旋轉一下整個形狀:

  <transform rotate="0.4 0.4 1.0 1.57"> <!-- 以 (0.4,0.4,1.0) 向量為軸轉 90 度 -->
    <transform translation="0 -1.5 0"> <!-- 微調位移 -->
      <shape><sphere/></shape>
    </transform>
    <transform translation="0 0 0" scale="0.5 1 0.5"> <!-- X 軸向與 Z 軸向各縮小成一半 -->
      <shape><cylinder/></shape>
    </transform>
    <transform translation="0 1.5 0">
      <shape><sphere/></shape>
    </transform>
  </transform>

結果會類似下面。下例我們順便加了個材質,不過並不影響我們對 <transform> 的理解:

<indexedFaceSet> - 使用多邊形建立各種物體

 

學會如何移動物體後,讓我們來看看怎麼樣建立任意形狀的物體。如同一個平面形狀我們可以想成是由許多線段連接組合而成一樣,一個立體的物體我們也可以當成是由許多塊多邊形拼湊而成的。舉例來說,一個金字塔形的物體有五個面、每個面有三個端點,總共則有五個點。

我們可以把這些多邊形有用到的端點三個三個一組寫出來,以金字塔為例:

  左前方的點:-1  0  1
  右前方的點: 1  0  1
  右後方的點: 1  0 -1
  左後方的點:-1  0 -1
  正上方的點: 0  1.7  0

由於空間中的點總是三個三個一組,我們可以把他寫成一串,並用一個標籤 <coordinate> 來記載這些資訊:

  <coordinate point="-1 0 1 1 0 1 1 0 -1 -1 0 -1 0 1.7 0"/>

現在我們有了點,但還不知道哪些點組成哪些面。正如剛剛所說的,金字塔有五面,每個面各由三個端點組成;而端點都已經記載在 <coordinate> 標籤中了,我們可以說「我要第三個點」,很容易就可以從 <coordinate> 中找到第三個點為 (1,0,-1) 。以此做為點的代號,我們可以列舉每個面所用到的點代號,比方說:

  金字塔的底面:0 3 2 1
  金字塔的前面:0 1 4
  金字塔的左面:0 4 3
  金字塔的右面:1 2 4
  金字塔的後面:2 3 4

(* 這裡用 0 代表第一個點, 1 代表第二個點,依此類推 )

為了撰寫方便,我們也把它們寫成一串,只是為了區隔不同面,我們在面與面之間插入「-1」來做區隔,這便是 indexedFaceSet 的格式:

  <indexedFaceSet coordIndex="0 3 2 1 -1 0 1 4 -1 0 4 3 -1 1 2 4 -1 2 3 4">
    <coordinate point="-1 0 1 1 0 1 1 0 -1 -1 0 -1 0 1.7 0"/>
  </indexedFaceSet>

注意 <coordinate> 是放在 indexedFaceSet 裡面的。於是,我們就畫好了金字塔:

( 同樣的我們在這裡也套上了金字塔的材質,不影響理解 indexedFaceSet 的使用 )

3D 長條圖

講到這裡,我們還是要回到資料視覺化,那就來畫個圖表吧!我們這次利用 X3Dom 來畫個立體長條圖看看吧。台灣近一年來的失業率為:

  103/05 3.99%
  103/06 3.97%
  103/07 3.95%
  103/08 3.94%
  103/09 3.90%
  103/10 3.87%
  103/11 3.87%
  103/12 3.84%
  104/01 3.78%
  104/02 3.74%
  104/03 3.75%
  104/04 3.75%

由於 X3D 是標記語言,我們可以輕易的利用 D3.js 建立資料與 3D 物體間的綁定:

d3.select("x3d scene").selectAll("transform").data(data).enter().append("transform")
d3.select("x3d scene").selectAll("transform").each(function(it,idx) {
  d3.select(this).attr({
    translation: (idx/2 - 3)+" "+(it.1 - 3.7)*4+" 0",
    scale: "0.1 "+ ((it.1 - 3.7) *4) + " 0.1"
  });
  shape = d3.select(this).append("shape");
  shape.append("appearance").append("material").attr("diffuseColor", "red");
  shape.append("box");
});

產生的結果如下 ( 顏色經另外設定,為求精簡此處未列出顏色部份的源碼,且為了讓視覺上的變化更明顯,資料經過縮剪與轉換 ):

這就是我們的第一個 3D 立體長條圖囉! ( 若沒有接觸過 D3.js ,可參考先前的文章:「網頁視覺化利器 - D3.js 簡介」 )

結語

我們在這次的 X3D 入門簡單說明了 X3D 的使用方式與基本的一些標籤,大家都有跟上嗎?這次我們說明的語法只是 X3D 規格中的一小部份,但相信已經可以幫助大家更快的進入狀況了;當我們操縱元素熟練後,就可以使用編輯軟體來產生 x3d 格式的模型檔,這時因為我們很了解檔案內容,這時就可以很輕易的透過 D3.js 來操作囉!

最後不妨參考一下這根 3D 香蕉,他使用 D3.js Fisheye Plugin 製作立體變形效果,是不是很軟 Q 呢?有機會我們再來介紹 X3D 更進階的使用方法吧!


Written by infographics.tw

發表迴響

你的電子郵件位址並不會被公開。 必要欄位標記為 *