上一節最後,我書寫了一個簡單案例代碼供大家一起查看Three.js
的HELLO WORLD
。案例沒有什麼複雜的東西,也就是一個旋轉的立方體。接下來,我帶着大家一起來講解一下是如何實現這個最簡單的案例,以及Three.js
的一些最基本的構件。
構建場景
按照代碼的運行順序,首先,我們需要先將Three.js
庫的js文件引入:
<script src="https://cdn.bootcss.com/three.js/92/three.js"></script>
然後在body上面添加了一個初始化的事件init
:
<body onload="init()">
頁面加載完畢後,回調會觸發我們設置的init
事件。init
函數裏面一共調用了五個方法,分別是:初始化渲染器,初始化場景,初始化相機,添加模型,添加動畫:
//初始化函數,頁面加載完成是調用
function init() {
initRenderer();
initScene();
initCamera();
initMesh();
animate();
}
**使用Three.js
顯示創建的內容,我們必須需要的三大件是:渲染器,相機和場景
。**相機獲取到場景內顯示的內容,然後再通過渲染器渲染到畫布上面。接下來,我們將分步驟分別講解他們的實現:
創建渲染器
//初始化渲染器
function initRenderer() {
renderer = new THREE.WebGLRenderer(); //實例化渲染器
renderer.setSize(window.innerWidth, window.innerHeight); //設置寬和高
document.body.appendChild(renderer.domElement); //添加到dom
}
查看initRenderer
函數內的代碼,第一行我們實例化了一個THREE.WebGLRenderer
,這是一個基於WebGL
渲染的渲染器,當然,Three.js
向下兼容,還有CanvasRenderer
,CSS2DRenderer
,CSS3DRenderer
和SVGRenderer
,這四個渲染器分別基於canvas2D
,CSS2D
,CSS3D
和SVG
渲染的渲染器。由於,作爲3D
渲染,WebGL
渲染的效果最好,並且支持的功能更多,我們以後的課程也只會用到THREE.WebGLRenderer
,需要使用其他渲染器時,會重點提示。
第二行,調用了一個設置函數setSize
方法,這個是設置我們需要顯示的窗口大小。案例我們是基於瀏覽器全屏顯示,所以設置了瀏覽器窗口的寬和高。
第三行,renderer.domElement
是在實例化渲染器時生成的一個canvas
畫布,渲染器渲染界面生成的內容,都將在這個畫布上顯示。所以,我們將這個畫布添加到了dom當中,來顯示渲染的內容。
創建相機
接着我們看一下相機的初始化:
//初始化相機
function initCamera() {
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 200); //實例化相機
camera.position.set(0, 0, 15);
}
Three.js
裏面有幾個不同的相機,我們這裏使用到的是THREE.PerspectiveCamera
,這個相機的效果是模擬人眼看到的效果,就是具有透視的效果,近大遠小。
第一行,我們實例化了一個透視相機,需要四個值分別是視野,寬高比,近裁面和遠裁面。我們分別介紹一下這四個值:
- 視野:當前相機視野的寬度,值越大,渲染出來的內容也會更多。
- 寬高比:默認是按照畫布的顯示的寬高比例來設置,如果比例設置的不對,會發現渲染出來的畫面有拉伸或者壓縮的感覺。
- 近裁面和遠裁面:這個是設置相機可以看到的場景內容的範圍,如果場景內的內容位置不在這兩個值內的話,將不會被顯示到渲染的畫面中。
第二行,我們設置了相機的位置,在講如何設置位置之前,我感覺應該先講一下WebGL
的座標系統:
WebGL
座標系統作爲3D
座標,在原來的2D
座標xy軸上面又多了一個z軸,大家注意z軸的方向,是座標軸朝向我們的方向是正軸,我們眼看去的方向是是z軸的負方向。
camera.position.set
函數是設置當前相機的位置,函數傳的三個值分別是x軸座標,y軸座標和z軸座標。我們只是將相機的放到了z正軸座標軸距離座標原點的15的位置。相機默認的朝向是朝向0點座標的,我們也可以設置相機的朝向,這將在後面相機介紹是,專門介紹相機的相關。
創建場景
初始化場景方法裏面很簡單,只有一行代碼,就是實例化了一個場景對象:
//初始化場景
function initScene() {
scene = new THREE.Scene(); //實例化場景
}
場景只是作爲一個容器,我們將需要顯示的內容都放到場景對象當中。如果我們需要將一個模型放入到場景當中,則可以使用scene.add
方法,如:
scene.add(mesh); //添加一個網格(模型)到場景
創建第一個模型
渲染器,場景和相機都全了,是不是就能顯示東西了?不能!因爲場景內沒有內容,即使渲染出來也是一片漆黑,所以我們需要往場景裏面添加內容。接下來,我們將查看initMesh
方法,看看如何創建一個最簡單的模型:
//創建模型
function initMesh() {
geometry = new THREE.BoxGeometry( 2, 2, 2 ); //創建幾何體
material = new THREE.MeshNormalMaterial(); //創建材質
mesh = new THREE.Mesh( geometry, material ); //創建網格
scene.add( mesh ); //將網格添加到場景
}
創建一個網格(模型)需要兩種對象:幾何體和材質。
- 幾何體代表模型的形狀,它是由固定的點的位置組成,點繪製出面,面組成了模型。
- 材質是我們看到當前模型顯示出來的效果,如顯示的顏色,質感等。
在創建模型函數的第一行代碼裏,我們實例化了一個THREE.BoxGeometry
立方體的幾何體對象,實例化的三個傳值分別代表着立方體的長度,寬度和高度。我們全部設置的相同的值,將渲染出一個標準的正立方體。
在第二行裏面,我們實例化了一個THREE.MeshNormalMaterial
材質,這種材質的特點是,它會根據面的朝向不同,顯示不同的顏色。
緊接着,通過THREE.Mesh
方法實例化創建了一個網格對象,THREE.Mesh
實例化需要傳兩個值,分別是幾何體對象和材質對象,纔可以實例化成功。
最後,我們通過場景的add
方法,將網格添加到了場景當中,我們沒有給立方體設置位置,立方體則會默認顯示在座標的原點上面。這時,相機就可以拍攝到我們創建的立方體的畫面了。
讓場景動起來
動畫,就是多幅圖片一直切換就顯示出來動畫的效果,爲了能顯示動畫的效果,我們首先了解一個函數requestAnimationFrame
,這個函數是專門爲了動畫而出現的一個函數。它與setInterval相比的優勢在於,不需要設置多長時間重新渲染,而是在當前線程內js空閒時自動渲染,並且最大幀數控制在一秒60幀。所以,我們書寫了一個可以循環調用的函數:
function animate() {
requestAnimationFrame(animate); //循環調用函數
...
}
在循環調用的函數中,每一幀我們都讓頁面重新渲染相機拍攝下來的內容:
renderer.render( scene, camera ); //渲染界面
渲染的render
方法需要兩個值,第一個值是場景對象,第二個值是相機對象。這意味着,你可以有多個相機和多個場景,可以通過渲染不同的場景和相機讓畫布上顯示不同的畫面。
但是,如果現在一直渲染的話,我們發現就一個立方體在那,也沒有動,我們需要做的是讓立方體動起來:
mesh.rotation.x += 0.01; //每幀網格模型的沿x軸旋轉0.01弧度
mesh.rotation.y += 0.02; //每幀網格模型的沿y軸旋轉0.02弧度
每一個實例化的網格對象都有一個rotation
的值,通過設置這個值可以讓立方體旋轉起來。在每一幀裏,我們讓立方體沿x軸方向旋轉0.01弧度,沿y軸旋轉0.02弧度(1π弧度等於180度角度)。
Three.js的性能檢測插件
在Three.js
裏面,遇到的最多的問題就是性能問題,所以我們需要時刻檢測當前的Three.js
的性能。現在Three.js
常使用的一款插件叫stats
。接下來我們看看如何將stats
插件在Three.js
的項目中使用:
- 首先將插件代碼在頁面中引入:
<script src="http://www.wjceo.com/lib/js/libs/stats.min.js"></script>
我這裏也直接給出了一個cdn的地址,可以直接引入即可。
- 然後,我們需要實例化一個
stats
對象,然後把對象內生成的dom
添加到頁面當中。
stats = new Stats();
document.body.appendChild(stats.dom);
- 最後一步,我們需要在
requestAnimationFrame
的回調裏面進行更新每次渲染的時間:
function animate() {
requestAnimationFrame(animate); //循環調用函數
stats.update(); //更新性能插件
renderer.render( scene, camera ); //渲染界面
}
在場景當中,我們能夠看到左上角會有一個性能檢測的小框:
前面的數值代表當前每秒的渲染幀率,後面的括號內的值是當前的場景渲染的幀率範圍。
說到這裏,我們的Three.js
的HELLO WORLD
也就實現了,我將在下一章裏面,給大家講解場景的數據結構和開發的輔助插件。
使用了性能檢測插件以後,我們的案例代碼結構應該是這樣的:
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8>
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Stats插件案例</title>
<style>
body {
margin: 0;
}
canvas {
width: 100%;
height: 100%;
display: block;
}
</style>
</head>
<body onload="init()">
<script src="https://cdn.bootcss.com/three.js/92/three.js"></script>
<script src="http://www.wjceo.com/lib/js/libs/stats.min.js"></script>
<script>
//聲明一些全局變量
var renderer, camera, scene, geometry, material, mesh, stats;
//初始化渲染器
function initRenderer() {
renderer = new THREE.WebGLRenderer(); //實例化渲染器
renderer.setSize(window.innerWidth, window.innerHeight); //設置寬和高
document.body.appendChild(renderer.domElement); //添加到dom
}
//初始化場景
function initScene() {
scene = new THREE.Scene(); //實例化場景
}
//初始化相機
function initCamera() {
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 200); //實例化相機
camera.position.set(0, 0, 15);
}
//創建模型
function initMesh() {
geometry = new THREE.BoxGeometry( 2, 2, 2 ); //創建幾何體
material = new THREE.MeshNormalMaterial(); //創建材質
mesh = new THREE.Mesh( geometry, material ); //創建網格
scene.add( mesh ); //將網格添加到場景
}
//運行動畫
function animate() {
requestAnimationFrame(animate); //循環調用函數
mesh.rotation.x += 0.01; //每幀網格模型的沿x軸旋轉0.01弧度
mesh.rotation.y += 0.02; //每幀網格模型的沿y軸旋轉0.02弧度
stats.update(); //更新性能檢測框
renderer.render( scene, camera ); //渲染界面
}
//性能檢測框
function initStats() {
stats = new Stats();
document.body.appendChild(stats.dom);
}
//初始化函數,頁面加載完成是調用
function init() {
initRenderer();
initScene();
initCamera();
initMesh();
initStats();
animate();
}
</script>
</body>
</html>