OpenGL+MFCBeginner.docx
- 文档编号:7754729
- 上传时间:2023-01-26
- 格式:DOCX
- 页数:23
- 大小:30.18KB
OpenGL+MFCBeginner.docx
《OpenGL+MFCBeginner.docx》由会员分享,可在线阅读,更多相关《OpenGL+MFCBeginner.docx(23页珍藏版)》请在冰豆网上搜索。
OpenGL+MFCBeginner
VisualC++6.0环境下的OpenGL编程
一.OpenGL简介
OpenGL(OpenGraphicsLibrary)是从SiliconGraphicsIncorporated(SGI公司)在其图形工作站(SGIIndigo/Octane)上开发高质量图像的接口函数库(GL:
GraphicsLibrary)移植到PC机上的开放式三维图形接口。
实际上它是图形软件和硬件的接口,是一个性能优越的图形应用程序设计界面(API),使程序员能够在PC机上用C/C++语言开发复杂的三维图形。
它以高性能的交互式三维图形建模能力和易于编程开发,得到了Microsoft、IBM、DEC、Sun、HP等大公司的认同。
由OpenGL具有跨平台性、简便、高效、功能完善,目前已经成为了三维图形制作方法中的工业标准,是从事三维真实感图形开发工作的必要工具。
自从WindowsNT3.51在微机平台上支持OpenGL以后,现在微软公司在Windows95OSR2、WindowsNT4.0中连续性的提供OpenGL开发环境。
VisualC++从4.2版本以后已经完全支持OpenGLAPI。
微软在VisualC++6.0中提供了三个OpenGL的函数库(opengl32.lib,glu32.lib,glaux.lib),包括OpenGL库函数141个,以gl作为前缀;OpenGL实用库函数44个,以glu作为前缀;OpenGL辅助库函数31个,以aux作为前缀。
另外,还有Windows专用库函数(WGL)6个,以wgl作为前缀及Win32API函数(WGL)5个,无前缀。
二.基础知识
1.OPENGL函数、变量命名准则
变量:
例如1.0f实际就是1.0,一般写成1.0f看上去好辨认一些。
前缀
类型
对应C变量
OpenGL类型定义
b
8-bitintsigned
char
Glbyte
s
16-bitint
short
Glshort
i
32-bitint
long
Glint,Glsizei
f
32-bitfloat
float
Glfloat,Glclampf
d
64-bitfloat
double
Gldouble,Glclampd
ub
8-bitunsignedint
unsignedchar
Glubyte,glboolean
us
16-bitunsignedint
unsignedshort
Glushort
ui
32-bitunsignedint
unsignedlong
Gluint,Glenum,Glbitfield
函数:
函数参数类型作为函数后缀
例如:
glVertex2i(2,4)表明是opengl基本函数(gl-)是绘点的函数(-Vertex-)是两个整型参数(-2i)
2.设备上下文与渲染上下文
具有Windows编程经验的人都知道,在Windows下用GDI绘图是通过设备上下文(DeviceContext简写DC)调用相应的函数来完成的。
同样,用OpenGL作图也是类似的,也必须使用DC。
但是,OpenGL必须处理特殊的DC设备上下文,这是DC中专为OpenGL使用的一种。
OpenGL函数是通过“渲染上下文”(RenderingContext简写RC)调用相应的函数来完成三维图形的绘制。
OpenGLforWindows的设计与OpenGLforUNIX的程序设计有一点小区别,关键就在于如何将OpenGL与不同的操作系统下的窗口系统联系起来。
如果调用OpenGL辅助库窗口管理函数,则不用考虑这些问题。
每一个GDI命令需要传给它一个DC,与GDI不同,OpenGL使用当前渲染上下文(RC)。
一旦在一个线程中指定了一个当前RC,所有在此线程中的OpenGL命令都使用相同的当前RC。
一个OpenGL渲染上下文内有OpenGL与Windows窗口系统相关的各种信息。
一个OpenGL应用首先必须创建一个渲染上下文,然后再启动它,最后在所定义的窗口内按常规方式调用OpenGL函数绘制图形。
一个渲染上下文不同于其它DC(它们调用每个GDI函数都需要一个句柄),而渲染上下文方式下只需一个句柄就可以任意调用OpenGL函数。
也就是说,只要当前启用了某个渲染上下文,那么在未删除渲染上下文之前都可以调用任何OpenGL函数,进行各种操作。
有以下需要注意的方面:
1.一个线程只能拥有一个渲染上下文(RC),也就是说,用户如果在一个线程内对不同设备DC作图,只能通过更换与RC对应的DC来完成,而RC在线程中保持不变。
(当然,删除旧的RC后再创建新的是可以的)与此对应,一个RC也只能属于一个线程,不能被不同线程同时共享。
2.设定DC位图格式等于设定了相应的窗口的位图格式,并且DC和窗口的位图格式一旦确定就不能再改变。
这一点只能期望以后的Windows版本改进了。
3.一个RC虽然可以更换DC,在任何时刻只能利用一个DC(这个DC称为RC的当前DC),但由于一个窗口可以让多个DC作图从而可以让多个线程利用多个RC在该窗口上执行OpenGL操作。
4.现在的Windows下的OpenGL版本对OpenGL和GDI在同一个DC上作图有一定的限制。
当使用双缓存用OpenGL产生动画时,不能使用GDI函数向该DC作图。
5.不建议用ANSIC在Windows下编写OpenGL程序。
这样的程序虽然具有跨平台的可移植性(比如很多SGI的例子程序),但是它们不能利用Windows操作系统的很多特性,实用价值不大。
3.像素格式结构
设备上下文DC包括许多如何在窗口上显示图形的信息,即指定画笔和刷子的颜色,设置绘图模式、调色板、映射模式以及其它图形属性,即支持“位图格式”(PIXELFORMAT)属性,和RC有着位图结构上的一致。
只要在创建RC时与一个DC建立联系(RC也只能通过已经建了位图格式的DC来创建),OpenGL的函数就可以通过RC对应的DC画到相应的显示设备上。
在创建一个图形操作表之前,首先必须设置像素格式。
像素格式含有设备绘图界面的属性,这些属性包括绘图界面是用RGBA模式还是颜色表模式,像素缓存是用单缓存还是双缓存,以及颜色位数、深度缓存和模板缓存所用的位数,还有其它一些属性信息。
每个OpenGL渲染上下文都支持一种指定的像素格式。
一般用一个名为PIXELFORMATDESCRIPTOR的结构来表示某个特殊的像素格式,这个结构包含26个属性信息。
Win32定义PIXELFORMATDESCRIPTOR如下所示:
typedefstructtagPIXELFORMATDESCRIPTOR
{//pfd
WORDnSize;
WORDnVersion;
DWORDdwFlags;
BYTEiPixelType;
BYTEcColorBits;
BYTEcRedBits;
BYTEcRedShift;
BYTEcGreenBits;
BYTEcGreenShift;
BYTEcBlueBits;
BYTEcBlueShift;
BYTEcAlphaBits;
BYTEcAlphaShift;
BYTEcAccumBits;
BYTEcAccumRedBits;
BYTEcAccumGreenBits;
BYTEcAccumBlueBits;
BYTEcAccumAlphaBits;
BYTEcDepthBits;
BYTEcStencilBits;
BYTEcAuxBuffers;
BYTEiLayerType;
BYTEbReserved;
DWORDdwLayerMask;
DWORDdwVisibleMask;
DWORDdwDamageMask;
}PIXELFORMATDESCRIPTOR;
三.OpenGLforWindows程序设计步骤
1.设置窗口像素格式-初始化PIXELFORMATDESCRIPTOR结构
设置显示设备DC的像素格式(PIXELFORMAT)属性。
这通过填充一个PIXELFORMATDESCRIPTOR的结构来完成(关于PIXELFORMATDESCRIPTOR中各项数据的意义,请参照VC的帮助信息),该结构决定了OpenGL作图的物理设备的属性,比如该结构中的数据项dwFlags中PFD_DOUBLEBUFFER位如果没有设置(置1),通过该设备的DC上作图的OpenGL命令就不可能使用双缓冲来做动画。
如:
定义PIXELFORMATDESCRIPTOR结构的pfd如下:
PIXELFORMATDESCRIPTORpfd={
sizeof(PIXELFORMATDESCRIPTOR),//sizeofthispfd
1,
PFD_DRAW_TO_WINDOW|//supportwindow
PFD_SUPPORT_OPENGL|//supportOpenGL
PFD_DOUBLEBUFFER,//doublebuffered
PFD_TYPE_RGBA,//RGBAtype
24,//24-bitcolordepth
0,0,0,0,0,0,//colorbitsignored
0,//noalphabuffer
0,//shiftbitignored
0,//noaccumulationbuff
0,0,0,0,//accumbitsignored
32,//32-bitz-buffer
0,//nostencilbuffer
0,//noauxiliarybuffer
PFD_MAIN_PLANE,//mainlayer
0,//reserved
0,0,0//layermasksignored
};
有一些位图格式(PIXELFORMAT)是DC支持的,而有一些DC就不支持了。
所以程序必须先用:
:
ChoosePixelFormat()来选择DC所支持的与指定位图格式最接近的位图格式,然后用:
:
SetPixelFormat()设置DC的位图格式。
2.产生及设置渲染上下文RC
2.1.利用刚才的设备DC建立渲染上下文RC(:
:
wglCreateContext())
2.2.联系RC与DC建立(:
:
wglMakeCurrent())。
3.真实感图形绘制
建立三维模型,调用OpenGL函数作图。
由于线程与RC一一对应,OpenGL函数的参数中都不指明本线程RC的句柄(handle)。
其一般过程为:
3.1.定义光照模型和材质
(1)光源
OpenGL提供了一系列建立光照模型的库函数,使用户可以十分方便地在三维场景中建立所需的光照模型。
OpenGL中的光照模型由环境光(AmbientLight)、漫反射光(DiffuseLight)、镜面反射光(SpecularLight)等组成,同时还可设置光线衰减因子来模拟真实的光源效果。
例如,定义一个黄色光源如下:
GlfloatLight_position[]={1.0,1.0,1.0,0.0,};
GlfloatLight_diffuse[]={1.0,1.0,0.0,1.0,};
glLightfv(GL_LIGHT0,GL_POSTTION,light_position);//定义光源0的位置
glLightfv(GL_LIGHT0,GL_DIFFUSE,light_diffuse);//定义光源0的漫射光
光源必须经过启动后才会影响三维场景中的实体,可以通过以下指令使光源有效:
glEnable(LIGHTING);//启动光照模型;
glEnable(GL_LIGHT0);//使光源GL_LIGHT0有效;
OpenGL中一共可以定义GL_LIGHT0~GL_LIGHT7八个光源。
(2)表面的反射系数
OpenGL中的材质是指构成三维实体的材料在光照模型中对于红、绿、蓝三原色的反射率。
与光源的定义类似,材质的定义分为环境、漫射、镜面反射成分,另外还有镜面高光指数、辐射成分等。
通过对三维实体的材质定义可以大大提高应用程序所绘制的三维场景的逼真程度。
例如:
//设置材质表面的反射系数
Glfloatmat_ambient[]={0.8,0.8,0.8,1.0};//环境反射系数:
灰色
Glfloatmat_diffuse[]={0.8,0.0,0.8,1.0};//漫反射:
紫色
Glfloatmat_specular[]={1.0,0.0,1.0,1.0};//镜面反射:
亮紫色
Glfloatmat_shiness[]={100.0};//高光系数
glMaterialfv(GL_FRONT,GL_AMBIENT,mat_ambient);//定义环境光反射系数
glMaterialfv(GL_FRONT,GL_DIFFUSE,mat_diffuse);//定义漫射光反射系数
glMaterialfv(GL_FRONT,GL_SPECULAR,mat_specular);//定义镜面光反射系数
glMaterialfv(GL_FRONT,GL_SHINESS,mat_shiness);//定义高光系数
(3)材质RGB值与光源RGB值的关系
OpenGL中材质的颜色与光照模型中光源的颜色含义略有不同。
对于光源,R、G、B值表示三原色在光源中所占有的比率;而对于材质定义,R、G、B的值表示具有这种材质属性的物体对于三原色的反射比率,场景中物体所呈现的颜色与光照模型、材质定义都相关。
例如,若定义的光源颜色是(Lr,Lg,Lb)=(1.0,1.0,1.0)(白光),物体的材质颜色定义为(Mr,Mg,Mb)=(0.0,0.0,0.8)(蓝色),则最终到达人眼的物体颜色应当是(Lr*Mr,Lg*Mg,Lb*Mb)=(0.0,0.0,0.8)(蓝色)。
3.2.构造三维模型数据
为了绘制三维实体,我们首先必须将预先生成的三维实体模型或从三维实体模型库中读出,并以一定的数据结构存储。
3.3.三维实体绘制
以下提供了绘制三角面片的方法:
glBegin(TRIANGLES);//定义三角形绘制开始
glVertexf((GLfloat)x1,(GLfloat)y1,(GLfloat)z1);//第一个顶点
glVertexf((GLfloat)x2,(GLfloat)y2,(GLfloat)z2);//第二个顶点
glVertexf((GLfloat)x3,(GLfloat)y3,(GLfloat)z3);//第三个顶点
glEnd();//绘制结束
为了提高三维实时动画的显示速度,我们利用了OpenGL库中的显示列表(DisplayList)的功能,将三维场景中的实体分别定义为单独的显示列表,预先生成三维实体。
在图形显示时,只需调用所需的显示列表即可显示相应的三维实体,而不需要重新计算实体在场景中的坐标,避免了大量的浮点运算。
在调用显示列表前所作的旋转、平移、光照、材质的设定都将影响显示列表中的三维实体的显示效果。
具体实现算法如下:
intlist=:
:
glGenLists
(1);//askforafreeidnumber
:
:
glNewList(list,GL_COMPILE_AND_EXECUTE);
:
:
glBegin(GL_TRIANGLES);
//stdglcallshere...fillvertices,normals,colors
:
:
glEnd();
:
:
glEndList();
Agoodcommandsequencetouseadisplaylistmaybe:
if(:
:
glIsList(list)==GL_TRUE)
:
:
glCallList(m_ListOpenGL);
例如:
for(ObjectNo=0;ObjectNo<实体个数;ObjectNo++)
{
glNewList(ObjectNo,GL_COMPILE);//创建第ObjectNo个实体的显示列表
for(FaceNo=0;FaceNo<第ObjectNo个实体的三角形面片数;FaceNo++)
{
glBegin(GL_TRIANGLES);
//开始画第FaceNo个三角形个三角形的第一个顶点的坐标
glVertex3fv((Glfloat*)Vertex[ObjectNo][(Face[ObjectNo][FaceNo][0])]);
定义第FaceNo个三角形的第二个顶点的法向量;
定义第FaceNo个三角形的第二个顶点的坐标;
定义第FaceNo个三角形的第三个顶点的法向量;
定义第FaceNo个三角形的第三个顶点的坐标;
glEnd();
}
glEndList();
}
………
//显示三维实体
{几何变换、投影变换、裁剪、视区变换、建立光照模型、定义实体材质等操作}
glCallList(ObjectNo);//显示第ObjectNo个三维实体
在这里,要提到的是在绘制三角形面片时,每个三角形顶点的定义前均有该定点法向量的定义,这是为了使得绘制出的三角形面片之间更加光滑、真实。
程序运行结束,须将已建立的显示列表删除,释放所占用的内存空间,可以通过以下函数调用实现删除显示列表:
//删除显示列表
DeleteList(ObjectNo);//删除第ObjectNo个实体的显示列表
4.删除渲染上下文RC
4.1.作图完毕以后,先通过置当前线程的RC为NULL(:
:
wglMakeCurrent(NULL,NULL);),断开当前线程和该渲染上下文的联系,由此断开与DC的联系;
4.2.先判断一下RC句柄的有效性(if(m_hrc):
:
wglDeleteContext(m_hrc);),再删除渲染上下文RC;
4.3.根据情况释放(Release)或者删除(Delete)设备上下文DC(对指针定义情况)。
四.OpenGL编程基本框架步骤
1.设置静态编译库
在菜单ProjectSettings...LinkObject/LibraryModules中加入
opengl32.libglu32.libglaux.lib(中间用空格隔开)
2.在视图类头文件MyView.h或StdAfx.h中包含以下头文件:
#include
#include
#include
3.在CMyView类中加入protected的成员变量:
HDCm_hDC;//设备上下文句柄(handleofDeviceContext)
HGLRCm_hRC;//渲染上下文句柄(handleofRenderingContext)
同时添加protected成员函数声明:
voidDrawScene();
4.在CMyView类中添加WM_CREATE消息,在OnCreate()函数中设置象素格式和渲染上下文RC:
intCMyView:
:
OnCreate(LPCREATESTRUCTlpCreateStruct)
{
if(CView:
:
OnCreate(lpCreateStruct)==-1)
return-1;
//TODO:
Addyourspecializedcreationcodehere
//定义像素存储格式:
初始化PIXELFORMATDESCRIPTOR结构
PIXELFORMATDESCRIPTORpfd=
{
sizeof(PIXELFORMATDESCRIPTOR),
1,
PFD_DRAW_TO_WINDOW|PFD_SUPPORT_OPENGL,
PFD_TYPE_RGBA,
24,
0,0,0,0,0,0,
0,0,0,0,0,0,0,
32,
0,0,
PFD_MAIN_PLANE,
0,
0,0,0,
};
//获取设备上下文句柄
m_hDC=GetDC()->GetSafeHdc();
intnPixelFormat;
//选择和设备最匹配的象素格式
if((nPixelFormat=:
:
ChoosePixelFormat(m_hDC,&pfd))==0)
{
MessageBox("在该DC上找不到与PFD接近的位图结构");
return-1;
}
//设置当前象素格式
if(:
:
SetPixelFormat(m_hDC,nPixelFormat,&pfd)==FALSE)
{
MessageBox("无法在该DC上设置位图结构");
return-1;
}
//创建一个新的OpenGL渲染上下文,使之适用于在m_hDC给出的设备上画图,
//并具有与之相同的象素格式
m_hRC=:
:
wglCreateContext(m_hDC);
//调用当前的OpenGL渲染上下文与设备上下文相联系,其后的所有OpenGL函数调
//用均输出到m_hDC提供的设备上
:
:
wglMakeCurrent(m_hDC,m_hRC);
//以下为初始化光源属性及材质属性
//定义第一个光源,这里设置的是缺省值,可以改动
GLfloatlight_ambient[]={0.0f,0.0f,0.0f,1.0f};
GLfloatlight_diffuse[]={1.0f,1.0f,1.0f,1.0f};
GLfloatlight_specular[]={1.0f,1.0f,1.0f,1.0f};
GLfloatlight_position[]={10.0f,0.0f,0.0f,1.0f};//无穷远光源
glLightfv(GL_LIGHT0,GL_AMBIENT,light_ambient);//setlightambient
glLightfv(GL_LIGHT0,GL_DIFFUSE,light_diffuse);//setlightdiffuse
glLightfv(GL_LIGHT0,GL_SPECULAR,light_specular);//setlightspecular
glLightfv(GL_LIGHT0,GL_POSITION,light_position);//setlightposition
//启动光照明
glEnable(GL_LIGHTING);
//启动第一个光源
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- OpenGL MFCBeginner