剛開始學前端視覺特效經常是這樣:
在開始Three.js之前,得先解釋清楚三者關係。接下來我也會不斷解釋三者關聯。
Three.js
上層的API函式庫,負責讓在Canvas裡面執行一些繪圖。他底層是用WebGL API處理繪圖的。
WebGL
一個JS的API,它讓工程師可以控制每一顆像素要呈現的RGB顏色。它會再跟CPU, GPU溝通,最後成功繪製畫面。
OpenGL 或他的子集OpenGL ES
是一個業界標準,他不像WebGL是函式庫,WebGL就是基於這個標準的API。另一方面,假如NVIDIA要制定GPU接口,就需要支援OpenGL標準。
繪製一張圖釐清關聯:
我們會從three.js開始,因為比較快速入門。
要滿足四樣東西。用現實世界來形容,就是:
camera
)捕捉了Scene
),透過Renderer
渲染到畫面上,並藉由requestAnimationFrame
不斷更新畫面前兩個很好理解,但後兩個是什麼?
Renderer
是什麼?比如說你的螢幕有1920 x 1280,那就有兩百多萬畫素。鏡頭camera
捕捉到了場景Scene
,要如何呈現在你兩百多萬畫素的螢幕?就需要透過 Renderer
了。
requestAnimationFrame
是什麼?理想上鏡頭一秒60幀渲染在畫面上。這個如「狂按快門」的邏輯就要靠requestAnimationFrame
來處理。
換句話說,WebGLRenderer
是鏡頭捕捉到的場景,呈現在上的重要推手。而requestAnimationFrame
使得它每一幀捕捉一次。在理想狀態下每秒捕捉60次(60FPS)。
在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等四個參數。
Camera主要跟兩個:PerspectiveCamera 跟 OrthographicCamera,前者有透視,透者則無。本次我們使用前者,需給定四個參數,以下介紹:
我這邊用攝影鏡頭補充說明。它跟FOV融會貫通:
我們拿最蝦趴的三鏡頭iPhone為例,他三顆分別是:長焦(52mm)、廣角(26mm)、高級廣角(13mm)。
而這個變化差距就稱作Field of View,也就是視野的範圍。在這裡,是以角度為單位,一般來說預設是50度(不是mm喔)。這是three.js當中少數以角度而非弧度做為單位的參數。
則代表畫面的寬高比例。基本上只要設定為Canvas的寬高比例即可。
代表鏡頭最短與最遠可以捕捉到的範圍。
(圖片來源)
開始程式碼: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物件。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();
有了以後東西之後,新場景就完成了。
https://codepen.io/umas-sunavan/pen/VwxpxVB?editors=0010
現實中,我們之所以可以看到東西,是因為有物體、眼睛跟光。你會問為什麼沒有光。
在Three.js的世界裡,一個Mesh(最常見的3D物件)由Geometry跟Material組成。
MeshBasicMaterail
不需要光,它只看工程師給定給它什麼顏色參數,它就直接傳遞給Shader渲染。我們給他純藍,那他就是純藍,所以這場景不需要光。這是一個延伸思考。實際上,鏡頭什麼的都是Three.js提供的,WebGL裡面沒有所謂鏡頭這東西,WebGL只要求你螢幕上每一個像素你都要給一個顏色。
可是Three.js將鏡頭所投影的成像,經過計算,算出螢幕中每一個像素的顏色。由此,你不需要考慮那麼底層,你只要考慮Three.js的鏡頭怎麼放置就好了。
我們以透視為例。今天Three.js要在WebGL之上實作透視,那要怎麼處理呢?現在有一條線在三維座標裡,XY的起點是(0,0)終點是(0,10)
可見,在這個計算下,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偉大的一步。我們明天介紹座標系。
參考資料