第四章
...
}
最后按照参考书上的方法设置了全屏显示按钮,可以实现全屏显示,同时可以进行屏幕工具栏的显示缩放。【16】 编译运行结果如图4-6所示:
图4-6 设置完成的机器人三维场景框架
::wglDeleteContext( m_hRC); if (m_hPalette)
DeleteObject(m_hPalette); if ( m_pDC ) { }
delete m_pDC;
4.3 机械手三维模型的建立
OpenGL中模型的建立一般有两种思路,一种通过一个是用3dsMax绘制出模型并将其转换成特定格式的文件,然后导入到程序的函数绘制出的模型则比较粗糙,但是灵活性好,实现起来也比较容易。
中;另一个是直接用OpenGL的函数绘制出所需要的模型。导入的模型比较美观漂亮,但是实现起来技术难度比较大,而OpenGL
4.3.1 导入机械手模型
利用openGL建立复杂物体的三维模型是一件比较麻烦和枯燥的事情,因为OPenGL并没有提供三维模型的高级命令,它也是通
过基本的几何图元一点、线及多边形来建立三维立体模型的。而利用3dsMax建立复杂物体的模型则显得轻松的多。可是,用3dsMax
制作的动画没有交互性,无法实时控制,而这正是OpenGL的优势所在。把这些模型转换成OpenGL程序,再对其进行控制是一种比较理想的方法。所以,如果可以把OpenGL与3dsMax结合起来实现三维仿真则可以事半功倍。但是由于时间和知识的限制,
无法完成对3D模型的读取工作,所以查找了一些读取3D模型文件的代码,利用这些代码将机械手模型转换成了OpenGL程序,将模型导入进了OpenGL里。为了节约时间作者没有在3dsMax里建立机械手的模型,而是在比较熟悉的Auto CAD里建立了机械手的模型,然后通过3dsMax软件模型文件导出成接口程序可识别的*.ASE格式的文件,再通过接口程序导入进OpenGL里。导入前的在3dsMax里的模型如图4-7所示:
图4-7 3dsMax里的机械手模型
导入后的机械手模型经过坐标变换、缩放、改变颜色并加入光照照亮后如下图4-8所示: 图4-8 导入的机械手模型
23
第四章
4.3.2 在OpenGL中建立机械手的模型
虽然实现了模型的导入,但是只实现了整体导入,未能实现分块导入机械手的模型,所以在OpenGL只实现了整体模型的控制,很显然满足不了机械手运动的要求,所以导入的模型只能作为仿真场景的一部分做静态显示。为了继续进行后续工作,作者直接在OpenGL中通过基本的几何图元一点、线及多边形建立了机械手的三维立体模型。机械手模型主要由底座、腰部、关节、手臂这几个部分组成,由于底座、腰部和关节都是圆柱型的,所以作者先利用glu库函数(gluCylinder和gluDisk)【17】画出一个共用的圆柱,然后通过不同的缩放(glScalef)、转移(glTranslatef)、旋转(glRotatef)变换绘制出了底座,腰部和关节的模型。这里取绘制关节的函数作为例子介绍绘制过程,代码如下: void CMyView::joint() {
glPushMatrix();//模型入栈
GLUquadricObj *obj1;
obj1=gluNewQuadric();//创建一个描述当前绘图模式、定位、光照模式、纹理模式和回调函数的不透明的状态变量 glTranslatef(0.0,0.0,-5.0);//向Z轴负方向即屏幕里移动5个像素的距离
glPushMatrix();
gluCylinder(obj1,10.0,10.0,10.0,360.0,2);//绘制一个顶面半径、底面半径和高度都为10的圆柱面
glPopMatrix(); glPushMatrix(); gluDisk(obj1,0.0,10.0,360,1);// glPopMatrix();
绘制一个半径为10的圆片堵住上述圆柱面的底面
glPushMatrix(); glTranslatef(0.0,0.0,10);//向Z轴正方向即屏幕外移动10个距离
gluDisk(obj1,0.0,10.0,360,1);//绘制一个半径为10的圆片堵住上述圆柱面的顶面 glPopMatrix(); glPopMatrix();//模型出栈
}
绘制手臂也很简单,手臂其实在这里同样是通过对一个立方体进行不同的然后通过不同的缩放(glScalef)、转移(glTranslatef)、
旋转(glRotatef)变换来完成绘制。OpenGL里也有对应画立方体的库函数,但是由于不太清楚怎么对用库函数绘制的模型进行纹理贴图,所以选用了一种比较熟悉和简单的方式来绘制,就是通过6个面来组合成一个立方体。对每个面都可以进行不同的纹绘制立方体的代码如下: void CMyView::shoubi() {
glPushMatrix();
理贴图。在后面的仿真场景建立过程中同样利用这个四方体也完成了实验室房间、木质方桌、大理石长桌、凳子和储物箱的绘制。
24
第四章
glColor4f(1.0f,1.0f,1.0f,1.0f);//定义面的颜色为白色 glBegin(GL_QUADS);//开始绘制
// 前面 glTexCoord2f(0.0f, 0.0f); glVertex3f(-100.0f, 0.0f, -100.0f); // 纹理和四边形的左下 glTexCoord2f(1.0f, 0.0f); glVertex3f( 100.0f, 0.0f, -100.0f); // 纹理和四边形的右下 glTexCoord2f(1.0f, 1.0f); glVertex3f(100.0f, 100.0f, -100.0f); // 纹理和四边形的右上 glTexCoord2f(0.0f, 1.0f); glVertex3f(-100.0f, 100.0f, -100.0f); // 纹理和四边形的左上 glEnd();
glBegin(GL_QUADS); // 后面
glTexCoord2f(1.0f, 0.0f); glVertex3f(-100.0f, 0.0f, 100.0f); // 纹理和四边形的右下 glTexCoord2f(1.0f, 1.0f); glVertex3f(-100.0f, 100.0f, 100.0f); // 纹理和四边形的右上 glTexCoord2f(0.0f, 1.0f); glVertex3f( 100.0f, 100.0f, 100.0f); // 纹理和四边形的左上 glTexCoord2f(0.0f, 0.0f); glVertex3f( 100.0f, 0.0f,100.0f); // 纹理和四边形的左下
glEnd();
... glPopMatrix();
}
绘制完机械手的各个部件以后就可以通过缩放(glScalef)、转移(glTranslatef)、旋转(glRotatef)变换来完成整个机械手模型的绘制。代码如下:
void CMyView::manipulator() {
glPushMatrix();
glTranslatef(0.0,-80.0,-20.0);//分别沿Y和X轴负方向移动80和20
//glRotatef(0.0,1.0,0.0,0.0);
glRotatef(-hand[i].rot1,0.0,1.0,0.0);//绕Y轴旋转-hand[i].rot1(第一个关节角 第i时刻的值) glPushMatrix();
glTranslatef(0.0,-15.0,0.0);
glRotatef(-90,1.0,0.0,0.0);//沿X轴逆时针旋转90° glPushMatrix();
glTranslatef(0.0,0.0,20.0);
glColor3f(1.0,0.0,0.0);//设置为红色
glScalef(1.0,1.0,5.0);//沿新的Z轴方向扩大5倍 joint();//绘制腰部 glPopMatrix();
25
第四章
glTranslatef(0.0,0.0,-5.0);
glScalef(3.0,3.0,0.5);//沿新的Y和X轴扩大3倍,新的Z轴方向缩小0.5倍 glColor3f(0.0,1.0,0.0);//设置为绿色 joint();//绘制底座
glPopMatrix();
...//同样的方法绘制关节和两个手臂,这里不再一一叙述
}
在RenderScene()里调用manipulator(),编译运行后的结果如图4-9所示:
图4-9 OpenGL中绘制的机械手模型
4.4 建立仿真场景
场景中模型的绘制和上节机械手模型的绘制很类似,所以在这里不再做介绍,这一节主要介绍纹理贴图的实现和设置光照。
4.4.1 纹理贴图的实现
OpenGL体系内有一块纹理内存,在有硬件加速的情况下,可能是位于显卡的VRAM里,否则会是OpenGL库管理的一块内存。在这个纹理内存里图片是以特定的内部格式保存的,有的显卡还支持压缩纹理技术,所以将纹理像素从应用程序内存传到纹理内存需要进行格式转换。这在OpenGL中是通过分别描述像素在应用程序内存和纹理内存的格式来完成的,真正转换工作OpenGL会在内部完成。定义纹理的命令是
glTexImage2/1D(GL_TEX_IMAGE_2/1D,level,components,width,height,border,format,type,*pixels );OpenGL术语称应用程序内存读出像素的过程为解码(UNPACK),而向纹理内存写像素的过程为编码(PACK)。用glPixelStore*(GL_[UN]PACK_*,参数值);命令设定
编码[解码]格式 。对于贴纹理过程我们只需关心解码过程。如今的显卡通常都有比较大的显存,其中有一部份是专门的纹理存储
区,有的卡还可以将最多64M系统内存映射为纹理内存,所以我们有可能把经常要用的纹理就保留在纹理内存里以提高程序性能。OpenGL从1.2开始提供了纹理对象技术,可以把在管道内放多个纹理,每个纹理对应一个数字(名字),需要用到是把这个名
字的纹理Bind为当前纹理就可以了。用glGenTextures (n,*textures)命令取得可用的纹理名字的。
在3D图形中,纹理映射是广泛使用的。纹理映射也是相当复杂的过程:1、 定义纹理;2、控制滤波;3、说明映射方式;4、绘制场景给出顶点的纹理坐标和几何坐标。(注意!!纹理映射只能在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表示多级分辨率的纹理图象B A;
的级数,若只有一种分辨率,level为0;components是从1到4的整数,1:选择R;2:选择R A;3:选择R G B;4:选择R G
26
第四章
width height是纹理的尺寸。format和type描述映射格式和数据类型。它们与前面讲glDrawPixels()中
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,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,说的数组。这些值只在采用GL_BLEND纹理函数时才采用。 4. 纹理坐标
坐标的定义:纹理图象是方形的,纹理坐标可定义成s,t,r,q坐标,仿照齐次坐标系的x,y,z,w坐标。void glTexCoord{1234}{sifd}[v](TYPE coords);设置当前纹理坐标,此后调用glVertex*()所产生的顶点都赋予当前的纹理坐标。
明纹理值与原来颜色不同的处理方式。pname是GL_TEXTURE_ENV_COLOR,则参数param是包含4个浮点数(R、G、B、A)
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时一个指向参数数组的指针。【16】
下面就本文中纹理贴图的例子做介绍:首先定义了一个读入位图的函数BOOL LoadBMP(TCHAR* szFileName),在位图函数中完成了纹理的定义;然后又定义了一个函数GLLoadTextures(GLuint* ptList)完成了对纹理的滤波、重复和缩限的控制,最后在场景模型的绘制中将纹理坐标赋给了模型顶点坐标。下面是本文中纹理贴图的部分代码: CLoadBMP::GLLoadTextures(GLuint* ptList) {
glGenTextures(18, ptList);//生成16个纹理名字
27
百度搜索“77cn”或“免费范文网”即可找到本站免费阅读全部范文。收藏本站方便下次阅读,免费范文网,提供经典小说综合文库大学电脑作业:大学论文(50页)(7)在线全文阅读。
相关推荐: