iT邦幫忙

2022 iThome 鐵人賽

DAY 2
0
Software Development

30天成為鍵盤麥可貝:前端視覺特效開發實戰系列 第 2

Day2: ThreeJS、OpenGL、WebGL:誰是誰?我要怎麼開始?

  • 分享至 

  • xImage
  •  

剛開始學前端視覺特效經常是這樣:

https://ithelp.ithome.com.tw/upload/images/20220917/20142505gHhLZLhHuO.png
在開始Three.js之前,得先解釋清楚三者關係。接下來我也會不斷解釋三者關聯。

三者關係

  • Three.js

    上層的API函式庫,負責讓在Canvas裡面執行一些繪圖。他底層是用WebGL API處理繪圖的。

  • WebGL

    一個JS的API,它讓工程師可以控制每一顆像素要呈現的RGB顏色。它會再跟CPU, GPU溝通,最後成功繪製畫面。

  • OpenGL 或他的子集OpenGL ES

    是一個業界標準,他不像WebGL是函式庫,WebGL就是基於這個標準的API。另一方面,假如NVIDIA要制定GPU接口,就需要支援OpenGL標準。

繪製一張圖釐清關聯:
https://ithelp.ithome.com.tw/upload/images/20220917/20142505KIuI3jkJHE.png

我們會從three.js開始,因為比較快速入門。

從three.js開始,要怎麼起頭?

從three.js開始:四個物件

要滿足四樣東西。用現實世界來形容,就是:

  1. 鏡頭(camera)捕捉了
  2. 畫面(Scene),透過
  3. Renderer 渲染到畫面上,並藉由
  4. requestAnimationFrame不斷更新畫面

前兩個很好理解,但後兩個是什麼?

https://ithelp.ithome.com.tw/upload/images/20220917/20142505NtlVqo0eZJ.png

從three.js開始:Renderer是什麼?

比如說你的螢幕有1920 x 1280,那就有兩百多萬畫素。鏡頭camera捕捉到了場景Scene,要如何呈現在你兩百多萬畫素的螢幕?就需要透過 Renderer了。

從three.js開始:requestAnimationFrame是什麼?

理想上鏡頭一秒60幀渲染在畫面上。這個如「狂按快門」的邏輯就要靠requestAnimationFrame 來處理。

從three.js開始:四者總結

換句話說,WebGLRenderer 是鏡頭捕捉到的場景,呈現在上的重要推手。而requestAnimationFrame 使得它每一幀捕捉一次。在理想狀態下每秒捕捉60次(60FPS)。

從three.js開始:three.js世界觀

在Three的世界裡,大家都是活在笛卡兒三維座標裡面,不像HTML的元素都活在XY的座標系統中。所以說,代表每個物件都有X,Y,Z位置,可以縮放、旋轉、位移。(幾乎啦,但也有例外,如ambientLight

開始程式碼

要製作最簡單的畫面,承上篇結尾需要四樣的東西。先從scene開始。

  • 開始程式碼:Scene。用來裝所有場景物件的根物件。

    const scene = new THREE.Scene();
    
  • 開始程式碼:Camera。做為我們的鏡頭,選用透視鏡頭PerspectiveCamera即可。

    // PerspectiveCamera 需設定四個參數,下面接著介紹
    const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
    // Camera 身為鏡頭,有位置屬性,設定在Z軸即可。
    camera.position.set(0, 0, 15)
    

    四個參數代表什麼?代表fov, aspect, far,near等四個參數。

    • 補充說明:PerspectiveCamera

      Camera主要跟兩個:PerspectiveCamera 跟 OrthographicCamera,前者有透視,透者則無。本次我們使用前者,需給定四個參數,以下介紹:

      我這邊用攝影鏡頭補充說明。它跟FOV融會貫通:

      https://ithelp.ithome.com.tw/upload/images/20220917/20142505FTryM7Vnba.png

      PerspectiveCamera透視說明。圖片來源

      PerspectiveCamera參數:Field Of View (FOV)

      我們拿最蝦趴的三鏡頭iPhone為例,他三顆分別是:長焦(52mm)、廣角(26mm)、高級廣角(13mm)。

      • 長焦:你去拿52mm對照上圖。它呈現的範圍相當小,則遠方的細節也相當清楚,看起來很扁。
      • 廣角:一個越廣角的鏡頭,它可以呈現的畫面越廣。
      • 超級廣角:你去拿13mm對照上圖會發現他扭曲的程度。我們假設攝影機可以捕捉的範圍是一個四角錐,則廣角鏡頭的四角錐範圍就相當大,然而邊緣都會扭曲。

      而這個變化差距就稱作Field of View,也就是視野的範圍。在這裡,是以角度為單位,一般來說預設是50度(不是mm喔)。這是three.js當中少數以角度而非弧度做為單位的參數。

      PerspectiveCamera參數:Aspect

      則代表畫面的寬高比例。基本上只要設定為Canvas的寬高比例即可。

      PerspectiveCamera參數:Near 與 Far

      代表鏡頭最短與最遠可以捕捉到的範圍。

      https://ithelp.ithome.com.tw/upload/images/20220917/20142505panmRBoxNs.png
      圖片來源

  • 開始程式碼:WebGLRenderer。渲染器,用來渲染場景。顧名思義就是渲染出畫布執行。過去three.js提供很多種渲染器,例如CSSRenderer,但以我們的需求WebGLRenderer即可。

    // 實例化渲染器
    const renderer = new THREE.WebGLRenderer();
    // 渲染器負責投影畫面在螢幕上,會需要寬高
    renderer.setSize(window.innerWidth, window.innerHeight);
    // 渲染器會產生canvas物件,我們在html的body放置它
    document.body.appendChild( renderer.domElement );
    
  • 開始程式碼:再回到Scene。我們要在Scene內建立Mesh物件

    • 可想像成Scene底下的物件。我們可以在場景上加上Mesh物件。
    • Mesh物件就是最常見的3D物件,你會用它呈現物體。除了Mesh以外,還有Curve、Light這種沒有體積的物件。
    • Mesh由兩個東西組成,一個是Geometry,一個是Material。可以想像成:Geometry是一個物體的形狀,可以定義成球形、平面、矩形等。Material則是物體所穿的衣服(亦即材質)。
    // 建立一個形狀,用來定義物體的形狀為長寬高為1的正方體
    const geometry = new THREE.BoxGeometry(1,1,1)
    // 建立一個材質,可想像成一個物體所穿的衣服,設定材質為藍色
    const material = new THREE.MeshBasicMaterial({color: 0x0000ff})
    // 依據前兩者,建立物體
    const cube = new THREE.Mesh(geometry, material);
    // 放到場景裡,預設位置會是(0,0,0)
    scene.add(cube);
    
  • 開始程式碼:requestAnimationFrame。用以捕捉每幀畫面。在調用requestAnimationFrame()時,它會呼叫自己以執行下一幀。作用很像setinterval()。

    提供這個函式後,工程師可以使用特定功能,例如Raycaster、Project、Unproect等等。

    // 很像setInterval的函式。每一幀都會執行這個函式
    function animate() {
    	// 每一幀物體都會自轉
      cube.rotation.x += 0.01;
      cube.rotation.y += 0.01;
    	// 它每一幀執行animate()
    	requestAnimationFrame( animate );
    	// 每一幀,場景物件都會被鏡頭捕捉
    	renderer.render( scene, camera );
    }
    // 函式起始點
    animate();
    

有了以後東西之後,新場景就完成了。

CodePen Example

https://codepen.io/umas-sunavan/pen/VwxpxVB?editors=0010

Three.js跟WebGL之間的關聯

兩者關聯:為什麼場景不需要光?

現實中,我們之所以可以看到東西,是因為有物體、眼睛跟光。你會問為什麼沒有光。

在Three.js的世界裡,一個Mesh(最常見的3D物件)由Geometry跟Material組成。

  • Geometry:代表物體的形狀。Three.js提供方形、球形等多種形狀,你也可以自訂。
  • Material:代表材質。則很像是物體「所穿的衣服」。Three提供很多種材質,有些可以反射光,有些根本不理會光。
    • Shader:three.js提供的Material基本上是由WebGL的Shader渲染。在WebGL的世界裡,沒有光的概念,它只Care每個螢幕像素要裝入什麼顏色。Three.js透過材質跟光,提供給WebGL一個算式,使得WebGL可以計算出每個像素的顏色。
    • 我們目前用的MeshBasicMaterail不需要光,它只看工程師給定給它什麼顏色參數,它就直接傳遞給Shader渲染。我們給他純藍,那他就是純藍,所以這場景不需要光。

兩者關聯:WebGL裡面真的會藏有鏡頭

這是一個延伸思考。實際上,鏡頭什麼的都是Three.js提供的,WebGL裡面沒有所謂鏡頭這東西,WebGL只要求你螢幕上每一個像素你都要給一個顏色。

可是Three.js將鏡頭所投影的成像,經過計算,算出螢幕中每一個像素的顏色。由此,你不需要考慮那麼底層,你只要考慮Three.js的鏡頭怎麼放置就好了。

兩者關聯:以透視為例

我們以透視為例。今天Three.js要在WebGL之上實作透視,那要怎麼處理呢?現在有一條線在三維座標裡,XY的起點是(0,0)終點是(0,10)

  1. 假設今天Z值都在1,那麼經由一個計算(10/1=10),它是10單位寬。
  2. 假設今天Z值改在2,那麼經由一個計算(10/2=5),變成5單位寬。
  3. 假設今天Z值改在3,那麼經由一個計算(10/3=3.33),變成3.33單位寬。

可見,在這個計算下,Z值越大,長度越短。

所以今天那條線是(0,0,1)到(0,10,3),那Z值大的地方比較短,Z值小的地方比較長。這時候透視就出來了,而這個計算概念變複雜點就變成透視的計算。

在WebGL裡面,透視要自己來做,但在three.js裡面,設定鏡頭的FOV跟位置就解決了。你要是用WebGL自幹透視,包你到最後認不得自己在寫啥,你同事看了保證也不想跟你Code review。所以three.js提供的鏡頭,為的就是加速底層的實作。同理可套用在文中一開始提到的Scene, Renderer,以及three.js官網的所有東西。

我這邊就先帶個概念就好。至少,我們已經做出three.js基本的畫面了。這是踏入Three.js偉大的一步。我們明天介紹座標系。

參考資料


上一篇
Day 1:前端視覺特效做出什麼內容?可以吃嗎?
下一篇
Day3: Three.js空間座標!讓世界繞著我旋轉!
系列文
30天成為鍵盤麥可貝:前端視覺特效開發實戰31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言