利用OpenGL实现三维绘图 在三维绘图蓬勃发展的过程中,计算机公司推出了大量的三维绘图软件包。其中 SGI公司推出的OpenGL,作为一个性能优越的图形应用程序设计界面(API)异军 突起,取得了很大的成就。它以高性能的交互式三维图形建模能力和易于编程开 发,得到了Microsoft、 IBM、 DEC、 Sun、 HP等大公司的认同。因此,OpenGL 已经成为一种三维图形开发标准,是从事三维图形开发工作的必要工具。 一.初始化OpenGL绘图环境 1. 定义颜色格式和缓冲模式 OpenGL提供两种颜色模式:RGB(RGBA)模式和颜色索引模式。在RGBA模式下所有 颜色的定义用RGB三个值来表示,有时也加上Alpha值(表示透明度)。RGB三个分 量值的范围都在0和1 之间,它们在最终颜色中所占的比例与它们的值成正比。如 :(1,1,0)表示黄色,(0,0,1)表示蓝色。颜色索引模式下每个象素的颜 色是用颜色索引表中的某个颜色索引值表示(类似于从调色板中选取颜色)。由 于三维图形处理中要求颜色灵活,而且在阴影,光照,雾化,融合等效果处理中 RGBA 的效果要比颜色索引模式好,所以,在编程时大多采用RGBA模式。 OpenGL提供了双缓存来绘制图像。即在显示前台缓存中的图像同时,后台缓存绘 制第二幅图像。当后台绘制完成后,后台缓存中的图像就显示出来,此时原来的 前台缓存开始绘制第三幅图像,如此循环往复,以增加图像的输出速度。 设置窗口显示模式函数: void auxInitDisplayMode( AUX_DOUBLE//双缓存方式 |AUX_RGBA);//RGBA颜色模式 2.设置光源 OpenGL的光源大体分为三种:环境光(Ambient light),即来自于周围环境没有 固定方向的光。漫射光(Diffuse light)来自同一个方向,照射到物体表面时在物 体的各个方向上均匀发散。镜面光(Specular light)则是来自于同一方向,也沿 同一个方向反射。全局环境光是一种特殊的环境光,它不来自特于某种定光源, 通常做为场景的自然光源。 指定光源函数: Void glLightfv(Glenum light, //光源号 Glenum pname, //指明光源种类 //GL_DIFFUSE 光源为漫射光光源 //GL_AMBIENT 光源为环境光光源 //GL_SPECULAR 光源为镜面光光源 const Glfloat* params);//指向颜色向量的指针 设置全局环境光函数: void glLightModelfv( GL_LIGHT_MODEL_ AMBIENT, const Glfloat* param ); //param 指向颜色向量的指针 起用光源函数: void glEnable(GL_LIGHTING); void glEnable(GL_enum cap);// cap :指明光源号 3. 设置材质 在OpenGL中,用材料对光的三原色(红绿蓝)的反射率大小来定义材料的颜色。与 光源相对应,材料的颜色,也分为环境色,漫反射色和镜面反射色,由此决定该 材料对应不同的光呈现出不同的反射率。由于人所看到物体的颜色是光源发出的 光经物体反射后进入眼睛的颜色。所以,物体的颜色是光源的环境光,漫反射光 和镜面反射光与材料的环境色,漫反射色和镜面反射色的综合。例如:OpenGL 的 光源色是(LR,LG,LB),材质色为(MR,MG,MB),那么,在忽略其他反射效 果的情况下,最终进入眼睛的颜色是(LR*MR,LG*MG,LB*MB)。 材质定义函数: void glMaterialfv( GLenum face, //指明在设置材质 的哪个表面的颜色。可以是GL_FRONT, GL_BACK,或GL_FRONT_AND_BACK GLenum pname,//与光源的pname参数相似 const float* params);//指向材质的颜色向量 4.定义投影方式 也即选择观察物体的角度和范围。由于我们是三维绘图,所以采用不同的视点和 观察范围,就会产生不同的观察效果。由于计算机只能显示二维图形,所以在表 示真实世界中的三维图形时,需将三维视景转换成二维视景。这是产生三维立体 效果的关键。OpenGL提供了两种将3D图形转换成2D图形的方式。正投影( Orthographic Projection)和透视投影(Perspective Projection)。其中,正投 影指投影后物体的大小与视点的远近无关,通常用于CAD设计;而透视投影则符合 人的心理习惯,离视点近的物体大,离视点远的物体小。此外,在OpenGL中还要 定义投影范围,只有在该范围中的物体才会被投射到计算机屏幕上,投影范围外 的物体将被裁减掉。 定义投影范围(不同的投影方式对应不同函数): void glOrtho( GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near,GLdouble far); //(left,bottom,near)和(right,top,far)分别给出正射投 //影投影范围的左下角和右上角的坐标。 二.定义与Windows接口的系统函数 1.定义绘图窗口的位置: void auxInitPosition(GLint x,GLint y,GLsizei width, GLsizei heigh);// (x,y)给出窗口左上角坐标 // width和 heigh给出窗口的宽高 2.定义绘图窗口的标题: void auxInitWindow(GLbyte* 窗口标题字串); 3.定义绘图窗口改变时的窗口刷新函数: void auxReshapeFunc(回调函数名称); //当窗口改变形状时调指定的回调函数 4.定义空闲状态的空闲状态函数以实现动画: void auxIdleFunc(回调函数名称); //当系统空闲时调用指定的回调函数 5.定义场景绘制函数(当窗口更新或场景改变时调用): void auxMainLoop(回调函数名称); //当窗口需要更新或场景变化时调用 三.VC下实现 在VC编辑器下键入下述代码后,保存为后缀是cpp的C++文件。开始编译,在The build command requires an active project workspace。 Would you like to create a default project workspace? 的提示后,选择“是(Y)”。进入 Project菜单,选择Setting项,弹出Project Setting 对话框,选择Link项,在 Libaray栏目中加入OpenGL提供的函数库:“opengl32.lib glu32.lib glaux.lib ”。(注:在执行时,Windows 的system目录下要包含opengl32.dll和glu32.dll 两个动态连接库)。 附源程序代码: #include "windows.h" #include "gl/gl.h" #include "gl/glaux.h" #include "gl/glu.h" #include "math.h" void myinit() { glClearColor(1,1,0,0); GLfloat ambient[]={.5,.5,.5,0}; glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient); GLfloat mat_ambient[]={.8,.8,.8,1.0}; GLfloat mat_diffuse[]={.8,.0,.8,1.0}; GLfloat mat_specular[]={1.0,.0,1.0,1.0}; GLfloat mat_shininess[]={50.0}; GLfloat light_diffuse[]={0,0,.5,1}; GLfloat light_position[]={0,0,1.0,0}; glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT,mat_ambient); glMaterialfv(GL_FRONT_AND_BACK,GL_DIFFUSE,mat_diffuse); glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,mat_specular); glMaterialfv(GL_FRONT_AND_BACK,GL_SHININESS,mat_shininess); glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse); glLightfv(GL_LIGHT0,GL_POSITION, light_position); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glDepthFunc(GL_LESS); glEnable(GL_DEPTH_TEST); } void CALLBACK display() { glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); auxSolidSphere(1.0);//绘制半径为1.0的实体球 glFlush(); //强制输出图像 auxSwapBuffers(); //交换绘图缓存 _sleep(100);} void CALLBACK Idledisplay() { //x,y满足x2+y2=0.01。这样可以使物体沿该圆轨 //迹运动。 static float x=-.1,y=0.0; static BOOL mark=TRUE; static float step=.01; x+=step; if(x<=.1&&x>=-.1) { if(step>0) y=sqrt(.01-x*x); else y=-sqrt(.01-x*x); glTranslatef(x,y,0); } else { step=0-step; } display(); } void CALLBACK myReshape(GLsizei w,GLsizei h) { glViewport(0,0,w,h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); if(w<=h) glOrtho(-3.5,3.5,-3.5*(GLfloat)w/(GLfloat)h, 3.5*(GLfloat)w/(GLfloat)h,-10,10); else glOrtho(-3.5*(GLfloat)w/(GLfloat)h,3.5* (GLfloat)w/(GLfloat)h,-3.5,3.5,-10,10); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void main() { auxInitDisplayMode(AUX_DOUBLE|AUX_RGBA); auxInitPosition(0,0,400,400); auxInitWindow(" circle "); myinit(); auxReshapeFunc(myReshape); auxIdleFunc(Idledisplay); auxMainLoop(display); }