实验四几何物体的表示——三角网格的表示与显示1.实验目的●熟悉三角网格的表示●熟悉三角网格的显示●熟悉纹理的显示2.实验内容●设计三角网格的数据结构●解析Obj文件格式的三角网格●显示三角网格及纹理贴图3.实验指导存储三角网格数据的文件格式多种多样,常见的文件格式有:●Wavefront OBJ (*.0bj)●3D Max (*.max, *.3ds)●VRML (*.vrl)●Inventor (*.iv)●PLY (*.ply, *,ply2)本实验主要以OBJ文件为例,了解其数据格式,建立三角网格在内存中的数据结构,从而将文件中的数据读取到内存,并最终显示出来。
3.1OBJ文件格式OBJ文件是Alias|Wavefront公司为他的一套基于工作站的3D建模和动画软件“Advanced Visualizer”开发的一种标准3D模型文件格式,很适合用于3D软件模型之间的数据交换。
目前几乎所有知名的3D软件如3dsMax,LightWave,Maya都支持OBJ文件的读写。
OBJ文件是一种纯文本文件,可以直接用写字板打开进行查看和编辑修改。
这种文件以纯文本的形式存储了模型的顶点、法线和纹理坐标和材质使用信息。
OBJ的每一行,都有极其相似的格式,每行的格式如下:其中,前缀标识了这一行所存储的信息类型,参数则是具体的数据。
OBJ文件的前缀可以有:面的格式说明:每个三角面片的数据由f开头,后面跟组成该三角面片的各顶点的顶点坐标索引,纹理坐标索引,顶点法向索引,其格式为:f 顶点坐标索引/纹理坐标索引/顶点法向索引……其中纹理坐标索引和顶点法向索引可以为空,如果为空的索引位于末尾时,’/’也可以省略,例如:f 1 2 3这样的行表示以第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。
除此之外,以“#”开头的表示注释,以g开头的表示组的前缀。
但这些前缀并不影响模型的外观,因此我们可以忽略它们。
下面来看一个具体的实例,该文件是用3dsMax创建的一个长方体存储成OBJ 文件的结果:Obj格式对纹理的支持则是通过关键字mtllib指定的。
从上述例子可以看出,mtllib指定了对应于该obj文件的材质文件,材质文件的格式类似于obj的文件格式,也是由关键字前缀+对应的内容组成。
Ka,kd,ks分别指定了物体材质中环境光,漫反射光,镜面光的颜色,map_Kd 指定了纹理图像的路径,通过该纹理图像的路径,可以读取obj文件对应的纹理图像。
3.2 三角网格的数据结构三角网格的数据由几何和连接关系两部分组成,几何包括顶点的x ,y ,z 坐标及顶点的法向量等,连接关系即三角网格是如何连在一块的。
如图0.1所示。
3v 12v 4v 6v f 1f 2f 3f 4e 1e 2e 3e 4e 5e 6e 7e 8e 9(a ) (b )图0.1三角网格数据常用的三角网格数据结构包括: ● 顶点—边(Vertex-Edge ) ● 顶点—面(Vertex-Face )● 邻接矩阵(Adjacency matrix )和邻接表(Adjacency List ) ● 顶点—边—面(Vertex-Edge-Face ) ● 半边结构(Half Edge )在各种数据结构的表示方式中,几何的表示基本一致,可以用链表或者数组的方式加以存储,对于图0.1.a 中的网格,可表示为:区别的地方主要在于连接关系的存储,不同的存储方式决定了顶点、边、面的邻接关系的复杂度:顶点—边(Vertex-Edge)的数据结构只包含边的连接关系,对于图0.1.a中的网格,可表示为:顶点—面(Vertex-Face)的数据结构只包含面的连接关系,对于图0.1.a中的网格,可表示为:邻接矩阵(Adjacency matrix)的数据结构以矩阵的方式存储点与点之间的连接关系,对于图0.1.a中的网格,可表示为:顶点—边—面(Vertex-Edge-Face)的数据结构同时包含了边和面的连接关系,对于图0.1.a中的网格,可表示为:数据结构的选取,取决于对三角网格所进行的操作,如果需要频繁的对三角面片进行删除和插入的操作,如对三角网格进行简化时,则需要利用链表而非数组的方式来存储顶点的几何信息及连接关系,如果需要频繁的对顶点、边、面的邻接关系进行查询,则需要在避免过多冗余信息的同时,尽可能保存多的邻接关系,使得查询的复杂度为(1),如在顶点—边—面(Vertex-Edge-Face)的数据结构中对边的信息增加保存邻接三角形的信息,对顶点增加邻接边的信息。
对于图0.1.a中的网格,边的信息变成为:3.3三角网格的绘制在OpenGL中,任何复杂的几何物体最终都要描述成一个顶点的有序集合,即使是连续的曲线曲面,也需要首先对这些曲线曲面进行离散化,而后这些有序的顶点集合再组装成基本图元的集合。
OpenGL中,基本图元包括点、线段、三角形、多边形,而三角网格即为三角形这种基本图元的集合。
顶点表示:glVertex{2, 3, 4}{s, i, f, d}[v](坐标)此函数指定顶点的各个坐标分量。
基本几何图元基本几何图元的定义,以函数glBegin()开始,glEnd()结束,两函数之间的部分由组成该几何图元的顶点序列组成,如显示一个三角形,可以表示成:glBegin(GL_TRIANGLES);glVertex3f(x0, y0, z0);glVertex3f(x1, y1, z1);glVertex3f(x2, y2, z2);glEnd();glBegin(mode);此函数标志着描述一个几何图元的顶点序列的开始,图元的类型由参数mode指定,如表1所示,各个图元类型的效果如图0.2所示。
表1几何图元的名称和意义图0.2几何图元的类型glEnd()此函数标识着顶点列表的结束。
glBegin()和glEnd()函数对之间除了可指定顶点坐标的信息外,还可以指定法向、颜色、纹理坐标等信息,分别通过glNormal*(),glColor*(),glTexCoord()来实现。
以下代码将绘制一个红色和绿色三角形。
glBegin(GL_TRIANGLES);//red triangleglColor3f(1.0f, 0.0f, 0.0f, 1.0f);glVertex3f(x0, y0, z0);glVertex3f(x1, y1, z1);glVertex3f(x2, y2, z2);//green triangleglColor3f(0.0f, 1.0f, 0.0f, 1.0f);glVertex3f(x3, y3, z3);glVertex3f(x4, y4, z4);glVertex3f(x5, y5, z5);glEnd();三角网格中面的法向及顶点法向的计算每个面片处的法向可以通过两条边的叉乘得到,而顶点处的法向则可以取为相邻各三角面片法向的平均值,如错误!未找到引用源。
所示。
01 n = (v1 - v0) x (v2 - v0) n = ∑ni图0.3三角网格中免得法向及顶点法向的计算方法Obj文件中不但包含顶点几何坐标,还可以指定纹理数据,mtlib命令指定了材质属性的文件,材质属性文件中可以指定纹理图像所在的文件。
纹理的显示包括如下三个主要步骤:(1)生成纹理数据(2)将纹理数据载入纹理内存(3)将纹理数据映射到物体表面3.4生成纹理数据纹理数据可以由程序生成,也可以通过载入图像文件得到。
对于纹理映射而言,其目的一般是为了增强图形的真实感,因此通过拍摄实际场景得到相应的图片,然后再将图片映射到物体表面是使用较多的一种方法。
本实验也将采用载入图像文件的方法得到纹理数据。
3.4.1位图图像数据对于位图图像数据,可以通过MFC中的LoadImage()函数载入内存,而后通过CBitmap类中的GetBitmap()函数获取图像的具体信息,如图像的宽度,高度等信息,这些信息由BITMAP这个数据结构表示,如图0.4所示。
但值得注意的是,通过LoadImage()函数从文件载入的位图图像,GetBitmap()函数获取的图像信息中不包含位图的像素信息,bmBits为空,这需要通过CBitmap类中另一个函数GetBitmapBits()获得。
位图数据使用完后,还要通过CBitmap::DeleteObject()函数进行内存释放。
MFC中获取位图信息的数据流如图0.5所示。
图0.4 BITMAP的数据结构图0.5 MFC中获取位图信息的数据流表示HANDLE LoadImage(hInstance, lpszName, uType, cxDesired, cyDesired, fuLoad) 该函数用于从文件和资源中装载图标、光标、或位图。
hInstance:指定装载图像的模块特例,对于从文件载入图像可以设置为NULL。
lpszName:图像文件的路径或资源名称uType:指定被装载图像类型。
此参数可以为下列值,其含义如下:IMAGE_BITMAP:装载位图;IMAGE_CURSOR:装载光标;IMAGE_ICON:装载图标。
cxDesired,cyDesired:指定图标、光标的宽度和高度,以像素为单位。
若为载入位图,通常指定为0。
fuLoad:指定装载的类型,对于载入位图图像而言,该参数设定为LR_LOADFROMFILE。
通常,载入位图图像的调用方式为:HBITMAP hBitmap = (HBITMAP)LoadImage(NULL, imagePath, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE)为获取位图的具体信息,需要将HBITAMP句柄转换为CBitmap类,这可以通过两种方式得到:(1)通过静态函数CBitmap::FromHandle()得到,即:CBitmap *pBitmap = CBitmap::FromHandle(hBitmap);这里需要注意的是,通过该函数得到位图变量pBitmap是一个指向临时CBitmap对象的指针,MFC框架会在空闲处理函数OnIdle()中自动释放这样的临时变量。