OpenGL基础篇


OpenGL 基础篇 本人水平有限,如有问题请以文章形式提出,大家可以讨论吗... [OPENGL 怎么用] OPENGL 编程类似 C 编程,实际接口就是 C,所以熟悉 C 是必要的 一般编程可用到的函数库包括: OPENGL 实用库:函数以 glu 开头 OPENGL 辅助库:函数以 aux 开头 Windows 专用函数库:函数以 wgl 开头 Win32API:无专用前缀 OPENGL 中有 115 个核心函数,可以在任何 OPENGL 平台上使用 OPENGL 实用库比上面这 115 个函数高一级,提供高级调用 OPENGL 辅助库本来是提供初学者入门的函数,不保证在任何平台的使用 但恰好可以在 WIN32 下使用,所以本讲座将大量引用 WIN32 下 OPENGL 编程有两个方便途径: 1 使用辅助库 2 使用 C++基于消息驱动的编程 显然 1 要简单一些,入门从这里开始吧。 [用之前的准备] 1 首先你需要下列*.lib 包含在你的工程中: opengl32.lib glu32.lib glaux.lib 本讲座所有例子“将”在 VC5 下调试通过,所以从 project->setting->link->general->object/libary modules 中加入上面三个*.lib (这些 LIB,VC4 以上版本已经自带,加入即可,不用在四处搜寻文件) 2 另外在你的运行程序路径下或\win95\system\下你需要一些*.dll 动态连接库 opengl32.dll glu32.dll rxddi.dll mga.drv 如果谁需要上述文件,跟我打个招呼 别跟我说要 Visual C++ 5.0 呦 [编程入门] 这里我将给出一个小例子让大家熟悉用辅助库的编程结构: // GLOS.H ////////////////////////////////////////////////////////// // This is an OS specific header file //判别操作系统的基本头文件 #include // disable data conversion warnings #pragma warning(disable : 4244) // MIPS #pragma warning(disable : 4136) // X86 #pragma warning(disable : 4051) // ALPHA ////////////////////////////////////////////////////////// //opengl.cpp //主程序 #include "glos.h" #include #include #include "windows.h" void main(void) { /*初始化:*/ auxInitDisplayMode(AUX_SINGLE|AUX_RGBA); //窗口显示单缓存和 RGB(彩色)模式 auxInitPosition(0,0,500,500); //大小 x=500 y=500 (0,0)是屏幕左上点 auxInitWindow("sample1"); //窗口初始化,参数是标题 glClearColor(0.0,0.0,0.0,0.0); //将窗口清为黑色 glClear(GL_COLOR_BUFFER_BIT); //将颜色缓存清为 glClearColor 命令所设置的颜色 //即背景色 /*绘图*/ glColor3f(1.0,0.0,0.0); //选颜色(R,G,B),参数 0 #include #include "windows.h" void myinit(void); void CALLBACK display(void); void CALLBACK reshape(GLsizei w,GLsizei h); //这里不用管 reshape(),只是一个用于窗口改变大小时的处理 //与绘图无关,后面会讲到 //这里的重点是 display(),请注意绘图的方法 void myinit(void) { /*初始化:*/ auxInitDisplayMode(AUX_SINGLE|AUX_RGBA); //窗口显示单缓存和 RGB(彩色)模式 auxInitPosition(0,0,500,500); //大小 x=500 y=500 (0,0)是屏幕左上点 auxInitWindow("sample1"); //窗口初始化,参数是标题 glClearColor(0.0,0.0,0.0,0.0); //将窗口清为黑色 glClear(GL_COLOR_BUFFER_BIT); //将颜色缓存清为 glClearColor 命令所设置的颜色 //即背景色 } void CALLBACK reshape(GLsizei w,GLsizei h) { glViewport(0,0,w,h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); if(w<=h) glOrtho(-20.0,20.0,-20.0*(GLfloat)h/(GLfloat)w, 20.0*(GLfloat)h/(GLfloat)w,-50.0,50.0); else glOrtho(-20.0*(GLfloat)h/(GLfloat)w, 20.0*(GLfloat)h/(GLfloat)w,-20.0,20.0,-50.0,50.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void CALLBACK display(void) { glBegin(GL_TRIANGLE_STRIP);//画连续填充的多个三角 glColor3f(1.0,0.0,0.0); glVertex3f(15.0,0.0,0.0); glColor3f(0.0,1.0,0.0); glVertex3f(-15.0,0.0,0.0); glColor3f(0.0,0.0,1.0); glVertex3f(0.0,15.0,15.0); //第一个三角 glColor3f(0.0,1.0,1.0); glVertex3f(10.0,15.0,-15.0); //第二个三角 glColor3f(1.0,1.0,0.0); glVertex3f(15.0,0.0,0.0); //第三个三角 glEnd(); glFlush(); } void main(void) { myinit(); auxReshapeFunc(reshape); auxMainLoop(display); } //end of sample 所谓连续填充就是 1、2、3 点组成三角 1 2、3、4 点组成三角 2... 这里一共画乐 3 个向连接的彩色三角,组成乐一个由三个彩色平面围成的空心三角 椎...enjoy it 上回书说道有个 reshape 需要进一步讲解这个函数功能是对用户改变窗口大小的操 作进行一些重绘的动作(类似 VC 中的 OnResize)。其中用到了一些变换的概念我 希望大家已经具备初步的计算机图形学的知识,这将有利于这部分的理解。如果还 没有,也没关系,我尽量讲解的通俗一些对于 3D 绘图,把其中 3D 坐标写成齐次坐 标系后,是 4*4 的矩阵形式(详细………………………………………………………… 可以参阅相关文献,后面也会讲到。)任何投影、旋转...操作都可以看成是矩阵相 乘的操作。矩阵操作----这个概念一定要形成!! 例如在 2D 中普通的旋转变换,可以写成: |cosθ sinθ 0 | [x' y' 1]=[x y 1] |-sinθ cosθ 0 | |0 0 1 | 3D 中道理完全一样,一个 3D 图形的显示包括下面步骤: 1.视点变换(将相机放在合适的地方对准 3D 景物) 2.模型变换(将 3D 物体放在合适的地方) 3.投影变换(将相机镜头调整,使 3D 景物投影在 2D 胶片上) 4.视口变换(决定胶片大小) 其中 1、2 并没有本质区别。 里面有几个关键性函数: 一 几何变换 1. void glTranslated( GLdouble x, GLdouble y, GLdouble z ); void glTranslatef( GLfloat x, GLfloat y, GLfloat z ); 目标沿 X Y Z 轴平移 x y z 2. void glRotated( GLdouble angle, GLdouble x, GLdouble y, GLdouble z ); void glRotatef( GLfloat angle, GLfloat x, GLfloat y, GLfloat z ); 目标分别以 X Y Z 轴为轴逆时针旋转 x y z 3. void glScaled( GLdouble x, GLdouble y, GLdouble z ); void glScalef( GLfloat x, GLfloat y, GLfloat z ); 目标在 X Y Z 方向缩放,缩放因子为 x y z 二 投影变换 1.正射投影(即没有“近大远小”) void glOrtho(GLdouble left,GLdouble right,GLdouble bottom,GLdouble top, GLdouble near,GLdouble far) 创建一个平行视景体(的矩阵),即投射线是平行线,把第一个矩形 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 视景投影到第二个矩形视景上。并用这个矩阵乘以当前矩阵,以完成变换。 近景第一个矩形的左上角三维空间坐标(left,bottom,-near),右下角的三维空间坐标 (right,top,-near); 远景第二个矩形左上角三维空间坐标(left,bottom,-far),右下角的三维空间坐标 (right,top,-far); 2.透射投影(“近大远小”) void glFrustum( GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble znear, GLdouble zfar ); 创建一个型如棱台的视景体,其近截取面由 left right bottom top znear 确定,远截取面由从视点投影近截取面到 Z 轴 zfar 位置决定 三 视口变换 void glViewport(GLint x,GLint y,GLsize width,GLsize height); 这个函数定义一个视口,x 和 y 是视口在屏幕窗口坐标系中左上坐标 缺省是(0,0)。width height 是宽和高。 注意使用中视口长宽比例的调整会导致图象变形。因此 reshape()中 要检测窗口尺寸,修正视口大小,保证图象不变形。 附: void glMatrixMode(GLenum mode ); 把当前矩阵转换为指定的矩阵类型 这三个函数的利用见例子中的中文说明: //////////////////////////////////////////////////////////// //sample.cpp #include "glos.h" #include #include #include "windows.h" void myinit(void); void CALLBACK display(void); void CALLBACK reshape(GLsizei w,GLsizei h); ///////////////////////////////////////////////////////////// void myinit(void) { /*初始化:*/ auxInitDisplayMode(AUX_SINGLE|AUX_RGBA); //窗口显示单缓存和 RGB(彩色)模式 auxInitPosition(0,0,500,500); //大小 x=500 y=500 (0,0)是屏幕左上点 auxInitWindow("sample1"); //窗口初始化,参数是标题 glClearColor(0.0,0.0,0.0,0.0); //将窗口清为黑色 glClear(GL_COLOR_BUFFER_BIT); //将颜色缓存清为 glClearColor 命令所设置的颜色 //即背景色 } ////////////////////////////////////////////////// void CALLBACK reshape(GLsizei w,GLsizei h) { //设定视口 X Y 方向不要大于 500 单位 if(w<=500&&h<=500) glViewport(0,0,w,h); if(w>500&&h<=500) glViewport(0,0,500,h); if(w<=500&&h>500) glViewport(0,0,w,500); if(w>500&&h>500) glViewport(0,0,500,500); //进入世界坐标系,准备变换 //必要的步骤,初始化变换矩阵步骤: 1确定类型 2清成单位阵 glMatrixMode(GL_PROJECTION); glLoadIdentity(); //定义一个合适的视景体 if(w<=h) glOrtho(-20.0,20.0,-20.0*(GLfloat)h/(GLfloat)w, 20.0*(GLfloat)h/(GLfloat)w,-50.0,50.0); else glOrtho(-20.0*(GLfloat)h/(GLfloat)w, 20.0*(GLfloat)h/(GLfloat)w,-20.0,20.0,-50.0,50.0); //把变换结果返回视点坐标系 glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } ////////////////////////////////////// //画三棱锥,功能同上一讲 void draw(void) { glBegin(GL_TRIANGLE_STRIP); glColor3f(1.0,0.0,0.0); glVertex3f(15.0,0.0,0.0); glColor3f(0.0,1.0,0.0); glVertex3f(-15.0,0.0,0.0); glColor3f(0.0,0.0,1.0); glVertex3f(0.0,15.0,15.0); // glColor3f(0.0,1.0,1.0); glVertex3f(10.0,15.0,-15.0); // glColor3f(1.0,1.0,0.0); glVertex3f(15.0,0.0,0.0); // glEnd(); } //////////////////////////////////////// void CALLBACK display(void) { //按上个例子中方法画第一个三棱锥 glClearColor(0.0,0.0,0.0,1.0); glClear(GL_COLOR_BUFFER_BIT); glLoadIdentity(); glTranslatef(0.0,0.0,-25.0); draw(); //开始画第二个三棱锥 //变换开始,清变换矩阵为单位阵 glLoadIdentity(); //先沿 X Y Z 平移-5 -10 -4,屏幕上看就是向左下移动 //Z 方向由于是平行投影,没有“近大远小”所以看不出效果 glTranslatef(-5.0,-10.0,-4.0); //再沿 Z 轴转 90 度 glRotatef(90,0.0,0.0,1.0); draw(); //绘图工作完成,强制绘图结束 glFlush(); } void main(void) { myinit(); auxReshapeFunc(reshape); auxMainLoop(display); } //sample ends here ///////////////////// 如果大家运行一下就知道上述函数的作用,无非是把物体移动、转动、变形缩放、 透视... 我想有了前 3 讲,OPENGL 的基本原理已经如此了它就是提供了一个标准的计算机 图形学所使用的数学模型到显示的接口,只要具备图形学知识,掌握 OPENGL API 函数使用,绘图很 EASY 啦基础部分已经完毕,慢慢再来谈高级一些的绘图功能如: 纹理、光源、动画... 真诚希望大家提提意见和高论 :) 前面三篇文章已经把 OPENGL 的编程基本结构描述完毕。以后会在这个基础上逐渐 深化,不断增添新内容。这一篇是讲述键盘操作和动画基础(实际还差的远哪)。 只是个简单的能由用户控制的动画,让物体前后移动,左右旋转。是我们自己的第 一个 QUAKE!当然这个版本谁买谁上当,呵呵。 这篇的另一个目的就是加深前面对于 CALLBACK 函数的认识以及对于变换的直观解 释,任何变换你都可以从屏幕上通过自己的操作看出来: 我只把和以前变化的部分标记中文解释 //////////////////////////////////////////////////// //sample.cpp #include "glos.h" #include #include #include "windows.h" void myinit(void); void CALLBACK display(void); void CALLBACK reshape(GLsizei w,GLsizei h); //注意到乐吗?这里新加乐 4 个 CALLBACK 函数 //类似 display() reshape(),也由主函数调用它们 //实现对键盘输入的响应 void CALLBACK left(void);//按 LEFT void CALLBACK right(void);//按 RIGHT void CALLBACK up(void);//按 UP void CALLBACK down(void);//按 DOWN //两个全局变量,z_motion 用来控制物体远近 //rotate 用来控制物体旋转 static int z_motion=0,rotate=0; void myinit(void) { auxInitDisplayMode(AUX_SINGLE|AUX_RGBA); auxInitPosition(0,0,500,500); auxInitWindow("sample1"); glClearColor(0.0,0.0,0.0,0.0); glClear(GL_COLOR_BUFFER_BIT); } void CALLBACK reshape(GLsizei w,GLsizei h) { if(w<=500&&h<=500) glViewport(0,0,w,h); if(w>500&&h<=500) glViewport(0,0,500,h); if(w<=500&&h>500) glViewport(0,0,w,500); if(w>500&&h>500) glViewport(0,0,500,500); glMatrixMode(GL_PROJECTION); glLoadIdentity(); ///*if(w<=h) // glOrtho(-20.0,20.0,-20.0*(GLfloat)h/(GLfloat)w, // 20.0*(GLfloat)h/(GLfloat)w,-50.0,50.0); //else // glOrtho(-20.0*(GLfloat)h/(GLfloat)w, // 20.0*(GLfloat)h/(GLfloat)w,-20.0,20.0,-50.0,50.0); //*/ /************************************************************/ //这里我们换一种投影方法:透射投影,有立体感的说 //取代上次的平行投影。前 4 个参数是控制第一个截取面的 //left right top bottom,然后两个参数控制近截取面的 //Z 坐标,远截取面的 Z 作标,函数声名如下: //void glFrustum(GLdouble left,GLdouble right, // GLdouble bottom,GLdouble top, // GLdouble near,GLdouble far); /************************************************************/ glFrustum(-20.0,20.0,-20.0,20.0,10.0,50.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void draw(void) { glBegin(GL_TRIANGLE_STRIP); glColor3f(1.0,0.0,0.0); glVertex3f(15.0,0.0,0.0); glColor3f(0.0,1.0,0.0); glVertex3f(-15.0,0.0,0.0); glColor3f(0.0,0.0,1.0); glVertex3f(0.0,15.0,15.0); // glColor3f(0.0,1.0,1.0); glVertex3f(10.0,15.0,-15.0); // glColor3f(1.0,1.0,0.0); glVertex3f(15.0,0.0,0.0); // glEnd(); } void CALLBACK display(void) { glClearColor(0.0,0.0,0.0,1.0); glClear(GL_COLOR_BUFFER_BIT); //glPushMatrix(); glLoadIdentity(); //根据 z_motion rotate 两个参数确定变换的具体数值: //z_motion 改变时,物体从原 Z 坐标-25,平移 z_motion 个单位 glTranslatef(0.0,0.0,-25.0+z_motion); //rotate 改变时,物体延 Y 轴(上下方向)旋转 5*rotate 度 glRotatef(rotate*5.0,0.0,1.0,0.0); draw(); //glPopMatrix(); glFlush(); } void CALLBACK left(void) { //每当按下 LEFT,rotate 数值 +1 //以下函数都类似 rotate++; } void CALLBACK right(void) { rotate--; } void CALLBACK up(void) { z_motion++; } void CALLBACK down(void) { z_motion--; } void main(void) { myinit(); //用辅助库的函数调用把 left() right() up() down() //设为标准键盘输入处理函数 auxKeyFunc(AUX_LEFT,left); auxKeyFunc(AUX_RIGHT,right); auxKeyFunc(AUX_UP,up); auxKeyFunc(AUX_DOWN,down); auxReshapeFunc(reshape); auxMainLoop(display); } //end of sample //////////////////////////////////////////////////// 如果运行这个程序会发现有较明显的闪烁感,这是单缓存模式造成的以后会讲到动 画应采用双缓存模式,这样可以避免闪烁 这回可能是 OPENGL 最简单的内容:颜色。 一 RGB 模式 一般来讲实现彩色是用 RGB 三基色来调配的。这就是 RGB 模式,我们前面一直用这种方法 (例如: glColor3f(1.0,0.0,0.0); glVertex3f(0.0,0.0,0/0); 绘制一个红色点。) void glColor3{b s i f d ub us ui}(TYPE r,TYPE g, TYPE b); void glColor4{b s i f d ub us ui}(TYPE r,TYPE g, TYPE b,TYPE a); void glColor3{b s i f d ub us ui}v(TYPE *v); void glColor4{b s i f d ub us ui}v(TYPE *v); {}内是任选一种数值精度(看前面的介绍); 参数 a 是表征透明度的 Alpha 值。 后两个带 v 后缀的函数表明他们的参数是向量(详细使用看本篇的例子)。 以 glColor3f 为例,其参数取值范围-1.0--1.0,其它数值类型的函数将 自动把参数均匀影射到这个区间,例如: 后缀 类型 MIN MIN 映射 MAX MAX 映射 b 1byte 整数 -128 -1.0 127 1.0 二 颜色索引模式 使用 void glIndex{s i f d}(TYPE c); void glIndex{s i f d}(TYPE *c); 来从颜色索引表中选取颜色。设置当前颜色索引值(调色板号),大于 总数时取模。 前面所有例子都是 RGB 模式,所以这里给出一个颜色索引的例子: //sample.cpp ////////////////////////////// #include "glos.h" #include #include #include "windows.h" void myinit(void); void InitPalette(void); void CALLBACK display(void); void CALLBACK reshape(GLsizei w,GLsizei h); void myinit(void) { auxInitDisplayMode(AUX_SINGLE|AUX_INDEX); auxInitPosition(0,0,500,500); auxInitWindow("sample1"); glClearColor(0.0,0.0,0.0,0.0); glClear(GL_COLOR_BUFFER_BIT); glShadeModel(GL_FLAT);//GL_FLAT 填色模式 } void CALLBACK reshape(GLsizei w,GLsizei h) { if(w<=500&&h<=500) glViewport(0,0,w,h); if(w>500&&h<=500) glViewport(0,0,500,h); if(w<=500&&h>500) glViewport(0,0,w,500); if(w>500&&h>500) glViewport(0,0,500,500); glMatrixMode(GL_PROJECTION); glLoadIdentity(); if(w<=h) glOrtho(-20.0,20.0,-20.0*(GLfloat)h/(GLfloat)w, 20.0*(GLfloat)h/(GLfloat)w,-50.0,50.0); else glOrtho(-20.0*(GLfloat)h/(GLfloat)w, 20.0*(GLfloat)h/(GLfloat)w,-20.0,20.0,-50.0,50.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void draw(void) { GLint n; //首先给定三角扇的坐标信息 GLfloat pp[8][2]={{7.0,-7.0},{0.0,-10.0},{-7.0,-7.0},{-10.0,0.0}, {-7.0,7.0},{0.0,10.0},{7.0,7.0},{10.0,0.0}}; //先画前两个点,然后用循环读取前面的向量(数组)信息绘制 //完整图形 glBegin(GL_TRIANGLE_FAN); glVertex2f(0.0,0.0); glVertex2f(10.0,0.0); for(n=0;n<8;n++) { //每次从颜色查找表中找出新颜色,然后以这个颜色绘制三角扇 //注意 glVertex2fv()中 v 后缀的使用,以前没有碰到过,v 后缀代表 //参数是个向量(数组) glIndexi(n+1); glVertex2fv(pp[n]); } glEnd(); } void InitPalette(void) { //这是本例子的关键,初始化颜色查找表,这里一共定义 //乐 8 种颜色,是从蓝到青的渐进。 GLint i; static GLfloat rgb[][3]={{0.0,0.0,0.2},{0.0,0.0,0.4},{0.0,0.0,0.6}, {0.0,0.0,1.0},{0.0,0.2,1.0},{0.0,0.4,1.0},{0.0,0.8,1.0},{0.0,1.0,1.0}}; //调用简单的辅助库函数设置系统调色板,把刚才 8 个颜色定为全部 //颜色索引的内容 for(i=0;i<8;i++) auxSetOneColor(i+1,rgb[i][0],rgb[i][1],rgb[i][2]); } void CALLBACK display(void) { //首先调用自定义的 InitPalette()初始化调色板 InitPalette(); glClearColor(0.0,0.0,0.0,1.0); glClear(GL_COLOR_BUFFER_BIT); glLoadIdentity(); draw(); glFlush(); } void main(void) { myinit(); auxReshapeFunc(reshape); auxMainLoop(display); } //end of sample 下次将介绍 OPENGL 的光照和材质效果,一个步入 3D 的阶梯, 大家快来呀 ...... 这部分是最重要的部分,前面只是基础。这里会介绍光照处理、明暗处理、光源 设置、材质定义以及相关计算机图形学的概念。 一般来说产生 3D 图象的步骤: 1 建模 2 将几何模型经变换投影到 2D 透视图 3 确定场景所有可见面,进行消隐 4 计算场景颜色 我们已经再前面介绍乐 1 2 两步 消隐是 OPENGL 的工作,我们不必关心 所以 4 就是这里的重点。 (一)光照 分为:反射、透射光 1 简单光照模型 简单光照模型只考虑物体表面反射光的视觉影响。假定物体表面光滑不透明而且由 理想材料构成,环境假设为白光照明。 一般反射光分为:环境反射、漫反射和镜面反射 3 个分量。 环境反射光(Ambient Light):入射光均匀从周围环境入射至表面并个方向等量反 射。 漫反射光(Diffuse Light):特定光源在物体表面的反射光中那些向各个方向均匀 反射的光。 镜面反射光(Specular Light):朝一定方向的反射光,例如光源在金属球上产生的 高光(Highlight)。 详细可参阅大学物理。呵呵 介绍一下重要的函数: (1) void glLight{if}[v](GLenum light,GLenum pname,TYPE param) 设置光源特性。 light 是名字例如:GL_LIGHT0,GL_LIGHT1...GL_LIGHT7。 pname 缺省值 说明 GL_AMBIENT 0,0,0,1 RGBA 模式的环境光 GL_DIFFUSE 1,1,1,1 RGBA 模式的漫反射光 GL_SPECULAR 1,1,1,1 RGBA 模式的镜面光 GL_POSTION 1,0,1,0 光源位置齐次坐标 GL_SPOT_DIRECTION 0,0,-1 点光源聚光方向矢量(x,y,z,w) GL_SPOT_EXPONENT 0 点光源聚光指数 GL_SPOT_CUTOFF 180 点光源聚光发散半角 GL_CONSTANT_ATTENUATION 1 常数衰减因子 GL_LINER_ATTENUATION 0 线性衰减因子 GL_QUADRATIC_ATTENUATION 0 平方衰减因子 说明:GL_DIFFUSE GL_SPECULAR 的缺省值只用于 GL_LIGHT0, 其他光源 GL_DIFFUSE GL_SPECULAR 缺省值为:(0.0,0.0,0.0,1.0) !!!我可能前面忘说了!!! TYPE 就是{}中的那些参数类型,例如:i 就是 int,f 就是 float。 v 是可选,表明可以数组作为参数,定义一组光源。 (2)启用光照/关闭光源 void glEnable(GLenum cap) void glDisable(GLenum cap) 例如使光源有效: glEnable(GL_LIGHT0); 下面给出简单光照的例子: ///////////////////////////////////////// //sample.cpp #include "glos.h" #include #include #include "windows.h" void myinit(void); void CALLBACK display(void); void CALLBACK reshape(GLsizei w,GLsizei h); void myinit(void) { auxInitDisplayMode(AUX_SINGLE|AUX_RGBA); auxInitPosition(0,0,500,500); auxInitWindow("sample1"); glClearColor(0.0,0.0,0.0,0.0); glClear(GL_COLOR_BUFFER_BIT); //glShadeModel 用来确定颜色填充模式,缺省的 GL_SMOOTH 的效果较好,但计算 //量大,如果你加上下面这句,那么填色是按照几何模型平面填充的,计算量 //大大减小,但是效果不好看。 // glShadeModel(GL_FLAT); //定义一个光源的位置坐标 GLfloat light_position[]={1.0,1.0,1.0,0.0}; glLightfv(GL_LIGHT0,GL_POSITION,light_position); //定义光源的漫反射颜色(兰色)以及环境光(红色),如果你上机试试这个 //程序,就可以看出光源的效果。如果没条件,可以想象一下:在淡淡的红色 //背景光下,受光照的部分呈现纯蓝色,而背光部分呈现红色。 //你还可以更详细按照上面表格指定其他属性,这里其他就用缺省的了。 GLfloat light_diffuse[]={0.0,0.0,1.0,1.0}; glLightfv(GL_LIGHT0,GL_DIFFUSE,light_diffuse); GLfloat light_ambient[]={1.0,0.0,0.0,1.0}; glLightfv(GL_LIGHT0,GL_AMBIENT,light_ambient); //关于 GL_LIGHTING 的说明: //If enabled, use the current lighting parameters to compute the //vertex color or index. If disabled, associate the current color //or index with each vertex. 如果 Enabled,使用当前光照参数计算每个 //点的颜色。你可以试试去掉这句,那么缺省的白色漫反射光源会代替你的 //灰色光源你将看见一个白色的没有立体感的球。 glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); //关于 GL_LESS 的说明 //Passes if the incoming z value is less than the stored z value. //This is the default value. //用 glEnable(GL_DEPTH_TEST)激活深度比较,然后定义如果 z 坐标小于 buffer 中 //的值(当前点 z 较小,更靠近观察点),则显示,实际就是消隐。 glDepthFunc(GL_LESS); glEnable(GL_DEPTH_TEST); } void CALLBACK reshape(GLsizei w,GLsizei h) { glViewport(0,0,w,h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); if(w<=h) glOrtho(-2.0,2.0,-2.0*(GLfloat)h/(GLfloat)w, 2.0*(GLfloat)h/(GLfloat)w,-10.0,10.0); else glOrtho(-2.0*(GLfloat)h/(GLfloat)w, 2.0*(GLfloat)h/(GLfloat)w,-2.0,2.0,-10.0,10.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void draw(void) { //调用辅助库函数画一个实心圆球。半径 1.0 auxSolidSphere(1.0); } void CALLBACK display(void) { glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); glLoadIdentity(); draw(); glFlush(); } void main(void) { myinit(); auxReshapeFunc(reshape); auxMainLoop(display); } //end of sample ///////////////////////////////////////////// 紧接上一次,这回讲材质: OPENGL 通过材料对 R、G、B 的近似反光率来近似定义材料颜色。 也分为环境、漫反射、镜面反射成分。他们决定材料对环境光、漫反射光和 镜面反射光的反射程度。将材料的特性与光源特性结合就是观察的最终显示 效果。例如红色塑料球,大部分是红色,在光源形成的高光处,则出现光源 的特性颜色。很 EASY,不是么? 材质的定义: void glMaterial{if}[v](GLenum face,GLenum pname,TYPE param); 其中: face:可以是 GL_FRONT、GL_BACK、GL_FRONT_AND_BACK,它表明当前材质应用 到物体的哪一个表面上。 pname 说明特定材质属性(很类似上一次光源的定义): pname 缺省值 说明 GL_AMBIENT 0.2,0.2,0.2,1.0 材质的环境反射光 GL_DIFFUSE 0.8,0.8,0.8,1.0 材质的漫反射光 GL_AMBIENT_AND_DIFFUSE 材质的环境光和漫反射光颜色 GL_SPECULAR 0.0,0.0.0.0,1.0 材质镜面反射光 GL_SHINESS 0.0 镜面指数(光照度) GL_EMISSION 0.0,0.0,0.0,1.0 材质的辐射颜色 GL_COLOR_INDEXES 0,1,1 材质的环境光、漫反射光和镜面 反射光颜色 请看下面材质的简单例子: //////////////////////////////////////////////////////////////////////// //sample.cpp #include "glos.h" #include #include #include "windows.h" void myinit(void); void CALLBACK display(void); void CALLBACK reshape(GLsizei w,GLsizei h); void myinit(void) { auxInitDisplayMode(AUX_SINGLE|AUX_RGBA); auxInitPosition(0,0,500,500); auxInitWindow("sample1"); glClearColor(0.0,0.0,0.0,0.0); glClear(GL_COLOR_BUFFER_BIT); // glShadeModel(GL_FLAT); //首先定义一个材质,定义方法非常类似前面讲到的光源定义。 GLfloat mat_ambient[]={0.8,0.8,0.8,1.0}; //定义 紫色 的漫反射特性 GLfloat mat_diffuse[]={0.8,0.0,0.8,1.0}; //定义 亮紫色 的镜面反射特性 GLfloat mat_specular[]={1.0,0.0,1.0,1.0}; //定义镜面反射的光亮度 GLfloat mat_shininess[]={50.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_SHININESS,mat_shininess); //这里我们简化光源,以显示材质的效果。 //这里我们只指定光源位置,其他默认:白色光源。 //你也可以加入光源的定义,看看光源和材质的合成的效果 //正是因为它们能够合成,才能产生比真实生活中多的多的效果,这也正是 //3D 技术吸引人的魅力所在。 GLfloat light_position[]={1.0,1.0,1.0,0.0}; glLightfv(GL_LIGHT0,GL_POSITION,light_position); // GLfloat light_diffuse[]={0.0,0.0,1.0,1.0}; // glLightfv(GL_LIGHT0,GL_DIFFUSE,light_diffuse); //将光源设置应用 glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); //着色消隐 //*******其实说白乐,这就是大名鼎鼎的 Z-BUFFER 呀************// glDepthFunc(GL_LESS); glEnable(GL_DEPTH_TEST); } void CALLBACK reshape(GLsizei w,GLsizei h) { glViewport(0,0,w,h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); if(w<=h) glOrtho(-2.0,2.0,-2.0*(GLfloat)h/(GLfloat)w, 2.0*(GLfloat)h/(GLfloat)w,-10.0,10.0); else glOrtho(-2.0*(GLfloat)h/(GLfloat)w, 2.0*(GLfloat)h/(GLfloat)w,-2.0,2.0,-10.0,10.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void draw(void) { auxSolidSphere(1.0); } void CALLBACK display(void) { glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); glLoadIdentity(); draw(); glFlush(); } void main(void) { myinit(); auxReshapeFunc(reshape); auxMainLoop(display); } //end of sample //////////////////////////////////////////////////// 通过以上的例子,我们会看到材质定义的实现和光源的效果是一样的,就我们日常 的感觉,定义材质更加符合人们的习惯。而光源使用白光。这里我们看到的是一个 紫色的球,其高光部分呈现亮紫色。 作为比较,下面我们给出一个 12 个彩色球的例子,大家可以看出各种光源、材质 应用的效果,在一起比较。 给出例子之前,介绍两个函数: void glPushMatrix(); void glPopMatrix(); 我前面已经讲过,所有几何投影变换都是矩阵相乘的结果。如果你希望保持一个初 始坐标点,就要用到这两个重要函数:矩阵入栈和矩阵出栈。 你可以这样理解,为了在左上角画一个球,你先保存当前矩阵,glPushMatrix()(入 栈),然后把坐标平移到左上角,然后调用 auxSolidSphere(1.0),画一个半径 1.0 的球,这时再调用 glPopMatrix()(矩阵出栈),就又回到原始坐标点,这时的球就 在左上角乐。这个程序很长,但实际上,长的部分统统是重复画 12 个球的工作, 所以也比较好理解。 ////////////////////////////////////////////////////////////////// //sample.cpp #include "glos.h" #include #include #include "windows.h" void myinit(void); void CALLBACK display(void); void CALLBACK reshape(GLsizei w,GLsizei h); void myinit(void) { auxInitDisplayMode(AUX_SINGLE|AUX_RGBA); auxInitPosition(0,0,500,500); auxInitWindow("sample1"); glClearColor(0.0,0.1,0.1,0.0); glClear(GL_COLOR_BUFFER_BIT); // glShadeModel(GL_FLAT); //首先定义光源 GLfloat light_ambient[]={0.0,0.0,0.0,1.0}; GLfloat light_diffuse[]={1.0,1.0,1.0,1.0}; GLfloat light_specular[]={1.0,1.0,1.0,1.0}; GLfloat light_position[]={0.0,3.0,2.0,0.0}; GLfloat Imodel_ambient[]={0.4,0.4,0.4,1.0}; //应用光源 glLightfv(GL_LIGHT0,GL_AMBIENT,light_ambient); glLightfv(GL_LIGHT0,GL_POSITION,light_position); glLightfv(GL_LIGHT0,GL_DIFFUSE,light_diffuse); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); //初始化 Z BUFFER glDepthFunc(GL_LESS); glEnable(GL_DEPTH_TEST); } void CALLBACK reshape(GLsizei w,GLsizei h) { glViewport(0,0,w,h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); if(w<=h) glOrtho(-6.0,6.0,-6.0*(GLfloat)h/(GLfloat)w, 6.0*(GLfloat)h/(GLfloat)w,-10.0,10.0); else glOrtho(-6.0*(GLfloat)h/(GLfloat)w, 6.0*(GLfloat)h/(GLfloat)w,-6.0,6.0,-10.0,10.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void draw(void) { auxSolidSphere(1.0); } void CALLBACK display(void) { glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); //建立材质数据库 GLfloat no_mat[]={0.0,0.0,0.0,1.0}; GLfloat mat_ambient[]={0.7,0.7,0.7,1.0}; GLfloat mat_ambient_color[]={0.8,0.8,0.2,1.0}; GLfloat mat_diffuse[]={0.1,0.5,0.8,1.0}; GLfloat mat_specular[]={1.0,1.0,1.0,1.0}; GLfloat no_shininess[]={0.0}; GLfloat low_shininess[]={5.0}; GLfloat high_shininess[]={100.0}; GLfloat mat_emission[]={0.3,0.2,0.2,0.0}; ////////////////////////////////////////////////////////// //1-1 仅有漫反射光,无环境光和镜面光 glPushMatrix(); glTranslatef(-3.75,3.0,0.0); glMaterialfv(GL_FRONT,GL_AMBIENT,no_mat); glMaterialfv(GL_FRONT,GL_DIFFUSE,mat_diffuse); glMaterialfv(GL_FRONT,GL_SPECULAR,no_mat); glMaterialfv(GL_FRONT,GL_SHININESS,no_shininess); glMaterialfv(GL_FRONT,GL_EMISSION,no_mat); draw(); glPopMatrix(); //1-2 有漫反射光,并且有低高光,无环境光 glPushMatrix(); glTranslatef(-1.25,3.0,0.0); glMaterialfv(GL_FRONT,GL_AMBIENT,no_mat); glMaterialfv(GL_FRONT,GL_DIFFUSE,mat_diffuse); glMaterialfv(GL_FRONT,GL_SPECULAR,mat_specular); glMaterialfv(GL_FRONT,GL_SHININESS,low_shininess); glMaterialfv(GL_FRONT,GL_EMISSION,no_mat); draw(); glPopMatrix(); //1-3 有漫反射光和镜面光,很亮的高光,无环境光 glPushMatrix(); glTranslatef(1.25,3.0,0.0); glMaterialfv(GL_FRONT,GL_AMBIENT,no_mat); glMaterialfv(GL_FRONT,GL_DIFFUSE,mat_diffuse); glMaterialfv(GL_FRONT,GL_SPECULAR,mat_specular); glMaterialfv(GL_FRONT,GL_SHININESS,high_shininess); glMaterialfv(GL_FRONT,GL_EMISSION,no_mat); draw(); glPopMatrix(); //1-4 有漫反射光和辐射光,无环境光和镜面反射光 glPushMatrix(); glTranslatef(3.75,3.0,0.0); glMaterialfv(GL_FRONT,GL_AMBIENT,no_mat); glMaterialfv(GL_FRONT,GL_DIFFUSE,mat_diffuse); glMaterialfv(GL_FRONT,GL_SPECULAR,no_mat); glMaterialfv(GL_FRONT,GL_SHININESS,no_shininess); glMaterialfv(GL_FRONT,GL_EMISSION,mat_emission); draw(); glPopMatrix(); ////////////////////////////////////////////////////////////// //2-1 有漫反射光和环境光,无镜面反射光 glPushMatrix(); glTranslatef(-3.75,0.0,0.0); glMaterialfv(GL_FRONT,GL_AMBIENT,mat_ambient); glMaterialfv(GL_FRONT,GL_DIFFUSE,mat_diffuse); glMaterialfv(GL_FRONT,GL_SPECULAR,no_mat); glMaterialfv(GL_FRONT,GL_SHININESS,no_shininess); glMaterialfv(GL_FRONT,GL_EMISSION,no_mat); draw(); glPopMatrix(); //2-2 有漫反射光、环境光和镜面光,而且有低高光 glPushMatrix(); glTranslatef(-1.25,0.0,0.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_SHININESS,low_shininess); glMaterialfv(GL_FRONT,GL_EMISSION,no_mat); draw(); glPopMatrix(); //2-3 有漫反射光环境光和镜面光,而且有很亮的高光 glPushMatrix(); glTranslatef(1.25,0.0,0.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_SHININESS,high_shininess); glMaterialfv(GL_FRONT,GL_EMISSION,no_mat); draw(); glPopMatrix(); //2-4 有漫反射光、环境光和辐射光,无镜面光 glPushMatrix(); glTranslatef(3.75,0.0,0.0); glMaterialfv(GL_FRONT,GL_AMBIENT,mat_ambient); glMaterialfv(GL_FRONT,GL_DIFFUSE,mat_diffuse); glMaterialfv(GL_FRONT,GL_SPECULAR,no_mat); glMaterialfv(GL_FRONT,GL_SHININESS,no_shininess); glMaterialfv(GL_FRONT,GL_EMISSION,mat_emission); draw(); glPopMatrix(); /////////////////////////////////////////////////////////////// //3-1 有漫反射光和有颜色的环境光,无镜面光 glPushMatrix(); glTranslatef(-3.75,-3.0,0.0); glMaterialfv(GL_FRONT,GL_AMBIENT,mat_ambient_color); glMaterialfv(GL_FRONT,GL_DIFFUSE,mat_diffuse); glMaterialfv(GL_FRONT,GL_SPECULAR,no_mat); glMaterialfv(GL_FRONT,GL_SHININESS,no_shininess); glMaterialfv(GL_FRONT,GL_EMISSION,no_mat); draw(); glPopMatrix(); //3-2 有漫反射光和有颜色的环境光以及镜面光,且有低高光 glPushMatrix(); glTranslatef(-1.25,-3.0,0.0); glMaterialfv(GL_FRONT,GL_AMBIENT,mat_ambient_color); glMaterialfv(GL_FRONT,GL_DIFFUSE,mat_diffuse); glMaterialfv(GL_FRONT,GL_SPECULAR,mat_specular); glMaterialfv(GL_FRONT,GL_SHININESS,low_shininess); glMaterialfv(GL_FRONT,GL_EMISSION,no_mat); draw(); glPopMatrix(); //3-3 有漫反射光和有颜色的环境光以及镜面光,且有很亮的高光 glPushMatrix(); glTranslatef(1.25,-3.0,0.0); glMaterialfv(GL_FRONT,GL_AMBIENT,mat_ambient_color); glMaterialfv(GL_FRONT,GL_DIFFUSE,mat_diffuse); glMaterialfv(GL_FRONT,GL_SPECULAR,mat_specular); glMaterialfv(GL_FRONT,GL_SHININESS,high_shininess); glMaterialfv(GL_FRONT,GL_EMISSION,no_mat); draw(); glPopMatrix(); //3-4 有漫反射光和有颜色的环境光以及辐射光,无镜面光 glPushMatrix(); glTranslatef(3.75,-3.0,0.0); glMaterialfv(GL_FRONT,GL_AMBIENT,mat_ambient_color); glMaterialfv(GL_FRONT,GL_DIFFUSE,mat_diffuse); glMaterialfv(GL_FRONT,GL_SPECULAR,no_mat); glMaterialfv(GL_FRONT,GL_SHININESS,no_shininess); glMaterialfv(GL_FRONT,GL_EMISSION,mat_emission); draw(); glPopMatrix(); glFlush(); } void main(void) { myinit(); auxReshapeFunc(reshape); auxMainLoop(display); } 这次把材质完全搞定,呵呵 上次 12 个不同的立体材质球的程序运用 glMaterialfv()来改变材质 有其固有的系统消耗。 另外同样的功能可以使用: void glColorMaterial(GLenum face,GLenum mode)来实现。 face 的取值: GL_FRONT GL_BACK GL_FRONT_AND_BACK mode 的取值: GL_AMBIENT GL_DIFFUSE GL_AMBIENT_AND_DIFFUSE GL_SPECULAR GL_EMISSION 在想要使用这个函数时,要启用 glEnable(GL_COLOR_MATERIAL)来是函数工作 在绘图时使用 glColor*()来改变材质颜色,或用 glMaterial()来改变材质成分 使用完毕,要用 glDisable(GL_COLOR_MATERIAL)来关闭这种材质模式。 例如: glColorMaterial(GL_FRONT,GL_DIFFUSE); glEnable(GL_COLOR_MATERIAL); glColor3f(0.3,0.5,0.7); //画一些物体: .. glColor3f(0.0,1.0,0.0); //再画另一些物体: .. glDisable(GL_COLOR_MATERIAL); 说明: 当需要改变场景中大部分方面的单个材质时,最好调用 glColorMaterial() 当修改不只一个材质参数,最好调用 glMaterial*(),这是目前我体会到 唯一的使用功能上的不同点吧。 请看下面例子: ///////////////////////////////////////////////////////// //sample.cpp #include "glos.h" #include #include #include "windows.h" void myinit(void); void CALLBACK display(void); void CALLBACK reshape(GLsizei w,GLsizei h); void myinit(void) { auxInitDisplayMode(AUX_SINGLE|AUX_RGBA); auxInitPosition(0,0,500,500); auxInitWindow("sample1"); glClearColor(0.0,0.1,0.1,0.0); glClear(GL_COLOR_BUFFER_BIT); // glShadeModel(GL_FLAT); //定义一个白色简单光源 GLfloat light_position[]={0.0,3.0,2.0,0.0}; glLightfv(GL_LIGHT0,GL_POSITION,light_position); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); //启用消隐 glDepthFunc(GL_LESS); glEnable(GL_DEPTH_TEST); //启用颜色材质模式 glColorMaterial(GL_FRONT,GL_DIFFUSE); glEnable(GL_COLOR_MATERIAL); } void CALLBACK reshape(GLsizei w,GLsizei h) { glViewport(0,0,w,h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); if(w<=h) glOrtho(-6.0,6.0,-6.0*(GLfloat)h/(GLfloat)w, 6.0*(GLfloat)h/(GLfloat)w,-10.0,10.0); else glOrtho(-6.0*(GLfloat)h/(GLfloat)w, 6.0*(GLfloat)h/(GLfloat)w,-6.0,6.0,-10.0,10.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void CALLBACK display(void) { glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); //先画一个黄色材质的球 glLoadIdentity(); glTranslatef(-0.7,0.0,0.0); glColor3f(1.0,1.0,0.0); auxSolidSphere(1.0); //再在黄球的右边 2.7 处画一个红色的球 glLoadIdentity(); glRotatef(-60.0,1.0,0.0,0.0); glTranslatef(2.7,0.0,0.0); glColor3f(1.0,0.0,0.0); auxSolidSphere(1.0); //再再黄球左边(两球之间)靠后的位置画一个青色三角锥 glLoadIdentity(); glTranslatef(-1.0,-1.0,-5.0); glRotatef(30.0,1.0,0.0,0.0); glColor3f(0.0,1.0,1.0); auxSolidCone(2.0,2.0); glFlush(); } void main(void) { myinit(); auxReshapeFunc(reshape); auxMainLoop(display); } //end of sample //////////////////////////////////////////////////////////// 通过这个例子,我们除了可以熟悉 glColorMaterial()的使用,还可以体会到 OPENGL 在消隐方面为我们做的工作。记的我们前面的用彩色平面绘制三角锥的例子吗?那 里可是哪个平面后画,哪个平面遮挡以前的平面。这里我们看到虽然三角锥虽然最 后画出,但是 OPENGL 的消隐功能已经保证乐它实际应该被消隐。你可以试试取消 myinit()中消隐的两句程序,那么三角锥就跑到前面来乐,呵呵。 OPENGL 的位图和图象 与一般的位图定义不同,OPENGL 中的位图是指用每个象素只有一位信息; 而图象一个象素可以包括多个信息(R、G、B、Alpha 值)。 另外位图可以用于掩码,遮掩别的图象,而图象的数据则简单的覆盖先前 的存在的数据或者与之融合。 (一)位图(BITMAP)和字符(FONT) 常常用来对窗口相应区域屏蔽,比如当前颜色为红色,则在矩阵中元素值为 1 的地方用红色取代,0 的地方保持不变。位图常用在字符显示。 光栅位置: void glRasterPos{234}{sifd}[v](TYPE x,TYPE y,TYPE z,TYPE w); 设置当前所画位图或图象的原点。一般颜色的设置应该放在 glRasterPos*() 的前面,则紧跟其后的位图都继承当前设置的这种颜色。 位图显示: void glBitmap(GLsizei width,GLsizei height,GLfloat xbo,GLflaot ybo, GLfloat xbi,GLfloat ybi,const GLubyte *bitmap); xbo,ybo 定义位图的原点,详细可参见例子: ////////////////////////////////////////////////////////////////// //sample.cpp #include "glos.h" #include #include #include "windows.h" void myinit(void); void CALLBACK display(void); void CALLBACK reshape(GLsizei w,GLsizei h); //定义字符位图,从下到上(即第一个数据是字模的最下一行,依次类推): // 11111111 // 11111111 // 11000011 // 11000011 // 11000011 // 11111111 // 11111111 // 11000011 // 11000011 // 11000011 // 11111111 // 11111111 //组成一个 8 字 GLubyte rasters[12]={0xff,0xff,0xc3,0xc3,0xc3,0xff,0xff, 0xc3,0xc3,0xc3,0xff,0xff}; void myinit(void) { auxInitDisplayMode(AUX_SINGLE|AUX_RGBA); auxInitPosition(0,0,500,500); auxInitWindow("sample1"); glClearColor(0.0,0.0,0.0,0.0); glClear(GL_COLOR_BUFFER_BIT); //描述位图数据在计算机内存中的存储方式,不必深究 glPixelStorei(GL_UNPACK_ALIGNMENT,1); } void CALLBACK reshape(GLsizei w,GLsizei h) { glViewport(0,0,w,h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0,w,0,h,-1.0,1.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void CALLBACK display(void) { glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); //先定义红色填充: glColor3f(1.0,0.0,1.0); glRasterPos2i(100,200); //在 2 个不同位置绘制 8 (紫色) glBitmap(8,12,0.0,0.0,20.0,20.0,rasters); glBitmap(8,12,0.0,0.0,20.0,20.0,rasters); //绘制另一个 8 (黄色) glColor3f(1.0,1.0,0.0); glRasterPos2i(150,200); glBitmap(8,12,0.0,0.0,0.0,0.0,rasters); glFlush(); } void main(void) { myinit(); auxReshapeFunc(reshape); auxMainLoop(display); } //end of sample /////////////////////////////////////////////////////// (二)图象 字符显示只是个小内容,我想大家也许会更关心图象的显示 1.象素读写 OPENGL 提供基本象素读写函数: void glReadPixels( GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels ); 函数参数(x,y)定义图象区域左下角坐标,width height 描述图象宽高。 pixel 是指针,指向图象数据数组。format 指出数据元素格式(索引或 R、G、B、A 值): format: 说明: GL_INDEX 单个颜色索引 GL_RGB 依次 红、绿、蓝分量 GL_RED 单个红色分量 GL_GREEN 单个绿色分量 GL_BLUE 单个蓝色分量 GL_ALPHA 单个 Alpha 值 GL_LUMINANCE_ALPHA GL_STENCIL_INDEX 单个模板索引 GL_DEPTH_COMPONENT 单个深度分量 而 type 指出元素数据类型: type: GL_UNSIGNED_BYTE 无符号 8 位整数 GL_BYTE 8 位整数 GL_BITMAP 无符号 8 位整数数组中单个数位 GL_UNSIGNED_SHORT 无符号 16 位整数 GL_SHORT 16 位整数 GL_UNSIGNED_INT 无符号 32 位整数 GL_INT 32 位整数 GL_FLOAT 单精度浮点数 类似的写象素函数: void glDrawPixels( GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels ); 2.象素拷贝 void glCopyPixels( GLint x, GLint y, GLsizei width, GLsizei height, GLenum type ); 这个函数很类似先用 glReadPixels()然后调用 glDrawPixels(),但是它不消耗 系统内存,只是拷贝, type 可以是:GL_COLOR GL_STENCIL GL_DEPTH 在拷贝前,type 要按如下方式转换成 format: 1)type 为 GL_DEPTH GL_STENCIL 那么 format 对应应该是 GL_DEPTH_COMPONENT 或 GL_STENCIL_INDEX 2)type 为 GL_COLOR 那么 format 应该是 GL_RGB 或 GL_COLOR_INDEX。 3.图象缩放 void glPixelZoom(GLfloat zoomx,GLfloat zoomy); zoomx zoomy 是 X Y 方向的缩放因子。缺省是 1.0。 #include "glos.h" #include #include #include "windows.h" void myinit(void); void CALLBACK display(void); void CALLBACK reshape(GLsizei w,GLsizei h); void myinit(void) { auxInitDisplayMode(AUX_SINGLE|AUX_RGBA); auxInitPosition(0,0,500,500); auxInitWindow("sample1"); glClearColor(0.0,0.0,0.0,0.0); glClear(GL_COLOR_BUFFER_BIT); // glPixelStorei(GL_UNPACK_ALIGNMENT,1); } void CALLBACK reshape(GLsizei w,GLsizei h) { glViewport(0,0,w,h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); if(w<=h) gluOrtho2D(0.0,15.0,0.0,15.0*(GLfloat)h/(GLfloat)w); else gluOrtho2D(0.0,15.0*(GLfloat)w/(GLfloat)h,0.0,15.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void draw() { //绘制一个彩色三角形(2D) glBegin(GL_TRIANGLES); glColor3f(1.0,0.0,0.0); glVertex2f(2.0,3.0); glColor3f(0.0,1.0,0.0); glVertex2f(12.0,3.0); glColor3f(0.0,0.0,1.0); glVertex2f(7.0,12.0); glEnd(); } void CALLBACK display(void) { glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); //在屏幕上方绘制原始图象 glPushMatrix(); glLoadIdentity(); glTranslatef(4.0,8.0,0.0); glScalef(0.5,0.5,0.5); draw(); glPopMatrix(); int i; for(i=0;i<5;i++) { //循环 5 次画出 5 个拷贝 //先定义显示缩放因子(递增) glPixelZoom(1.0+0.1*i,1.0+0.1*i); //再定义拷贝原点(越来越向右上) glRasterPos2i(1+i*2,i); //图象拷贝 glCopyPixels(160,310,180,160,GL_COLOR); } glFlush(); } void main(void) { myinit(); auxReshapeFunc(reshape); auxMainLoop(display); } //end of sample //////////////////////////////////////////////////////////// 这个例子在屏幕上方中央绘制一个彩色三角然后在下面从左到右,依次绘制它的拷 贝,拷贝位置不断向右上方向而且(通过增加缩放因子)新的拷贝变的越来越大。 当然后绘制的拷贝会遮盖前面的图形。 OPENGL 的纹理 在 3D 图形中,纹理映射是广泛使用的。纹理映射也是相当复杂的过程: 一 定义纹理 二 控制滤波 三 说明映射方式 四 绘制场景给出顶点的纹理坐标和几何坐标 注意!!纹理映射只能在 RGBA 模式下使用,不适用于颜色索引模式 1.纹理定义 void glTexImage2D( GLenum target, GLint level, GLint components, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels ); 定义一个二维纹理映射。 target 是常数 GL_TEXTURE_2D level 表示多级分辨率的纹理图象的级数。若只有一种分辨率,level 为 0。 components 是从 1 到 4 的整数,1:选择 R;2:选择 R A;3:选择 R G B; 4:选择 R G B A; width height 是纹理的尺寸。 format 和 type 描述映射格式和数据类型。它们与前面讲的 glDrawPixels()中 的意义相同。你可以把纹理当成贴图来看待。 另外还有一维纹理定义: void glTexImage1D( GLenum target, GLint level, GLint components, GLsizei width, GLint border, GLenum format, GLenum type, const GLvoid *pixels ); 不同之处在于 target 是常数 GL_TEXTURE_1D,例外提供的数据应该是一维数组。 一般纹理数据维数应该是 2 的幂次,有时还要根据类型加上边界数据。 2.纹理控制(滤波和重复与缩限) 所有纹理控制通过: void glTexParameter{if}[v](GLenum target,GLenum pname,TYPE param); 来实现,target 可以是 GL_TEXTURE_1D GL_TEXTURE_2D 来说明是一维还是二维 纹理。pname 和 param 的可能取值见下: pname: param: GL_TEXTURE_WRAP_S GL_CLAMP GL_REPEAT GL_TEXTURE_WRAP_T GL_CLAMP GL_REPEAT GL_TEXTURE_MAG_FILTER GL_NEAREST GL_LINEAR GL_TEXTURE_MIN_FILTER GL_NEAREST GL_NEAREST_MIPMAP_NEAREST GL_NEAREST_MIPMAP_LINEAR GL_LINEAR_MIPMAP_NEAREST GL_LINEAR_MIPMAP_LINEAR 2.1 滤波 原始纹理图象是个方形图象,把它映射到奇形怪状的物体上,一般不可能图象 上的一个象素对应屏幕的一个象素。因此局部放大缩小时,就要定义合适的滤 波方式(以 2D 为例): void glTexParameter(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); void glTexParameter(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); 前者是放大滤波(GL_TEXTURE_MAG_FILTER), 后者是缩小滤波(GL_TEXTURE_MIN_FILTER); 另外,GL_NEAREST 是利用最坐标最靠近象素中心的纹理元素,这有可能使图样 走型,但计算速度快;GL_LINEAR 利用线形插值,效果好但计算量大。 2.2 重复与缩限 纹理映射可以重复映射或者缩限映射,重复映射时纹理可以在自己的坐标 S T 方 向重复。 对于重复映射: void glTexParameterfv(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT); void glTexParameterfv(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT); 参数 GL_REPEAT 改为 GL_CLAMP,则缩限,所有大于 1 的纹理元素值置为 1。所 有小于 0 的纹理元素值置为 0。 3. 映射方式 处理纹理本身图案颜色和物体本身颜色的关系: void glTexEnv{if}[v](GLenum target,GLenum pname,TYPE param); target 必须是 GL_TEXTURE_ENV; pname 是 GL_TEXTURE_ENV_MODE,则 param 可以是 GL_DECAL GL_MODULATE 或 GL_BLEND,说明纹理值与原来颜色不同的处理方式。 pname 是 GL_TEXTURE_ENV_COLOR,则参数 param 是包含 4 个浮点数(R、G、 B、A) 的数组。这些值只在采用 GL_BLEND 纹理函数时才采用。 4. 纹理坐标 坐标的定义:纹理图象是方形的,纹理坐标可定义成 s,t,r,q 坐标,仿照齐次 坐标系的 x,y,z,w 坐标。 void glTexCoord{1234}{sifd}[v](TYPE coords); 设置当前纹理坐标,此后调用 glVertex*()所产生的顶点都赋予当前的纹理坐标。 5. 坐标自动产生 有时不需要为每个物体顶点赋予纹理坐标,可以使用 void glTexGen{if}(GLenum coord,GLenum pname,TYPE param); coord 为:GL_S GL_T GL_R 或 GL_Q,指明哪个坐标自动产生 pname 为 GL_TEXTURE_GEN_MODE 时 param 为常数:GL_OBJECT_LINEAR GL_EYE_LINEAR 或 GL_SPHERE_MAP,它们 决定用 哪个函数来产生纹理坐标 pname 为 GL_OBJECT_PLANE 或 GL_EYE_PLANE,param 时一个指向参数数组的指 针。 先请看一个简单的例子: //////////////////////////////////////////// //sample.cpp #include "glos.h" #include #include #include "windows.h" void myinit(void); void CALLBACK display(void); void CALLBACK reshape(GLsizei w,GLsizei h); //创建纹理图象的子程序 #define TEXTUREWIDTH 64 #define TEXTUREHEIGHT 64 GLubyte Texture[TEXTUREWIDTH][TEXTUREHEIGHT][3]; void makeTexture(void) { int i,j,r,g,b; for(i=0;i #include #include "windows.h" void myinit(void); void CALLBACK display(void); void CALLBACK reshape(GLsizei w,GLsizei h); //定义一个一维纹理的数据,从生成来看,保持红色、兰色分量 255(MAX), //所以是渐变的紫色纹理,饱和度不断变化。 #define TEXTUREWIDTH 64 GLubyte Texture[3*TEXTUREWIDTH]; void makeTexture(void) { int i; for(i=0;i0,缺省 1 void glLineWidth(GLfoat width); 设置线宽,width>0,缺省为 1 void glLineStipple(GLint factor,GLushort pattern); 设置线的模式,factor 用于对模式进行拉伸的比例因子,pattern 是线的模式 例如 11001100 是虚线(1 绘制,0 不绘制) 必须要启用 glEnable(GL_LINE_STIPPLE)才能使用以上函数,不再使用时调用 glDisable(GL_LINE_STIPPLE)关闭,这与以前的 glEnable();glDisable();的 用法都是类似的。请看下面例子: /////////////////////////////////////////////////////////////////// //sample.cpp #include "glos.h" #include #include #include "windows.h" void myinit(void); void CALLBACK display(void); void CALLBACK reshape(GLsizei w,GLsizei h); void myinit(void) { auxInitDisplayMode(AUX_SINGLE|AUX_RGBA); auxInitPosition(0,0,600,500); auxInitWindow("sample1"); glClearColor(0.0,0.0,0.0,0.0); glClear(GL_COLOR_BUFFER_BIT); glShadeModel(GL_FLAT); } /* void CALLBACK reshape(GLsizei w,GLsizei h) { glViewport(0,0,w,h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); if(w<=h) glOrtho(-4.0,4.0,-4.0*(GLfloat)h/(GLfloat)w, 4.0*(GLfloat)h/(GLfloat)w,-4.0,4.0); else glOrtho(-4.0*(GLfloat)h/(GLfloat)w, 4.0*(GLfloat)h/(GLfloat)w,-4.0,4.0,-4.0,4.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } */ //自定义的绘制直线的函数,参数为起始点和终止点坐标 void line2i(GLint x1,GLint y1,GLint x2,GLint y2) { glBegin(GL_LINES); glVertex2f(x1,y1); glVertex2f(x2,y2); glEnd(); } void CALLBACK display(void) { glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); //首先绘制一系列点,点的大小不断增加 int i; glColor3f(0.8,0.6,0.4); for(i=1;i<=10;i++) { glPointSize(i*2); glBegin(GL_POINTS); glVertex2f(30.0+((GLfloat)i*50.0),330.0); glEnd(); } //再绘制两条虚线,第二条比第一条松散一些,由 pattern 参数即可看出 glLineWidth(1.0); glEnable(GL_LINE_STIPPLE); glLineStipple(1,0x0f0f);//间隔 1 位 glColor3f(1.0,0.0,0.0); line2i(20,250,250,250); glLineStipple(1,0x00ff);//间隔 2 位 glColor3f(0.0,0.0,1.0); line2i(300,250,550,250); //改变线的绘制宽度的效果--加宽 //重新画出上面两条虚线 glLineWidth(5.0); glEnable(GL_LINE_STIPPLE); glLineStipple(1,0x0f0f); glColor3f(1.0,0.0,0.0); line2i(50,150,250,150); glLineStipple(1,0x00ff); glColor3f(0.0,0.0,1.0); line2i(300,150,550,150); glFlush(); } void main(void) { myinit(); // auxReshapeFunc(reshape); auxMainLoop(display); } //end of sample ////////////////////////////////////////////// 2.多边形 void glPolygonMode(GLenum face,GLenum mode); 控制多边形指定面的绘图模式, face 为:GL_FRONT GL_BACK 或 GL_FRONT_AND BACK mode 为:GL_POINT GL_LINE 或 GL_FILL 表示多边型的轮廓点、轮廓线和填充模 式 的绘制方式。缺省是填充方式。 void glPolygonStipple(const GLubyte *mask); 其中 mask 必须是指向 32*32 的位图指针,1 是绘制、0 不绘制 使用上述函数也要调用: glEnable(GL_POLYGON-STIPPLE); glDisable(GL_POLYGON_STIPPLE); 请看下面例子: ///////////////////////////////////////////// //sample.cpp #include "glos.h" #include #include #include "windows.h" void myinit(void); void CALLBACK display(void); void CALLBACK reshape(GLsizei w,GLsizei h); //定义填充模式 32*32 点阵 GLubyte pattern[]={ 0x00,0x01,0x80,0x00, 0x00,0x03,0xc0,0x00, 0x00,0x07,0xe0,0x00, 0x00,0x0f,0xf0,0x00, 0x00,0x1f,0xf8,0x00, 0x00,0x3f,0xfc,0x00, 0x00,0x7f,0xfe,0x00, 0x00,0xff,0xff,0x00, 0x01,0xff,0xff,0x80, 0x03,0xff,0xff,0xc0, 0x07,0xff,0xff,0xe0, 0x0f,0xff,0xff,0xf0, 0x1f,0xff,0xff,0xf8, 0x3f,0xff,0xff,0xfc, 0x7f,0xff,0xff,0xfe, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0x7f,0xff,0xff,0xfe, 0x3f,0xff,0xff,0xfc, 0x1f,0xff,0xff,0xf8, 0x0f,0xff,0xff,0xf0, 0x07,0xff,0xff,0xe0, 0x03,0xff,0xff,0xc0, 0x01,0xff,0xff,0x80, 0x00,0xff,0xff,0x00, 0x00,0x7f,0xfe,0x00, 0x00,0x3f,0xfc,0x00, 0x00,0x1f,0xf8,0x00, 0x00,0x0f,0xf0,0x00, 0x00,0x07,0xe0,0x00, 0x00,0x03,0xc0,0x00, 0x00,0x01,0x80,0x00 }; void myinit(void) { auxInitDisplayMode(AUX_SINGLE|AUX_RGBA); auxInitPosition(0,0,400,400); auxInitWindow("sample1"); glClearColor(0.0,0.0,0.0,0.0); glClear(GL_COLOR_BUFFER_BIT); glShadeModel(GL_FLAT); } /* void CALLBACK reshape(GLsizei w,GLsizei h) { glViewport(0,0,w,h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); if(w<=h) glOrtho(-4.0,4.0,-4.0*(GLfloat)h/(GLfloat)w, 4.0*(GLfloat)h/(GLfloat)w,-4.0,4.0); else glOrtho(-4.0*(GLfloat)h/(GLfloat)w, 4.0*(GLfloat)h/(GLfloat)w,-4.0,4.0,-4.0,4.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } */ void CALLBACK display(void) { glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); //选用兰色作为填充色 glColor3f(0.0,0.0,1.0); //启用多边形绘制模式 glEnable(GL_POLYGON_STIPPLE); //利用定义好的填充模式绘制多边形 glPolygonStipple(pattern); //绘制长方形 glRectf(48.0,80.0,210.0,305.0); glFlush(); } void main(void) { myinit(); // auxReshapeFunc(reshape); auxMainLoop(display); } //end of sample 例子中的运行结果是给出一个表面有定义图样的长方形 这里讲解 OPENGL 的曲线生成 1.曲线定义 void glMap1{fd}(GLenum target,TYPE u1,TYPE u2,GLint stride, GLint order,const TYPE *points); target 指出控制点的意义以及在 points 参数中需要多少值。具体如下: target 意义 GL_MAP1_VERTEX_3 X Y Z 顶点坐标 GL_MAP1_VERTEX_4 X Y Z W 顶点坐标 GL_MAP1_INDEX 颜色索引 GL_MAP1_COLOR_4 R G B A GL_MAP1_NORMAL 法向量 GL_MAP1_TEXTURE_COORD_1 S 纹理坐标 GL_MAP1_TEXTURE_COORD_2 S T 纹理坐标 GL_MAP1_TEXTURE_COORD_3 S T R 纹理坐标 u1,u2 是曲线变量 U 的范围(具体可以参阅图形学书籍)一般是 0 到 1 stride 是跨度,表示 points 中控制点偏移量(或说是控制点的维数) order 是阶数,与控制点数一样 points 是控制点坐标 曲线定义后要启用才能进行绘制,同样用 glEnable(),glDisable()。 2.曲线计算绘制 void glEvalCoord1{fd}[v](TYPE u); 利用控制点产生曲线 U 坐标下某点的坐标,调用一次只能产生一个坐标。 一般的曲线的绘制方法是让 U 变化从 0 到 1(步长自定)然后把这些坐标 用直线连接起来。 用以上这两个函数就可以完成绘制曲线的功能。 另外还有两个函数可以实现类似功能: void glMapGrid1{fd}(GLint n,TYPE u1,TYPE u2); 定义一个空间网格,从 u1 到 u2 分为 n 步,是等间隔的(曲线参数)。 void glEvalMesh1(GLenum mode,GLint p1,GLint p2); 计算并绘制坐标点。mode 可以是 GL_POINT、GL_LINE 即延曲线绘点或连接直线 段 它等价于: glBegin(GL_POINT); for(i=p1;i<=p2;i++) glEvalCoord1(u1+i*(u2-u1)/n); glEnd(); 下面给出一个 BEZIER 曲线的例子: //////////////////////////////////////////////////////////////////// //sample.cpp #include "glos.h" #include #include #include "windows.h" void myinit(void); void CALLBACK display(void); void CALLBACK reshape(GLsizei w,GLsizei h); //定义四个控制点的坐标 GLfloat points[4][3]={ {-4.0,-4.0,0.0},{-2.0,4.0,0.0},{2.0,-4.0,0.0},{4.0,4.0,0.0}}; void myinit(void) { auxInitDisplayMode(AUX_SINGLE|AUX_RGBA); auxInitPosition(0,0,500,500); auxInitWindow("sample1"); glClearColor(0.0,0.0,0.0,0.0); glClear(GL_COLOR_BUFFER_BIT); //定义曲线,并启用绘制曲线的模式 glMap1f(GL_MAP1_VERTEX_3,0.0,1.0,3,4,&points[0][0]); glEnable(GL_MAP1_VERTEX_3); glShadeModel(GL_FLAT); } void CALLBACK reshape(GLsizei w,GLsizei h) { glViewport(0,0,w,h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); if(w<=h) glOrtho(-5.0,5.0,-5.0*(GLfloat)h/(GLfloat)w, 5.0*(GLfloat)h/(GLfloat)w,-5.0,5.0); else glOrtho(-5.0*(GLfloat)h/(GLfloat)w, 5.0*(GLfloat)h/(GLfloat)w,-5.0,5.0,-5.0,5.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void CALLBACK display(void) { glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); glColor3f(0.0,0.0,1.0); glBegin(GL_LINE_STRIP); //首先以 30 步的直线段连接,来绘制曲线,注意使用 GL_LINE_STRIP 来连接直线 段 int i; for(i=0;i<=30;i++) glEvalCoord1f((GLfloat)i/30.0); glEnd(); //下面绘制出 4 个控制点 glPointSize(4.0); glColor3f(1.0,0.0,0.0); glBegin(GL_POINTS); for(i=0;i<4;i++) glVertex3fv(&points[i][0]); glEnd(); glFlush(); } void main(void) { myinit(); auxReshapeFunc(reshape); auxMainLoop(display); } //end of sample 本例子绘制出一个有 4 个控制点的 BEZIER 曲线。曲线经过头尾两个控制点中间曲 线形状由控制点次序和位置决定,总之落在其包围的四边形以内。 下次将会用大篇幅介绍曲面的生成和其表面纹理、颜色的应用 曲面的构造可以是网格线和填充曲面形式,其实与曲线很类似只是变为 二维而已。 1.曲面定义 void glMap2{fd}(GLenum target,TYPE u1,TYPE u2,GLint ustride,GLint uorder, TYPE v1,TYPE v2,GLint vstride,GLint vorder,TYPE points); target 的定义同上次介绍的曲线中 target 的定义。 U V 是二维曲面坐标 uorder,vorder;ustride,vstride 的定义都类似曲线定义。 points 是控制点坐标 2.曲面任意一点的计算 void glEvalCoord2{fd}[v](TYPE u,TYPE v); 以曲线坐标 U V 来计算曲面内任意一点的世界坐标的位置 3.曲面绘制的控制 void glMapGrid2{fd}(GLenum nu,TYPE u1,TYPE u2,GLenum nv,TYPE v1,TYPE v2); 定义曲面参数空间均匀网格,从 u1 到 u2 分为等间隔 nu 步,从 v1 到 v2 分为等间 隔 nv 步。 下面给出一个以网格线描绘曲面的例子: //////////////////////////////////////////////////////////// //sample.cpp #include "glos.h" #include #include #include "windows.h" void myinit(void); void CALLBACK display(void); void CALLBACK reshape(GLsizei w,GLsizei h); //控制点坐标 GLfloat points[4][4][3]={ {{-1.5,-1.5,2.0},{-0.5,-1.5,2.0}, {0.5,-1.5,-1.0},{1.5,-1.5,2.0}}, {{-1.5,-0.5,1.0},{-0.5,1.5,2.0}, {0.5,0.5,1.0},{1.5,-0.5,-1.0}}, {{-1.5,0.5,2.0},{-0.5,0.5,1.0}, {0.5,0.5,3.0},{1.5,-1.5,1.5}}, {{-1.5,1.5,-2.0},{-0.5,1.5,-2.0}, {0.5,0.5,1.0},{1.5,1.5,-1.0}}}; //为了清楚显示控制点而设置的一组颜色 GLfloat color[4][3]={ {1.0,0.0,0.0},{0.0,1.0,0.0},{0.0,0.0,1.0},{1.0,1.0,1.0}}; void myinit(void) { auxInitDisplayMode(AUX_SINGLE|AUX_RGBA); auxInitPosition(0,0,500,500); auxInitWindow("sample1"); glClearColor(0.0,0.0,0.0,0.0); glClear(GL_COLOR_BUFFER_BIT); //利用 glEnable()来启用曲面模式 glMap2f(GL_MAP2_VERTEX_3,0,1,3,4,0,1,12,4,&points[0][0][0]); glEnable(GL_MAP2_VERTEX_3); glMapGrid2f(20,0.0,1.0,20,0.0,1.0); glEnable(GL_DEPTH_TEST); } void CALLBACK reshape(GLsizei w,GLsizei h) { glViewport(0,0,w,h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); if(w<=h) glOrtho(-5.0,5.0,-5.0*(GLfloat)h/(GLfloat)w, 5.0*(GLfloat)h/(GLfloat)w,-5.0,5.0); else glOrtho(-5.0*(GLfloat)h/(GLfloat)w, 5.0*(GLfloat)h/(GLfloat)w,-5.0,5.0,-5.0,5.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void CALLBACK display(void) { glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); glColor3f(0.0,0.0,1.0); glPushMatrix(); glRotatef(35.0,1.0,1.0,1.0); //用直线段的连接描绘曲面结构 glBegin(GL_LINE_STRIP); int i,j; //在 U V 方向各画出 8 条网格线,用以构造曲面结构 for(j=0;j<=8;j++) { //在 U 方向用 30 条直线段描绘一条曲线 glBegin(GL_LINE_STRIP); for(i=0;i<=30;i++) glEvalCoord2f((GLfloat)i/30.0,(GLfloat)j/8.0); glEnd(); //在 V 方向用 30 条直线段描绘一条曲线 glBegin(GL_LINE_STRIP); for(i=0;i<=30;i++) glEvalCoord2f((GLfloat)j/8.0,(GLfloat)i/30.0); glEnd(); } //用不同的颜色把控制点显示出来 glPointSize(4.0); glBegin(GL_POINTS); for(i=0;i<4;i++) for(j=0;j<4;j++) { glColor3fv(color[i]); glVertex3fv(&points[i][j][0]); } glEnd(); glPopMatrix(); glFlush(); } void main(void) { myinit(); auxReshapeFunc(reshape); auxMainLoop(display); } //end of sample /////////////////////////////////////////////////////// 应用中常常不满足曲面的框架结构。下面就介绍填充曲面的绘制方法: void glEvalMesh2(GLenum mode,GLint p1,GLint p2,GLint q1,GLint q2); 把用 glMapGrid2{fd}()设置的网格应用到已经启用的曲面计算上。 mode 可以是 GL_POINT GL_LINE GL_FILL。顾名思义,GL_FILL 就是生成填充曲面 这里给出一个有光照处理的 BEZIER 曲面的例子(这个曲面除了绘制方法不同,数 学 形式和前面的是一样的)。 /////////////////////////////////////////////////////// //sample.cpp #include "glos.h" #include #include #include "windows.h" void myinit(void); void CALLBACK display(void); void CALLBACK reshape(GLsizei w,GLsizei h); GLfloat points[4][4][3]={ {{-1.5,-1.5,2.0},{-0.5,-1.5,2.0}, {0.5,-1.5,-1.0},{1.5,-1.5,2.0}}, {{-1.5,-0.5,1.0},{-0.5,1.5,2.0}, {0.5,0.5,1.0},{1.5,-0.5,-1.0}}, {{-1.5,0.5,2.0},{-0.5,0.5,1.0}, {0.5,0.5,3.0},{1.5,-1.5,1.5}}, {{-1.5,1.5,-2.0},{-0.5,1.5,-2.0}, {0.5,0.5,1.0},{1.5,1.5,-1.0}}}; GLfloat color[4][3]={ {1.0,0.0,0.0},{0.0,1.0,0.0},{0.0,0.0,1.0},{1.0,1.0,1.0}}; //初始化光照、材质的过程 void initlights(void) { GLfloat ambient[]={0.4,0.6,0.2,1.0}; GLfloat position[]={0.0,1.0,3.0,1.0}; GLfloat mat_diffuse[]={0.2,0.4,0.8,1.0}; GLfloat mat_specular[]={1.0,1.0,1.0,1.0}; GLfloat mat_shininess[]={80.0}; glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glLightfv(GL_LIGHT0,GL_AMBIENT,ambient); glLightfv(GL_LIGHT0,GL_POSITION,position); glMaterialfv(GL_FRONT,GL_DIFFUSE,mat_diffuse); glMaterialfv(GL_FRONT,GL_SPECULAR,mat_specular); glMaterialfv(GL_FRONT,GL_SHININESS,mat_shininess); } void myinit(void) { auxInitDisplayMode(AUX_SINGLE|AUX_RGBA); auxInitPosition(0,0,500,500); auxInitWindow("sample1"); glClearColor(0.0,0.0,0.0,0.0); glClear(GL_COLOR_BUFFER_BIT); glMap2f(GL_MAP2_VERTEX_3,0,1,3,4,0,1,12,4,&points[0][0][0]); glEnable(GL_MAP2_VERTEX_3); glEnable(GL_AUTO_NORMAL); // glEnable(GL_NORMALIZE); glMapGrid2f(20,0.0,1.0,20,0.0,1.0); glEnable(GL_DEPTH_TEST); //初始化光照、材质 initlights(); // glShadeModel(GL_FLAT); } void CALLBACK reshape(GLsizei w,GLsizei h) { glViewport(0,0,w,h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); if(w<=h) glOrtho(-5.0,5.0,-5.0*(GLfloat)h/(GLfloat)w, 5.0*(GLfloat)h/(GLfloat)w,-5.0,5.0); else glOrtho(-5.0*(GLfloat)h/(GLfloat)w, 5.0*(GLfloat)h/(GLfloat)w,-5.0,5.0,-5.0,5.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void CALLBACK display(void) { glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); glColor3f(0.0,0.0,1.0); glPushMatrix(); glRotatef(35.0,1.0,1.0,1.0); //将原来网格的绘制部分注释掉,代替以填充曲面的绘制方法 glEvalMesh2(GL_FILL,0,20,0,20); int i,j; /* for(j=0;j<=8;j++) { glBegin(GL_LINE_STRIP); for(i=0;i<=30;i++) glEvalCoord2f((GLfloat)i/30.0,(GLfloat)j/8.0); glEnd(); glBegin(GL_LINE_STRIP); for(i=0;i<=30;i++) glEvalCoord2f((GLfloat)j/8.0,(GLfloat)i/30.0); glEnd(); } */ glPointSize(4.0); glBegin(GL_POINTS); for(i=0;i<4;i++) for(j=0;j<4;j++) { glColor3fv(color[i]); glVertex3fv(&points[i][j][0]); } glEnd(); glPopMatrix(); glFlush(); } void main(void) { myinit(); auxReshapeFunc(reshape); auxMainLoop(display); } //end of sample //////////////////////////////////////////////////////////////// 一个真正具有实体的曲面就展现出来乐。控制点的不同,可以控制曲面的形状 可以看出 OPENGL 的强大功能,只用很少的原代码就产生真实的 3D 效果。 在以此篇结束复杂建模前,给出一个绘制 NURBS 曲面(非均匀 B 样条曲面)的例 子, 这里用到乐一些 OPENGL 实用库提供的专门函数。所有的新东东都在程序中做乐注 释,注释给出过程的含义,具体函数的操作可以详细查阅联机手册。 /////////////////////////////////////////////////////////////// //sample.cpp #include "glos.h" #include #include #include "windows.h" void myinit(void); void CALLBACK display(void); void CALLBACK reshape(GLsizei w,GLsizei h); //定义一组控制点的存储空间 GLfloat points[4][4][3]; //定义一个指向 NURBS 曲面对象的指针 GLUnurbsObj *theNurb; //用来生成控制点的过程,取代被注释掉的原来直接赋值方式生成控制点 void init_surface(void) { int u,v; for(u=0;u<4;u++) { for(v=0;v<4;v++) { points[u][v][0]=2.0*((GLfloat)u-1.5); points[u][v][1]=2.0*((GLfloat)v-1.5); if((u==1||u==2)&&(v==1||v==2)) points[u][v][2]=3.0; else points[u][v][2]=-3.0; } } } /* GLfloat points[4][4][3]={ {{-1.5,-1.5,2.0},{-0.5,-1.5,4.0}, {0.5,-1.5,3.0},{1.5,-1.5,2.0}}, {{-1.5,-0.5,3.0},{-0.5,1.5,4.0}, {0.5,0.5,2.0},{1.5,-0.5,-1.0}}, {{-1.5,0.5,2.0},{-0.5,0.5,4.0}, {0.5,0.5,5.0},{1.5,-1.5,1.5}}, {{-1.5,1.5,-2.0},{-0.5,1.5,3.0}, {0.5,0.5,1.0},{1.5,1.5,-1.0}}}; */ GLfloat color[4][3]={ {1.0,0.0,0.0},{0.0,1.0,0.0},{0.0,0.0,1.0},{1.0,1.0,1.0}}; void initlights(void) { GLfloat ambient[]={0.4,0.6,0.2,1.0}; GLfloat position[]={0.0,1.0,3.0,1.0}; GLfloat mat_diffuse[]={0.2,0.4,0.8,1.0}; GLfloat mat_specular[]={1.0,1.0,1.0,1.0}; GLfloat mat_shininess[]={80.0}; glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glLightfv(GL_LIGHT0,GL_AMBIENT,ambient); glLightfv(GL_LIGHT0,GL_POSITION,position); glMaterialfv(GL_FRONT,GL_DIFFUSE,mat_diffuse); glMaterialfv(GL_FRONT,GL_SPECULAR,mat_specular); glMaterialfv(GL_FRONT,GL_SHININESS,mat_shininess); //首先初始化控制点 init_surface(); //创建一个 NURBS 曲面的对象 theNurb=gluNewNurbsRenderer(); //修改此曲面对象的属性 gluNurbsProperty(theNurb,GLU_SAMPLING_TOLERANCE,25.0); gluNurbsProperty(theNurb,GLU_DISPLAY_MODE,GLU_FILL); } void myinit(void) { auxInitDisplayMode(AUX_SINGLE|AUX_RGBA); auxInitPosition(0,0,500,500); auxInitWindow("sample1"); glClearColor(0.0,0.0,0.0,0.0); glClear(GL_COLOR_BUFFER_BIT); glMap2f(GL_MAP2_VERTEX_3,0,1,3,4,0,1,12,4,&points[0][0][0]); glEnable(GL_MAP2_VERTEX_3); glEnable(GL_AUTO_NORMAL); glEnable(GL_NORMALIZE); glMapGrid2f(20,0.0,1.0,20,0.0,1.0); glEnable(GL_DEPTH_TEST); initlights(); // glShadeModel(GL_FLAT); } void CALLBACK reshape(GLsizei w,GLsizei h) { glViewport(0,0,w,h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); if(w<=h) glOrtho(-5.0,5.0,-5.0*(GLfloat)h/(GLfloat)w, 5.0*(GLfloat)h/(GLfloat)w,-5.0,5.0); else glOrtho(-5.0*(GLfloat)h/(GLfloat)w, 5.0*(GLfloat)h/(GLfloat)w,-5.0,5.0,-5.0,5.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void CALLBACK display(void) { //B 样条曲面(NURBS)的控制向量,可以参阅图形学的相关书籍 GLfloat knots[8]={0.0,0.0,0.0,0.0,1.0,1.0,1.0,1.0}; glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); glColor3f(0.0,0.0,1.0); glPushMatrix(); glRotatef(35.0,1.0,1.0,1.0); glRotatef(-60.0,1.0,0.0,0.0); glScalef(0.5,0.5,0.5); //注释掉原来的曲面绘制函数,代之以新的 NURBS 曲面绘制函数 // glEvalMesh2(GL_FILL,0,20,0,20); //定义曲面的数学模型,确定其形状 gluNurbsSurface(theNurb, 8,knots, 8,knots, 4*3, 3, &points[0][0][0], 4,4, GL_MAP2_VERTEX_3); //结束曲面的绘制,此结构很类似于标准的 glBegin()...glEnd() gluEndSurface(theNurb); int i,j; glPointSize(4.0); glBegin(GL_POINTS); for(i=0;i<4;i++) for(j=0;j<4;j++) { glColor3fv(color[i]); glVertex3fv(&points[i][j][0]); } glEnd(); glPopMatrix(); glFlush(); } void main(void) { myinit(); auxReshapeFunc(reshape); auxMainLoop(display); } //end of sample ////////////////////////////////////////////////////////////////// 至此,复杂建模告于段落。下次介绍特殊光照 为什么 3D 作图常常能产生另人震惊的效果?因为利用 3D 作图,你可以生成一些 现实中难得实现的真实的感受。特别是一些特殊的光影效果。 其实光源前面已经讲的很全面了,只是缺少一些专门的例子。这里我们来稍微 加深一下认识,我们将在例子中看到一个地方的光源对不同物体发出不同的光 这在现实中是少见的吧? 1.双面光照: void glLightModeli(LIGHT_MODEL_TWO_SIDE,GL_TRUE); 光照计算通常是对多边形进行的。一般设置光照的条件总是对正面的多边形,而 不考虑背面,但是如果考虑一个物体被劈开,光源的个数又会影响可见性,那么 有必要对多边形双面都进行计算,这就是上面函数的功能;取消这一功能只须把 第三个参数改为 GL_FALSE。 2.聚光源 定义聚光源的函数仍是利用 glLightfv(),需要设定的参数包括:光源位置、光源 发散半角和光源聚光方向。 具体实现可以看下面例子: ////////////////////////////////////////////////////////// //sample.cpp #include "glos.h" #include #include #include "windows.h" void myinit(void); void CALLBACK display(void); void CALLBACK reshape(GLsizei w,GLsizei h); void initlights(void) { //定义物体材质特性的数值 GLfloat mat_ambient[]={0.2,0.2,0.2,1.0}; GLfloat mat_diffuse[]={0.8,0.8,0.8,1.0}; GLfloat mat_specular[]={1.0,1.0,1.0,1.0}; GLfloat mat_shininess[]={80.0}; //定义第 0 个光源的属性(这是平行的环境光,没有聚光效果) GLfloat light0_diffuse[]={0.0,0.0,1.0,1.0}; GLfloat light0_position[]={1.0,1.0,1.0,0.0}; //定义第一个光源的属性(聚光灯一) GLfloat light1_ambient[]={0.2,0.2,0.2,1.0}; GLfloat light1_diffuse[]={1.0,0.0,0.0,1.0}; GLfloat light1_specular[]={1.0,0.6,0.6,1.0}; GLfloat light1_position[]={-3.0,-3.0,3.0,1.0}; GLfloat spot_direction[]={1.0,1.0,-1.0}; //定义第二个光源的属性(聚光灯二) GLfloat light2_ambient[]={0.2,0.6,0.2,1.0}; GLfloat light2_diffuse[]={0.0,1.0,0.0,1.0}; GLfloat light2_specular[]={0.0,1.0,0.0,1.0}; GLfloat light2_position[]={-3.0,-3.0,3.0,1.0}; GLfloat spot2_direction[]={1.0,1.0,-1.0}; //!!!我们看到第一和第二个聚光源除了在颜色的定义上一个偏红,一个偏绿 //其他没有任何不同,所以如果象我们后面作的,对一个物体开启一个光源,对 //另一个物体开启另一个光源,就会产生一个点光源对不同物体发出不同光的效果 //将前面的属性定义加以应用 glLightfv(GL_LIGHT0,GL_DIFFUSE,light0_diffuse); glLightfv(GL_LIGHT0,GL_POSITION,light0_position); glLightfv(GL_LIGHT1,GL_AMBIENT,light1_ambient); glLightfv(GL_LIGHT1,GL_DIFFUSE,light1_diffuse); glLightfv(GL_LIGHT1,GL_SPECULAR,light1_specular); glLightfv(GL_LIGHT1,GL_POSITION,light1_position); //定义聚光灯发散角 glLightf(GL_LIGHT1,GL_SPOT_CUTOFF,30.0); //定义聚光灯发射方向的向量 glLightfv(GL_LIGHT1,GL_SPOT_DIRECTION,spot_direction); glLightfv(GL_LIGHT2,GL_AMBIENT,light2_ambient); glLightfv(GL_LIGHT2,GL_DIFFUSE,light2_diffuse); glLightfv(GL_LIGHT2,GL_SPECULAR,light2_specular); glLightfv(GL_LIGHT2,GL_POSITION,light2_position); glLightf(GL_LIGHT2,GL_SPOT_CUTOFF,30.0); glLightfv(GL_LIGHT2,GL_SPOT_DIRECTION,spot2_direction); 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_SHININESS,mat_shininess); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_LIGHT1); glEnable(GL_LIGHT2); } void myinit(void) { auxInitDisplayMode(AUX_SINGLE|AUX_RGBA); auxInitPosition(0,0,500,500); auxInitWindow("sample1"); glClearColor(0.0,0.0,0.0,0.0); glClear(GL_COLOR_BUFFER_BIT); glDepthFunc(GL_LESS); glEnable(GL_DEPTH_TEST); initlights(); // glShadeModel(GL_FLAT); } void CALLBACK reshape(GLsizei w,GLsizei h) { glViewport(0,0,w,h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); if(w<=h) glOrtho(-5.0,5.0,-5.0*(GLfloat)h/(GLfloat)w, 5.0*(GLfloat)h/(GLfloat)w,-5.0,5.0); else glOrtho(-5.0*(GLfloat)h/(GLfloat)w, 5.0*(GLfloat)h/(GLfloat)w,-5.0,5.0,-5.0,5.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void CALLBACK display(void) { glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); //首先标出聚光源一、二的位置(同一位置) glPushMatrix(); glTranslated(-3.0,-3.0,3.0); glDisable(GL_LIGHTING); glColor3f(1.0,0.0,0.0); auxWireCube(0.1); glEnable(GL_LIGHTING); glPopMatrix(); //关掉第二个光源,只启用第一个光源(红),绘制球体一 glPushMatrix(); glDisable(GL_LIGHT2); glTranslated(0.0,2.0,0.0); auxSolidSphere(2.0); glPopMatrix(); //关掉第一个光源,只启用第二个光源(绿),绘制球体二 glPushMatrix(); glDisable(GL_LIGHT1); glEnable(GL_LIGHT2); glTranslated(0.0,-2.0,0.0); auxSolidSphere(2.0); glPopMatrix(); glFlush(); } void main(void) { myinit(); auxReshapeFunc(reshape); auxMainLoop(display); } //end of sample /////////////////////////////////////////////////////////////// 一个现实中难以见到的情景出现了。还不快试试? 结束光源之前,再给出一个光源移动的例子,其中用到了鼠标消息的响应,实现 非常简单,以 OPENGL 的辅助库提供的方式调用一个 CALLBACK 函数即可,和以前 讲 的响应键盘输入的方法完全一样。 ////////////////////////////////////////////////////////////// //sample.cpp #include "glos.h" #include #include #include "windows.h" void myinit(void); void CALLBACK display(void); void CALLBACK reshape(GLsizei w,GLsizei h); //控制光源移动的变量 static int step=0; //鼠标响应的 CALLBACK 函数 void CALLBACK movelight(AUX_EVENTREC *event) { step=(step+15)%360; } void initlights(void) { GLfloat mat_ambient[]={0.2,0.2,0.2,1.0}; GLfloat mat_diffuse[]={0.8,0.8,0.8,1.0}; GLfloat mat_specular[]={1.0,1.0,1.0,1.0}; GLfloat mat_shininess[]={80.0}; GLfloat light0_diffuse[]={0.0,0.0,1.0,1.0}; GLfloat light0_ambient[]={0.2,0.5,0.5,1.0}; glLightfv(GL_LIGHT0,GL_DIFFUSE,light0_diffuse); glLightfv(GL_LIGHT0,GL_AMBIENT,light0_ambient); 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_SHININESS,mat_shininess); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); } void myinit(void) { auxInitDisplayMode(AUX_SINGLE|AUX_RGBA); auxInitPosition(0,0,500,500); auxInitWindow("sample1"); glClearColor(0.0,0.0,0.0,0.0); glClear(GL_COLOR_BUFFER_BIT); glDepthFunc(GL_LESS); glEnable(GL_DEPTH_TEST); initlights(); // glShadeModel(GL_FLAT); } void CALLBACK reshape(GLsizei w,GLsizei h) { glViewport(0,0,w,h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); if(w<=h) glOrtho(-5.0,5.0,-5.0*(GLfloat)h/(GLfloat)w, 5.0*(GLfloat)h/(GLfloat)w,-5.0,5.0); else glOrtho(-5.0*(GLfloat)h/(GLfloat)w, 5.0*(GLfloat)h/(GLfloat)w,-5.0,5.0,-5.0,5.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void CALLBACK display(void) { GLfloat position[]={0.0,0.0,1.5,1.0}; glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); glPushMatrix(); glTranslatef(0.0,0.0,-5.0); glPushMatrix(); //光源的位置只由旋转变量 step 决定,每按下鼠标左键一下,step 的值便会改变 //导致光源位置的改变 glRotated((GLdouble)step,-1.0,1.0,1.0); glLightfv(GL_LIGHT0,GL_POSITION,position); glTranslated(0.0,0.0,1.5); glDisable(GL_LIGHTING); glColor3f(1.0,1.0,0.0); auxWireSphere(0.25); glEnable(GL_LIGHTING); glPopMatrix(); auxSolidTorus(0.5,2.5); glPopMatrix(); glFlush(); } void main(void) { myinit(); auxMouseFunc(AUX_LEFTBUTTON,AUX_MOUSEDOWN,movelight); auxReshapeFunc(reshape); auxMainLoop(display); } //end of sample ///////////////////////////////////////////////////////////// 在例子中,黄色的小球表示当前光源的位置,它的旋转导致了环状体表面受光照部 分的光影的变化,每按下鼠标左键一下,光源就会作响应的旋转。 OPENGL 的特殊效果 1 融合 前面从未接触过透明或半透明的物体,因为我们从未启用过融合处理 所谓融合就是假设在 RGBA 模式下,源色为(Rs,Gs,Bs,As),目标色为 (Rd,Gd,Bd,Ad),源因子为(Sr,Sg,Sb,Sa),目的因子为(Dr,Dg,Db,Da) 则融合的最终效果为: (Rs*Sr+Rd*Dr,Gs*Sg+Gd*Dg,Bs*Sb+Bd*Db,As*Sa+Ad*Da) 然后再归一。公式挺复杂,不过仔细看看,跟平常的融合倒是定性一致。 关键就是如何设定融合因子(Sr,Sg,Sb,Sa)(Dr,Dg,Db,Da)来实现不同的融合效果 利用函数: void glBlendFunc(GLenum sfactor,GLenum dfactor); 其中两个参数可以取下面值: 取值 相关因子 计算后融合因子 GL_ZERO 源、目的 (0,0,0,0) GL_ONE 源、目的 (1,1,1,1) GL_DST_COLOR 源 (Rd,Gd,Bd,Ad) GL_SRC_COLOR 目的 (Rs,Gs,Bs,As) GL_ONE_MINUS_DST_COLOR 源 (1,1,1,1)-(Rd,Gd,Bd,Ad) GL_ONE_MINUS_SRC_COLOR 目的 (1,1,1,1)-(Rs,Gs,Bs,As) GL_SRC_ALPHA 源、目的 (As,As,As,As) GL_ONE_MINUS_SRC_ALPHA 源、目的 (1,1,1,1)-(As,As,As,As) GL_DST_ALPHA 源、目的 (Ad,Ad,Ad,Ad) GL_ONE_MINUS_DST_ALPHA 源、目的 (1,1,1,1)-(Ad,Ad,Ad,Ad) GL_SRC_ALPHA_SATURATE 源 (f,f,f,1)-min(As,1-Ad) 还要利用 glEnable(GL_BLEND) glDisable(gl_blend)来启用、关闭融合处理。 //////////////////////////////////////////////// //sample.cpp #include "glos.h" #include #include #include "windows.h" void myinit(void); void CALLBACK display(void); void CALLBACK reshape(GLsizei w,GLsizei h); void myinit(void) { auxInitDisplayMode(AUX_SINGLE|AUX_RGBA); auxInitPosition(0,0,500,500); auxInitWindow("sample1"); glClearColor(0.0,0.0,0.0,0.0); glClear(GL_COLOR_BUFFER_BIT); //设置融合效果并启用 glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); // glDepthFunc(GL_LESS); // glEnable(GL_DEPTH_TEST); glShadeModel(GL_FLAT); } void CALLBACK reshape(GLsizei w,GLsizei h) { glViewport(0,0,w,h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); if(w<=h) gluOrtho2D(0.0,1.0,0.0,1.0*(GLfloat)h/(GLfloat)w); else gluOrtho2D(0.0,1.0*(GLfloat)w/(GLfloat)h,0.0,1.0); glMatrixMode(GL_MODELVIEW); } void CALLBACK display(void) { glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); //三个不同颜色和透明度的方块重合,颜色融合的效果 glColor4f(1.0,0.0,0.0,0.7); glRectf(0.25,0.4,0.75,0.9); glColor4f(0.0,1.0,0.0,1.0); glRectf(0.1,0.1,0.6,0.6); glColor4f(0.0,0.0,1.0,0.3); glRectf(0.4,0.1,0.9,0.6); glFlush(); } void main(void) { myinit(); auxReshapeFunc(reshape); auxMainLoop(display); } //end of sample ///////////////////////////////////////////////////////// 你可以试试调节参数大小,体会一下个参数的含义。 2 反走样 Anti-aliasing 由于计算机以离散点生成图形,生成图形必然与真实景物存在差距,这种差距 表现为:直线或光滑曲面的锯齿、花纹失去原有色彩形状、细小物体在画面的 消失等。统统叫做走样。反走样可以减少这种情况。粗略设想一下,就是把原 来边界的地方锯齿部分用低饱和度的点补上,这样既不影响整体轮廓,又获得 较好的平滑效果。反走样前提供“提示”采用函数: void glHint(GLenum target,GLenum hint); 其中 hint 可以是:GL_FASTEST 给出最有效的选择 GL_NICEST 给出最高质量的选择 GL_DONT_CARE 没有选择 target 意义 GL_POINT_SMOOTH_HINT 指定点、 GL_LINE_SMOOTH_HINT 线、 GL_POLYGON_SMOOTH_HINT 多边形的采样质量 GL_FOG_HINT 指出雾化计算是按每个象素进行(GL_NICEST) 还是按每个顶点进行(GL_FASTEST) GL_PERSPECTIVE_CORRECTION_HINT 指定颜色纹理插值的质量 其中 GL_PERSPECTIVE_CORRECTION_HINT 用以纠正单纯线性插值带来的观察错 误。 当然最主要的工作还是 glEnable()来完成的。 先给出一个点、线反走样的例子。需要说明的是这个工作最好在 RGBA 模式下进行, 首先利用 glEnable()(参数为 GL_POINT_SMOOTH GL_LINE_SMOOTH 或 GL_POLYGON_SMOOTH)启用反走样。在 RGBA 模式下启用反走样,必须启用融 合处理。 而且最常用的融合因子分别是:GL_SRC_ALPHA(源)和 GL_ONE_MINUS_SRC_ALPHA 或 GL_ONE(目的)。 /////////////////////////////////////////////////////////////////////// //sample.cpp #include "glos.h" #include #include #include "windows.h" void myinit(void); void CALLBACK display(void); void CALLBACK reshape(GLsizei w,GLsizei h); void myinit(void) { auxInitDisplayMode(AUX_SINGLE|AUX_RGBA); auxInitPosition(0,0,500,500); auxInitWindow("sample1"); glClearColor(0.0,0.0,0.0,0.0); glClear(GL_COLOR_BUFFER_BIT); //例子所有新内容都在一下 4 句,你可以试试取消反走样的语句(1、4) //则可以很清楚的比较一下反走样对图象质量的改进。 glEnable(GL_LINE_SMOOTH); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); glHint(GL_LINE_SMOOTH_HINT,GL_DONT_CARE); glDepthFunc(GL_LESS); glEnable(GL_DEPTH_TEST); glShadeModel(GL_FLAT); } void CALLBACK reshape(GLsizei w,GLsizei h) { glViewport(0,0,w,h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); if(w<=h) glOrtho(-5.0,5.0,-5.0*(GLfloat)h/(GLfloat)w, 5.0*(GLfloat)h/(GLfloat)w,-5.0,5.0); else glOrtho(-5.0*(GLfloat)h/(GLfloat)w, 5.0*(GLfloat)h/(GLfloat)w,-5.0,5.0,-5.0,5.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void CALLBACK display(void) { glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); glLineWidth(5.0); glColor4f(1.0,1.0,0.0,0.7); glPushMatrix(); glRotatef(45.0,1.0,1.0,0.0); auxWireOctahedron(2.0); glPopMatrix(); glFlush(); } void main(void) { myinit(); auxReshapeFunc(reshape); auxMainLoop(display); } //end of sample /////////////////////////////////////////////////////////////////// 再给出一个多边形反走样的例子 /////////////////////////////////////////////////////////////////// //sample.cpp #include "glos.h" #include #include #include "windows.h" void myinit(void); void CALLBACK display(void); void CALLBACK reshape(GLsizei w,GLsizei h); void myinit(void) { auxInitDisplayMode(AUX_SINGLE|AUX_RGBA); auxInitPosition(0,0,500,500); auxInitWindow("sample1"); glClearColor(0.0,0.0,0.0,0.0); glClear(GL_COLOR_BUFFER_BIT); GLfloat mat_ambient[]={0.5,0.5,0.0,1.0}; GLfloat mat_diffuse[]={1.0,0.8,0.1,1.0}; GLfloat position[]={1.0,0.0,1.0,0.0}; glMaterialfv(GL_FRONT,GL_AMBIENT,mat_ambient); glMaterialfv(GL_FRONT,GL_DIFFUSE,mat_diffuse); glLightfv(GL_LIGHT0,GL_POSITION,position); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glCullFace(GL_BACK); glEnable(GL_CULL_FACE); //启用多边形反走样 glEnable(GL_POLYGON_SMOOTH); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA,GL_ONE); glDepthFunc(GL_LESS); glEnable(GL_DEPTH_TEST); glShadeModel(GL_FLAT); } void CALLBACK reshape(GLsizei w,GLsizei h) { glViewport(0,0,w,h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); if(w<=h) glOrtho(-5.0,5.0,-5.0*(GLfloat)h/(GLfloat)w, 5.0*(GLfloat)h/(GLfloat)w,-5.0,5.0); else glOrtho(-5.0*(GLfloat)h/(GLfloat)w, 5.0*(GLfloat)h/(GLfloat)w,-5.0,5.0,-5.0,5.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void CALLBACK display(void) { glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); glLineWidth(5.0); glColor4f(1.0,1.0,0.0,0.7); glPushMatrix(); glRotatef(45.0,1.0,1.0,0.0); auxSolidIcosahedron(2.0); glPopMatrix(); glFlush(); } void main(void) { myinit(); auxReshapeFunc(reshape); auxMainLoop(display); } //end of sample //////////////////////////////////////////////////////////////////// 比较采用反走样后的结果,可以明显看出多变形直线边界出锯齿得到了平滑。 3 雾化 最后来介绍雾化,雾化不但可以使景物更加真实,而且大大减少计算量,开过 F22 的玩家不会忘记雾化的远近直接影响游戏的速度吧。一般的雾化模型是考虑 把物体实际颜色和雾化颜色向融合。具体雾化的浓淡有定义的数学模型来决定 包括线性变化、指数变化和指数平方变化等。定义雾化也很简单,只要遵循下面 步骤: 一 启用雾化 glEnable(GL_FOG); 二 控制雾化 glFog*() void glFog{if}[v](GLenum,TYPE param); 当 GLenum 是 GL_FOG MODE 时,param 可以是 GL_EXP(指数) GL_EXP2(指数平方) GL_LINEAR(线性) 当 GLenum 是 GL_FOG_DENSITY GL_FOG_START GL_FOG_END 时,param 分别 指定不同 雾化数学模型下不同计算公式的参量,具体可以参阅连机手册。 当 GLenum 时 GL_FOG_COLOR 时,param 是指向颜色向量的指针 三 必要时可以用 glHint(GL_FOG_HINT,XX)指定雾化效果 下面给出例子: /////////////////////////////////////////////////////////////////// //sample.cpp #include "glos.h" #include #include #include "windows.h" void myinit(void); void CALLBACK display(void); void CALLBACK reshape(GLsizei w,GLsizei h); void myinit(void) { auxInitDisplayMode(AUX_SINGLE|AUX_RGBA); auxInitPosition(0,0,500,500); auxInitWindow("sample1"); glClearColor(0.0,0.0,0.0,0.0); glClear(GL_COLOR_BUFFER_BIT); GLfloat mat_ambient[]={0.0,0.1,0.8,1.0}; GLfloat mat_diffuse[]={0.0,0.3,0.6,1.0}; GLfloat mat_specular[]={1.0,0.0,1.0,1.0}; GLfloat mat_shininess[]={15.0}; GLfloat position[]={5.0,5.0,5.0,0.0}; GLfloat fogColor[4]={0.6,0.6,0.0,1.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_SHININESS,mat_shininess); glLightfv(GL_LIGHT0,GL_POSITION,position); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glFrontFace(GL_CW); // glEnable(GL_POLYGON_SMOOTH); // glEnable(GL_BLEND); // glBlendFunc(GL_SRC_ALPHA,GL_ONE); glDepthFunc(GL_LESS); glEnable(GL_DEPTH_TEST); //启用雾化处理 glEnable(GL_FOG); { //采用线性变化的雾化效果 glFogi(GL_FOG_MODE,GL_LINEAR); //指定雾化颜色(黄色) glFogfv(GL_FOG_COLOR,fogColor); //指定按线性变化时计算公式的参量 glFogf(GL_FOG_START,3.0); glFogf(GL_FOG_END,15.0); //规定雾化效果的质量 glHint(GL_FOG_HINT,GL_DONT_CARE); } // glShadeModel(GL_FLAT); } void CALLBACK reshape(GLsizei w,GLsizei h) { glViewport(0,0,w,h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); if(w<=h*3) glOrtho(-6.0,6.0,-2.0*(GLfloat)h*3/(GLfloat)w, 2.0*(GLfloat)h*3/(GLfloat)w,0.0,10.0); else glOrtho(-6.0*(GLfloat)h/(GLfloat)w, 6.0*(GLfloat)h/(GLfloat)w,-2.0,2.0,0.0,10.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void CALLBACK display(void) { glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); //在不同远近(Z 方向)绘制同样大小、颜色的环,显示雾化的效果 glPushMatrix(); glTranslatef(-3.0,-1.5,-3.0); auxSolidTorus(0.6,1.5); glPopMatrix(); glPushMatrix(); glTranslatef(-0.5,-0.5,-6.0); auxSolidTorus(0.6,1.5); glPopMatrix(); glPushMatrix(); glTranslatef(2.0,0.5,-8.0); auxSolidTorus(0.6,1.5); glPopMatrix(); glFlush(); } void main(void) { myinit(); auxReshapeFunc(reshape); auxMainLoop(display); } //end of sample //////////////////////////////////////////////////////////////////// OPENGL 的显示列表 (一)简介 所谓显示列表就是一组预选存储起来的留待以后调用的函数语句。调用此显示列表 时就按次序执行其中函数。以前所有的函数调用可以称之为立即方式(Immediate mode),现在我们将介绍显示列表方式(Display list)。 显示列表可以优化程序运行性能。它被设计成命令的高速缓存,而不是动态的数据 库缓存。一旦建立列表,删除前,则不能被修改,否则会带来系统效率的降低。 显示列表主要目的就是提高运行效率。例如任何一个几何变换的函数处于显示列表 中时,系统只保留最后的变换矩阵,这样调用时,工作量比现由函数参数生成数学 模型,再矩阵变换效率显然提高。一般来讲显示列表食用于: 一 矩阵操作 将最终变换矩阵保存以提高效率 二 光栅位图和图象编译后的列表与高级的程序数据不同,将是匹配硬件实现的数 据 三 光、材质和光照模型 省却复杂的模型计算 四 纹理 五 多边形的图案填充模式 注意以下函数一般不用于显示列表,作为用于传递参数或者返回数值的函数,则列 表有可能在参数作用域外被调用,因此即使列表中有这些函数,实际系统也往往按 照立即方式完成,有时甚至会出错。 glDeleteLists() glIsEnable() glFeedbackBuffer() glIsList() glFinish() glPixelStore() glGenLists() glRenderMode() glGet*() glSelectBuffer() (二)实现 创建列表(格式很类似 glBegin()...glEnd()) void glNewList(GLuint list,GLenum mode); 说明列表的开始,其后的函数存入列表直至结束标志(见后)。 mode:GL_COMPILE 只编译保存 GL_COMILE_AND_EXECUTE 编译保存后,再立即执行一次 void glEndList(void) 列表结束 要调用时,只需使用 void glCallList(GLuint list); 就象调用子过程一样简单 ///////////////////////////////////////////// 例如,你在某处定义: { GLuint listName=1; glNewList(listName,GL_COMPILE); glColor3f(1.0,0.0,0.0); glBegin(GL_TRIANGLES); ... glEnd(); glEndList(); } 再绘制函数中调用: { glCallList(listName); } 即可。 //////////////////////////////////////////////// (三)管理显示列表 上面的例子中我们指定乐使用 1 显示列表,万一这个索引已经被占用乐呢? 因此需要一套管理列表的工具。 void GLuint glGenList(GLsize range); 系统生成 range 个相临的未被占用的可用列表空间,返回其索引 若不能正常分配空间,返回 0 void GLboolean glIsList(GLuint list); 判断列表是否已经被占用 void GLDeleteLists(GLuint list,GLsizei range); 删除一组连续的显示列表,从 list 开始,连续删除 range 个。 说明:当以一个已经存在的索引创建列表时,以前的旧列表被删除。 一个安全的创建可以如下: listIndex=glGenLists(1); if(listIndex!=0) { glNewList(listIndex,GL_COMPILE); ... glEndList(); } (四)多级显示列表 显示列表中可以调用另一个显示列表 例如: 已经创建显示列表 1 2 3 现在创建显示列表 4 glNewList(4,GL_COMPILE); glBegin(GL_OLYGON); glCallList(1); glCallList(2); glCallList(3); glEnd(); glEndList(); 由于这部分内容没有任何视觉上的新享受,所以就不给出新例子乐。 大家可以试试自己修改前面的程序,至少我没看出太大差别,xixi。 OPENGL 帧缓存和动画 作为最后一关,我们将架设自己即时光影的动画,让没有 VOODOO 的玩家看看 OPENGL 这震撼(至少我是这么认为的吧)的效果,完成所有这将近 20 次灌水最终目标。 我们前面(好象是第三还是第四次)讲过如何用几何变换实现动画。那时的效果 现在看肯定不尽人意,因为频繁的闪烁不是我们需要的。因为那时(到这之前也 是)采用的是单缓存模式。对正在显示的部分边计算边修改必然造成速度瓶颈, 出现闪烁。一般正规的动画制作在实现上都是通过双缓存实现的(硬件也好,软 件也好)大家可以参考《家用电脑与游戏机》的 98-2 中的一篇文章。当前台显示 缓存用于显示时,后台缓存已经进行计算,计算完毕把所有内容通过缓存拷贝一 次性完成,防止闪烁的出现。 一 OPENGL 帧缓存的实现 1 颜色缓存(Color Buffer)其中内容可以是颜色索引或者 RGBA 数据,如果用的 OPENGL 系统支持立体图,则有左右两个缓存。 2 深度缓存(Depth Buffer) 就是 Z-BUFFER,用于保存象素 Z 方向的数值,深度 大的被深度小的代替,用以实现消隐 3 模板缓存(Stencil Buffer)用以保持屏幕上某些位置图形不变,而其他部分 重绘。例如大家熟悉的开飞机和赛车的游戏的驾驶舱视角,只有挡风外面的景物 变化,舱内仪表等等并不变化。 4 累计缓存(Accumulation Buffer) 只保存 RGBA 数据,用于合成图象,例如有某 缓存中的位图调入这里合成一幅新图。 二 帧缓存的清除 对高分辨率模式清除缓存是工作量巨大的任务,OPENGL 一般先寻求硬件同时完成, 否则软件依次解决。我们前面每次必用的 glClearColor()大家已经不陌生吧。 首先设置清除值 void glClearColor(GLclampf red,GLclampf green,GLclampf blue,GLclampf alpha); void glClearIndex(GLfloat index); void glClearDepth(GLclampd depth); void glClearStencil(GLint s); void glClerAccum(GLfloat red,GLfloat green,GLfloat blue,GLfloat alpha); 然后进行清除 void glClear(GLbitfield mask); mask: GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT| GL_STENCIL_BUFFER_BIT| GL_ACCUM_BUFFER_BIT 三 双缓存动画 你可以把所有的变换工作看成后台缓存的计算,然后把所有结果拷贝到前台即可。 因此我们只需两个新内容: 首先初始化时调用 auxInitDisplayMode(AUX_DOUBLE|AUX_RGBA); 用 AUX_DOUBLE 代替 AUX_SINGLE 设置成双缓存模式 然后在绘制完毕(glFlush();后)调用 auxSwapBuffers(); 进行缓存拷贝。Easy like sunday morning!! 当然不同系统下,这个函数也许不同(毕竟是辅助库函数么),例如 X-WINDOWS 下可以使用 glxSwapBuffers(),意思完全一样。 先说说下面这个例子的功能: 有一个兰色的环作为主体,有一个黄色高亮的球表示光源的位置。 小球不断从屏幕左方运动到右方,可以看出环状物上光影的变化。 操作: 鼠标左键/右键:开始/停止光源的运动 键盘 上/下/左/右:控制环状物的 前进/后退/旋转 ////////////////////////////////////////////////////////////////// //sample.cpp #include "glos.h" #include #include #include "windows.h" void myinit(void); void CALLBACK display(void); void CALLBACK reshape(GLsizei w,GLsizei h); void CALLBACK stepDisplay(void); void CALLBACK startIdleFunc(AUX_EVENTREC *event); void CALLBACK stopIdleFunc(AUX_EVENTREC *event); //step 是表示环状物旋转的参数;z 是控制其前后坐标的参数 static GLfloat step=0.0,z=0.0; //position 是控制光源的位置的参数 static GLfloat position[]={-20.0,0.0,-5.0,1.0}; void myinit(void) { //初始化注意是双缓存模式 auxInitDisplayMode(AUX_DOUBLE|AUX_RGBA); auxInitPosition(0,0,500,500); auxInitWindow("sample1"); glClearColor(0.0,0.0,0.0,0.0); glClear(GL_COLOR_BUFFER_BIT); glFrontFace(GL_CW); glEnable(GL_LIGHTING); glFrontFace(GL_CW); // glEnable(GL_POLYGON_SMOOTH); // glEnable(GL_BLEND); // glBlendFunc(GL_SRC_ALPHA,GL_ONE); glDepthFunc(GL_LESS); glEnable(GL_DEPTH_TEST); // glShadeModel(GL_FLAT); } void CALLBACK reshape(GLsizei w,GLsizei h) { glViewport(0,0,w,h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); /*if(w<=h*3) glOrtho(-2.0,2.0,-2.0*(GLfloat)h/(GLfloat)w, 2.0*(GLfloat)h/(GLfloat)w,-10.0,10.0); else glOrtho(-2.0*(GLfloat)h/(GLfloat)w, 2.0*(GLfloat)h/(GLfloat)w,-2.0,2.0,-10.0,10.0); */ //启用立体的视景,具有近大远小的效果 glFrustum(-6.0,6.0,-6.0,6.0,3.0,20.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void CALLBACK display(void) { glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); //首先在当前位置设置光源 glPushMatrix(); GLfloat light_ambient[]={0.3,0.5,0.3}; GLfloat light_diffuse[]={1.0,1.0,1.0}; GLfloat light_specular[]={0.8,0.8,0.0}; glLightfv(GL_LIGHT0,GL_AMBIENT,light_ambient); glLightfv(GL_LIGHT0,GL_DIFFUSE,light_diffuse); glLightfv(GL_LIGHT0,GL_SPECULAR,light_specular); glLightfv(GL_LIGHT0,GL_POSITION,position); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); //在光源旁边绘制黄色高亮的小球,标志光源位置 //小球和光源位置由 position[]决定 glTranslatef(position[0],position[1],position[2]-1.0); GLfloat mat_ambient[]={1.0,0.0,0.0,1.0}; GLfloat mat_diffuse[]={1.0,1.0,0.0,1.0}; GLfloat mat_specular[]={1.0,1.0,0.0,1.0}; GLfloat mat_shininess[]={50.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_SHININESS,mat_shininess); auxSolidSphere(0.5); glPopMatrix(); //在当前位置绘制兰色环状物,其位置由 step z 共同决定 glPushMatrix(); glTranslatef(0.0,0.0,-8.0+z); glRotatef(135.0+step,0.0,1.0,0.0); GLfloat mat2_ambient[]={0.0,0.0,1.0,1.0}; GLfloat mat2_diffuse[]={0.2,0.0,0.99,1.0}; GLfloat mat2_specular[]={1.0,1.0,0.0,1.0}; GLfloat mat2_shininess[]={50.0}; glMaterialfv(GL_FRONT,GL_AMBIENT,mat2_ambient); glMaterialfv(GL_FRONT,GL_DIFFUSE,mat2_diffuse); glMaterialfv(GL_FRONT,GL_SPECULAR,mat2_specular); glMaterialfv(GL_FRONT,GL_SHININESS,mat2_shininess); auxSolidTorus(2.0,3.5); glPopMatrix(); glFlush(); //绘制完毕,缓存交换 auxSwapBuffers(); } void CALLBACK Up(void) { //键盘“上”的处理 z=z+0.05; } void CALLBACK Down(void) { //键盘“下”的处理 z=z-0.05; } void CALLBACK Left(void) { //键盘“左”的处理 step=step+2.0; } void CALLBACK Right(void) { //键盘“右”的处理 step=step-2.0; } void CALLBACK stepDisplay(void) { //系统闲时的调用过程 position[0]=position[0]+0.5; if(position[0]>20.0) position[0]=-20.0; display(); } void CALLBACK startFunc(AUX_EVENTREC *event) { //鼠标左键的处理 auxIdleFunc(stepDisplay); } void CALLBACK stopIdleFunc(AUX_EVENTREC *event) { //鼠标右键的处理 auxIdleFunc(0); } void main(void) { myinit(); auxReshapeFunc(reshape); auxIdleFunc(stepDisplay); auxMouseFunc(AUX_LEFTBUTTON,AUX_MOUSEDOWN,startFunc); auxMouseFunc(AUX_RIGHTBUTTON,AUX_MOUSEDOWN,stopIdleFunc); auxKeyFunc(AUX_UP,Up); auxKeyFunc(AUX_DOWN,Down); auxKeyFunc(AUX_LEFT,Left); auxKeyFunc(AUX_RIGHT,Right); auxMainLoop(display); } //end of sample //////////////////////////////////////////////////////////////////// 其中用到大量 CALLBACK 函数,分别处理不同消息比较前面演示的动画效果,闪烁 的现象明显改善乐(你可以把这个程序两个关于双缓存的地方改成单缓存:设置 AUX_SINGLE 和去掉 auxSwapBuffers(),看看闪烁的多么厉害),至于本身的绘图 可能的拖尾现象,只能怪自己机器不好乐。 写到这里,终于打穿乐 OPENGL!!!实现当初提出的“长长见识”的设想。 今后有什么新内容我还会尽量补充,大家也可以自由补充。
还剩105页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 15 金币 [ 分享pdf获得金币 ] 8 人已下载

下载pdf

pdf贡献者

fmms

贡献于2011-01-01

下载需要 15 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf