青少年勵(lì)志成長發(fā)育three.js小筆記

? ? ? ? 之前用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ū)別我盜兩張圖感受一下就知道了

遠(yuǎn)景相機(jī)
正交相機(jī)

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ì)。

材質(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)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Three.js是一個(gè)3DJavaScript庫,基于右手坐標(biāo)系,可以創(chuàng)建簡單或是比較復(fù)雜的三維圖形并應(yīng)用豐富多彩...
    呆呆的木木閱讀 25,040評(píng)論 42 59
  • 1,前天晚上突然想找個(gè)工作,投了簡歷,今天去面試了。不為工資,不問時(shí)間,只看公司人多不多,妹子多不多。條件越不好的...
    畢缽羅子閱讀 196評(píng)論 0 1
  • 八月接近尾聲,開學(xué)季來了。不管你愿不愿意,開學(xué)的時(shí)間都會(huì)如期而至,我們又將重返講臺(tái)。 每年的開學(xué)時(shí)節(jié),心里總有...
    楊正歡閱讀 215評(píng)論 0 0

友情鏈接更多精彩內(nèi)容