第3章图形学 输入及交互_第1页
第3章图形学 输入及交互_第2页
第3章图形学 输入及交互_第3页
第3章图形学 输入及交互_第4页
第3章图形学 输入及交互_第5页
已阅读5页,还剩61页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、第三章输入与交互基本内容常见的输入设备物理设备逻辑设备输入模式事件驱动的输入光滑动画中的双缓存用GLUT进行事件输入编程Sketchpad项目Ivan Sutherland (MIT 1963年)建立了刻划交互式计算机图形学的基本框架: 用户在显示器上看到一个对象用户利用输入设备(光笔、鼠标、跟踪球等)点选该对象对象发生了改变(移动、旋转、变形等)重复上述过程图形输入设备输入设备可以用下述两种属性中的一种进行描述述 物理属性鼠标键盘跟踪球逻辑属性设备通过API返回给程序的内容是什么?位置对象标识模式如何以及何时获取输入?请求(主动)或者事件(被动)物理设备物理设备输入值 类似于数据板等设备直接

2、向操作系统返回位置 类似于鼠标、跟踪球以及游戏操纵杆等设备向操作系统返回一个步进输入(即速度)必须把这些输入累加在一起得到一个绝对位置 鼠标滚轮的旋转 跟踪球的滚动 难以得到绝对位置 输入可能相当敏感逻辑设备 考虑C与C+代码: C+: cinx; C: scanf(”%d”, &x); 输入设备是什么? 代码中并没有指明 可以是键盘、文件、或者其它程序的输出 上述代码要求的是就是逻辑输入 不管物理设备是什么,程序得到一个数(int)作为输入图形逻辑设备 相对于在标准程序中的输入通常只是数字、字 符或者字节而言,图形输入的形式更多 在GKS和PHIGS这两个老的API中,定义了六 种类

3、型的逻辑输入: 定位:返回一个位置 选取:返回对象的标识ID 键盘:返回字符串 轨迹:返回位置数组 求值:返回浮点数 选择:返回n项中的一项X Window的输入 在X Window系统中引入了适用于工作站网络的 客户-服务模型 客户 client: OpenGL程序 图形服务器:具有点选设备和键盘的位图显示设备客户-服务体系输入模式 在输入设备中具有一个触发器(trigger), 它可以向操作系统发送一个信息 鼠标上的按钮 按放键盘上的键 当触发后,输入设备向系统返回信息(相 应的度量) 鼠标返回位置信息 键盘返回ASCII代码请求模式 只有当用户触发了设备后,输入才提供给程序 键盘输入为典

4、型例子 在按回车键(触发器)之前,可以删除(回退)、编辑、修 改输入的信息事件模式 绝大部分系统具有多个输入设备,每个设备都 可能被用户在任意时间触发 每个触发生成一个事件,事件的度量信息放到 事件队列中,用户程序检查该队列事件类型 窗口:改变尺寸、重新显示、缩成图标 鼠标:点击一个或多个按钮,移动 键盘:按下或释放某个键 空闲:“没有事件” 可以定义如果队列中没有其它事件就可以进行 的某种操作回调 回调是事件驱动输入方式的程序界面 为图形系统可以识别的每种类型事件定义 一个回调函数 当相应的事件出现时,就会自动执行用户 指定的函数 GLUT示例 glutMouseFunc(mymouse);

5、GLUT中的回调函数 GLUT识别在各种窗口系统(Windows, X, Macintosh)中都有的一组事件 glutDisplayFunc glutMouseFunc glutReshapeFunc glutKeyboardFunc glutIdleFunc glutMotionFunc glutPassiveMotionFuncGLUT中的事件循环 记住: 在任何利用GLUT的程序中,main()的最 后一行实质语句必须是 glutMainLoop(); 这条语句使得程序进入一个无穷的事件循环 每经过事件循环一次,GLUT进行下述操作 查看事件队列中的事件 对于在队列中的每个事件,如果定

6、义了相应的回调函数, GLUT就执行这个回调函数 如果对该事件没有定义回调函数,那么就忽略该事件显示回调函数 只要GLUT确定需要刷新窗口,那么就会执行 显示回调函数,例如 当第一次打开窗口的时候 当改变了窗口形状的时候 当重新露出了窗口的时候 当用户程序决定需要改变显示内容的时候 在main()中, glutDisplayFunc(mydisplay)确定要执行的显示 回调整函数 每个GLUT程序都必须有一个显示回调函数,必要时可 以设置为空函数标识重新显示 许多事件都会导致调用显示回调函数 这会导致遍历一次事件循环的过程中多次执行显示回调 函数 可以用下列方法避免这个问题 glutPost

7、Redisplay(); 这条语句设置一个标志 当事件循环结束时,GLUT会检查是否设置了上 述标志 如果设置了标志,那么就会执行显示回调函数动画 当通过显示回调函数重新绘制显示结果时,我 们通常会首先清除整个窗口: glClear() 然后再绘制已发生了变化的显示结果 问题:帧缓冲区中的信息在显示器上的显示结 果出现了错位 图形系统可以同时向显存中写入内容和从中读出内容 从而我们会看到部分显示内容 例:single_double.c 旋转的立方体 显卡若启动了硬件加速功能,不会发现上述问题双缓存 不只用一个颜色缓冲区,而是应用两个缓冲区 前缓冲区:显示它的内容,但不向它写入内容 后缓冲区:写

8、入内容,不显示 程序在main()中请求使用双缓存 glutInitDisplayMode(GLUT_RGB|GLUT_DOUBLE) 在显示回调函数结束之前,交换两个缓冲区 void mydisplay() glClear(); / 绘制图形 glutSwapBuffers(); 空闲回调的应用当在事件队列中没有事件时,就执行该回调函数glutIdleFunc(myidle)在动画中非常有用void myidle() /改变一些内容t += dt;glutPostRedisplay();void mydisplay() glClear();/ 根据t绘制图形glutSwapBuffers()

9、;全局变量的应用 所有GLUT回调函数的调用形式是固定的 void mydisplay(); void mymouse(GLint button, GLint state, GLint x, GLint y); 为了向回调函数传递信息,必须应用全局变量 float t; /全局变量 void mydisplay() /根据t绘制图形 回调的应用 利用GLUT回调函数设计交互程序 鼠标 键盘 改变窗口形状 在GLUT中定义菜单鼠标回调函数 glutMouseFunc(mymouse) void mymouse(GLint button, GLint state, GLint x, GLint y

10、) 其中button的值可能是 GLUT_LEFT_BUTTON, GLUT_MIDDLE_BUTTON, GLUT_RIGHT_BUTTON 表示哪个按钮导致了事件发生 state表示相应按钮的状态: GL_UP, GL_DOWN x, y表示在窗口中的位置定位在屏幕上的位置通常是以像素为单位的,原点在左上角 因为显示器自顶向下刷新显示内容 在OpenGL中应用一个世界坐标系,其原点在左下角 在这个坐标系中的y坐标需要从窗口高度中减去回调函数返回的y值:y := h-y获取窗口尺寸 为了完成y坐标的转换,需要知道窗口的 尺寸 在程序执行过程中高度可能发生改变 需要利用一个全局变量跟踪其变化

11、新高度值返回给形状改变回调函数(见后) 也可以用查询函数glGetIntv和 glGetFloatv获取,因为高度是状态的一部 分结束程序 在以前的程序中没有办法通过OpenGL结 束当前程序 可以利用简单的鼠标回调函数做到这一点 void mouse(GLint btn, GLint state, GLint x, GLint y) if(btn=GLUT_RIGHT_BUTTON & state = GLUT_DOWN) exit(0); 鼠标位置的应用 在要构造的例子中,每单击一次鼠标左按 钮,就会在当前鼠标位置处画一个小方框 在这个例子中并没有用到显示回调函数, 但是由于GLU

12、T要求必须有一个显示回调 函数,因此要定义一个空函数 mydisplay() 在指针处画方框void mymouse(GLint btn, GLint state, GLint x, GLint y) if(btn = GLUT_RIGHT_BUTTON & state = GLUT_DOWN) exit(0);if(btn = GLUT_LEFT_BUTTON & state = GLUT_DOWN)drawSquare(x, y);void drawSquare(GLint x, GLint y) y = h-y; /转化y坐标glColor3ub( (char)rand(

13、)%256, (char) rand()%256,(char)rand()%256);glBegin(GL_POLYGON);glVertex2f(x - size, y - size);glVertex2f(x + size, y - size);glVertex2f(x + size, y + size);glVertex2f(x - size, y + size);glEnd();鼠标移动回调函数的应用 通过利用移动回调函数可以在不释放鼠标 按钮的情况下,连续画一系列方框 (square.c) glutMotionFunc(drawSquare) 应用被动移动回调函数,可以不用按鼠标 按

14、钮就可以连续画方框 glutPassiveMotionFunc(drawSquare)键盘的应用 glutKeyboardFunc(mykey) void mykey(GLubyte key, GLint x, GLint y) 返回键盘上被按下键的ASCII码和鼠标位置 注意在GLUT中并不把释放键做为一个事件 void mykey(GLubyte key, GLint x, GLint y) if(key = Q | key = q) exit(0); 特殊按键 GLUT在glut.h中定义了特殊按键: 功能键1: GLUT_KEY_F1 向上方向键:GLUT_KEY_UP if(key

15、= GLUT_KEY_F1) 其它键 可以利用glutGetModifiers()探测是否按下了 GLUT_ACTIVE_SHIFT, GLUT_ACTIVE_CTRL, GLUT_ACTIVE_ALT 由此可以利用单钮或双钮鼠标模拟三钮鼠标窗口形状的改变 通过拖动窗口的角点可以改变窗口的形状 和尺寸 那么其中的显示内容该如何处理? 必须由应用程序重新绘制 有两种可能性 显示原来内容的一部分 通过强迫适应新窗口来显示所有内容 可能会改变了显示长宽比率形状改变的可能性形状改变的回调函数 glutReshapeFunc(myreshape) void myreshape(GLint w, GLin

16、t h) 返回新窗口以像素为单位的宽度与高度 在回调函数执行后自动发送重新显示的事件 GLUT有一个缺省的形状改变的回调函数,但用户可能 需要定义自己的行为 这个回调函数是放置照相机函数的恰当地方, 因为当窗口第一次被打开时就会调用这个函数例子在下面的例子中通过改变窗和世界窗口使得长宽比率不变void myreshape(GLint w, GLint h) glViewport(0, 0, w, h);glMatrixMode(GL_PROJECTION); /*switch matrix mode */glLoadIdentity();if(w=h)gluOrtho2D(-2.0, 2.0,

17、 -2.0*(GLfloat)h/(GLfloat)w,2.0*(GLfloat)h/(GLfloat)w);else gluOrtho2D(-2.0*(GLfloat)w/(GLfloat)h,2.0*(GLfloat)w/(GLfloat)h, -2.0, 2.0);glMatrixMode(GL_MODELVIEW); /*return to modelview mode*/窗口资源 许多窗口系统提供了一个工具包或者一组库函 数用来建立用户界面,界面中用到了一些特殊 类型的窗口,称为资源构件(widget) 构件集合中包含下述工具: 菜单 滑动条 对话框 输入文本框 但上述构件通常都是与

18、平台相关的 GLUT只提供了包含菜单在内的很少几个构件菜单 GLUT支持弹出式菜单 可以有子菜单 创建弹出式菜单的三个步骤 定义菜单内各条目 定义每个菜单项的行为 如果条目被选择执行的操作 把菜单连接到鼠标按钮上定义一个简单菜单 在main()中 menu_id = glutCreateMenu(mymenu); glutAddMenuEntry(“Clear Screen”, 1); glutAddMenuEntry(“Exit”, 2); glutAttachMenu(GLUT_RIGHT_BUTTON);菜单的行为 菜单回调函数 void mymenu(GLint id) if(id=1

19、) glClear(); if(id=2) exit(0); 注意在创建菜单时每项都有一个标识符 添加子菜单 glutAddSubMenu(char *submenu_name, GLint submenu_id)子菜单 void createGLUTMenus() int menu,submenu; submenu = glutCreateMenu(processMenuEvents); glutAddMenuEntry(Red,RED); glutAddMenuEntry(Blue,BLUE); glutAddMenuEntry(Green,GREEN); menu = glutCreat

20、eMenu(processMenuEvents); glutAddMenuEntry(White,WHITE); glutAddSubMenu(RGB Menu,submenu); glutAttachMenu(GLUT_RIGHT_BUTTON); GLUT中其它的函数 动态窗口 在执行期间创建或关闭窗口 子窗口 多重窗口 在执行期间改变回调函数 计时器 字体 glutBitmapCharacter glutStrokeCharacterGLUT教程 请访问 http:/ 主要内容学会设计更复杂的交互程序,采用的方法为为 选取 从显示结果中选择对象 三种方法 橡皮条 直线与矩形的交互绘制方法

21、 显示列表 图形的记忆模式选取(picking) 从显示结果中识别用户定义的对象 原则上说要达到这个目标是很简单的,因 为鼠标可以提供位置信息,我们因当能够 根据位置确定对应的是哪个对象 实际操作中的困难 流水线式的图形体系是单向的,很难从二维屏 幕返回到三维世界 二维屏幕,三维世界,也使问题变得复杂 离位置多近可以认为选择了对象?三种方法击中(hit)列表最一般性的方法,实现也最困难 当绘制对象时,利用后缓冲区或者其它缓冲区存贮对象的标识矩形映射(容易实现)显示输出模式 利用glRenderMode(mode)可以设置 OpenGL用下述三种模式显示内容 GL_RENDER: 正常显示到帧缓

22、冲区中(缺省模 式) GL_FEEDBACK:提供要显示的几何列表,但 并不输出到帧缓冲区中 GL_SELECTION:在视景体中的每个几何体创 建一个击中记录,这个记录放到名称堆栈中, 稍后要被检测显示输出模式 利用glRenderMode(mode)可以设置 OpenGL用下述三种模式显示内容 GL_RENDER: 正常显示到帧缓冲区中(缺省模 式) GL_FEEDBACK:提供要显示的几何列表,但 并不输出到帧缓冲区中 GL_SELECTION:在视景体中的每个几何体创 建一个击中记录,这个记录放到名称堆栈中, 稍后要被检测选择模式中用到的函数 glSelectBuffer(): 指定名

23、称缓冲区 glInitNames(): 初始化名称缓冲区 glPushName(id): 把id放到名称缓冲区中 glPopName(): 把名称缓冲区顶部的名称弹出 glLoadName(id): 取代在名称缓冲区顶部的 名称 id是由应用程序设置,用以识别对象选择模式的应用 初始化名称缓冲区 进入选择模式 (例如:应用鼠标) 显示输出场景,场景中对象具有用户指定 的标识 重新进入正常的显示输出模式 该操作返回击中对象个数 检查名称缓冲区的内容(击中记录) 击中记录包含id与深度信息选择模式与选取 在选择模式中不能进行选取,因为这时在 视景体中的每个对象都会生成一个击中记 录 改变视图参数,

24、使得只有靠近鼠标指针的 对象位于新的视景体中 利用glutPickMatrix()应用屏幕上的区域 在许多应用程序中,屏幕采用简单的矩形 分划 例如:这时,相比于应用选择模式进行选取而言,通过查看鼠标位置,确定屏幕的区域就更简单了通过另外的缓冲区和颜色进行选取 对于很少数目的对象,可以给每个对象赋 以唯一的颜色(有时是在颜色索引模式中) 然后把场景输入到不是前缓冲区的一个颜 色缓冲区中,这样就不会看到显示出来的 结果 然后获到鼠标位置,利用 glReadPixels()读取缓冲区中的颜色 根据返回的颜色确定是哪个对象像素的写入模式XOR写入模式 缺省的写入模式是直接用源像素取代目标像素, 即d

25、=s 用这种方法不能绘制一条临时直线,因为我们不能用快 速简单的方法恢复在临时直线下面的内容 排它型OR操作(XOR):d = d s 相同值为0,不同值为1 (x y) y = x 因此如果应用XOR模式画一条直线,那么只要在原地再 画一遍这条直线就可以删除这条直线橡皮条式绘图 切换到XOR写入模式 绘制对象 利用第一次单击鼠标固定线段的一个端点,然后再利用 鼠标移动的回调函数连续更新第二个端点 每次鼠标移动的时候,重新一遍原来的直线从而删除它, 然后再从固定的第一个端点到新的第二个端点之间画一 条直线 最后切换回到正常的绘图模式并绘制直线 也适用于其它对象:矩形、圆橡皮条型直线OpenGL中的逻辑操作 在两位之间有16种可能的逻辑操作 OpenGL支持所有的这16种操作 必须首先启动逻辑操作 glEnable(GL_COLOR_LOGIC_OP) 然后选择逻辑操作

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论