利用OpenGL进行模型表现
2013-04-27 12:26
555 查看
这篇文章介绍一下用OpenGL来表现模型对象。比如用3DMAX等工具做好一个模型后,我们希望能够在自己的程序中使用它。一般使用这些工具的导出功能导出特定的文件,然后读取其中我们感兴趣的部分,再把这些感兴趣的数据在我们的程序中组织好就可以了。利用OpenGL来表现静态模型是很简单的。
这里为了简单起见,模型对象文件为txt文本,只包含了模型的顶点个数,面个数,顶点坐标,索引信息。当然在一些复杂的模型文件中,还包括法线,贴图坐标等等信息。重要的问题是,如何组织这些信息。
如上面的信息一样,第一行数据为该模型的顶点个数,第二行数据为该模型的三角形面个数。后面的数据分别人顶点坐标和每一个面的顶点索引信息。很自然的,首先可以想到要在程序中表现该模型,需要vertex的数据类型,于是可以定义这样的结构体,可以包含顶点的坐标和法线信息。
[cpp] view
plaincopy
struct Face
{
int Idx[3];
};
一个面由3个顶点构成,只要有了顶点的索引就可以了。最后就是模型的数据类型了。
[cpp]
view plaincopy
struct Model
{
int vNum;
int fNum;
Vertex *VertList;
Face *FaceList;
};
这个数据类型包含了模型顶点数vNum,面数fNum,指向顶点列表的指针VertList和指向面列表的指针FaceList。然后这样只要用Model来定义一个模型就可以了。Model MyModel;下面来看看如果从文件中读取信息。可以把读取文件的部分写成一个函数,比如叫LoadModel,函数的参数只需要提供文件名就可以了。
上面的代码比较长,但是内容很简单。首先打开文件,然后分别以整型的方式读取顶点个数和面个数,保存在结构体变量中。然后为顶点列表和面列表动态申请内存空间,在循环中分别读取每个数据。由于顶点包括法线信息,所以这里还进行了法线的计算。这里采用平均法线,即把一个顶点的多个法线加在一起,然后再进行单位化。由于一个顶点可能被用于多个面,所以该顶点的法线就是所有面的面法线的和。然后在程序初始化的时候,我们就可以调用这个函数来进行对模型的初始化。最后就是里利用OpenGL来渲染模型了。有了上面的所有信息,要渲染模型就很简单了。我们只用利用画三角形的方法就可以了。
这里为了简单起见,模型对象文件为txt文本,只包含了模型的顶点个数,面个数,顶点坐标,索引信息。当然在一些复杂的模型文件中,还包括法线,贴图坐标等等信息。重要的问题是,如何组织这些信息。
[cpp] view plaincopy219483 435667 3.031209 1.875606 1.422974 3.030240 1.863352 1.404835 3.030240 1.863352 1.404835 3.021032 1.846390 1.404004 3.021032 1.846390 1.404004 3.050020 1.888645 1.389481 3.040140 1.883706 1.400404 . . . 2869 2977 2973 2973 2974 2869 2979 2978 2975 2980 2979 2976 2976 2979 2975 2982 3104 3101 3096 2983 2982 3094 2984 3096 2984 2983 3096 3094 2972 2984 2979 2873 2978 2979 2980 2988 . .
如上面的信息一样,第一行数据为该模型的顶点个数,第二行数据为该模型的三角形面个数。后面的数据分别人顶点坐标和每一个面的顶点索引信息。很自然的,首先可以想到要在程序中表现该模型,需要vertex的数据类型,于是可以定义这样的结构体,可以包含顶点的坐标和法线信息。
[cpp] view plaincopystruct Vertex { double P[3]; double N[3]; }; 这里的顶点结构包含了顶点坐标P和法线N。然后就是面的数据类型:
[cpp] view
plaincopy
struct Face
{
int Idx[3];
};
一个面由3个顶点构成,只要有了顶点的索引就可以了。最后就是模型的数据类型了。
[cpp]
view plaincopy
struct Model
{
int vNum;
int fNum;
Vertex *VertList;
Face *FaceList;
};
这个数据类型包含了模型顶点数vNum,面数fNum,指向顶点列表的指针VertList和指向面列表的指针FaceList。然后这样只要用Model来定义一个模型就可以了。Model MyModel;下面来看看如果从文件中读取信息。可以把读取文件的部分写成一个函数,比如叫LoadModel,函数的参数只需要提供文件名就可以了。
[cpp] view plaincopyvoid LoadModel(char *fname) { FILE *fp; fp = fopen(fname, "r"); fscanf(fp, "%d", &MyModel.vNum); fscanf(fp, "%d", &MyModel.fNum); MyModel.VertList = new Vertex [MyModel.vNum]; MyModel.FaceList = new Face [MyModel.fNum]; int vNum = MyModel.vNum; int fNum = MyModel.fNum; for (int i = 0; i < vNum; ++i) { double x, y, z; fscanf(fp, "%lf %lf %lf", &x, &y, &z); MyModel.VertList[i].P[0] = x; MyModel.VertList[i].P[1] = y; MyModel.VertList[i].P[2] = z; MyModel.VertList[i].N[0] = 0.0; MyModel.VertList[i].N[1] = 0.0; MyModel.VertList[i].N[2] = 0.0; } for (int i = 0; i < fNum; ++i) { int idx0, idx1, idx2; fscanf(fp, "%d %d %d", &idx0, &idx1, &idx2); MyModel.FaceList[i].Idx[0] = idx0; MyModel.FaceList[i].Idx[1] = idx1; MyModel.FaceList[i].Idx[2] = idx2; double x0, y0, z0; double x1, y1, z1; double x2, y2, z2; x0 = MyModel.VertList[idx0].P[0]; y0 = MyModel.VertList[idx0].P[1]; z0 = MyModel.VertList[idx0].P[2]; x1 = MyModel.VertList[idx1].P[0]; y1 = MyModel.VertList[idx1].P[1]; z1 = MyModel.VertList[idx1].P[2]; x2 = MyModel.VertList[idx2].P[0]; y2 = MyModel.VertList[idx2].P[1]; z2 = MyModel.VertList[idx2].P[2]; double ax, ay, az; double bx, by, bz; double nx, ny, nz; ax = x1 - x0; ay = y1 - y0; az = z1 - z0; bx = x2 - x0; by = y2 - y0; bz = z2 - z0; nx = ay * bz - az * by; ny = az * bx - ax * bz; nz = ax * by - ay * bx; MyModel.VertList[idx0].N[0] += nx; MyModel.VertList[idx0].N[1] += ny; MyModel.VertList[idx0].N[2] += nz; MyModel.VertList[idx1].N[0] += nx; MyModel.VertList[idx1].N[1] += ny; MyModel.VertList[idx1].N[2] += nz; MyModel.VertList[idx2].N[0] += nx; MyModel.VertList[idx2].N[1] += ny; MyModel.VertList[idx2].N[2] += nz; } for (int i = 0; i < vNum; ++i) { double nx, ny, nz; nx = MyModel.VertList[i].N[0]; ny = MyModel.VertList[i].N[1]; nz = MyModel.VertList[i].N[2]; double norm = sqrt(nx * nx + ny * ny + nz * nz); MyModel.VertList[i].N[0] /= norm; MyModel.VertList[i].N[1] /= norm; MyModel.VertList[i].N[2] /= norm; } }
上面的代码比较长,但是内容很简单。首先打开文件,然后分别以整型的方式读取顶点个数和面个数,保存在结构体变量中。然后为顶点列表和面列表动态申请内存空间,在循环中分别读取每个数据。由于顶点包括法线信息,所以这里还进行了法线的计算。这里采用平均法线,即把一个顶点的多个法线加在一起,然后再进行单位化。由于一个顶点可能被用于多个面,所以该顶点的法线就是所有面的面法线的和。然后在程序初始化的时候,我们就可以调用这个函数来进行对模型的初始化。最后就是里利用OpenGL来渲染模型了。有了上面的所有信息,要渲染模型就很简单了。我们只用利用画三角形的方法就可以了。
[cpp] view plaincopyglBegin(GL_TRIANGLES); for (int i = 0; i < MyModel.fNum; ++i) { int idx0, idx1, idx2; idx0 = MyModel.FaceList[i].Idx[0]; idx1 = MyModel.FaceList[i].Idx[1]; idx2 = MyModel.FaceList[i].Idx[2]; glNormal3dv(MyModel.VertList[idx0].N); glVertex3dv(MyModel.VertList[idx0].P); glNormal3dv(MyModel.VertList[idx1].N); glVertex3dv(MyModel.VertList[idx1].P); glNormal3dv(MyModel.VertList[idx2].N); glVertex3dv(MyModel.VertList[idx2].P); } glEnd(); 可以看到,渲染部分很简单,就是在循环中依次访问面列表中每个面。并且给出每个面每个顶点的法线。
相关文章推荐
- 利用opengl进行模型表现
- 利用OpenGL进行模型表现
- 利用OpenGL进行模型表现
- 利用NABC模型进行竞争性需求分析
- 利用训练好的参数模型对图片进行分类
- 机器学习笔记-利用线性模型进行分类
- Qt环境下利用OpenGL显示三维模型
- Tensorflow学习教程------利用卷积神经网络对mnist数据集进行分类_训练模型
- Tensorflow学习教程------利用卷积神经网络对mnist数据集进行分类_利用训练好的模型进行分类
- 计算机图形学-实验3-掌握利用OpenGL函数进行鼠标、键盘操作,创建菜单
- 2.利用NABCD模型进行竞争性需求分析
- 利用NABCD模型进行竞争性需求分析
- 利用NABCD模型进行竞争性需求分析
- 在OpenGL中利用shader进行实时瘦脸大眼等脸型微调
- 孙其功陪你学之——OpenGL加载OBJ模型文件并进行纹理修饰
- C++搭建框架,利用OpenGL、GDAL、Qt进行分块显示遥感影像
- 利用NABCD模型进行竞争性需求分析
- 利用NABCD模型进行竞争性需求分析
- 在对话框picture control中利用opengl进行绘图
- 【django】利用fbv模型与orm进行页面登录操作