? ? ? ? 之前用qunee做了一個(gè)2.5d的機(jī)房監(jiān)控,丑的閃瞎我的卡姿蘭大眼,后來含淚用three.js做個(gè)3d的換掉,結(jié)果依然好看不到哪去(畢竟恐怖的直男審美),錯(cuò)怪qunee了,不是它丑,是我丑。記筆記了記筆記了。
? ? ? ? 引用下Mono大佬的文章,圖片資源都是順手從這拿的html5,不只是看上去很美
一、步驟
1.畫布
首先準(zhǔn)備一塊畫布框,并對(duì)渲染器做設(shè)置
var renderer;
function initRender(){
? ? renderer = new THREE.WebGLRenderer({antialias: true}); //創(chuàng)建渲染器對(duì)象,antialias抗鋸齒有效
? ? renderer.setSize( window.innerWidth, window.innerHeight );//設(shè)置渲染器寬高
? ? document.body.appendChild( renderer.domElement );//追加到頁面中
? ? renderer.setClearColor(0xFFFFFF, 1.0);設(shè)置清除色
}
常用渲染器對(duì)象有CanvasRenderer和WebGLRenderer,它們的區(qū)別前者是繪制場景使用的是canvas 2d context,而后者是WebGL(仿佛是句廢話),相較而言,后者的性能要優(yōu)于前者
2.相機(jī)
var camera;
function initCamera(){
? ? camera = new THREE.PerspectiveCamera( 45, window.innerWidth/window.innerHeight, 0.1, 1000);//創(chuàng)建遠(yuǎn)景相機(jī)
? ? camera.position.z = 100; //設(shè)置相機(jī)位置(set)
? ? camera.position.y = 40;
? ? camera.position.x = 50;
? ? camera.lookAt({x:0,y:0,z:0});//設(shè)置視野位置
}
參數(shù)分別表示視野角度、縱橫比、相機(jī)相對(duì)于物體的最近、最遠(yuǎn)距離
相機(jī)中常見的有OrthographicCamera(正交相機(jī))和PerspectiveCamera(遠(yuǎn)景相機(jī),也叫透視投影),它們的區(qū)別我盜兩張圖感受一下就知道了
3.場景
一個(gè)三維的空間
var scene;
function initScene(){
? ? scene = new THREE.Scene();
}
4.光源
光源的作用很明顯,現(xiàn)實(shí)生活中光是有顏色的,在人的眼中,物體的顏色是由光源照射后決定的,就好像賣豬肉都要打個(gè)燈,看上去仿佛很新鮮。
var light;
function initLight(){
? ? light = new THREE.DirectionalLight((0xFF0000,1.0,0));//平行光
? ? light.position.set(x,y,z);//設(shè)置光源坐標(biāo)
? ? scene.add(light);//將光源添加到場景中
}
常見光源有AmbientLight(環(huán)境光):這種光源的顏色和強(qiáng)度會(huì)加在照射范圍內(nèi)的所有對(duì)象上
? ? ? ? ? ? ? ? ? DirectionalLight(平行光):是一種從特定方向照射的光源,它產(chǎn)生的光線都是平行的
? ? ? ? ? ? ? ? ? PointLight(點(diǎn)光源):有特定位置的光源,照射到各個(gè)角落
? ? ? 有光源就有陰影,這樣才能顯示出3d的效果,可以通過對(duì)castShadow設(shè)置true來表示該光源是可以產(chǎn)生陰影的,再由物體receiveShadow=true接收陰影。對(duì)陰影研究不多,任性的跳過。
5.物體
? ? ? 追加物體這一塊要考慮物體的網(wǎng)格和材質(zhì)。就那什么什么兔子來著,就是由不知道多少個(gè)三角形網(wǎng)格拼接而成的模型兔子,網(wǎng)格越密集,精確度越高(畢竟我是畫過cad、做過流固體耦合的男人,hhh)
? ? ? 幾何模型對(duì)象有一大堆,因?yàn)槎际且恍┓椒秸臋C(jī)器,所以用的到一般都是BoxGeometry(盒子模型)、CylinderGeometry(圓柱體模型)、PlaneGeometry(平面模型)等等。
? ? ? 就比如var geometry=newTHREE.BoxGeometry(1,1,1);這就創(chuàng)建了一個(gè)長寬高1,1,1的正方體的網(wǎng)格模型。
? ? ? 之后應(yīng)該選擇模型的材質(zhì),找了下文檔,發(fā)現(xiàn)了那么多材質(zhì),可以根據(jù)實(shí)際情況選擇不同的材質(zhì),比如機(jī)房設(shè)備都是金屬,我選擇MeshPhongMaterial這種表面有光澤的材質(zhì)。
? ? ? 再然后有些物體要添加一些貼圖,也就是紋理。如果對(duì)多個(gè)面添加不同的紋理,可以load每張圖片或者圖片放在一張上使用UV映射。
? ? ? var texture=new THREE.TextureLoader().load(圖片路徑);
? ? ? texture.wrapS=THREE.RepeatWrapping;
? ? ? texture.wrapT=THREE.RepeatWrapping;
? ? ? texture.repeat.set(4,4);
? ? ? material.map = texture;//將紋理添加到材質(zhì)中
? ? ? 網(wǎng)格和材質(zhì)都創(chuàng)建完后,開始使用Mesh類創(chuàng)建模型:var cube = new THREE.Mesh(geometry,material);也可以對(duì)模型的position做設(shè)置,記得將模型加入到場景中scene.add(cube);
? ? ? 最后舉個(gè)完整的例子,畫個(gè)滅火器:
air3D.prototype.createFire = function(x,z){
? //滅火器
? ? var fire_body_material = new THREE.MeshPhongMaterial({map:THREE.ImageUtils.loadTexture("./image/room/fire_extinguisher_side.jpg")});
? ? var fire_body = new THREE.CylinderGeometry(0.5,0.5,2.5);
? ? var fire_body_cube = new THREE.Mesh(fire_body,fire_body_material);
? ? fire_body_cube.position.y = 4;
? ? // scene.add(fire_body_cube);
? ? var fire_top_material = new THREE.MeshPhongMaterial({color:'#B81F18'});
? ? var fire_top = new THREE.SphereGeometry(0.5);
? ? var fire_top_cube = new THREE.Mesh(fire_top,fire_top_material);
? ? fire_top_cube.position.y = 5.25;
? ? // scene.add(fire_top_cube);
? ? var fire_on_material = new THREE.MeshPhongMaterial({color:'#FFF504'});
? ? var fire_on = new THREE.SphereGeometry(0.2);
? ? var fire_on_cube = new THREE.Mesh(fire_on,fire_on_material);
? ? fire_on_cube.position.y = 5.8;
? ? // scene.add(fire_on_cube);
? ? var handle_material = new THREE.MeshPhongMaterial({color:'#846f6f'});
? ? var handle1 = new THREE.CylinderGeometry(0.1,0.08,1.2);
? ? var handle_cube1 = new THREE.Mesh(handle1,handle_material);
? ? handle_cube1.rotation.x += -0.45*Math.PI;
? ? handle_cube1.position.y = 5.8;
? ? // scene.add(handle_cube1);
? ? var handle2 = new THREE.CylinderGeometry(0.08,0.08,2);
? ? var handle_cube2 = new THREE.Mesh(handle2,handle_material);
? ? handle_cube2.rotation.x += -0.05*Math.PI;
? ? handle_cube2.position.z = 0.7;
? ? handle_cube2.position.y = 4.75;
? ? // scene.add(handle_cube2);
? var fireObj = new THREE.Object3D();
? fireObj.add(fire_body_cube);
? fireObj.add(fire_top_cube);
? fireObj.add(fire_on_cube);
? fireObj.add(handle_cube1);
? fireObj.add(handle_cube2);
? this.scene.add(fireObj);
? objects.push(fireObj);
? fireObj.position.y = -2.65;
? fireObj.position.x = x;
? fireObj.position.z = z;
? fireObj.rotation.y += -0.5*Math.PI;
};
效果大概是這樣:
? ? 有些不規(guī)則的模型,不知道別人是怎么操作的,我是單獨(dú)繪制各個(gè)部位的模型,然后創(chuàng)建Object3D對(duì)象。還有一種二元操作也可以創(chuàng)建一些規(guī)
則的模型,比如insert(相交)、union(聯(lián)合)、subtract(相減),我機(jī)房大門就是一塊大的BoxGeometry? subtract掉一塊小的BoxGeometry,
但是之后我就不知道怎么給指定面添加紋理了(迷茫。。。)。
6.渲染
init();
render();
function render() {
? ? requestAnimationFrame( render );
? ? renderer.render( scene, camera );
}
function init(){
? ? initRender();
? ? initScene();
? ? initCamera();
? ? initLight();
? ? initObject();
}
? ? 可以在render()中添加代碼實(shí)現(xiàn)動(dòng)畫,對(duì)于three.js,動(dòng)畫就是每一秒的多次繪制。requestAnimationFrame的作用和setInterval很相似,不同的是setInterval需要指定fps,而requestAnimationFrame則是自動(dòng)設(shè)定,setInterval在一些特殊情況下,例如瀏覽器處理繁忙時(shí),fps就可能會(huì)低于設(shè)定值,一般低于20就容易出現(xiàn)卡頓的情況。
二、補(bǔ)充
1.? 模型的拾取,思路還不太清晰,容我復(fù)習(xí)一下線性代數(shù)冷靜冷靜(。。。),從源碼中扒的
air3D.prototype.onRightClick = function(event){
? ? event.preventDefault();
? ? var t = air3DObj;
? ? var raycaster = new THREE.Raycaster();
? ? var rect = t.renderer.domElement.getBoundingClientRect();
? ? var mouse = new THREE.Vector2();
? ? mouse.x = ( (event.clientX - rect.left) / rect.width ) * 2 - 1;
? ? mouse.y = - ( (event.clientY - rect.top) / rect.height ) * 2 + 1;
? ? raycaster.setFromCamera(mouse,t.camera);
? ? var intersects = raycaster.intersectObjects(t.scene.children);
? ? if(intersects.length>0){
? ? ? ? var obj = intersects[0].object;
? ? }
};
2.項(xiàng)目中的問題,多次刪除添加后瀏覽器奔潰,scene.remove(cube)只是從場景中刪除,記得清除geometry數(shù)據(jù)
3.調(diào)用OrbitControls.js后會(huì)出現(xiàn)dom沒法選中的現(xiàn)象,因?yàn)樗J(rèn)監(jiān)聽的是document,記得創(chuàng)建時(shí)添加場景的canvas(renderer.domElement)。