第3章圖形學 輸入及交互_第1頁
第3章圖形學 輸入及交互_第2頁
第3章圖形學 輸入及交互_第3頁
第3章圖形學 輸入及交互_第4頁
第3章圖形學 輸入及交互_第5頁
已閱讀5頁,還剩61頁未讀 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領

文檔簡介

1、第三章輸入與交互基本內(nèi)容常見的輸入設備物理設備邏輯設備輸入模式事件驅(qū)動的輸入光滑動畫中的雙緩存用GLUT進行事件輸入編程Sketchpad項目Ivan Sutherland (MIT 1963年)建立了刻劃交互式計算機圖形學的基本框架: 用戶在顯示器上看到一個對象用戶利用輸入設備(光筆、鼠標、跟蹤球等)點選該對象對象發(fā)生了改變(移動、旋轉(zhuǎn)、變形等)重復上述過程圖形輸入設備輸入設備可以用下述兩種屬性中的一種進行描述述 物理屬性鼠標鍵盤跟蹤球邏輯屬性設備通過API返回給程序的內(nèi)容是什么?位置對象標識模式如何以及何時獲取輸入?請求(主動)或者事件(被動)物理設備物理設備輸入值 類似于數(shù)據(jù)板等設備直接

2、向操作系統(tǒng)返回位置 類似于鼠標、跟蹤球以及游戲操縱桿等設備向操作系統(tǒng)返回一個步進輸入(即速度)必須把這些輸入累加在一起得到一個絕對位置 鼠標滾輪的旋轉(zhuǎn) 跟蹤球的滾動 難以得到絕對位置 輸入可能相當敏感邏輯設備 考慮C與C+代碼: C+: cinx; C: scanf(”%d”, &x); 輸入設備是什么? 代碼中并沒有指明 可以是鍵盤、文件、或者其它程序的輸出 上述代碼要求的是就是邏輯輸入 不管物理設備是什么,程序得到一個數(shù)(int)作為輸入圖形邏輯設備 相對于在標準程序中的輸入通常只是數(shù)字、字 符或者字節(jié)而言,圖形輸入的形式更多 在GKS和PHIGS這兩個老的API中,定義了六 種類

3、型的邏輯輸入: 定位:返回一個位置 選取:返回對象的標識ID 鍵盤:返回字符串 軌跡:返回位置數(shù)組 求值:返回浮點數(shù) 選擇:返回n項中的一項X Window的輸入 在X Window系統(tǒng)中引入了適用于工作站網(wǎng)絡的 客戶-服務模型 客戶 client: OpenGL程序 圖形服務器:具有點選設備和鍵盤的位圖顯示設備客戶-服務體系輸入模式 在輸入設備中具有一個觸發(fā)器(trigger), 它可以向操作系統(tǒng)發(fā)送一個信息 鼠標上的按鈕 按放鍵盤上的鍵 當觸發(fā)后,輸入設備向系統(tǒng)返回信息(相 應的度量) 鼠標返回位置信息 鍵盤返回ASCII代碼請求模式 只有當用戶觸發(fā)了設備后,輸入才提供給程序 鍵盤輸入為典

4、型例子 在按回車鍵(觸發(fā)器)之前,可以刪除(回退)、編輯、修 改輸入的信息事件模式 絕大部分系統(tǒng)具有多個輸入設備,每個設備都 可能被用戶在任意時間觸發(fā) 每個觸發(fā)生成一個事件,事件的度量信息放到 事件隊列中,用戶程序檢查該隊列事件類型 窗口:改變尺寸、重新顯示、縮成圖標 鼠標:點擊一個或多個按鈕,移動 鍵盤:按下或釋放某個鍵 空閑:“沒有事件” 可以定義如果隊列中沒有其它事件就可以進行 的某種操作回調(diào) 回調(diào)是事件驅(qū)動輸入方式的程序界面 為圖形系統(tǒng)可以識別的每種類型事件定義 一個回調(diào)函數(shù) 當相應的事件出現(xiàn)時,就會自動執(zhí)行用戶 指定的函數(shù) GLUT示例 glutMouseFunc(mymouse);

5、GLUT中的回調(diào)函數(shù) GLUT識別在各種窗口系統(tǒng)(Windows, X, Macintosh)中都有的一組事件 glutDisplayFunc glutMouseFunc glutReshapeFunc glutKeyboardFunc glutIdleFunc glutMotionFunc glutPassiveMotionFuncGLUT中的事件循環(huán) 記住: 在任何利用GLUT的程序中,main()的最 后一行實質(zhì)語句必須是 glutMainLoop(); 這條語句使得程序進入一個無窮的事件循環(huán) 每經(jīng)過事件循環(huán)一次,GLUT進行下述操作 查看事件隊列中的事件 對于在隊列中的每個事件,如果定

6、義了相應的回調(diào)函數(shù), GLUT就執(zhí)行這個回調(diào)函數(shù) 如果對該事件沒有定義回調(diào)函數(shù),那么就忽略該事件顯示回調(diào)函數(shù) 只要GLUT確定需要刷新窗口,那么就會執(zhí)行 顯示回調(diào)函數(shù),例如 當?shù)谝淮未蜷_窗口的時候 當改變了窗口形狀的時候 當重新露出了窗口的時候 當用戶程序決定需要改變顯示內(nèi)容的時候 在main()中, glutDisplayFunc(mydisplay)確定要執(zhí)行的顯示 回調(diào)整函數(shù) 每個GLUT程序都必須有一個顯示回調(diào)函數(shù),必要時可 以設置為空函數(shù)標識重新顯示 許多事件都會導致調(diào)用顯示回調(diào)函數(shù) 這會導致遍歷一次事件循環(huán)的過程中多次執(zhí)行顯示回調(diào) 函數(shù) 可以用下列方法避免這個問題 glutPost

7、Redisplay(); 這條語句設置一個標志 當事件循環(huán)結(jié)束時,GLUT會檢查是否設置了上 述標志 如果設置了標志,那么就會執(zhí)行顯示回調(diào)函數(shù)動畫 當通過顯示回調(diào)函數(shù)重新繪制顯示結(jié)果時,我 們通常會首先清除整個窗口: glClear() 然后再繪制已發(fā)生了變化的顯示結(jié)果 問題:幀緩沖區(qū)中的信息在顯示器上的顯示結(jié) 果出現(xiàn)了錯位 圖形系統(tǒng)可以同時向顯存中寫入內(nèi)容和從中讀出內(nèi)容 從而我們會看到部分顯示內(nèi)容 例:single_double.c 旋轉(zhuǎn)的立方體 顯卡若啟動了硬件加速功能,不會發(fā)現(xiàn)上述問題雙緩存 不只用一個顏色緩沖區(qū),而是應用兩個緩沖區(qū) 前緩沖區(qū):顯示它的內(nèi)容,但不向它寫入內(nèi)容 后緩沖區(qū):寫

8、入內(nèi)容,不顯示 程序在main()中請求使用雙緩存 glutInitDisplayMode(GLUT_RGB|GLUT_DOUBLE) 在顯示回調(diào)函數(shù)結(jié)束之前,交換兩個緩沖區(qū) void mydisplay() glClear(); / 繪制圖形 glutSwapBuffers(); 空閑回調(diào)的應用當在事件隊列中沒有事件時,就執(zhí)行該回調(diào)函數(shù)glutIdleFunc(myidle)在動畫中非常有用void myidle() /改變一些內(nèi)容t += dt;glutPostRedisplay();void mydisplay() glClear();/ 根據(jù)t繪制圖形glutSwapBuffers()

9、;全局變量的應用 所有GLUT回調(diào)函數(shù)的調(diào)用形式是固定的 void mydisplay(); void mymouse(GLint button, GLint state, GLint x, GLint y); 為了向回調(diào)函數(shù)傳遞信息,必須應用全局變量 float t; /全局變量 void mydisplay() /根據(jù)t繪制圖形 回調(diào)的應用 利用GLUT回調(diào)函數(shù)設計交互程序 鼠標 鍵盤 改變窗口形狀 在GLUT中定義菜單鼠標回調(diào)函數(shù) glutMouseFunc(mymouse) void mymouse(GLint button, GLint state, GLint x, GLint y

10、) 其中button的值可能是 GLUT_LEFT_BUTTON, GLUT_MIDDLE_BUTTON, GLUT_RIGHT_BUTTON 表示哪個按鈕導致了事件發(fā)生 state表示相應按鈕的狀態(tài): GL_UP, GL_DOWN x, y表示在窗口中的位置定位在屏幕上的位置通常是以像素為單位的,原點在左上角 因為顯示器自頂向下刷新顯示內(nèi)容 在OpenGL中應用一個世界坐標系,其原點在左下角 在這個坐標系中的y坐標需要從窗口高度中減去回調(diào)函數(shù)返回的y值:y := h-y獲取窗口尺寸 為了完成y坐標的轉(zhuǎn)換,需要知道窗口的 尺寸 在程序執(zhí)行過程中高度可能發(fā)生改變 需要利用一個全局變量跟蹤其變化

11、新高度值返回給形狀改變回調(diào)函數(shù)(見后) 也可以用查詢函數(shù)glGetIntv和 glGetFloatv獲取,因為高度是狀態(tài)的一部 分結(jié)束程序 在以前的程序中沒有辦法通過OpenGL結(jié) 束當前程序 可以利用簡單的鼠標回調(diào)函數(shù)做到這一點 void mouse(GLint btn, GLint state, GLint x, GLint y) if(btn=GLUT_RIGHT_BUTTON & state = GLUT_DOWN) exit(0); 鼠標位置的應用 在要構(gòu)造的例子中,每單擊一次鼠標左按 鈕,就會在當前鼠標位置處畫一個小方框 在這個例子中并沒有用到顯示回調(diào)函數(shù), 但是由于GLU

12、T要求必須有一個顯示回調(diào) 函數(shù),因此要定義一個空函數(shù) 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; /轉(zhuǎn)化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();鼠標移動回調(diào)函數(shù)的應用 通過利用移動回調(diào)函數(shù)可以在不釋放鼠標 按鈕的情況下,連續(xù)畫一系列方框 (square.c) glutMotionFunc(drawSquare) 應用被動移動回調(diào)函數(shù),可以不用按鼠標 按

14、鈕就可以連續(xù)畫方框 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 由此可以利用單鈕或雙鈕鼠標模擬三鈕鼠標窗口形狀的改變 通過拖動窗口的角點可以改變窗口的形狀 和尺寸 那么其中的顯示內(nèi)容該如何處理? 必須由應用程序重新繪制 有兩種可能性 顯示原來內(nèi)容的一部分 通過強迫適應新窗口來顯示所有內(nèi)容 可能會改變了顯示長寬比率形狀改變的可能性形狀改變的回調(diào)函數(shù) glutReshapeFunc(myreshape) void myreshape(GLint w, GLin

16、t h) 返回新窗口以像素為單位的寬度與高度 在回調(diào)函數(shù)執(zhí)行后自動發(fā)送重新顯示的事件 GLUT有一個缺省的形狀改變的回調(diào)函數(shù),但用戶可能 需要定義自己的行為 這個回調(diào)函數(shù)是放置照相機函數(shù)的恰當?shù)胤剑?因為當窗口第一次被打開時就會調(diào)用這個函數(shù)例子在下面的例子中通過改變窗和世界窗口使得長寬比率不變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*/窗口資源 許多窗口系統(tǒng)提供了一個工具包或者一組庫函 數(shù)用來建立用戶界面,界面中用到了一些特殊 類型的窗口,稱為資源構(gòu)件(widget) 構(gòu)件集合中包含下述工具: 菜單 滑動條 對話框 輸入文本框 但上述構(gòu)件通常都是與

18、平臺相關的 GLUT只提供了包含菜單在內(nèi)的很少幾個構(gòu)件菜單 GLUT支持彈出式菜單 可以有子菜單 創(chuàng)建彈出式菜單的三個步驟 定義菜單內(nèi)各條目 定義每個菜單項的行為 如果條目被選擇執(zhí)行的操作 把菜單連接到鼠標按鈕上定義一個簡單菜單 在main()中 menu_id = glutCreateMenu(mymenu); glutAddMenuEntry(“Clear Screen”, 1); glutAddMenuEntry(“Exit”, 2); glutAttachMenu(GLUT_RIGHT_BUTTON);菜單的行為 菜單回調(diào)函數(shù) void mymenu(GLint id) if(id=1

19、) glClear(); if(id=2) exit(0); 注意在創(chuàng)建菜單時每項都有一個標識符 添加子菜單 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中其它的函數(shù) 動態(tài)窗口 在執(zhí)行期間創(chuàng)建或關閉窗口 子窗口 多重窗口 在執(zhí)行期間改變回調(diào)函數(shù) 計時器 字體 glutBitmapCharacter glutStrokeCharacterGLUT教程 請訪問 http:/ 主要內(nèi)容學會設計更復雜的交互程序,采用的方法為為 選取 從顯示結(jié)果中選擇對象 三種方法 橡皮條 直線與矩形的交互繪制方法

21、 顯示列表 圖形的記憶模式選取(picking) 從顯示結(jié)果中識別用戶定義的對象 原則上說要達到這個目標是很簡單的,因 為鼠標可以提供位置信息,我們因當能夠 根據(jù)位置確定對應的是哪個對象 實際操作中的困難 流水線式的圖形體系是單向的,很難從二維屏 幕返回到三維世界 二維屏幕,三維世界,也使問題變得復雜 離位置多近可以認為選擇了對象?三種方法擊中(hit)列表最一般性的方法,實現(xiàn)也最困難 當繪制對象時,利用后緩沖區(qū)或者其它緩沖區(qū)存貯對象的標識矩形映射(容易實現(xiàn))顯示輸出模式 利用glRenderMode(mode)可以設置 OpenGL用下述三種模式顯示內(nèi)容 GL_RENDER: 正常顯示到幀緩

22、沖區(qū)中(缺省模 式) GL_FEEDBACK:提供要顯示的幾何列表,但 并不輸出到幀緩沖區(qū)中 GL_SELECTION:在視景體中的每個幾何體創(chuàng) 建一個擊中記錄,這個記錄放到名稱堆棧中, 稍后要被檢測顯示輸出模式 利用glRenderMode(mode)可以設置 OpenGL用下述三種模式顯示內(nèi)容 GL_RENDER: 正常顯示到幀緩沖區(qū)中(缺省模 式) GL_FEEDBACK:提供要顯示的幾何列表,但 并不輸出到幀緩沖區(qū)中 GL_SELECTION:在視景體中的每個幾何體創(chuàng) 建一個擊中記錄,這個記錄放到名稱堆棧中, 稍后要被檢測選擇模式中用到的函數(shù) glSelectBuffer(): 指定名

23、稱緩沖區(qū) glInitNames(): 初始化名稱緩沖區(qū) glPushName(id): 把id放到名稱緩沖區(qū)中 glPopName(): 把名稱緩沖區(qū)頂部的名稱彈出 glLoadName(id): 取代在名稱緩沖區(qū)頂部的 名稱 id是由應用程序設置,用以識別對象選擇模式的應用 初始化名稱緩沖區(qū) 進入選擇模式 (例如:應用鼠標) 顯示輸出場景,場景中對象具有用戶指定 的標識 重新進入正常的顯示輸出模式 該操作返回擊中對象個數(shù) 檢查名稱緩沖區(qū)的內(nèi)容(擊中記錄) 擊中記錄包含id與深度信息選擇模式與選取 在選擇模式中不能進行選取,因為這時在 視景體中的每個對象都會生成一個擊中記 錄 改變視圖參數(shù),

24、使得只有靠近鼠標指針的 對象位于新的視景體中 利用glutPickMatrix()應用屏幕上的區(qū)域 在許多應用程序中,屏幕采用簡單的矩形 分劃 例如:這時,相比于應用選擇模式進行選取而言,通過查看鼠標位置,確定屏幕的區(qū)域就更簡單了通過另外的緩沖區(qū)和顏色進行選取 對于很少數(shù)目的對象,可以給每個對象賦 以唯一的顏色(有時是在顏色索引模式中) 然后把場景輸入到不是前緩沖區(qū)的一個顏 色緩沖區(qū)中,這樣就不會看到顯示出來的 結(jié)果 然后獲到鼠標位置,利用 glReadPixels()讀取緩沖區(qū)中的顏色 根據(jù)返回的顏色確定是哪個對象像素的寫入模式XOR寫入模式 缺省的寫入模式是直接用源像素取代目標像素, 即d

25、=s 用這種方法不能繪制一條臨時直線,因為我們不能用快 速簡單的方法恢復在臨時直線下面的內(nèi)容 排它型OR操作(XOR):d = d s 相同值為0,不同值為1 (x y) y = x 因此如果應用XOR模式畫一條直線,那么只要在原地再 畫一遍這條直線就可以刪除這條直線橡皮條式繪圖 切換到XOR寫入模式 繪制對象 利用第一次單擊鼠標固定線段的一個端點,然后再利用 鼠標移動的回調(diào)函數(shù)連續(xù)更新第二個端點 每次鼠標移動的時候,重新一遍原來的直線從而刪除它, 然后再從固定的第一個端點到新的第二個端點之間畫一 條直線 最后切換回到正常的繪圖模式并繪制直線 也適用于其它對象:矩形、圓橡皮條型直線OpenGL中的邏輯操作 在兩位之間有16種可能的邏輯操作 OpenGL支持所有的這16種操作 必須首先啟動邏輯操作 glEnable(GL_COLOR_LOGIC_OP) 然后選擇邏輯操作

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論