OpenGL读取Obj模型文件
2017-05-22 22:44
435 查看
昨天,帮助别人写了一个程序,读取obj文件中的3D模型,就学习了下使用OpenGL如何读取这种文件。
Obj文件格式
想要顺利读取obj模型文件,先要了解这种文件的格式,OBJ文件格式是非常简单的。这种文件以纯文本的形式存储了模型的顶点、法线和纹理坐标和材质使用信息。OBJ文件的每一行,都有极其相似的格式。在OBJ文件中,每行的格式如下:
前缀
参数1
参数2
参数3 ...
其中,前缀标识了这一行所存储的信息类型。参数则是具体的数据。OBJ文件常见的的前缀有
§ v
表示本行指定一个顶点。
前缀后跟着3个单精度浮点数,分别表示该定点的X、Y、Z坐标值
§ vt
表示本行指定一个纹理坐标。此前缀后跟着两个单精度浮点数。分别表示此纹理坐标的U、V值
§ vn
表示本行指定一个法线向量。此前缀后跟着3个单精度浮点数,分别表示该法向量的X、Y、Z坐标值
§ f
表示本行指定一个表面(Face)。一个表面实际上就是一个三角形图元。此前缀行的参数格式后面将详细介绍。
§ usemtl
此前缀后只跟着一个参数。该参数指定了从此行之后到下一个以usemtl开头的行之间的所有表面所使用的材质名称。该材质可以在此OBJ文件所附属的MTL文件中找到具体信息。
§ mtllib
此前缀后只跟着一个参数。该参数指定了此OBJ文件所使用的材质库文件(*.mtl)的文件路径
现在,我们再来看一下OBJ文件的结构。在一个OBJ文件中,首先有一些以v、vt或vn前缀开头的行指定了所有的顶点、纹理坐标、法线的坐标。然后再由一些以f开头的行指定每一个三角形所对应的顶点、纹理坐标和法线的索引。在顶点、纹理坐标和法线的索引之间,使用符号“/”隔开的。一个f行可以以下面几种格式出现:
§ f 1 2 3
这样的行表示以第1、2、3号顶点组成一个三角形。
§ f 1/3 2/5 3/4
这样的行表示以第1、2、3号顶点组成一个三角形,其中第一个顶点的纹理坐标的索引值为3,第二个顶点的纹理坐标的索引值为5,第三个顶点的纹理坐标的索引值为4。
§ f 1/3/4 2/5/6 3/4/2
这样的行表示以第1、2、3号顶点组成一个三角形,其中第一个顶点的纹理坐标的索引值为3,其法线的索引值是4;第二个顶点的纹理坐标的索引值为5,其法线的索引值是6;第三个顶点的纹理坐标的索引值为6,其法线的索引值是2。
§ f 1//4 2//6 3//2这样的行表示以第1、2、3号顶点组成一个三角形,且忽略纹理坐标。其中第一个顶点的法线的索引值是4;第二个顶点的法线的索引值是6;第三个顶点的法线的索引值是2。
值得注意的是文件中的索引值是以1作为起点的,这一点与C语言中以0作为起点有很大的不同。在渲染的时候应注意将从文件中读取的坐标值减去1。
obj文件在OpenGL中的读取
我拿到的Obj文件,内容如下:
# Max2Obj Version 4.0 Mar 10th, 2001
#
# object Line01 to come ...
#
v -9.574153-2.220963 -2.000000
v -7.893424-2.280989 -2.000000
...省略若干相同格式的行
v -7.195892-1.380599 -0.980160
v -9.580536-1.320573 -1.967912
# 160 vertices
vn -0.071382-1.998675 0.014198
vn -0.035691-0.999338 0.007099
...同样省略若干相同格式的行
vn -0.8252241.736366 -0.551397
vn 0.0394181.999438 0.026341
# 160 vertex normals
g Line01
s 1
f 1//1 12//12 2//2
f 1//1 11//11 12//12
s 4
f 2//2 13//13 3//3
f 2//2 12//12 13//13
...同样的省略若干相同格式的行
s 4
f 160//160 1//1 151//151
f 160//160 10//10 1//1
# 320 faces
g
前面带有'#'的行是注释行,这个文件中包含的前缀有:v,表示顶点;vn,表示法线;g,表示组,行"g
Line01" 和行
"g" 之前的所有行表示一个名为"Line01"的组;f,表示一个面;s,表示光滑组。
由于文件中只出现顶点和法线数据,每个面存储顶点和法线索引,所以我们要声明如下几个全局函数:
int v_num=0; //记录点的数量
int vn_num=0;//记录法线的数量
int f_num=0; //记录面的数量
GLfloat **vArr; //存放点的二维数组
GLfloat **vnArr;//存放法线的二维数组
int **fvArr; //存放面顶点的二维数组
int **fnArr; //存放面法线的二维数组
string s1;
GLfloat f2,f3,f4;
为了给存放顶点法线等二维数组分配存储空间,需要知道顶点和法线等的数量,使用下面的函数计算点、法线、面的数量:
int readfile(string addrstr) //将文件内容读到数组中去
{
vArr=new GLfloat*[v_num];
for (inti=0;i<v_num;i++)
{
vArr[i]=newGLfloat[3];
}
vnArr=new GLfloat*[vn_num];
for (i=0;i<vn_num;i++)
{
vnArr[i]=newGLfloat[3];
}
fvArr=new int*[f_num];
fnArr=new int*[f_num];
for (i=0;i<f_num;i++)
{
fvArr[i]=newint[3];
fnArr[i]=newint[3];
}
ifstream infile(addrstr.c_str());
string sline;//每一行
int ii=0,jj=0,kk=0;
while(getline(infile,sline))
{
if(sline[0]=='v')
{
if(sline[1]=='n')//vn
{
istringstream sin(sline);
sin>>s1>>f2>>f3>>f4;
vnArr[ii][0]=f2;
vnArr[ii][1]=f3;
vnArr[ii][2]=f4;
ii++;
}
else//v
{
istringstream sin(sline);
sin>>s1>>f2>>f3>>f4;
vArr[jj][0]=f2;
vArr[jj][1]=f3;
vArr[jj][2]=f4;
jj++;
}
}
if (sline[0]=='f') //读取面
{
istringstreamin(sline);
GLfloata;
in>>s1;//去掉前缀f
inti,k;
for(i=0;i<3;i++)
{
in>>s1;
cout<<s1<<endl;
//取得顶点索引和法线索引
a=0;
for(k=0;s1[k]!='/';k++)
{
a=a*10+(s1[k]-48);
}
fvArr[kk][i]=a;
a=0;
for(k=k+2;s1[k];k++)
{
a=a*10+(s1[k]-48);;
}
fnArr[kk][i]=a;
}
kk++;
}
}
return 0;
}
然后在绘制之前,初始化时,调用这两个函数读取模型即可:
getLineNum("wan.obj");
readfile("wan.obj");
相应的绘制代码:
for (inti=0;i<f_num;i++)
{
glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
glBegin(GL_TRIANGLES);
glNormal3f(vnArr[fnArr[i][0]-1][0],vnArr[fnArr[i][0]-1][1],
vnArr[fnArr[i][0]-1][2]);
glVertex3f(vArr[fvArr[i][0]-1][0],vArr[fvArr[i][0]-1][1],
vArr[fvArr[i][0]-1][2]);
glNormal3f(vnArr[fnArr[i][1]-1][0],vnArr[fnArr[i][1]-1][1],
vnArr[fnArr[i][1]-1][2]);
glVertex3f(vArr[fvArr[i][1]-1][0],vArr[fvArr[i][1]-1][1],
vArr[fvArr[i][1]-1][2]);
glNormal3f(vnArr[fnArr[i][2]-1][0],vnArr[fnArr[i][2]-1][1],
vnArr[fnArr[i][2]-1][2]);
glVertex3f(vArr[fvArr[i][2]-1][0],vArr[fvArr[i][2]-1][1],
vArr[fvArr[i][2]-1][2]);
glEnd();
}
这样就完成了绘制,上面的代码仅仅针对我的wan.obj这个文件,对于想读取其他的obj文件,相应的分配一个存储空间,读取相应的数据,然后在绘制时使用这些数据就行了。
******************************
Obj文件格式
想要顺利读取obj模型文件,先要了解这种文件的格式,OBJ文件格式是非常简单的。这种文件以纯文本的形式存储了模型的顶点、法线和纹理坐标和材质使用信息。OBJ文件的每一行,都有极其相似的格式。在OBJ文件中,每行的格式如下:
前缀
参数1
参数2
参数3 ...
其中,前缀标识了这一行所存储的信息类型。参数则是具体的数据。OBJ文件常见的的前缀有
§ v
表示本行指定一个顶点。
前缀后跟着3个单精度浮点数,分别表示该定点的X、Y、Z坐标值
§ vt
表示本行指定一个纹理坐标。此前缀后跟着两个单精度浮点数。分别表示此纹理坐标的U、V值
§ vn
表示本行指定一个法线向量。此前缀后跟着3个单精度浮点数,分别表示该法向量的X、Y、Z坐标值
§ f
表示本行指定一个表面(Face)。一个表面实际上就是一个三角形图元。此前缀行的参数格式后面将详细介绍。
§ usemtl
此前缀后只跟着一个参数。该参数指定了从此行之后到下一个以usemtl开头的行之间的所有表面所使用的材质名称。该材质可以在此OBJ文件所附属的MTL文件中找到具体信息。
§ mtllib
此前缀后只跟着一个参数。该参数指定了此OBJ文件所使用的材质库文件(*.mtl)的文件路径
现在,我们再来看一下OBJ文件的结构。在一个OBJ文件中,首先有一些以v、vt或vn前缀开头的行指定了所有的顶点、纹理坐标、法线的坐标。然后再由一些以f开头的行指定每一个三角形所对应的顶点、纹理坐标和法线的索引。在顶点、纹理坐标和法线的索引之间,使用符号“/”隔开的。一个f行可以以下面几种格式出现:
§ f 1 2 3
这样的行表示以第1、2、3号顶点组成一个三角形。
§ f 1/3 2/5 3/4
这样的行表示以第1、2、3号顶点组成一个三角形,其中第一个顶点的纹理坐标的索引值为3,第二个顶点的纹理坐标的索引值为5,第三个顶点的纹理坐标的索引值为4。
§ f 1/3/4 2/5/6 3/4/2
这样的行表示以第1、2、3号顶点组成一个三角形,其中第一个顶点的纹理坐标的索引值为3,其法线的索引值是4;第二个顶点的纹理坐标的索引值为5,其法线的索引值是6;第三个顶点的纹理坐标的索引值为6,其法线的索引值是2。
§ f 1//4 2//6 3//2这样的行表示以第1、2、3号顶点组成一个三角形,且忽略纹理坐标。其中第一个顶点的法线的索引值是4;第二个顶点的法线的索引值是6;第三个顶点的法线的索引值是2。
值得注意的是文件中的索引值是以1作为起点的,这一点与C语言中以0作为起点有很大的不同。在渲染的时候应注意将从文件中读取的坐标值减去1。
obj文件在OpenGL中的读取
我拿到的Obj文件,内容如下:
# Max2Obj Version 4.0 Mar 10th, 2001
#
# object Line01 to come ...
#
v -9.574153-2.220963 -2.000000
v -7.893424-2.280989 -2.000000
...省略若干相同格式的行
v -7.195892-1.380599 -0.980160
v -9.580536-1.320573 -1.967912
# 160 vertices
vn -0.071382-1.998675 0.014198
vn -0.035691-0.999338 0.007099
...同样省略若干相同格式的行
vn -0.8252241.736366 -0.551397
vn 0.0394181.999438 0.026341
# 160 vertex normals
g Line01
s 1
f 1//1 12//12 2//2
f 1//1 11//11 12//12
s 4
f 2//2 13//13 3//3
f 2//2 12//12 13//13
...同样的省略若干相同格式的行
s 4
f 160//160 1//1 151//151
f 160//160 10//10 1//1
# 320 faces
g
前面带有'#'的行是注释行,这个文件中包含的前缀有:v,表示顶点;vn,表示法线;g,表示组,行"g
Line01" 和行
"g" 之前的所有行表示一个名为"Line01"的组;f,表示一个面;s,表示光滑组。
由于文件中只出现顶点和法线数据,每个面存储顶点和法线索引,所以我们要声明如下几个全局函数:
int v_num=0; //记录点的数量
int vn_num=0;//记录法线的数量
int f_num=0; //记录面的数量
GLfloat **vArr; //存放点的二维数组
GLfloat **vnArr;//存放法线的二维数组
int **fvArr; //存放面顶点的二维数组
int **fnArr; //存放面法线的二维数组
string s1;
GLfloat f2,f3,f4;
为了给存放顶点法线等二维数组分配存储空间,需要知道顶点和法线等的数量,使用下面的函数计算点、法线、面的数量:
int readfile(string addrstr) //将文件内容读到数组中去
{
vArr=new GLfloat*[v_num];
for (inti=0;i<v_num;i++)
{
vArr[i]=newGLfloat[3];
}
vnArr=new GLfloat*[vn_num];
for (i=0;i<vn_num;i++)
{
vnArr[i]=newGLfloat[3];
}
fvArr=new int*[f_num];
fnArr=new int*[f_num];
for (i=0;i<f_num;i++)
{
fvArr[i]=newint[3];
fnArr[i]=newint[3];
}
ifstream infile(addrstr.c_str());
string sline;//每一行
int ii=0,jj=0,kk=0;
while(getline(infile,sline))
{
if(sline[0]=='v')
{
if(sline[1]=='n')//vn
{
istringstream sin(sline);
sin>>s1>>f2>>f3>>f4;
vnArr[ii][0]=f2;
vnArr[ii][1]=f3;
vnArr[ii][2]=f4;
ii++;
}
else//v
{
istringstream sin(sline);
sin>>s1>>f2>>f3>>f4;
vArr[jj][0]=f2;
vArr[jj][1]=f3;
vArr[jj][2]=f4;
jj++;
}
}
if (sline[0]=='f') //读取面
{
istringstreamin(sline);
GLfloata;
in>>s1;//去掉前缀f
inti,k;
for(i=0;i<3;i++)
{
in>>s1;
cout<<s1<<endl;
//取得顶点索引和法线索引
a=0;
for(k=0;s1[k]!='/';k++)
{
a=a*10+(s1[k]-48);
}
fvArr[kk][i]=a;
a=0;
for(k=k+2;s1[k];k++)
{
a=a*10+(s1[k]-48);;
}
fnArr[kk][i]=a;
}
kk++;
}
}
return 0;
}
然后在绘制之前,初始化时,调用这两个函数读取模型即可:
getLineNum("wan.obj");
readfile("wan.obj");
相应的绘制代码:
for (inti=0;i<f_num;i++)
{
glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
glBegin(GL_TRIANGLES);
glNormal3f(vnArr[fnArr[i][0]-1][0],vnArr[fnArr[i][0]-1][1],
vnArr[fnArr[i][0]-1][2]);
glVertex3f(vArr[fvArr[i][0]-1][0],vArr[fvArr[i][0]-1][1],
vArr[fvArr[i][0]-1][2]);
glNormal3f(vnArr[fnArr[i][1]-1][0],vnArr[fnArr[i][1]-1][1],
vnArr[fnArr[i][1]-1][2]);
glVertex3f(vArr[fvArr[i][1]-1][0],vArr[fvArr[i][1]-1][1],
vArr[fvArr[i][1]-1][2]);
glNormal3f(vnArr[fnArr[i][2]-1][0],vnArr[fnArr[i][2]-1][1],
vnArr[fnArr[i][2]-1][2]);
glVertex3f(vArr[fvArr[i][2]-1][0],vArr[fvArr[i][2]-1][1],
vArr[fvArr[i][2]-1][2]);
glEnd();
}
这样就完成了绘制,上面的代码仅仅针对我的wan.obj这个文件,对于想读取其他的obj文件,相应的分配一个存储空间,读取相应的数据,然后在绘制时使用这些数据就行了。
******************************
相关文章推荐
- OpenGL读取Obj模型文件
- OpenGL读取Obj模型文件
- OpenGL读取Obj模型文件
- OpenGL读取obj模型 源码 以及文件解析
- opengl读取OBJ模型文件
- opengl读取OBJ模型文件
- obj 文件读取 openGL 显示3D图
- DirectX11 With Windows SDK--19 模型加载:obj格式的读取及使用二进制文件提升读取效率
- opengl | openmesh 读取显示3d模型文件
- opengl解析obj模型文件
- OPENGL读取OBJ模型(包围盒、法向等计算)附加源码与资源下载页面
- 项目实践与用OpenGL实现OBJ模型文件的读入
- opengl | openmesh 读取显示3d模型文件
- [计算机图形学] OpenGL读取obj文件并显示其3D效果
- OpenGL读取PLY模型文件并绘制 Read and draw ply model by OpenGL
- MFC环境下搭建OpenGL框架并进行数据文件读取以及模型绘制的注意事项
- 读取Obj格式的模型文件(Dx10)
- 孙其功陪你学之——OpenGL加载OBJ模型文件并进行纹理修饰
- Opengl教程之读取stl文件并绘制在picturecontrol控件内
- TensorFlow学习Day3读取csv文件,动手写个logistic,softmax分类模型