[從零構建光柵渲染器] 4.透視投影

[從零構建光柵渲染器] 4.透視投影

非常感謝和推薦Sokolov的教程,Sokolov使用500行C++代碼實現一個光柵渲染器。教程學習過程非常平滑,從畫點、線和三角形開始教學,在逐步深入三維變換,投影,再到頂點着色器,片段着色器等等。教程地址:https://github.com/ssloy/tinyrenderer。Sokolov的教程爲英文,我翻譯了其文章。

在學習過程中,有些內容可能您可能雲裏霧裏,這時就需要查閱《計算機圖形學》的書籍了,這裏面的算法和公式可以幫助您理解代碼。

作者:憨豆酒(YinDou),聯繫我[email protected],熟悉圖形學,圖像處理領域,本章的源代碼可在此倉庫中找到https://github.com/douysu/person-summary:如果對您有幫助,還請給一個star,如果大家發現錯誤以及不合理之處,還希望多多指出。

我的知乎
我的Github
我的博客

本章運行結果

圖片

提醒:

翻譯作者內容:相比上一章,本章的渲染效果加上了投影。如果本章內容有看不懂的地方,建議找一本圖形學的書籍閱讀一下變換部分,或者閱讀LearnOpenGL上面的教程。

已經有很多中文資料講解變換和投影了,可以配合着中文資料來學習本章。

目標

在前面的課程中,我們通過簡單地忘記z座標,用正投影的方式渲染了模型,今天我們學習如何使用透視除法。

2D幾何

一個平面上的線性變換可以用對應的矩陣表示,如果我們取一個點(x,y),那麼它的變換可以寫成如下所示。

圖片

比較簡單的變換是本身的變換,矩陣不移動任何點。

圖片

矩陣的對角線係數給出了沿座標軸的縮放比例。讓我們舉例說明一下,如果我們進行以下變換:

圖片

然後,白色物體(被砍掉一個角的白色方塊)將被轉化爲黃色物體。紅色和綠色的線段分別給出單位長度的向量,分別與x和y對齊。

圖片

文章使用的圖像生成代碼在這裏。this code.

我們爲什麼要用矩陣?因爲它很方便。首先,用矩陣的形式,我們可以這樣表達整個對象的變換。

圖片

在這個表達式中,變換矩陣與前一個表達式相同,但2x5矩陣除了我們的方塊狀對象的頂點外,其他的都不是什麼。我們只需把數組中的所有頂點都拿出來,乘以變換矩陣,就得到了變換後的對象。很酷吧?

那麼,真正的原因就隱藏在這裏:很多時候,我們希望在一連串的變換中,用很多變換來變換我們的對象。想象一下,在你的源碼中,你寫的變換函數像:

vec2 foo(vec2 p) return vec2(ax+by, cx+dy);
vec2 bar(vec2 p) return vec2(ex+fy, gx+hy);
[..]
for (each p in object) {
    p = foo(bar(p));
}

這段代碼對我們的對象的每個頂點進行兩次線性變換,往往我們計算這些頂點的次數是以百萬計。而且一排幾十次變換的情況並不罕見,導致幾千萬次的運算,真的很浪費資源。在矩陣形式下,我們可以將所有的變換矩陣進行預乘,對我們的對象進行一次變換。對於一個只有乘法的表達式,我們可以把括號放在想放的地方,是不是?

好了,讓我們繼續說下去。我們知道,矩陣的對角線係數是沿着座標軸對我們的對象進行縮放。那麼其他係數的作用是什麼呢?讓我們考慮一下下面的變換。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-lbuNPA1B-1588752935016)(https://uploader.shimo.im/f/Sk7uYu2V4yqbhnKH.png!thumbnail)]

結果成這樣:

圖片

這是一個簡單的沿x軸的錯切,另一個反對角元素沿着y軸錯切空間。因此,在一個平面上有兩個基礎線性變換:縮放和錯切。很多讀者反映,那旋轉呢?

原事實證明,任何旋轉(繞原點旋轉)都可以表示爲三個錯切的組合動作,這裏白色對象被轉換爲紅色對象,然後轉換爲綠色對象,最後轉換爲藍色對象:

圖片

但這些就很複雜了,爲了讓事情簡單,我們直接寫一個旋轉矩陣。

翻譯作者內容:可以查看圖形學的書籍變換的內容。

我們可以按照任意順序進行乘法,但我們要記住,對於矩陣的乘法是不換向的。

圖片

這句話是有道理的:錯切一個物體,然後再去旋轉它,和旋轉後再去錯切它是不一樣的!

圖片

翻譯作者內容:在線性代數中學習過,矩陣相乘是不可逆的。

2D 仿射變換

所以,平面上的任何線性變換都是由縮放和錯切變換組成的。而這意味着,我們可以做任何線性變換,原點永遠不會移動! 這些可行性是很大,但如果我們不能進行簡單的變換,我們的編程就很難過。我們能做到嗎?好吧,平移不是線性的,沒問題,讓我們在執行完線性部分後,再嘗試着進行附加平移。

圖片

這個表達方式真的很酷。我們可以進行旋轉、縮放、錯切和平移。然而,讓我們回想一下,我們感興趣的是組成多個變換。下面是兩個變換的組成是什麼樣子的(記住,我們需要組成幾十個這樣的變換)。

圖片

只是一個頂點(x,y)變換就得嵌套這麼多層,這顯然是不行的。

下面我們就想辦法換成一個矩陣。

齊次座標

好了,現在是黑魔法的時候了。想象一下,我在我們的變換矩陣中加入一列,一列(從而使其成爲3x3),並將一個座標始終等於1的座標附加到要變換的向量上。例如下面這樣。

圖片

如果我們把這個矩陣和向量乘以1,我們就會得到另一個向量,最後一個分量中的1,但另外兩個分量的形狀和我們想要的完全一樣很神奇。觀察到與上一節的計算結果是相同的。

事實上,這個想法其實很簡單。平行平移在2D空間中不是線性的。所以我們把我們的2D空間嵌入到3D空間中(只需在第3個分量中加1即可)。也就是說,我們的2D空間就是3D空間中的平面z=1。然後我們進行線性三維變換,並將結果投影到我們的二維物理平面上。平行變換並沒有變成線性變換,但流水線很簡單。

我們如何將3D投影到2D平面上?很簡單,就是用3D分量進行除法。

圖片

等一下,禁止除以0!

誰說的這個。讓我們回顧一下管線

  • 我們將2D嵌入到3D中,將其嵌入到z=1的平面內。
  • 我們可以在3D中爲所欲爲
  • 對於每一個要從三維投影到二維的點,我們在原點和要投影的點之間畫一條直線,然後找到它與平面z=1的交點。

在這個圖像中,我們的二維平面是品紅色的,點(x,y,z)被投影到(x/z,y/z)上。

圖片

讓我們想象一下點(x,y,1)垂直投影會投影到哪裏?答案是在(x,y)平面上:

圖片

現在讓我們下降到軌道上,例如,將點(x,y,1/2)投影到(2x,2y)上:

圖片

讓我們繼續,點(x,y,1/4) 成爲(4x, 4y):

圖片

如果我們繼續這個過程,接近z=0,那麼投影就會在(x,y)的方向上離原點更遠。換句話說,點(x,y,0)在(x,y)的方向上被投影到一個無限遠的點上。它是什麼呢?對了,它只是一個向量!

齊次座標可以區分向量和點。如果一個程序員寫了vec2(x,y),那麼它到底是向量還是點呢?很難說。結論就是:在齊次座標中,所有z=0的東西都是向量,其餘的都是點。看:向量+向量=向量。向量-向量=向量。點+向量=點。這樣比較好,不是嗎?

綜合變換

正如我之前說的,我們應該積累數十個轉換。什麼要這樣做?讓我們想象一下,我們需要將一個物體(2D)圍繞一個點(x0,y0)旋轉。怎麼做呢?我們可以在某處查找公式,或者我們可以自己去完成,我們需要的工具都有了!

圖片

在3D中,動作的序列會有點長,但想法是一樣的:我們需要知道一些基本的變換,在它們的幫助下,我們可以表示任何組成的動作。

翻譯作者內容:先平移,在旋轉,在平移回來就完成了。

等一下,3×3矩陣的底部是做什麼用的呢

這部分比較難理解,推薦看這裏。https://zhuanlan.zhihu.com/p/66384929

對角線是縮放,右邊是平移,那下面的內容是幹什麼用的呢。

當然可以!讓我們將以下轉換應用於我們的標準方形對象:

圖片

回想一下原始對象是白色的,單位軸矢量是紅色和綠色的:

圖片

這是轉換後的:

圖片

這裏又發生了另一種有意思的事情(白色!)。你還記得我們的y-buffer練習嗎?這裏我們也要做同樣的練習:我們把二維物體投射到垂直線x=0上,讓我們把規則變困難一點:我們必須使用中心投影,我們的相機在點(5,0)上,並指向原點。爲了找到投影,我們需要在攝像機和要投影的點(黃色)之間畫出直線,並找到與屏幕線的交點(白色垂直)。

圖片

現在,我用轉換後的對象代替原來的對象,但我不碰我們之前畫的黃色線條:

圖片

如果我們用標準的正交投影法將紅色物體投射到屏幕上,那麼我們就會發現完全一樣的點! 讓我們仔細觀察一下變換的工作原理:所有的垂直段都會被變換成垂直段,但靠近攝像頭的部分會被拉伸,而遠離攝像頭的部分會被縮小。如果我們正確地選擇係數(在我們的變換矩陣中是-1/5係數),我們就會得到一個透視(中心)投影的圖像!如果我們正確地選擇係數,我們就會得到一個透視(中心)投影的圖像。

到3D空間工作了

讓我們解釋一下其中的奧妙。對於二維仿射變換,對於三維仿射變換,我們將使用齊次座標:將一個點(x,y,z)用1(x,y,z,1)增強,然後在4D中進行變換,再投射回三維。例如,如果我們取下面的變換。

圖片

逆向投影給了我們以下的三維座標。

圖片

讓我們記住這個結果,但暫時把它放在一邊。讓我們回到中心投影的標準定義,沒有任何花哨的東西作爲4D變換。給定一個點P=(x,y,z),我們要把它投影到z=0的平面上,相機在z軸上的點(0,0,c)。

圖片

三角形ABC和ODC是相似的。這意味着我們可以寫出以下的內容。 |AB|/|AC|=|OD|/|OC| => x/(c-z) = x’/c。換句話說,就是:

圖片

通過對三角形CPB和CP’D進行同樣的推理,很容易找到下面的表達式。

圖片

這真的和我們剛纔的結果很相似,但是在那裏我們通過一個矩陣乘法得到了結果。我們得到了係數的規律:r=-1/c。

讓我們總結我們今天學習的公式

如果你在不理解上面的材料的情況下就簡單地複製粘貼這個公式,我恨死你了。

所以,如果我們想用一個位於z軸上的攝像頭(重要!)相機在距離原點c的z軸上,計算出一箇中心投影,那麼我們把這個點用1的方法將其增強到4D中,然後用下面的矩陣乘以1,再將其逆向投影到3D中。

圖片

我們對我們的對象進行了變形,只需忘記它的Z座標,我們就可以得到一個透視畫。如果我們要使用z-buffer,那麼自然也不要忘記z。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章