利用OpenGL进行CAD三维图形消隐处理
三维图形消隐算法已比较成熟,但要普通编程人员对复杂三维图形进行消隐 编程,却不是容易的事。OpenGL图形库中提供了消隐处理函数,但消隐却不知因 何原因而质量不高,如消隐时直线断断续续。为此笔者进行了一定改进和精细消 隐处理,下面介绍两种办法。 一、一般消隐 这种方法为首先设置消隐使能,初始化深度缓存,设置消隐比较,直接进行 绘图即可。但此种结果是直线断断续续,时有时无,效果差。改进只需将直线线 宽加粗,若需多边形边框一同绘出,则要GL_LINES方式将边框线段重绘。具体方 法如下: glEnable(GL_DEPTH_TEST);//设置消隐使能 glClearDepth(1.0);//设置初始化深度缓存值 glClear(GL_DEPTH_BUFFER_BIT);//深度缓存消除 gldephFunc(GL_LEQUAL);//设置消隐比较 glLineWidth(2.0);//直线线宽应比多连形线宽多一倍 下述设置后,即可开始绘图。消隐能正常显示,只是直线线宽均比多边形宽 一倍,图形变得粗糙,效果不十理想。 二、精细消隐处理 精细消隐设计分三步: 1、首先在消隐方式下对所有多边形面进行绘图,其目的是在深度缓存中写入 消隐后的多边形面的Z值,并比较后再写入深度缓存,即大大简化了多边形面的Z缓存计 算。 2、将直线及多边形边框直线由目标坐标转换成窗口坐标下的值,并将线段离 散化为窗口坐标下的象素点,再比较象素点的窗口坐标下Z值与深度缓存值,从而 将隐藏直线段消除,记录显示线段,并窗口坐标由三维换为二维。 3、消除显示缓存,重新显示二维窗口坐标下的可视线段。 这样处理,一方面极大地简化了Z缓存计算,使普通编程人员能胜任高级三维 CAD软件设计,消隐显示质量高;另一方面获得的可视线段,便于纯Windows图形打 印,大大地增强了OpenGL图形打印能力;此处还可便于三维图形标注。有关编程核 心部分见函数Zbuffer_Calculate()。调用顺序为先绘多边形。(Display_Polygon()), 再将直线传给Zbuffer_Calculate()进行消隐计算。下面程序为本人开发的,应用软 件中的精细消隐部分,对高质量打印十分有用。编程环境为VC++5.0,完全通过。 #include“windows.h” #include“math.h” #include“GL/gl.h” #include“GL/gl.h” typedef struct point_2d{ float X; float Y;}Point_2D; GLdouble modelm3D[16]; GLdouble projm3D[16]; GLint vp[4]; BYTE lineABC(double*,double*,BYTE&,float&,float&, float&,float&,float&,float&,float&); voitd Display_Polyon(); void Zbuffer_Calculate(float X1,float Y1,Float Z1), flat X2,float Y2,float Z2)//Z缓存计算,进行精确消隐显示。 { BYTE mflage=(); BYTE m_frmfrac=3,mHide; GLdouble wincor1[3],wincor2[3]; GLfloat delta,kxy,kz,min,max,xy1,z1,i; GLfloat xy,z,x,y,X[2],Y[2],Zbuf; int N=0,M; Point_2Dp0,p1; gluProject(X1,Y1,Z1,modelm3D,projm3D,vp; &wincor1[0],&wincor[1],&wincor1[2]; gluProject(X2,Y2,Z2,Modelm3D,projm3D,vp, &wincor2[0],&wincor2[1],&wincor2[2]; if(lineAbc(wincor1,wincor2,mflage,delta,min, max,xy1,z1,kxy,kz)=3)return; N=0,M=FALSE;mIIide=3; for(i=0.0;i xy=xy1+kxy*i; z=z1+kz*i; if(mflage=1) {x=min i;y=xy;} if(mflage=2 {x=xy; y=min+i;} glReadPixels((int)x,(int)x,(int)y,1,1,GL_DEPTII_COMPONENT,GL_FLOAT,&Zbuf ); if(z>=Zbuf){ X[N]=x;Y[N]=Y,N=1; if(N==2){N=1;M=TRUE;} } else{ if(M==TRUE){ pO.X=X[0];pO.Y=Y[0]; pl.X=X[1];P1.Y=Y[1]; //then AddNode(pO,p1,TRUE,line_width);保存该直线段,实线线型,线宽 } M=FALSE; N=0; } } if(M==TRUE&&N=1){ p0.X=X[0];p0.Y=Y[0]; p1.X=X[1];P1.Y=Y[1]; //then AddNode(p0,p1,TRUE,line_width);保存该直线段,实线线型,线宽 } } //lincAbc为计算直线窗口平面坐标下的斜率等 BYTE lineAbc(double*wincor1,double*wincor2,BYTE&MFLAGE, float&delta,float&in,float&max,float&xy1, float&z1,float&kxy,float &kz) { if(fabs(wincor1[0]-wincor2[0])=0.0&& fabs(wincor1[1]-wincor2[1]==0.0)return 3; if(fabs(wincor1[0]-wincor2[0])> fabs(wincor1[1]-wincor2[1](( { mflage=1;delta=(float)fabs(wincor1[0]-wincor2[0]); if(delta>1.0) if(wincor1[0] min=wincor1[0];max=wincor2[0]; xy1=wincor1[1]; z1=wincor1[2]; kxy=(wincor2[1]-wincor1[1])/(wincor2[0]-wincor1[0]); kz=(wincor2[2]-wincor1[2])/(wincor2[0]-wincor1[0]); } else{ min=wincor2[0];max=wincor1[0]; xy1=wincor2[1]; z1=wincor2[2]; kxy=(wincor1[1]-wincor2[1]/(wincor1[0]-wincor2[0]); kz=(wincor1[2]-wincor2[2]/(wincor1[0]-wincor2[0]); } } else{ mflage=2;delta=fabs(wincor1[1]-wincor2[1]); if(delta>1.0) if(wincor1[1] min=wincor1[1];max=wincor2[1]; xy1=wincor1[0]; z1=wincor1[2]; kxy=(wincor2[0]-wincor1[0])/(wincor2[1]-wincor1[1]); kz=(Wincor2[2]-wincor1[2])/(wincor2[1]-wincor1[1]); } else{ min=wincor2[1];max=wincor1[1]; xy1=wincor2[0]; z1=wincor2[2]; kxy=(wincor1[0]-wincor2[0])/(wincor1[1]-wincor2[1]); kz=(wincor1[2]-wincor2[2]/(wincor1[1]-wincor2[1]); } } return mflage; } void Display_Polygon() {//SetViewPort();设置视口大小 //SetProjection();设置投影变换 //SetModelView();设置视图变换 glGetDoublev(GL_MODEL VIEW_MATRIX,modelm3D);//获取视图矩阵 glGetDoublev(GL_PROJECTION_MATRIX,projm3D);//获取投影矩阵 glGetIntegerv(GL_VIEWPORT,vp);//获取视口大小 //Display();绘出所有多边形,但不显示,即若双缓存,则不交换缓存。 } |
|