Three.js 是一款運行在瀏覽器中的 3D 引擎,你可以用它在 web 中創(chuàng)建各種三維場景廷区,包括了攝影機(jī)壳澳、光影淹魄、材質(zhì)等各種對象。使用它可以讓我們更加直觀的了解 webgl 的世界靖苇。
3D場景前置知識
1.場景(Scene):是物體嘶朱、光源等元素的容器溯捆,可以配合 chrome 插件使用配并,拋出 window.scene即可實時調(diào)整 obj 的信息和材質(zhì)信息括荡。
2.相機(jī)(Camera):場景中的相機(jī),代替人眼去觀察溉旋,場景中只能添加一個畸冲,一般常用的是透視相機(jī)(PerspectiveCamera)
3.物體對象(Mesh):包括二維物體(點、線观腊、面)邑闲、三維物體,模型等等
4.光源(Light):場景中的光照梧油,如果不添加光照場景將會是一片漆黑苫耸,包括全局光、平行光婶溯、點光源等
5.渲染器(Renderer):場景的渲染方式鲸阔,如webGL\canvas2D\Css3D。
6.控制器(Control): 可通過鍵盤迄委、鼠標(biāo)控制相機(jī)的移動
下面我們依次詳細(xì)學(xué)習(xí)以上的細(xì)分知識點褐筛。
相機(jī)
Three.js中我們常用的有兩種類型的相機(jī):
正交(orthographic)相機(jī)、透視(perspective)相機(jī)叙身。
一般情況下為了模擬人眼我們都是使用透視相機(jī)渔扎;
正交鏡頭的特點是,物品的渲染尺寸與它距離鏡頭的遠(yuǎn)近無關(guān)信轿。也就是說在場景中移動一個物體晃痴,其大小不會變化。正交鏡頭適合2D游戲财忽。
透視鏡頭則是模擬人眼的視覺特點倘核,距離遠(yuǎn)的物體顯得更小。透視鏡頭通常更適合3D渲染即彪。
THREE.PerspectiveCamera(fov,aspect,near,far)
參數(shù) | 描述 |
---|---|
fov |
視野角度紧唱,從鏡頭可以看到的場景的部分。通常3D游戲的FOV取值在60-90度之間較好的默認(rèn)值為60 |
aspect |
渲染區(qū)域的縱橫比隶校。較好的默認(rèn)值為window.innerWidth/window.innerHeight
|
near |
最近離鏡頭的距離 |
far |
遠(yuǎn)離鏡頭的距離 |
創(chuàng)建攝像機(jī)以后還要對其進(jìn)行移動漏益、然后對準(zhǔn)物體積聚的場景中心位置,分別是設(shè)置其 position和調(diào)用 lookAt 方法深胳,參數(shù)均是一個 xyz向量(new THREE.Vector3(x,y,z))
camera.position:控制相機(jī)在整個3D環(huán)境中的位置(取值為3維坐標(biāo)對象-THREE.Vector3(x,y,z))
camera.lookAt:控制相機(jī)的焦點位置绰疤,決定相機(jī)的朝向(取值為3維坐標(biāo)對象-THREE.Vector3(x,y,z))
燈光
在Three.js中光源是必須的,如果一個場景你不設(shè)置燈光那么世界將會是一片漆黑舞终。Three.js內(nèi)置了多種光源以滿足特定場景的需要轻庆。大家可以根據(jù)自己的項目需要來選擇何種燈光
- 光源分類
光源 | 說明 |
---|---|
AmbientLight |
環(huán)境光癣猾,其顏色均勻的應(yīng)用到場景及其所有對象上。 這種光源為場景添加全局的環(huán)境光余爆,這種光沒有特定的方向煎谍,不會產(chǎn)生陰影。 通常不會把 AmbientLight 作為唯一的光源龙屉,而是和SpotLight 、DirectionalLigh t等光源結(jié)合使用满俗,從而達(dá)到柔化陰影转捕、添加全局色調(diào)的效果。指定顏色時要相對保守唆垃,例如#0c0c0c五芝。設(shè)置太亮的顏色會導(dǎo)致整個畫面過度飽和,什么都看不清辕万。 |
PointLight |
3D 空間中的一個點光源枢步,向所有方向發(fā)出光線 |
SpotLight |
產(chǎn)生圓錐形光柱的聚光燈,臺燈渐尿、天花板射燈通常都屬于這類光源醉途。 這種光源的使用場景最多,特別是在你需要陰影效果的時候砖茸。 |
DirectionalLight |
也就無限光隘擎,光線是平行的。 典型的例子是日光,用于模擬遙遠(yuǎn)的凉夯,類似太陽那樣的光源货葬。 該光源與 SpotLight 的主要區(qū)別是,它不會隨著距離而變暗劲够,所有被照耀的地方獲得相同的光照強(qiáng)度震桶。 |
HemisphereLight |
特殊光源,用于創(chuàng)建戶外自然的光線效果征绎。 此光源模擬物體表面反光效果蹲姐、微弱發(fā)光的天空,模擬穹頂(半球)的微弱發(fā)光效果,讓戶外場景更加逼真炒瘸。 使用 DirectionalLight + AmbientLight 可以在某種程度上來模擬戶外光線淤堵,但是不夠真實,因為無法體現(xiàn)大氣層的散射效果顷扩、地面或物體的反射效果 |
AreaLight |
面光源拐邪,指定一個發(fā)光的區(qū)域 |
LensFlare |
不是光源,用于給光源添加鏡頭光暈效果 |
關(guān)于光源的詳細(xì) API 大家可以參考 threejs 官網(wǎng)隘截,很詳細(xì)扎阶,demo 也很完整 傳送門
Mesh
在計算機(jī)的世界里汹胃,一條弧線是由有限個點構(gòu)成的有限條線段連接得到的。當(dāng)線段數(shù)量越多东臀,長度就越短着饥,當(dāng)達(dá)到你無法察覺這是線段時,一條平滑的弧線就出現(xiàn)了惰赋。 計算機(jī)的三維模型也是類似的宰掉。只不過線段變成了平面,普遍用三角形組成的網(wǎng)格來描述赁濒。我們把這種模型稱之為 Mesh 模型轨奄。 在 threeJs 的世界中,材質(zhì)(Material)+幾何體(Geometry)就是一個 mesh拒炎。設(shè)置其name屬性可以通過scene.getObjectByName(name)獲取該物體對象;Geometry就好像是骨架挪拟,材質(zhì)則類似于皮膚,對于材質(zhì)和幾何體的分類見下表格
- 材質(zhì)分類
材質(zhì) | 說明 |
---|---|
MeshBasicMaterial |
基本的材質(zhì)击你,顯示為簡單的顏色或者顯示為線框玉组。不考慮光線的影響 |
MeshDepthMaterial |
使用簡單的顏色,但是顏色深度和距離相機(jī)的遠(yuǎn)近有關(guān) |
MeshNormalMaterial |
基于面Geometry的法線(normals)數(shù)組來給面著色 |
MeshFacematerial |
容器丁侄,允許為Geometry的每一個面指定一個材質(zhì) |
MeshLambertMaterial |
考慮光線的影響惯雳,啞光材質(zhì) |
MeshPhongMaterial |
考慮光線的影響,光澤材質(zhì) |
ShaderMaterial |
允許使用自己的著色器來控制頂點如何被放置绒障、像素如何被著色 |
LineBasicMaterial |
用于THREE.Line對象吨凑,創(chuàng)建彩色線條 |
LineDashMaterial |
用于THREE.Line對象,創(chuàng)建虛線條 |
RawShaderMaterial |
僅和THREE.BufferedGeometry聯(lián)用户辱,優(yōu)化靜態(tài)Geometry(頂點鸵钝、面不變)的渲染 |
SpriteCanvasMaterial |
在針對單獨的點進(jìn)行渲染時用到 |
SpriteMaterial |
在針對單獨的點進(jìn)行渲染時用到 |
PointCloudMaterial |
在針對單獨的點進(jìn)行渲染時用到 |
幾何圖形
- 2D
圖形 | 說明 |
---|---|
THREE.PlaneGeometry |
外觀上是一個矩形new THREE.PlaneGeometry(width, height, widthSegments, heightSegments)
|
THREE.CircleGeometry |
外觀上是一個圓形或者扇形// 半徑為3的圓new THREE.CircleGeometry(3, 12) ;半徑為3的半圓 new THREE.CircleGeometry(3, 12, 0, Math.PI) ;第三個參數(shù)和第四個分別是起始角度和結(jié)束角度,默認(rèn)0-2*PI |
THREE.RingGeometry |
外觀上是一個圓環(huán)或者扇環(huán)new THREE.RingGeometry(innerRadius, outerRadius, thetaSegments, phiSegments,thetaStart, thetaLength)
|
THREE.ShapeGeometry |
該形狀允許你創(chuàng)建自定義的二維圖形庐镐,其操作方式類似于SVG/Canvas中的畫布 |
- 3D
圖形 | 說明 |
---|---|
THREE.BoxGeometry |
這是一個具有長寬高的盒子BoxGeometry(width, height, depth, widthSegments, heightSegments, depthSegments) |
THREE.SphereGeometry |
這是一個三維球體/不完整球體SphereGeometry(radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength)
|
THREE. CylinderGeometry |
可以繪制圓柱恩商、圓筒、圓錐或者截錐new THREE.CylinderGeometry(radiusTop,radiusBottom,height,radialSegments,heightSegments,openEnded)
|
加載外部模型
一般來講我們的場景中不可能都是一些奇奇怪怪的形狀必逆,或多或少項目中都會用到一些外部的模型資源怠堪,不如動物啊,裝飾物啊什么的名眉,再加上一些動畫粟矿,這樣整個場景更加顯得生動,那么 threejs 中我們可以通過哪些方式來加載外部的模型資源呢损拢?
加載外部模型陌粹,是通過Three.js加載器(Loader)實現(xiàn)的。加載器把文本/二進(jìn)制的模型文件轉(zhuǎn)化為Three.js對象結(jié)構(gòu)福压。 每個加載器理解某種特定的文件格式掏秩。
需要注意的是或舞,由于貼圖的尺寸必須是(2的冪數(shù))X (2的冪數(shù)),如:1024X512蒙幻,所以為了防止貼圖變形映凳,平面的寬度比例需要與貼圖的比例一致。
- 支持的格式
格式 | 說明 |
---|---|
JSON |
Three.js自定義的邮破、基于JSON的格式诈豌。 可以聲明式的定義一個Geometry或者Scene.利用該格式,你可以方便的重用復(fù)雜的Geometry或Scene |
OBJ / MTL |
OBJ是Wavefront開發(fā)的一種簡單3D格式抒和,此格式被廣泛的支持队询,用于定義Geometry,MTL用于配合OBJ构诚,它指定OBJ使用的材質(zhì) |
Collada(dae) |
基于XML的格式,被大量3D應(yīng)用程序铆惑、渲染引擎支持 |
STL |
STereoLithography的簡寫范嘱,在快速原型領(lǐng)域被廣泛使用。 3D打印模型通常使用該格式定義Three.js提供了STLExporter.js员魏,使用它可以把Three.js模型導(dǎo)出為STL格式 |
CTM |
openCTM定義的格式丑蛤,以緊湊的格式存儲基于三角形的Mesh |
VTK |
Visualization Toolkit定義的格式,用于聲明頂點和面撕阎。 此格式有二進(jìn)制/ASCII兩種變體受裹,Three.js僅支持ASCII變體 |
AWD |
3D場景的二進(jìn)制格式,主要被away3d引擎使用虏束,Three.js不支持AWD壓縮格式 |
Assimp |
開放資產(chǎn)導(dǎo)入庫(Open asset import library)是導(dǎo)入多種3D模型的標(biāo)準(zhǔn)方式棉饶。 使用該Loader你可以導(dǎo)入多種多樣的3D模型格式 |
VRML |
虛擬現(xiàn)實建模語言(Virtual Reality Modeling Language)是一種基于文本的格式。 現(xiàn)已經(jīng)被X3D格式取代盡管Three.js不直接支持X3D镇匀,但是后者很容易被轉(zhuǎn)換為其它格式 |
Babylon |
游戲引擎Babylon的私有格式 |
PLY |
常用于存儲來自3D掃描儀的信息 |
在項目一開始嘗試是使用 dae 文件照藻,后面發(fā)現(xiàn) json 文件更加方便一點,所以最終使用的是 jsonloader 導(dǎo)入 json 文件汗侵。json文件可以通過 blender 或者3DsMax 導(dǎo)出幸缕,他們都有各自的 export json的插件。在軟件中處理好模型貼圖和動畫以后晰韵,導(dǎo)出 json 文件和相應(yīng)的貼圖文件給到前端即可发乔。
var jsonLoader = new THREE.JSONLoader();
jsonLoader.load('model.json', function (geometry, materials) {
materials.forEach(function (mat) {
//這里面可以設(shè)置材質(zhì)的各種信息
mat.skinning = true;
mat.color = new THREE.Color("rgb(233,203,113)"); //模型顏色
mat.emissive = new THREE.Color("rgb(110,110,110)"); //自發(fā)光顏色
});
var model = new THREE.SkinnedMesh(geometry, new THREE.MeshFaceMaterial(materials));
model.name = "model name";
scene.add(model); //下面是播放模型中的動畫內(nèi)容
var sceneAnimationClip = model.geometry.animations[0]
var mixer = new THREE.AnimationMixer(model);
mixers.push(mixer);
var sceneAnimation = mixer.clipAction(sceneAnimationClip);
sceneAnimation.play();
});
同理其他類型的文件也可以使用相應(yīng)的 loader 導(dǎo)入文件,控制其材質(zhì)信息和動畫播放雪猪,具體的可以查看官網(wǎng)的 demo栏尚。
粒子THREE.Sprite
在WebGlRenderer渲染器中使用THREE.Sprite創(chuàng)建的粒子可以直接添加到scene中。創(chuàng)建出來的精靈總是面向鏡頭的浪蹂。即不會有傾斜變形之類透視變化抵栈,只有近大遠(yuǎn)小的變化告材。
比如一個紋理為花瓣的粒子示例:
//花瓣的貼圖
var textureList = [
__uri("../../img/flower-1.png"),
__uri("../../img/flower-2.png"),
__uri("../../img/flower-3.png"),
__uri("../../img/flower-4.png"),
__uri("../../img/flower-5.png"),
__uri("../../img/flower-6.png"),
__uri("../../img/flower-7.png"),
__uri("../../img/flower-8.png"),
__uri("../../img/flower-9.png"),
__uri("../../img/flower-10.png")
]
var particles = []; //存儲生成的粒子
//粒子從Z軸產(chǎn)生區(qū)間在-20到20
for (var zpos = -20; zpos < 20; zpos += 0.5) {
var texturerain = textLoader.load(textureList[Math.floor(Math.random() * 10)])
var material = new THREE.SpriteMaterial({
transparent: true,
opacity: util.getRandomInt(0.7, 1),
map: texturerain
}); //生成粒子
particle = new THREE.Sprite(material);
particle.name = "particle"
//隨即產(chǎn)生x軸,y軸
particle.position.x = Math.random() * 100
particle.position.y = Math.random() * 100; //設(shè)置z軸
particle.position.z = zpos; //將產(chǎn)生的粒子添加到場景
scene.add(particle); //將粒子位置的值保存到數(shù)組
particles.push(particle);
}
//移動粒子的函數(shù)
function updateParticles() { //遍歷每個粒子
for (var i = 0; i < particles.length; i++) {
particle = particles[i]; //設(shè)置粒子向前移動的速度依賴于鼠標(biāo)在平面Y軸上的距離
particle.position.y -= i / particles.length / 50;
particle.position.x -= i / particles.length / 80;
if (particle.position.y < -7) { //溢出視野以后設(shè)置回原位置
particle.position.x = Math.random() * 100 - 50;
particle.position.y = 7;
}
}
}
場景交互
Three.js中并沒有直接提供“點擊”功能,一開始使用的時候我也覺得一臉懵逼古劲,后來才發(fā)現(xiàn)我們可以基于THREE.Raycaster來判斷鼠標(biāo)當(dāng)前對應(yīng)到哪個物體,用來進(jìn)行碰撞檢測.
//核心代碼
var clickObjects = [];
//存儲哪些 obj 需要交互
var _raycaster = new THREE.Raycaster();
//射線拾取器
var raycAsix = new THREE.Vector2();
//屏幕點擊點二維坐標(biāo)
var container = null;
function onMouseMove(event) {
event.preventDefault();
container = document.getElementById("Canvas1");
raycAsix.x = ((event.pageX - $(container).offset().left) / container.offsetWidth) * 2 - 1;
raycAsix.y = -((event.pageY - $(container).offset().top) / container.offsetHeight) * 2 + 1;
_raycaster.setFromCamera(raycAsix, Camera);
//獲取射線上與存儲的可被點擊物體的集合的交集斥赋,集合的第一個物體為距離相機(jī)最近的物體,最后一個則為離相機(jī)最遠(yuǎn)的产艾。
var intersects = _raycaster.intersectObjects(clickObjects);
if (intersects.length > 0) {
document.body.style.cursor = 'pointer';
console.log(intersects[0].object.name) //打印導(dǎo)入模型時設(shè)置的model name
} else {
document.body.style.cursor = 'default';
}
}
其他的交互比如點擊事件都是基于此疤剑。
動畫
場景中如果我們添加了各種 mesh 和模型并給他加入了一些 tweend動畫會發(fā)現(xiàn)他并不會運動,因為你的場景并沒有實時渲染闷堡,所以要讓場景真的動起來隘膘,我們需要用到requestAnimationFrame;關(guān)于它的詳細(xì)使用請大家自行 google,核心代碼如下:
var requestAnimationFrame = window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame;
function animate() {
var delta = clock.getDelta();
if (mixers.length > 0) {
for (var i = 0; i < mixers.length; i++) {
mixers[i].update(delta);
}
} //Renderer即我們實例化的 webglRender 對象杠览;
updateParticles()
Renderer.clear();
Renderer.render(scene, Camera);
requestAnimationFrame(animate); //如果有使用 Tween做一些補間動畫弯菊,也需要在此調(diào)用 TWEEN.update();
TWEEN.update();
}
另外我們?nèi)绻胱约?K 動畫也是可以的,不過我覺得應(yīng)該沒有人這么無聊
jsonLoader.load('../resource/hudie.json', function (geometry, materials) {
materials.forEach(function (mat) {
mat.skinning = true;
mat.color = new THREE.Color("rgb(0,255, 0)");
mat.emissive = new THREE.Color("rgb(255, 0, 255)");
});
var tracks = []; //NumberKeyframeTrack(name踱阿,times管钳,values),依次去K模型的位置软舌,縮放和旋轉(zhuǎn)
tracks.push(new THREE.NumberKeyframeTrack('.position', [0, 1, 2], [-15, -5, -7, 0, 0, 0, 5, 0.3, -9]));
tracks.push(new THREE.NumberKeyframeTrack('.scale', [0, 1, 2], [0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04]));
tracks.push(new THREE.NumberKeyframeTrack('.rotation', [0, 1, 2], [0, 0.2, 0, 0, 0, 0, 0, 0.2, 0.2]));
var model = new THREE.SkinnedMesh(geometry, new THREE.MeshFaceMaterial(materials));
model.name = "蝴蝶";
var clip = new THREE.AnimationClip('Action', -1, tracks);
var mixer = new THREE.AnimationMixer(model);
mixers.push(mixer);
mixer.clipAction(clip).play();
scene.add(model);
});
轉(zhuǎn)載自http://www.reibang.com/p/5420e71b017d