這篇文章主要介紹怎么使用Chrome控制臺(tái)進(jìn)行3D模型編輯的實(shí)現(xiàn),文中介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們一定要看完!
創(chuàng)新互聯(lián)主打移動(dòng)網(wǎng)站、成都做網(wǎng)站、成都網(wǎng)站制作、網(wǎng)站改版、網(wǎng)絡(luò)推廣、網(wǎng)站維護(hù)、域名注冊(cè)、等互聯(lián)網(wǎng)信息服務(wù),為各行業(yè)提供服務(wù)。在技術(shù)實(shí)力的保障下,我們?yōu)榭蛻?hù)承諾穩(wěn)定,放心的服務(wù),根據(jù)網(wǎng)站的內(nèi)容與功能再?zèng)Q定采用什么樣的設(shè)計(jì)。最后,要實(shí)現(xiàn)符合網(wǎng)站需求的內(nèi)容、功能與設(shè)計(jì),我們還會(huì)規(guī)劃穩(wěn)定安全的技術(shù)方案做保障。
前言:3D模型編輯的核心是對(duì)頂點(diǎn)位置和紋理顏色的編輯,這個(gè)研究的目的在于尋找一種通過(guò)編程方式直接對(duì)模型進(jìn)行編輯的方法,這種編輯方法和時(shí)下流行的通過(guò)鼠標(biāo)點(diǎn)選、拖拽進(jìn)行編輯的方法之間的關(guān)系,和前端編程中“程序員編寫(xiě)靜態(tài)網(wǎng)頁(yè)”與“美工進(jìn)行網(wǎng)頁(yè)切圖”之間的關(guān)系很相似。
一、工具用法:
1、訪問(wèn) https://ljzc002.github.io/SnowMiku/HTML/MakeRibbon.html打開(kāi)條帶網(wǎng)格生成器頁(yè)面
在場(chǎng)景世界坐標(biāo)系的(0,-10,0),(0,0,0),(0,10,0)處各有一個(gè)綠色小球作為參考點(diǎn),使用上下左右和鼠標(biāo)拖動(dòng)可以進(jìn)行場(chǎng)景漫游。
2、按F12鍵打開(kāi)Chrome控制臺(tái),在控制臺(tái)中輸入:MakeRibbon(MakeRing(5,12),-10,2,11,"mesh_ribbon")回車(chē):
在場(chǎng)景中繪制了一個(gè)半徑為5,曲面細(xì)分度為12,左端位于-10,每?jī)蓚€(gè)圓環(huán)間距2,共由11個(gè)圓環(huán)組成的圓柱面。
拉近查看:
3、輸入ShowNormals(mesh_origin)將用紅色線段顯示每個(gè)頂點(diǎn)的法線方向
輸入DeleteMeshes([lines_normal])可以刪除所有的法線,輸入DeleteMeshes([mesh_origin])則刪除圓柱面網(wǎng)格。
4、鼠標(biāo)移入網(wǎng)格上的三角形,會(huì)顯示三角形的頂點(diǎn)信息:
其中“1:2-5”表示這是三角形的第一個(gè)頂點(diǎn),這個(gè)頂點(diǎn)位于索引是2的圓環(huán)上(第三個(gè)圓環(huán)),這個(gè)頂點(diǎn)在圓環(huán)中的索引是5(也就是第六個(gè)頂點(diǎn))。
5、輸入PickPoints([[2,5],[3,5],[2,6]],mesh_origin)可以選定這些頂點(diǎn)
被選中頂點(diǎn)所影響的所有邊框線標(biāo)示為黃色,這個(gè)“選中”只是改變外觀而已。
6、輸入TransVertex(mesh_origin,arr_ij,BABYLON.Matrix.Translation(0,0,-10))將所選的頂點(diǎn)向z軸負(fù)方向移動(dòng)10,被移動(dòng)的頂點(diǎn)和前面選中的頂點(diǎn)其實(shí)沒(méi)有關(guān)系,其中arr_ij也可以直接用索引數(shù)組[[2,5],[3,5],[2,6]]代替。
另一類(lèi)變形可以通過(guò)輸入:TransVertex(mesh_origin,arr_ij,BABYLON.Matrix.RotationX(Math.PI/2))實(shí)現(xiàn),這可以把被選中的頂點(diǎn)繞X中旋轉(zhuǎn)90度。
輸入DeleteMeshes([lines_inpicked])取消被選中的效果,輸入ChangeMaterial(mesh_origin,mat_blue)將邊框換成藍(lán)色紋理:
可以看到變形后的效果,接下來(lái)還可以繼續(xù)選擇頂點(diǎn)并變形
7、輸入ExportMesh("1",mat_blue),以txt格式導(dǎo)出babylon模型文件,文件名為“1.txt”
8、將導(dǎo)出的txt改名為9.babylon后放入網(wǎng)站目錄中,訪問(wèn)https://ljzc002.github.io/SnowMiku/HTML/LoadBabylon.html模型預(yù)覽頁(yè)面,在控制臺(tái)輸入ImportMesh("","../ASSETS/SCENE/","9.babylon")即可加載剛才導(dǎo)出的模型。
二、編程思路:
1、首先要建立一個(gè)可以進(jìn)行各種測(cè)試的基礎(chǔ)場(chǎng)景,使用的代碼如下:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>建立一個(gè)條帶網(wǎng)格生成器,能夠輸入?yún)?shù)生成起始條帶,然后通過(guò)命令行選取并修改pathArray,最后導(dǎo)出生成的條帶</title> <link href="../CSS/newland.css" rel="stylesheet"> <link href="../CSS/stat.css" rel="stylesheet"> <script src="../JS/MYLIB/Events.js"></script> <script src="../JS/MYLIB/FileText.js"></script> <script src="../JS/MYLIB/View.js"></script> <script src="../JS/LIB/babylon.32.all.maxs.js"></script><!--V3.2的穩(wěn)定版本--> <script src="../JS/MYLIB/newland.js"></script> <script src="../JS/LIB/stat.js"></script> </head> <body> <div id="div_allbase"> <canvas id="renderCanvas"></canvas> <div id="fps" style="z-index: 301;"></div> </div> </body> <script> var VERSION=1.0,AUTHOR="[email protected]"; var machine,canvas,engine,scene,gl,MyGame={}; canvas = document.getElementById("renderCanvas"); engine = new BABYLON.Engine(canvas, true); gl=engine._gl;//可以結(jié)合使用原生OpenGL和Babylon.js; scene = new BABYLON.Scene(engine); var divFps = document.getElementById("fps"); window.onload=beforewebGL; function beforewebGL() { if(engine._webGLVersion==2.0)//輸出ES版本 { console.log("ES3.0"); } else{ console.log("ES2.0"); } //MyGame=new Game(0,"first_pick","","http://127.0.0.1:8082/"); /*0-startWebGL * */ webGLStart(); } //從下面開(kāi)始分成簡(jiǎn)單測(cè)試和對(duì)象框架兩種架構(gòu) //簡(jiǎn)單測(cè)試 //全局對(duì)象 var light0//全局光源 ,camera0//主相機(jī) ; //四種常用材質(zhì) var mat_frame = new BABYLON.StandardMaterial("mat_frame", scene); mat_frame.wireframe = true; var mat_red = new BABYLON.StandardMaterial("mat_red", scene); mat_red.diffuseColor = new BABYLON.Color3(1, 0, 0); mat_red.backFaceCulling=false; var mat_green = new BABYLON.StandardMaterial("mat_green", scene); mat_green.diffuseColor = new BABYLON.Color3(0, 1, 0); mat_green.backFaceCulling=false; var mat_blue = new BABYLON.StandardMaterial("mat_blue", scene); mat_blue.diffuseColor = new BABYLON.Color3(0, 0, 1); mat_blue.backFaceCulling=false; var mesh_origin; var advancedTexture=BABYLON.GUI.AdvancedDynamicTexture.CreateFullscreenUI("ui1");//全屏GUI function webGLStart() { window.addEventListener("resize", function () {//自動(dòng)調(diào)整視口尺寸 engine.resize(); }); camera0 =new BABYLON.FreeCamera("FreeCamera", new BABYLON.Vector3(0, 0, -80), scene); camera0.attachControl(canvas, true); camera0.speed=0.5;//相機(jī)移動(dòng)速度是默認(rèn)速度的一半 camera0.minZ=0.01;//相機(jī)位置距前視錐截面的距離,也就是說(shuō)到相機(jī)距離小于0.01的圖元都不會(huì)顯示,這個(gè)值不能過(guò)小,否則Babylon.js內(nèi)置的鼠標(biāo)選取將失效 camera0.layerMask=2;//相機(jī)的遮罩層次,這個(gè)相機(jī)將只能顯示遮罩層次同為2的網(wǎng)格,如果不設(shè)置這個(gè)屬性,似乎可以顯示所有遮罩層次的網(wǎng)格 scene.activeCameras.push(camera0);//將相機(jī)加入活躍相機(jī)列表,默認(rèn)情況下Babylon.js只使用一個(gè)活躍相機(jī),但是也可以強(qiáng)行使用多個(gè) light0 = new BABYLON.HemisphericLight("Hemi0", new BABYLON.Vector3(0, 1, 0), scene);//半球光源 //三個(gè)參照物,MeshBuilder是新版Babylon.js中使用的網(wǎng)格構(gòu)建對(duì)象,之前翻譯入門(mén)教程時(shí)還沒(méi)有這個(gè)對(duì)象,它的特點(diǎn)是把一大堆參數(shù)統(tǒng)一整理到一個(gè)option參數(shù)中 var mesh_base=new BABYLON.MeshBuilder.CreateSphere("mesh_base",{diameter:1},scene); mesh_base.material=mat_green; mesh_base.position.x=0; mesh_base.layerMask=2; var mesh_base1=new BABYLON.MeshBuilder.CreateSphere("mesh_base1",{diameter:1},scene); mesh_base1.position.y=10; mesh_base1.position.x=0; mesh_base1.material=mat_green; mesh_base1.layerMask=2; var mesh_base2=new BABYLON.MeshBuilder.CreateSphere("mesh_base2",{diameter:1},scene); mesh_base2.position.y=-10; mesh_base2.position.x=0; mesh_base2.material=mat_green; mesh_base2.layerMask=2; 。 。 。 MyBeforeRender(); } function MyBeforeRender() { scene.registerBeforeRender(function() { if(scene.isReady()) { 。 。 。 。 。 。 } }); engine.runRenderLoop(function () { engine.hideLoadingUI(); if (divFps) { // Fps divFps.innerHTML = engine.getFps().toFixed() + " fps"; } scene.render(); }); } </script> </html>
這個(gè)3D場(chǎng)景包括了簡(jiǎn)單測(cè)試所需要的一些基本元素,這里使用的是包含全部組件的未壓縮版Babylon.js庫(kù),在實(shí)際使用中考慮到節(jié)省帶寬,可以使用Babylon.js官網(wǎng)提供的工具定制精簡(jiǎn)版或壓縮版的Babylon.js庫(kù)
2、建立一個(gè)基礎(chǔ)網(wǎng)格
計(jì)劃通過(guò)對(duì)一個(gè)基礎(chǔ)網(wǎng)格進(jìn)行頂點(diǎn)變換來(lái)產(chǎn)生各種各樣的簡(jiǎn)單模型,在Babylon.js中“條帶”是一種非常適合頂點(diǎn)變換的網(wǎng)格類(lèi)型,Babylon.js官方教程中有關(guān)于條帶構(gòu)造和變形的文檔,可以在這里下載中文翻譯http://down.51cto.com/data/2449757。
用來(lái)建立基礎(chǔ)網(wǎng)格的代碼如下:
//下面這些函數(shù)都通過(guò)控制臺(tái)調(diào)用 //在ZoY平面里建立一個(gè)圓環(huán)路徑 //radius:半徑,sumpoint:使用幾個(gè)點(diǎn) function MakeRing(radius,sumpoint) { var arr_point=[]; var radp=Math.PI*2/sumpoint; for(var i=0.0;i<sumpoint;i++) { var x=0; var rad=radp*i; //var y=sswr(radius*Math.sin(rad),null,5);//在這里需要降低一些精確度?否則Babylon.js在計(jì)算頂點(diǎn)數(shù)據(jù)時(shí)可能和這里不一致? //var z=sswr(radius*Math.cos(rad),null,5); var y=radius*Math.sin(rad); var z=radius*Math.cos(rad); arr_point.push(new BABYLON.Vector3(x,y,z)); } arr_point.push(arr_point[0].clone());//首尾相連,不能這樣相連,否則變形時(shí)會(huì)多出一個(gè)頂點(diǎn)!!,看來(lái)這個(gè)多出的頂點(diǎn)無(wú)法去掉,只能在選取時(shí)額外處理它 return arr_point; } var arr_path=[];//核心數(shù)據(jù) //arr_point:?jiǎn)蝹€(gè)路徑的點(diǎn)數(shù)組,xstartl:第一個(gè)扁平路徑放在最左側(cè),spacing:路徑的間距,sumpath:一共使用幾條路徑, function MakeRibbon(arr_point,xstartl,spacing,sumpath,name) {//將一條圓環(huán)路徑擴(kuò)展成相互平行的多個(gè)圓環(huán)路徑,然后使用這些路徑生成條帶 arr_path=[]; for(var i=0;i<sumpath;i++)//對(duì)于每一條路徑 { var x=xstartl+spacing*i; //var arr=arr_point.concat(null);//為什么拷貝失靈了? //var [ ...arr ] = arr_point;//ES6的新擴(kuò)展運(yùn)算符?-》也不好使,因?yàn)閿?shù)組里的元素是指針?!! var len=arr_point.length; var arr=[]; for(var j=0;j<len;j++) { var obj=arr_point[j].clone(); obj.x=x; // arr.push(obj); } arr_path.push(arr); arr=null; } mesh_origin.dispose(); mesh_origin=BABYLON.MeshBuilder.CreateRibbon(name,{pathArray:arr_path,updatable:true,closePath:false,closeArray:false}); //mesh_origin=mesh;//用一個(gè)全局變量保存最終會(huì)被導(dǎo)出的mesh mesh_origin.sideOrientation=BABYLON.Mesh.DOUBLESIDE;//顯示網(wǎng)格的前后兩面 mesh_origin.material=mat_frame; mesh_origin.layerMask=2; }
編程中遇到的幾個(gè)問(wèn)題:
a、路徑中設(shè)置的坐標(biāo)值在實(shí)際顯示時(shí)可能發(fā)生微小的變化,比如5可能變成4.999999999999999999,但是似乎沒(méi)什么影響。
b、雖然圓環(huán)路徑分成12段應(yīng)該由12個(gè)頂點(diǎn)組成,但是如果只有12個(gè)頂點(diǎn),那么在調(diào)用CreateRibbon方法時(shí),如果把closePath參數(shù)設(shè)為true則Babylon.js會(huì)自動(dòng)添加一個(gè)不受控制的頂點(diǎn)來(lái)閉合圓環(huán)路徑,如果設(shè)為false,則路徑無(wú)法閉合。為此在第18行再添加一個(gè)與起始點(diǎn)
重合的頂點(diǎn)使圓環(huán)路徑閉合。
c、原計(jì)劃直接使用數(shù)組的concat方法復(fù)制arr_point數(shù)組產(chǎn)生更多的圓環(huán)路徑,但concat方法似乎只能對(duì)一維數(shù)組使用(棧?),而arr_point的每個(gè)元素都是一個(gè)BABYLON.Vector3對(duì)象,所以只好用for循環(huán)一個(gè)點(diǎn)一個(gè)點(diǎn)的生成路徑
d、MakeRing是一個(gè)生成圓環(huán)路徑的方法,使用者可以根據(jù)需要編寫(xiě)各種其他類(lèi)型的路徑生成方法。
3、調(diào)整網(wǎng)格的屬性:
可以對(duì)網(wǎng)格的屬性進(jìn)行一些調(diào)整,但是這些調(diào)整只在這個(gè)編輯器里生效,并不會(huì)被導(dǎo)出。
比如對(duì)網(wǎng)格材質(zhì)的調(diào)整:
function ChangeMaterial(mesh,mat) { mesh.material=mat; }
4、顯示網(wǎng)格頂點(diǎn)法線方向
代碼如下:
var lines_normal={}; /*ShowNormals(mesh_origin) DeleteMeshes([lines_normal]); * */ //顯示所有的頂點(diǎn)法線 function ShowNormals(mesh) { //DeleteMeshes(arr_line_normal); if(lines_normal.dispose) { lines_normal.dispose(); } //遍歷頂點(diǎn) var vb=mesh.geometry._vertexBuffers; var data_pos=vb.position._buffer._data;//頂點(diǎn)數(shù)據(jù) var data_mormal=vb.normal._buffer._data;//法線數(shù)據(jù) var len=data_pos.length; var lines=[]; for(var i=0;i<len;i+=3) {//CreateLineSystem使用一個(gè)網(wǎng)格包含很多分立的線段(路徑),CreateLines則是一條首尾相連的路徑 // var vec=new BABYLON.Vector3(data_pos[i],data_pos[i+1],data_pos[i+2]); var vec2=vec.clone().add(new BABYLON.Vector3(data_mormal[i],data_mormal[i+1],data_mormal[i+2]).normalize().scale(1)); lines.push([vec,vec2]); } lines_normal=new BABYLON.MeshBuilder.CreateLineSystem("lines_normal",{lines:lines,updatable:false},scene); lines_normal.color=new BABYLON.Color3(1, 0, 0); }
Babylon.js內(nèi)置的CreateLineSystem方法可以方便的建立一個(gè)由很多條線段組成的網(wǎng)格。
5、刪除網(wǎng)格
代碼如下:
function DeleteMeshes(arr)//假設(shè)一個(gè)數(shù)組里的都是mesh,徹底清空它 { var len=arr.length; for(var i=0;i<len;i++) { if(arr[i].dispose) arr[i].dispose(); } arr=[]; }
值得注意的是,直接在控制臺(tái)里執(zhí)行mesh.dispose()并不生效。
這里也體現(xiàn)出CreateLineSystem的方便之處——?jiǎng)h除法線時(shí)只需要dispose一個(gè)對(duì)象。
6、鼠標(biāo)移入時(shí)顯示三角形信息
鼠標(biāo)動(dòng)作監(jiān)聽(tīng)代碼:
mesh_origin=new BABYLON.Mesh("mesh_origin",scene);//建立一個(gè)空的初始網(wǎng)格對(duì)象 mesh_surface=new BABYLON.Mesh("mesh_surface",scene); mesh_surface0=new BABYLON.Mesh("mesh_surface0",scene); if(true) {//初始化三個(gè)GUI標(biāo)簽 label_index1=new BABYLON.GUI.TextBlock(); label_index1.text = "label_index1"; label_index1.color="white"; label_index1.isVisible=false;//初始化時(shí)標(biāo)簽不可見(jiàn) //label_index1.linkWithMesh(mesh_surface0);//TextBlock并不是頂層元素 advancedTexture.addControl(label_index1); label_index2=new BABYLON.GUI.TextBlock(); label_index2.text = "label_index2"; label_index2.color="white"; label_index2.isVisible=false; //label_index2.linkWithMesh(mesh_surface0); advancedTexture.addControl(label_index2); label_index3=new BABYLON.GUI.TextBlock(); label_index3.text = "label_index3"; label_index3.color="white"; label_index3.isVisible=false; //label_index3.linkWithMesh(mesh_surface0); advancedTexture.addControl(label_index3); } //監(jiān)聽(tīng)鼠標(biāo)移動(dòng) canvas.addEventListener("mousemove", function(evt){ var pickInfo = scene.pick(scene.pointerX, scene.pointerY,null,null,camera0);//如果同時(shí)有多個(gè)激活的相機(jī),則要明確的指出使用哪個(gè)相機(jī) //cancelPropagation(evt); //cancelEvent(evt); label_index1.isVisible=false; label_index2.isVisible=false; label_index3.isVisible=false; if(mesh_surface.dispose) { mesh_surface.dispose(); } if(mesh_surface0.dispose) { mesh_surface0.dispose(); } if(pickInfo.hit&&(pickInfo.pickedMesh.name=="mesh_origin"||pickInfo.pickedMesh.name=="mesh_ribbon"))//找到鼠標(biāo)所在的三角形并重繪之 { //在鼠標(biāo)所指的地方繪制一個(gè)三角形,表示被選中的三角形 var faceId=pickInfo.faceId; var pickedMesh=pickInfo.pickedMesh; var indices=[pickedMesh.geometry._indices[faceId*3] ,pickedMesh.geometry._indices[faceId*3+1],pickedMesh.geometry._indices[faceId*3+2]]; var vb=pickedMesh.geometry._vertexBuffers; var position=vb.position._buffer._data; var normal=vb.normal._buffer._data; var uv=vb.uv._buffer._data; var len=arr_path[0].length; var p1={index:indices[0],position:[position[indices[0]*3],position[indices[0]*3+1],position[indices[0]*3+2]] ,normal:[normal[indices[0]*3],normal[indices[0]*3+1],normal[indices[0]*3+2]] ,uv:[uv[indices[0]*2],uv[indices[0]*2+1]],ppi:("1:"+(Math.round(indices[0]/len)+0))+"-"+(indices[0]%len)};//pathpointindex var p2={index:indices[1],position:[position[indices[1]*3],position[indices[1]*3+1],position[indices[1]*3+2]] ,normal:[normal[indices[1]*3],normal[indices[1]*3+1],normal[indices[1]*3+2]] ,uv:[uv[indices[1]*2],uv[indices[1]*2+1]],ppi:("2:"+(Math.round(indices[1]/len)+0))+"-"+(indices[1]%len)}; var p3={index:indices[2],position:[position[indices[2]*3],position[indices[2]*3+1],position[indices[2]*3+2]] ,normal:[normal[indices[2]*3],normal[indices[2]*3+1],normal[indices[2]*3+2]] ,uv:[uv[indices[2]*2],uv[indices[2]*2+1]],ppi:("3:"+(Math.round(indices[2]/len)+0))+"-"+(indices[2]%len)}; var po=[(p1.position[0]+p2.position[0]+p3.position[0])/3,(p1.position[1]+p2.position[1]+p3.position[1])/3,(p1.position[2]+p2.position[2]+p3.position[2])/3]; var numm=2; mesh_surface0=newland.make_tryangle(p1,p2,p3,"mesh_surface1",scene);//使用三個(gè)點(diǎn)繪制一個(gè)三角形 mesh_surface0.material=mat_green; mesh_surface0.sideOrientation==BABYLON.Mesh.DOUBLESIDE; mesh_surface0.layerMask=2; label_index1.isVisible=true; //label_index1.layerMask=2;//這個(gè)屬性對(duì)于gui被管對(duì)象并不生效? label_index1.text=p1.ppi;//ppi表示這個(gè)頂點(diǎn)是三角形的第幾個(gè)頂點(diǎn),以及這個(gè)頂點(diǎn)位于第幾條路徑的第幾個(gè)位置 var pos1=new BABYLON.Vector3(p1.position[0],p1.position[1],p1.position[2]); label_index1.moveToVector3(pos1,scene); label_index1.itspos=pos1; label_index2.isVisible=true; label_index2.text=p2.ppi; var pos2=new BABYLON.Vector3(p2.position[0],p2.position[1],p2.position[2]); label_index2.moveToVector3(pos2,scene); label_index2.itspos=pos2; label_index3.isVisible=true; label_index3.text=p3.ppi; var pos3=new BABYLON.Vector3(p3.position[0],p3.position[1],p3.position[2]); label_index3.moveToVector3(pos3,scene); label_index3.itspos=pos3; //將三個(gè)點(diǎn)移動(dòng)到場(chǎng)景的中心 pt=[(p1.position[0]-po[0])*numm,(p1.position[1]-po[1])*numm,(p1.position[2]-po[2])*numm]; p1.position=pt; pt=[(p2.position[0]-po[0])*numm,(p2.position[1]-po[1])*numm,(p2.position[2]-po[2])*numm]; p2.position=pt; pt=[(p3.position[0]-po[0])*numm,(p3.position[1]-po[1])*numm,(p3.position[2]-po[2])*numm]; p3.position=pt; //在場(chǎng)景的中心再繪制一個(gè)大一倍的三角形 mesh_surface=newland.make_tryangle(p1,p2,p3,"mesh_surface",scene); mesh_surface.material=mat_green; mesh_surface.sideOrientation==BABYLON.Mesh.DOUBLESIDE; mesh_surface.layerMask=1;//遮罩層次是1 } },false);
需要注意的有以下幾點(diǎn):
a、鼠標(biāo)指向時(shí)顯示的是這個(gè)點(diǎn)在arr_path中的索引,而不是在“頂點(diǎn)數(shù)據(jù)對(duì)象”中的索引!
b、最初的計(jì)劃是像魔獸爭(zhēng)霸3一樣在窗口的左下角建立一個(gè)遮罩層次為1的小視口,用來(lái)特寫(xiě)被選中的三角形(mesh_surface),但是后來(lái)發(fā)現(xiàn)Babylon.js的GUI不能設(shè)置遮罩層次,也不能像鼠標(biāo)選取一樣設(shè)置相對(duì)于哪一個(gè)相機(jī),所以取消了左下角的小視口。
如果需要添加視口,代碼如下:
var mm = new BABYLON.FreeCamera("minimap", new BABYLON.Vector3(0,0,-20), scene); mm.layerMask = 1; var xstart = 0.0, ystart = 0.1;//意為占屏幕寬高的比例 var width = 0.2, height = 0.2; //視口邊界從左下角開(kāi)始 mm.viewport = new BABYLON.Viewport( xstart, ystart, width, height ); scene.activeCameras.push(mm);var mm = new BABYLON.FreeCamera("minimap", new BABYLON.Vector3(0,0,-20), scene); mm.layerMask = 1; var xstart = 0.0, ystart = 0.1;//意為占屏幕寬高的比例 var width = 0.2, height = 0.2; //視口邊界從左下角開(kāi)始 mm.viewport = new BABYLON.Viewport( xstart, ystart, width, height ); scene.activeCameras.push(mm);
c、需要注意的是GUI是一次性繪制在視口上的,默認(rèn)情況下即使相關(guān)的網(wǎng)格位置發(fā)生變化,GUI仍然會(huì)保持在視口上最初的位置。linkWithMesh方法可以將GUI和網(wǎng)格綁定起來(lái),但是似乎只能對(duì)部分“底層的”GUI類(lèi)型生效,為了使得標(biāo)簽跟隨頂點(diǎn)移動(dòng),需要手動(dòng)在每一幀渲染之前更新標(biāo)簽的位置:
scene.registerBeforeRender(function() { if(scene.isReady()) { if(label_index1.isVisible==true)//不斷刷新gui的位置 {//在具有多個(gè)激活相機(jī)時(shí)選擇了錯(cuò)誤的參考相機(jī)?-》會(huì)強(qiáng)制選擇最新建立的相機(jī) label_index1.moveToVector3(label_index1.itspos,scene); label_index2.moveToVector3(label_index2.itspos,scene); label_index3.moveToVector3(label_index3.itspos,scene); } } });
7、選取頂點(diǎn):
3DsMax和Blender(Babylon.js庫(kù)在3D模型部分參考了Blender的許多設(shè)計(jì))都使用鼠標(biāo)在網(wǎng)格中選取需要修改的區(qū)域,我不是很習(xí)慣這種方式,所以考慮用JS函數(shù)代替鼠標(biāo)操作:
var lines_inpicked={};//線段系統(tǒng)對(duì)象,表示所有被選中點(diǎn)影響的線段 var arr_ij=[];//記錄被選中的點(diǎn)在arr_path中的索引的數(shù)組 /*DeleteMeshes([lines_inpicked]); * PickPoints([[0,0],[0,12],[1,1]],mesh_origin) * */ //選定一些頂點(diǎn) function PickPoints(arr,mesh) { if(arr_path.length==0) { alert("尚未生成路徑數(shù)組!"); return; } if(lines_inpicked.dispose) { lines_inpicked.dispose(); } //arr_ij=[]; //為了后面考慮,需要先對(duì)arr整體遍歷一遍,把首尾接口處關(guān)聯(lián)起來(lái) arr_ij=arr;//把路徑和點(diǎn)的位置記錄下來(lái) var vb=mesh.geometry._vertexBuffers; var data_pos=vb.position._buffer._data; var len_pos=data_pos.length; var data_index=mesh.geometry._indices; var len_index=data_index.length; var len=arr.length; var lines=[]; for(var i=0;i<len;i++)//對(duì)于每一個(gè)需要顯示的被選擇點(diǎn) { var int0=arr[i][0]; var int1=arr[i][1]; var vec=arr_path[int0][int1];//獲取到路徑數(shù)組中的一個(gè)Vector3對(duì)象 //假設(shè)路徑數(shù)組和頂點(diǎn)數(shù)據(jù)是一一對(duì)應(yīng)的?同時(shí)假設(shè)每一條路徑的長(zhǎng)度都和第一條相同 var arr_index=[int0*arr_path[0].length+int1];//所有位于所選位置的頂點(diǎn)在頂點(diǎn)數(shù)據(jù)對(duì)象中的索引 //下面遍歷網(wǎng)格的繪制索引,找出這個(gè)被選中的頂點(diǎn)每一次的使用情況 for(var j=0;j<len_index;j+=3)//根據(jù)頂點(diǎn)在三角形中的位置分三種情況討論 {//繪制出和這個(gè)頂點(diǎn)相關(guān)的所有線段,這樣繪制會(huì)有很?chē)?yán)重的線段重合!觀察是否對(duì)性能有很大的影響 var len2=arr_index.length; for(var k=0;k<len2;k++) { if(arr_index[k]==data_index[j])//三角形的第一個(gè)頂點(diǎn) {//把這個(gè)頂點(diǎn)和三角形中的另兩個(gè)頂點(diǎn)用黃線連起來(lái) var num2=data_index[j+1]*3; var num3=data_index[j+2]*3; var vec2=new BABYLON.Vector3(data_pos[num2],data_pos[num2+1],data_pos[num2+2]); var vec3=new BABYLON.Vector3(data_pos[num3],data_pos[num3+1],data_pos[num3+2]); lines.push([vec,vec2]); lines.push([vec,vec3]); } else if(arr_index[k]==data_index[j+1])//三角形的第一個(gè)頂點(diǎn) { var num2=data_index[j]*3; var num3=data_index[j+2]*3; var vec2=new BABYLON.Vector3(data_pos[num2],data_pos[num2+1],data_pos[num2+2]); var vec3=new BABYLON.Vector3(data_pos[num3],data_pos[num3+1],data_pos[num3+2]); lines.push([vec,vec2]); lines.push([vec,vec3]); } else if(arr_index[k]==data_index[j+2])//三角形的第一個(gè)頂點(diǎn) { var num2=data_index[j]*3; var num3=data_index[j+1]*3; var vec2=new BABYLON.Vector3(data_pos[num2],data_pos[num2+1],data_pos[num2+2]); var vec3=new BABYLON.Vector3(data_pos[num3],data_pos[num3+1],data_pos[num3+2]); lines.push([vec,vec2]); lines.push([vec,vec3]); } } } } lines_inpicked=new BABYLON.MeshBuilder.CreateLineSystem("lines_normal",{lines:lines,updatable:false},scene); lines_inpicked.color=new BABYLON.Color3(1, 1, 0); } //需要一些選擇選定頂點(diǎn)的方法
有以下幾處需要注意:
a、因?yàn)镸akeRing生成的路徑里包括兩個(gè)重合的點(diǎn)(首尾),為了保證變形后的網(wǎng)格仍然連續(xù),在選擇時(shí)這兩個(gè)重合的點(diǎn)必須同時(shí)被選中,經(jīng)過(guò)考慮,規(guī)定這個(gè)確保首尾重合點(diǎn)同時(shí)選中的操作在生成arr數(shù)組參數(shù)時(shí)進(jìn)行。
b、因?yàn)榛A(chǔ)網(wǎng)格mesh_origin的每一個(gè)頂點(diǎn)恰好位置不同,所以一開(kāi)始我以為“與被選中的點(diǎn)的位置相同的點(diǎn)”應(yīng)該具有與被選中點(diǎn)相同的變形效果,但是事實(shí)上經(jīng)過(guò)網(wǎng)格變形后,完全可能出現(xiàn)兩個(gè)不相干的頂點(diǎn)位于同一位置的情況,這時(shí)應(yīng)用相同的變形效果會(huì)出現(xiàn)錯(cuò)誤,比如人可以把手放在腿上,這時(shí)手和腿的接觸點(diǎn)具有相同的位置,但如果因此對(duì)這兩個(gè)點(diǎn)應(yīng)用相同的變形效果,就很詭異了。同時(shí)如前文所說(shuō),有時(shí)arr_path中存儲(chǔ)的位置和頂點(diǎn)數(shù)據(jù)中存儲(chǔ)的位置可能出現(xiàn)微小的偏差。
最后采取的方法是將arr_path中的數(shù)組轉(zhuǎn)化為頂點(diǎn)數(shù)據(jù)對(duì)象中的數(shù)組。
c、這里直接選取了幾個(gè)點(diǎn)進(jìn)行變形,還需要編寫(xiě)一些按照某種規(guī)則批量選取點(diǎn)的方法,也希望大家能幫我想一想怎樣選取頂點(diǎn)比較方便。
8、網(wǎng)格變形:
代碼如下:
/* * TransVertex(mesh_origin,arr_ij,BABYLON.Matrix.RotationX(Math.PI/2)) *TransVertex(mesh_origin,arr_ij,BABYLON.Matrix.Translation(0.5,0,0)) * */ //移動(dòng)選定的頂點(diǎn),同時(shí)更新網(wǎng)格、選取線、法線 function TransVertex(mesh,arr,matrix) { var len=arr.length; for(var i=0;i<len;i++)//移動(dòng)路徑數(shù)組里的每個(gè)頂點(diǎn) { //var vec=arr_path[arr[i][0]][arr[i][1]]; arr_path[arr[i][0]][arr[i][1]]=BABYLON.Vector3.TransformCoordinates(arr_path[arr[i][0]][arr[i][1]],matrix); } //更新網(wǎng)格,發(fā)現(xiàn)設(shè)置了closePath:true之后在頂點(diǎn)數(shù)據(jù)里還是會(huì)自動(dòng)補(bǔ)上一個(gè)接續(xù)點(diǎn),這還不如一開(kāi)始自己添加!起碼自己添加可以保證路徑數(shù)組和頂點(diǎn)數(shù)據(jù)的一致性!!!! mesh=BABYLON.MeshBuilder.CreateRibbon(mesh.name,{pathArray:arr_path,updatable:true,instance:mesh,closePath:false,closeArray:false}); //上面的更新重繪是默認(rèn)重算法線方向的,這與直接修改頂點(diǎn)數(shù)據(jù)不同 //清空法線 DeleteMeshes([lines_normal]); //更新選取頂點(diǎn)表示 PickPoints(arr,mesh); } //需要一些生成變化矩陣的方法
其實(shí)這里移動(dòng)的頂點(diǎn)和前面選擇的點(diǎn)沒(méi)有關(guān)系,不選擇也可以直接變形,只不過(guò)不會(huì)有黃線顯示罷了。這里也同樣需要一些生成更復(fù)雜的變換矩陣的方法。
9、導(dǎo)出:
導(dǎo)出方法的調(diào)用如下:
/*ExportMesh("1",mat_blue)*/ function ExportMesh(filename,mat) { try{ newland.ExportBabylonMesh([mesh_origin],filename,mat); } catch(e) { console.error(e) } }
函數(shù)的第一個(gè)參數(shù)是導(dǎo)出后的文件名(不包括擴(kuò)展名),第二個(gè)參數(shù)是網(wǎng)格使用的材質(zhì)對(duì)象(暫時(shí)只支持簡(jiǎn)單的顏色材質(zhì))。
其中ExportBabylonMesh是我編寫(xiě)的newland庫(kù)中的一個(gè)方法,這個(gè)方法在前面關(guān)與3D模型的文章中也有提到過(guò)(https://www.cnblogs.com/ljzc002/p/6884252.html),這個(gè)方法整體上沒(méi)有太大的改變。但是在舊版的Babylon.js中,頂點(diǎn)數(shù)據(jù)對(duì)象中的data屬性是數(shù)組類(lèi)型(?),而在新版中data屬性則是typedArray類(lèi)型,雖然這兩種數(shù)據(jù)類(lèi)型看起來(lái)很像,但在使用JSON.stringify(arr)轉(zhuǎn)化為JSON字符串時(shí),對(duì)于同樣的數(shù)據(jù)[1,2,3],前者會(huì)轉(zhuǎn)化為"[1,2,3]"后者則是"{"0":1,"1":2,"2":3}"!
在導(dǎo)入模型文件時(shí)后者會(huì)報(bào)錯(cuò):“attempt to access out of range vertices in attribute 0”,這意味著在導(dǎo)入模型時(shí)頂點(diǎn)數(shù)據(jù)數(shù)組沒(méi)有正確的載入,我的解決方案是導(dǎo)出前將data屬性強(qiáng)制轉(zhuǎn)化為數(shù)組類(lèi)型:
//將TypedArray轉(zhuǎn)化為普通array newland.BuffertoArray2=function(arr) { var arr2=[]; var len=arr.length; for(var i=0;i<len;i++) { arr2.push(arr[i]); } return arr2; }
對(duì)于舊版的Babylon.js應(yīng)該可以不加修改的使用這個(gè)方法,因?yàn)閠ypedArray也具有數(shù)組的大部分功能。
10、導(dǎo)入:
在另一個(gè)頁(yè)面里實(shí)現(xiàn)模型導(dǎo)入功能,這個(gè)頁(yè)面同樣在“基礎(chǔ)場(chǎng)景”的基礎(chǔ)上編寫(xiě),其中導(dǎo)入方法如下:
function ImportMesh(objname,filepath,filename) { BABYLON.SceneLoader.ImportMesh(objname, filepath, filename, scene , function (newMeshes, particleSystems, skeletons) {//載入完成的回調(diào)函數(shù) if(mesh_origin&&mesh_origin.dispose) { mesh_origin.dispose(); } mesh_origin=newMeshes[0]; //mesh_origin.material=mat_frame; //mesh_origin.layerMask=2; } ); }
以上是怎么使用Chrome控制臺(tái)進(jìn)行3D模型編輯的實(shí)現(xiàn)的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對(duì)大家有幫助,更多相關(guān)知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!
網(wǎng)頁(yè)標(biāo)題:怎么使用Chrome控制臺(tái)進(jìn)行3D模型編輯的實(shí)現(xiàn)
當(dāng)前地址:http://m.2m8n56k.cn/article4/gsejoe.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供全網(wǎng)營(yíng)銷(xiāo)推廣、網(wǎng)站建設(shè)、建站公司、動(dòng)態(tài)網(wǎng)站、ChatGPT、微信公眾號(hào)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話(huà):028-86922220;郵箱:[email protected]。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)