寫在前面:vcglib的坑真的是太深了,慎入!!! 各種文檔不全,使用全靠看源碼和猜,網上資料很少而且基本都是複製粘貼,有用的信息真的很少!(附上vcglib的文檔 真的能看到自閉)
本文使用vcglib庫進行讀取obj文件(其他各種3d模型格式也是同樣的方法)並顯示,但是目前仍然遇到一些問題沒有解決:讀取的紋理信息全部缺失(讀出來全部爲0,因此下圖並沒有貼紋理),查了很久真心不知道爲什麼,如果有哪位大佬偶爾看到本文,勞煩告知如何解決。
一、安裝vcglib庫
vcglib的安裝非常簡單
-
下載源碼
下載地址:https://github.com/cnr-isti-vclab/vcglib/
-
解壓得到下面六個文件夾
然後將vcg、wrap、eigenlib文件夾添加到編譯器的include文件夾中。如果是使用qt則include的路徑爲
xx:\Qt\Qt5.6.1\5.6\mingw49_32\include -
文件夾的解釋(轉自博客https://www.cnblogs.com/icmzn/p/6640752.html)
vcg:這是整個庫的核心,其中定義了所有的算法和數據結構。該部分所有的C++代碼都是STL支持的普通數據結構和算法,不包含任何其它標準庫之外的庫,而且可以發現,該部分只包含頭文件(.h)*;
**wrap:**這裏包含一些針對特定需求/上下文/庫的VCG概念的封裝。例如,所有的用於計算機硬盤上很多格式的網格數據的導入和導出;用OpenGL渲染三角形網格的代碼;普通GUI工具如跟蹤球,等等;
**apps:**這個文件夾包含一些用VCG Lib開發的命令行程序應用。很多例子都能在MeshLab中找到,apps/simple文件夾包含了這些程序的一個基礎的子集,是一個初學者很好的入口點;
**docs:**文檔(包括這個教程)eigenLib:線性代數的eigen庫最近的穩定版本的一個副本(相當於就是借用第三方庫了),VCGLib中的高級矩陣操作都是基於這個庫的。
二、 vcglib的使用
1. 定義數據類型
下面是定義了點、線、面和網格的數據類型,直接添加在代碼中即可,如需要進一步瞭解可以看下這個博文
class MyVertex; class MyEdge; class MyFace;
struct MyUsedTypes : public vcg::UsedTypes<vcg::Use<MyVertex> ::AsVertexType,
vcg::Use<MyEdge> ::AsEdgeType,
vcg::Use<MyFace> ::AsFaceType>{};
//存儲點,法向量,紋理座標
class MyVertex : public vcg::Vertex< MyUsedTypes, vcg::vertex::Coord3f,vcg::vertex::TexCoord2f, vcg::vertex::Normal3f, vcg::vertex::BitFlags >{};
class MyFace : public vcg::Face< MyUsedTypes, vcg::face::FFAdj, vcg::face::VFAdj,vcg::face::VertexRef, vcg::face::BitFlags > {};
class MyEdge : public vcg::Edge< MyUsedTypes> {};
class MyMesh : public vcg::tri::TriMesh<std::vector<MyVertex>, std::vector<MyFace>, std::vector<MyEdge> > {};
2. 讀取obj文件
MyMesh m_obj; //定義一個網格對象
int mask; //定義mask
//注意your filePath不能有中文路徑
vcg::tri::io::ImporterOBJ<MyMesh>::Open(m_obj, "your filePath",mask);
//爲每個點計算法線並歸一化
vcg::tri::RequirePerVertexNormal(m_obj);
vcg::tri::UpdateNormal<MyMesh>::PerVertexNormalized(m_obj);
其他類型的文件也是同樣的操作,只是將語句
vcg::tri::io::ImporterOBJ::Open(m_obj, “your filePath”,mask);變成vcg::tri::io::ImporterXXX::Open(m_obj, “your filePath”,mask);
XXX爲你所使用的數據類型,具體支持的類型可以在\wrap\io_trimesh目錄下看到
上面幾句話已經將obj模型讀進m_obj對象中了,下面就是重點了,如何從m_obj中獲取數據
vector<MyVertex>& vs = m_obj.vert;
vector<MyEdge>& es = m_obj.edge;
vector<MyFace>& fs = m_obj.face;
給幾個例子說明一下數據如何取出
MyVertex v = vs[0]; //取第一個點
//v.P()儲存了點的位置座標,v.N()儲存了點的法線座標,v.T()儲存了點的紋理座標
v.P().X() v.P().Y() v.P().Z() //取第一個點的XYZ座標
v.N().X() v.N().Y() v.N().Z() //取第一個點法線的XYZ座標
v.T().U() v.T().V() //取第一個點的紋理UV座標
3. 在opengl中使用數據
下面爲將數據保存到頂點數組中的具體操作
vector<MyFace>& fs = m_obj.face; //取出所有面
vertex = new GLdouble[fs.size()*3*3];// 數據類型GLdouble *vertex;
normal = new GLdouble[fs.size()*3*3];// 數據類型GLdouble *normal;
int v_index = 0;
//遍歷所有面
for(auto fi = m_obj.face.begin(); fi!=m_obj.face.end(); ++fi )
{
for( int j =0; j<3 ; j++){
MyVertex* v= fi->V(j);
if(fi == m_obj.face.begin())
qDebug()<< v->T().u();
normal[v_index] = v->N().X();
vertex[v_index++] = v->P().X();
normal[v_index] = v->N().Y();
vertex[v_index++] = v->P().Y();
normal[v_index] = v->N().Z();
vertex[v_index++] = v->P().Z();
}
}
最後在opengl中繪製即可
void Widget::renderObj()
{
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
vector<MyFace>& fs = m_obj.face;
glVertexPointer(3,GL_DOUBLE,0,vertex);
glNormalPointer(GL_DOUBLE,0,normal);
glDrawArrays(GL_TRIANGLES,0,fs.size()*3);
}
經過以上步驟即可完成obj的讀取和繪製,雖然整理之後的過程比較簡單,但是在搜索資料的過程真的很痛苦, 有一個好的說明文檔真的很重要!!!