觀察和轉換
----------------------------------------------------------------------------------
01.相機如何在OpenGL中工作?
02.我怎樣才可以移動我的視點, 或者是相機,在我的場景中?
03.我的相機該去哪, 模型視點矩陣還是投射矩陣?
04.我該怎么執行一個縮放操作?
05.給出當前模型視點矩陣,我怎么才可以確定相機的物體空間位置?
06.我怎樣才可以使照相機在我的場景中圍繞一個點轉?
07.如何自動計算一個觀察點可以顯示全部的場景?(我知道邊界球體和向上分量(bounding sphere
and up vector ). )
08.gluLookAt是如何工作的?
09.我怎樣才可以得到一個在場景正中間的點?
10.我把我的gluLookAt調用放在我的投影矩陣中,現在霧, 光線, 紋理貼圖都不能正確工作了, 究竟
發生什么了?
11.我怎樣才可以建造一個立體的視覺?
12.我不能使轉換正常的工作,哪里才可以知道更多的關于矩陣的東西?
13.OpenGL的矩陣是縱排列,還是橫排列的?
14.OpenGL的坐標單位是什么?
15.坐標是如何轉換的?坐標空間有什么不同?
16.我怎么樣才可以轉換我場景中的唯一一個物體或者給每個物體都有單獨的變換?
17.如何才可以在3D渲染圖上繪制2D圖?
18.我如何才可以饒過OpenGL的矩陣轉換, 而直接把2D坐標光柵化?
19.使用絕對坐標和相對坐標的優缺點在哪呢?
20.我怎樣才可以畫一個場景中的不同視圖?
21.我怎么樣才可以使我的物體圍繞一個固定的坐標系統轉換, 而不是本地的坐標系統?
22.相對于使用gluPerspective, 使用glFrustum的優缺點?
23.我怎樣才可以調用glFrustum來匹配調用gluPerspective?
24.我怎樣才可以畫一個全屏幕的矩形?
25.對于一個給定的物體空間坐標, 我如何確定相應的屏幕坐標?
26.怎么樣才可以找到一個屏幕上點的物體空間坐標?
27.怎樣才可以找到一個被模型視點矩陣轉換過的頂點坐標?
28.怎樣計算觀察者到給定點的物體空間距離?
29.在窗口被重定義大小后, 我怎么樣才可以保持我的比例系數?
30.我怎么樣才可以使OpenGL使用左手坐標系?
31.怎樣轉換一個物體是它可以指向或者緊跟著另一個場景中的物體或點
32.我如何才可以使用傾角, 仰角,斜角來轉換一個物體?
33.我怎樣才可以渲染一個鏡面?
34.我怎樣才可以做我自己的透視縮放?
==================================================================================
1.OpenGL的照相機是如何工作的?
OpenGL是非常注意的,這里是沒有照相機的.更確切的說是, 這個站相機總是位于眼空間坐標處( 0.0, 0.0, 0.0, 0.0 ).為了給出移動相機的效果,你必須反向的移動你的場景,把轉換矩陣放入模型視點矩陣來實現.這就是通常被引用的視點轉換.
在實際練習中, 這是一個和相機轉換相等的等式, 但更有效率,因為更多的模型轉換 和相機轉換被連接到了一個單獨的矩陣中去.因此,但相機且只有相機在模型視點當中時,這些操作是必須被執行的.比如,當安置一個光源在世界坐標系的時候, 當視點轉換且只有視點轉換被應用于模型視點矩陣的時候, 它是必須被重安置的.
---------------------------------------------------------------------------------
2.我如何才可以移動我的眼睛, 或者相機在我的場景中?
OpenGL并沒有使用相機模型來提供一個接口完成這項工作.然后, GLU的庫提供了一個gluLookAt函數, 它可以確定一個眼睛的位置, 一個可以看, 有向上向量,在物體空間中的坐標.這個函數可以根據它的參數,計算出它的相機的反向轉換然后在乘以系統的當前矩陣.
----------------------------------------------------------------------------------
3.我的照相機該去哪兒, 模型視點矩陣還是投影矩陣?
這GL_PROJECTION矩陣只能夠包含投影變換,它轉換眼坐標到裁減坐標.
這GL_MODELVIEW矩陣, 正如它名字所暗示的那樣,包含了模型和視點轉換, 它將會轉換物體的空間坐標到眼坐標.請記住把相機轉換代碼放入GL_MODELVIEW矩陣中去, 不要放入GL_PROJECTION 去.
考慮投影矩陣就好像在描述照相機的特性一樣, 例如field of view, focal length, fish eye lens, etc.考慮模型視點矩陣, 就好像你和照相機的位置, 以及你的朝向.
在 gamedev FAQ有更多的描述兩個矩陣的信息.
讀 Steve Bakers's 文章關于濫用投影.這篇文章是被高度評價和推薦的.它幫助了很多的OpenGL的新程序員們.
----------------------------------------------------------------------------------
4. 我如何執行一個縮放操作?
一個簡單的來縮放的方法就是使用統一的模型視點矩陣.然后, 這通常容易導致假如模型被放的太大的話, 會被zNear和zFar所裁減.
一個更好的方法就是通常對投影的視景體進行寬和高的限制.
例如,你的程序通常會根據用戶的輸入, 來提供一個縮放的參數,可能是一個浮點數.當設置了一個1.0的值后,將會沒有縮放操作執行. 更大的值會有更大的縮放,和更多的關于視景體的限制, 而更小的值將會導致相反的情況.制造這種效果的代碼有可能像這樣:
static float zoomFactor; /* 全局變量, 假如你需要,可以通過用戶輸入來改變,初始值為1.0*/
/* 一段函數來設置投影矩陣. 可能的調用會來自一段典型的程序的重定義穿口大小的句柄. 將會
給出重畫大小的寬和高的整數值. 使用正確的畫面比例系數和縮放因子建造一個投影矩陣 */
void setProjectMatrix( int width, int height )
{
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
gluPerspective( 50.0*zoomFactor, (float)width/(float)height, zNear, zFar );
/* zNear 和 zFar 將會由你來填入*/
}
為了代替gluPerspective, 你的程序有可能使用glFrustum().這里有點小技巧,因為這個左, 右, 底,頂部參數, 還有近平面參數, 也會影響到可視域. 假設你想保持一個固定的zNear面的距離(非常合理的假設),glFrustum的代碼可能會看起來如下:
glFrustum( left *zoomFactor, right *zoomFactor,
bottom *zoomFactor, top *zoomFactor,
zNear, zFar );
glOrtho()也是類似的.
----------------------------------------------------------------------------------
5.給出了當前的模型視點矩陣, 我如何確定相機的物體空間位置?
這個"相機"或者視點在眼坐標中是在( 0.0, 0.0, 0.0 ).當你把它轉換成( 0, 0, 0, 1)并與模型視點矩陣的逆矩陣相乘時, 得出來的向量就是相機的物體空間坐標.
OpenGL并沒有方法(使用glGet* 相關的函數)來得到模型視點矩陣的逆矩陣.你需要使用自己的代碼來計算它.
----------------------------------------------------------------------------------
6.我怎樣才可以使我場景中的照相機圍繞一個頂點在轉動?
你可以在同樣的場景中通過平移/轉動 場景/物體來模擬軌道 .例如, 圍繞一個在Y軸上的物體, 而且不間斷的望向原點, 有可能會這樣寫:
gluLookAt( camera[0], camera[1], camera[2], /* 相機位置 */
0, 0, 0, /* 相機朝向原點 */
0, 1, 0 ); /* 正Y軸方向 */
glRotatef( orbitDegree, 0.0f,1.0f, 0.0f ); /* 圍繞 Y 軸 */
/* ... 旋轉角度, 也可以來自于鼠標的運動 */
glCallList( SCENE ); /* 繪制場景 */
假如你堅持使用物理的移動這個相機的位置, 那么在把它放進你的模型視點轉換中之前,你必須得轉換當前相機位置的向量.
在任意一種事件中,我都建議你研究下gluLookAt(如果你已經沒有是用它了);
----------------------------------------------------------------------------------
7.我如何才可以自動計算一個點,使它可以顯示我場景中的全部模型(我知道球體范圍和向上向量)?
接下來的是來自Dave Shreiner 設置的一個可視系統.
首先,計算所有在你的場景中的物體的球體范圍. 這些將會提供給你兩點信息: 球體的中心位置 (讓 (cx, cy, cz) 來記錄這點)和它的直徑.
接下來,選擇一個zNear剪裁面距離的值.大部分的建議都是選擇一個大于,但接近一的數, 所以,讓我們把它設為:
zNear = 1.0;
zFar = zNear + diam;
按下面這種方式,組織你的矩陣調用(對一個正射投影矩陣)
GLdouble left = c.x - diam;
GLdouble right = c.x + diam;
GLdouble bottom = c.y - diam;
GLdouble top = c.y + diam;
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
glOrtho( left, right, bottom, top, zNear, zFar );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
這種方法會設置你的物體在窗口的正中心, 伸展邊界以符合.(i.e.,這個是假設你窗口的比例系數為1.0).假如你的窗口是非正方形的, 將會按上面的方式計算左, 右, 上, 下, 在調用glOrtho以前放入下面的邏輯代碼段中.
GLdouble aspect = (GLdouble) windowWidth / windowHeight;
if ( aspect < 1.0 ) { /* 窗口的高比寬長 */
bottom /= aspect;
top /= aspect;
} else {
left *= aspect;
right *= aspect;
}
上面的的代碼將會把你的 場景中的物體放在相對正確的位置. 假如你想試著去操作它( i.e.旋轉,etc), 你需要增加一個可視轉換.
一個典型的可視轉換將會和模型視點矩陣想混和, 而且看起來,應該是這樣:
gluLookAt( 0.0, 0.0, 2.0 * diam, c.x, c.y, c.z, 0.0, 1.0, 0.0 );
----------------------------------------------------------------------------------
8.為什么gluLookAt()不能工作?
這個通常是被不正確的轉換所導致的.
假設你使用投影矩陣堆棧中的gluPerspective的zNear, zFar來作為第三, 第四個參數,你就必須設置模型視點矩陣堆棧上的gluLookAt(), 并傳遞相應的參數,使你的物體, 可以落在zNear和zFar之間.
當你正試圖去了解可視轉換的時候,最好的方法就是寫一些代碼來做試驗.讓我們想一下,你正在看一個位于原點的一單位的球體.你可能想按下面的方式來設置你的轉換:
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
gluPerspective( 50.0, 1.0, 3.0, 7.0 );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
gluLookAt( 0.0, 0.0, 5.0,
0.0, 0.0, 0.0,
0.0, 1.0, 0.0 );
非常重要的一點就是,知道投影和模型視點矩陣是如何一起工作的.
在這個例子中, 這個投影轉換,設立了一個50度的可視角度的觀察域, 和一個窗口的為1的比例系數. 這個zNear的剪裁面是視點的前三個單位,這個zFar的剪裁面的是視點前的7個單位.它們分配了一個在Z軸長度為4的視景體,對于一個單位的球體是絕對足夠的.
這個模型視點轉換設置了眼坐標為(0.0, 0.0, 5.0), 而所觀看的點是原點,也就是我們單位圓的正中心.請注意這個眼坐標的位置離觀察點的位置有5個單位的距離. 這點是相當重要的,因為眼睛前面的5個單位的距離是在投影轉換所定義的視景體的正中間.假如這個gluLookAt()把眼睛設置在( 0.0, 0.0, 1.0 )它只會離原點一個單位的距離.它將會沒有足夠的長度去包含物體在視景體中, 物體將會被zNear所剪裁.
相同地,假如你把眼坐標設置在( 0.0, 0.0, 10.0 ), 10個單位的離觀察點的距離,將會導致這個球體和視點有10個單位的距離, 并且被zFar剪裁面在7單位距離處, 被剪裁.
假如這些東西, 困擾著你,讀OpenGL紅皮書,或者OpenGL詳解(specification)中有關于轉換的內容.在你理解了物體空間坐標, 眼坐標空間,和剪裁面坐標空間, 這些上面的知識, 將會變的非常的容易, 清晰. 最好, 在經常寫些小的測試程序來實踐一下. 假如你在你的主程序中依然有相當的困難來使用正確的變換矩陣,那么寫些更簡單幾何體的測試程序是非常有意義的.
--------------------------------------------------------------------------------
9.我如何去設置一個精確的點在場景的中間顯示?
gluLookAt是非常容易做到這個的.簡單的設置你的點的X, Y, 和Z值作為你的gluLookAt的第三, 第四, 第5個參數.
---------------------------------------------------------------------------------
10.我把我的gluLookAt調用放入我的投影矩陣中, 現在霧,光線,和紋理映射都不能正常工作了, 這
是為什么?
這個通常是因為把轉換放在錯誤的矩陣中所導致的, 看下問題三對這個問題的解釋. 然后也有可能, 是因為特殊的光照問題, 所導致的,因為有錯誤的矩陣被放入模型視點矩陣中,然后定義了光照的位置. 當一個光源點被定位在眼空間坐標中,i.e.和眼睛相關,它將會被重新定位到一個單位矩陣在模型視點矩陣堆棧中的時候.當可視矩陣被放入模型視點堆棧中時, 原先世界坐標系中的光源位置講會被重定位. 當一個光源的位置是和一個轉換的物體相關的時候, 當物體的模型矩陣和在模型視點矩陣堆棧中的可視矩陣想乘時,這個光源也會被重新定位,請記住在任何的光源被渲染前, 它都有可能被重新定位.假如光源在結構中相對于眼睛在移動, 那么每個結構都必須使用合適的矩陣來進行重定位.
----------------------------------------------------------------------------------
11.我如何可以建造一個立體視圖?
立體視圖就是通過展現觀察者觀察到的左眼和右眼所看見的畫面來形成的.這些圖像必須正確的匹配到顯示器上,一符合觀察者實際所看到的, 更多的習慣是使用多于一副的3D圖像. 附加一點就是這種方法會和被使用的顯示器技術有相當緊密的聯系. 一些圖形系統和顯示設備, 會在硬件上支持立體視圖和支持像左緩存和右緩存, 另外就是在系統上支持雙緩存的前緩存和后緩存. 另外的系統就會在顯示屏幕顯示兩個視口來支持立體視圖, 并詳細定義顯卡的模式來發送這些圖像到顯示屏幕.另外與此相關的就是觀察者通常帶著一個特殊的眼鏡,它會自動對圖像進行揀選來以此來符合每個眼睛所看到的.然而即使沒有這些圖像特性, 一個開發者仍然可以使用顏色過濾在一些基于紅或藍色過濾器的選擇圖像的地方來實現立體視圖,例如畫左眼和右眼的圖像到到紅,藍幀緩中來實現這點. 或者更加簡單的就是有多個系統或是圖形顯卡(或者一塊顯卡)來產生兩個完全分離的視頻信號, 來分別畫一個左眼的和右眼的圖像.這個視頻將會被發送到正確的眼睛中,使用一個display employing polarizing filters 或者一個 head mounted display 或者一些別的用戶自己的顯示設備, 執行一些原理相同的操作.
從一個OpenGL透視投影中, 立體渲染的設備將會使用合適的操作來渲染到左眼和右眼(作圖像掩碼, 分離上下文 或是不同視口)并且匹配到與OpenGL投影相關的觀察者的左眼和右眼的幾何圖像到顯示器上. 這個最后的OpenGL設備要求是這個在"虛擬"世界中的眼睛位置必須得來自模型視點矩陣中的分離的瞳孔位置, 這種分離會在眼空間坐標產生一個轉換 但不會在別的相同的方法中被計算.
Paul Bourke 整理了很多關于立體OpenGL立體視圖的資料:
· 3D Stereo Rendering Using OpenGL
· Calculating Stereo Pairs
· Creating Anaglyphs using OpenGL
----------------------------------------------------------------------------------
12.我不能使轉換正常的工作. 我可以從哪里得到更多的關于矩陣的知識?
一個系統的關于基礎的矩陣數學和線性代數的知識已經超出這份FAQ的范圍. 相關的概念已經在美國的高校數學課上解釋過了。
假如你知道這些基本概念, 但還是有點糊涂(一個共同的問題就是經驗), 讀下Steve Baker's
review of matrix concepts 和他的 article on Euler angles.
執行基本向量, 矩陣, 四元數的操作的delphi代碼 可以在這里找到.
----------------------------------------------------------------------------------
13.OpenGL的矩陣是行排列的, 還是縱排列的?
出于編程的原因, OpenGL的矩陣是一個在內存中依次相連的16元素的數組,轉換部分占據了16元素的矩陣中的第13, 14, 15, 16位置.
縱隊排列相對橫隊排列純粹就是一種符號協定. 注意自右的和縱排列矩陣相乘將會產生和自左的和行排列矩陣相乘產生同樣的結果. 在 OpenGL詳解和OpenGL參考手冊都使用了縱排列矩陣. 你可以使用任意一種, 只要你清楚的聲明了。
不辛地,使用在spec和藍皮書中的縱排列格式會在OpenGL社區中導致無止境的困擾.縱排列的格式會導致一個程序員不期望的矩陣在內存中的排列格式.
更多的關于這方面的內容, 請點擊:這兒.
----------------------------------------------------------------------------------
14.OpenGL的坐標單位是什么?
最簡短的回答就是. 只要你希望, 它可以使用任何你所希望的單位.
根據你幾何數據庫中的內容, 對于你的程序可能非常方便的處理OpenGL的坐標單位,比如一個毫米,或者一光年, 或者這其中的任何距離(或大或小).
OpenGL也允許你自己定義你的幾何體使用不同值的坐標系統.例如, 你可能會發現在對飛機模型控制的時候使用厘米, 而對機身使用米, 而對所飛行的空間使用千米.OpenGL的模型視點矩陣可以縮放不同的坐標系統來形成同樣的眼坐標系統.
程序有義務來確定投影和模型視點矩陣的構造所提供的圖像和觀察者保持一個合適的距離, 和合適的可視域, 以及能夠保持zNear和zFar剪裁面在一個合適的范圍內.一個在微米程度縮放的顯示分子的程序, 例如, 就不能把觀察者放在一個10英尺距離, 60度觀察域的地方.
----------------------------------------------------------------------------------
15.坐標是如何轉換的?坐標空間有什么不同?
物體坐標被模型視點矩陣所轉換來產生眼坐標.
眼坐標被投影矩陣所轉換來產生裁減坐標.
裁減坐標 X, Y, Z將會被裁減坐標W所除來產生設備規范化坐標.
設備規范化坐標會被視口參數所縮放和轉移來產生窗口坐標.
物體坐標也就是當你提交給OpenGL glVertex*()或著是 glVertexPointer()的時候的原始坐標.它將會顯示你的物體或者別的你想渲染的幾何體的坐標.
許多的程序設計者使用世界坐標系統. 物體通常在同一個坐標系統中被模型化, 縮放, 平移, 旋轉都會在你的建造的世界中轉換. 世界坐標系統是通過存儲在模型視點矩陣中的模型轉換而形成的物體坐標轉換而來. 然而,OpenGL并沒有世界坐標系統的概念. 世界坐標系統純粹就是一個程序架構.
眼坐標是通過被模型視點矩陣轉換的物體坐標而來. 這個模型視點矩陣中包含了模型轉換和視點轉換, 它定義了觀察者位于原點,而且朝向Z的負半軸.
裁減坐標是由眼坐標通過投影轉換而來.裁減坐標空間范圍是從 -Wc 到 Wc 在所有的三個軸方向上, Wc 是裁減坐標W的值. OpenGL會裁減所有位于范圍之外的東西.
裁減坐標上所執行的透視除法會產生標準設備規范化坐標, 在三個軸的范圍是從 -1 到 1.
窗口坐標是來通過視口來縮放,平移設備規范化坐標轉換而來. glViewport()和glDepthRange()的參數控制這些轉換.使用視口, 你可以映射設備規范化坐標的立方體到你的窗口的任何位置 和深度緩存中.
更多的信息, 請看 OpenGL specification , 圖解2.6
----------------------------------------------------------------------------------
16. 我如何轉換我場景中的唯一物體或者是給我場景中的每一個物體都有單獨轉換?
OpenGL提供了矩陣堆棧來實現這個功能. 這樣的話, 就是使用模型視點矩陣堆棧.
一個典型的OpenGL程序第一次設置這個矩陣模式是使用 glMatrixMode( GL_MODELVIEW )并且加載一個可視轉換, 例如一個到gluLookAt().更多的信息可利用的在 gluLookAt.
然后這個代碼渲染每個場景中的物體使用他們自己的轉換通過包裝這個調用glPushMatrix()和glPopMatrix().例如:
glPushMatrix();
glRotatef( 90.0, 1.0, 0.0, 0.0 );
gluCylinder( quad, 1, 1, 2, 36, 12 );
glPopMatrix();
上面的代碼渲染一個圓柱體圍繞X軸旋轉90度. 這個模型視點矩陣將會被重新存儲為先前的值在glPopMatrix()調用后. 相似的調用序列可以渲染場景中的并發物體.
17.我如何在我的3D渲染上, 繪制我的2D控制器?
基本的策略是對于要描繪的控制點設置一個2D投影.你既可以在你的3D渲染上做這些, 也可以在覆蓋面上來完成這個. 假如你選擇在3D渲染上繪制這些, 那么在每幀結束的時候, 都要重畫這些控制點(在交換緩存前, 立即重畫). 假如你選擇畫在覆蓋面上, 你只需要在跟新的時候重畫.
為了設置一個2D投影, 你需要改變這個投影矩陣. 正常地,這是非常容易設置一個世界坐標的單元, 來對應于一個屏幕的像素, 像這樣:
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
gluOrtho2D( 0, windowWidth, 0, windowHeight );
gluOrtho2D()設置一個-1.0到1.0的Z軸的范圍, 所以你可以使用glVertex2*()函數中的一種來確保你的幾何圖元不會被zNear和zFar剪裁面所剪裁.
正常地, 當繪制2D控制點的時候, 都把模型視點矩陣設置會單位矩陣.所以你會發現非常容易做些別的( 例如, 你可以使用交叉存取的平移矩陣來重復的畫控制點).
假如需要精確的像素操作, 你可以把一個小型的平移放入模型視點矩陣, 如下所示:
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
glTranslatef( 0.375, 0.375, 0.0 );
假如你在一個3D 深度緩存的圖像上進行繪制, 當你繪制你的2D幾何圖元你需要關閉深度測試(depth test). 你可以通過調用glDisable( GL_DEPTH_TEST ) 或者是glDepthFunc( GL_ALWASY ).根據你的程序, 你可能在開始2D繪制的時候簡單的清除了深度緩存. 最終, 繪制2D幾何圖元在你最你的Z軸深度上, 也不失為一個解決方法。
在2D投影矩陣按如上所示的建立后,你就可以渲染正確的OpenGL圖元到屏幕上, 詳細的定義他們的X, Y像素的地址( 使用OpenGL 中心的屏幕坐標, (0, 0)在左下角).
---------------------------------------------------------------------------------
18.我如何越過OpenGL矩陣變換而直接讓2D坐標光柵化?
這并不是OpenGL矩陣轉換中的一種開關模式. 然而, 假如你使用了glLoadIdentity()調用設置了設置了兩個單位矩陣, 典型的OpenGL執行有足夠的智力,來知道這是一個單位矩陣轉換是不用操作的并因此來行動.
更多的有關純粹把OpenGL當做一個光柵化的API的信息, 請參考:OpenGL Game Developer's FAQ.
----------------------------------------------------------------------------------
19.使用絕對坐標,或者是相對坐標的好處, 和壞處?
一些OpenGL可能需要在單一的場景中使用更多的坐標來渲染同樣的物體.OpenGL有兩中方法做這個:
1.使用"絕對坐標".支持每個物體的多份拷貝, 每個都有他們自己唯一的頂點集合.你不需要去改變
模型視點矩陣在需要的位置對物體進行渲染.
2.使用"相對坐標".會保存物體的唯一一 份拷貝, 通過壓入模型視點矩陣堆棧, 設置需要的轉換,
發送幾何數據, 并彈出堆棧來對他們進行多次的渲染. 對每個物體重復這些步驟.
一般來說, 經常的改變狀態, 例如模型視點矩陣, 會對你的效率有負面的影響. 假如你沒有對每個單獨的圖元都在模型視點轉換使用大量的轉換, 那OpenGL是可以快速的處理你的幾何數據的.
然而,你有時候可能需要權衡對圖像進行多分的拷貝來放棄對內存的節省. 讓我們假設你定義了一個高擬真度的門把手,使用了大約200, 300個三角形, 而你對一個房子的建模將會使用50個門把手,它們都使用了相同的門把手. 這個更加好的是使用單獨門把手的顯示列表, 然后定義多個單獨的矩陣轉換, 而不是在內存使用大約10-15K的空間來定義絕對坐標.
當有更多的計算主題時, 那么不得不在CPU時間和存儲空間之間進行選擇. 而你也不得不對此進行更多的嘗試.
----------------------------------------------------------------------------------
20.我如何在我的場景中繪制更多的視圖?
你可以通過使用glViewport()調用在同樣的窗口繪制兩個視圖. 設置glViewport()對準那個你想作為第一個視圖的地區, 設置你的場景觀察, 和渲染. 然后設置glViewport()對準那個你想作為第二個視圖的地區, 再一次設置你的場景觀察, 和渲染.
你可能需要注意有些操作并不會為glViewport()所注意, 例如glSwapBuffers()和glClear(). glSwapBuffers()總是交換當前的整個窗口. 然而, 你可以通過使用剪取矩形來限制你的glClear()專門只對一個矩形窗口.
你的程序有可能只允許在不同的窗口中使用不同的視圖. 假如這樣, 你需要在兩個渲染器中執行一個針對當前的操作. 假如兩個窗口共享一個上下文環境, 你需要向上面描述的那樣改變場景的視圖. 假如你的程序對每個窗口使用分離的上下文, 那么這些操作是沒有必要的.
----------------------------------------------------------------------------------
21.我如何使我的物體圍繞一個固定的坐標系統, 而不是物體的本地坐標系統?
假如你使一個物體, 圍繞Y軸旋轉, 你就會發現X軸, 和Y軸在圍繞這個物體旋轉.一個后續的旋轉圍繞這些軸進行新的轉換軸旋轉, 而不使用原始的軸. 在一個固定坐標軸系統中, 而不是物體本地坐標系統中, 執行轉換是非常另人滿意的.
這個 OpenGL Game Developer's FAQ 包含了使用四元數來存儲轉換的信息, 在解決這類問題時, 這些資料有可能很有用.
導致這個問題的主要原因是OpenGL矩陣操作是在矩陣堆棧上的自右乘法, 因此導致了出現在物體空間的轉換.為了影響屏幕空間轉換, 你需要使用自左乘法. OpenGL并沒有提供模式的開關來對應矩陣相乘的順序, 所以你需要手動自左乘法.一個程序可能在每幀后通過取回當前矩陣來對此進行實現. 這個程序乘以新的轉換來對應于單位矩陣上的下一幀, 并乘以使用glMultiMatrix()的矩陣上的堆積的當前轉換(來自上一幀).
你需要注意的就是每次得到一幀的模型視點矩陣都可能對你的程序執行效率有負面的影響. 然而你可以對這些操作進行測試, 因為這個執行效率是對應于不同的執行而不同的 .
----------------------------------------------------------------------------------
22.使用glFrustum相對于gluPerspective的好處, 于壞處?為什么我們
glFrustum()和gluPerspective()都會產生透視投影矩陣, 它們把眼坐標轉換到剪裁坐標而來.這個主要的不同在于glFrustum更多的是產生和允許離軸(off-axis)的矩陣, 而gluperspective可以產生對稱的on-axis的矩陣. 確實, 你可以使用glFrustum來頂替gluPerspective. 然而,除了這個層次的調用本身是glu接口本身的一部分外, 使用glFrustum所產生的矩陣對于gluPerspective來說, 本身也沒有什么特殊的性能優勢.
自從glFrustum相對于gluPerspective更加的常規, 你就可以在萬一不能使用gluPerspective的時候, 使用它. 一些例子包括在 projection shadeows, 平鋪渲染, 和立體視圖中.
平鋪渲染 使用在多非軸線矩陣來渲染場景中的不同地方. 這個結果是集合了一個大型的圖象數據數組來產生最終的圖象 這個是非常必要的當這個最終渲染的期望的維數超過了OpenGL所能執行的最大視口維數.
在立體視圖中, 同樣的場景的兩個渲染圖將會被作為有輕微的改變的可視觀察位置. 當這個觀察軸在眼睛的右邊, 每個視圖都必須使用輕微的off-axis矩陣到任一邊, 來達到正確的可視結果.
---------------------------------------------------------------------------------
23.我如何執行一個到glFrustum的調用來匹配我對gluPerspective()?
這個你的glFrustum()調用的可視的角度(fov)是:
fov * 0.5 = arctan( (top - bottom) * 0.5 / near )
當 bottom == -top 對于由gluPerspective所產生的對稱投影, 然后:
top = (tan( fov * 0.5)) * near;
bottom = -top
注意: 上面公式中的fov的單位必須使用弧度,來配合在C語言的數學庫. 假如你想使用角度計算FOV(例如調用gluPerspective), 需要這樣進行計算:
top = tan( fov * 3.1415926 / 360 )
左邊和右邊的參數,只需要簡單的使用 top, bottom, aspect來計算:
left = aspect * bottom
right = aspect * topp
OpenGL Reference Manual 顯示了使用雙函數所產生的矩陣.
----------------------------------------------------------------------------------
24. 我如何畫一個全屏幕的四邊形?
這個問題通常的意思是, " 我如何畫一個可以填充我全部視口的四邊形?" 有很多方法可以做到.
最直接的方法就是先設置你所希望的顏色, 設置投影和模型視點矩陣為單位矩陣, 并調用glRectf()來畫一個和GL_QUADS相等的圖元. 你的矩形或者四面體的Z軸深度應該在-1.0到1.0之間, -1.0映射到zNear剪裁面, 1.0映射到zFar剪裁面.
這里有個例子, 這兒有個如何畫一個全屏幕的四面體在zNear剪裁面上:
glMatrixMode( GL_MODELVIEW );
glPushMatrix();
glLoadIdentity();
glMatrixMode( GL_PROJECTION );
glPushMatrix();
glLoadIdentity();
glBegin( GL_QUADS );
glVertex3i( -1, -1, -1 );
glVertex3i( 1, -1, -1 );
glVertex3i( 1, 1, -1 );
glVertex3i( -1, 1, -1 );
glEnd();
glPopMatrix();
glMatrixMode( GL_MODELVIEW );
glPopMatrix();
你的程序, 有可能是需要相應的矩形有個最大的Z軸值, 那樣的話, 你只需要修改Z軸用1來代替-1.
當繪制一個全屏幕的矩形時, 你可能屏蔽掉一些緩沖區來讓只有特殊的緩沖區可被影響. 例如, 你可能屏蔽掉顏色緩沖區并設置深度函數為 GL_ALWAYS, 所以就只有深度緩沖區被繪制.你也可以設置 碼區(mask)來允許模板緩存被設置或者更多的別的復合緩存.
----------------------------------------------------------------------------------
25.我如何找到一個給定物體的空間坐標所對應的屏幕坐標?
假如你只打算得到少數的幾個點, 你可以使用GLU庫中中的gluProject()函數. 假如要大堆的坐標, 使用系統的回掉機制可以更有效率.
為了使用gluProject(),你必須提供模型視點矩陣, 投影矩陣, 視口, 和輸入的物體空間坐標.屏幕空間的坐標將會被以 X, Y, Z的形式返回, 其中Z會被歸一化(normalize).
----------------------------------------------------------------------------------
26.我如何可以找到屏幕上一個像素所在的物體空間坐標?
這個GLU庫提供了gluUnProject()函數來實現這個操作.
你需要讀取深度緩存來獲得屏幕上的坐標的Z軸值在 X, Y處, 這段代碼可以如下.
GLdouble z;
glReadPixels( x, y, 1, 1, GL_DEPTH_COMPONENT, GL_DOUBLE, &z );
注意 X和Y是相對于屏幕左下角的(0, 0)位置.
你需要提供屏幕空間的X, Y和Z值作為 gluUnProject的輸入,另外還有當前像素正在被渲染的模型視點矩陣, 投影矩陣, 和視口.
----------------------------------------------------------------------------------
27.我如何可以找到被模型視點矩陣轉換過的像素坐標?
這個通常被用于得到頂點的眼坐標空間的值(i.e,物體的空間頂點被模型視點所轉換).你可以通過得到當前的模型視點矩陣并執行向量和矩陣的乘法來得到.
----------------------------------------------------------------------------------28.我如何計算觀察者到給定點的物體空間距離?
轉換這點到眼坐標空間,通過乘以模型視點矩陣. 然后簡單的計算它到原點的距離.(假如這個不能正常的工作, 你可能是把視點轉換放入了投影矩陣堆棧中了.)
來得到當前的模型視點矩陣:
GLfloat m[16];
glGetFloatv( GL_MODELVIEW_MATRIX, m );
對于使用任何的OpenGL調用, 你都可以使用glGet*()函數來得當前的窗口的上下文.
----------------------------------------------------------------------------------
29.在一個窗口被重新定義大小后, 我如何保持我的比例系數?
這個是基于你是如何設置你的投影矩陣. 對于任何一種情況, 你都需要知道你的窗口的維數(寬和高). 如何得到這些是基于你使用的平臺. 在GLUT, 例如, 這個維數是作為參數傳遞給 reshape回調函數.
接下來的是假設你的視口都使用和你的窗口同樣的尺寸. 假如你沒有, 使用窗口高度和寬度來代替視口的高度和寬度.
假如你使用gluPerspective來設置你的投影矩陣, 第二個參數是控制這個比例系數. 當你的程序捕捉到了一個新的大小, 你需要修改的投影矩陣如下:
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
gluPerspective( fov, (float) width/(float) height, zNear, zFar );
假如你使用glFrustum, 這個比例系數是將會是使用視景體的寬度和高度. 你可是使用下面的重設窗口大小代碼來支持1:1的比例系數:
float cx, halfWidth = windowWidth * 0.5f;
float aspect = (float)windowWidth/(float)windowHeight;
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
/* cx is the eye space center of the zNear plane in X */
glFrustum( cx - halfWidth*aspect, cx + halfWidth*aspect, bottom, top, zNear, zFar);
glOrtho()和gluOrho2D() 的調用視和glFrustum()非常相似的.
----------------------------------------------------------------------------------
30. 我可以讓OpenGL使用一個左手系空間?
OpenGL 并沒有這樣一個模式開關來轉換左右手的坐標. 然而,你可以非常容易的通過乘以以個負的Z軸縮放到模型視點矩陣來得到左手坐標系統. 例如:
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
glScalef( 1.0, 1.0, -1.0 );
/* 像平常那樣乘以視點轉換 */
/* 像平常那樣乘以模型轉換 */
----------------------------------------------------------------------------------
31.我如何轉換一個物體使它指向或者跟隨場景中的一個物體,或點?
你需要構造一個矩陣 , 它可以轉換你的物體的本地坐標系統到一個你所希望面對的方向的坐標系統. 看 example code , 是如何建造這類矩陣的.
假如你僅僅指是想渲染一個物體, 使它可以面對觀察者, 你可以考慮通過在眼坐標空間中把模型視點矩陣設置成單位矩陣來實現.
----------------------------------------------------------------------------------
32.我如何轉變我的物體使用一個給定的傾角, 仰角, 和斜角?
轉換矩陣的左上角的3X3部分, 是由自右轉換坐標空間的 X, Y, Z所構成.
----------------------------------------------------------------------------------
33.我該如何渲染處鏡面效果?
把你的場景渲染兩次, 其中一次, 作為你的鏡面的反光. 還有一次,作為來自正常的視圖(非反射的). Example Code 示范了這種技術.
對于一個軸對齊的鏡面, 例如一個在YZ平面上的鏡面. 這個反射場景可以使用簡單的縮放和平移. 使用 -1.0來乘以鏡面的法向量, 并進行縮放. 并平移兩倍的鏡面距離.使用適當的轉換來渲染這些場景會產生場景在鏡面中的反射.使用矩陣堆棧來保存上一次的可視轉換的值.
下一步, 使用glClear( GL_DEPTH_BUFFER_BIT )來清空深度緩存.然后渲染鏡面.對于一個完美反射的鏡面, 只需要簡單的渲染深度緩存. 真正的鏡面通常是不完美反射的, 他們很容易被一些別的光線所擾亂。為了建造這種效果, 使用混合技術來渲染一個鏡面使用0.05的alpha值. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_ALPHA)對于這個目的來說, 是個很好的混合函數.
最終, 渲染這些非反射場景. 當這個完全的反射場景存在于顏色緩存中, 而不是部分的場景在鏡面中.你可能需要改變反射場景中的全部像素來覆蓋這個區域,這樣它們會變的不可見.
--------------------------------------------------------------------------------
34.我如何做我自己的透視縮放?
OpenGL會用模型視點矩陣乘以你的坐標系, 然后通過投影矩陣來得到剪裁坐標系統. 然后會執行透視除法來獲得規范化設備坐標系.在透視除法階段會建立一個透視渲染, 使一個在遠處的幾何體會比在近處的小.透視除法階段通過用裁減坐標X, Y, Z的值來除以W來完成的. 例如:
Xndc = Xcc/Wcc;
Yndc = Ycc/Wcc;
Zndc = Zcc/Wcc;
為了執行你自己的透視糾正, 你需要獲得裁減坐標W的值.回掉(feedback)緩存提供了同樣的坐標系統對于XYZ設備坐標和W剪裁坐標.你可以使用 glGetFloatv(GL_CURRENT_RASTER_POSITION, ... ), 而且W值會再一次在裁減坐標系統中, XYZ在設備坐標系統中。