• <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>

            Z坐標、深度緩存和透視投影

            第一部分:
             這個是3D圖形學中很重要的內容,雖然基礎,但對理解3D圖形世界非常關鍵。所以了解透徹是很有用處的。

            下面先講講Z坐標。Z坐標和X、Y坐標一樣。在變換、裁減和透視除法后,Z的范圍為-1.0~1.0。DepthRange映射指定Z坐標的變換,這與用于將X和Y映射到窗口坐標的視口變換類似,但DepthRange映射又與視口映射有所不同,因為深度緩存的硬件方案對應用程序來說是隱藏的。調用DepthRange的參數是[0.0,1.0],與一片斷相聯的Z值(深度值)表示到眼睛的距離。在默認情況下,最接近眼睛的片斷(在近截面上)被映射到0.0,離眼睛最遠的片斷(在遠截面上)映射到1.0。片斷可以映射為深度緩存范圍的子集(通過在DepthRange中指定更小的值)。映射也可以相反,這樣的話離眼睛最遠的片斷在0.0,最近的片斷在1.0(調用DepthRange(1.0,0.0)),雖然這樣反向映射是可以的,但對實際應用作用不大。

            要理解為什么渲染質量上會不一致,最重要的是要理解屏幕Z坐標的特性。Z值指定了從片斷到眼睛的距離。在正交投影中距離和Z值的關系是線性的,但在透視投影中卻不是的。在透視投影中這種關系是非線性的,而且非線性的程度與Frustum函數中的far/near(或gluPerspective函數中的zFar/zNear)成比例。這種非線性在靠近近截面時增加了Z值的精度,而且增加了深度緩存的效率;但是在視見體的其它部分則降低了精度,也就減少了深度緩存的精確性。根據經驗,far/near比值大于1000會有這種不好的效果。所以一般far/near比值應小于1000。要想解決這個問題,最簡單的方法是通過將近截面遠離眼睛來降低far/near比值,其唯一的副作用是離眼睛很近的物體可能會被裁減掉,但在特定的應用程序中這很少是個問題,近截面的位置對X、Y坐標的投影沒有影響,因此這對圖像的影響很小。

            還有OpenGL光柵化和深度緩存的一些其他方面值得一提。一個大問題是光柵化過程使用不精確的算法,所以很難處理共面的圖元,除非它們共享相同的平面方程。這個問題在有限精度的深度緩存實現中更加嚴重。這些問題包括:貼花(decaling)、隱藏線消除、輪廓多邊形和陰影等。不過現在已經提出許多方法來解決這些問題,如PolygonOffset技術等。

            深度緩存的位數是衡量深度緩存精度的參數。深度緩存位數越高,則精確度越高,目前的顯卡一般都可支持16位的Z Buffer,一些高級的顯卡已經可以支持32位的Z Buffer,但一般用24位Z Buffer就已經足夠了。


            第二部分:

             ? 幾乎所有目前的 3D 顯示晶片都有 Z buffer 或 W buffer。不過,還是常常可以看到有人對 Z buffer 和 W buffer 有一些基本的問題,像是 Z buffer 的用途、Z buffer 和 W buffer 的差別、或是一些精確度上的問題等等。這篇文章的目的就是要簡單介紹一下 Z buffer 和 W buffer。
              Z buffer 和 W buffer 是做什么用的呢?它們的主要目的,就是去除隱藏面,也就是 Hidden surface elimination(或是找出可見面,Visible surface detemination,這是同樣意思)。在 3D 繪圖中,只要有兩個以上的三角面,就可能會出現某個三角面會遮住另一個三角面的情形。這是很明顯的現象,因為近的東西總是會遮住遠的(假設這些三角面都是不透明的)。所以,在繪制 3D 場景時,要畫出正確的結果,就一定要處理這個問題。
              
              不過,這個問題是相當困難的,因為它牽扯到三角面之間的關系,而不只是某個三角面本身而已。所以,在做去除隱藏面的動作時,是需要考慮場景中所有的三角面的。這讓問題變得相當的妖雜。而且,三角面往往并不是整個被遮住,而常常是只有一部分被遮住。所以,這讓問題變得更妖雜。
              
              要做到去除隱藏面的最簡單方法,就是「畫家演算法」(Painter's algorithm)。這個方法的原理非常簡單,也就是先畫遠的東西,再畫近的東西。這樣一來,近的東西自然就會蓋住遠的東西了。因為油畫的畫家通常會用這樣的方法,所以這個方法被稱為「畫家演算法」。下圖是一個例子:
              
             

              上圖中,紅色的圓形最遠,所以最先畫。然后是黃色的三角形,最后是灰色的方形。照遠近的順序來畫,就可以達到去除隱藏面的效果。所以,只要把 3D 場景中的三角面,以對觀察者的距離遠近排序,再從遠的三角面開始畫,應該就可以畫出正確的結果了。
              
              不過,實際上并沒有這么理想。在 3D 場景中,一個三角面可能有些地方遠,有些地方近,因為三角面有三個頂點,而這三個頂點和觀察者的距離,通常都是不同的。所以,要以哪個頂點來排序呢?或是以三角面的中心來排序?事實上,不管以什么為依據來排序,都可能會有問題。下圖是一個「畫家演算法」無法解決的情形:
              
             

              上圖中,三個三角面互相遮住對方,所以不管用什么順序去畫,都無法得到正確的結果。另外,這個方法也無法處理三角面有交叉的情形。
              
              當然,如果相當確定場景中不會出現這么奇怪的情形,那「畫家演算法」一般還是可以用的。不過,它還有一個很大的問題,就是效率不佳。首先,畫家演算法需要對場景中,在視角范圍內所有的三角面做一個排序的動作。最好的排序演算法也需要 O(n log n) 的時間。也就是說,(大致上來說)如果三角面的數目從一千個變一萬個,排序需要的時間會變成約 13.3 倍。而且,因為這需要對場景中所有的三角面來做,因此也不適合用特別的硬體來做加速。另外,這個方法還有一個很大的問題,就是它會花很多時間去畫一些根本就會被遮住的部分,因為每個三角面的每個 pixel 都需要畫出來。這也會讓效率變差。
              
              如果場景是靜態(不動)的,只有觀察者會變動的話,那是有方法可以加快排序的速度。一個很常用的方法是 binary space paritioning(BSP)。這個方法需要事先對場景建立一個樹狀結構。建立這個結構后,不管觀察者的位置、角度是如何,都可以很快找出正確的繪制順序。而且,BSP 會視需要切開三角面,以處理像上圖那樣,三個三角面互相遮住對方的情形。
              
              不過,BSP 結構在建立時,要花很多時間,所以不太可能即時運算。因此,通常只能用在場景中的靜態部分,而會動的部分還是需要另外排序。而且,BSP 常會需要切開三角面,也會讓三角面的數目增加。另外,BSP 仍然無法解決需要畫出那些被遮住的 pixel 的問題。
              
              另一種去除隱藏面的方法,是直接以 pixel 為單位,而不是以三角面為單位,來考慮這個問題。其中最簡單的方法是由 Catmull 在 1974 年時提出來的,也就是 Z buffer(或稱 depth buffer)。這個方法非常簡單,又容易由特別設計的硬體來執行,所以在記憶體容量不再是問題后,就變得非常受歡迎。
              
              Z buffer 的原理非常簡單。在繪制 3D 場景時,除了存放繪制結果的 frame buffer 外,另外再使用一個額外的空間,也就是 Z buffer。Z buffer 記錄 frame buffer 上,每個 pixel 和觀察者的距離,也就是 Z 值。在開始繪制場景前,先把 Z buffer 中所有的值先設定成無限遠。然后,在繪制三角面時,對三角面的每個 pixel 計算該 pixel 的 Z 值,并和 Z buffer 中存放的 Z 值相比較。如果 Z buffer 中的 Z 值較大,就表示目前要畫的 pixel 是比較近的,所以應該要畫上去,并同時更新 Z buffer 中的 Z 值。如果 Z buffer 中的 Z 值較小,那就表示目前要畫的 pixel 是比較遠的,會被目前 frame buffer 中的 pixel 遮住,所以就不需要畫,也不用更新 Z 值。這樣一來,就可以用任意的順序去畫這些三角面,即可得到正確的繪制結果。下圖是一個例子:
              
             

              上圖中,紅色的三角面雖然先畫出來,但是因為使用了 Z buffer,所以后畫的黃色方塊還是只會遮住適當的部分,而不會連較近的部分都遮住。這就顯示出 Z buffer 的效果。
              
              實際上 Z buffer 中能存放的數字當然會有一定的限度,所以通常會把 Z 值縮小到 0 ~ 1 的范圍。因此,在繪制 3D 場景時,就會需要把可能出現的 Z 值限制在某個范圍內。通常是用兩個和投影平面平行的平面,把所有超出這兩個平面范圍的三角面都切掉。這兩個平面通常分別稱為 Z near 和 Z far,分別表示較近的平面和較遠的平面。而在 Z near 平面的 Z 值為 0,在 Z far 的 Z 值為 1。
              
              在效率上 Z buffer 并不一定會比「畫家演算法」要快。但是,它比較簡單。而且,它的效率和三角面的數目并沒有太大的關系,而是和繪制的 pixel 數目有關。所以,而且可以很容易設計出特定的 3D 硬體來做這個動作,而不需要由 CPU 來做。而 Z buffer 所需要的額外記憶體,在今天已經顯得不是很重要。所以現在幾乎所有的 3D 顯示晶片都是使用 Z buffer。
              
              不過,Z buffer 并非全無問題。一個很大的問題是在於精確度上。如果有兩個三角面很靠近,而其中一個完全在另一個之前,那應該只能看到一個三角面才對。但是,如果 Z buffer 的精確度不夠,那這兩個三角面每個 pixel 的 Z 值可能會很接近。再加上計算出來的 Z 值一定會有誤差,所以,很可能會造成應該被遮住的三角面,卻有一些 pixel 沒有被遮住。這種情形稱為 Z fighting。下圖中,球在地面上的影子就是一個例子:
              
             

              要避免這類問題,就要避免在場景中出現太過靠近,且接近平行的三角面。一般的場景不太會出現這個情形。不過,Z buffer 的精確度問題并不只是這樣而已。在下一部分會對這個問題有更詳細的說明。
              
              前面把 Z buffer 的原理做了一個大概的說明,聽起來 Z buffer 似乎是個很理想的技術。但是,實際上 Z buffer 有一個很大的問題,就是精確度的問題。
              
              在前一頁后面所提到的,兩個非常接近的平面所出現的 Z fighting 情形,其實是相當少見,而且很容易避免的。當然,遇而還是會看到有一些游戲會出現這種情形。不過,Z buffer 最嚴重的問題是在離觀察者較遠的部分。如果 Z buffer 的精確度不夠,而場景又很遠的的話,那遠處的東西就會出現一些非常奇怪的現象。下圖是一個例子:
              
             

              Z aliasing
              
             

              無 Z aliasing
              當然,上面的例子是比較極端的情形。實際上一般情形下并不會有這么夸張的 Z aliasing 現象。不過,我相信大家多少都在一些場景較大的游戲中,看過類似的情形。
              
              為什么會有這樣的現象呢?這就要從 Z buffer 的結構談起了。如果前一頁所說的,一般的顯示晶片,是把 Z 值限制在 0 ~ 1 的范圍,再用一個定點數去表示它。例如,一個 16 位元的 Z buffer,可能會用 0 ~ 65535(一個 16 位元數字可表示的范圍)來表示這個 0 ~ 1 之間的 Z 值。
              
              如果 Z buffer 的分布在 eye space 中是線性的,也就是它的每個數字之間的間隔都相等的話,那這樣的精確度應該是蠻高的才對。因為,假設觀察者可以看到一公里遠的東西,那每個間隔就是約 1.5 公分。如果用更高精確度的數字來表示的話(像是 24 位元數字),那精確度還會更高。然而,Z buffer 在 eye space 中并不是線性的。它是在 projection space 中為線性。
              
              如果你覺得這些聽起來像是外星話的話,現在就要來「翻譯」這些外星話。首先,先來看一張示意圖:
              

              上圖是一個眼睛在透視投影的情形下,觀看場景中的一個紅色平面的情形。靠近眼睛的平面(上面有黃色點的)是代表投影平面,也就是 3D 繪圖中的螢幕。黃色的點紅色平面投影到螢幕上的 pixel,他們當然是等間距的。但是,注意看這些「等間距」的 pixel,他們所對映的 Z 值(也就是 Z 軸上的那些灰色的點),并不是等間距的。實際上,離眼睛愈遠的 pixel,其 Z 軸上的間距就愈大。
              
              這其實透視投影的一個明顯的性質。因為在透視投影的情形下,愈遠的東西看起來愈小,所以,在螢幕上同樣的間距,在比較遠的地方,就會變得比較大。因此,雖然三角面是平面,但是它在每個 pixel 上的 Z 值卻不是線性的變化。因此,就無法用線性內插來計

            posted on 2006-10-25 11:17 zmj 閱讀(4271) 評論(4)  編輯 收藏 引用

            評論

            # re: Z坐標、深度緩存和透視投影 2007-04-19 19:56 OUYANG2008

            你好,我有個問題
            在3D場景中,實際上X,Y,Z用的值要有很大的空間,而他有規定他們的范圍都是
            -1~1,特別是在NDC中,這樣做有什么必要?
            事實上,在渲染3D場景中,我們用的坐標都很是世界坐標,給他們是不是一個東西。  回復  更多評論   

            # re: Z坐標、深度緩存和透視投影[未登錄] 2007-05-10 15:02 Leo

            @OUYANG2008
            16bit Z buffer的分辨精度是 2^16,類似的24bit 是 2^24.和芯片硬件設計相關。無法精確表達3個double值獲得的深度值。

            深度值是世界坐標XYZ在屏幕坐標UVW的W方向的投影。

            OpenGL 將其轉換為0-1之間的浮點值,我認為是為了使OpenGL調用與硬件配置無關,類似的設計在顏色設置等方面也可以看到。

              回復  更多評論   

            # re: Z坐標、深度緩存和透視投影 2007-06-16 13:21 OUYANG2008

            @Leo
            若果說是因為硬件相關性的話,那么他表示成FLOAT類型的值也是與硬件相關的,有一些PAPER上說是精度的需要,深度數據和真實的深度不是單純的比例關系,總之我不怎么清楚,呵呵。OPENGL里大量應用這方面的操作,不麻煩的話請再寫一個這方面的說明,THS.

              回復  更多評論   

            # re: Z坐標、深度緩存和透視投影 2008-03-14 21:27 Nico

            最后一幅圖: 如果紅線上的點是等間距的,平行投影到“Z軸”,
            怎么會變成不是等間距的呢? 這不是簡單的幾何原理嗎 ?
              回復  更多評論   

            成人午夜精品久久久久久久小说| 久久综合九色综合网站 | 欧美日韩精品久久久久| 品成人欧美大片久久国产欧美| 久久www免费人成看国产片| 久久国内免费视频| 99国产精品久久久久久久成人热| 国产精品99久久久久久www| 久久人妻AV中文字幕| 精品午夜久久福利大片| 一级女性全黄久久生活片免费| 久久久久亚洲av无码专区| 久久亚洲中文字幕精品一区四| 伊人久久大香线蕉av一区| 久久香蕉国产线看观看猫咪?v| 久久久精品2019免费观看| 亚洲国产精品综合久久网络| 久久精品国产亚洲av麻豆小说 | 久久国产亚洲高清观看| 久久午夜无码鲁丝片午夜精品| 久久国产亚洲高清观看| 中文字幕无码久久久| 精品久久久久久99人妻| 国产精品一久久香蕉产线看| 精品久久久久久中文字幕大豆网| 国产精品亚洲美女久久久| 国产成人精品久久一区二区三区| 久久综合久久综合亚洲| 久久黄视频| 久久精品国产亚洲av瑜伽| 亚洲嫩草影院久久精品| 久久久久四虎国产精品| 精品国产一区二区三区久久| 久久亚洲精品成人AV| 99精品久久精品| 久久国产精品久久| 国产99精品久久| 国产成人无码精品久久久免费| 亚洲国产精品久久66| 精品一久久香蕉国产线看播放| 精品久久人人妻人人做精品|