使用Chrome控制台進行3D模型編輯的嘗試

来源:https://www.cnblogs.com/ljzc002/archive/2018/07/25/9353101.html
-Advertisement-
Play Games

前言:3D模型編輯的核心是對頂點位置和紋理顏色的編輯,這個研究的目的在於尋找一種通過編程方式直接對模型進行編輯的方法,這種編輯方法和時下流行的通過滑鼠點選、拖拽進行編輯的方法之間的關係,和前端編程中“程式員編寫靜態網頁”與“美工進行網頁切圖”之間的關係很相似。 一、工具用法: 1、訪問 https: ...


前言:3D模型編輯的核心是對頂點位置和紋理顏色的編輯,這個研究的目的在於尋找一種通過編程方式直接對模型進行編輯的方法,這種編輯方法和時下流行的通過滑鼠點選、拖拽進行編輯的方法之間的關係,和前端編程中“程式員編寫靜態網頁”與“美工進行網頁切圖”之間的關係很相似。

一、工具用法:

1、訪問 https://ljzc002.github.io/SnowMiku/HTML/MakeRibbon.html打開條帶網格生成器頁面

在場景世界坐標系的(0,-10,0),(0,0,0),(0,10,0)處各有一個綠色小球作為參考點,使用上下左右和滑鼠拖動可以進行場景漫游。

2、按F12鍵打開Chrome控制台,在控制臺中輸入:MakeRibbon(MakeRing(5,12),-10,2,11,"mesh_ribbon")回車:

在場景中繪製了一個半徑為5,曲面細分度為12,左端位於-10,每兩個圓環間距2,共由11個圓環組成的圓柱面。

拉近查看:

3、輸入ShowNormals(mesh_origin)將用紅色線段顯示每個頂點的法線方向

輸入DeleteMeshes([lines_normal])可以刪除所有的法線,輸入DeleteMeshes([mesh_origin])則刪除圓柱面網格。

4、滑鼠移入網格上的三角形,會顯示三角形的頂點信息:

其中“1:2-5”表示這是三角形的第一個頂點,這個頂點位於索引是2的圓環上(第三個圓環),這個頂點在圓環中的索引是5(也就是第六個頂點)。

5、輸入PickPoints([[2,5],[3,5],[2,6]],mesh_origin)可以選定這些頂點

被選中頂點所影響的所有邊框線標示為黃色,這個“選中”只是改變外觀而已。

6、輸入TransVertex(mesh_origin,arr_ij,BABYLON.Matrix.Translation(0,0,-10))將所選的頂點向z軸負方向移動10,被移動的頂點和前面選中的頂點其實沒有關係,其中arr_ij也可以直接用索引數組[[2,5],[3,5],[2,6]]代替。

另一類變形可以通過輸入:TransVertex(mesh_origin,arr_ij,BABYLON.Matrix.RotationX(Math.PI/2))實現,這可以把被選中的頂點繞X中旋轉90度。

輸入DeleteMeshes([lines_inpicked])取消被選中的效果,輸入ChangeMaterial(mesh_origin,mat_blue)將邊框換成藍色紋理:

可以看到變形後的效果,接下來還可以繼續選擇頂點並變形

7、輸入ExportMesh("1",mat_blue),以txt格式導出babylon模型文件,文件名為“1.txt”

 8、將導出的txt改名為9.babylon後放入網站目錄中,訪問https://ljzc002.github.io/SnowMiku/HTML/LoadBabylon.html模型預覽頁面,在控制台輸入ImportMesh("","../ASSETS/SCENE/","9.babylon")即可載入剛纔導出的模型。

二、編程思路:

1、首先要建立一個可以進行各種測試的基礎場景,使用的代碼如下:

  1 <!DOCTYPE html>
  2 <html lang="en">
  3 <head>
  4     <meta charset="UTF-8">
  5     <title>建立一個條帶網格生成器,能夠輸入參數生成起始條帶,然後通過命令行選取並修改pathArray,最後導出生成的條帶</title>
  6     <link href="../CSS/newland.css" rel="stylesheet">
  7     <link href="../CSS/stat.css" rel="stylesheet">
  8     <script src="../JS/MYLIB/Events.js"></script>
  9     <script src="../JS/MYLIB/FileText.js"></script>
 10     <script src="../JS/MYLIB/View.js"></script>
 11     <script src="../JS/LIB/babylon.32.all.maxs.js"></script><!--V3.2的穩定版本-->
 12     <script src="../JS/MYLIB/newland.js"></script>
 13     <script src="../JS/LIB/stat.js"></script>
 14 </head>
 15 <body>
 16 <div id="div_allbase">
 17     <canvas id="renderCanvas"></canvas>
 18     <div id="fps" style="z-index: 301;"></div>
 19 </div>
 20 </body>
 21 <script>
 22     var VERSION=1.0,AUTHOR="[email protected]";
 23     var machine,canvas,engine,scene,gl,MyGame={};
 24     canvas = document.getElementById("renderCanvas");
 25     engine = new BABYLON.Engine(canvas, true);
 26     gl=engine._gl;//可以結合使用原生OpenGL和Babylon.js;
 27     scene = new BABYLON.Scene(engine);
 28     var divFps = document.getElementById("fps");
 29 
 30     window.onload=beforewebGL;
 31     function beforewebGL()
 32     {
 33         if(engine._webGLVersion==2.0)//輸出ES版本
 34         {
 35             console.log("ES3.0");
 36         }
 37         else{
 38             console.log("ES2.0");
 39         }
 40         //MyGame=new Game(0,"first_pick","","http://127.0.0.1:8082/");
 41         /*0-startWebGL
 42          * */
 43         webGLStart();
 44     }
 45     //從下麵開始分成簡單測試和對象框架兩種架構
 46     //簡單測試
 47     //全局對象
 48     var light0//全局光源
 49             ,camera0//主相機
 50             ;
 51     //四種常用材質
 52     var mat_frame = new BABYLON.StandardMaterial("mat_frame", scene);
 53     mat_frame.wireframe = true;
 54     var mat_red = new BABYLON.StandardMaterial("mat_red", scene);
 55     mat_red.diffuseColor = new BABYLON.Color3(1, 0, 0);
 56     mat_red.backFaceCulling=false;
 57     var mat_green = new BABYLON.StandardMaterial("mat_green", scene);
 58     mat_green.diffuseColor = new BABYLON.Color3(0, 1, 0);
 59     mat_green.backFaceCulling=false;
 60     var mat_blue = new BABYLON.StandardMaterial("mat_blue", scene);
 61     mat_blue.diffuseColor = new BABYLON.Color3(0, 0, 1);
 62     mat_blue.backFaceCulling=false;
 63     var mesh_origin;
 64     var advancedTexture=BABYLON.GUI.AdvancedDynamicTexture.CreateFullscreenUI("ui1");//全屏GUI
 65     function webGLStart()
 66     {
 67         window.addEventListener("resize", function () {//自動調整視口尺寸
 68             engine.resize();
 69         });
 70         camera0 =new BABYLON.FreeCamera("FreeCamera", new BABYLON.Vector3(0, 0, -80), scene);
 71         camera0.attachControl(canvas, true);
 72         camera0.speed=0.5;//相機移動速度是預設速度的一半
 73         camera0.minZ=0.01;//相機位置距前視錐截面的距離,也就是說到相機距離小於0.01的圖元都不會顯示,這個值不能過小,否則Babylon.js內置的滑鼠選取將失效
 74         camera0.layerMask=2;//相機的遮罩層次,這個相機將只能顯示遮罩層次同為2的網格,如果不設置這個屬性,似乎可以顯示所有遮罩層次的網格
 75         scene.activeCameras.push(camera0);//將相機加入活躍相機列表,預設情況下Babylon.js只使用一個活躍相機,但是也可以強行使用多個
 76         light0 = new BABYLON.HemisphericLight("Hemi0", new BABYLON.Vector3(0, 1, 0), scene);//半球光源
 77      //三個參照物,MeshBuilder是新版Babylon.js中使用的網格構建對象,之前翻譯入門教程時還沒有這個對象,它的特點是把一大堆參數統一整理到一個option參數中
 78         var mesh_base=new BABYLON.MeshBuilder.CreateSphere("mesh_base",{diameter:1},scene);
 79         mesh_base.material=mat_green;
 80         mesh_base.position.x=0;
 81         mesh_base.layerMask=2;
 82         var mesh_base1=new BABYLON.MeshBuilder.CreateSphere("mesh_base1",{diameter:1},scene);
 83         mesh_base1.position.y=10;
 84         mesh_base1.position.x=0;
 85         mesh_base1.material=mat_green;
 86         mesh_base1.layerMask=2;
 87         var mesh_base2=new BABYLON.MeshBuilder.CreateSphere("mesh_base2",{diameter:1},scene);
 88         mesh_base2.position.y=-10;
 89         mesh_base2.position.x=0;
 90         mesh_base2.material=mat_green;
 91         mesh_base2.layerMask=2;
 92       。
 93       。
 94       。
 95         MyBeforeRender();
 96     }
 97     function MyBeforeRender()
 98     {
 99         scene.registerBeforeRender(function() {
100             if(scene.isReady())
101             {
102                 
103 
104 105 106 107 108             }
109         });
110         engine.runRenderLoop(function () {
111             engine.hideLoadingUI();
112             if (divFps) {
113                 // Fps
114                 divFps.innerHTML = engine.getFps().toFixed() + " fps";
115             }
116             scene.render();
117         });
118 
119     } 
120 </script>
121 </html>   

這個3D場景包括了簡單測試所需要的一些基本元素,這裡使用的是包含全部組件的未壓縮版Babylon.js庫,在實際使用中考慮到節省帶寬,可以使用Babylon.js官網提供的工具定製精簡版或壓縮版的Babylon.js庫

2、建立一個基礎網格

計劃通過對一個基礎網格進行頂點變換來產生各種各樣的簡單模型,在Babylon.js中“條帶”是一種非常適合頂點變換的網格類型,Babylon.js官方教程中有關於條帶構造和變形的文檔,可以在這裡下載中文翻譯http://down.51cto.com/data/2449757。

用來建立基礎網格的代碼如下:

 1     //下麵這些函數都通過控制台調用
 2     //在ZoY平面里建立一個圓環路徑
 3     //radius:半徑,sumpoint:使用幾個點
 4     function MakeRing(radius,sumpoint)
 5     {
 6         var arr_point=[];
 7         var radp=Math.PI*2/sumpoint;
 8         for(var i=0.0;i<sumpoint;i++)
 9         {
10             var x=0;
11             var rad=radp*i;
12             //var y=sswr(radius*Math.sin(rad),null,5);//在這裡需要降低一些精確度?否則Babylon.js在計算頂點數據時可能和這裡不一致?
13             //var z=sswr(radius*Math.cos(rad),null,5);
14             var y=radius*Math.sin(rad);
15             var z=radius*Math.cos(rad);
16             arr_point.push(new BABYLON.Vector3(x,y,z));
17         }
18         arr_point.push(arr_point[0].clone());//首尾相連,不能這樣相連,否則變形時會多出一個頂點!!,看來這個多出的頂點無法去掉,只能在選取時額外處理它
19         return arr_point;
20     }
21     var arr_path=[];//核心數據
22     //arr_point:單個路徑的點數組,xstartl:第一個扁平路徑放在最左側,spacing:路徑的間距,sumpath:一共使用幾條路徑,
23     function MakeRibbon(arr_point,xstartl,spacing,sumpath,name)
24     {//將一條圓環路徑擴展成相互平行的多個圓環路徑,然後使用這些路徑生成條帶
25         arr_path=[];
26         for(var i=0;i<sumpath;i++)//對於每一條路徑
27         {
28             var x=xstartl+spacing*i;
29             //var arr=arr_point.concat(null);//為什麼拷貝失靈了?
30             //var [ ...arr ] = arr_point;//ES6的新擴展運算符?-》也不好使,因為數組裡的元素是指針?!!
31             var len=arr_point.length;
32             var arr=[];
33             for(var j=0;j<len;j++)
34             {
35                 var obj=arr_point[j].clone();
36                 obj.x=x;
37                 //
38                 arr.push(obj);
39             }
40             arr_path.push(arr);
41             arr=null;
42         }
43         mesh_origin.dispose();
44         mesh_origin=BABYLON.MeshBuilder.CreateRibbon(name,{pathArray:arr_path,updatable:true,closePath:false,closeArray:false});
45         //mesh_origin=mesh;//用一個全局變數保存最終會被導出的mesh
46         mesh_origin.sideOrientation=BABYLON.Mesh.DOUBLESIDE;//顯示網格的前後兩面
47         mesh_origin.material=mat_frame;
48         mesh_origin.layerMask=2;
49     }

編程中遇到的幾個問題:

a、路徑中設置的坐標值在實際顯示時可能發生微小的變化,比如5可能變成4.999999999999999999,但是似乎沒什麼影響。

b、雖然圓環路徑分成12段應該由12個頂點組成,但是如果只有12個頂點,那麼在調用CreateRibbon方法時,如果把closePath參數設為true則Babylon.js會自動添加一個不受控制的頂點來閉合圓環路徑,如果設為false,則路徑無法閉合。為此在第18行再添加一個與起始點

重合的頂點使圓環路徑閉合。

c、原計劃直接使用數組的concat方法複製arr_point數組產生更多的圓環路徑,但concat方法似乎只能對一維數組使用(棧?),而arr_point的每個元素都是一個BABYLON.Vector3對象,所以只好用for迴圈一個點一個點的生成路徑

d、MakeRing是一個生成圓環路徑的方法,使用者可以根據需要編寫各種其他類型的路徑生成方法。

3、調整網格的屬性:

可以對網格的屬性進行一些調整,但是這些調整隻在這個編輯器里生效,並不會被導出。

比如對網格材質的調整:

1 function ChangeMaterial(mesh,mat)
2     {
3         mesh.material=mat;
4     }

4、顯示網格頂點法線方向

代碼如下:

 1 var lines_normal={};
 2     /*ShowNormals(mesh_origin)
 3      DeleteMeshes([lines_normal]);
 4     * */
 5     //顯示所有的頂點法線
 6     function ShowNormals(mesh)
 7     {
 8         //DeleteMeshes(arr_line_normal);
 9         if(lines_normal.dispose)
10         {
11             lines_normal.dispose();
12         }
13         //遍歷頂點
14         var vb=mesh.geometry._vertexBuffers;
15         var data_pos=vb.position._buffer._data;//頂點數據
16         var data_mormal=vb.normal._buffer._data;//法線數據
17         var len=data_pos.length;
18         var lines=[];
19         for(var i=0;i<len;i+=3)
20         {//CreateLineSystem使用一個網格包含很多分立的線段(路徑),CreateLines則是一條首尾相連的路徑
21             //
22             var vec=new BABYLON.Vector3(data_pos[i],data_pos[i+1],data_pos[i+2]);
23             var vec2=vec.clone().add(new BABYLON.Vector3(data_mormal[i],data_mormal[i+1],data_mormal[i+2]).normalize().scale(1));
24             lines.push([vec,vec2]);
25         }
26         lines_normal=new BABYLON.MeshBuilder.CreateLineSystem("lines_normal",{lines:lines,updatable:false},scene);
27         lines_normal.color=new BABYLON.Color3(1, 0, 0);
28     }

Babylon.js內置的CreateLineSystem方法可以方便的建立一個由很多條線段組成的網格。

5、刪除網格

代碼如下:

 1 function DeleteMeshes(arr)//假設一個數組裡的都是mesh,徹底清空它
 2     {
 3         var len=arr.length;
 4         for(var i=0;i<len;i++)
 5         {
 6             if(arr[i].dispose)
 7                 arr[i].dispose();
 8         }
 9         arr=[];
10     }

值得註意的是,直接在控制台里執行mesh.dispose()並不生效。

這裡也體現出CreateLineSystem的方便之處——刪除法線時只需要dispose一個對象。

6、滑鼠移入時顯示三角形信息

滑鼠動作監聽代碼:

 1 mesh_origin=new BABYLON.Mesh("mesh_origin",scene);//建立一個空的初始網格對象
 2         mesh_surface=new BABYLON.Mesh("mesh_surface",scene);
 3         mesh_surface0=new BABYLON.Mesh("mesh_surface0",scene);
 4         if(true)
 5         {//初始化三個GUI標簽
 6             label_index1=new BABYLON.GUI.TextBlock();
 7             label_index1.text = "label_index1";
 8             label_index1.color="white";
 9             label_index1.isVisible=false;//初始化時標簽不可見
10             //label_index1.linkWithMesh(mesh_surface0);//TextBlock並不是頂層元素
11             advancedTexture.addControl(label_index1);
12             label_index2=new BABYLON.GUI.TextBlock();
13             label_index2.text = "label_index2";
14             label_index2.color="white";
15             label_index2.isVisible=false;
16             //label_index2.linkWithMesh(mesh_surface0);
17             advancedTexture.addControl(label_index2);
18             label_index3=new BABYLON.GUI.TextBlock();
19             label_index3.text = "label_index3";
20             label_index3.color="white";
21             label_index3.isVisible=false;
22             //label_index3.linkWithMesh(mesh_surface0);
23             advancedTexture.addControl(label_index3);
24         }
25 //監聽滑鼠移動
26 canvas.addEventListener("mousemove", function(evt){
27 var pickInfo = scene.pick(scene.pointerX, scene.pointerY,null,null,camera0);//如果同時有多個激活的相機,則要明確的指出使用哪個相機
28             //cancelPropagation(evt);
29             //cancelEvent(evt);
30             label_index1.isVisible=false;
31             label_index2.isVisible=false;
32             label_index3.isVisible=false;
33             if(mesh_surface.dispose)
34             {
35                 mesh_surface.dispose();
36             }
37             if(mesh_surface0.dispose)
38             {
39                 mesh_surface0.dispose();
40             }
41             if(pickInfo.hit&&(pickInfo.pickedMesh.name=="mesh_origin"||pickInfo.pickedMesh.name=="mesh_ribbon"))//找到滑鼠所在的三角形並重繪之
42             {
43           //在滑鼠所指的地方繪製一個三角形,表示被選中的三角形
44                 var faceId=pickInfo.faceId;
45                 var pickedMesh=pickInfo.pickedMesh;
46                 var indices=[pickedMesh.geometry._indices[faceId*3]
47                     ,pickedMesh.geometry._indices[faceId*3+1],pickedMesh.geometry._indices[faceId*3+2]];
48                 var vb=pickedMesh.geometry._vertexBuffers;
49                 var position=vb.position._buffer._data;
50                 var normal=vb.normal._buffer._data;
51                 var uv=vb.uv._buffer._data;
52                 var len=arr_path[0].length;
53                 var p1={index:indices[0],position:[position[indices[0]*3],position[indices[0]*3+1],position[indices[0]*3+2]]
54                     ,normal:[normal[indices[0]*3],normal[indices[0]*3+1],normal[indices[0]*3+2]]
55                     ,uv:[uv[indices[0]*2],uv[indices[0]*2+1]],ppi:("1:"+(Math.round(indices[0]/len)+0))+"-"+(indices[0]%len)};//pathpointindex
56                 var p2={index:indices[1],position:[position[indices[1]*3],position[indices[1]*3+1],position[indices[1]*3+2]]
57
              
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1 常用指令 v-if指令 v-show指令 v-else指令 v-for指令 v-bind指令 v-model v-on指令 v-text指令 1.1 v-if是條件渲染指令,它根據表達式的真假來刪除和插入元素,它的基本語法如下: expression是一個返回bool值的表達式,表達式可以是一個 ...
  • 為了實現一個可定製化高的react工程,我們往往會自己搭建一個react工程。所以本文會從零開始搭建一個react腳手架工程。解釋webpack中配置的含義。 基於webpack 4.0 。包含 開發環境配置,生產環境配置,代碼分離,css提取,gzip壓縮,base64載入資源,打包分析,ssh一... ...
  • 1、對象的概念及分類 1.1 ECMAScript中沒有類,但定義了“對象”,邏輯上等價於其他程式設計語言中的類。 var o = new Object(); 1.2 本地對象(native object)和宿主對象(host object) 由ECMAScript定義的本地對象.獨立於宿主環境的 ...
  • 一、引入必要文件 下載地址:(https://github.com/nghuuphuoc/bootstrapvalidator/archive/v0.4.5.zip) 一、引入必要文件 下載地址:(https://github.com/nghuuphuoc/bootstrapvalidator/ar ...
  • [1]工作流程 [2]安裝和啟動 [3]入門示例 [4]項目結構 [5]編輯器界面 [6] ...
  • 前端時間,開發了一個資訊類的項目,但銷售部門進行微信推廣時,分享的鏈接直接是網頁鏈接加分享符號,即難看又不正規,於是研究了一下微信自定義的分享功能 前期準備工作: 1.認證公眾號的appId,appSecret 2.各種獲取微信信息鏈接(此部分查找微信自定義分享API,地址:https://mp.w ...
  • 一、在app.module.ts模塊中,註入JsonpModule模塊 二、創建服務httpService,並註入jsonp和map(分裝好的服務 可以直接調用) 三、業務組件調用httpService let newUrl = localServer + "/search"; //查詢網址 let ...
  • 如上,tab標簽選中加了預設樣式,但是其他無論怎麼換,首頁都是預設選中的樣式 這裡給index的路由加一個exact就可以了。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...