用編程方式編寫Babylon格式的宇宙飛船3D模型

  使用上一篇文章(https://www.cnblogs.com/ljzc002/p/9353101.html)中提出的方法,編寫一個簡單的宇宙飛船3D模型,在這篇文章中對模型製做流程和數學計算步驟進行介紹,併爲模型添加簡單的材質。  html

  咱們首先對3D模型的輪廓進行估計,而後製做一個擁有足夠多頂點的、與模型輪廓近似的網格對象(這裏選用條帶類網格對象),接着對網格的部分頂點進行位置變換以產生模型的細節,最後爲模型設置一個材質。算法

  固然Babylon.js還支持更復雜的紋理類型,我翻譯了Babylon.js官方教程中關於反射與折射,反射探查,地圖紋理,多重材質,動態紋理,高亮描邊的文檔(部分文檔翻譯的不明確,由於官方文檔自己的表述也不是很明確),能夠在http://down.51cto.com/data/2450646下載。數組

  一、從頂部看,估計飛船的首尾長度爲30單位,船體最寬處半徑爲7單位,船頭處呈圓滑的錐形;從船頭方向看,船體頂部爲較扁的圓弧,船底部邊緣圓滑中間平直(有點像上個世紀的航天飛機)。草圖以下:ide

  對於船體上部,高度低於2的部分直接使用半徑爲7的圓弧做爲倉壁,高於2的部分則將高度削減二分之一;對於船體下部,將大體形狀設爲壓扁到四分之一的半圓,再將高度低於-1的部分設爲平直的船底。函數

  規定船體沿x軸方向擺放,船體中心位於世界座標系原點,船頭朝向x軸負方向,船頂朝向y軸正方向。ui

  事實上,在編寫3D模型時固定的長度數值並無決定性的意義(固然過大或太小可能致使物體脫出視場),決定模型形狀的關鍵是各處尺寸之間的比例關係,具體的尺寸大小均可以在載入模型後根據須要進行縮放,這裏將船體長度設爲30單位是爲了在預設的編輯場景裏方便查看。spa

  而後開始構建一個符合上述輪廓的條帶網格。翻譯

  二、開始編寫條帶網格的路徑(頂點數組),首先生成一個半徑是7的圓形路徑,規定圓弧由128個頂點組成(事實上最終生成的路徑有129個頂點):設計

 1 function MakeRing(radius,sumpoint)//兩個參數分別是圓形的半徑和圓形由多少個頂點組成
 2     {
 3         var arr_point=[];//頂點數組
 4         var radp=Math.PI*2/sumpoint;//每個頂點在圓弧上轉過的角度
 5         for(var i=0.0;i<sumpoint;i++)
 6         {
 7             var x=0;
 8             var rad=radp*i;
 9         //算出頂點的y、z座標
10             var y=radius*Math.sin(rad);
11             var z=radius*Math.cos(rad);
12             arr_point.push(new BABYLON.Vector3(x,y,z));
13         }
14         arr_point.push(arr_point[0].clone());//爲了保持首尾相連,要再添加一次第一個頂點
15         return arr_point;
16     }

  計算y、z座標的示意圖以下:3d

  y和z的計算須要用到初中數學的三角函數知識。

  接下來使用「var arr1=TranceRing1(MakeRing(7,128));」將圓形路徑變成咱們設計的船體截面路徑,TranceRing1方法代碼以下:

 1 //上下擠壓,對於每一個頂點都生效的變換儘可能只執行一次
 2 function TranceRing1(arr)
 3 {
 4     var len=arr.length;
 5     for(var j=0;j<len;j++)
 6     {
 7         var obj=arr[j];
 8         if(obj.y<0)
 9         {
10             obj.y=obj.y/4;
11             if(obj.y<-1)
12             {
13                 obj.y=-1;
14             }
15         }
16         else if(obj.y>2)
17         {
18             obj.y=(obj.y-2)/2+2;
19         }
20     }
21     return arr;
22 }

  這裏的算法很簡單,遍歷路徑中的每一個頂點,而後根據上面的設計進行邏輯判斷便可。

  三、將上面生成的一條路徑克隆爲多條路徑,規定每兩條路徑之間的距離爲0.25:

1 arr_path=[];//路徑數組
2     var xstartl=-15;//設置船頭(也就是第一個圓環路徑)在x軸上的位置    
3     var arr1=TranceRing1(MakeRing(7,128));
4     for(var i=0;i<121;i++)
5     {
6         var arr_point=CloneArrPoint(arr1);//克隆一條路徑
7         arr_path.push(MoveX(arr_point,i*0.25+xstartl));//將克隆出的路徑沿x軸方向平移
8     }

  路徑克隆的示意圖以下:

  克隆路徑和x軸平移的方法以下:

 1 //克隆複製對象數組
 2     function CloneArrPoint(arr)
 3     {
 4         var arr2=[];
 5         var len=arr.length;
 6         for(var i=0;i<len;i++)
 7         {
 8             arr2.push(arr[i].clone());
 9         }
10         return arr2;
11     }
12     //平移x軸
13     function MoveX(path,dis)
14     {
15         var len=path.length;
16         for(var i=0;i<len;i++)
17         {
18             path[i].x+=dis;
19         }
20         return path;
21     }

  四、使用上一篇文章中提到的方法生成條帶網格:

1 var arr7=MakePointPath(new BABYLON.Vector3(15,0,0),129);//用一個點封口
2     arr_path.push(arr7);
3 
4     mesh_origin=BABYLON.MeshBuilder.CreateRibbon("mesh_origin",{pathArray:arr_path
5         ,updatable:true,closePath:false,closeArray:false});
6     mesh_origin.material=mat_frame;

  這裏的arr7是位於同一個位置的129個頂點,用來給敞開的船尾封口(使用多餘的頂點算是條帶網格模型的一個缺點,但這個缺點和條帶網格的易用性比起來能夠接受)至於船首的封口則由後面的網格變換負責。

  MakePointPath代碼以下:

 1 //用一個重合點路徑封口
 2 function MakePointPath(vec,size)
 3 {
 4     var arr_point=[];
 5     for(var i=0;i<size;i++)
 6     {
 7         arr_point.push(vec.clone());
 8     }
 9     return arr_point;
10 }

  生成的輪廓網格以下圖:

  五、經過頂點變換生成錐形的船頭:

  按照設計,從頂部俯視船體的前半部分是一個z向半徑爲七、x向半徑爲15的「圓弧形」,從側面看船頭的上部是y向半徑爲3.2五、x向半徑爲5的圓弧形,船頭的下部是y向半徑爲一、x向半徑爲2的圓弧形。

  側面示意圖以下:

  船首的變形代碼以下:

 1 //有的頂點變換會受到周圍頂點的影響,因此要在已經構造好的基礎上進行變換
 2 function TransCraft()
 3 {
 4     var len=arr_path.length;
 5     //遍歷每一個點,用程序判斷這個點是否符合某些標準,並進行相應變化
 6     for(var i=0;i<len;i++)
 7     {
 8         var arr_point=arr_path[i];
 9         var len2=arr_point.length;
10         for(var j=0;j<len2;j++)
11         {
12             var obj=arr_point[j];
13             //var x=obj.x;
14             //var y=obj.y;
15             //var z=obj.z;
16             //船首呈椎體狀
17             if(obj.x<-13&&obj.y<0)//從側面看的船首下部
18             {
19                 var rate=Math.sin(Math.acos((-13-obj.x)/2/1));//y軸方向縮放係數
20                 obj.y=obj.y*rate;
21             }
22             if(obj.x<-10&&obj.y>0)//從側面看的船首上部
23             {
24                 var rate=Math.sin(Math.acos((-10-obj.x)/(5/3.25)/3.25));//y軸方向縮放係數
25                 obj.y=obj.y*rate;
26             }
27             if(obj.x<0)//從頂部看的船首
28             {
29                 var rate=Math.sin(Math.acos((-obj.x)/(15/7)/7));//y軸方向縮放係數
30                 obj.z=obj.z*rate;
31             }

  用不一樣的比例對路徑進行壓縮,將原來尺寸相同的路徑變成尺寸漸變的路徑,路徑連成的條帶網格就會呈現椎體的形狀,那麼問題就在於如何計算這個縮放的比例,使得椎體的表面呈現爲圓滑的弧形。

  我將圓弧定義爲拉伸的正圓形的一部分,而後由x座標值計算出對應路徑的縮放比例,原理圖以下(以「從側面看的船首上部」爲例):

  首先將從側面看船頭上部的中間截面經過將x座標除以(5/3.25)的方式變換爲正圓的一部分,用(-10-obj.x)/(5/3.25)計算出「xsize」的長度,由於y軸縮放比例等於在這個截面上頂點高度(y值)和半徑(r)的比等於sin(a),因此只需求出角a的大小便可算出比例,而角a的大小能夠由(xsize/r)的反餘弦得出。如此得出y方向的縮放比例。

  從頂部看的縮放比例也是如此計算,這時計算獲得的是z軸方向的縮放比例。

  縮放後的顯示效果以下:

  能夠看到船頭的129個頂點被縮放到了同一位置,船頭呈現圓滑的弧線。

  六、生成飛船的後掠翼,生成原理與船首相似:

 1 //後掠翼,具備圓弧狀的邊緣
 2             if(obj.x>0&&obj.y>0&&obj.y<1)
 3             {
 4                 //這一層翼面和最小翼面的邊緣差值
 5                 var rate=Math.cos(Math.asin(Math.abs(0.5-obj.y)/(0.5/1)/1));
 6                 var size1=1*rate;
 7                 var h=14+size1;
 8                 var w=6.5+size1;
 9                 if((15-obj.x)<h)
10                 {
11                     var rate2=Math.cos(Math.asin(Math.abs(15-obj.x)/(h/w)/w));
12                     if(obj.z>0)
13                     {
14                         obj.z+=w*rate2;
15                     }
16                     else if(obj.z<0)
17                     {
18                         obj.z-=w*rate2;
19                     }
20                     var rate3=3/(15-Math.abs(obj.z))
21                     obj.x+=rate3;
22                 }
23 
24             }

  想象翼面在y方向由多層相互重疊的結構組成,每一片的尺寸不一樣,所以頁面能夠具備兩重的圓弧邊緣,示意圖以下:

 

  認爲翼面由多層組成,參考下圖,最大的一層寬度爲7.5,最小的一層寬度爲6.5,其中某一層與最小層的寬度差爲size1,使用和船頭圓弧相似的方法算出size1的值,進而算出這一層的尺寸。

   而後參考上圖,在一個短軸爲w長軸爲h的拉伸扇形中計算每一個頂點向左或右側的偏移量。

  隨後編寫一個方法讓機翼向後傾斜,距機身越遠的頂點向後移動的距離越大。

  尾翼的生成方式和水平翼類似。

  執行效果以下:

  附實際開發時使用的草圖:

 

  七、在控制檯執行ChangeMaterial(mesh_origin,mat_blue)能夠將材質轉化爲純藍色,由於條帶網格的法線方向默認指向飛船內部,這時飛船外部將不能顯示光照的鏡面反射效果,解決辦法是在初始化材質時設置:

 

1 mat_blue.twoSidedLighting=true;//雙面光照選項

  執行ChangeMaterial(mesh_origin,mat_alpha)能夠將材質轉化爲半透明,一樣須要對mat_alpha設置上述屬性,不然將只有飛船的內表面可見,半透明效果以下圖:

相關文章
相關標籤/搜索