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

来源:https://www.cnblogs.com/ljzc002/archive/2018/08/14/9473438.html
-Advertisement-
Play Games

使用上一篇文章(https://www.cnblogs.com/ljzc002/p/9353101.html)中提出的方法,編寫一個簡單的宇宙飛船3D模型,在這篇文章中對模型製作流程和數學計算步驟進行介紹,併為模型添加簡單的材質。 我們首先對3D模型的輪廓進行估計,然後製作一個擁有足夠多頂點的、與模 ...


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

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

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

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

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

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

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

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

  2、開始編寫條帶網格的路徑(頂點數組),首先生成一個半徑是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坐標的示意圖如下:

  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 }

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

  3、將上面生成的一條路徑克隆為多條路徑,規定每兩條路徑之間的距離為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     }

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

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 }

  生成的輪廓網格如下圖:

  5、通過頂點變換生成錐形的船頭:

  按照設計,從頂部俯視船體的前半部分是一個z向半徑為7、x向半徑為15的“圓弧形”,從側面看船頭的上部是y向半徑為3.25、x向半徑為5的圓弧形,船頭的下部是y向半徑為1、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個頂點被縮放到了同一位置,船頭呈現圓滑的弧線。

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

 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的拉伸扇形中計算每個頂點向左或右側的偏移量。

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

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

  執行效果如下:

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

 

  7、在控制台執行ChangeMaterial(mesh_origin,mat_blue)可以將材質轉化為純藍色,因為條帶網格的法線方向預設指向飛船內部,這時飛船外部將不能顯示光照的鏡面反射效果,解決辦法是在初始化材質時設置:

 

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

  執行ChangeMaterial(mesh_origin,mat_alpha)可以將材質轉化為半透明,同樣需要對mat_alpha設置上述屬性,否則將只有飛船的內錶面可見,半透明效果如下圖:

 

  

    


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 簡單定義:根據不同參數長度來實現讓同一個函數,進行不同處理。 使用: 測試: ...
  • 無意看了這篇 "《jQuery插件開發精品教程,讓你的jQuery提升一個臺階》" 文章,現在做一下總結。 一、jQuery插件的創建可以有三種方法 1.通過$.extend()來擴展jQuery 2.通過$.fn 向jQuery添加新的方法 3.通過$.widget()應用jQuery UI的部件 ...
  • 話不多說直接上代碼 因為項目需求,該動畫中需要顯示即時更新的數據,所以和普通的canvas畫出來的不一樣。但是又不能直接把html畫到canvas中去,彆著急有辦法。 為了繪製 HTML 內容,你要先用<foreignObject> 元素包含 HTML 內容,然後再將這個 SVG 圖像繪製到你的 c ...
  • 最近做項目時碰到一個需求,就是在移動端支付頁面點擊支付按鈕彈出一個支付鍵盤,類似於支付寶的那種。由於項目只是單純的手機網站,而並非app,所以這個功能得由前端來實現。話不多說,先上圖看看效果。 尼瑪,這不就是支付寶app那個支付鍵盤嗎? 沒錯,咱們UI就是參照支付寶做的這個鍵盤。你可能會問,為什麼不 ...
  • 來自:https://blog.csdn.net/wd4871/article/details/50517597 侵刪 父頁面中的iframe :如下 <iframe name="sunPage" id="sunPage" src="sun.html" width="300px" height="a ...
  • 刪除數組第一個元素使用var length = arr.shift(); 刪除arr的第一個元素後, 返回值是刪除後的數組長度 刪除數組最後一個元素使用var length = arr.pop(); 刪除arr的最後一個元素後, 返回值是刪除後數組的長度 在數組開頭添加元素使用var length ...
  • 一、前言 時隔三年,記得第一次寫博客還是2015年了,經過這幾年的洗禮,我也從一個後端的小萌新變成現在略懂一點點知識的文青。如今對於前端的東東也算有一知半解,個人能力總的來說,也能夠獨立開發產品級項目吧。至於為什麼會前端的東西,估計學.NET的人應該大部分都懂些,之前自己搭建過一套框架,但覺得現在的 ...
  • 用兩個伺服器處理一個項目的代碼,其中一臺伺服器只處理介面請求。 本來PHP可以使用CURL來處理,但是領導不允許使用PHP來處理數據。會影響網站的功能。如果介面端出現問題會導致整個網站或其頁面的崩潰,因此使用前端處理數據。就不會產生這種問題。 本文適用於知道跨域,但是不知道跨域為何物的程式員。 話不 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...