版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
22三维图像可视化技术的设计与开发第一章医学图象可视化技术的研究引言现代各种医学成像技术如X射线断层成像(X-CT),磁共振成像(MRI),超声成像(US),单光子发射断层成像(SPECT和正电子发射断层成像(PET),为人体不同部位提供了多种不同时空分辨率的二维医学图像,形成了现代医学影像诊断学的基础,极大的推动了医学诊断和临床治疗的发展。随着现代计算机辅助技术、虚拟现实等现代技术的发展,人们对直观的三维医学图像的需求也日益增强,医学图像三维可视化近来成为医学成像发展的热点。1.2医学图像三维可视化技术1.2.1三维可视化概述医学图像的三维可视化的方法很多,但基本步骤大体相同。从CT/MRI或超声等成像系统获得二维断层图像,然后需要将图像格式(如DICOM)转化成计算机方便处理的格式。通过二维滤波,减少图像的噪声影响,提高信噪比和消除图像的尾迹。采取图像插值方法,对医学关键部位进行各向同性处理,获得体数据。经过三维滤波后,不同组织器官需要进行分割和归类,对同一部位的不同图像进行配准和融合,以利于进一步对某感兴趣部位的操作。根据不同的三维可视化要求和系统平台的能力,选择不同的方法进行三维体绘制,实现三维重构。1.2.2关键技术下面主要讨论在二维图像的获取后,进行三维图像重构过程中的一些关键技术:图像分割是三维重构的基础,分割效果直接影像三维重构的精确度。图像分割是将图像分割成有意义的子区域,由于医学图像的各区域没有清楚的边界,为了解决在医学图像分割中遇到不确定性的问题,引入模糊理论的模糊阀值、模糊边界和模糊聚类等概念。快速准确的分离出解剖结构和定位区域位置和形状,自动或半自动的图像分割方法是非常重要的。在实际应用中有聚类法、统计学模型、弹性模型、区域生长、神经网络等适用于医学图像分割的具体方法。由于可以对同一部位用不同的成像仪器多次成像,或用同一台仪器多次成像,这样产生了多模态图像。多模态图像提供的信息经常相互覆盖和具有互补性,为了综合使用多种成像模式以提供更全面的信息,需要对各个模态的原始图像进行配准和数据融合,其整个过程称为数据整合。整合的第一步是将多个医学图像的信息转换到一个公共的坐标框架内的研究,使多幅图像在空间域中达到几何位置的完全对应,称为三维医学图像的配准问题。建立配准关系后,将多个图像的数据合成表示的过程,称为融合。在医学应用中,不同模态的图像还提供了不互相覆盖的结构互补信息,比如,当CT提供的是骨信息,MRI提供的关于软组织的信息,所以可以用逻辑运算的方法来实现它们图像的合成。当分割归类或数据整合结束后,对体数据进行体绘制。体绘制一般分为直接体绘制和间接体绘制,由于三维医学图像数据量很大,采用直接体绘制方法,计算量过重,特别在远程应用和交互操作中,所以一般多采用间接体绘制。在图形工作站上可以进行直接体绘制,近来随着计算机硬件快速发展,新的算法,如三维纹理映射技术,考虑了计算机图形硬件的特定功能及体绘制过程中的各种优化方法,从而大大地提高了直接体绘制的速度。体绘制根据所用的投影算法不同加以分类,分为以对象空间为序的算法(又称为体素投影法)和以图像空间为序的算法(又称为光线投射法),一般来说,体素投影法绘制的速度比光线投射法快。由于三维医学图像的绘制目的在于看见内部组织的细节,真实感并不是最重要的,所以在医学应用中的绘制要突出特定诊断所需要的信息,而忽略无关信息。另外,高度的可交互性是三维医学图像绘制的另一个要求,即要求一些常见操作,如旋转,放大,移动,具有很好的实时性,或至少是在一个可以忍受的响应时间内完成。这意味着在医学图像绘制中,绘制时间短的可视化方法更为实用。未来的三位可视化技术将与虚拟现实技术相结合,不仅仅是获得体数据的工具,更主要的是能创造一个虚拟环境。1.3在医学诊断和临床中的应用1.3.1虚拟内窥镜虚拟内窥镜是利用CT、MRI或超声波获得二维断层结构图像,进行三维可视化处理,在计算机屏幕上生成具有内窥镜可视效果的、病人结构组织序列的三维可视化图像,结合虚拟现实技术,计算机可以显示连续的三维器官内腔结构图,并且可以沿这个虚拟的内部空腔做飞行观察,模拟传统的内窥检查过程,观察到类似于光学内窥镜观察到的影像,甚至还可以显示解剖结构的三维外观图像以及各个方位的CT和MRI切片图像。虚拟内窥镜结合了传统内窥镜和CT、MRI等断层成像的优点。作为新式的非侵入式疗法,不会产生感染出血等不良后果,具有重复性使用、动态病理分析等优点,此外可以模拟检查光学内窥镜不能检查的人体部位,如脑,内耳,脊髓管、血管等。在虚拟耳鼻喉镜、虚拟胃肠镜、虚拟食道-支气管等方面已经处于初期临床阶段,通用电器公司GE还开发了一套虚拟内窥镜医学应用系统VEMS(VirtualEndoscopyMedicalSystem)。图像分辨率是虚拟内窥镜应用的最大障碍,在临床应用中,虚拟内窥镜图像的分辨率比不上真正的光学内窥镜,这有待于高分辨率的医学成像系统的开发应用;由于准确配准的算法等限制也无法做到在解剖结构上很精确,在显示和实时交互方面还需要进一步研究,评价虚拟内窥镜系统的标准也在建立当中,所以虚拟内窥镜在临床上实际应用还需要在理沦上进一步完善和大规模临床试验。1.3.2计算机辅助手术和图像指导治疗技术在外科手术中引入计算机辅助手术系统,可以提高手术的可靠性和精度,减少外科创伤,制定更加科学的手术计划。例如早期在脑神经外科手术中的应用,应用CT图像引导手术导航系统,准确地定位脑肿瘤的位置和大小,并通过可视化手段让医生看到在病灶周围的血管结构和神经,再经过详细的手术计划论证,最后确定一个最佳的进刀方案,尽量减少肿瘤周围健康的脑神经和脑组织在手术中的损伤。医生可以结合可视化的脉管,进行切除计划的增加或减少,从而在实际中调整手术计划,这种手术计划的导航在三维中是可视化的,从而使得医生的主观决定得到更好的保证。肝脏肿瘤手术计划中三维可视化技术的应用,它显示了肝部主要脉管系统,肿瘤,安全区域边缘,以及肿瘤切割计划。不仅手术前进行三维重建的静态图像,而且可以实现在手术中显示手术场景计算机模型建模和手术工具、实际创伤及目标结构的空间位置、相互作用及形态改变,能够测试医生对治疗工具相对目标区的最佳定位能力,实现手术室中基于虚拟现实显示的手术导航。在放疗中,由CT与MR图像的融合提供的可视化图像为放疗提供指导。利用MR图像勾勒画出肿瘤的轮廓线,它描述肿瘤的大小;利用CT图像计算出放射剂量的大小以及剂量的分布,减少患者所受的辐射量,并及时修正治疗方案。1.3.3远程医疗中应用利用网络环境,对CT、MRI、超声等图像数据进行远程传送和数据共享,进一步可以实时协同可视化操作,最终达到远程放射计划与治疗、远程手术及远程医学培训等目的。远程医疗和远程手术的基础核心之一就是可视化的医学模型和图像,远程会诊和手术就依赖于三维可视化技术向分布式协同可视化的发展。分布式协同可视化是指多个用户同时进行数据的可视化操作,用户之间及用户与数据之间均可进行交互作用。由于远程手术系统的交互式操作,对三维医学图像体绘制和显示有更高的技术要求,其难点在于如何实现协同可视化。具体实现时,考虑在并行分布式网络计算的总体构架下,采用服务器-客户端总体结构,使用VRML(virtualrealitymodelinglanguage)、JAVA,OpenGL等编程工具来实现基于Web的医学图像三维可视化。分布式协同可视化在远程医疗领域一个例子是TelelnViVo系统,利用自由手臂上的超声传感器获得图像和位置等信息,合成三维图像,存储为符合DICOM3标准格式,通过TCP/IP通讯协议传输。另外医学图像三维可视化在医学计算机辅助教学中得到应用,可以模拟真实人体环境,进行模拟解剖等训练。美国国家医学图书馆提供的虚拟可视化人体数据库VHD(VisibleHumanDatasets),不仅呈现了完整的三维可视化真实人体,还可以通过网络远程交互式操作,学习人体结构和解剖。1.4结束语上面各种技术应用的基础之一是医学图像的三维可视化。作为新兴的医学影像诊断技术,在临床上还存在诸如精确度不高、医生观察视角不适应实时交互性差等各种问题,应用三维可视化的诊断治疗方法如虚拟内窥镜、手术导航计划等,目前不可能代替传统的诊断治疗方法。但是上述领域的应用显示了医学图像三维可视化技术的广阔发展前景,它已经为医学诊断和治疗提供全新、科学、方便的工具,随着计算机辅助技术和虚拟技术等现代技术的进一步推动,三维可视化技术和成像设备的完善,三维可视化技术必将在医学领域发挥其越来越重要的作用。在下面的一章里,我将重点介绍虚拟技术相关,并用VC++6.0实现一个三维图象的编辑见面。在读入3DS数据的时候涉及到3DS模型的三维重建处理技术,限于篇幅的关系,本文只就编辑框架的内容做相关设计,其他的技术请读者参阅相关的资料或者本文中的程序实现。第二章对三维数据模型的交互控制2.1引言科学可视化,计算机动画和虚拟显示已经成为近年来计算机图形学的三大热门的话题,其技术核心是三维图形。在医学领域中模拟可视化技术的应用越来越被大家重视,本文就使用到的程序编辑器做相关的介绍,以设计三维数据模型的编辑控制界面为主,涉及到的内核请读者参阅最终的程序。OpenGL作为一种三维软件包,在交互式三维图形建模能力和变成方面有不可比拟的优越性。OpenGL灵活方便地实现了二维和三维的高级图形技术,在性能上表现得异常优越。它具有建模、变换,、光线处理、色彩处理、动画以及更先进的能力,如纹理映射、物体运动模糊效果、雾化效果等。OpenGL为实现逼真的三维立体效果,建立交互的三维场景提供了优秀的软件工具。在三维图形程序设计过程中,对于那些非常复杂的图形,如一个非常精致的动物模型和一个逼真的地形场景等,我们不可能依赖与单纯利用OpenGL的是实例库提供的基本几何体来构造,一次性的内存中使用基本的绘图语句编写显然也不能实现。因此,我们自然会想到,利用三维建模工具建好模型以后,以一定的三维数据存储结构保存这些复杂的三维模型。然后利用OpenGL读取模型数据,再利用OpenGL提供的功能操作和管理自己用辛勤汗水制作的精美作品。3DS是一种非常普遍的数据格式,以3DS格式保存的三维图形文件非常丰富。种类繁多的各种三维图形素材光盘以及内容丰富的网上图形站点,都有丰富的3DS文件库。微机上的3DStudio软件制作起来也很方便。因此学会读取和操作的3DS文件对于建立比较实用的三维图形应用软件非常重要。简单介绍一下VC中OpenGL编程的步骤:1)使用AppWizard创建应用程序框架,在文档和视结构中,视负责窗口中内容的显示,因此所有的OpenGL绘制工作应放在视图类进行;2)利用ClassWizard给视类添加如下成员函数:重载PreCreateWindow函数;响应WM_CREATE消息的OnCreate函数;响应WM_SIZE消息的OnSize函数;响应WM_ERASEBKGND消息的OnEraseBKgnd函数;响应WM_DESTROY消息的OnDestroy函数;重载OnInitialUpdate函数。其他的设计步骤读者参阅以下网站的介绍:/~Engineer/window/vc_Opengl.htm3DS模型的读取2.2.13DS模型文件格式3DS模型文件有许多块组成,每个块首先描述其后的信息类别,即该块是如何组成的,块的信息类别是用ID来标识的,块还包含了下一个块的相对位置信息。因此即使你不了解下一个块的含义,你也可以很容易地跳过它,因为其中指出了下一个块相对于该块起始位置的偏移字节数。与许多文件格式一样,3DS模型二进制文件中的数据也是按低位在前,高位在后的方式组织的,例如,两个十六进制字节:4A5C组成的整型数,表明是高位字节,是低位字节。对于长整型数如:4A5C3B8F表明5C4A是低位字,而8F3B是高位字。块的具体定义为:块的前两项信息分别为:块的ID和块的长度(也即是下一个相对于该块的字节偏移量),块的ID是一个整型数,而块的长度是一个长整型数。每个块实际上是一个层次结构,不同类型(ID)的块,其层次结构也不相同.3DS的模型文件中有一个基本块,其ID是4D4D,每一个3DS模型文件的开头都是由这样的构成。基本块内的快称为主块。主块包括3D编辑器块和关键块,其中3D编辑器包括材质列表块,两配置块,视口定义块,物体定义块等,这其中的块有可以逐级包含一些块,总之3DS模型文件是由块组成的一种层次结构。在关于3DS文件格式可参考相关资料,由篇幅关系,在此不一一详述。2・2・2读取3DS文件到自定义的图形数据结构熟悉3DS模型文件结构以后,在程序中,为读取模型,我们需要建立对应的模型数据结构。我们定义了9个全局结构,分别用来定义模型的面、顶点、纹理映射、面材质列表、颜色块、纹理、面材质、形体对象、场景对象的结构。面结构包含顶点序号p0,p1,p2以及有关顶点顺序的面信息flags,其代码如下:typedefstruct{unsignedshortp0,p1,p2;shortflags;}H3dsFace;顶点结构包含顶点的三维信息x,y,z,其代码如下:typedefstruct{floatx;floaty;floatz;}H3dsVert;纹理映射结构包含为顶点定义的俩个浮点型纹理映射坐标u,v,其代码如下:typedefstruct{floatu;floatv;}H3dsMap;材质列表结构包含材质名称,与该材质相关的面的数目,相关的面列表的序号,其代码如下:typedefstruct{charname[MAXNAMESIZE];unsignedshortNumFaces;short*faces;}H3dsMatlist;颜色结构包含了3个浮点数分别表示颜色的红,绿,蓝分量,其代码如下:typedefstruct{floatred;floatgreen;floatblue;}H3dsRGBColor;纹理结构包含纹理名称以及一个相关的选项,其代码如下:typedefstruct{chartexturenname[MAXNAMESIZE];shortoptions;}H3dsTexture;材质对象包含材质名称,环境光,漫反射光,镜面光,材质,透明度等,其代码如下:typedefstruct{charnameH3dsRGBColorambient;H3dsRGBColordifusse;H3dsRGBColorspecular;Shortmat_type;intdoublesided;H3dsTexturetexture;intoffset;floatalpha;}H3dsMat;形体对象是整个3DS模型的核心部分,包括形体的名称,形体中顶点的个数,面个数,顶点列表,面列表,平移矩阵等一些重要的信息,其代码如下:typedefstruct{charname[MAXNAMESIZE];unsignedshortNumFaces;listunsignedshortNumVerts;unsignedshortNumMaps;dinatesinmaplist;intmatrix;unsignedshortNumBindings;H3dsFace*faceslist;H3dsVert*verlist;H3dsMap*maplist;floatTraMatrix[3*];translationmatrixH3dsMatlist*bindings;intoffset;list}H3dsMeshObj;场景对象是我们需要获得的3DS模型数据的最终进入的结构,其中包括3DS模型数据所有形体对象的个数,形体,对象列表,材质树木,材质列表,代码如下:typedefstruct{unsignedshortmeshobjs;inmeshobjeslistH3dsMeshObj*meshobjlist;unsignedshortNumMateriarls;inthescenceH3dsMat*Material;}H3dsScence;具体操作的时候,我们定义了读入当前形体对象的顶点列表ReadVertList(H3dsMeshObj*meshobj),读入当前面函数列表ReadFace(H3dsMeshObj*meshobj),读入纹理映射坐标函数ReadmapList(H3dsMeshObj*meshobj),读入形体数据结构ReadTriMeshBlocks(intposition,char*name);读入物体块函数ReadObjBlock(intposition)等,根据3DS模型文件有块组成,每个块都有ID标识,且块还包含下一个块的相对信息的特点,定义好获取模型的数据结构和操作函数以后,我们可以方便地实现模型的读取,即使某些块我们不知道具体的含义,亦可方便地跳过它。在READ3DSclasses类里定义了以上数据结构,结构参考下图:3DS模型的绘制以上我们针对3DS模型文件的特点,定义了相关的数据结构,模型数据就可以方便地进入这些数据结构,但是在VC++6.0中要利用OpenGL将模型数据真实地绘制出来却是一件难事,为此我们在获取模型数据以后,并不是急于将它绘制出来,而是将它们转换到另外一个易于读取的数据结构中。在转换的过程中,我们将顶点的法向量,纹理坐标与顶点联结在一起,将材质名称,各个分量光强度,面数目,面顶点的序号,纹理名称等与物体对象连接在一起。我们定义的用OpenGL绘制的数据结构如下://颜色结构typedefstruct{GLfloatred;GLfloatgreen;GLfloatblue;GLfloatalpha;}OUTGL_RGB;//顶点结构(顶点纹理,法向量,顶点坐标)typedefstruct{GLfloatu,v;GLfloatnx,ny,nz;GLfloatx,y,z;}OUTGL_VERTEX://三角形的法向量结构typedefstruct{GLfloatp0,p1,p2;GLfloatn0,n1,n2;}OUTGL_TRIANGLE;//三角形面结构typedefstruct{GLunitp0,p1,p2;GLfloatn0,n1,n2;}OUTGL_SURFACE://三角形列表接点typedefOUTGL_TRIANGLENODE{OUTGL_SURFACE*triangle;Tices.LinksstructOUTGL_TRIANGLENODE*next;};typedefstructOUTGL_TRIANGLENODETRIANGLENODE;typedefstruct{charname[50];OUTGL_RGBambient;OUTGL_RGBdiffuse;OUTGL_RGBspecular;intdoublesided;GLunitNumTriangles;OUTGL_SURFACE*TriangleIndexList;OUTGL_TRIANGLE*CrunchedTriangleIndexList;chartexturenname[50];GLunittextureID;GLunitNumStrips;GLunit*StripStart;GLunit*StripLength;GLunitNumStipPoints;GLunit*StripPoint;}typedefstruct{GLunitNumVertex;OUTGL_VERTEX*VertexLists;GLunitNumPolygon;GLunitstrip;POLYGONLIST*PolygonList;}M3DSObject;最终模型由下面的函数绘制出来:voidDraw3DSObject(M3DSObject*object){unsignedintI,j/*,k*/;intcurrent;if(!oblect->strip){gllnterleavedArrays(GL_T2F_N3F_V3F,0,object->VertexLists);for(unsignedintm=0;m<object->NumPoygon;m++){glMaterialfv(GL_FRONT,GL_AMBIENT,(GLfloat*)&object->Polygonlists[m],ambient);glMaterialfv(GL_FRONT,GL_DIFFUSE,(GLfloat*)&object->Polygonlists[m],diffuse);glMaterialfv(GL_FRONT,GL_SPECULAR,(GLfloat*)&object->Polygonlists[m],specular);glDrawElements(GL_TRIANGLES,object->PolyonList[m].NumTriangles*3,GL_UNSIGNED_INT,object->PolygonLists[m].CrunchedTriangleIndexList);}}else{gllnterleavedArrays(GL_T2F_N3F_V3F,0,object->VertexLists);for(inti=0;i<object->NumPoygon;i++){glMaterialfv(GL_FRONT,GL_AMBIENT,(GLfloat*)&object->Polygonlists[m],ambient);glMaterialfv(GL_FRONT,GL_DIFFUSE,(GLfloat*)&object->Polygonlists[m],diffuse);glMaterialfv(GL_FRONT,GL_SPECULAR,(GLfloat*)&object->Polygonlists[m],specular);current=0;for(intj=0;j<object->PoygonList[i].NumStrips;j++){glDrawElements(GL_TRIANGLE_STRIP,object->PolyonList[i].NumStripLength,GL_UNSIGNED_INT,&object->PolygonLists[i].StripPoint[current]);current+=object->PloygonLists[i].StripLength[j];}}}}后台对三维模型的处理的参考类似的模版,在图形的处理过程当中对三角矢量的处理技术不在本文的介绍之列,但是读者可以从程序的Globols里参阅程序文档。3DS示例(实现骨骼一皮肤动画)为了更好地说明虚拟技术在医学领域的应用,引入骨骼—皮肤动画程序的示例。骨骼一皮肤动画技术是3D动画领域的一项比较高级的技术,由于其生动、逼真的效果,在动态仿真等领域起着重要的作用。只有使用骨骼—皮肤技术,才能制作出广播级的动态作品,以满足在现实领域里的应用。顾名思义,骨骼—皮肤动画的含义是使用一系列的骨骼去带动一张皮肤进行运动。其特点是:第一,作为皮肤的网格是一个整体,而不是分成区段的。在简单的区段动画中,一个复杂的物体是由许多“坚硬”的段组成的,最典型和常见的例子是人体,是由头、躯干、手臂、腿、脚等组成,而躯干又分上身、下身,手臂又分上臂、前臂和手,腿部又分为大腿和小腿。分别为这些段定义运动,就可以组成人体的较复杂的运动了。这种技术的优点是实现方便而且运算速度快,适用于对视觉效果要求不高的场所。但是有一个致命的缺点是会在段与段之间相联接的地方出现明显的接缝,而在进行某些动作的时候会出现段与段分离的现象,这些在广播级的动画中是绝对不允许出现的。但是由于骨骼—皮肤动画中的皮肤是一个整体,所以避免了这些情况的发生。第二,皮肤的形状是可以改变的,并且完全是由与其相关的骨骼决定的。皮肤不再是一个“硬梆梆的”网格,而是“有弹力的、能拉伸的”。相比区段动画中所有的由形状不变的生硬网格组成的区段,骨骼—皮肤动画中的皮肤能在任何时刻保持光滑、生动的外表,在制作一些像蛇、动物的尾巴等软的东西时尤其出色。既然骨骼—皮肤动画有这些好处,它又是怎么实现的呢?首先,需要有一个做皮肤用的网格和一系列骨骼。对于网格有一些要求,例如,在关节处的多边形数目应该多一些等等。而骨骼的大小和位置关系应该与皮肤相对应,因为要靠位置来判断骨骼影响了皮肤网格上的哪些点。然后,决定网格上的点是由哪快骨骼影响的。每一块骨骼都有一个作用范围,在这个范围中的点都要受该骨骼的影响。在关节处的点往往会受到多于一块的骨骼的影响,这些骨骼的影响通过不同的权值叠加在点上,使网格的关节部分尽量保持平滑。每一块骨骼都包括一组位置、朝向信息,借助这组信息,网格上的点确定自己的位置和朝向。这样就实现了骨骼对皮肤的影响。最后,设置骨骼的运动信息,带动受其影响的网格上的各点运动,就形成了动画。在一些3D动画软件中,第二步是即时算出来的,也就是说,一开始网格并不知道受哪些骨骼控制,只有建立了骨骼,并把它付给网格后,软件才开始计算各点的位置是否在某一块骨骼的影响区域中,在所有的点都找到了自己的骨骼后,才可以进行动画。这种机制一般运用于各种3D动画设计软件中。在要求高效率的场合(如游戏中),这种方式是不可取的。为了实现高效率,预处理是一种普遍而有用的方法。所谓预处理,就是在程序之外尽可能的把所有固定的数据处理好,并且为程序中优化的算法奠定基石。预处理的一个比较成功的例子是ID公司的Quake中对地图的处理。为了使Quake—帧场景中所需处理的多边形数目最少,需要对地图数据进行优化。采用BSP树和创立可能可见集可以达到优化的要求,但是为每一幅地图生成一棵BSP的过程是极其缓慢的。据ID公司的资料纪录,当时生成一棵BSP树用了十多分钟,而创立可能可见集的工作在一台有四个CPU的机器上用了约一个小时。然而每一地图的BSP树和可能可见集一旦生成,就可以在游戏中不再改变。这种情况下就可以采用预处理。借助于这种方法,ID公司的人员将Quake引擎的效率提高了一半以上。由这个例子我们可以看出预处理的道理就是“长痛不如短痛”。在高效率3D程序中要实现骨骼一皮肤动画,也需要预先制作好皮肤和骨骼,并且在数据中记录皮肤网格上的各点分别受哪些骨骼控制。这就是一个预处理过程。另外,为了实现简化,在预处理时可以不靠骨骼的“影响区域”来确定骨骼影响的点。因为这样做需要确定网格的点与骨骼作用区域的相包含关系,这将是一个繁琐的过程。下面我们通过一个骨骼—皮肤动画的程序例子来分析一下其具体实现。我们选用的这个例子为一工作台模式的程序,采用VC++6.0编译,使用OpenGL加速,需要OpenGL实用工具库Glut的支持,并且使用了一个提供从3DS文件中读取数据的辅助库。此程序的功能包括两大部分:读入一个皮肤网格和一系列骨骼,确定网格和骨骼的关系;读入一系列动画信息,实现一个简单的动画。前者就是我们提到过的所谓预处理过程,是程序的重点。程序中首先定义了如下结构:typedeffloatMATRIX[4][3];/*矩阵的定义*/structBone{structBone*NextPtr;/*使用链表存储骨骼*/charName[12];/*这块骨骼的名字*/longNrVerts;/*这块骨骼影响的顶点的个数*/MATRIXMatrix;/*骨骼的初始化矩阵*/MATRIX*AnimPtr;/*指向表示动画信息的矩阵数组的指针*/};structSkin{longNrFrames;/*动画的帧数*/point3ds*PointPtr;/*顶点缓存,用于暂时存储变换位置后的顶点*/Bone*BonePtr;/*骨骼链表的头节点*/mesh3ds*MeshPtr;/*网格皮肤*/};structBonePoint{point3dsPoint;/*顶点的位置*/intIndex;/*在网格中的原始位置索引*/Bone*BonePtr;/*指向影响此顶点的骨骼的指针*/};然后,定义一些宏和全局变量如下,很简单:#defineWORLD_NEAR1000#defineWORLD_FAR25000#defineONEDEGREE((1.0f/180.0f)*3.1415926)longCurFrame=0;/*Currentframeinanimation*/floatAngleY=-25*ONEDEGREE;/*Currentangleofthecamera*/floatDistance=-12000;/*Currentcameradistancefromtheworldcenter(0,0,0)*/floatHeight=0;/*Currentcameraheightfromtheworldcenter(0,0,0)*/BOOLPaused=FALSE;/*Istheanimationplaybackpausedornot*/Skin*SkinPtr;/*pointertothecharacter/skindisplayed*/下面是程需的核心部分—检索顶点位置来确定其受哪一块骨骼影响。voidSolveBoneInfluences(database3ds*db,Skin*skinptr){/*参数说明:db(in):—个3ds数据库对象的指针,其中存储了一些网格等信息。skinptr(in&out)如果函数成功,将以一个新的皮肤结构对象填写此结构*//*Allocateabigworkbuffer*/BonePoint*bonepointptr=(BonePoint*)malloc(30000*sizeof(BonePoint));BonePoint*curbonepoint=bonepointptr;longNrBoneVerts=0;/*建立一个新的顶点缓冲,将所有骨骼的点依次放进去,每一个点都记录了是从哪一块骨骼来的。由于此程序例中所有的骨骼都是由一个网格和一个矩阵组成,而且所有骨骼网格的点都与皮肤网格的点一一对应。实际上骨骼的网格是皮肤网格的一部分,每一块骨骼网格上的所有点都是皮肤网格所有点的子集,本程序的核心思想就是通过这种关系确定皮肤上的点是由哪块骨骼影响的*/MATRIXtmpmat;Bone*boneptr=skinptr->BonePtr;/*获得皮肤的骨骼链表*/while(boneptr)/*遍历骨骼链表*/{mesh3ds*bonemesh=NULL;/*bonemesh对象是一个mesh3ds结构的对象,定义见3dsftk.h*/GetMeshByName3ds(db,boneptr->Name,&bonemesh);/*按照已有的boneptr中的名字从db中读取一个骨骼网格给boneptr,保证读取的是与骨骼对应的。*/assert(bonemesh);Copy3dsMatrix(tmpmat,bonemesh->locmatrix);/*将骨骼网格的矩阵也一并读出*/InverseMatrix(tmpmat,boneptr->Matrix);/*因为3ds与OpenGL中的矩阵定义方式不同,需要做转换。InverseMatrix函数定一见源代码。这两步重要的操作实现了将bonemash中的矩阵付给boneptr的矩阵对象*//*下面的操作将每一块骨骼网格的所有顶点写入前面开辟的顶点缓冲中*/point3ds*bonemeshpoints=bonemesh->vertexarray;NrBoneVerts+=bonemesh->nvertices;assert(NrBoneVerts<30000);for(inti=0;invertices;i++)/*把骨骼顶点的位置信息写入缓冲:*/curbonepoint->Point.x=bonemeshpoints->x;curbonepoint->Point.y=bonemeshpoints->y;curbonepoint->Point.z=bonemeshpoints->z;/*纪录缓冲中当前顶点是骨骼链表中哪一块骨骼的*/curbonepoint->BonePtr=boneptr;bonemeshpoints++;curbonepoint++;}RelMeshObj3ds(&bonemesh);/*释放bonemash对象*/boneptr=boneptr->NextPtr;}/*这里的定义有些混乱*/mesh3ds*skinmesh=skinptr->MeshPtr;/*定义一个网格对象指针指向skinptr中的网格*/point3ds*skinmeshpoints=skinmesh->vertexarray;/*定义一个顶点数组指针并令其指向网格对象中的顶点们*/BonePoint*skinpointptr=(BonePoint*)malloc(skinmesh->nvertices*sizeof(BonePoint));/*由于顶点数组不包含骨骼信息,程序申请一片与顶点数组同样大小的骨骼顶点数组空间*/BonePoint*curskinpoint=skinpointptr;/*设一个指针记录骨骼顶点数组中的当前位置*//*对于每一个网格顶点数组中的顶点,找到离它最近的骨骼顶点。*/inti;for(i=0;invertices;i++)/*遍历顶点数组,并用顶点数组中的位置信息填充与之等大的骨骼顶点数组*/{curskinpoint->Point.x=skinmeshpoints->x;curskinpoint->Point.y=skinmeshpoints->y;curskinpoint->Point.z=skinmeshpoints->z;curskinpoint->Index=i;/*按由小到大的顺序纪录原始的骨骼顶点序号*/curskinpoint->BonePtr=NULL;/*现在的骨骼顶点数组是由网格数组直接得来的,其中还没有任何骨骼信息,暂且称之皮肤骨骼顶点数组*/curbonepoint=bonepointptr;/*先前由一块块骨骼网格依次建立的骨骼顶点数组,称之骨骼骨骼顶点数组*/floatmindist=1e6;for(intj=0;j<NrBoneVerts;j++)/*内层循环,为皮肤骨骼顶点在骨骼骨骼顶点数组中找最近的顶点*/{floatdist=CalcDistNotSquared(skinmeshpoints,&curbonepoint->Point);if(dist<mindist){mindist=dist;curskinpoint->BonePtr=curbonepoint->BonePtr;}curbonepoint++;}curskinpoint++;/*双层嵌套循环*/skinmeshpoints++;}/*按照骨骼对皮肤顶点排序,并且对多边形进行新的分配*/skinmeshpoints=skinmesh->vertexarray;/*网格对象的顶点数组*/face3ds*skinfaces=skinmesh->facearray;/*网格对象的多边形数组*/longCurIndex=0;boneptr=skinptr->BonePtr;while(boneptr){curskinpoint=skinpointptr;for(i=0;invertices;i++){if(curskinpoint->BonePtr==boneptr){Transform(boneptr->Matrix,(float*)&curskinpoint->Point,(float*)skinmeshpoints);RemapFaceList(skinmesh,curskinpoint->Index,CurIndex++);boneptr->NrVerts++;skinmeshpoints++;}curskinpoint++;}boneptr=boneptr->NextPtr;}/*双层循环完成对网格的顶点的按骨骼排序*//*清理工作*/CleanUpFaceList(skinmesh);free(skinpointptr);free(bonepointptr);}到这里,简单介绍了此原程序的核心算法。这里有几点需要注意:首先,在程序中,是在运行期间调用此函数生成骨骼和皮肤的关系,而在一般的应用时,这一部分和动画等操作应该是分离的,这个函数应该用于生成一个确定了骨骼—皮肤对应关系的文件。另外,程序给出的算法并不是唯一的骨骼—皮肤系统的的实现。这只是一个简单的例子,有许多种算法比这个要优越,例如在DirectX8.0的D3DX辅助库中的算法。该算法加入了各点受骨骼影响的权值的概念,因此动画更平滑。还有一点,就是本程序对模型文件的要求,即骨骼网格是整张皮肤的一部分,具体应该如何,在3DSMAX中导入本程序所用的3ds文件一看就明白了。本站提供此源程序和所用到的库(3dsftk.lib、glut32实用库)的下载。
注解:glut32实用库的用法:解压缩后将*.dll文件与例子程序放在同一目录下,即可使程序正常运行。如果要编译源代码,需要将*.h文件拷贝到VC的include/GL目录下,将*.lib文件拷贝到VC的lib目录下,将*.dll文件放在windows的system32目录下。另:2.4的程序的最终实现不在本文的叙述之列,其目的是更好的说明OpenGL在医学图象三维技术中的应用,只作为本文的参考。3DS模型的读入并进行交互式样控制为了验证前面定义的数据结构的可能性,我开发了一个演示程序,结果成功地读取了由3DSStudio系统和3DSMax以.3DS文件格式导出的模型数据,并很容易地实现了模型纹理读取与映射。3DS模型数据进入OpenGL中以后,利用OpenGL提供的坐标变换,颜色处理,光照处理,效果处理,选择及反馈模式等功能可实现三维物体更加真实的显示,同时还可以实现三维物体的缩放,移动,旋转,选中,删除,飞行等交互的三维应用软件。在Dialog里引用一个基于CFormView的文本编辑框,在其中应用程序的工具编辑相应的控制按扭,其相应的程序见付程序清单。W5Til-IdatVi«vZd-v-bttFro-j»ctBus1dLk^duITooli.VindonllalpBl»WUr(ToiREAD3DSiresDurresAmj&I飙迥toiroiREAD3DSiresDurresAmj&I飙迥toirSOCursor白“nDi-aHoij卜胃IDD_ABOUTBOX同iddZform_commaw由••口Icon[J_JMenuEl23SiringTable®TocltoairIS匚]Vcrshon為abl□区國QB述飙色,背最色.'5□□口坏漫囲r反混淆曲mi理血叫]r"HT:lli|iiirj"iHlr进照ajutn|.点化^.Kcrtcz]rIlPriA^tTace]|►p-i.OoiId/Iiflb'Ji\~FindinFiln-s1~"J?~Findin,FdJ把3DS模式应用于OpenGL中,降低了OpenGL复杂建模的难度,我们可以得到较为真实的三维发展物体模型。在实验系统中,我们在OpenGL程序中,引入3DS数据模型文件得到较好的三维动画效果,同时还可以进行交互处理,成果可以用于飞行运动模拟公路勘察,城市规划和医学虚拟显示等领域中。当然在读取的模型数据量很大的时候,如何比较快的显示模型,要利用有关图形加速显示技术,如LOD显示技术等,同时如何在读入的模型中加入属性信息,难度比较大,还有待于进一步的研究,而且本文所设计到的一些技术相关也可能具有一定的片面性,还望各位前辈多多指教。其程序实现度入3DS模型的结果如下::iri|拂吋縊革:=丈岸的邛幽恵口歩士:iri|拂吋縊革:=丈岸的邛幽恵口歩士.2禅卑g碍赶亦厂戌七丈胆*凸1昭禄左键摘起域充型述制参谊走馥眶式绘制爹辺形车边琢体竝制血点送石一旦韧始设蟹按所燈择地師色蛊斯绘常焙吕苗设貿框I掠席选捽地频包里耕超制环惶光咨号惬II採所迭择曲誠色重粘制徨度射光谀匿框I是晋口口光照昭禄左键摘起域充型述制参谊走馥眶式绘制爹辺形车边琢体竝制血点送石一旦韧始设蟹按所燈择地師色蛊斯绘常焙吕苗设貿框I掠席选捽地频包里耕超制环惶光咨号惬II採所迭择曲誠色重粘制徨度射光谀匿框I是晋口口光照品否对雲iS电进材光信it理产生罐蒔戢I晅按所滋捋tfe藏色重新绘制號面光设置惟亟睛计尊缩歆匕匕例#三生你罚■?-:4切换是養辰走巒注解:程序实现及流程综述与致谢纸上得来终觉浅,绝知此事要躬行。理论知识只有在实践中才能被深刻地了解和掌握,四年的所学化做最后的沉淀,我的毕业设计课题是:医学影像三维重建技术的研究。在论文的实现过程中我分为两部分:其一为着重介绍医学影像三维重建及其可视化的相关技术;另一部分是程序的实现(只是实现了在该领域的一个细微末节,即实现在医学虚拟领域对三维图象的交互操作),我实现的是:VC++6.0
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2027届四川省北师大广安实验学校八上数学期末预测试题含解析
- PCR技术培训结业考核试题及详细答案
- 污水处理工考试题库与答案(完整版)
- 2026年人教版(2025)九年级化学上册阶段测试试卷含答案
- 2025-2026学年贵州省安顺市初中历史七年级期末下册自我评估重点试题(详细参考解析)
- OWLS英语水平测试模拟试题(含详细答案解析)
- OpenStack 技术综合考试题(含答案)
- 校车日常检查维保方案
- 楼宇电梯广告屏内容上刊与巡检维护方案
- 肿瘤康复中国专家共识(2026版)
- 道口开设施工方案
- 拆除原有瓦屋面施工方案
- 塑料模具开模合同协议书
- 高一家长心理讲座
- 锡林郭勒2025年锡林郭勒盟盟直事业单位引进急需紧缺人才笔试历年参考题库附带答案详解
- 概率论和数理统计3二维随机变量及其分布
- DB31/T 1093-2018混凝土砌块(砖)用再生骨料技术要求
- 民兵情报系统培训实务纲要
- 中班艺术活动:彩绘伞面
- 第八章第二节 脑梗死课件
- 猕猴桃炭疽病防控措施
评论
0/150
提交评论