• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            隨筆 - 132  文章 - 51  trackbacks - 0
            <2025年5月>
            27282930123
            45678910
            11121314151617
            18192021222324
            25262728293031
            1234567

            常用鏈接

            留言簿(7)

            隨筆分類

            隨筆檔案

            文章分類

            文章檔案

            cocos2d-x

            OGRE

            OPenGL

            搜索

            •  

            最新評論

            閱讀排行榜

            評論排行榜

            使用OpenGL實現三維坐標的鼠標揀選
            Implementation of RIP(Ray-Intersection-Penetration)
            3D Coordinates Mouse Selection Using OpenGL
            顧 露 (武漢理工大學 計算機系 中科院智能設計與智能制造研究所 湖北武漢 430070)
            摘要(Abstract):
            本文提出并實現一種用于三維坐標揀選的RIP(Ray-Intersection-Penetration)方法。介紹了如何在已經渲染至窗口的三維場景
            中,使用鼠標或者相關設備揀選特定三維對象的方法。此方法對于正交投影或透視投影均有效,相對于OpenGL自帶的選擇與反饋機制,本方法無論是揀選精度
            還是算法實現效率均高出許多,是一種比較通用的解決方案。關鍵詞(Keywords) 正交投影(Ortho-Projection)、透視投影(Perspective-Projection)
            世界坐標系、屏幕坐標系、三維揀選、OpenGL
            一、簡介(Introduction)
            OpenGL是一種比較“純粹”的3D圖形API,一般僅用于三維圖形的渲染,對于特定領域的開發者(如游戲開發者)而言,如果選擇使用
            OpenGL進行開發,類似碰撞檢測的機制就都需要自行編寫了。但是由于鼠標在圖形程序中的應用非常非常之廣泛(例如現在已經很少有PC游戲能完全地脫離
            鼠標),OpenGL在圖形庫的基礎上添加了選擇與反饋機制(Select &
            Feedback)來滿足用戶使用鼠標實時操作三維圖形的需要。但由于種種原因,我們需要更為特殊的選擇機制以滿足特定需求,在這里我們提出了一種簡單迅
            速的RIP(Ray-Intersection-Penetration)方法,可以滿足絕大多數典型應用的需要。
            二、相關研究(Related Work) 用過OpenGL選擇與反饋機制的開發者,或多或少可能都會覺得它難以令人滿意。大致表現在下面幾個方面:
            一、 編寫程序比較繁瑣。
            想要使用選擇反饋機制就需要切換渲染模式,操作命名堆棧,計算揀選矩陣,檢查選中記錄,這些繁瑣的步驟很容易出錯,而且非常不便于調試,只會降低工作效率和熱情。二、 只能做基于圖元的選定。

            下圖(1 – a),使用GL_TRIANGLES繪制了一個三角形,三個頂點分別為
            P1、P2和P3。若使用該機制,你將只能判斷是否在三維場景中選中了這個三角形(用戶點擊處是否在P1、P2和P3的范圍內),而無法判斷用戶是點擊了
            這個三角形哪一部分(是左邊的m區域內還是右邊的n區域內),因為所繪制的P1、P2和P3本身構成的三角形就是一個基本圖元,對于揀選機制而言是不可分
            的。當然,把這個三角形拆成兩個三角形再分別進行測試也是一個可行的方案,可是看看圖(1 – b),這可怎么拆呢?還有圖(1 –
            c)呢?另外,如果n和m兩個平面不共面呢?對于使用者而言,OpenGL提供的揀選機制功能的確有限。

            三、降低了渲染效率。
            OpenGL
            中的選擇和反饋是與普通渲染方式不同的一種特殊的渲染方式。我們使用時一般是先在幀緩存中渲染普通場景,然后進入選擇模式重繪場景,此時幀緩存的內容并無
            變化。也就是說,為了選擇某些物體,我們需要在一幀中使用不同的渲染方式將其渲染兩遍。我們知道對對象進行渲染是比較耗時的操作,當場景中需要選擇的對象
            多而雜的時候,采用這個機制是非常影響速度的。
            另外在OpenGL紅寶書中介紹了一種簡便易行的辦法:在后緩沖中使用不同的顏色重繪所有對象,每個對象用一個單色來標示其顏色,這樣畫好之后我們讀取鼠
            標所在點的顏色,就能夠確定我們揀選了哪個物體。這種方法有一個缺陷,當場景中需要選擇的對象的數目超出一定限度時,可能會出現標識數的溢出。對于這個問
            題,紅寶書給出的解決辦法就是多次掃描。實踐證明這種方法的確簡便易行,但仍有不少局限性,而且做起來并不比第一種機制方便多少。限于篇幅,不再贅述。三、具體描述(Related Work) 看過了上面兩種方法,我們會發現這兩種方法都不是十分的方便,而且使用者不能對其進行完全的控制,不能精確地判定鼠標定位與實際的世界空間中三維坐標的關系。那么有什么更好的辦法能夠更簡單更精確地對其加以控制呢? 實際上此處給出的解決方案十分簡單,就是一個很普通也很有用的 GLU 函數 gluUnProject()。
            此函數的具體用途是將一個OpenGL視區內的二維點轉換為與其對應的場景中的三維坐標。
            轉換過程如下圖所示(由點P在窗口中的XY坐標得到其在三維空間中的世界坐標):

            這個函數在glu.h中的原型定義如下:int APIENTRY gluUnProject (
            GLdouble winx,
            GLdouble winy,
            GLdouble winz,
            const GLdouble modelMatrix[16],
            const GLdouble projMatrix[16],
            const GLint viewport[4],
            GLdouble *objx,
            GLdouble *objy,
            GLdouble *objz);  其中前三個值表示窗口坐標,中間三個分別為模型視圖矩陣(Model/View Matrix),投影矩陣(Projection Matrix)和視區(ViewPort),最后三個為輸出的世界坐標值。  可能你會問:窗口坐標不是只有X軸和Y軸兩個值么,怎么這里還有Z值?這就要從二維空間與三維空間的關系說起了。 
             眾所周知,我們通過一個放置在三維世界中的攝像機,來觀察當前場景中的對象。通過使用諸如gluPerspective()
            這樣的OpenGL函數,我們可以設置這個攝像機所能看到的視野的大小范圍。這個視野的邊界所圍成的幾何體是一個標準的平截頭體(Frustum),可以
            看做是金字塔狀的幾何體削去金字塔的上半部分后形成的一個臺狀物,如果還原成金字塔狀,就得到了通常我們所說的視錐(View
            Frustum)這個視錐的錐頂就是視點(View Point)也就是攝像機所在的位置。平截頭體,視錐以及視點之間的關系,如下圖所示:

            在上面的圖中,遠裁剪面ABCD和近裁剪面A’B’C’D’構成了平截頭體,加上虛線部分就是視錐,頂點O就是攝像機所在的視點。我們在窗口中所能看到的東東,全部都在此平截頭體內。這跟前面的窗口坐標Z值有什么關系呢?看下圖


            此圖所示,點P和點P’分別在遠裁剪面ABCD和近裁剪面A’B’C’D’上。我們點擊屏幕上的點P,反映到視錐中,就是選中了所有的從點P到點P’的
            點。舉個形象的例子,這就像是我們挽弓放箭,如果射出去的箭近乎筆直地飛出(假設力量非常之大近乎無窮),從挽弓的地點直至擊中目標,在這條直線的軌跡上
            任何物體都將被一穿而過。對應這里的情況,用戶單擊鼠標獲得屏幕上的某一點,即是指定了從視點指向屏幕深處的某一方向,也就確定了屏幕上某條從O點出發的
            射線(在圖中即為OP)。在這里,我們稱呼其為揀選射線。
            因此,從窗口的XY坐標,我們僅僅只能獲得一條出發自O點的揀選射線,并不能得到用戶想要的點在這條射線上的確切位置。
            這時候窗口坐標的Z值就能派上用場了。我們通過Z值,來指定我們想要的點在射線上的位置。假如用戶點擊了屏幕上的點(100,100)得到了這條射線OP,那么我們傳入值1.0f就表示近裁剪面上的P點,而值0.0f則對應遠裁剪面上的P’點。

            樣,我們通過引入一個窗口坐標的Z值,就能指定視錐內任意點的三維坐標。與此同時,我們還解決了前面紅寶書給出的方法中存在的缺陷——同一位置上重疊物體
            的選擇問題。解決辦法是:從屏幕坐標得到射線之后,分別讓重疊的物體與該射線求交,得到的交點,然后根據這些與視點的遠近確定選擇的對象。如此我們就不必
            受“僅僅只能選取屏幕中離觀察者最近的物體”的限制了。這樣一來,如果需要的話,我們甚至可以用代碼來作一定的限定,通過判斷交點與視點的距離,使得與該
            揀選射線相交的物體中,離視點遠的對象才能被選取,這樣就能夠對那些暫時被其他對象遮住的物體進行選取。
            至于如何求揀選射線與對象的交點,在各種圖形學的書中的數學部分均有講述,在此不再贅述。
            四、例程(Sample Code Fragment)

            前面講述了RIP方法,現在我們來看如何編寫代碼以實現之,以及一些需要注意的問題。
            由于揀選射線以線段形式存儲更加便于后面的計算,況且我們可以直接得到縱跨整個平截頭體的線段(即前面圖中的線段PP’),故我們直接計算出這條連接遠近裁剪面的線段。我們將揀選射線的線段形式稱之為揀選線段。
            在下面的代碼前方聲明有兩個類Point3f和LineSegment這分別表示由三個浮點數構成的三維空間中的點,以及由兩個點構成的空間中的一條線段。
            應注意代碼中用到了類Point3f的一個需要三個浮點參數的構造函數,以及類LineSegment的一個需要兩個點參數的構造函數。
            獲取揀選射線的例程如下所示(使用C++語言編寫):class Point3f;
            class LineSegment;
            LineSegment GetSelectionRay(int mouse_x, int mouse_y) {
            // 獲取 Model-View、Projection 矩陣 & 獲取Viewport視區
            GLdouble modelview[16];
            GLdouble projection[16];
            GLint viewport[4];
            glGetDoublev (GL_MODELVIEW_MATRIX, modelview);
            glGetDoublev (GL_PROJECTION_MATRIX, projection);
            glGetIntegerv (GL_VIEWPORT, viewport); GLdouble world_x, world_y, world_z; // 獲取近裁剪面上的交點
            gluUnProject( (GLdouble) mouse_x, (GLdouble) mouse_y, 0.0,
            modelview, projection, viewport,
            &world_x, &world_y, &world_z);
            Point3f near_point(world_x, world_y, world_z); // 獲取遠裁剪面上的交點
            gluUnProject( (GLdouble) mouse_x, (GLdouble) mouse_y, 1.0,
            modelview, projection, viewport,
            &world_x, &world_y, &world_z);
            Point3f far_point(world_x, world_y, world_z); return LineSegment(near_point, far_point);
            }

            如果你是使用Win32平臺進行開發,那么應當注意傳入正確的參數。因為無論是使用Win32 API 還是DirectInput
            來獲取鼠標坐標,得到的Y值都應取反后再傳入。因為OpenGL默認的原點在視區的左下角,Y軸從左下角指向左上角,而Windows默認的原點在窗口的
            左上角,而Y軸方向與OpenGL相反,從左上角指向左下角。如下圖所示:

            我們可以看到代碼被注釋分為了三個部分:獲取當前矩陣及視區,獲取近裁剪面的交點,獲取遠裁剪面的交點。
            我們通過OpenGL提供的查詢函數輕松得到當前的ModelView和Projection矩陣,以及當前的Viewport(視區,也就是窗口的客戶端區域,如果整個窗口區域用于OpenGL渲染的話)。
            獲得兩個裁剪面上的交點的代碼基本上是一樣的,唯一的不同點是我們前面曾經詳細地討論過的窗口的Z坐標。不錯,這個坐標表示的就是“深淺”的概念。它的值從點P’到點P的變化是從0.0f逐漸增至1.0f。此處類似于OpenGL的深度測試機制。
            在得到兩個交點之后,我們使用它們通過返回語句直接構建一條線段。在這里僅僅作為實例代碼,故簡捷清晰地直接返回線段對象,而沒有通過引用參數來提高效率。

            時用戶可以使用這個函數來判斷所選擇的對象了。只需在需要的地方判斷對象是否與此線段相交即可判斷對象是否被選中,還可以通過進一步計算其交點位置來得到
            詳細的交點信息。這些計算均是常見的計算機圖形學與三維數學計算,比如線段與三角形求交,線段與面求交,線段與球體求交,線段與柱體或錐體求交,等等。請
            參考所列出的計算機圖形學書籍。
            五、結論(Conclusion)

            在本文中,我們介紹了一種行之有效的三維坐標拾取方法,主要使用GLU庫中的實用工具實現。這種方法速度快,效率高,能在不必重新繪制對象的前提下完成揀選工作。對比OpenGL自帶的揀選機制來看,RIP的確在各種方面均有一定的優勢。
            六、參考文獻(Reference) 【1】《OpenGL Programming Guide》
            OpenGL ARB Mason Woo, Jackie Heider, Tom Davis, Dave Shreiner
            【2】《OpenGL Reference Manual》
            OpenGL ARB
            【3】《Computer Graphics》
            Donald Heam, M. Pauline Baker
            【4】《Computer Graphics using OpenGL 2nd Edition》
            F.S. Hill, JR.
            posted on 2010-06-05 18:45 風輕云淡 閱讀(3050) 評論(0)  編輯 收藏 引用 所屬分類: OpenGL
            色综合久久久久综合体桃花网| 99久久精品免费看国产一区二区三区 | 久久久久人妻一区二区三区vr | 国产国产成人精品久久| 亚洲精品WWW久久久久久| 国产产无码乱码精品久久鸭 | 午夜精品久久久久久久| 久久精品a亚洲国产v高清不卡| 69国产成人综合久久精品| 999久久久免费国产精品播放| 日韩精品久久久肉伦网站| 色欲综合久久躁天天躁| 国产亚州精品女人久久久久久 | 狠狠狠色丁香婷婷综合久久五月 | 国产—久久香蕉国产线看观看| 99精品久久精品| 伊人久久大香线蕉精品不卡| 精品久久久久久久无码| 亚洲国产成人精品91久久久 | 品成人欧美大片久久国产欧美... 品成人欧美大片久久国产欧美 | 久久天天婷婷五月俺也去| 欧美国产精品久久高清| 久久无码国产| 欧美激情精品久久久久| 精品国产热久久久福利| 久久亚洲精品中文字幕| 婷婷久久五月天| 尹人香蕉久久99天天拍| 久久久久久毛片免费看| 久久亚洲国产精品成人AV秋霞| 精品久久久一二三区| 久久精品国产精品亚洲| 亚洲午夜久久影院| 精品国产福利久久久| 国产精品久久久福利| 国产精品岛国久久久久| 久久久久久久久久久久中文字幕 | 精品国产乱码久久久久软件| 久久九九免费高清视频| 久久久国产精品| 日韩一区二区三区视频久久|