原文地址:http://blog.sina.com.cn/s/blog_e7779a160102wpt1.html
3D圖形學理論入門指南
介紹
當我還小的時候,我曾以為計算機圖形學是最酷的玩意兒。但是隨即我認識到,學習圖形學——創建那些超級閃亮的計算機程序——比我想象的要難上許多。我四處出擊,閱讀OpenGL渲染管線詳解之類的文章,瀏覽關於圖形工作原理的博客、網站等,對照着教程學習,試圖搞懂一切。結果呢,一無所獲。當我按照NeHe的教程設置好一切,卻因為錯誤的調用了某個glXXX()函數,導致各種錯誤。我不具備正確調試程序的基礎理論知識,所以我放棄了——就像我那個年紀的少年在遇到挫折時通常會做的那樣。
然而,在若干年之后,我有機會能夠在大學里參加一些計算機圖形學的課程。這次我終於知道它們是如何正確工作了。如果我早知道這些,我那時應該能獲得更多成功。所以,為了幫助和我有類似困境的人們,我打算分享下我學到的東西。
圖形學背后的理念
概覽
先想想真實世界的樣子。在3D真實世界里,光線從許多個不同的光源發出,在多個物體間跳轉,然后一部分光子通過眼球刺激到你的視網膜。在真實的場景里,3D的世界投影到2D的表面。雖然你的大腦從環境中獲取各種視覺元素然后組成一個立體的影像來反映整個3D空間,但這些都源於2D信息。當場景中的物體移動,或者你相對於你的場景發生移動,或光照改變時,視網膜上的2D圖像也立刻發生改變。我們的視覺系統快速處理圖像,然后大腦據此構造出3D模型。


如果我們能夠獲取一些圖片,然后以類似或更高的速率來交替顯示它們,就能生成一個看起來像真實空間的場景。電影大致基於同一原理工作。在電影里,3D場景的圖片高速閃過,看起來就像連續的一樣。請參照上面馬的例子。如果我們能夠在計算機上持續的繪制一個運動的場景,那么它看起來就像一個3D的世界一樣。圖形學就是這樣工作的:將虛擬的3D世界快速轉換成2D表現形式,讓大腦感覺像是一個3D的場景一樣。
約束條件
人類視覺將一系列圖片看作連續的閾值大約時16Hz。對計算機來說,我們有最多62.5毫秒來完成下列事情:
判斷虛擬場景中眼睛看向哪里。
計算場景在這個角度下如何呈現。
計算需要被繪制在屏幕上的像素的顏色。
用這些顏色填充幀緩存。
將緩存發送至顯示設備。
顯示圖片。
這是一個復雜的
問題。時間上的限制意味着我們不能直接硬來——比如往3D場景里扔一堆光子,計算它們的軌跡和強度,算出哪些能夠照進眼睛並將之映射到2D的圖片上,最后繪制。(這並不完全正確,因為這有點像光線追蹤時做的事。但光線追蹤技術相當復雜,而且完全是另一回事,因此也可以這么說。)幸運的是,我們可以利用一些很酷的技巧來大大縮減計算量。
基礎圖形理論
整個世界是一個舞台

如果我們能夠獲取一些圖片,然后以類似或更高的速率來交替顯示它們,就能生成一個看起來像真實空間的場景。電影大致基於同一原理工作。在電影里,3D場景的圖片高速閃過,看起來就像連續的一樣。請參照上面馬的例子。如果我們能夠在計算機上持續的繪制一個運動的場景,那么它看起來就像一個3D的世界一樣。圖形學就是這樣工作的:將虛擬的3D世界快速轉換成2D表現形式,讓大腦感覺像是一個3D的場景一樣。
約束條件
人類視覺將一系列圖片看作連續的閾值大約時16Hz。對計算機來說,我們有最多62.5毫秒來完成下列事情:
判斷虛擬場景中眼睛看向哪里。
計算場景在這個角度下如何呈現。
計算需要被繪制在屏幕上的像素的顏色。
用這些顏色填充幀緩存。
將緩存發送至顯示設備。
顯示圖片。
這是一個復雜的
問題。時間上的限制意味着我們不能直接硬來——比如往3D場景里扔一堆光子,計算它們的軌跡和強度,算出哪些能夠照進眼睛並將之映射到2D的圖片上,最后繪制。(這並不完全正確,因為這有點像光線追蹤時做的事。但光線追蹤技術相當復雜,而且完全是另一回事,因此也可以這么說。)幸運的是,我們可以利用一些很酷的技巧來大大縮減計算量。
基礎圖形理論
整個世界是一個舞台

假設我們要分解一個球形。我們可以將球體的中心位置定為本地的原點。這樣我們就可以用一個公式來獲得球面上的一些點然后將這些點連接成多邊形以供繪制。一個常用的公式是S(u,v)=[r sin u cos v,r sin u sin v,r cos v],u和v的取值范圍分別是u∈[0,π],v∈[0,2π],r是球體的半徑。就像你在圖中看到的那樣,球體表面的點被繪制成矩形。我們能夠很方便的把它們連成三角形。
球面上的點位於所謂的模型坐標系。坐標相對於本地的原點定義,比如示例中球體的中心位置。如果我們想要將物體放置於場景中,我們可以定義一個從場景原點到場景中球體的原點的向量,然后把這個向量和球面上每個點的坐標相加。這樣我們就將模型放置到了世界坐標系中。
世界坐標系——將物體置於世界中
到這兒我們的圖形學之旅才真正開始。我們在某處定義一個原點,場景中的每個點都基於從原點到該點的一個向量來定義。雖然場景是3D的,我們還是得用一個4維的坐標來定義每個點[x,y,z,w],代表該點的坐標為[x/w,y/w,z/w]。這種映射稱為齊次坐標。使用齊次坐標有一些好處,但是這里不做討論。只需知道我們使用齊次坐標就夠了。
假設我們要在場景中移動,那么問題來了。如果我們要移動視線,或者移動相機到另一個位置,或者讓整個世界圍着相機移動。在計算機的世界里,移動整個世界更容易一點,所以我們就這樣做,讓相機固定不動。模型-視圖矩陣(modelview matrix)是一個4x4矩陣,可以用來移動世界中的每一個點,然后讓相機固定不動。這個矩陣基本上就是一系列旋轉、位移、縮放的集合。我們在世界坐標系中將點和模型-視圖矩陣相乘,這將使我們進入觀察坐標系(viewing coordinates)。

假設我們要分解一個球形。我們可以將球體的中心位置定為本地的原點。這樣我們就可以用一個公式來獲得球面上的一些點然后將這些點連接成多邊形以供繪制。一個常用的公式是S(u,v)=[r sin u cos v,r sin u sin v,r cos v],u和v的取值范圍分別是u∈[0,π],v∈[0,2π],r是球體的半徑。就像你在圖中看到的那樣,球體表面的點被繪制成矩形。我們能夠很方便的把它們連成三角形。
球面上的點位於所謂的模型坐標系。坐標相對於本地的原點定義,比如示例中球體的中心位置。如果我們想要將物體放置於場景中,我們可以定義一個從場景原點到場景中球體的原點的向量,然后把這個向量和球面上每個點的坐標相加。這樣我們就將模型放置到了世界坐標系中。
世界坐標系——將物體置於世界中
到這兒我們的圖形學之旅才真正開始。我們在某處定義一個原點,場景中的每個點都基於從原點到該點的一個向量來定義。雖然場景是3D的,我們還是得用一個4維的坐標來定義每個點[x,y,z,w],代表該點的坐標為[x/w,y/w,z/w]。這種映射稱為齊次坐標。使用齊次坐標有一些好處,但是這里不做討論。只需知道我們使用齊次坐標就夠了。
假設我們要在場景中移動,那么問題來了。如果我們要移動視線,或者移動相機到另一個位置,或者讓整個世界圍着相機移動。在計算機的世界里,移動整個世界更容易一點,所以我們就這樣做,讓相機固定不動。模型-視圖矩陣(modelview matrix)是一個4x4矩陣,可以用來移動世界中的每一個點,然后讓相機固定不動。這個矩陣基本上就是一系列旋轉、位移、縮放的集合。我們在世界坐標系中將點和模型-視圖矩陣相乘,這將使我們進入觀察坐標系(viewing coordinates)。
我們可以改變這個矩陣以應對不同情況如正交或透視。透視圖里有一個消失的點,正交視圖沒有。通常在繪畫里見到的是透視圖,正交視圖在技術圖中中較為多見。因為這個矩陣決定了物體是如何投影到屏幕上的,所以也叫做投影矩陣。t,b,l,r,n,f代表頂部、底部、左側、右側、近處、原處的裁剪面的坐標。乘以投影矩陣將使點從觀察坐標系前往所謂的裁剪坐標系(clip coordinates)。



我們可以改變這個矩陣以應對不同情況如正交或透視。透視圖里有一個消失的點,正交視圖沒有。通常在繪畫里見到的是透視圖,正交視圖在技術圖中中較為多見。因為這個矩陣決定了物體是如何投影到屏幕上的,所以也叫做投影矩陣。t,b,l,r,n,f代表頂部、底部、左側、右側、近處、原處的裁剪面的坐標。乘以投影矩陣將使點從觀察坐標系前往所謂的裁剪坐標系(clip coordinates)。

裁剪坐標系——只繪制能看到的
這個坐標系有點不同,因為它是左手坐標系(在此之前我們一直使用的右手坐標系),而且是從我們之前定義的視錐體映射到一個x,y,z范圍都在(-1,1)之間的正方體。
到現在為止,我們一直追蹤場景中的所有點。然而,一旦進入裁剪空間,我們就可以開始裁掉一部分了。還記得坐標從4D到3D的轉換嗎?我們曾說過,[x,y,z,w]4D=[x/w,y/w,z/w]3D。因為我們只需要位於視錐范圍以內的點,我們接下來只需處理符合−1≤x/w≤1或−w≤x≤w的點即可。y和z坐標也一樣。這是一個簡單的辦法分辨一個點是否位於我們視野之內。
如果某些點位於視錐體內,我們對它們執行透視出發(perspective divide),對每個坐標除以w來將其從4D坐標轉換成3D坐標。這些點還是位於左手裁剪坐標系中,但是到了這個階段,我們稱其為規格化設備坐標(normalized device coordinates)。
規格化設備坐標系——計算遮擋關系
你可以把這個想成映射到圖像的中間步驟。想象一下所有可能的圖像大小,我們並不想渲染成一張圖片然后進行各種縮放或拉伸或當圖像大小發生改變時重新渲染。規格化設備坐標(NDC)很有用,因為無論圖片最終大小是多少,你可以在NDC里面針對性的進行合適的縮放。在NDC里你將看到圖片如何被構造。被渲染的圖像是視錐體里的物體在近裁剪面上的投影。因此,一個點在Z軸上的值越小,這個點就越近。
這個階段,通常我們不再進行矩陣計算,而是應用一個視窗變換。這通常只是拉伸坐標來適應視窗,或最終圖像大小。最后一步是通過轉換坐標到窗口坐標來繪制圖像。
窗口坐標系——將物體縮放到畫布
窗口是圖像最終被繪制的地方。在這里,我們的3D世界呈現為近裁剪面上的一張2D圖像。我們可以使用一系列的線條和多邊形算法來繪制最終圖像。此時,一些2D效果,如抗鋸齒和多邊形裁剪,在圖片被被繪制之前執行。
然而,窗口可能有不同的坐標系統。比如,有時圖片基於向右為X+,向下為Y+繪制。為了正確繪制圖片,有時候可能需要做一些轉換。
又回來了——圖形渲染管線
上述步驟你不用都親歷親為。某種程度上,你會使用圖形渲染庫來定義諸如模型視圖矩陣、投影矩陣以及世界坐標系中的多邊形之類的東西,渲染庫會搞定一切。如果你在設計一個游戲,你不需要在意多邊形是如何被繪制的,只需確保它們執行的正確又快速,對嗎?
OpenGL和DirectX之類的庫效率很高,而且能夠有效利用精密的圖形硬件來簡單又快速的執行這些計算。它們廣泛使用,所以最好適應它們。它們還給你留下很大空間來自定義一些事情,你會為你能做到的某些事感到驚訝的!
結論
這是一個關於圖形學理論的簡單概覽。渲染過程中后續還有很多步驟發生,但是這應該能給你一個大致的方向,讓你在閱讀其它文章或論壇里的相關技術時能夠理解的更好。
外部鏈接
原文標題:The Total Beginner's Guide to 3D Graphics Theory