opengl读取OBJ模型文件

来源:互联网 发布:淘宝销售统计 编辑:程序博客网 时间:2024/04/30 21:23
原文地址:


http://blog.sina.com.cn/s/blog_6db61ebf01017325.html


要顺利读取obj模型文件,先要了解obj文件的格式。OBJ文件格式是非常简单的,它以纯文本的形式存储了模型的顶点、法线和纹理坐标和材质使用信息。

OBJ文件的每行的格式如下:前缀参数1参数2参数3 ...

其中,前缀标识了这一行所存储的信息类型。参数则是具体的数据。

OBJ文件常见的的前缀有:

§ v 表示本行指定一个顶点。 前缀后跟着3个单精度浮点数,分别表示该定点的XYZ坐标值


§ vt 表示本行指定一个纹理坐标。此前缀后跟着两个单精度浮点数。分别表示此纹理坐标的UV


§ vn 表示本行指定一个法线向量。此前缀后跟着3个单精度浮点数,分别表示该法向量的XYZ坐标值


§ f 表示本行指定一个表面(Face)。一个表面实际上就是一个三角形图元。此前缀行的参数格式后面将详细介绍。


§ usemtl 此前缀后只跟着一个参数。该参数指定了从此行之后到下一个以usemtl开头的行之间的所有表面所使用的材质名称。该材质可以在此OBJ文件所附属的MTL文件中找到具体信息。


§ mtllib 此前缀后只跟着一个参数。该参数指定了此OBJ文件所使用的材质库文件(*.mtl)的文件路径


现在,我们再来看一下OBJ文件的结构。在一个OBJ文件中,首先有一些以vvtvn前缀开头的行指定了所有的顶点、纹理坐标、法线的坐标。然后再由一些以f开头的行指定每一个三角形所对应的顶点、纹理坐标和法线的索引。在顶点、纹理坐标和法线的索引之间,使用符号“/”隔开的。

一个f行可以以下面几种格式出现:

§ f  1  2  3 这样的行表示以第123号顶点组成一个三角形。

§ f  1/3  2/5  3/4 这样的行表示以第123号顶点组成一个三角形,其中第一个顶点的纹理坐标的索引值为3,第二个顶点的纹理坐标的索引值为5,第三个顶点的纹理坐标的索引值为4

§ f  1/3/4  2/5/6  3/4/2 这样的行表示以第123号顶点组成一个三角形,其中第一个顶点的纹理坐标的索引值为3,其法线的索引值是4;第二个顶点的纹理坐标的索引值为5,其法线的索引值是6;第三个顶点的纹理坐标的索引值为6,其法线的索引值是2

§ f  1//4  2//6  3//2这样的行表示以第123号顶点组成一个三角形,且忽略纹理坐标。其中第一个顶点的法线的索引值是4;第二个顶点的法线的索引值是6;第三个顶点的法线的索引值是2

值得注意的是文件中的索引值是以1作为起点的,这一点与C语言中以0作为起点有很大的不同。在渲染的时候应注意将从文件中读取的坐标值减去1。

   由于通常我们拿到的文件中只出现顶点和法线数据,每个面存储顶点和法线索引,所以我们要声明如下几个全局函数:

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;


为了给存放顶点法线等二维数组分配存储空间,需要知道顶点和法线等的数量,使用下面的函数计算点、法线、面的数量:

void getLineNum(const std::string & sFileName)
{
 ifstream infile(sFileName.c_str());
 string sline;
 v_num=vn_num=f_num=0;
 getline(infile,sline);
 //while(getline(infile,sline))
 while(sline.size()!=0)
 {//从指定文件逐行读取
  if(sline[0]=='v')
  {
   if(sline[1]=='n')
    vn_num++;
   else
    v_num++;
  }
  else if(sline[0]=='f')
       f_num++;
     getline(infile,sline);
 }
 infile.close();
}

用下面的函数把文件内容读到上面定义的数组中去

int readfile(string addrstr)

{

vArr=new GLfloat*[v_num];

for (int i=0;i<v_num;i++)

{

  vArr[i]=new GLfloat[3];

}

vnArr=new GLfloat*[vn_num];

for (i=0;i<vn_num;i++)

{

  vnArr[i]=new GLfloat[3];

}


fvArr=new int*[f_num];


fnArr=new int*[f_num];


for (i=0;i<f_num;i++)


{


  fvArr[i]=new int[3];


  fnArr[i]=new int[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') //读取面

{

  istringstream in(sline);


  GLfloat a;

  in>>s1;//去掉前缀f

  int i,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("cow.obj");


readfile("cow.obj");


相应的绘制代码:


for (int i=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();


}


如果想读取其他的obj文件,相应的分配一个存储空间,读取相应的数据,然后在绘制时使用这些数据就行了。

0 0
原创粉丝点击