概述
Canvas -> JavaScript -> Three.js -> WebGL -> OpenGL ->.... -> 顯卡
- OpenGL是底層的驅(qū)動級的圖形接口(是顯卡有直接關(guān)系的) 類似于 DirectX
- WebGL是瀏覽器API,允許JS去調(diào)用部分封裝過的 OpenGL ES2.0標(biāo)準(zhǔn)接口,并為Canvas提供硬件級別的3D圖形加速功能
- Three.js 是使用 JavaScript對 WebGL接口進(jìn)行封裝與簡化而形成的一個易用的 3D庫。
- Canvas區(qū)別于原本的 2dcontext,還可以作為WebGL的載體提供 webglcontext 。


兼容性
WebGL本身可以通過引入WebGL.js進(jìn)行檢測
<script type="module">
import { WEBGL } from '/js/three/WebGL.js';
if(!WEBGL.isWebGLAvailable()){
...
}
if(WEBGL.isWebGL2Available()){
...
}
</script>
除此之外還使用了以下特性或API,可能需要進(jìn)行Polyfills
- Typed Arrays
- Web Audio API
- WebVR API
- Blob
基本步驟
- 創(chuàng)建一個容納三維空間的場景 — Sence
- 將需要繪制的元素加入到場景中,對元素的形狀、材料、陰影等進(jìn)行設(shè)置
- 給定一個觀察場景的位置和角度 — Camera
- 將相機(jī)和場景通過渲染器呈現(xiàn)到頁面上 — Renderer

場景 Scene
場景是各種繪制內(nèi)容的容器
- 通過
.add()可以向場景中添加內(nèi)容,默認(rèn)添加在(0,0,0)位置
var scene = new THREE.Scene();
- 通過
.remove()可以刪除場景中的內(nèi)容,注意刪除mesh時還要對其材料和紋理調(diào)用.dispose()
targetGold.geometry.dispose();
targetGold.material.dispose();
scene.remove(targetGold);
場景具有以下屬性
| key | 作用 | 默認(rèn)值 |
|---|---|---|
| fog:Fog | 定義了影響場景中的每個物體的霧的類型 | null |
| overrideMaterial:Material | 強(qiáng)制場景中的每個物體使用這里的材質(zhì)來渲染 | null |
| autoUpdate:boolean | 自動檢查每一幀是否需要更新場景及物體矩陣 | true |
| background:Color/Texture | 場景背景,且背景總是首先被渲染 | null |
霧化
場景中的物體離攝像機(jī)越遠(yuǎn)就會變得越模糊。
Fog( color : Integer, near : Float = 1, far : Float = 1000 )
分別為顏色、霧化開始時距離、全霧化處距離
scene.fog = new THREE.Fog(0xf7d9aa, 100, 950);
也可以隨著距離呈指數(shù)增長的霧化效果,只需要設(shè)置霧的顏色和濃度即可。如:
scene.fog = new THREE.FogExp2(0xffffff,0.02);
坐標(biāo)系
Three.js中使用的坐標(biāo)系為右手坐標(biāo)系。

scene.add(new THREE.AxisHelper(600));
相機(jī)
- 當(dāng)窗口大小變化時保持物體尺寸不變
視野高度計算公式:
visible_height = 2 * Math.tan( ( Math.PI / 180 ) * camera.fov / 2 ) * distance_from_camera; - 窗口大小變化時保持視野居中
此時如果窗口高不變寬變短,則物體大小不變,依然居中
此時如果窗口寬不變高變短,則物體變小
window.addEventListener('resize', handleWindowResize, false);
function handleWindowResize() {
// update height and width of the renderer and the camera
HEIGHT = window.innerHeight;
WIDTH = window.innerWidth;
renderer.setSize(WIDTH, HEIGHT);
camera.aspect = WIDTH / HEIGHT;
camera.updateProjectionMatrix();
}
正投影相機(jī) OrthographicCamera( left, right, top, bottom, near, far )
只是簡單的將三維空間法向投影到二維屏幕(相機(jī)),遠(yuǎn)近高低比例都相同。

通常我們將瀏覽器窗口的寬度和高度作為視景體的高度和寬度,相機(jī)正好在窗口的中心點上:
new THREE.OrthographicCamera( window.innerWidth / - 2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / - 2, 1, 1000 );
透視投影相機(jī) PerspectiveCamera( fov, aspect, near, far )
近大遠(yuǎn)小,符合視覺的投影

- fov 視角
視角越小,物體看起來越大 - aspect 寬高比
通常等同于canvas寬高比,否則會失真
var camera = new THREE.PerspectiveCamera(45,window.innerWidth / window.innerHeight, 0.1, 1000);
position、lookAt
- position屬性指定了相機(jī)所處的位置(即設(shè)置了
camera.position.x、camera.position.y、camera.position.z)。 - lookAt函數(shù)指定相機(jī)觀察的方向(即設(shè)置了相機(jī)角度),通常選擇場景中心。
- up屬性指定了相機(jī)以哪個方向為上方(即設(shè)置了
camera.up.x、camera.up.y、camera.up.z)。
camera.position.set(30, 40, 50);
camera.up.set(0, 0, 1);
camera.lookAt(scene.position);//new THREE.Vector3(0, 0, 0)
如僅改變.position而不重新調(diào)用.lookAt(),則視角不變,等于相機(jī)在場景中平移。
updateProjectionMatrix
改變相機(jī)實例構(gòu)造函數(shù)入?yún)?yīng)屬性時,需要調(diào)用camera.updateProjectionMatrix()才會生效。
camera.aspect = WIDTH / HEIGHT;
camera.updateProjectionMatrix();
renderer.render(scene, camera);
相機(jī)縮放
通常使用以下屬性
- .getFocalLength () : Float
返回當(dāng)前.fov(視野角度)相對于.filmGauge(膠片尺寸)的焦距。 - .setFocalLength ( focalLength : Float ) : null
通過相對于當(dāng)前.filmGauge的焦距,設(shè)置FOV。
document.body.addEventListener('mousewheel', (e) => {
e.preventDefault();
var isDown = (e.wheelDelta < 0);
var m = camera.getFocalLength();
if (isDown) {
if (m > _minFocalLength) m -= m * 0.05;
} else {
if (m < _maxFocalLength) m += m * 0.05;
}
camera.setFocalLength(m);
}, { passive: false })
相機(jī)旋轉(zhuǎn)
場景Scene、相機(jī)Camera、物體模型Object Model,均可以旋轉(zhuǎn)
- 歐拉角旋轉(zhuǎn)
最常用的旋轉(zhuǎn)方式,直接指定圍繞其自身的一條軸旋轉(zhuǎn)(因此當(dāng)相機(jī)自身lookAt不是正交坐標(biāo)軸時,如果進(jìn)行旋轉(zhuǎn),看到的內(nèi)容也是傾斜的)
//旋轉(zhuǎn)90°
camera.rotateY(Math.PI / 2);
camera.rotation.y = Math.PI / 2;
矩陣(Matrix)
mutiply():矩陣的乘法。
transpose():矩陣轉(zhuǎn)置。
getInverse(m):求逆矩陣。
makeRotationFromEuler(euler) :通過一個歐拉類型的值來設(shè)置矩陣的值。
makeRotationFromQuaternion(q):通過一個四元數(shù)類型的值來設(shè)置矩陣。
makeRotationonAxis(axis,theta):按一個軸旋轉(zhuǎn)θ°,然后設(shè)置矩陣的值。四元數(shù)(Quaternion)
獲取相機(jī)當(dāng)前旋轉(zhuǎn)向量的三個方法
var vector1 = new THREE.Vector3(0, 0, - 1);
vector1.applyQuaternion(camera.quaternion);
console.log(vector1);
var vector2 = new THREE.Vector3(0, 0, -1);
vector2.applyEuler(camera.rotation, camera.eulerOrder);
console.log(vector2);
var vector3 = new THREE.Vector3(0, 0, 0);
camera.getWorldDirection( vector3 );
console.log(vector3);
渲染器
渲染器需要與一個canvas元素綁定,可以在實例化時就指定一個現(xiàn)有的canvas元素。此時可以直接使用WebGL2。
var canvas = document.getElementById('canvas');
var context = canvas.getContext('webgl2', { alpha: false });
var renderer = new THREE.WebGLRenderer({ canvas: canvas, context: context });
如未指定則renderer.domElement將是一個虛擬dom元素
var renderer = new THREE.WebGLRenderer();
renderer.render(scene, camera);
document.getElementById("container").appendChild(renderer.domElement);
實例化參數(shù)
| key | 作用 | 默認(rèn)值 |
|---|---|---|
| canvas | 綁定渲染器的canvas | null |
| context | 將渲染器附加到已有的渲染環(huán)境中 | null |
| antialias | 抗鋸齒 | false |
| alpha | canvas是否支持透明度 (不透明時canvas內(nèi)無法有透明物體,且canvas下的html也總是被覆蓋) | false |
常用屬性、方法
| key | 作用 | 默認(rèn)值 |
|---|---|---|
| autoClear | 每次render是否自動清除上一次內(nèi)容,若為false則每次渲染會疊加 | true |
| domElement | 一個canvas,渲染器在其上繪制輸出。渲染器的構(gòu)造函數(shù)會自動創(chuàng)建(如果沒有傳入canvas參數(shù)) | null |
| shadowMap | 關(guān)于陰影的一些屬性配置 | - |
| .clear( color : Boolean = true, depth : Boolean = true, stencil : Boolean = true) | 清除緩存 | - |
| .dispose() | 清理渲染器 | - |
| .render() | 進(jìn)行渲染 | - |
| .setClearColor(color:Color, alpha:Float) | 設(shè)置默認(rèn)顏色和透明度 | - |
| .setSize( width : Integer, height : Integer, updateStyle : Boolean ) | 調(diào)整大小,通常使用入?yún)?code>window.innerWidth,window.innerHeight
|
- |
| .setPixelRatio(value : number) | 設(shè)置設(shè)備像素比。通常用于避免HiDPI設(shè)備上繪圖模糊,通常入?yún)⒅苯邮褂?code>window.devicePixelRatio | - |