搜档网
当前位置:搜档网 › 利用OpenGL实现三维绘图

利用OpenGL实现三维绘图

第23卷 第3期2002年9月内蒙古农业大学学报

Journal of I nner M ongolia Agricultural U niversity

Vol.23 N o.3

Sep.2002

文章编号:1009-3575(2002)03-0098-05

利用OpenGL实现三维绘图

曹洪军1 原英2

(1. 哈尔滨市公安局技侦支队,哈尔滨 150010;2. 内蒙古农业大学经管学院,呼和浩特 010010)

摘要: 本文通过一个用OpenG L显示图像的Windows程序实例,介绍了OpenGL编程的基本概念、步骤,以及如何利用OpenGL函数库实现三维绘图。

关键词: 光源; 材质; 投影; 像素

中图分类号: T P317.4 文献标识码: A

IMPLEMENT THREE-DIMENSION IMAGES USED OPENGL

C AO Hong-jun,1 YUAN Ying2

(1. T echnological and Reconnoitr e Detachment,Haerbin City Public Secur ity Bureau,Harbin 150010;

2. College of Economic M anag ement,Inner M ongolia A gricultural U niversity,Huhhot 010019)

Abstract: The basic concepts and approaches are introduced in this paper by illustrating a w indow s program of displaying im ages used OpenGL.And how to implement three-dimension imag es used OpenGL is also argued. Key words: lamp-house; material; projection; pixel

在三维绘图蓬勃发展的过程中,计算机公司推出了大量的三维绘图软件包。其中SGI公司推出的OpenGL,作为1个性能优越的图形应用程序设计界面(APl)异军突起,取得了很大的成就。它以高性能的交互式三维图形建模能力和易于编程开发,得到了Microsoft、IBM、DEC、Sun、HP等大公司的认同。因此, OpenGL已经成为1种三维图形开发标准,是从事三维图形开发工作的必要工具。

1 初始化OpenGL绘图环境

1.1 定义颜色格式和缓冲模式

OpenGL提供两种颜色模式:RGB(RGBA)模式和颜色索引模式。在RGBA模式下所有颜色的定义用RGB3个值来表示,有时也加上Alpha值(表示透明度)。RGB3个分量值的范围都在0和1之间,它们在最终颜色中所占的比例与它们的值成正比。如:(1,1,0)表示黄色,(0,0,1)表示蓝色。颜色索引模式下每个象素的颜色是用颜色索引表中的某个颜色索引值表示(类似于从调色板中选取颜色)。由于三维图形处理中要求颜色灵活,而且在阴影,光照,雾化,融合等效果处理中RGBA的效果要比颜色索引模式好,所以,在编程时大多采用RGBA模式。

OpenGL提供了双缓存来绘制图像。即在显示前台缓存中的图像同时,后台缓存绘制第2幅图像。当后台绘制完成后,后台缓存中的图像就显示出来,此时原来的前台缓存开始绘制第3幅图像,如此循环往复,以增加图像的输出速度。

收稿日期: 2001-11-29

作者简介: 曹洪军(1971~),男,助理工程师,从事公安技侦的研究.

设置窗口显示模式函数:

void aux lnitDisplayM ode(

AUX_DOUBLE//双缓存方式

|AUX_RGBA);//RGBA 颜色模式

1.2 设置光源

OpenGL 的光源大体分为3种:环境光(Ambient lig ht),即来自于周围环境没有固定方向的光。漫射光(Diffuse light)来自同一个方向,照射到物体表面时在物体的各个方向上均匀发散。镜面光(Specular light)则是来自于同一方向,也沿同一个方向反射。全局环境光是1种特殊的环境光,它不来自特于某种定光源,通常做为场景的自然光源。

指定光源函数:

Void glLightfv(Glenum light,//光源号

Glenum pname,//指明光源种类

//GL_DIFFU SE 光源为漫射光光源

//GL_AMBIENT 光源为环境光光源

//GL_SPECULAR 光源为镜面光光源

const Glfloat *params);//指向颜色向量的指针

设置全局环境光函数:

void glLightModelfv(GL_LIGH T_M ODEl_

AMBIENT,const Glfloat *param);

//param 指向颜色向量的指针

起用光源函数:

void glEnable(GL_LIGHT ING);

void glEnable(GL_enum cap);//cap:指明光源号

1.3 设置材质

在OpenGL 中,用材料对光的三原色(红绿蓝)的反射率大小来定义材料的颜色。与光源相对应,材料的颜色,也分为环境色,漫反射色和镜面反射色,由此决定该材料对应不同的光呈现出不同的反射率。由于人所看到物体的颜色是光源发出的光经物体反射后进入眼睛的颜色。所以,物体的颜色是光源的环境光,漫反射光和镜面反射光与材料的环境色,漫反射色和镜面反射色的综合。例如:OpenGL 的光源色是(LR,LG,

LB),材质色为(M R,M G,MB),那么,在忽略其他反射效果的情况下,最终进入眼睛的颜色是(LR *MR,LG *MG,LB *M B)。

材质定义函数:

void g lM aterialfv (GLenum face,//指明在设置材质的哪个表面的颜色。可以是GL_FRONT,GL_BACK,或GL_FRONT_AND_BACK

GLenum pname,//与光源的pname 参数相似

const float *params);//指向材质的颜色向量

1.4 定义投影方式

也即选择观察物体的角度和范围。由于我们是三维绘图,所以采用不同的视点和观察范围,就会产生不同的观察效果。由于计算机只能显示二维图形,所以在表示真实世界中的三维图形时,需将三维视景转换成二维视景。这是产生三维立体效果的关键。OpenGL 提供了2种将3D 图形转换成2D 图形的方式。正投影(Orthographic Projection)和透视投影(Perspective Projection)。其中,正投影指投影后物体的大小与视点的远近无关,通常用于CAD 设计:而透视投影则符合人的心理习惯,离视点近的物体大,离视点远的物体小。此外,在OpenGL 中还要定义投影范围,只有在该范围中的物体才会被投射到计算机屏幕上,投影范围外的物体将被裁减掉。

定义投影范围(不同的投影方式对应不同函数):void glOrtho(GLdouble left,GLdouble right,

99第3期 曹洪军等: 利用O penGL 实现三维绘图

100内蒙古农业大学学报 2002年

GLdouble bottom,GLdouble top,

GLdouble near,GLdouble far);

//(1eft,bottom,near)和(right,top,far)分别给出正射投影范围的左下角和右上角的坐标。

2 定义与Windows接口的系统函数

2.1 定义绘图窗口的位置

void aux lnitPosition(GLint x,GLint y,GLsizei w idth,GLsizei heigh);//(x,y)

给出窗口左上角坐标

//w idth和heigh给出窗口的宽高

2.2 定义绘图窗口的标题

void aux lnit Window(GLbyte*窗口标题字串);

2.3 定义绘图窗口改变时的窗口刷新函数

void aux ReshapeFunc(回调函数名称):

//当窗口改变形状时调指定的回调函数

2.4 定义空闲状态的空闲状态函数以实现动画

void aux ldleFunc(回调函数名称);

//当系统空闲时调用指定的回调函数

2.5 定义场景绘制函数(当窗口更新或场景改变时调用)

void aux MainLoop(回调函数名称):

//当窗口需要更新或场景变化时调用

3 VC下实现

本文将给出1个例子,这个例子是1个用OpenGL显示图像的Windows程序,通过这个程序我们也可以知道用OpenGL编程的基本要求。我们知道,GDI是通过设备句柄(Dev ice Contex t以下简称 DC)来绘图,而OpenGL则需要绘制环境(Rendering Contex t,以下简称RC)。每1个GDI命令需要传给它1个DC,与GDI不同,OpenGL使用当前绘制环境(RC)。一旦在1个线程中指定了1个当前RC,所有在此线程中的OpenGL命令都使用相同的当前RC。虽然在单一窗口中可以使用多个RC,但在单一线程中只有1个当前RC。本例将首先产生1个OpenGL RC并使之成为当前RC,分为3个步骤:设置窗口像素格式;产生RC;设置为当前RC。

3.1 首先创建工程用AppWizard产生1个EXE文件,选择工程目录,并在工程名字中输入GLSampllel,保持其他的不变;第1步、选单文档(SDl);第2步、不支持数据库;第3步、不支持OLE;第4步、不选中浮动工具条、开始状态条、打印和预览支持、帮助支持的复选框(选中也可以,本文只是说明最小要求),选中三维控制(3D Controls);第5步、选中产生源文件注释并使用MFC为共享动态库;第6步、保持缺省选择。按Finish结束,工程创建完毕,如图1所示。

3.2 将此工程所需的OpenGL文件和库加入到工程中

在工程菜单中,选择 Build下的 Settings项。单击 Link标签,选择 General目录,在Obiect ILibrary M odules的编辑框中输入 OpenGL32.1ib glu32.1ib glaux.1ib(注意,输入双引号中的内容,各个库用空格分开:否则会出现链接错误),选择 OK结束。然后打开文件: stdafx.h,将下列语句插入到文件中(划下划线的语句为所加语句):

#define VC_EXTRALEAN//Ex clude rarely-used stuff from Windows headers

#include//M FC core and standard com ponents

#include//M FC extensions

#ifndef_AFX_NO_AFXCM N_SUPPORT

#include//M FC support for Window s95Com mon Controls

#endif//_AFX_NO_AFXCM N_SUPPORT

3.3 改写OnPreCreate 函数并给视类添加成员函数和成员变量

OpenGL 需要窗口加上WS_CLIPCH ILDREN(创建父窗口使用的Window s 风格,用于重绘时裁剪子窗口所覆盖的区域)和WS_CLIPSIBLINGS(创建子窗口使用的Window s 风格,用于重绘时剪裁其他子窗口所覆盖的区域)风格。把OnPreCreate 改写成如下所示:

BOOL CGLSam ple 1View ::PreCr-eateWindow (CREATEST RUCT&cs)

{ cs.Sty le =(WS_CLIPCH I-LDREN1WS_CLIPSIBLINGS);

return CView ::PreCreate-Window (cs);

}

产生1个RC 的第1步是定义窗口的像素格式。像素格式决定窗口着所显示的图形在内存中是如何表示的。由像素格式控制的参数包括:颜色深度、缓冲模式和所支持的绘画接口。在下面将有对这些参数的设置。我们先在CGLSample 1V iew 的类中添加1个保护型的成员函数BOOL SetWindow Pixel-Format(HDC hDC)(用鼠标右键添加),并编辑其中的代码,见程序1。

BOOL CGLSam plelV iew ::SetWindow PixelFormat(HDC hDC)

{

PIXELFORMATDESCRIPT OR pixelDesc;

pixelDesc.nSize=sizeof(PIXELFORMATDESCRIPTOR);

pixelDesc.nVersion=1;

pixelDesc.dw Flags=PFD DRAW_TO_WINDOW

PFD_DRAW_T O_BITMAP

PFD_SU PPORT_OpenGL

PFD_SU PPORT_GDl1

PFD_STEREO_DONTCARE;

pixelDesc.iPixelType=PFD_T YPE_RGBA;

https://www.sodocs.net/doc/066361446.html,olorBits=32;

Pix elDesc.cRedBits:8;

pixelDesc.cRedShift=16;

pixelDesc.cGreenBits=8;

pixelDesc.cGreenShift=8;

pixeDese.cBlueBits=8;

pixelDese.cBlueShift=0;

pixelDese.cAlphaBits=0;

pixelDesc.cAlphaShift=0;

pixelDese.cAccumBits=64:

pixeIDesc.cAccumRedBits=16;

pixelDese.cAccumGreenBitS=16;

pixelDesc.cAccumBlueBits=16:

pixelDesc.cAccumAlphaBits=0;

pixelDesc.cDepthBits=32;

pixelDesc.cStencilBitS=8;

pixelDese.cAuxBuffers=0;

pixelDese.iLayerType=PFD_MAIN_PLANE;

pixelDese.bReserved=0;

pixelDesc.dw LayerM ask=0;pixelDesc.dw VisibleM ask=0;

101第3期 曹洪军等: 利用O penGL 实现三维绘图

102内蒙古农业大学学报 2002年

pixelDese.dw Dam ageM ask=0;

m_GLPixeIIndex=ChoosePixelFormat(hDC,&pixelDesc):

if(m_GLPix eIIndex=0)//Let!s choose a default index.

{m_GLPix eIlndex=1;

if(DescribePixelFormat(hDC,m_GLPixellndex.

sizeof(PIXELFORMATDESCRIPTOR),&pixelDese)=0)

{return FALSE;

}

}

if(SetPix elFormat(hDC,m-GLPix elIndex,&pixelDese)=FALSE)

{return FALSE:

}

return T RUE;

}

接着用鼠标右键在CGLSample1VieW中添加保护型的成员变量:

int m_GlPix ellndex;

3.4 用C1ass Wizard添加WM_CREATE的消息处理函数OnCreate

添加OnCreate函数后如程序1所示。

至此,OpenGL工程的基本框架就建好了。但如果你现在运行此工程,则它与一般的MFC程序看起来没有什么两样。

3.5 代码解释

现在我们可以看一看Describe-PixeIFormat提供有哪几种像素格式,并对代码进行一些解释:

PIXELFORMATDESCRIPT OR包括了定义像素格式的全部信息。

DWFlags定义了与像素格式兼容的设备和接口。

通常的OpenGL发行版本并不包括所有的标志(flag)。w Flags能接收以下标志:

PFD_DRAW_T O_W INDOW使之能在窗口或者其他设备窗口画图;

PFD_DRAW_T O_BITMAP使之能在内存中的位图画图;

PFD_SUPPORT_GDI使之能调用GDI函数(注:如果指定了PFD_DOUBLEBU FFER,这个选项将无效):

PFD_SU PPORT_OpenGL使之能调用OpenGL函数;

PFD_GENERIC_FORMAT假如这种象素格式由Windows GDI函数库或由第3方硬件设备驱动程序支持,则需指定这一项;

PFD_NEED_FPALET TE告诉缓冲区是否需要调色板,本程序假设颜色是使用24或32位色,并且不会覆盖调色板;

PFD_NEED_SYSTEM_PALETTE这个标志指明缓冲区是否把系统调色板当作它自身调色板的一部分;

PFD_DOUBLEBUFFER指明使用了双缓冲区(注:GDI不能在使用了双缓冲区的窗口中画图);

PFD_STEREO指明左、右缓冲区是否按立体图像来组织。

Pix elType定义显示颜色的方法。PFD_TYPE_RGBA意味着每一位(bit)组代表着红、绿、蓝各分量的值。PFD_T YPE_COLORINDEX意味着每一位组代表着在彩色查找表中的索引值。本例都是采用了PFD_ T YPE_RGBA方式。

参 考 文 献:

[1] 唐荣锡.计算机图形学教程?M#.北京:科学出版社,2000.

[2] 王新成.高级图形处理技术?M#.北京:中国科学技术出版社,2001.

[3] 刘静华,王永心.最新V C++绘图程序设计技巧与实例教程?M#.北京:科学出版社,2001.

相关主题