OGRE Camera(轉)


 

------OGRE Camera簡介

“偉大航路,我把世界上的一切都放在了那里,有種的話就去領取吧”

這是OGRE中文網的一則廣告,我想,無論作為OGRE的學習者還是漫漫人生的一名旅人,這句話都是非常不錯的激勵語。放在這,是否能夠讓你隱約看到屬於自己的新世界?

前言

相機的概念,在三維里面是舉足輕重的,在一個個三維場景中,看着無數的人物、動畫,我想無數的三維開發者在瀏覽三維時,腦海中都會浮現出一款屬於自己的相機吧,在不停的移動,如同狗仔隊那般專業的相機,耳邊還回盪着成龍的那句“感動常在”。可惜我沒有銀子,所以只能擁有一顆單反的心而已了。

相機在三維里面屬於不可或缺的成分,在紅寶書里放在第三章,就彰顯了其重要性。公司開發的三維GIS軟件,一上來就是一個地球展現在你的眼前,依照慣性自傳,吸引着我的眼球,懷着憧憬和疑問,花了兩天的時間研究公司的相關代碼,但是也贊嘆這套相機和相機的實現確實在架構上很工整,在實現上也把復雜問題模塊化,簡化了問題,本來涉及到公司代碼,不方便多講,但在學習OGRE之后,發現原來公司就是移植OGRE里面的相機實現(當然在上面涉及地球GIS部分有一些接口擴展),於是針對OGRE的實現,在此總結如下吧。

視圖概念

在OpenGL視圖中,參考現實生活中使用照相機拍照的原理,定義了在計算機中的視圖概念。在OpenGL中的照相機類比法很清晰的抽象出四個概念:視圖、模型、投影、視口。

圖1:照相機比喻

image

如上圖所示,OpenGL通過相機,經過模型轉換、投影轉換,將物體映射到了viewport中,而在程序步驟中,也吻合如上的使用方式。

image

圖2:轉換流程

上面的四個過程簡單如下:

l 模型數據獲取
獲取實物的頂點數組和索引數組信息

l 視圖矩陣轉換
根據視圖相機的成像原理,轉換成對應的視圖矩陣,主要負責物體在視口中的現實位置和角度
glMatrixMode(GL_MODELVIEW);
glLoadMatrixf(mat);

l 投影矩陣轉換
投影矩陣主要負責裁剪,實現物體在視口中的顯示部分的裁剪
glMatrixMode(GL_PROJECTION);
glLoadMatrixf(mat);

l 顯示
OpenGL在設置完投影和視圖矩陣后,進行渲染

視錐

OGRE中提供了Frustum類來模擬人眼的視覺效果,在接口上按照用戶的使用習慣,方便用戶轉換模型坐標,而在內部實現中,通過圖形學的矩陣換算,完成人體視覺和計算機視口之間的數學轉換,達到一個比較好的過渡作用。視錐體類比用戶在計算機中看到的圖像效果圖,圖示大致如下:

image

圖3:Frustum類模擬

如上所示,在Frustum類中,提供的一些關鍵屬性,而這些屬性最終決定了顯示的物體的位置,角度以及裁剪。類比地球,人眼則如同一顆繞地衛星,隨着旋轉和拉近,注視着世間萬物。

Radian mFOVy;模擬人眼的角度,該角度決定了顯示范圍的大小,類比睜大眼睛或者眯眼看物體,

Real mFarDist; Real mNearDist;最遠最近裁剪距離

根據角度,結合最遠最近裁剪距離,即可獲取上面的近裁剪面和遠裁剪面的數據信息。

mutable Matrix4 mProjMatrix;投影矩陣,決定物體的裁剪,有最遠最近裁剪面獲取該矩陣數值。

投影矩陣的計算有些復雜,在OGRE中注釋也只是簡單的換算過程,我在初次看到時真的感嘆當初實現人員的偉大(其實是OGRE的偉大和數學家在當初設計OpenGL時扎實的功底),接着就是頭暈,但投影矩陣又算是相機中相對核心重要的,在這里也簡單說明一下推導過程(網上很多,我本人也是閱讀網絡資源學習的)

image

圖4:投影推到

這個坐標系是DX的左手坐標系,Y向上,X向右,Z向內,幾何坐標已經經過了相機坐標系的變換,相機位置為(0,0,0),假設遠裁減面距離為f,近裁減面距離為n,近裁減面左邊為l,右邊為r,上為t,下為b。要投影的2個頂點A和B坐標分別為A(Xa,Ya,Za),B (Xb,Yb,Zb),現在我們要求他們的投影點坐標A0和B0,這里我以B點為例子,講解投影最簡單的幾何知識。

首先我們來計算B0點的X坐標,(虛線都是輔助線),我們看三角形B C O和三角形B0 K O,利用初中幾何知識可得他們是相似的,由相似三角形定理可知,B0 K = B C * ( n /  C O );而B C等於Xb, C O等於Zb;所以Xb0 = Xb * n / Zb; 同理Yb0 = Yb * n / Zb;我們知道DX把3D坐標映射到了一個X(-1,1) Y(-1,1) Z(0,1)的一個立方體內。那現在我們就需要對投影后的坐標進行一維映射了,(其實投影就是映射,數學形式為函數,將一個值域一一映射到另外一個值域),現在在X軸上,我們要把l - r這個值域中的值一一映射到-1 - 1之間,what should we do?很顯然我們有一個等式  ( x - l ) / ( r - l ) = ( x0 - ( -1) ) / ( 1 - ( -1 )); x是l - r之間的一個值,x0是-1 - 1之間的一個值,我們得到 x0 = (2x - ( r + l )) / ( r - l );這里的x換成Xb0得:x0 = (2n * Xb) / (Zb*( r - l )) - ( r + l )/( r - l );同理:y0 = (2n * Yb) / (Zb*( t - b )) - ( t + b )/( t - b ); Zb映射最簡單,將n - f之間的值映射到0 - 1之間, z0 = Zb / ( f - n ) - n / ( f- n );現在我們把這些四則運算用矩陣形式來表示:
[x, y, z, 1] * [ 2n/(r-l)    , 0               , 0             , 0 ]
                   [ 0,            , 2n/(t-b)     , 0             , 0 ]
                   [ -(r+l)/(r-l), -(t+b)/(t-b), z/(f-n)     , 1 ]
                   [ 0,            , 0               , -z*n/(f-n), 0 ]
得到的結構應該是[x0, y0, z0, w]->[x0/w, y0/w, z0/w, 1]
但為了3D引擎后期光柵化時方便的對中間象素的Z值進行線性插值,最好直接保存頂點的1/Z值,然后將1/Z嵌位到0~1之間。
[x, y, z, 1] * [ 2n/(r-l)    , 0               , 0         , 0 ]
                   [ 0,            , 2n/(t-b)     , 0         , 0 ]
                   [ -(r+l)/(r-l), -(t+b)/(t-b), 0         , 1 ]
                   [ 0,            , 0               , 1         , 0 ]    //直接保存1/Z
將1/Z嵌位到0~1之間(線形映射) z0 = (1/Bz - 1/n)/(1/f - 1/n); [1/n->0, 1/f->1]
轉換先形式使矩陣理解更加明了:z0 = f/(f-n) - f*n/((f-n)*z)
[x, y, z, 1] * [ 2n/(r-l)    , 0               , 0             , 0 ]
                   [ 0,            , 2n/(t-b)     , 0             , 0 ]
                   [ -(r+l)/(r-l), -(t+b)/(t-b), f/(f-n)      , 1 ]
                   [ 0,            , 0               , -f*n/(f-n) , 0 ]
備注:斜體部分取自網上資料:[3D基礎]投影矩陣的推導(1)

mutable Matrix4 mViewMatrix;視圖矩陣,將模型和視圖矩陣合為一個,負責模型顯示的距離和角度。

視圖矩陣相對簡單一些,但也是最常用的,一般投影矩陣只要人眼的角度,最大最遠距離不變,矩陣都不會變化,而視圖矩陣則變化的比較平凡,角度的變化,視角的拉近拉遠,人物的走動,都需要觸發視圖矩陣的變更,進而引起渲染的變化。

視圖矩陣的推到則相對常見和簡單,如果學過圖形學算法,二維和三維在這方面是一樣的,無怪乎平移和旋轉兩個矩陣,在相機中,個人感覺沒有用到矩陣縮放的地方(如果三維有鷹眼的話,或許會用到,不過目前的主要應用中沒有)。視圖矩陣計算的數學基礎不在此多說了,相信任何一本圖形學教程都會有,平移和旋轉的矩陣公式和推導過程。

如上,OGRE Frustum類的核心屬性基本保證了相機功能在OpenGL中的簡單運行。而提供的接口,則可以方便用戶和上層界面來進行調用和實現自己的調度策略,OGRE盡可能在底層處理完繁雜的數學運算,給上層提供一個相對簡介的接口。

另外還存在世界矩陣,這個在OGRE中並沒用使用,行列式均為1,其實這個只是定義清晰一些,實際中可以和視圖矩陣整合一起,OGRE本身內部也是二合一的實現思路。這個還沒深入了解。

相機使用

有了視錐體類,只能獲取一個角度下靜態的渲染效果,而三維時一個動態,實時刷新的過程,因此,在Frustum基礎上,OGRE提供了Camera類,正式推出相機的概念。

相機的作用相對簡單,也比較容易理解,就是能夠將Frustum的靜態矩陣實時化更新,並且能夠響應上層的交互操作,在OGRE中,Root中保存相機列表,實現在不同相機下對同一個場景的顯示效果,物體是不變的,而相機的矩陣各不相同,達到橫看成嶺側成峰的效果。

Camera類不多說了,在交互式操作中,通常鼠標和aswd鍵會引起移動和距離的拉伸,導致視圖矩陣距離的變化,鼠標的移動則引起視圖矩陣角度的變化,Camera類開發了move和rotate函數來進行視圖矩陣的調整,獲取最新的視圖矩陣。

而在GLRenderSystem提供了set*Matrix接口,獲取相機的矩陣參數值,調用OpenGL提供的矩陣接口,完成渲染,完成整個相機的轉換流程(圖2)

相機擴展

Camera類只是一個簡單的核心相機類,用戶可以根據自己的業務需要擴展各種CameraEx類來滿足自己的需要,比如以地球為視圖對象的相機類,則以假想地球為規則球體,地球半徑來考慮最大最小裁剪距離,則很容易在Camera之上擴展出一個世界相機來,相似的道理,我們可以實現滿足各種不同的擴展相機,上至星空,下至海底。

另外還要提一個缺點,OGRE在渲染時為單線程,雖然看似簡單,但是其過分以過程為驅動的方式顯得過於混亂,復雜不清晰,這本身並不是相機的問題,個人認為在渲染這塊即使為單線程,也應該在渲染過程中以對象為基礎進行架構的整理,更讓代碼清晰易懂,相機矩陣的設置也會清晰很多,當然,可能是我考慮不夠,在實踐中會暴漏其他問題吧,比如這樣一個一個對象的設置反而影響效率等。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM