三维图形消隐算法已比较成熟,但要普通编程人员对复杂三维图形进行消隐
编程,却不是容易的事。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();绘出所有多边形,但不显示,即若双缓存,则不交换缓存。
}