作者:Rick
1 引言
数据可视化技术指的是运用计算机图形学和图像处理技术,将数据换为图形或图像在屏幕上显示出来,并进行交互处理的理论、方法和技术。它涉及到计算机图形学、图像处理、计算机辅助设计、计算机视觉及人机交互技术等多个领域。数据可视化概念首先来自科学计算可视化,随着计算机技术的发展,数据可视化概念已大大扩展,几乎可以应用于自然科学、工程技术、金融、通信和商业等各种领域。
通过三维图形开发库OpenGL开发三维可视化结果,使得开发出来的软件具有重用性、通用性、实时性等特点。
2 使用OpenGL技术的优势
OpenGL是个专业的3D程序接口,是一个功能强大,调用方便的底层3D图形库。OpenGL是个与硬件无关的软件接口,可以在不同的平台如Windows、UNIX、Linux/MacOS、OS/2之间进行移植。因此,支持OpenGL的软件具有很好的移植性,可以获得非常广泛的应用。
OpenGL显示列表(Display List)是由一组预先存储起来留待以后调用的OpenGL函数语句组成的,当调用这张显示列表时就依次执行表中所列出的函数语句。OpenGL显示列表的设计能优化程序运行性能,尤其是网络性能。它被设计成命令高速缓存,而不是动态数据库缓存。蒙板缓存(Stencil Buffer)可以保持屏幕上某些部位的图形不变,而其他部位仍然可以进行图形绘制。正确灵活地运用梦板缓存,可以实现对象的反射、折射和阴影等显示效果。利用OpenGL开发库驱动图形显示,可以充分利用显卡的3D加速性能,从而实现满足可视化要求的实施显示,实现画面的流畅驱动。
3 实现步骤
3.1建立应用框架
启动VC++ 6.0,选择菜单File|New,打开New对话框。在New对话框中选择Projects选项卡,在列表中选择MFC ActiveX ControlWizard,建立一个工程名为OglInstrOcx的控件工程。
通过AppWizard生成的框架应用不支持OpenGL的显示。首先需要设置设备显示模式,从而能支持OpenGL的显示。
∥填充像素格式的结构:PIXELFORMATDESCRIPTOR
static PIXELFORMATDESCRIPTOR pfd=
{
sizeof(PIXELFORMATDESCRIPTOR),1,
PFD_DRAW_TO_WINDOW|
PFD_SUPPORT_OPENGL|
∥支持OpenGL的显示
PFD_DOUBLEBUFFER,
∥支持双缓存的显示
PFD_TYPE_RGBA,24,
//24位颜色深度
0,0,0,0,0,0,
0,
0,
0,0,0,0,
32, //32深度缓存
1, //使用蒙版缓存
0,
PFD_MAIN_PLANE,
0,
0,0,0
};
int pixelformat;
//选择设备的像素显示格式,返回像素格式的索引值
if( (pixelformat=ChoosePixelFormat(hdc,&pfd))==0)
return FALSE;
//利用返回的索引值设置设备的像素显示格式
if (SetPixelFormat(hdc, pixelformat, &pfd)==FALSE)
return FALSE;
//创建一个与设备关联的OpenGL渲染上下文环境
m_hrc=xvglCreateContext(hdc);
//指定此OpenGL渲染环境为当前渲染环境
wglMakeCurrent(hdc, m_hrc);
接着,需要设置OpenGL的视窗大小、投影变换。这里的有些参数可以根据实际需要作相应的调整:
glClearDepth(l.0f);
glEnable(GL_DFPTH_TEST); //打开深度检测
∥设置视窗大小
::glViewport(0,0,rc.right – rc.left,rc.bottom – rc.top);
glMatrixMode(GL_PROJECTION);
∥指定投影矩阵为当前矩阵
glLoadIdentity();
float x,y,xl,yl;
x= rc.lefi;
y=rc.top;
xl= rc.right;
yl= rc.bottom;
float xRatio= (x1 -x)/(float)(rc.right – rc.left);
float yRatio= (y1-y)/(float)(rc.bottom – rc.top);
float componentAspect= (x1 -x)/(y1- y);
∥设置当前的可视空间为正投影空间
if (xRatio>yRatio)
glOrtho(0,xl-x,0,(float)( rc.bottom – rc.top)* xRatio,-50000.0,50000.0);
else
glOrtho(0,(float)(rc.right-rc.left)* yRatio,0,yl – y,-50000.0,50000.0);
glMatrixMode(GL_MODELVIEW);
∥指定模型矩阵为当前矩阵
3.2 实现三维统计数字
为了实现三维数字的显示,需要做3方面的工作:①利用三维建模软件实现数字0-9的三维模型;②利用工具把模型转换成CPP的输出文件;③把生成的CPP文件内容加入工程中,编程驱动。
首先,生成0~9的三维模型。在这里选用multigencreator软件,当然,用3DMAX等软件都是可以的。Creator软件是美国MultiGen公司的新一代实时仿真建模软件,它区别于其他建模软件,主要考虑在满足实时性的前提下如何生成用户需要的三维模型。其强大的建模功能可为众多不同类型的图像发生器提供建模系统及工具,其Openflight输出格式在实时三维领域中成为最流行的图像生成格式,并成为视景仿真领域事实上的行业标准。
打开creator软件,新建一个空文件。在工具栏中选择Geometry中的3D Text按钮,弹出Text对话框,在Text String编辑框中输入0123456789,点击OK按钮,生成平面的数字模型,如图1所示。
图1平面数字
接下来,在工具栏中选择Geometry中的Wall按钮,弹出Wall对话框,在Height编辑框中输入三维数字的厚度,点击OK按钮,生成三维的数字模型,如图2所示。
图2三维数字
保存三维数字模型,输出文件number0.flt。 其次,利用Deep Exploration三维模型转换工具把刚才生成的openflight三维数字模型转换成OpenGL CPP的代码。具体转化过程比较简单,在Deep Exploration软件中打开number0.flt丈件,选择菜单Save as,在保存类型中选择OpenGL CPP code (*.cpp),根据需要点击Setting按钮,设置输出的具体选项。因为需要把生成的代码嵌入到工程中,所以在Setting的cpp code中选择Display List类型,生成number0.cpp代码文件。在这个文件中,包含一个OpenGL对象显示列表的函数如下:
void Gen3DObjectList()
{
∥新建一个序号为10的显示列表对象
gINewList(10, GL_COMPILE);
∥以三角面片的方式构建显示列表
glBegin (GL_TRIANGLES);
for(i=0;i<sizeof(face_indicies)/
sizeof(face_indicies[0]);i++)
{ //根据对象的面的数量循环
If(!mcount)
{ //设置面的材质
SelectMaterial(material_ref[mindex][O]);
Mcount=material_ref[mindex][1];
mindex++,
}
mcount–;
∥设置三角面片的的三个点
for(j=0;j<3;j++)
{
int vi=face_indicies[il[j];
int ni=face_indicies [j+3];//Normal index
int ti=face_indicies [j+6];//Texture index
∥设置顶点的法线
glNorma13f (normals[ni][0],normals[ni][1],
normals[ni][2]);
//设置顶点的坐标
glVertex3f (vertices[vi][0],vertices[vi][1],
vertices[vi][2]);
}
}
glEnd();
glEndList();
}; //函数中使用的数组变量,包括材质、法线、面和顶点的值都在number0.cpp中
使用同样的方法步骤,得到了number1.cpp~number9.cpp。利用这10个显示列表函数,可以通过程序调用生成任意的三维数据显示。下面以两位数为例说明:
//显示个位数字
glPushMatrix();
//设置个位数字显示的位置
glTranslatef(-18.5,high_tai+high1+1.5,2.0);
//设置个位数字显示的旋转角度
glRotatef(num_rot,0.0,-1.0,0. 0);
//调用显示列表,显示个位数字
glCallList(num1+10);
glPopMatrix();
//显示十位数字
glPushMatrix();
//设置十位数字显示的位置
glTranslatef(-17.0,high_tai+high1+1.5,2.0);
//设置十位数字显示的旋转角度
glRotatef’(num_rot,0.0,-1.0,0.0);
//调用显示列表,显示十位数字
glCallList(num2+10);
glPopMatrix();
3.3 反射效果的实现
创建镜面反射显示效果,使用蒙板缓存技术。首先,需要先把各个缓存清空,包括蒙板缓存。
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_ BUFFER_BIT|GL_STENCIL_BUFFER _BIT);
接着,需要设置作为反射面的对象。在这里,把序号为1的显示列表对象floor作为反射平面。因为,反射生成的影像都绘制在反射面上,不需要深度检测。另外,反射面只是作为蒙板的参考,不需要显示,所以设置了颜色掩码,把参数设为GL_FALSE,这样就不会在屏幕上绘制任何东西。
glDisable(GL_DEPTH_TEST);//关闭深度检测
glColorMask(GL_FALSE,GL_FALSE, GL_FALSE,GL_FALSE);∥设置颜色掩码
接着,启用蒙板测试,这样就可以修改蒙板缓存中的值了。蒙板函数用于确定一个颜色片段是应该丢弃还是保留(被绘制)。蒙板缓存区中的值与参考值进行比较,比较标准是glStencilFunc所指定的比较函数。参考值和蒙板缓存区的值都可以与掩码进行AND操作。蒙板测试的结果还导致蒙板缓存区根据glStencilOp函数所指定的行为进行修改。
glEnable(GI._STENCIL_TEST);
//启用蒙板测试,用glStencilFune函数所指定的参考值
//替换蒙板参数值
glStencilOp(GL_REPLACE, GL_REPLACE,GL_REPLACE);
glStencilFunc(GL_ALWAYS,1,0 xffffffff);
//总是通过蒙板测试
glCallList(l); //floor
//恢复opengl的状态
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE,GL_TRUE);//允许绘制
glEnable(GL_DEPTH_TEST);//允许深度检测
glStencilFunc(GL_EQUAL,1, Oxffffffff);
//当绘制时,不改变蒙板缓存区的值
glStencilOp(GL_KEFP,GL_KEEP, GL_KEEP);
//保持当前的蒙板缓存区值
启用了蒙板缓存,只能在蒙板中值为1的地方绘制,反射的实质就是在反射屏幕的对应位置再绘制一个物体,并把它放置在反射平面中,而这个物体的Y向正好是反的:
glScalef(1.0,-1.0,1.0);
由于在蒙板缓存上绘制的对象要与实际显示的floor融合在一起,所以需要把OpenGL的融合打开,并设置相应的融合系数。
lEnable(GL_BLEND);//打开融合
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MlNUS_SRC_ALPHA);//设置融合系数
//在这里绘制相应的三维统计结果
glDisable(GL_BLEND);∥关闭融合
glDisable(GL_STENCIL_TEST);//关闭蒙板测试以融合方式显示地板
最后,正常显示周围的效果画面和三维统计结果。
3.4 动态阴影的实现
使用OpenGL蒙板缓冲区实现平面阴影,关键是利用光源的矢量位置和地板的法线矢量,生成平面阴影投射矩阵。然后:
glEnable(GL_STENCIL_TEST);//启用蒙板测试
glStencilFunc(GL_LESS,2, Oxffffffff);
glStencilOp(GL_REPLACE, GL_REPLACE,
GL_REPLACE);
glEnable(GL_BLEND);//启动融合
glBlendFunc(GL_SRC_ALPHA, GL_ONE_
MINUS_SRC_ALPHA);
glDisable(GL_LIGHTING);//关闭灯光
glColor4f(0.0,0.0,0.0,0.5);//设置阴影颜色glPushMatrix();
glMultMatrixf((GLfloat*)floorShadow);
//对显示画面乘上平面阴影投射矩阵
//在这里,显示产生阴影的对象
glPopMatrix();
glDisable(GL_BLEND);//关闭融合
glEnable(GL_LIGHTING);//打开灯光
glDisaHe(GL_STENCIL_TEST);//关闭蒙板测试
4结束语
利用OpenGL技术,可以充分利用当前各种具有3D加速功能的显卡,使得开发的显示系统具有实时性和可移植性。文中实现的实时投票结果三维显示,给人以生动、直观的视觉效果。它可以应用到不同的领域,如投票表决、数据可视、结果统计等。
>> 本文固定链接: http://www.vcgood.com/archives/3594