Three.js - 走進3D的奇妙世界

摘要:本文將經過Three.js的介紹及示例帶咱們走進3D的奇妙世界。html

文章來源:宜信技術學院 & 宜信支付結算團隊技術分享第6期-支付結算部支付研發團隊前端研發高級工程師-劉琳《three.js - 走進3D的奇妙世界》

分享者:宜信支付結算部支付研發團隊前端研發高級工程師-劉琳前端

原文首發於支付結算團隊公號-「野指針」python

隨着人們對用戶體驗愈來愈重視,Web開發已經不知足於2D效果的實現,而把目標放到了更加炫酷的3D效果上。Three.js是用於實現web端3D效果的JS庫,它的出現讓3D應用開發更簡單,本文將經過Three.js的介紹及示例帶咱們走進3D的奇妙世界。git

1、Three.js相關概念

1.1 Three.JS

Three.JS是基於WebGL的Javascript開源框架,簡言之,就是可以實現3D效果的JS庫。github

1.2 WebGL

WebGL是一種Javascript的3D圖形接口,把JavaScript和OpenGL ES 2.0結合在一塊兒。web

1.3 OpenGL

OpenGL是開放式圖形標準,跨編程語言、跨平臺,Javascript、Java 、C、C++ 、 python 等都能支持OpenG ,OpenGL的Javascript實現就是WebGL,另外不少CAD製圖軟件都採用這種標準。OpenGL ES 2.0是OpenGL的子集,針對手機、遊戲主機等嵌入式設備而設計。編程

1.4 Canvas

Canvas是HTML5的畫布元素,在使用Canvas時,須要用到Canvas的上下文,能夠用2D上下文繪製二維的圖像,也可使用3D上下文繪製三維的圖像,其中3D上下文就是指WebGL。json

2、Three.js應用場景

利用Three.JS能夠製做出不少酷炫的3D動畫,而且Three.js還能夠經過鼠標、鍵盤、拖拽等事件造成交互,在頁面上增長一些3D動畫和3D交互能夠產生更好的用戶體驗。canvas

經過Three.JS能夠實現全景視圖,這些全景視圖應用在房產、家裝行業可以帶來更直觀的視覺體驗。在電商行業利用Three.JS能夠實現產品的3D效果,這樣用戶就能夠360度全方位地觀察商品了,給用戶帶來更好的購物體驗。另外,使用Three.JS還能夠製做相似微信跳一跳那樣的小遊戲。隨着技術的發展、基礎網絡的建設,web3D技術還能獲得更普遍的應用。微信

3、主要組件

在Three.js中,有了場景(scene)、相機(camera)和渲染器(renderer) 這3個組建才能將物體渲染到網頁中去。

1)場景

場景是一個容器,能夠看作攝影的房間,在房間中能夠佈置背景、擺放拍攝的物品、添加燈光設備等。

2)相機

相機是用來拍攝的工具,經過控制相機的位置和方向能夠獲取不一樣角度的圖像。

3)渲染器

渲染器利用場景和相機進行渲染,渲染過程比如攝影師拍攝圖像,若是隻渲染一次就是靜態的圖像,若是連續渲染就能獲得動態的畫面。在JS中可使用requestAnimationFrame實現高效的連續渲染。

3.1 經常使用相機

1)透視相機

透視相機模擬的效果與人眼看到的景象最接近,在3D場景中也使用得最廣泛,這種相機最大的特色就是近大遠小,一樣大小的物體離相機近的在畫面上顯得大,離相機遠的物體在畫面上顯得小。透視相機的視錐體如上圖左側所示,從近端面到遠端面構成的區域內的物體才能顯示在圖像上。

透視相機構造器

PerspectiveCamera( fov : Number, aspect : Number, near : Number, far : Number )

  • fov — 攝像機視錐體垂直視野角度
  • aspect — 攝像機視錐體長寬比
  • near — 攝像機視錐體近端面
  • far — 攝像機視錐體遠端面

2)正交相機

使用正交相機時不管物體距離相機遠或者近,在最終渲染的圖片中物體的大小都保持不變。正交相機的視錐體如上圖右側所示,和透視相機同樣,從近端面到遠端面構成的區域內的物體才能顯示在圖像上。

正交相機構造器

OrthographicCamera( left : Number, right : Number, top : Number, bottom : Number, near : Number, far : Number )

  • left — 攝像機視錐體左側面
  • right — 攝像機視錐體右側面
  • top — 攝像機視錐體上側面
  • bottom — 攝像機視錐體下側面
  • near — 攝像機視錐體近端面
  • far — 攝像機視錐體遠端面

3.2 座標系

在場景中,能夠放物品、相機、燈光,這些東西放置到什麼位置就須要使用座標系。Three.JS使用右手座標系,這源於OpenGL默認狀況下,也是右手座標系。從初中、高中到大學的課堂上,教材中所涉及的幾何基本都是右手座標系。

上圖右側就是右手座標系,五指併攏手指放平,指尖指向x軸的正方向,而後把四個手指垂直彎曲大拇指分開,併攏的四指指向y軸的正方向,大拇指指向的就是Z軸的正方向。

在Three.JS中提供了座標軸工具(THREE.AxesHelper),在場景中添加座標軸後,畫面會出現3條垂直相交的直線,紅色表示x軸,綠色表示y軸,藍色表示z軸(以下圖所示)。

3.3 示例代碼

/* 場景 */
var scene = new THREE.Scene();
scene.add(new THREE.AxesHelper(10)); // 添加座標軸輔助線

/* 幾何體 */
// 這是自定義的建立幾何體方法,若是建立幾何體後續會介紹
var kleinGeom = createKleinGeom(); 
scene.add(kleinGeom); // 場景中添加幾何體

/* 相機 */
var camera = new THREE.PerspectiveCamera(45, width/height, 1, 100);
camera.position.set(5,10,25); // 設置相機的位置
camera.lookAt(new THREE.Vector3(0, 0, 0)); // 相機看向原點

/* 渲染器 */
var renderer = new THREE.WebGLRenderer({antialias:true});
renderer.setSize(width, height);
// 將canvas元素添加到body
document.body.appendChild(renderer.domElement);
// 進行渲染
renderer.render(scene, camera);

4、幾何體

計算機內的3D世界是由點組成,兩個點可以組成一條直線,三個不在一條直線上的點就可以組成一個三角形面,無數三角形面就可以組成各類形狀的幾何體。

以建立一個簡單的立方體爲例,建立簡單的立方體須要添加8個頂點和12個三角形的面,建立頂點時須要指定頂點在座標系中的位置,添加面的時候須要指定構成面的三個頂點的序號,第一個添加的頂點序號爲0,第二個添加的頂點序號爲1…

建立立方體的代碼以下:

var geometry = new THREE.Geometry();

// 添加8個頂點
geometry.vertices.push(new THREE.Vector3(1, 1, 1));
geometry.vertices.push(new THREE.Vector3(1, 1, -1));
geometry.vertices.push(new THREE.Vector3(1, -1, 1));
geometry.vertices.push(new THREE.Vector3(1, -1, -1));
geometry.vertices.push(new THREE.Vector3(-1, 1, -1));
geometry.vertices.push(new THREE.Vector3(-1, 1, 1));
geometry.vertices.push(new THREE.Vector3(-1, -1, -1));
geometry.vertices.push(new THREE.Vector3(-1, -1, 1));

// 添加12個三角形的面
geometry.faces.push(new THREE.Face3(0, 2, 1));
geometry.faces.push(new THREE.Face3(2, 3, 1));
geometry.faces.push(new THREE.Face3(4, 6, 5));
geometry.faces.push(new THREE.Face3(6, 7, 5));
geometry.faces.push(new THREE.Face3(4, 5, 1));
geometry.faces.push(new THREE.Face3(5, 0, 1));
geometry.faces.push(new THREE.Face3(7, 6, 2));
geometry.faces.push(new THREE.Face3(6, 3, 2));
geometry.faces.push(new THREE.Face3(5, 7, 0));
geometry.faces.push(new THREE.Face3(7, 2, 0));
geometry.faces.push(new THREE.Face3(1, 3, 4));
geometry.faces.push(new THREE.Face3(3, 6, 4));

4.1 正面和反面

建立幾何體的三角形面時,指定了構成面的三個頂點,如: new THREE.Face3(0, 2, 1),若是把頂點的順序改爲0,1,2會有區別嗎?

經過下圖能夠看到,按照0,2,1添加頂點是順時針方向的,而按0,1,2添加頂點則是逆時針方向的,經過添加頂點的方向就能夠判斷當前看到的面是正面仍是反面,若是頂點是逆時針方向添加,當前看到的面是正面,若是頂點是順時針方向添加,則當前面爲反面。

下圖所看到的面就是反面。若是很差記,可使用右手沿頂點添加的方向握住,大拇指所在的面就是正面,很像咱們上學時學的電磁感應定律。

5、材質

建立幾何體時經過指定幾何體的頂點和三角形的面肯定了幾何體的形狀,另外還須要給幾何體添加皮膚才能實現物體的效果,材質就像物體的皮膚,決定了物體的質感。常見的材質有以下幾種:

  • 基礎材質:以簡單着色方式來繪製幾何體的材質,不受光照影響。
  • 深度材質:按深度繪製幾何體的材質。深度基於相機遠近端面,離近端面越近就越白,離遠端面越近就越黑。
  • 法向量材質:把法向量映射到RGB顏色的材質。
  • Lambert材質:是一種須要光源的材質,非光澤表面的材質,沒有鏡面高光,適用於石膏等表面粗糙的物體。
  • Phong材質:也是一種須要光源的材質,具備鏡面高光的光澤表面的材質,適用於金屬、漆面等反光的物體。
  • 材質捕獲:使用存儲了光照和反射等信息的貼圖,而後利用法線方向進行採樣。優勢是能夠用很低的消耗來實現不少特殊風格的效果;缺點是僅對於固定相機視角的狀況較好。

下圖是使用不一樣貼圖實現的效果:

6、光源

前面提到的光敏材質(Lambert材質和Phong材質)須要使用光源來渲染出3D效果,在使用時須要將建立的光源添加到場景中,不然沒法產生光照效果。下面介紹一下經常使用的光源及特色。

6.1 點光源

點光源相似蠟燭放出的光,不一樣的是蠟燭有底座,點光源沒有底座,能夠把點光源想象成懸浮在空中的火苗,點光源放出的光線來自同一點,且方向輻射向四面八方,點光源在傳播過程當中有衰弱,以下圖所示,點光源在接近地面的位置,物體底部離點光源近,物體頂部離光源遠,照到物體頂部的光就弱些,因此頂部會比底部暗些。

6.2 平行光

平行光模擬的是太陽光,光源發出的全部光線都是相互平行的,平行光沒有衰減,被平行光照亮的整個區域接受到的光強是同樣的。

6.3 聚光燈

相似舞臺上的聚光燈效果,光源的光線從一個錐體中射出,在被照射的物體上產生聚光的效果。聚光燈在傳播過程也是有衰弱的。

6.4 環境光

環境光是通過屢次反射而來的光,環境光源放出的光線被認爲來自任何方向,物體不管法向量如何,都將表現爲一樣的明暗程度。

環境光一般不會單獨使用,經過使用多種光源可以實現更真實的光效,下圖是將環境光與點光源混合後實現的效果,物體的背光面不像點光源那樣是黑色的,而是呈現出深褐色,更天然。

7、紋理

在生活中純色的物體仍是比較少的,更多的是有凹凸不平的紋路或圖案的物體,要用Three.JS實現這些物體的效果,就須要使用到紋理貼圖。3D世界的紋理是由圖片組成的,將紋理添加在材質上以必定的規則映射到幾何體上,幾何體就有了帶紋理的皮膚。

7.1 普通紋理貼圖

在這個示例中使用上圖左側的地球紋理,在球形幾何體上進行貼圖就能製做出一個地球。

代碼以下:

/* 建立地球 */
function createGeom() {
    // 球體
    var geom = new THREE.SphereGeometry(1, 64, 64);
    // 紋理
    var loader = new THREE.TextureLoader();
    var texture = loader.load('./earth.jpg');
    // 材質
    var material = new THREE.MeshLambertMaterial({
        map: texture
    });
    var earth = new THREE.Mesh(geom, material);
    return earth;
}

7.2 反面貼圖實現全景視圖

這個例子是經過在球形幾何體的反面進行紋理貼圖實現的全景視圖,實現原理是這樣的:建立一個球體構成一個球形的空間,把相機放在球體的中心,相機就像在一個球形的房間中,在球體的裏面(也就是反面)貼上圖片,經過改變相機拍攝的方向,就能看到全景視圖了。

材質默認是在幾何體的正面進行貼圖的,若是想要在反面貼圖,須要在建立材質的時候設置side參數的值爲THREE.BackSide,代碼以下:

/* 建立反面貼圖的球形 */
// 球體
var geom = new THREE.SphereGeometry(500, 64, 64);
// 紋理
var loader = new THREE.TextureLoader();
var texture = loader.load('./panorama.jpg');
// 材質
var material = new THREE.MeshBasicMaterial({
    map: texture,
    side: THREE.BackSide
});
var panorama = new THREE.Mesh(geom, material);

7.3 凹凸紋理貼圖

凹凸紋理利用黑色和白色值映射到與光照相關的感知深度,不會影響對象的幾何形狀,隻影響光照,用於光敏材質(Lambert材質和Phong材質)。

若是隻用上圖左上角的磚牆圖片進行貼圖的話,就像一張牆紙貼在上面,視覺效果不好,爲了加強立體感,可使用上圖左下角的凹凸紋理,給物體增長凹凸不平的效果。

凹凸紋理貼圖使用方式的代碼以下:

// 紋理加載器
var loader = new THREE.TextureLoader();
// 紋理
var texture = loader.load( './stone.jpg');
// 凹凸紋理
var bumpTexture = loader.load( './stone-bump.jpg');
// 材質
var material =  new THREE.MeshPhongMaterial( {
    map: texture,
    bumpMap: bumpTexture
} );

7.4 法線紋理貼圖

法線紋理也是經過影響光照實現凹凸不平視覺效果的,並不會影響物體的幾何形狀,用於光敏材質(Lambert材質和Phong材質)。上圖左下角的法線紋理圖片的RGB值會影響每一個像素片斷的曲面法線,從而改變物體的光照效果。

使用方式的代碼以下:

// 紋理
var texture = loader.load( './metal.jpg');
// 法線紋理
var normalTexture = loader.load( './metal-normal.jpg');
var material =  new THREE.MeshPhongMaterial( {
    map: texture,
    normalMap: normalTexture
} );

7.5 環境貼圖

環境貼圖是將當前環境做爲紋理進行貼圖,可以模擬鏡面的反光效果。在進行環境貼圖時須要使用立方相機在當前場景中進行拍攝,從而得到當前環境的紋理。立方相機在拍攝環境紋理時,爲避免反光效果的小球出如今環境紋理的畫面上,須要將小球設爲不可見。

環境貼圖的主要代碼以下:

/* 立方相機 */
var cubeCamera = new THREE.CubeCamera( 1, 10000, 128 );
/* 材質 */
var material = new THREE.MeshBasicMaterial( {
    envMap: cubeCamera.renderTarget.texture
});
/* 鏡面反光的球體 */
var geom = new THREE.SphereBufferGeometry( 10, 32, 16 );
var ball = new THREE.Mesh( geom, material );
// 將立方相機添加到球體
ball.add( cubeCamera );
scene.add( ball );

// 立方相機生成環境紋理前將反光小球隱藏
ball.visible = false;
// 更新立方相機,生成環境紋理
cubeCamera.update( renderer, scene );
balls.visible = true;

// 渲染
renderer.render(scene, camera);

8、加載外部3D模型

Three.JS已經內置了不少經常使用的幾何體,如:球體、立方體、圓柱體等等,可是在實際使用中每每須要用到一些特殊形狀的幾何體,這時可使用3D建模軟件製做出3D模型,導出obj、json、gltf等格式的文件,而後再加載到Three.JS渲染出效果。

上圖的椅子是在3D製圖軟件繪製出來的,chair.mtl是導出的材質文件,chair.obj是導出的幾何體文件,使用材質加載器加載材質文件,加載完成後獲得材質對象,給幾何體加載器設置材質,加載後獲得幾何體對象,而後再建立場景、光源、攝像機、渲染器等進行渲染,這樣就等獲得如圖的效果。主要的代碼以下:

// .mtl材質文件加載器
var mtlLoader = new THREE.MTLLoader();
// .obj幾何體文件加載器
var objLoader = new THREE.OBJLoader();

mtlLoader.load('./chair.mtl', function (materials) {
    objLoader.setMaterials(materials)
        .load('./chair.obj', function (obj) {
            scene.add(obj);
            …
        });
});

9、說明

以上內容對Three.JS的基本使用進行了介紹,文中涉及到的示例源碼已上傳到github,感興趣的同窗能夠下載查看,下載地址:https://github.com/liulinsp/t...。使用時若是有不清楚的地方能夠查看Three.JS的官方文檔:https://threejs.org/docs/inde...

相關文章
相關標籤/搜索