如何使用threejs實(shí)現(xiàn)第一人稱視角的移動(dòng)_第1頁(yè)
如何使用threejs實(shí)現(xiàn)第一人稱視角的移動(dòng)_第2頁(yè)
如何使用threejs實(shí)現(xiàn)第一人稱視角的移動(dòng)_第3頁(yè)
如何使用threejs實(shí)現(xiàn)第一人稱視角的移動(dòng)_第4頁(yè)
如何使用threejs實(shí)現(xiàn)第一人稱視角的移動(dòng)_第5頁(yè)
已閱讀5頁(yè),還剩12頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

1、如何使用thr實(shí)現(xiàn)第嚴(yán)人稱視角的移動(dòng)在數(shù)據(jù)可視化領(lǐng)域利用e來創(chuàng)建三維場(chǎng)景或已經(jīng)越來越普遍,各種開發(fā)框架也應(yīng)運(yùn)而生。今天我們就通過最基本的threejS來完成第一人稱視角的場(chǎng)景巡檢功能。如果你是一位threejS的初學(xué)者或正打算入門,我強(qiáng)烈推薦你仔細(xì)閱讀本文并在我的代碼基礎(chǔ)之上繼續(xù)深入學(xué)習(xí)。因?yàn)樗鼘⑹悄隳軌蛟诰W(wǎng)上找到的最好的免費(fèi)中文教程,通過本文你可以學(xué)習(xí)到一些基本的三維理論,threejS的接口以及你應(yīng)該掌握的數(shù)學(xué)知識(shí)。當(dāng)然要想完全掌握threejS可能還有很長(zhǎng)的路需要走,但至少今天我將帶你入門并傳授一些獨(dú)特的學(xué)習(xí)技巧。第一人稱視角的場(chǎng)景巡檢主要需要解決兩個(gè)問題,人物在場(chǎng)景中的移動(dòng)和碰撞檢測(cè)。

2、移動(dòng)與碰撞功能是所有三維場(chǎng)景首先需要解決的基本問題。為了方便理解,首先需要構(gòu)建一個(gè)簡(jiǎn)單的三維場(chǎng)景并在遇到問題的時(shí)候向你演示如何解決它。hthteheadethrset平移與碰撞tteScriScriptSrc=ptSrc=jS/three.jSriptletSceneamera,renderer,leftPreSS,cube;init()helper(createBer(te(nctionnit(初/始化場(chǎng)景newSTcHenReEE=.Scene();Scenne.ewbaTcHkRgErEo.uCnodlo=r(0創(chuàng)/建渲染器newreeRreErE=.WebGLRenderer(anva

3、S:document.getElementBinCanvaStru,e抗鋸齒ntialiaS:truelpha:reerer.SetSize(wiinnerWidth,.innerHeight創(chuàng)建透視相機(jī)erWidtherHeightnewcTaHmRerEaE.=PerSpectiveCamer參/數(shù)初始化newmToHuRSEeE=.Vector2();nerwayTcHaRSEteEr.=RaycaSter環(huán)/境光arambientnLeiwgThtHR=EE.AmbientLight(0 x60Scene.add(ambientLight);平行光ardirectionnaewlLTi

4、HgRhEtE=.DirectionalLightdirectionalLight.poition.Set(1Scene.add(directionalLight);ctiheolnper()rgrindew=THREE.GridHelper(grid.materiaitygrid.mtarute;erial.tranSparent55scene.add(grid);56varaxesHelper=newTHREE.AxesHelper(30);scene.add(axesHelper);60functionanimate()requestAnimationFrame(animate);ren

5、derer.render(scene,camera);65functioncreateBoxer()vargeometry=newTHREE.BoxGeometry(5,5,5);varmaterial=newTHREE.MeshPhongMaterial(color:0 x00ff00);cube=newTHREE.Mesh(geometry,material);scene.add(cube);72737475767778798081828384858687888990$(window).mousemove(function(event)event.preventDefault();if(l

6、eftPress)cube.rotateOnAxis(newTHREE.Vector3(0,1,0),event.originalEvent.movementX/500);cube.rotateOnAxis(newTHREE.Vector3(1,0,0),event.originalEvent.movementY/500););$(window).mousedown(function(event)event.preventDefault();leftPress=true;91);92$(window).mouseup(function(event)event.preventDefault();

7、leftPress=false;);很多js的開發(fā)人員非常熟悉jquery,我引用它確實(shí)讓代碼顯得更加簡(jiǎn)單。首先我在nit()方法里初始化了一個(gè)場(chǎng)景。我知道在大部分示例中包括官方提供的demo里都是通過threejs動(dòng)態(tài)的在document下創(chuàng)建一節(jié)點(diǎn)。我強(qiáng)烈建議你不要這樣做,因?yàn)樵诤芏鄦雾?yè)面應(yīng)用中(例如:Vue和Angular)直接操作DOM都不被推薦。接下來我使用helper()方法創(chuàng)建了兩個(gè)輔助對(duì)象:一個(gè)模擬地面的網(wǎng)格和一個(gè)表示世界坐標(biāo)系的AxesHelper。最后我利用createBoxer()方法在視角中央擺放了一個(gè)綠色的立方體以及綁定了三個(gè)鼠標(biāo)動(dòng)作用來控制立方地旋轉(zhuǎn)。如圖:你可以嘗

8、試將代碼復(fù)制到本地并在瀏覽器中運(yùn)行,移動(dòng)鼠標(biāo)看看效果。接下來,為了讓方塊移動(dòng)起來,我們需要添加一些鍵盤響應(yīng)事件,以及給方塊的“正面”上色。平移與碰撞letscene,camera,renderer,leftPress,cube;letleft,right,front,back;init();helper();createBoxer();animate();19functioninit()/初始化場(chǎng)景scene=newTHREE.Scene();scene.background=newTHREE.Color(0 xffffff);24/創(chuàng)建渲染器renderer=newTHREE.WebGLRe

9、nderer(canvas:document.getElementById(mainCanvas),antialias:true,/抗鋸齒alpha:true);renderer.setSize(window.innerWidth,window.innerHeight);3233/創(chuàng)建透視相機(jī)camera=newTHREE.PerspectiveCamera(75,window.innerWidth/window.innerHeight,0.1,1000);camera.position.set(0,40,30);camera.lookAt(0,0,0);38/參數(shù)初始化mouse=newTH

10、REE.Vector2();raycaster=newTHREE.Raycaster();4243/環(huán)境光4445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108varambientLight=newTHREE.AmbientLight(0 x606060);scene.add(ambientLight);/vardirectionalLight=newTHREE.Direc

11、tionalLight(0 xBCD2EE);directionalLight.position.set(1,0.75,0.5).normalize();scene.add(directionalLight);functionhelper()vargrid=newTHREE.GridHelper(100,20,0 xFF0000,0 x000000);grid.material.opacity=0.1;grid.material.transparent=true;scene.add(grid);varaxesHelper=newTHREE.AxesHelper(30);scene.add(ax

12、esHelper);functionanimate()requestAnimationFrame(animate);renderer.render(scene,camera);if(front)cube.translateZ(-1)if(back)cube.translateZ(1);if(left)cube.translateX(-1);if(right)cube.translateX(1);functioncreateBoxer()vargeometry=newTHREE.BoxGeometry(5,5,5);varmats=;mats.push(newTHREE.MeshPhongMat

13、erial(color:0 x00ff00);mats.push(newTHREE.MeshPhongMaterial(color:0 xff0000);cube=newTHREE.Mesh(geometry,mats);for(letj=0;jgeometry.faces.length;j+)if(j=8|j=9)geometry.facesj.materialIndex=1;elsegeometry.facesj.materialIndex=0;scene.add(cube);$(window).mousemove(function(event)event.preventDefault()

14、;if(leftPress)cube.rotateOnAxis(newTHREE.Vector3(0,1,0),event.originalEvent.movementX/500);cube.rotateOnAxis(newTHREE.Vector3(1,0,0),event.originalEvent.movementY/500););1091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511

15、52$(window).mousedown(function(event)event.preventDefault();leftPress=true;);$(window).mouseup(function(event)event.preventDefault();leftPress=false;);$(window).keydown(function(event)switch(event.keyCode)case65:/aleft=true;break;case68:/dright=true;break;case83:/sback=true;break;case87:/wfront=true

16、;break;);$(window).keyup(function(event)switch(event.keyCode)case65:/aleft=false;break;case68:/dright=false;break;case83:/sback=false;break;case87:/wfront=false;break;);我們添加了keydown()事件和keyup()事件用來捕獲鍵盤響應(yīng)。我們還修改了createBoxer()方法,給朝向我們的那一面涂上紅色。你一定發(fā)現(xiàn)了BoxGeometry所代表的立方體雖然只有6個(gè)面,可是為了給1個(gè)面吐色我們卻需要同時(shí)在2個(gè)面啲材質(zhì)上著色。

17、這是因?yàn)樵谌S場(chǎng)景中,“面啲含義表示由空間中3個(gè)點(diǎn)所代表的區(qū)域,而一個(gè)矩形由兩個(gè)三角形拼接而成。完成以后的樣子如下:設(shè)想一下在第一人稱視角的游戲中,我們抬高視角觀察周圍后再降低視角,地平線是否依然處于水平狀態(tài)。換句話說,無(wú)論我們?nèi)绾瓮蟿?dòng)鼠標(biāo),紅色的那面在朝向我們的時(shí)候都不應(yīng)該傾斜。要解釋這個(gè)問題,我們首先需要搞清楚三維場(chǎng)景中的坐標(biāo)系概念。在hreejs的世界中存在兩套坐標(biāo)體系:世界坐標(biāo)系和自身坐標(biāo)系。世界坐標(biāo)系是整個(gè)場(chǎng)景的坐標(biāo)系統(tǒng),通過它可以定位場(chǎng)景中的物體。而自身坐標(biāo)系就比較復(fù)雜,實(shí)際上一個(gè)物體的自身坐標(biāo)系除了用來表示物體各個(gè)部分的相對(duì)關(guān)系以外主要用來表示物體的旋轉(zhuǎn)。想象一下月球的自轉(zhuǎn)和公轉(zhuǎn)

18、,在地月坐標(biāo)系中,月球圍繞地球公轉(zhuǎn),同時(shí)也繞著自身的Y軸旋轉(zhuǎn)。在我們上面的場(chǎng)景中,立方體自身的坐標(biāo)軸會(huì)隨著自身的旋轉(zhuǎn)而改變,當(dāng)我們的鼠標(biāo)自下而上滑動(dòng)后,Y軸將不再垂直于地面。如果這時(shí)我們?cè)贆M向滑動(dòng)鼠標(biāo)讓立方體繞Y軸旋轉(zhuǎn),自然整個(gè)面都會(huì)發(fā)生傾斜。如果你還不理解可以在自己的代碼中多嘗試幾次,理解世界坐標(biāo)系和自身坐標(biāo)系對(duì)于學(xué)習(xí)webgl尤其重要。很顯然,要模擬第一人稱的視角轉(zhuǎn)動(dòng)我們需要讓視角上下移動(dòng)的旋轉(zhuǎn)軸為自身坐標(biāo)系的軸,左右移動(dòng)的旋轉(zhuǎn)軸固定為穿過自身中心的一條與世界坐標(biāo)系Y軸保持平行的軸線。理解這個(gè)問題很不容易,可是解決它卻非常簡(jiǎn)單othreejs為我們提供了方法,我們只需要修改mousemov

19、e()方法:$(window).mousemove(function(event)event.preventDefault();if(leftPress)cube.rotateOnWorldAxis(newTHREE.Vector3(0,1,0),event.originalEvent.movementX/500);cube.rotateOnAxis(newTHREE.Vector3(1,0,0),event.originalEvent.movementY/500););有了控制視角的方式,接下來我們移動(dòng)一下方塊。新的問題又出現(xiàn)了:盒子的運(yùn)動(dòng)方向也是沿著自身坐標(biāo)系的。就和我們看著月亮行走并不會(huì)

20、走到月亮上去的情形一樣,如果要模擬第一人稱視角的移動(dòng),視角的移動(dòng)方向應(yīng)該永遠(yuǎn)和世界坐標(biāo)系保持平行,那么我們是否可以通過世界坐標(biāo)系來控制物體的移動(dòng)呢:functionanimate()requestAnimationFrame(animate);renderer.render(scene,camera);if(front)/cube.translateZ(-1)cube.position.z-=1;TOC o 1-5 h zif(back)/cube.translateZ(1);cube.position.z+=1;if(left)/cube.translateX(-1);cube.positi

21、on.x-=1;if(right)/cube.translateX(1);cube.position.x+=1;盡管這個(gè)需求顯得如此合理,可是threejs似乎并沒有提供有效的解決方案,就連官方示例中提供的基于第一人稱的移動(dòng)也僅僅是通過固定物體Y軸數(shù)值的方法實(shí)現(xiàn)的。在射擊游戲中不能蹲下或爬上屋頂實(shí)在不能讓玩家接受。為了能夠在接下來的變換中分解問題和測(cè)試效果,我們?cè)谀P蜕咸砑觾蓚€(gè)箭頭表示物體的前后方向。letarrowFront,arrowBack;2functionanimate()requestAnimationFrame(animate);renderer.render(scene,cam

22、era);arrowFront.setDirection(cube.getWorldDirection(newTHREE.Vector3().normalize();arrowFront.position.copy(cube.position);arrowBack.setDirection(cube.getWorldDirection(newTHREE.Vector3().negate().normalize();arrowBack.position.copy(cube.position);if(front)/cube.translateZ(-1)cube.position.z-=1;TOC

23、o 1-5 h zif(back)/cube.translateZ(1);cube.position.z+=1;if(left)/cube.translateX(-1);cube.position.x-=1;if(right)/cube.translateX(1);cube.position.x+=1;27functioncreateBoxer()vargeometry=newTHREE.BoxGeometry(5,5,5);varmats=;mats.push(newTHREE.MeshPhongMaterial(color:0 x00ff00);mats.push(newTHREE.Mes

24、hPhongMaterial(color:0 xff0000);cube=newTHREE.Mesh(geometry,mats);for(letj=0;jgeometry.faces.length;j+)if(j=8|j=9)geometry.facesj.materialIndex=1;elsegeometry.facesj.materialIndex=0;scene.add(cube);arrowFront=newTHREE.ArrowHelper(cube.getWorldDirection(),cube.position,15,0 xFF0000);scene.add(arrowFr

25、ont);arrowBack=newTHREE.ArrowHelper(cube.getWorldDirection().negate(),cube.position,15,0 x00FF00);scene.add(arrowBack);有了箭頭的輔助,我們能夠以比較直觀的方式測(cè)試算法是否有效。如果你能夠認(rèn)真讀到這里,可能已經(jīng)迫不及待想繼續(xù)了,但是還請(qǐng)稍安勿躁。進(jìn)入下個(gè)環(huán)節(jié)前,我們需要首先了解幾個(gè)重要的概念。三維向量(Vector3):可以表征三維空間中的點(diǎn)或來自原點(diǎn)(0,0,0)的矢量。需要注意,Vector3既可以表示空間中的一個(gè)點(diǎn)又可以表示方向。因此為了避免歧義,我建議在作為矢量的時(shí)候通

26、過normalize。方法對(duì)向量標(biāo)準(zhǔn)化。具體api文檔。歐拉角(Euler):表示一個(gè)物體在其自身坐標(biāo)系上的旋轉(zhuǎn)角度,歐拉角也是一個(gè)很常見的數(shù)學(xué)概念,優(yōu)點(diǎn)是對(duì)于旋轉(zhuǎn)的表述相對(duì)直觀,不過我們?cè)陧?xiàng)目中并不常用。四元數(shù)(Quaternion):四元數(shù)是一個(gè)相對(duì)高深的數(shù)學(xué)概念,幾何含義與歐拉角類似。都可以用來表征物體的旋轉(zhuǎn)方向,優(yōu)點(diǎn)是運(yùn)算效率更咼。四維矩陣(Matrix4):在threejs的世界中,任何一個(gè)對(duì)象都有它對(duì)應(yīng)的四維矩陣。它集合了平移、旋轉(zhuǎn)、縮放等操作。有時(shí)我們可以通過它來完成兩個(gè)對(duì)象的動(dòng)作同步。叉積(.cross():向量叉積表示由兩個(gè)向量所確定的平面的法線方向。叉積的用途很多,例如在第

27、一人稱的視角控制下,實(shí)現(xiàn)左右平移就可以通過當(dāng)前視角方向z與垂直方向y做叉積運(yùn)算獲得:乙cross(y)。點(diǎn)積(.dot():與向量叉積不同,向量點(diǎn)積為一長(zhǎng)度數(shù)據(jù)。vect_a.dot(vect_b)表示向量b在向量a上的投影長(zhǎng)度,具體如何使用我們馬上就會(huì)看到在理解了上面的概念以后,我們就可以實(shí)現(xiàn)沿視角方向平移的操作:我們知道,物體沿平面XOZ)坐標(biāo)系運(yùn)動(dòng)都可以分解為X方向上的運(yùn)動(dòng)分量和Z軸方向上的運(yùn)動(dòng)分量。首先獲取視角的方向,以三維向量表示。接著我們需要以這個(gè)向量和X軸方向上的一個(gè)三維向量做點(diǎn)積運(yùn)算,從而得到一個(gè)投影長(zhǎng)度。這個(gè)長(zhǎng)度即代表物體沿視角方向移動(dòng)的水平x軸方向上的運(yùn)動(dòng)分量。同理,我們?cè)?/p>

28、計(jì)算與Z軸方向上的點(diǎn)積,又可以獲得物體沿視角方向移動(dòng)的z軸方向的運(yùn)動(dòng)分量。同時(shí)執(zhí)行兩個(gè)方向上的運(yùn)動(dòng)分量完成平移操作。接下來,我們先通過實(shí)驗(yàn)觀察是否能夠獲得這兩個(gè)運(yùn)動(dòng)分量和投影長(zhǎng)度。平移與碰撞letscene,camera,renderer,leftPress,cube,arrowFront,arrowFrontX,arrowFrontZ;letleft,right,front,back;init();/helper();createBoxer();animate();19functioninit()/初始化場(chǎng)景scene=newTHREE.Scene();scene.background=newTHREE.Color(0 xffffff);24/創(chuàng)建渲染器renderer=newTHREE.WebGLRenderer(canvas:document.getElementById(mainCanv

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論