《OpenGL程序设计基础》由会员分享,可在线阅读,更多相关《OpenGL程序设计基础(85页珍藏版)》请在金锄头文库上搜索。
1、OpenGL程序设计基础1什么是OpenGL?OpenGL的前身是SGI公司为其图形工作站设计的一个图形开发软件库IRISGL(GraphicsLibrary),由于其性能优越,因此受到了用户的一致推崇。SGI公司有针对性地对GL进行了改进,特别是扩展了GL的可移植性,使之成为一个跨平台的开放式图形编程接口,这就是OpenGL。OpenGL是目前实际上的底层图形应用程序接口标准,由OpenGLARB(ArchitectureReviewBoard体系结构评审委员会)管理,目前的正式版本是2.1,本门课程讲解OpenGL1.1。OpenGL(OpenGraphicsLibrary)是一个针对图形
2、硬件的软件接口。从程序开发人员的角度来看,OpenGL是一组绘图命令的API集合。利用这些API能够方便地描述二维和三维几何物体,并控制这些物体按某种方式绘制到显示缓冲区中。对OpenGL实现者来说,OpenGL是影响图形硬件操作的一组命令。如果硬件只包含显存,OpenGL的指令完全依赖主机的CPU来实现。通常情况下,图形硬件有不同水平的图形加速能力,OpenGL实现者的任务是在CPU和图形硬件之间对图形命令进行合理的划分,以便充分利用图形硬件的处理能力。2参考书及课程邮箱1、参考书1)OpenGL Programming Guide SixthEdition: The Official Gu
3、ide to Learning OpenGL Version2.1 OpenGLArchitectureReviewBoard,Addison-Wesley,20072)OpenGL SUPERBIBLEFourthEdition:Comprehensive Tutorial and ReferenceAddison-Wesley,RichardS.Wright, Jr.BenjaminLipchakNicholasHaemel,20073)OpenGL:三维图形程序设计廖朵朵,张华军编著,星球地图出版社19964)各种网站(例如:)2、两个邮箱课程邮箱:密码:bhopengl2008作业信箱
4、:3考核方式及内容1、考核方式考勤及课堂表现20,期末考核大作业80%。2、大作业内容(运行工程final2009)在一个真实的起伏地形上方,几架飞机沿一个环形路线在等速飞行,使用者可以通过鼠标和键盘控制观察的角度和距离;并可以通过点击使飞机侧身。同时在屏幕的右下角显示作者的照片和姓名。3、提交内容及方式1)电子版:完整工程的压缩文件,文件名为学号加姓名,例如:36241600郝学生。提交至作业信箱:2)书面版:打印的设计说明及屏幕截图(5页以内),在第一页上手写姓名和学号,提交至新主楼A817办公室(老师不在就塞门缝)3)提交时间:6月1日前提交所有作业,过时不候。4)注意:个人独立完成作业
5、,不能多人提交一个工程,否则全无成绩!4主要内容一、通过辅助库(GLAUX)使用窗口系统二、几何图元与场景表达三、OpenGL中的变换四、OpenGL中的照明五、在OpenGL中使用纹理六、OpenGL中的曲线和曲面*七、选择与拾取八、显示列表与文字九、融合与反走样*5一、通过辅助库使用窗口系统OpenGL本身不涉及具体的窗口系统,但一个完整的图形程序又离不开窗口系统。OpenGL辅助库(GLAUX)专门为学习OpenGL而设计,通过使用辅助库可以摆脱对具体窗口系统的依赖,直接学习OpenGL的核心内容。1、本课程需要用到的aux函数查看工程:gl_1_1voidauxInitWindow(G
6、Lbyte*titleString);voidauxInitDisplayMode(GLbitfieldmask);voidauxInitPosition(GLintx,GLinty,GLsizeiwidth,GLsizeiheight);voidauxReshapeFunc(void(*function)(GLsizei,GLsizei);voidauxKeyFunction(GLintkey,void(*function)(void);voidauxMouseFunc(GLintbutton,Glintmode,void(*function)(AUX_EVENTREC*);voidauxM
7、ainLoop(void(*displayFunc)(void);62、与窗口系统进行交互查看工程:gl_1_2(1)本例将颜色位面设置为双缓冲双缓冲(1)本例借助鼠标进行视点变换视点变换,以便改变视点的角度。(2)另外本例还涉及投影变换投影变换,以便保持坐标轴的比例关系。查看工程:gl_1_3(1)本例首先改变了投影方式:由平行投影平行投影改变为透视投影透视投影。(2)借助键盘和鼠标进行视点变换视点变换,增加了改变视点位置的功能。(3)有了一个模型茶壶73、两个函数的说明(1)voidauxKeyFunc(GLintkey,void(*function)(void)定义键盘响应函数:参数fu
8、nction就是当按下key键时所调用的函数指针,辅助库为参数key定义了几个常量:AUX_0至AUX_9、AUX_A至AUX_Z、AUX_a至AUX_z、AUX_LEFT、AUX_RIGHT、AUX_UP、AUX_DOWN(方向键)、AUX_ESCAPE、AUX_SPACE或AUX_RETURN。(2)voidauxMouseFunc(GLintbutton,Glintmode,void(*function)(AUX_EVENTREC*)定义鼠标响应函数:参数function就是当鼠标以mode方式作用于button时所调用的函数。参数button有AUX_LEFTBUTTON、AUX_MI
9、DDLEBUTTON或AUX_RIGHTBUTTON;参数mode代表鼠标触击状态,击中时为AUX_MOUSEDOWN,释放时为AUX_MOUSEUP,移动时为AUX_MOUSELOC;参数function必须带一个参数,它是指向结构AUX_EVENNTREC的指针。当函数auxMouseFunc()被调用时,这个结构中具有相关的信息。例如鼠标的位置:voidfunction(AUX_EVENTREC*event)GLintx,y;x=event-dataAUX_MOUSEX;y=event-dataAUX_MOUSEY;.84、数据类型及函数命名规则后缀数据类型相应C语言类型OpenGL类型
10、bsifdubusui8-bitinteger16-bitinteger32-bitinteger32-bitfloating-point64-bitfloating-point8-bitunsignedinteger16-bitunsignedinteger32-bitunsignedintegersignedcharshortlongfloatdoubleunsignedcharunsignedshortunsignedlongGLbyteGLshortGLint,GLsizeiGLfloat,GLclampfGLdouble,GLclampdGLubyte,GLbooleanGLusho
11、rtGLuint,GLenum,GLbitfield(1)数据类型:OpenGL在自己的函数原型中,对数据类型使用自己的记法:94、数据类型及函数命名规则(续)(2)函数名称:OpenGL的同一函数对不同维数和各种数据类型通常都进行定义,例如:voidglVertex2d(GLdoublex,GLdoubley);voidglVertex2f(GLfloatx,GLfloaty);voidglVertex2i(GLintx,GLinty);voidglVertex2s(GLshortx,GLshorty);voidglVertex3d(GLdoublex,GLdoubley,GLdoublez
12、);voidglVertex3f(GLfloatx,GLfloaty,GLfloatz);voidglVertex3i(GLintx,GLinty,GLintz);voidglVertex3s(GLshortx,GLshorty,GLshortz);voidglVertex4d(GLdoublex,GLdoubley,GLdoublez,GLdoublew);voidglVertex4f(GLfloatx,GLfloaty,GLfloatz,GLfloatw);voidglVertex4i(GLintx,GLinty,GLintz,GLintw);voidglVertex4s(GLshortx
13、,GLshorty,GLshortz,GLshortw);104、数据类型及函数命名规则(续)还会用矢量表示:voidglVertex2dv(constGLdouble*v);voidglVertex2fv(constGLfloat*v);voidglVertex2iv(constGLint*v);voidglVertex2sv(constGLshort*v);voidglVertex3dv(constGLdouble*v);voidglVertex3fv(constGLfloat*v);voidglVertex3iv(constGLint*v);voidglVertex3sv(constGL
14、short*v);voidglVertex4dv(constGLdouble*v);voidglVertex4fv(constGLfloat*v);voidglVertex4iv(constGLint*v);voidglVertex4sv(constGLshort*v);注:矢量表示并非一定有,也未必一定全。11二、几何图元与场景表达glBegin(GL_POLYGON);glVertex2f(0.0,0.0);glVertex2f(0.0,3.0);glVertex2f(3.0,3.0);glVertex2f(4.0,1.5);glVertex2f(3.0,0.0);glEnd();glBe
15、gin(GL_POINTS);glVertex2f(0.0,0.0);glVertex2f(0.0,3.0);glVertex2f(3.0,3.0);glVertex2f(4.0,1.5);glVertex2f(3.0,0.0);glEnd();1、几何图元的表示122、几何图元的种类GL_POINTS单个顶点集GL_LINES多组双顶点线段GL_POLYGON单个简单填充凸多边形GL_TRAINGLES多组独立填充三角形GL_QUADS多组独立填充四边形GL_LINE_STRIP不闭合折线GL_LINE_LOOP闭合折线GL_TRAINGLE_STRIP线型连续填充三角形串GL_TRAING
16、LE_FAN扇形连续填充三角形串GL_QUAD_STRIP连续填充四边形串133、使用几何图元表达场景复杂物体由几何图元构成,程序中可以包含复杂物体的数据,也可以从其它软件获取建模数据。这时需要知道模型数据文件的格式,或其它工具程序。查看工程:gl_2_1此例场景中多了个直角四面体。为了获得正确的图象,使用了深度位面(Z-Buffer),并通过函数glEnable(GL_DEPTH_TEST)启动了消隐功能,而且每帧都对Z缓冲进行清理。14三、OpenGL中的变换应用程序几何光栅投影光照和着色屏幕映射OpenGL中的变换负责完成图形绘制管线的几何阶段。模型|视点裁减查看工程:gl_2_1151
17、、模型及视点变换1)平移变换:voidglTranslatefd(TYPEx,TYPEy,TYPEz);2)旋转变换:voidglRotatefd(TYPEangle,TYPEx,TYPEy,TYPEz);3)比例变换:voidglScalefd(TYPEx,TYPEy,TYPEz);以上变换既可用于模型变换也可用于视点变换,所有变换在视点坐标中进行。平移变换旋转变换比例变换162、投影变换1)平行投影:voidglOrtho(GLdoubleleft,GLdoubleright,GLdoublebottom,GLdoubletop,GLdoublenear,GLdoublefar);前4个参
18、数是坐标,后两个是距离。voidgluOrtho2D(GLdoubleleft,GLdoubleright,GLdoublebottom,GLdoubletop);它的near和far缺省值分别为-1.0和1.0,所有二维物体的Z坐标都为0.0。2)透视投影:voidglFrustum(GLdoubleleft,GLdoubleRight,GLdoublebottom,GLdoubletop,GLdoublenear,GLdoublefar);voidgluPerspective(GLdoublefovy,GLdoubleaspect,GLdoublezNear,GLdoublezFar);
19、这个函数更常用。173、屏幕映射glViewport(GLintx,GLinty,GLsizeiwidth,GLsizeiheight);由投影变换产生的规范立方体,通过屏幕映射被变换到视口(Viewport)中。此函数的参数x和y是视口在窗口坐标系中的左下角左下角点坐标,参数width和height分别是视口的宽度和高度。184、OpenGL对变换矩阵的管理(1)变换矩阵:所有OpenGL变换都可以用一个44的矩阵表示,需要一个16个单元的数组存储(注意矩阵的摆法,使用仍然是注意矩阵的摆法,使用仍然是M*X):|m0m4m8m12|M=|m1m5m9m13|m2m6m10m14|m3m7m1
20、1m15|(2)OpenGL中使用两个矩阵堆栈来存储变换矩阵,即投影变换堆栈和观察变换堆栈。投影变换堆栈的深度较浅,至少是2;而观察变换堆栈的深度较深,至少是32。矩阵操作的函数作用于那个堆栈通过下面的方法设置:glMAtrixMode(GL_PROJECTION):开始操作投影变换堆栈。glMatrixMode(GL_MODELVIEW):开始操作观察变换堆栈。投影变换堆栈用来保存投影矩阵;观察变换堆栈用来保存模型及视点变换;视口变换不需要矩阵。194、OpenGL对变换矩阵的管理(续)(3)矩阵操作函数1)voidglLoadIdentity(void);/栈顶装载单位矩阵2)voidgl
21、LoadMatrixfd(constTYPE*m);/栈顶装载矩阵m3)voidglMultMatrixfd(constTYPE*m);/栈顶矩阵被矩阵M乘(C*M)。4)voidglPushMatrix(void);/堆栈下推一次,并复制栈顶矩阵5)voidglPopMatrix(void);/堆栈上弹一次,原栈顶矩阵丢失6)得到栈顶变换矩阵:voidglGetDoublev(GL_PROJECTION_MATRIX,GLdouble*params);voidglGetFloatv(GL_PROJECTION_MATRIX,GLfloat*params);voidglGetDoublev(G
22、L_MODELVIEW_MATRIX,GLdouble*params);voidglGetFloatv(GL_MODELVIEW_MATRIX,GLfloat*params);4、每进行一次变换,都相当于栈顶矩阵被变换矩阵作乘法。205、视点变换函数gluLookAt()如前述:由平移和旋转可以方便地实现视点变换,但OpenGL提供的方便函数glLookAt使视点变换更加直观,与理论讲述更加一致。void gluLookAt( GLdouble eyex,GLdouble eyey,GLdouble eyez,GLdouble centerx,GLdouble centery,GLdouble
23、 centerz,GLdouble upx,GLdouble upy,GLdouble upz);eyex, eyey, eyez:视点在世界坐标系中的三维坐标。centerx, centery, centerz :焦点在世界坐标系中的三维坐标。upx, upy, upz:定义视点的上方向。21四、OpenGL中的照明OpenGL中使用简单光照模型实现顶点颜色的计算,然后通过着色模式的设置,决定内部象素的颜色确定方式。OpenGL中有两种颜色着色模式:单一着色(flat)和渐变着色(smooth)。通过函数glShadeModel(GLenummode);进行设置。其中:mode参数为GL_F
24、LAT或GL_SMOOTH,分别表示使用单一着色模式和渐变颜色模式。在使用渐变颜色模式时,OpenGL使用Gouraud模型对颜色进行插值:线段内部使用顶点颜色的线性插值,多边形内部使用顶点颜色的双线性插值。查看工程:gl_4_1为了增加显示效果,增加了一个圆锥体模型。221、设置光源(1)启动和关闭光照glEnable(GL_LIGHTING);/之后使用光照模型计算顶点颜色。glDisable(GL_LIGHTING);/之后顶点颜色为当前颜色,当前颜色可以通过glColor*函数指定。(2)启动和关闭特定光源glEnable(GL_LIGHTi);glDisable(GL_LIGHTi)
25、;其中:GL_LIGHTi为GL_LIGHT0、GL_LIGHT1、.、GL_LIGHT7、,光源的最大数目为GL_MAX_LIGHTS,但至少有8个可用光源。23(3)设置光源性质voidglLightifv(GLenumlight,GLenumpname,TYPEparam);参数light指定是第几个光源,即GL_LIGHT0、GL_LIGHT1、。参数pname指定是什么性质,其可能的值见下页表。参数param设置相应的光源特性值。函数名有v,这个参数是指针,可以设置所有特性值,函数名没有v,则只能设置只有一个特性值的特性。具体意义见下页表。注意:表中列出的GL_DIFFUSE和GL_
26、SPECULAR的缺省值只用于GL_LIGHT0,其他几个光源的GL_DIFFUSE和GL_SPECULAR缺省值为(0.0,0.0,0.0,1.0)1、设置光源(续)24pname参数名param缺省值说明GL_AMBIENT(0.0,0.0,0.0,1.0)RGBA模式下环境光强度GL_DIFFUSE(1.0,1.0,1.0,1.0)RGBA模式下漫反射光强度GL_SPECULAR(1.0,1.0,1.0,1.0)RGBA模式下镜面反射光强度GL_POSITION(0.0,0.0,1.0,0.0)光源位置齐次坐标(x,y,z,w),w=0时为方向光。这个坐标接受此时的观察变换GL_SPOT
27、_DIRECTION(0.0,0.0,-1.0)点光源聚光方向矢量(x,y,z),w0。这个方向接受此时的观察变换。GL_SPOT_EXPONENT0.0点光源聚光指数:光方向与点光源到顶点连线角余玄的次方数。这个余玄的幂作为方向衰减系数。0表示均匀光。GL_SPOT_CUTOFF180.0点光源聚光截止角:点光源的最大有效角,范围在0,90,特例180表示均匀光GL_CONSTANT_ATTENUATION1.0常数衰减因子。GL_LINER_ATTENUATION0.0线性衰减因子。GL_QUADRATIC_ATTENUATION 0.0平方衰减因子。光线到达顶点后的强度为发射光强乘总衰减
28、因子,总衰减因子是三部分和的倒数:常数衰减因子、线性衰减因子乘光源到顶点的距离、平方衰减因子乘光源到顶点距离的平方。252、设置材质性质(1)常规方法与设置光源的性质类似,但材料只有一种,就是当前材料:voidglMaterialifv(GLenumface,GLenumpname,TYPEparam);参数face可以是GL_FRONT、GL_BACK、GL_FRONT_AND_BACK,面的正面是根据顶点顺序由右手系定义的(而不是各个点的法线)。参数pname指定一个特定的性质。参数param是指定性质的具体数值。pname参数名param缺省值说明GL_AMBIENT(0.2,0.2,0
29、.2,1.0)材料的环境光反射系数GL_DIFFUSE(0.8,0.8,0.8,1.0)材料的漫反射光反射系数GL_SPECULAR(0.0,0.0,0.0,1.0)材料的镜面反射光反射系数,通常指定相同的三分量以反映光源的颜色。GL_EMISSION(0.0,0.0,0.0,1.0)材料的辐射光颜色GL_SHININESS0.0余玄指数(光洁程度)GL_AMBIENT_AND_DIFFUSE 参见GL_AMBIENT和GL_DIFFUSE同时设置GL_AMBIENT和GL_DIFFUSE,相当于调用两次函数。GL_COLOR_INDEXES(0,1,1)为颜色索引模式同时设置材料的环境光、漫
30、反射光和镜面反射光反射索引262、设置材质性质(续)(2)使用下列函数快速设置材质1)glEnable(GL_COLOR_MATERIAL);2)glDisable(GL_COLOR_MATERIAL);3)voidglColorMaterial(GLenumface,GLenummode);参数face指定面,可以是GL_FRONT、GL_BACK或GL_FRONT_AND_BACK(缺省值)。参数mode指定特定的材料性质材质(就是上页的pname),值有GL_AMBIENT、GL_DIFFUSE、GL_AMBIENT_AND_DIFFUSE(缺省值)、GL_SPECULAR、GLEMIS
31、SION。4)glColor*();273、设置光照模式光照模式用于确定涉及照明的其它参数,它使用下列函数:voidglLightModelifv(GLenumpname,TYPEparam);pname参数名param缺省值说明GL_LIGHT_MODEL_AMBIENT(0.2,0.2,0.2,1.0)全局环境光,它不属于任何光源。GL_LIGHT_MODEL_LOCAL_VIEWER 0GL_FALSE视点参数:0表示视点无穷远处,视线方向为视点坐标系的z轴方向;其它值(GL_TRUE)表示视点在视点坐标系的原点。GL_LIGHT_MODEL_TWO_SIDE0GL_FALSE0表示只对
32、表面正面照明;其它值(GL_TRUE)表示使用双向照明。284、设置顶点指定法线glBegin(GL_TRIANGLES);glNormal3d(-1.0,-1.0,1.0);glVertex3d(0.0,0.0,0.0);glNormal3d(1.0,-1.0,1.0);glVertex3d(1.0,0.0,0.0);glNormal3d(-1.0,1.0,1.0);glVertex3d(0.0,1.0,0.0);glEnd();295 5、补充:、补充:简单光照明模型简单光照明模型简单光照明模型模拟物体表面对光的反射作用。光源被假定为点光源,反射作用被细分为镜面反射(Specular Re
33、flection)和漫反射(Diffuse Reflection)。简单光照明模型只考虑物体对直接光照的反射作用,而物体间的光反射作用,只用环境光(Ambient Light)来表示。即:简单光照明模型漫反射镜面反射环境光301)理想漫反射模型()理想漫反射模型(Lambert余弦定理)余弦定理)漫反射由表面的粗糙不平引起,它均匀地向各方向传播,与视点无关。记入射光强为Ip,物体表面上点P 的法向为N ,从点P指向光源的向量为L,两者间的夹角为,则漫反射光强为:Id = Ip*Kd*cos() (0,/2)其中,Kd是与物体有关的漫反射系数, 0Kd1 。当L、N为单位向量时:Id = Ip*
34、Kd* (LN)在有多个光源的情况下,有如下表示:Id = KdIpi * (LiN) 在RGB颜色模型下,漫反射系数Kd有三个分量Kdr,Kdg,Kdb分别代表RGB三原色的漫反射系数,它们是反映物体的颜色的,通过调整它们,可以改变物体的颜色。同样,我们也可以把入射光强I设为三个分量Ir,Ig,Ib,通过这些分量的值来调整光源的颜色。 PNL5 5、补充:、补充:简单光照明模型(续)简单光照明模型(续)312)镜面反射()镜面反射(Phong模型)模型)对于理想镜面,反射光集中在一个方向,并遵守反射定律。对一般的光滑表面,反射光集中在一个范围内,且由反射定律决定的反射方向光强最大。因此,对于
35、同一点来说,从不同位置所观察到的镜面反射光强是不同的。镜面反射光强可表示为:Is = Ip*Ks*cosn(), (0,/2)其中Ks是与物体有关的镜面反射系数,为视线方向V与反射方向R的夹角,n为反射指数,反映了物体表面的光泽程度,一般为12000,数目越大物体表面越光滑。镜面反射光会在反射方向附近形成很亮的光斑,称为高光现象。同样,将V和R都格式化为单位向量,镜面反射光强可表示为:Is = Ip*Ks*(R V ) n对多个光源的情形,镜面反射光强可表示为:Is = KsIpi (Ri V)n镜面反射光产生的高光区域只反映光源的颜色,镜面反射系数Ks是一个与物体的颜色无关的参数(与光洁程度
36、有关)。综合前面已经提到的,在简单光照明模型中,我们只能通过改变物体的漫反射系数来控制物体的颜色。 5 5、补充:、补充:简单光照明模型(续)简单光照明模型(续)323)环境光)环境光环境光是指光源间接对物体的影响,是在物体和环境之间多次反射,最终达到平衡时的一种光。我们近似地认为同一环境下的环境光,其光强分布是均匀的,它在任何一个方向上的分布都相同。例如,透过厚厚云层的阳光就可以称为环境光。在简单光照明模型中,我们用一个常数来模拟环境光:Ie=IaKa其中:Ia为环境光的光强,Ka为物体对环境光的反射系数。5 5、补充:、补充:简单光照明模型(续)简单光照明模型(续)335 5、补充:、补充
37、:简单光照明模型(续)简单光照明模型(续)综合上面介绍的光反射作用的各个部分,简单光照明模型有这样的一个表述:由物体表面上一点P反射到视点的光强I为环境光的反射光强Ia、理想漫反射光强Id、和镜面反射光Is的总和,即: 34在用简单光照模型进行真实感图形计算时,对物体表面上的每个点P,均需计算光线的反射方向R,再由V计算(RV)。为减少计算量,我们可以作如下假设:a)光源在无穷远处。即光线方向L为常数;b)视点在无穷远处,即视线方向V为常数;c)用(HN)近似(RV)。这里H为L和V的平分向量,H=(L+V)/|L+V|。在这种简化下,由于对所有的点总共只计算一次H的值(N总是需要计算的),节
38、省了计算时间。5 5、补充:、补充:简单光照明模型(续)简单光照明模型(续)35五、在OpenGL中使用纹理物体的表面细节称为纹理(Texture)。OpenGL使用图像表示纹理,这也是最常用的表示纹理的方式。二维纹理用得最多,一维和三维纹理也各有其用途。361、纹理映射纹理被“贴到”物体上的过程被称为纹理映射。(1)纹理空间:纹理有自己的单位坐标空间,称为纹理空间。二维纹理的纹理空间是:0s1,0t1。为了表示物体的表面细节,需要进行物体坐标(x,y,z)到纹理坐标(s,t)的映射,在确定点(x,y,z)的色彩时,取纹理坐标(s,t)对应的纹理色彩(或参与)。(2)映射变换:决定映射的变换通
39、常用两种方式指定:在绘制图元时,为每个顶点指定纹理坐标。例如三角形:(x0,y0,z0)(s0,t0)(x1,y1,z1)(s1,t1)(x2,y2,z2)(s2,t2)指定映射关系:s=a0x+a1y+a2z+a3t=b0x+b1y+b2z+b337(3)Mip-Map纹理反走样技术:纹理映射将纹理图案映射到不同大小的景物表面上。当图形象素与对应区域的纹理象素的数目相匹配时,它们之间形成近似一对一的映射;当图形象素的数目大于对应区域的纹理象素时,纹理象素会被放大插值。这时的显示都没有问题。但当图形象素的数目远远小于对应区域的纹理象素时,每个屏幕象素将控制一大片纹理区域并与其中心对应。这时,图
40、形位置的微小变化,将引起象素中心对应的纹理坐标发生较大的变化,表现为运动图形的闪烁。这是一种走样现象。这时,应当取象素对应纹理区域上的平均值作为一个象素对应的纹理属性。Mip是拉丁文“multum in parvo”的缩写,意为“聚集在一块小区域内的许多东西”。 Mip-Map技术保留了一幅图像的多个分辨率版本,最高版本是原始图像,低级版本的分辨率是其上级版本的1/2,其每个象素是上级图像四个象素的平均值,所有版本构成了一个图像金字塔。在进行纹理映射时,将选择一个最合适的分辨率版本。高低1、纹理映射(续)382、操作步骤(1)图像准备:既可以在程序中生成图像,也可以读取图像文件。(2)生成纹理
41、号:适用于多个纹理反复切换时,一个纹理时不需要。(3)设置当前纹理:切换纹理,又称为纹理绑定。(4)定义纹理:指定当前纹理的图象。(5)设置纹理参数:指定纹理的重叠方式和插值方式。(6)设置纹理环境参数:决定怎样使用纹理颜色,例如是否与光照色合成。(7)启动纹理功能,绘制场景,给出顶点的纹理坐标和几何坐标注:纹理坐标可以直接指定,也可以启用自动计算纹理坐标功能。(8)退出前删除纹理。查看工程:gl_5_1393、用到的函数(1)生成纹理号的函数:voidglGenTextures(GLsizein,GLuint*textures);同时生成多个纹理号。(2)设置当前纹理:voidglBindT
42、exture(GLenumtarget,GLuinttexture);target:GL_TEXTURE_1D、GL_TEXTURE_2Dtexture:纹理号(3)删除纹理voidglDeleteTextures(GLsizein,GLuint*textures);注:删除的纹理必须存在403、用到的函数(续)(4)定义纹理voidglTexImage2D(GLenumtarget,GLintlevel,GLintcomponents,GLsizeiwidth,glsizeiheight,GLintborder,GLenumformat,GLenumtype,constGLvoid*pixe
43、ls);voidglTexImage1D(GLenumtarget,GLintlevel,GLintcomponents,GLsizeiwidth,GLintborder,GLenumformat,GLenumtype,constGLvoid*pixels);说明:1)target:纹理维数。例如:GL_TEXTURE_1D、GL_TEXTURE_2D2)level:纹理层次,0层表示最底层纹理(使用原始图像)3)components:14的整数,说明象素有几个分量4)width、height:象素宽度和高度,一定是二次幂两个边界宽度5)border:边界宽度,0或14)format:说明各个
44、分量的意义,例如:GL_RGB5)type:每个分量的类型,例如:GL_UNSIGNED_BYTE6)pixels:数据地址413、用到的函数(续)或定义MipMap纹理intgluBuild1DMipmaps(GLenumtarget,GLintcomponents,GLintwidth,GLenumformat,GLenumtype,constvoid*data);intgluBuild2DMipmaps(GLenumtarget,GLintcomponents,GLintwidth,GLintheight,GLenumformat,GLenumtype,constvoid*data);说
45、明:1)target:纹理维数。例如:GL_TEXTURE_1D、GL_TEXTURE_2D3)components:14的整数,说明象素有几个分量4)width、height:象素宽度和高度,不必是二次幂4)format:说明各个分量的意义,例如:GL_RGB5)type:每个分量的类型,例如:GL_UNSIGNED_BYTE6)data:数据地址423、用到的函数(续)(5)设置纹理参数voidglTexParameterifv(GLenumtarget,GLenumpname,TYPEparam);target:纹理维数。例如:GL_TEXTURE_1D、GL_TEXTURE_2Dpna
46、me:参数名param:参数值GL_TEXTURE_WRAP_SGL_CLAMP(截断,大于1的等于1)GL_REPEAT(重复,即舍整数部分)GL_TEXTURE_WRAP_TGL_CLAMPGL_REPEATGL_TEXTURE_MAG_FILTERGL_NEARESTGL_LINEARGL_TEXTURE_MIN_FILTERGL_NEARESTGL_LINEARGL_NEAREST_MIPMAP_NEARESTGL_NEAREST_MIPMAP_LINEARGL_LINEAR_MIPMAP_NEARESTGL_LINEAR_MIPMAP_LINEAR433、用到的函数(续)pname:
47、参数名param:参数值GL_TEXTURE_ENV_MODEGL_DECALGL_MODULATEGL_BLENDGL_TEXTURE_ENV_COLORGL_BLEND时需要四个环境色分量,它们在这里设置。这时param是有四个元素的数组,也就是后面表中的Cc。(6)设置纹理环境参数voidglTexEnvifv(GLenumtarget,GLenumpname,TYPEparam);target:GL_TEXTURE_ENVpname:GL_TEXTURE_ENV_MODE、GL_TEXTURE_ENV_COLORC和L表示多色和单色;A表示不透明度;下标f、t、c、v分别表示图元、纹理
48、、环境和结果。443、用到的函数(续)(7)启动纹理功能,绘制场景,给出顶点的纹理坐标和几何坐标其中纹理坐标可以直接指定,也可以启用自动计算坐标功能。1)glEnable(GL_TEXTURE_2D);/同时只能启动一种纹理2)glEnable(GL_TEXTURE_1D);3)voidgTexCoord1234sifdv(TYPEcoords);4)voidglTexGenifv(GLenumcoord,GLenumpname,TYPEparam);coord:GL_S、GL_T、GL_R、GL_Qpname:参数名param:参数值GL_TEXTURE_GEN_MODEGL_OBJECT_
49、LINEAR(坐标为物体坐标)、GL_EYE_LINEAR(坐标为视点坐标,经ModelView变换)、GL_SPHERE_MAP(可产生物体对纹理的反射效果)GL_OBJECT_PLANE数组:对象坐标系的四个系数,对应GL_OBJECT_LINEARGL_EYE_PLANE数组:眼坐标系的四个系数,GL_EYE_LINEAR注:param是GL_SPHERE_MAP时,不需要设置系数,而是使用视点坐标系中的特定公式见MSDN。5)glEnable(GL_TEXTURE_GEN_S)、glEnable(GL_TEXTURE_GEN_T)、454、图象读取的方便函数(1)AUX_RGBImag
50、eRec*auxRGBImageLoad(LPCSTR);(2)AUX_RGBImageRec*auxDIBImageLoad(LPCSTR);typedefstruct_AUX_RGBImageRecGLintsizeX,sizeY;unsignedchar*data;AUX_RGBImageRec;46OpenGL实用库(GLU)支持对NURBS曲线、曲面的绘制。然而在底层(核心库GL)OpenGL只支持对Bezier曲线和曲面的绘制,这种实现方式基于以下结论:(1)AnypolynomialformcanbeconvertedtoBezierformbypropergenerationo
51、fcontrolpoints.(基函数)(2)NURBS曲线曲面是其控制点在齐次坐标空间中的B样条曲线曲面在原空间的投影。OpenGL对Bezier曲线和曲面的支持是通过所谓的求值程序(evaluator)机制实现的。查看工程:gl_6_1六OpenGL中的曲线和曲面471、OpenGL中的求值程序(Evaluator)求值程序生成基于Bernstein基的Bzier曲线和曲面。实际上它是OpenGL中唯一的生成曲线和曲面的核心图形函数,且具有良好的硬件支持。如果要使用求值程序绘制基于其它基函数的曲线和曲面,必须知道如何将所使用的基转换为Bzier基。要使用求值程序,首先需要定义曲线并使其生效
52、,然后用glEvalCoord1()或glEvalCoord2()命令生成曲线、曲面上的点坐标。这两个函数代替glVertex*()命令,可以用来绘制表现曲线、曲面的点、线段、面片。其它的几个函数可以用来进一步自动生成一系列的点,这些点的参数是等间距的,可以产生参数规则的网格。48(1)一维求值程序(一维Bzier)1)定义一维求值程序:void glMap1fd( GLenum target,TYPE u1,TYPE u2,GLint stride,GLint order,const TYPE *points);target:既是求值程序要产生的数据内容,也是points中存放的数据内容。其
53、可能的值为:Target取值意义GL_MAP1_VERTEX_3x,y,z顶点坐标GL_MAP1_VERTEX_4x,y,z,w顶点坐标GL_MAP1_INDEX颜色索引GL_MAP1_COLOR_4R,G,B,AGL_MAP1_NORMAL法向量GL_MAP1_TEXTURE_COORD_1s纹理坐标GL_MAP1_TEXTURE_COORD_2s,t纹理坐标GL_MAP1_TEXTURE_COORD_3s,t,r纹理坐标GL_MAP1_TEXTURE_COORD_4s,t,r,q纹理坐标49(1)一维求值程序(续)u1,u2:参数范围(类型为double或float)stride:poin
54、ts中前后两个数据点的间隔(double或float),即可不连续排放。order:控制点的数目,也就是阶数,即多项式次数1。points:控制点数据(类型为double或float)2)启动一维求值程序,即:使其生效glEnable(GL_MAP1_*);/例如:glEnable(GL_MAP1_VERTEX_3);3)计算一维函数值void glEvalCoord1fd V( TYPE u);u:参数值或参数指针说明:说明:在同一时刻,可以使用多个求值程序进行计算。例如:如果两个求值程序GL_MAP1_VERTEX_3、GL_MAP1_COLOR_4被定义(不同的target)并生效,则调
55、用glEvalCoord1()将同时生成坐标和颜色。同一时刻、同一类型的求值程序只能启动一个。同一类型指顶点类型、法线类型、颜色类型和纹理类型。如果在同一时刻启动了同一类型的一个以上的求值程序,则维数最高的一个被启用。求值程序不影响当前颜色、法线和纹理坐标。50(1)一维求值程序(续)4)等间距参数的一维求值程序虽然可以用函数glEvalCoord1()求取一个坐标点(或其它),但最常用的是一次产生一系列的等间距参数的坐标点(或其它),这需要以下3个步骤(多一个):定义求值程序,并启动:(同前)void glMap1fd(*); glEnable(GL_MAP1_*);定义等间距网格:void
56、 glMapGrid1fd(GLintun,TYPEu1, TYPEu2);un:网格数目(顶点从0un)u1, u2:网格区间,它可不同于参数(1)中的参数区间绘制网格void glEvalMesh1( GLenum mode,GLint i1,GLint i2);mode:绘制模式,可取值:GL_POINT 、GL_LINEi1,i2:网格子区间(整数),这个区间可以小于(2)中的网格数目注:注:这个函数已经包含了glBegin和glEnd。这个函数对所有启用的一维求值程序都有效。查看工程:gl_6_251(2)二维求值程序(二维Bzier)除了需要两个参数外,二维的情况和一维是非常相似的
57、。使用二维求值程序的过程就像一维函数一样,有下面3个步骤:1)定义二维求值程序(以double型为例):void glMap2d( GLenum target,GLdouble u1,GLdouble u2,GLint ustride,GLint uorder,GLdouble v1,GLdouble v2,GLint vstride,GLint vorder,const GLdouble *points);这里需要注意的是两个跨距:ustride同一维的情况一样,但vstride则是行间跨距(即ustride*每行数据点数)。2)启动二维求值程序,即:使其生效glEnable(GL_MAP2
58、_*);3)计算二维函数值void glEvalCoord2d(GLdouble u,GLdouble v);查看工程:gl_6_352(2)二维求值程序(二维Bzier)4)等间距参数的二维求值程序同一维的情况一样,有下面3个步骤(其实是4个,(1)中包含两个):定义求值程序,并启动:(同前)void glMap2fd(*); glEnable(GL_MAP2_*);定义等间距网格:void glMapGrid2fd(GLint un,TYPE u1,TYPE u2,GLint vn,TYPE v1,TYPE v2);un,vn:u向和v向网格数目u1, u2 ,v1 ,v2:网格区间,可以
59、不同于参数(1)中的参数区间绘制网格void glEvalMesh2( GLenum mode, GLint i1, GLint i2, GLint j1, GLint j2 );mode:绘制模式,可取值:GL_POINT 、GL_LINE、GL_FILL i1, i2, j1, j2:u向和v向网格子区间,这个区间可以小于(2)中的网格数目5)glEnable(GL_AUTO_NORMAL)在用二维求值程序计算顶点坐标时(GL_MAP2_VERTEX_3或GL_MAP2_VERTEX_4),可以自动计算顶点的法线。查看工程:gl_6_4和gl_6_5532、OpenGL中的NURBSOpe
60、nGL中的NURBS是建立在求值程序(Bzier)基础之上的,位于GLU库中。1、绘制、绘制NURBS曲线或(非剪切)曲线或(非剪切)NURBS曲面的步骤曲面的步骤(1)如果需要光照,调用glEnable(GL_AUTO_NORMAL),以便自动计算曲面的法线(也可以其它方式计算见(6)。(2)调用gluNewNurbsRenderer()产生一个NURBS对象。(3)如果需要,调用gluNurbsProperty()设置NURBS参数。(4)调用gluNurbsCallback()设置NURBS错误处理函数(虽然这会稍微降低效率,但强烈建议这么做)。(5)启动NURBS绘制:gluBegin
61、Curve()或gluBeginSurface()。(6)绘制NURBS曲线和曲面:调用gluNurbsCurve()或或gluNurbsSurface()至少一次。给定控制点(有理的或非有理的)、节点序列和多项式的阶数(次数1)。或许还需要调用这两个函数来指定曲面的表面法线和/或纹理坐标。(7)结束NURBS绘制:gluEndCurve()或或gluEndSurface()。(8)调用gluDeleteNurbsRenderer()删除一个NURBS对象。查看工程:gl_6_6542、相关函数说明、相关函数说明(1)GLUnurbsObj* gluNewNurbsRenderer( void
62、);void gluDeleteNurbsRenderer( GLUnurbsObj *nobj);(2) void gluNurbsProperty(*):见后(3)void gluNurbsCallback( GLUnurbsObj *nobj,GLenum which,void (* fn)();nobj:gluNewNurbsRenderer()返回的对象指针。which:只能是GLU_ERROR。fn:函数指针,这个函数没有参数也没有返回值。(4)void gluBeginCurve( GLUnurbsObj *nobj);void gluEndCurve(GLUnurbsObj *
63、nobj);void gluBeginSurface( GLUnurbsObj *nobj);void gluEndSurface(GLUnurbsObj *nobj);552、相关函数说明(续)、相关函数说明(续)(5)void gluNurbsCurve( GLUnurbsObj *nobj,GLint nknots,GLfloat *knot,GLint stride,GLfloat *ctlarray,GLint order,GLenum type);void gluNurbsSurface( GLUnurbsObj *nobj, GLint sknot_count, GLfloat
64、*sknot, GLint tknot_count, GLfloat *tknot, GLint s_stride, GLint t_stride, GLfloat *ctlarray, GLint sorder, GLint torder, GLenum type );nobj:gluNewNurbsRenderer()返回的对象指针。sknot_count:s参数的节点数目sknot:s参数的节点数组tknot_count:t参数的节点数目tknot :t参数的节点数组s_stride :s方向的数据跨度(相邻数据跨度)t_stride :t方向的数据跨度(行间数据跨度)ctlarray
65、:控制点数组Sorder:s方向多项式阶数torder :t方向多项式阶数type :曲面的类型,例如:GL_MAP2_VERTEX_3、GL_MAP2_COLOR_3等。563、特殊说明、特殊说明(1)在函数gluBeginSurface(*)和gluEndSurface(*)之间,至少需要一个用类型GL_MAP2_VERTEX_*说明的曲面函数gluNurbsSurface(*),但还可能有计算法线和纹理坐标的函数,形如:gluBeginSurface(nobj);gluNurbsSurface(nobj, ., GL_MAP2_TEXTURE_COORD_2);gluNurbsSurfa
66、ce(nobj, ., GL_MAP2_NORMAL);gluNurbsSurface(nobj, ., GL_MAP2_VERTEX_3);gluEndSurface(nobj);函数gluBeginCurve(*)和gluEndCurve(*)也类似。(2)可以为NURBS的每个控制点指定权因子wi,这个权是通过齐次坐标的第4维指定的,这一维既是齐次坐标的权,又是控制点的权。因此如果需要为(x,y,z)点指定权w,则点坐标应写为(wx,wy,wz,w)。查看工程:gl_6_7573、特殊说明(续)、特殊说明(续)属性属性(property)缺省值缺省值值值(value )的意义的意义 GL
67、U_SAMPLING_METHODGLU_PATH_LENGTHGLU_PATH_LENGTH:参见GLU_SAMPLING_TOLERANCEGLU_PARAMETRIC_ERROR:参见GLU_PARAMETRIC_TOLERANCEGLU_DOMAIN_DISTANCE:参见GLU_U_STEP和GLU_V_STEPGLU_SAMPLING_TOLERANCE50.0f当采样方法(GLU_SAMPLING_METHOD)为GLU_PATH_LENGTH时,这个参数用来设置用来近似曲面的小多边形边长的最大值,单位:象素。GLU_PARAMETRIC_TOLERANCE0.5f当采样方法为G
68、LU_PARAMETRIC_ERROR时,这个参数用来设置多边形网格近似真实曲面时的最大误差,单位:象素。GLU_U_STEP100当采样方法为GLU_DOMAIN_DISTANCE时,这个参数用来设置U方向单位参数区间的采样个数。GLU_V_STEP100当采样方法为GLU_DOMAIN_DISTANCE时,这个参数用来设置V方向单位参数区间的采样个数。GLU_DISPLAY_MODEGLU_FILL GLU_FILL:显示填充多边形GLU_OUTLINE_POLYGON:显示用于近似的多边形网格线(密)GLU_OUTLINE_PATCH:只显示每个曲面片的边界线(疏)GLU_CULLING
69、GL_FALSE GL_TRUE:如果所有控制点在视景体之外,则不做近似,可加速。GL_FALSE:始终做近似,因为NURBS还是可能与视景体相交。GLU_AUTO_LOAD_MATRIXGL_TRUEGL_TRUE:需要的矩阵(投影、观察和视口)由系统提供。GL_FALSE:矩阵由用户调用gluLoadSamplingMatrices()提供。(3)void gluNurbsProperty( GLUnurbsObj *nobj, GLenum property, GLfloat value );581、选择概述OpenGL支持对图形的交互操作,允许用户使用选择机制从屏幕上选取一个区域内的对
70、象,或者拾取一个屏幕上的对象。由于屏幕上的对象可能经历了反复的变换,所以从三维场景中直接选取对象是比较困难的。OpenGL提供了一种选择(selection)机制,可以准确告知你哪些对象落在你指定的区域内。你可以使用这种机制和一个专用的函数来实现拾取(picking):通过鼠标选择屏幕上的一个对象。所以拾取是选择的一个特殊情况。通常,在使用OpenGL的选择机制时,你会首先将场景画入帧缓冲,然后进入选择模式重新绘制场景。然而,一旦你进入选择模式,在返回绘图模式前,帧缓冲的内容就不会再改变了,而是返回与视景体相交的对象列表。查看工程:gl_7_1七、选择与拾取592、“选择”的基本步骤使用函数g
71、lSelectBuffer()指定一个用来存储”命中”(hit)记录的数组使用函数glRenderMode(GL_SELECT)进入选择模式使用函数glInitNames()和glPushName()初始化名称栈定义用于选择的视景体。通常情况下它不同于用于绘制场景的视景体。所以你可能需要使用glPushMatrix()和glPopMatrix()保存或者恢复当前的变换状态。绘制所有对象。在绘制每个对象前,先设置好名称栈,然后绘制这个对象。如果这个对象与当前视景体相交,则这个对象被“命中”(hit),这次命中将导致名称栈内的所有名称(及其它一些信息)被写入中指定的记录数组。退出选择状态,处理返回
72、的选择数据即“命中记录”601)voidglSelectBuffer(GLsizeisize,GLuint*buffer);在进入选择模式之前调用它,用来指定存放“命中”记录的数组。size的单位是无符号长整数。2)GLintglRenderMode(GLenummode);mode:GL_RENDER、GL_SELECT、GL_FEEDBACK调用此函数,就退出了以前的模式。如果之前的状态是GL_SELECT或GL_FEEDBACK,这个函数的返回值是有意义的:它表示退出以前状态时,命中记录的数目或者是反馈数组中值的数目。3)voidglInitNames(void);这个函数初始化(清空)
73、名称栈,4)voidglPushName(GLuintname);将名称name压入名称栈。5)voidglPopName(void);将栈顶名称弹出(放弃)。6)voidglLoadName(GLuintname);将栈顶名称替换为name。3、相关函数614、“选择数据”的产生过程及内容1)何时产生命中记录在选择模式中,对象与视景体的相交引起“命中”,当执行一次名称栈管理命令或者调用函数glRenderMode()时,如果自上次发出名称堆栈管理命令或调用函数glRenderMode()后,又有命中发生,则添加一个命中记录。2)命中记录的内容。每个命中记录中包含4个部分,依次是:产生“命中记
74、录”时,名称堆栈中名字的数目。自上个记录以后,所有与视景体相交图元(是组成对象的图元,而不是对象)的顶点坐标的最小和最大窗口z坐标值(0232-1,与深度位面的数目无关)。命中发生时,名称栈中的内容,首先是栈低内容。3)返回值的意义当你进入选择模式时,OpenGL将一个指针指向选择数组的开始。每次一个命中记录被写进这个数组,这个指针也作相应更新。假如某此写入使数组中的数据超过了函数glSelectBuffer()指定的数目,则OpenGL写入尽可能多的数据填满这个数组,然后设置一个溢出标志。当你调用函数glRenderMode()退出选择模式的时候,这个函数返回写入的命中记录的数目(包括不完全
75、记录)、清理名称堆栈、重设溢出标志、复位堆栈指针。假如溢出标志已经被设置,这个函数返回1。625、拾取(picking)1)拾取与选择的关系:借助工具函数gluPickMatrix(),通过使用选择机制,可以在窗口中完成拾取功能。void gluPickMatrix( GLdouble x, GLdouble y, GLdouble width, GLdouble height, GLint viewport4 );x, y:拾取区域中心的窗口坐标width, height:拾取区域的宽度和高度(窗口坐标)Viewport:当前视口,可以由函数glGetIntegerv(GL_VIEWPORT
76、,GLint*viewport)获取这个函数的功能是将原来投影变换后的单位立方体中的一小部分(由这个函数的参数可以确定)放大为单位立方体。使用这个新立方体(对应新的视景体)的“选择”就是“拾取”。所以绘图时的投影变换要首先对图形进行变换。2)拾取过程1)获取鼠标当前位置,(如果需要)将其转换为OpenGL窗口坐标。2)在GL_PROJECTION矩阵模式下用gluPickMatrix()定义拾取范围。3)在选择模式下按照当前屏幕图形的绘制方式重新绘制场景。4)处理选择结果。查看工程:gl_7_2、gl_7_363八、显示列表与文字1、显示列表1)OpenGL的两种绘图方式:瞬时方式(immed
77、iatemode):最常用方式,通过调用OpenGL绘图函数绘制图形。构造显示列表(DisplayList):然后用显示列表绘图。可以提高显示速度,特别是网络传输速度。显示列表是一组预先存储起来的、留待以后调用的OpenGL命令组成。调用这张显示列表,就会依次执行列表中的所有命令。构造显示列表时会对其内容进行极大的优化:变量已由其值代替、函数已经经过计算、逆矩阵已得到、图像格式已转换成硬件格式、材质、纹理等都经过处理,所以其执行效率很高。通常是完成一些独立的显示功能,在不同场合反复调用,用来提高速度。查看工程:gl_8_1642)显示列表的使用过程为显示列表获取名称:名称就是一个无符号整数GL
78、uint glGenLists( GLsizei range);该函数按指定的数目产生连续的列表名称,并返回首个名称。注:也可以随意使用一个列表名称,而不调用上面的函数。但为了防止冲突,使用前应使用下列函数进行检查:GLboolean glIsList( GLuint list);生成显示列表:void glNewList( GLuint list,GLenum mode);void glEndList(void);使用上述函数对可以为指定名称产生显示列表,显示列表的内容放在两函数之间。注:并不是所有的OpenGL函数都可以放入显示列表中,生成显示列表时要符合逻辑,一般都可以。执行显示列表:v
79、oid glCallList( GLuint list); 652、文字1)文字有两种形式:矢量字和光栅字。对矢量字的显示就是显示表示字体的轮廓多边形,它和矢量图形一样。光栅字在OpenGL中用位图表示和显示位图:是一类特殊的图像,即单一位面图像。它只用一位表示一个象素,常用作掩码。662)光栅字的显示过程查看工程:gl_8_2准备文字位图,并说明其在内存中的数据格式。void glPixelStoreif( GLenum pname,TYPE param);指定文字颜色,背景为透明voidglColor*(); 指定显示位置void glRasterPos234sifdv(TYPE x, T
80、YPE y, TYPE z, TYPE w);显示文字位图void glBitmap( GLsizei width, GLsizei height, GLfloat xbo, GLfloat ybo, GLfloat xbi, GLfloat ybi, const GLubyte *bitmap);注意:在注意:在glRasterPos之前指定的颜色才是文字的颜色。之前指定的颜色才是文字的颜色。673)需要说明的函数void glPixelStoreif( GLenum pname,TYPE param);该函数用来控制内存中图像(包括位图)的存储方式。有以下概念:Packing和unpack
81、ing:象素数据写入和读出客户内存的方式。Formats和type:每个象素由14个部分构成(例如RGB有3个部分组成),每个部分称为一个元素。formats参数用来指定元素的数目和顺序,type参数用来指定元素的类型(例如float型)。glPixelStore:图像通常在内存中保存为一个23维的数组,通常需要显示的只是其中的一个部分。不同系统字节的存储顺序不同;不同的硬件对不同的数据边界(单字节、双字节、4字节等)其存取效率有很大的区别。你可以通知函数glPixelStore对字节的存储方式进行控制,可以设置的参数见下表。683)需要说明的函数(续)可设置参数可设置参数(pname)类型类
82、型缺省值缺省值范围范围意义意义GL_UNPACK_S,GL_PACK_SGLbooleanFALSETRUEFALSE前后字节是否颠倒,只对多字节元素有效。GL_UNPACK_LSB_FIRST,GL_PACK_LSB_FIRSTGLbooleanFALSETRUEFALSE字节内的位是否颠倒,用于位图。GL_UNPACK_ROW_LENGTH,GL_PACK_ROW_LENGTHGLint0非负整数图像宽度,0表示宽度由使用此格式的OpenGL函数指定,例如glBitmapGL_UNPACK_SKIP_ROWS,GL_PACK_SKIP_ROWSGLint0非负整数子图垂直跳过的行数,见上图
83、。GL_UNPACK_SKIP_PIXELS,GL_PACK_SKIP_PIXELSGLint0非负整数子图水平跳过的象素,见上图。GL_UNPACK_ALIGNMENT,GL_PACK_ALIGNMENTGLint41,2,4,8每行图像的字节数是此参数的整数倍。用于提高存取效率。693)需要说明的函数(续)void glBitmap( GLsizei width, GLsizei height, GLfloat xbo, GLfloat ybo, GLfloat xbi, GLfloat ybi, const GLubyte *bitmap);width,height文字图像的宽度和高度,
84、不要求是8的倍数。xbo,ybo文字原点,相对当前光栅位置xbi,ybi显示文字后,光栅位置的增量703、在Windows环境下的写字方法矢量字:下面的函数用来生成单位尺寸(矢量字:下面的函数用来生成单位尺寸(XY方向)的字形方向)的字形BOOL wglUseFontOutlinesA( /要求设备中有TrueType字体HDC hdc, /设备句柄DWORD first, /字体库中的起始字符编码DWORD count, /生成的显示列表数目(字符数目)DWORD listBase, /起始显示列表,即显示列表偏移FLOAT deviation, /与理论轮廓的最大玄差,越小越接近设计轮廓F
85、LOAT extrusion, /在-z方向的拉伸int format, /是生成多边形还是生成线框LPGLYPHMETRICSFLOAT lpgmf /生成字形的几何参数);typedef struct _GLYPHMETRICSFLOAT / gmfFLOAT gmfBlackBoxX; /黑包围盒(字形的最小包围盒)的宽度FLOAT gmfBlackBoxY; /黑包围盒的高度POINTFLOAT gmfptGlyphOrigin; /黑包围盒的左上角位置FLOAT gmfCellIncX; /当前字符到下一字符的X方向距离FLOAT gmfCellIncY; /当前字符到下一字符的Y方
86、向距离 GLYPHMETRICSFLOAT;713、在Windows环境下的写字方法(续)矢量字举例:矢量字举例:gl_8_3HFONThFont=CreateFont(12,0,0,0,FW_NORMAL,FALSE,FALSE,0,ANSI_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,DEFAULT_PITCH|FF_SWISS,Arial);HFONThOldFont=SelectObject(hDC,hFont);longb=wglUseFontOutlinesA(hDC,0,255,1000,0.0f,
87、0.1f,WGL_FONT_POLYGONS,agmf);glListBase(1000);glColor3f(1.0,1.0,0.0);glCallLists(3,GL_UNSIGNED_BYTE,ABC);glCallLists需要需要glListBase为其指定起点即偏移为其指定起点即偏移voidglListBase(GLuint base):初值为0voidglCallLists(GLsizei n,GLenum type,constGLvoid*lists)n:执行的显示类表数目lists:显示内容的地址,地址中存放的是在显示列表中的偏移type:Lists指向的数据类型723、在W
88、indows环境下的写字方法(续)光栅字:光栅字:BOOLwglUseFontBitmapsA(HDChdc,/设备句柄DWORDfirst,/字体库中的起始字符编码DWORDcount,/生成的显示列表数目(字符数目)DWORDlistBase,/起始显示列表);光栅字举例:光栅字举例: gl_8_3wglUseFontBitmapsA(hDC,0,255,2000);glListBase(2000);glColor3f(1.0,1.0,0.0);glCallLists(3,GL_UNSIGNED_BYTE,ABC);注意:光栅字可以不需要矢量字体,但需要通过字体控制大小。注意:光栅字可以不
89、需要矢量字体,但需要通过字体控制大小。733、在Windows环境下的写字方法(续)中文字中文字汉字太多,不易同时生成众多显示列表,应该临时生成。下面的函数用于显示中文,它们与前面的英文函数参数完全一致,用于生成UNICODE字符:BOOLwglUseFontOutlinesW();BOOLwglUseFontBitmapsW();事实上有#ifdefUNICODE#definewglUseFontOutlineswglUseFontOutlinesW#else#definewglUseFontOutlineswglUseFontOutlinesA#endif/!UNICODE另外函数Mult
90、iByteToWideChar可以将字符串映射为双字节字符串:intMultiByteToWideChar(UINTCodePage,DWORDdwFlags,LPCSTRlpMultiByteStr,intcbMultiByte,LPWSTRlpWideCharStr,intcchWideChar );743、在Windows环境下的写字方法(续)中文字举例:中文字举例: gl_8_3wchar_tdwChar256;intnLength=MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,中国,-1,dwChar,256);glColor3d(1.0,0.0
91、,0.0);/glRasterPos3d(0.0,0.0,0.0);/生成光栅字时for(i=0;inLength-1;i+)intListNum=glGenLists(1);wglUseFontOutlinesW(hDC,dwChari,1,ListNum,0.0f,0.02f,WGL_FONT_POLYGONS,agmf);/wglUseFontBitmapsW(hDC,dwChari,1,ListNum);/生成光栅字时glCallList(ListNum);glDeleteLists(ListNum,1);获取设备句柄获取设备句柄HWND hWnd=auxGetHWND();HDC h
92、DC=GetDC(hWnd);754、关于字体字体字体(font):字体是拥有相同字形:字体是拥有相同字形(typeface)、风格、风格(style)和尺寸和尺寸( size)的一组字母的一组字母和符号的集合。和符号的集合。字形(typeface):指字体中字符的特有性质,例如:粗细笔画的宽度和是否有衬线(serif)。衬线是非连接笔画终点的短横线,没有衬线的字体通常被称为无衬线(sans-serif)字体。风格(style):指字体的深浅(weight)和倾斜(slant)程度。字的深浅从低到高,其可能值为Thin、Extralight、Light、Normal、Medium、Semibo
93、ld、Bold、Extrabold、Heavy;倾斜的可能取值为roman、oblique、和italic。Roman字体是垂直向上的;oblique字体是由roman字体经过错切变换得来的;italic在设计时就是倾斜的,是真正的倾斜字体。尺寸(size):是一个不准确的值,通常可以由一个小写的g的底线到一个相邻的大写字母M的顶线确定。尺寸的单位是点(points),1点大约是1/72英寸。字体族字体族(font families):字体族是具有相同笔画宽度和衬线的字体集合。它有:字体族是具有相同笔画宽度和衬线的字体集合。它有6个可个可能的取值:能的取值:FF_DECORATIVE、FF_D
94、ONTCARE、FF_MODERN、FF_ROMAN、FF_SCRIPT和和FF_SWISS。其中。其中FF_DONTCARE表示缺省字体族,所以实际上只有表示缺省字体族,所以实际上只有5个字体族。个字体族。761、融合:融合(Blending)是透明、数字合成和绘画技术的核心。固名思义,融合就是指两种颜色依据一定的比例混合在一起。而这种比例就来源于Alpha值,即RGBA中的A或(r,g,b,a)中的a值,通常称a为不透明度,称(1-a)为透明度。查看工程:gl_9_1、gl_9_2说明:tree工程需要将png目录中的.h和lib文件拷贝到VC的gl和lib目录中才能编译。九、融合与反走样
95、771)源(source)因子和目标(destination)因子源是指即将写入象素的颜色(RGBA),目标是指已经在象素中的颜色(RGBA)。融合就是将源和目标进行合成。这个过程分为两步:设置融合因子:指定源和目标所占比例的计算方法。两个比例分别称为源因子和目标因子,都是一个四元组,即(R,G,B,A)的比例。设源因子和目的因子分别为(Sr,Sg,Sb,Sa)和(Dr,Dg,Db,Da),而源和目标分别为(Rs,Gs,Bs,As)和(Rd,Gd,Bd,Ad)则融合的最终结果是:(R,G,B,A)=(Rs*Sr+Rd*Dr,Gs*Sg+Gd*Dg,Bs*Sb+Bd*Db,As*Sa+Ad*Da
96、)这两个因子是通过函数glBlendFunc()进行设置的:void glBlendFunc(GLenum sfactor,GLenum dfactor)sfactor:如何计算源因子。意义见后面表格。dfactor:如何计算目标因子。意义见后面表格。启动混合功能glEnable(GL_BLEND) 注:融合不影响注:融合不影响zbuffer的变化,完全透明的图元照样更新的变化,完全透明的图元照样更新zbuffer,所以在,所以在显示透明物体时应当禁止显示透明物体时应当禁止zbuffer的更新(通过函数的更新(通过函数glDepthMask设置)。设置)。782)融合因子的意义void glB
97、lendFunc(GLenum sfactor,GLenum dfactor);因子因子(factor)枚举值枚举值可用于因子可用于因子因子数值因子数值GL_ZEROsfactor和dfactor(0,0,0,0)GL_ONEsfactor和dfactor(1,1,1,1)GL_DST_COLORsfactor(Rd,Gd,Bd,Ad)GL_SRC_COLORdfactor(Rs,Gs,Bs,As)GL_ONE_MINUS_DST_COLORsfactor(1,1,1,1)-(Rd,Gd,Bd,Ad)GL_ONE_MINUS_SRC_COLORdfactor(1,1,1,1)-(Rs,Gs,B
98、s,As)GL_SRC_ALPHAsfactor和dfactor(As,As,As,As)GL_ONE_MINUS_SRC_ALPHAsfactor和dfactor(1,1,1,1)-(As,As,As,As)GL_DST_ALPHAsfactor和dfactor(Ad,Ad,Ad,Ad)GL_ONE_MINUS_DST_ALPHAsfactor和dfactor(1,1,1,1)-(Ad,Ad,Ad,Ad)GL_SRC_ALPHA_SATURATEsfactor(f,f,f,1);f=min(As,1-Ad)792、反走样(Antialiasing)1)走样与反走样常常可以观察到OpenGL中
99、画的线(特别是近似水平和垂直的线)呈现出锯齿状。这是因为一条理想线段需要用一系列的象素来近似,而象素又必须落在规则的象素网格上。这种锯齿状是一种走样(aliasing),而用于减少走样的技术被称为反走样(antialiasing)。并非只有线段会发生走样,多边形的边缘、纹理、细小物体都会发生走样,总之,走样是指图象与真实景物之间的误差。下图中显示的是两条交叉线段,左面的图形出现了走样,右面的图形经过了反走样处理。为了突出效果,图形经过了放大。802、反走样原理下图中显示了一条一个象素宽度的斜线段,在与这条线段相交的象素中,有些象素被覆盖的面积小,有些象素被覆盖的面积大。事实上,在进行反走样处理
100、时,OpenGL为每个相交的象素计算了一个覆盖率(如下图所示),然后用这个覆盖率去乘要写入象素的颜色中的alpha值,然后用这个结果alpha值作为源的alpha与目标去做融合。其结果就产生了反走样线段。813)效率与精度的平衡象素覆盖率的计算细节是多种多样的,很难一概而论,而且与OpenGL的具体实现有关。可以使用函数glHint()在效率与质量之间作出权衡,但是并非所有的OpenGL函数实现都会考虑这个设置。void glHint(GLenum target,GLenum hint);target:预控制的OpenGL行为。hint:控制值。GL_FASTEST(最快)、GL_NICEST
101、(质量最高)、GL_DONT_CARE(不介意,由系统自定)。控制行为(target)意义GL_POINT_SMOOTH_HINTGL_LINE_SMOOTH_HINTGL_POLYGON_SMOOTH_HINT点、线、面进行反走样处理时,指定需要的采样质量GL_FOG_HINT指定雾(fog)化计算方式:逐个象素(GL_NICEST)、按顶点计算(GL_FASTEST)。GL_PERSPECTIVE_CORRECTION_HINT指定颜色和纹理插值方式:屏幕空间线型插值(GL_FASTEST)、透视校准方式(GL_NICEST)。纹理插值常常需要使用透视校准。824)点和线的反走样查看工程:
102、gl_9_3glEnable(GL_POINT_SMOOTH)或glEnable(GL_LINE_SMOOTH)或许,使用glHint()指定点、线反走样质量设置融合因子,启动融合功能使用适当的alpha值绘制点、线。835)多边形的反走样查看工程:gl_9_4多边形的走样出现在其边缘。在这种情况下,对象的相交对多边形反走样的影响要比点和线的情况大的多,绘图的顺序和融合的准确性就变得十分关键。实际上,假如正在对多于一个多边形进行反走样,就需要将这些多边形从前向后进行排序,并且用glBlendFunc(GL_SRC_ALPHA_SATURATE,GL_ONE)为源和目标设置融合因子。845)多边
103、形的反走样(续)一般说来,多边形反走样的步骤是:一般说来,多边形反走样的步骤是: (1)glEnable(GL_POLYGON_SMOOTH)(2)或许,使用glHint()指定多边形反走样质量(3)禁止zbuffer更新;设置融合因子;启动融合功能:glDepthMask(GL_FALSE);/作融合时一般如此glBlendFunc(GL_SRC_ALPHA_SATURATE,GL_ONE);这种设置的效果是:最终颜色是目标与一定比例的源之和。源的比例是源的alpha值与目标alpha的补(1-alpha)中的最小值。这意味着:一个alpha值很大的象素,其后的绘制对其影响不大,因为(1-alpha)几乎是零。而在多边形边缘的象素(其覆盖比例小,因此其alpha值也小)就可能受到后面绘制的融合影响。最后,这种设置需要使多边形的绘制顺序保持从前向后。glEnable(GL_BLEND);(4)从前向后对多边形进行排序并按此顺序显示。85