计算机图形学实验几何物体的表示三角网格的表示与显示.docx
- 文档编号:15110932
- 上传时间:2023-06-30
- 格式:DOCX
- 页数:24
- 大小:235.59KB
计算机图形学实验几何物体的表示三角网格的表示与显示.docx
《计算机图形学实验几何物体的表示三角网格的表示与显示.docx》由会员分享,可在线阅读,更多相关《计算机图形学实验几何物体的表示三角网格的表示与显示.docx(24页珍藏版)》请在冰点文库上搜索。
计算机图形学实验几何物体的表示三角网格的表示与显示
实验四几何物体的表示——三角网格的表示与显示
1.实验目的
●熟悉三角网格的表示
●熟悉三角网格的显示
●熟悉纹理的显示
2.实验内容
●设计三角网格的数据结构
●解析Obj文件格式的三角网格
●显示三角网格及纹理贴图
3.实验指导
存储三角网格数据的文件格式多种多样,常见的文件格式有:
●WavefrontOBJ(*.0bj)
●3DMax(*.max,*.3ds)
●VRML(*.vrl)
●Inventor(*.iv)
●PLY(*.ply,*,ply2)
本实验主要以OBJ文件为例,了解其数据格式,建立三角网格在内存中的数据结构,从而将文件中的数据读取到内存,并最终显示出来。
3.1OBJ文件格式
OBJ文件是Alias|Wavefront公司为他的一套基于工作站的3D建模和动画软件“AdvancedVisualizer”开发的一种标准3D模型文件格式,很适合用于3D软件模型之间的数据交换。
目前几乎所有知名的3D软件如3dsMax,LightWave,Maya都支持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)的文件路径
面的格式说明:
每个三角面片的数据由f开头,后面跟组成该三角面片的各顶点的顶点坐标索引,纹理坐标索引,顶点法向索引,其格式为:
f顶点坐标索引/纹理坐标索引/顶点法向索引……
其中纹理坐标索引和顶点法向索引可以为空,如果为空的索引位于末尾时,’/’也可以省略,例如:
f 123
这样的行表示以第1、2、3号顶点组成一个三角形,等同于1//2//3//。
f 1/32/53/4
这样的行表示以第1、2、3号顶点组成一个三角形,其中第一个顶点的纹理坐标的索引值为3,第二个顶点的纹理坐标的索引值为5,第三个顶点的纹理坐标的索引值为4。
f 1/3/42/5/63/4/2
这样的行表示以第1、2、3号顶点组成一个三角形,其中第一个顶点的纹理坐标的索引值为3,其法线的索引值是4;第二个顶点的纹理坐标的索引值为5,其法线的索引值是6;第三个顶点的纹理坐标的索引值为6,其法线的索引值是2。
f 1//42//63//2
这样的行表示以第1、2、3号顶点组成一个三角形,且忽略纹理坐标。
其中第一个顶点的法线的索引值是4;第二个顶点的法线的索引值是6;第三个顶点的法线的索引值是2。
除此之外,以“#”开头的表示注释,以g开头的表示组的前缀。
但这些前缀并不影响模型的外观,因此我们可以忽略它们。
下面来看一个具体的实例,该文件是用3dsMax创建的一个长方体存储成OBJ文件的结果:
#Max2ObjVersion4.0Mar10th,2001
#
mtllib./Box.mtl
g
#object(null)tocome...
#
v-46.508743-45.05295950.796341
v49.442947-45.05295950.796341
v-46.508743-45.052959-48.019585
v49.442947-45.052959-48.019585
v-46.50874348.03450450.796341
v49.44294748.03450450.796341
v-46.50874348.034504-48.019585
v49.44294748.034504-48.019585
#8vertices
vt0.0000000.0000000.000000
vt1.0000000.0000000.000000
vt0.0000001.0000000.000000
vt1.0000001.0000000.000000
vt0.0000000.0000000.000000
vt1.0000000.0000000.000000
vt0.0000001.0000000.000000
vt1.0000001.0000000.000000
vt0.0000000.0000000.000000
vt1.0000000.0000000.000000
vt0.0000001.0000000.000000
vt1.0000001.0000000.000000
#12texturevertices
g(null)
f1/103/124/11
f4/112/91/10
f5/96/108/12
f8/127/115/9
f1/52/66/8
f6/85/71/5
f2/14/28/4
f8/46/32/1
f4/53/67/8
f7/88/74/5
f3/11/25/4
f5/47/33/1
#12faces
Obj格式对纹理的支持则是通过关键字mtllib指定的。
从上述例子可以看出,mtllib指定了对应于该obj文件的材质文件,材质文件的格式类似于obj的文件格式,也是由关键字前缀+对应的内容组成。
newmtlblinn1SG
illum4
Ka0.20.20.2
Kd111
Ks0.20.20.2
Tf1.001.001.00
map_Kdcarp.bmp
Ka,kd,ks分别指定了物体材质中环境光,漫反射光,镜面光的颜色,map_Kd指定了纹理图像的路径,通过该纹理图像的路径,可以读取obj文件对应的纹理图像。
3.2三角网格的数据结构
三角网格的数据由几何和连接关系两部分组成,几何包括顶点的x,y,z坐标及顶点的法向量等,连接关系即三角网格是如何连在一块的。
如图3.1所示。
(a)(b)
图3.1三角网格数据
常用的三角网格数据结构包括:
●顶点—边(Vertex-Edge)
●顶点—面(Vertex-Face)
●邻接矩阵(Adjacencymatrix)和邻接表(AdjacencyList)
●顶点—边—面(Vertex-Edge-Face)
●半边结构(HalfEdge)
在各种数据结构的表示方式中,几何的表示基本一致,可以用链表或者数组的方式加以存储,对于图3.1.a中的网格,可表示为:
顶点
坐标
v1
(x1,y1,z1)
v2
(x2,y2,z2)
v3
(x3,y3,z3)
v4
(x4,y4,z4)
v5
(x5,y5,z5)
v6
(x6,y6,z6)
区别的地方主要在于连接关系的存储,不同的存储方式决定了顶点、边、面的邻接关系的复杂度:
顶点—边(Vertex-Edge)的数据结构只包含边的连接关系,对于图3.1.a中的网格,可表示为:
边
顶点
e1
(v1,v3)
e2
(v1,v2)
e3
(v2,v3)
e4
(v2,v4)
e5
(v3,v5)
e6
(v3,v6)
e7
(v4,v6)
e8
(v4,v5)
e9
(v5,v6)
顶点—面(Vertex-Face)的数据结构只包含面的连接关系,对于图3.1.a中的网格,可表示为:
面
顶点(CCW)
f1
(v1,v2,v3)
f2
(v2,v4,v3)
f3
(v3,v4,v6)
f4
(v4,v5,v6)
邻接矩阵(Adjacencymatrix)的数据结构以矩阵的方式存储点与点之间的连接关系,对于图3.1.a中的网格,可表示为:
v1
v2
v3
v4
v5
v6
v1
1
1
v2
1
1
1
v3
1
1
1
1
v4
1
1
1
1
v5
1
1
v6
1
1
1
顶点—边—面(Vertex-Edge-Face)的数据结构同时包含了边和面的连接关系,对于图3.1.a中的网格,可表示为:
边
顶点
e1
(v1,v3)
e2
(v1,v2)
e3
(v2,v3)
e4
(v2,v4)
e5
(v3,v5)
e6
(v3,v6)
e7
(v4,v6)
e8
(v4,v5)
e9
(v5,v6)
面
顶点(CCW)
边
f1
(v1,v2,v3)
(e1,e2,e3)
f2
(v2,v4,v3)
(e3,e4,e5)
f3
(v3,v4,v6)
(e5,e7,e6)
f4
(v4,v5,v6)
(e7,e8,e9)
数据结构的选取,取决于对三角网格所进行的操作,如果需要频繁的对三角面片进行删除和插入的操作,如对三角网格进行简化时,则需要利用链表而非数组的方式来存储顶点的几何信息及连接关系,如果需要频繁的对顶点、边、面的邻接关系进行查询,则需要在避免过多冗余信息的同时,尽可能保存多的邻接关系,使得查询的复杂度为
,如在顶点—边—面(Vertex-Edge-Face)的数据结构中对边的信息增加保存邻接三角形的信息,对顶点增加邻接边的信息。
对于图3.1.a中的网格,边的信息变成为:
边
顶点
邻接面
e1
(v1,v3)
f1
e2
(v1,v2)
f1
e3
(v2,v3)
f1,f2
e4
(v2,v4)
f2
e5
(v3,v5)
f2,f3
e6
(v3,v6)
f3
e7
(v4,v6)
f3,f4
e8
(v4,v5)
f4
e9
(v5,v6)
f4
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指定,如表3所示,各个图元类型的效果如图3.2所示。
表3几何图元的名称和意义
值
意义
GL_POINTS
独立的点
GL_LINES
一对顶点表示一条线段
GL_LINE_STRIP
一系列相连的线段
GL_LINE_LOOP
同上,在开始和结束顶点之间增加一条线段,使其闭合
GL_TRIANGLES
三个顶点表示一个三角形
GL_TRIANGLE_STRIP
连接的三角形带
GL_TRIANGLE_FAN
连接的三角形扇面
GL_QUADS
四个顶点表示一个四边形
GL_QUAD_STRIP
连接的四边形带
GL_POLYGON
简单的、凸多边形边界
图3.2几何图元的类型
glEnd()
此函数标识着顶点列表的结束。
glBegin()和glEnd()函数对之间除了可指定顶点坐标的信息外,还可以指定法向、颜色、纹理坐标等信息,分别通过glNormal*(),glColor*(),glTexCoord()来实现。
以下代码将绘制一个红色和绿色三角形。
glBegin(GL_TRIANGLES);
//redtriangle
glColor3f(1.0f,0.0f,0.0f,1.0f);
glVertex3f(x0,y0,z0);
glVertex3f(x1,y1,z1);
glVertex3f(x2,y2,z2);
//greentriangle
glColor3f(0.0f,1.0f,0.0f,1.0f);
glVertex3f(x3,y3,z3);
glVertex3f(x4,y4,z4);
glVertex3f(x5,y5,z5);
glEnd();
三角网格中面的法向及顶点法向的计算
每个面片处的法向可以通过两条边的叉乘得到,而顶点处的法向则可以取为相邻各三角面片法向的平均值,如图2.14所示。
图3.3三角网格中免得法向及顶点法向的计算方法
Obj文件中不但包含顶点几何坐标,还可以指定纹理数据,mtlib命令指定了材质属性的文件,材质属性文件中可以指定纹理图像所在的文件。
纹理的显示包括如下三个主要步骤:
(1)生成纹理数据
(2)将纹理数据载入纹理内存
(3)将纹理数据映射到物体表面
3.4生成纹理数据
纹理数据可以由程序生成,也可以通过载入图像文件得到。
对于纹理映射而言,其目的一般是为了增强图形的真实感,因此通过拍摄实际场景得到相应的图片,然后再将图片映射到物体表面是使用较多的一种方法。
本实验也将采用载入图像文件的方法得到纹理数据。
3.4.1位图图像数据
对于位图图像数据,可以通过MFC中的LoadImage()函数载入内存,而后通过CBitmap类中的GetBitmap()函数获取图像的具体信息,如图像的宽度,高度等信息,这些信息由BITMAP这个数据结构表示,如图4.1所示。
但值得注意的是,通过LoadImage()函数从文件载入的位图图像,GetBitmap()函数获取的图像信息中不包含位图的像素信息,bmBits为空,这需要通过CBitmap类中另一个函数GetBitmapBits()获得。
位图数据使用完后,还要通过CBitmap:
:
DeleteObject()函数进行内存释放。
MFC中获取位图信息的数据流如图4.2所示。
typedefstructtagBITMAP
{
LONGbmType;
LONGbmWidth;
LONGbmHeight;
LONGbmWidthBytes;
WORDbmPlanes;
WORDbmBitsPixel;
LPVOIDbmBits;
}BITMAP
图4.1BITMAP的数据结构
图4.2MFC中获取位图信息的数据流表示
HANDLELoadImage(hInstance,lpszName,uType,cxDesired,cyDesired,fuLoad)
该函数用于从文件和资源中装载图标、光标、或位图。
hInstance:
指定装载图像的模块特例,对于从文件载入图像可以设置为NULL。
lpszName:
图像文件的路径或资源名称
uType:
指定被装载图像类型。
此参数可以为下列值,其含义如下:
IMAGE_BITMAP:
装载位图;IMAGE_CURSOR:
装载光标;IMAGE_ICON:
装载图标。
cxDesired,cyDesired:
指定图标、光标的宽度和高度,以像素为单位。
若为载入位图,通常指定为0。
fuLoad:
指定装载的类型,对于载入位图图像而言,该参数设定为LR_LOADFROMFILE。
通常,载入位图图像的调用方式为:
HBITMAPhBitmap=(HBITMAP)LoadImage(NULL,imagePath,IMAGE_BITMAP,0,0,LR_LOADFROMFILE)
为获取位图的具体信息,需要将HBITAMP句柄转换为CBitmap类,这可以通过两种方式得到:
(4)通过静态函数CBitmap:
:
FromHandle()得到,即:
CBitmap*pBitmap=CBitmap:
:
FromHandle(hBitmap);
这里需要注意的是,通过该函数得到位图变量pBitmap是一个指向临时CBitmap对象的指针,MFC框架会在空闲处理函数OnIdle()中自动释放这样的临时变量。
因此,不适合于作为类的成员变量反复使用。
(5)通过成员函数Attach()实现,即:
CBitmapbitmap;
bitmap.Attach(hBitmap);
对于网格中的纹理数据而言,由于需要在整个程序运行期间保存该数据,通常采用第二种方式实现。
整个读取过程可以用代码表示为:
//readtheimage
HBITMAPhBitmap=(HBITMAP)LoadImage(NULL,texturePath,IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
if(hBitmap)
{
CBitmap*pBitmap=newCBitmap();
pBitmap->Attach(hBitmap);
pMesh->SetTexture(pBitmap);
}
位图图像的具体数据可以通过函数CBitmap:
:
GetBitmap()获得,即:
BITAMPbitmap;
pBitmap->GetBitmap(&bitmap);
BITMAP中包含了图像的宽度(bmWidth)、高度(bmHeight),每个像素的颜色位数(bmBitsPixel),图像的像素数据(bmBits)等信息,但对于从文件载入的位图图像数据,bmBits为空,即不能通过该方法获取位图的像素数据,需通过CBitmap:
:
GetBitmapBits()方法得到,其通常用法如下:
intnCount=bitmap.bmWidthBytes*bitmap.bmHeight;
unsignedchar*pData=newunsignedchar[nCount];
pMesh->m_pTexture->GetBitmapBits(nCount,pData);
……
delete[]pData;
这里需要考虑的另一个问题是:
Windows中位图数据的存储是从左下角开始的,而OpenGL中的纹理坐标原点则通常是在左上角,因此,如果要能够正常显示位图纹理,需要将位图数据沿水平方向进行镜像操作,如图4.3所示。
图4.3位图数据翻转示意图
其代码实现为:
//verticalflip
unsignedchar*temp=newunsignedchar[bitmap.bmWidthBytes];
intstart=0,target=nCount-bitmap.bmWidthBytes;
for(inti=0;i<(bitmap.bmHeight>>1);++i)
{
memcpy(temp,&pData[start],bitmap.bmWidthBytes);
memcpy(&pData[start],&pData[target],bitmap.bmWidthBytes);
memcpy(&pData[target],temp,bitmap.bmWidthBytes);
start+=bitmap.bmWidthBytes;
target-=bitmap.bmWidthBytes;
}
delete[]temp;
3.4.2常用图像数据类型
然而,目前图像文件的格式多种多样,常用的有bmp,jpg,png,tiff等,为了方便对多种图像格式的支持,增强代码的鲁棒性,我们利用OpenCV库来实现纹理图像的读取。
OpenCV(OpenComputerVision)是Intel开源计算机视觉库。
它由一系列C函数和少量C++类构成,实现了图像处理和计算机视觉方面的很多通用算法。
OpenCV库可以通过访问网址
3.4.3OpenCV的使用方法
同其他以动态链接库(dll,Dynamic-LinkLibrary)方式提供的功能库一样,需要将OpenCV相关的头文件,lib文件,dll文件放在系统能够找到的路径下。
这里所谓系统能够找到的路径,包括:
(1)当前路径;
(2)系统路径;(3)path所定义的路径。
本实验中需要用到的头文件、lib文件以及dll文件分别包括:
头文件:
highgui.h,该头文件同时依赖于cxcore.h,cxtypes.h,cxerror.h,cvver.h等头文件;
Lib文件:
cxcore.lib,highgui.lib;
Dll文件:
cxcore100.dll,highgui100.dll。
3.4.4图像文件的读取
cvLoadImage(filename,flags)
参数中filename即为要读入的图像文件的路径,flags指定读入图像的颜色和深度:
指定的颜色可以将输入的图片转为3信道(CV_LOAD_IMAGE_COLOR),单信道(CV_LOAD_IMAGE_GRAYSCALE),或者保持不变(CV_LOAD_IMAGE_ANYCOLOR)。
目前支持如下文件格式:
●Windows位图文件-BMP,DIB;
●JPEG文件-JPEG,JPG,JPE;
●便携式网络图片-PNG;
●便携式图像格式-PBM,PGM,PPM;
●Sunrasters-SR,RAS;
●TIFF文件-TIFF,TIF;
●OpenEXRHDR图片-EXR;
●JPEG2000图片-jp2。
同位图图像数据一样,如果图像数据是以左上角开始存储,通常需要沿水平方向进行翻转,这可以通过cvFlip()函数实现,即:
IplImage*pImage;
If(pImage
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 计算机 图形学 实验 几何 物体 表示 三角 网格 显示
![提示](https://static.bingdoc.com/images/bang_tan.gif)