擷取模組化得到的三維模型并解析
STL****檔案
STL (STereoLithography, 立體光刻)是由3D Systems軟體公司創立、原本用于立體光刻計算機輔助設計軟體的檔案格式。它有一些事後諸葛的字頭語如“标準三角語言(Standard Triangle Language)”、“标準曲面細分語言(Standard Tessellation Language)”、“立體光刻語言(STereolithography Language)”和“(立體光刻曲面細分語言)”。許多套裝軟體支援這種格式,它被廣泛用于快速成型、3D列印和計算機輔助制造(CAM)。STL檔案僅描述三維物體的表面幾何形狀,沒有顔色、材質貼圖或其它常見三維模型的屬性。
STL格式有文字和二進碼兩種型式。二進碼型式因較簡潔而較常見。
ASCII格式
ASCII碼格式的STL檔案逐行給出三角面片的幾何資訊,每一行以1個或2個關鍵字開頭。
在STL檔案中的三角面片的資訊單元 facet 是一個帶矢量方向的三角面片,STL三維模型就是由一系列這樣的三角面片構成。整個STL檔案的首行給出了檔案路徑及檔案名。在一個 STL檔案中,每一個facet由7 行資料組成,facet normal 是三角面片指向實體外部的法矢量坐标,outer loop 說明随後的3行資料分别是三角面片的3個頂點坐标,3頂點沿指向實體外部的法矢量方向逆時針排列。
ASCII格式的STL 檔案結構如下:
//字元段意義
solidfilenamestl//檔案路徑及檔案名
facetnormalxyz//三角面片法向量的3個分量值
outerloop
vertexxyz//三角面片第一個頂點坐标
vertexxyz//三角面片第二個頂點坐标
vertexxyz//三角面片第三個頂點坐标
endloop
endfacet//完成一個三角面片定義
…//其他facet
解析輸出為:
二進制格式
二進制STL檔案用固定的位元組數來給出三角面片的幾何資訊。
檔案起始的80個位元組是檔案頭,用于存貯檔案名;(如下所示)
solid Exported from Blender-2078 (sub 0)
緊接着用 4 個位元組的整數來描述模型的三角面片個數,如下所示,即為一個三角得資訊。
facet normal -0.000000 0.000000 -1.000000
outer loop
vertex 1.000000 1.000000 -1.000000
vertex 1.000000 -1.000000 -1.000000
vertex -1.000000 -1.000000 -1.000000
endloop
endfacet
後面逐個給出每個三角面片的幾何資訊。每個三角面片占用固定的50個位元組,依次是:
3個4位元組浮點數(角面片的法矢量)
3個4位元組浮點數(1個頂點的坐标)
3個4位元組浮點數(2個頂點的坐标)
3個4位元組浮點數(3個頂點的坐标)個
三角面片的最後2個位元組用來描述三角面片的屬性資訊。
一個完整二進制STL檔案的大小為三角形面片數乘以 50再加上84個位元組。
二進制格式的STL 檔案結構如下:
UINT8//Header//檔案頭
UINT32//Numberoftriangles//三角面片數量
//foreachtriangle(每個三角面片中)
REAL32[3]//Normalvector//法線矢量
REAL32[3]//Vertex1//頂點1坐标
REAL32[3]//Vertex2//頂點2坐标
REAL32[3]//Vertex3//頂點3坐标
UINT16//Attributebytecountend//檔案屬性統計
解析輸出為:
OBJ檔案
obj(或者.obj)是一種幾何定義檔案格式,第一次是 Wavefront Technologies在他們的可視化加強動畫包裡面使用的。檔案格式是公開的,并能很好的在其他的3D應用中被支援。
Obj檔案格式是一種簡單的單獨表示3D幾何圖元的檔案格式——也就是,頂點的坐标,每個頂點紋理的UV坐标,頂點法向量,以及組成多邊形的面的頂點坐标、以及紋理UV坐标序列。面的頂點預設為逆時針順序,法向量不是必須的。OBJ檔案并非歸一化的,但是可以在注釋中加入縮放資訊。
Obj檔案可以是ASCII的編碼(.obj)方式也可以是二進制格式(.mod)。但是二進制類型其作為專利未公開,是以這裡不作讨論。以ASCII格式存儲的obj檔案必須用.obj作為檔案拓展名。
檔案的開始,有以哈希字元(#)開始的一行表示注釋。
# this is a comment
對于obj來說,一個obj格式的檔案可能包含了頂點資料,自由形式的曲面/表面屬性,繪制索引序列,自由形式的曲面/表面内容聲明,關聯自由形式的表面,組和渲染屬性資訊。大多數常見的繪制索引表現為幾何頂點,紋理坐标,頂點法線以及多邊形的面:
[外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-gK3xEAGG-1590112131477)(https://raw.githubusercontent.com/adonispeace/adonispeace.github.io/master/dailyPic/20200401/clip_image012.jpg “Image_border+rounded”)]{:.border.rounded}
通常處理obj檔案的時候,會抛棄頂點法線資料,而通過頂點資訊來進行計算。有了以上的頂點坐标、法線、紋理坐标等資訊,就可以進行3D模型檔案的渲染了。
解析輸出為:
g “Image_border+rounded”)]{:.border.rounded}
3DS檔案
3ds檔案是3D Max的一種二進制存儲格式,它始終沒被官方公開,但是也基本被大家hack出來了大半。其“格式”總的來說非常簡單,這裡介紹一個概念:chunk。3ds檔案裡的資料都是按chunk一塊一塊隔離的。每個chunk都有兩個标記:2個位元組大小的chunkId,用來辨別這個chunk存的是什麼資料。接着是一個4個位元組大小的chunkLen,它根據chunkId不同,可能表示該chunk的大小,也可能表示下一個chunk的位置偏移。
-----------------------------
chunk Id 2 Byte
chunk Len 4 Byte
-----------------------------
想要讀取3ds檔案,chunk Id是比較重要的部分,下面是hack出來的常用Id:
---------------------------------------------------------------------------------------
0x4D4D // Main Chunk
├─ 0x3D3D // 3D Editor Chunk
│ ├─ 0x4000 // Object Block
│ │ ├─ 0x4100 // Triangular Mesh
│ │ │ ├─ 0x4110 // Vertices List
│ │ │ ├─ 0x4120 // Faces Description
│ │ │ │ └─ 0x4130 // Faces Material
│ │ │ ├─ 0x4140 // Mapping Coordinates List
│ │ │ │ └─ 0x4150 // Smoothing Group List
│ │ │ └─ 0x4160 // Local Coordinates System
│ │ ├─ 0x4600 // Light
│ │ │ └─ 0x4610 // Spotlight
│ │ └─ 0x4700 // Camera
│ └─ 0xAFFF // Material Block
│ ├─ 0xA000 // Material Name
│ ├─ 0xA010 // Ambient Color
│ ├─ 0xA020 // Diffuse Color
│ ├─ 0xA030 // Specular Color
│ ├─ 0xA200 // Texture Map 1
│ ├─ 0xA230 // Bump Map
│ └─ 0xA220 // Reflection Map
│ │
│ ├─ 0xA300 // Mapping Filename
│ └─ 0xA351 // Mapping Parameters
└─ 0xB000 // Keyframer Chunk
├─ 0xB002 // Mesh Information Block
├─ 0xB007 // Spot Light Information Block
└─ 0xB008 // Frames (Start and End)
├─ 0xB010 // Object Name
├─ 0xB013 // Object Pivot Point
├─ 0xB020 // Position Track
├─ 0xB021 // Rotation Track
├─ 0xB022 // Scale Track
└─ 0xB030 // Hierarchy Position
---------------------------------------------------------------------------
一般來說,一個chunk可能還包含子chunk,比如0x4100表示Mesh的chunk裡就包含了0x4110,0x4120等chunk,同時,0x4100這個chunk的長度辨別,是把這些子chunk的長度也計算在内的。一般來說,我們讀取3ds檔案,隻要解析主要chunk,遇到不識别的chunk,跳過即可。因為3ds裡的資料,大多數并不是程式員需要的。我們隻要頂點和uv這些就夠了。
讀取資料并載入:
配置環境
本人使用的VS2019,在配置環境時,希望能夠講庫進行集中管理,是以在E盤單獨建立檔案夾,将include和lib項放入,并在項目中引入,雖然在後期建立項目中需要重複引入,但是保證了相關檔案的整理便捷性。
同時添加依賴項:
繪制三維模型
對于三種檔案格式來說,有很多的選擇來進行重繪,在這裡我選擇使用glut的庫來完成操作。
上邊已經顯示出資料的解析,并且得到繪制的圖形。可以先使用如下函數進行繪制,設視窗的大小以及形式等資訊。
glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH | GLUT_RGBA | GLUT_MULTISAMPLE);
glutInitWindowPosition(100, 100);
glutInitWindowSize(GL_WIN_WIDTH, GL_WIN_HEIGHT);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); // | GLUT_STENCIL
glutInit(argc, argv);
GLUTwindow = glutCreateWindow("powerful_reader~");
glutReshapeFunc(glutResize);
最後的選擇如上圖所示。
Arcball實作控制
由于螢幕是二維的,無法直接表示旋轉,可以通過輔助幾何體來完成。想象在螢幕後面有一個球體,球體正好與這個螢幕相切,如下圖的俯視圖所示
我們在求旋轉軸之前需要将二維坐标轉化為三維向量OP,如下圖,T為螢幕上的二維坐标,P為T向螢幕後方發出射線與球體相交的點。這裡做作OM水準面平行于螢幕,P到水準線的深度z需要先行求得。
為了運算處理友善,我們需要将T坐标限定在[-1,1]之間,設定這個球體的半徑為1。
x = 2*x / 螢幕寬度 - 1
y = -(2*y / 螢幕高度 - 1)
由勾股定理可知,z = sqrt(1-xx-yy)。
當xx+yy大于1時,P不在球體上,那就在OM上找一點P,且滿足P在球體上,PT垂直于平面OM,即xx+yy大小限定在1這個值,z = 0.
具體流程如下:
x,y,z的值可以唯一确定OP向量,接下來,給定輸入的兩個坐标T1,T2,我們做出如下處理:
\1. 分别求出T1和T2對應的三維向量OP1和OP2
\2. s = OP1 · OP2,即先求兩個向量的内積
\3. v = OP1 × OP2,即求兩個向量的外積
\4. 記四元數q = [s, v],将其機關化,此時q為旋轉四元數。
\5. 旋轉角α = 2arccos(q.s) ,旋轉軸V = ( q.v / sqrt(1-q.s*q.s) )
結果展示
如下所示,stl格式的ascii、binary存儲格式的讀取和繪制以及控制,以及obj和3DS格式,運作過程存儲為gif****格式,可另行儲存觀看。
或者在百度雲下載下傳:
連結:https://pan.baidu.com/s/1mH9cy1nC9WNrOfa7HDqa9g
提取碼:xv8t
STL_binary
STL_ASCII
OBJ
3DS