2019年1月6日 星期日

Three.js 學習筆記 (一) : 執行環境配置

週五在市圖借了張亞飛寫的 "WebGL 專業級 3D 引擎降臨", 在書中第 18 章讀到基於 WebGL 的 3D 框架 Three.js, 它封裝了 WebGL API 的許多細節, 讓網頁 3D 的開發者能快速建立 3D 場景, 可說是 WebGL 的瑞士刀. 參考 :

https://threejs.org/
https://zh.wikipedia.org/wiki/Three.js

雖說從 WebGL API 以 Bottom-Up 方式著手才能徹底了解 WebFGL 3D 繪圖的精隨, 但那太費時間, 我覺得從較高階的 Three.js 以 Top-Down 方式來學才是快速上手之道.

Three.js 為 Ricardo Cabello 於 2012 年發布之 3D Javascript 函式庫開源專案, 它是基於 WebGL 的物件導向框架, 不需要像以前那樣依賴外部 Plug in 即可進行 3D 繪圖, 並可使用 GPU 加速. 其原始碼與教學文件寄存於 GitHub, 自發布以來經過多位貢獻者持續改進, 目前穩定版本為 r83 :

https://github.com/mrdoob/three.js/

關於 Three.js 的發展摘要如下 :
  1. 前身是 Ricardo Cabello 以 ActionScript 實作的場景演示. 
  2. Ricardo Cabello 於 2009 年移植到 Javascript.
  3. 2010 年首次發布於 GitHub.
  4. Paul Brunt 為 Three.js 添加渲染功能.
  5. Cabello 開發了 CanvasRenderer 與 SVGRenderer 等 API.
  6. Branislav Ulicny 貢獻了素材, 著色器, 後處理, 以及渲染能力之強化. 
  7. Three.js 在所有支持 WebGL 1.0 的瀏覽器皆可運行.
  8. Three.js 以最寬鬆的 MIT 授權開源. 
要在網頁主機佈署 Three.js 執行環境, 可從官網下載壓縮過的 three.min.js 檔 (約 540KB 左右), 按滑鼠右鍵選 "另存連結為", 將此 js 檔放到網頁專案的 js 目錄下 :

http://threejs.org/build/three.min.js

然後在網頁中連結此 js 檔即可 :

<script src="js/three.min.js"></script>

也可以使用 Google API 提供的各版本的 CDN (r84, r83, r82, r81, r80, r79, r78, r77, r76, r75, r74, r73, r72, r71, r70, r69, r68, r67) :

<script src="https://ajax.googleapis.com/ajax/libs/threejs/r84/three.min.js"></script>

另外, cdnjs 網站上也有提供各版本 Three.js CDN 的服務, 參考 :

https://cdnjs.com/libraries/three.js/




選擇所要版本後底下就會出現 three.min.js 的 CDN 超連結, 將其複製到網頁中 Script 標籤的 src 屬性即可.

<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r83/three.min.js"></script>

基本網頁架構如下 :

使用本地 three.min.js

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <script src="js/three.min.js"></script>
  </head>
  <body>
    <script> /* …your code here… */ </script>
  </body>
</html>

使用 Google API CDN 上的 three.min.js

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <script src="https://ajax.googleapis.com/ajax/libs/threejs/r84/three.min.js"></script>
  </head>
  <body>
    <script> /* …your code here… */ </script>
  </body>
</html>

使用 CDN.JS 的 three.min.js

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r83/three.min.js"></script>
  </head>
  <body>
    <script> /* …your code here… */ </script>
  </body>
</html>

這樣就可以開始用 three.js 撰寫 WebGL 3D 網頁繪圖程式了. 但在那之前須對 Three.js 的 3D 繪圖架構, 術語, 以及概念有個基本了解才行, 摘要如下 :
  1. 場景 (Scene) :
    場景是一個容器, 用來放置曲面 (Mesh), 光源 (Light) 與相機 (Camera) 等 3D 繪製元素. 繪製器 (Renderer) 就根據這些元素進行繪製. 
  2. 相機 (Camera) :
    相機是觀察點, 也就是觀察者眼睛的視角投影, 要將 3D 物件呈現在 2D 平面上需要透過相機的投影. 投影有兩種模式 :
    (1). 透視投影 : 使用四稜錐建模將 3D 物件投影到 2D 平面, 有立體感.
    (2). 正交投影 : 模擬長焦鏡頭拍攝相片方式將 3D 投影至 2D, 無立體感.
    透視投影與人類視覺最類似, 有景深空間立體感, 但大小會被扭曲; 正交投影則建立 3D 物件的 2D 幾何圖形, 提供無扭曲的投影視圖, 以便精確縮放與放置. 一般 3D 繪圖流程是先以正交投影來建立場景, 得到精確之製圖與建模, 然後用透視投影來繪製輸出. Three.js 提供 PerspectiveCamera 類別與 OrthographicCamera 類別來實現透視與正交投影相機功能. 
  3. 繪製器 (Renderer) :
    繪製器是用來繪製 3D 圖形的畫布, 也就是 HTML5 中的 Canvas 元素. Three.js 提供兩種繪製器 :
    (1). WebGLRenderer :
           使用 WebGLRenderingContext 實現 GPU 加速之 2D/3D 繪圖
    (2). CanvasRenderer  (目前已被移除) :
           使用 CanvasRenderingContext2D 實現 2D 繪圖
    在特定情況下 CanvasRenderer 可用 2D 環境模擬出 3D 效果, 但與材質或光源有關的 3D 功能無法模擬, 故 3D 繪圖還是以 WebGLRenderer 為主. 若瀏覽器不支援 WebGLRenderer, 那也只好用 CanvasGLRenderer 來模擬. 
使用 Three.js 繪製 3D 圖形的流程如下 :
  1. 建立場景 :
    使用 new THREE.scene() 
  2. 建立曲面圖形物件 (mesh) :
    使用 new THREE.mesh(geometr, material)
    呼叫場景物件的 add() 方法將曲面物件加入場景中
    曲面就是 3D 空間中的物體, 由幾何形狀或模型 (geometry) 與材質 (material) 組成. 其中 geometry 由三個屬性定義 :
    (1). vertices : 頂點清單
    (2). faces : 面
    (3). faceVertexUv : 紋理座標
  3. 建立相機 :
    使用 new THREE.PerspectiveCamera()
    相機建立後會自動加入場景中, 不須呼叫 add()
  4. 建立繪製器 :
    使用 new THREE.WebGLRenderer() 建立繪製器物件 (canvas 元素)
    將繪製器物件加入網頁的 DOM 結構中
    呼叫繪製器物件的 render(scene, camera) 方法繪製指定場景與相機
若要讓 3D 圖形動起來, 通常會將這四個設定程序都寫在自訂函數 init() 中, 因為它們只在 Javascript 程式載入時執行一次. 而動作的部分寫在自訂函數 animate() 中. 以下範例改寫自 Three.js 的 Wiki, 參考 :

https://zh.wikipedia.org/wiki/Three.js

測試 1 : 移動中的 3D 立方體 [始碼]

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <style>
      body {
        background-color: #ffffff;
        margin: 0;
        overflow: hidden;
        }
    </style>
    <script src="js/three.min.js"></script>
  </head>
  <body>
    <script>
    //宣告全域變數
    var scene, camera, renderer;
    var geometry, material, mesh;
    init(); //呼叫起始設定函數
    animate(); //呼叫動畫函數
    //定義起始設定函數
    function init() {
      //1.建立場景
      scene=new THREE.Scene();
      //2.建立曲面圖形物件
      geometry=new THREE.BoxGeometry(500, 500, 500); //建立幾何形狀
      material=new THREE.MeshBasicMaterial({  //建立材質
        color: 0xff0000,
        wireframe: true});
      mesh=new THREE.Mesh(geometry, material); //以幾何形狀與材質建立曲面
      scene.add(mesh);  //曲面圖形物件加入場景中
      //3.建立相機
      camera=new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 1, 10000);
      camera.position.z=1000;
      //4.建立繪圖器
      renderer=new THREE.WebGLRenderer({antialias:true});  //建立 WebGL 繪圖器
      renderer.setClearColor("#ffffff");  //設定背景色為白色
      renderer.setSize(window.innerWidth, window.innerHeight); //設定畫布為瀏覽器大小
      document.body.appendChild(renderer.domElement); //將畫布加入瀏覽器 DOM 中
      }
    //定義動畫函數
    function animate() {
      requestAnimationFrame(animate);
      mesh.rotation.x += 0.01;
      mesh.rotation.y += 0.02;
      renderer.render(scene, camera);
      }
    </script>
  </body>
</html>

此例 Three.js 我改用 Google API 提供的 CDN 服務, 執行結果如下 :


此立方體實際上是不斷旋轉移動的.

下面範例改寫自 "Game Development with Three.js (Packt, 2013)" 第一章範例, 此程式使用了 CanvasRenderer, 新版 Three.js 已不支援, 需使用較舊的版本, 參考 :

https://mrdoob.com/projects/htmleditor/


測試 2 : 轉動的 3D 球體 [始碼]

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <style>
      body {
        background-color: #ffffff;
        margin: 0;
        overflow: hidden;
        }
    </style>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r59/three.min.js"></script>
  </head>
  <body>
    <script>
    var scene, camera, renderer;
    var geometry, material, mesh;
    init();
    animate();
    function init() {
      scene=new THREE.Scene();
      geometry=new THREE.IcosahedronGeometry(200, 1);
      material=new THREE.MeshBasicMaterial({
                     color: 0x000000,
                     wireframe: true,
                     wireframeLinewidth: 2});
      mesh=new THREE.Mesh(geometry, material);
      scene.add(mesh);

      camera=new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 1000);
      camera.position.z=500;

      renderer=new THREE.WebGLRenderer({antialias:true});
      renderer.setClearColor("#ffffff");
      renderer.setSize(window.innerWidth, window.innerHeight);
      document.body.appendChild(renderer.domElement);
      }

    function animate() {
      requestAnimationFrame(animate);
      mesh.rotation.x=Date.now() * 0.00005;
      mesh.rotation.y=Date.now() * 0.0001;
      renderer.render(scene, camera);
      }
    </script>
  </body>
</html>

執行結果如下 :

這是一個轉動的球體.

參考 :

[Da13] Three.js API 簡介
Three.js 101 : Hello World! (Part 1)

2019-01-07 補充 :

由於所有主流瀏覽器均已支援 WebGLRenderer, 因此 Three.js 已經將 CanvasRenderer 移除了, 如果要執行含有此繪圖器的程式, 可在 r59 或之前版本的 Three.min.js 上執行.

2020-09-15 補充 :

今天在接案網站上看到有人徵 three.js 案子, 想起我好像曾經玩過 (可惡的失憶), 於是就複習了一下, 順便找到下面不錯的教學 :

3D 網站開發入門筆記_Three.js 入門

這個好玩, 我有空會再回來玩的.

沒有留言 :