OpenGL矩陣變換的數學推導

2021-02-08 騰訊光影研究室

說起OpenGL的矩陣變換,我是之前在我們的項目天天P圖、布丁相機中開發3D效果時才比較深入地研究了其中的原理,一直想寫這篇文章,由於很忙(lǎn),拖了很久,再不寫我自己也要忘了。
一開始時,也只是知道怎麼去用這些矩陣,卻不知道這些矩陣是怎麼得來的,當出現一些莫名其妙的問題時,如果不了解其中的原理,就不知道如何解決,於是想徹底搞懂其中的原理,還好自己對數學挺有興趣,於是從頭到尾把推導過程研究了一遍,總算掌握了其中的奧秘,不得不佩服OpengGL的設計者,其中的數學變換過程令人陶醉,下面我們一起來看看。
這些矩陣當中最重要的就是模型矩陣(Model Matrix)、視圖矩陣(View Matrix)、投影矩陣(Projection Matrix),本文也只分析這3個矩陣的數學推導過程。這三個矩陣的計算OpenGL的API都為我們封裝好了,我們在實際開發時,只需要給API傳對應的參數就能得到這些矩陣,下面帶大家來看看究竟是怎樣計算得到的。


什麼是OpenGL的矩陣變換


我們先來看一張經典圖:



這張圖相信很多同學在學習OpenGL的過程中都看到過,它比較直觀地展示了OpenGL矩陣變換的過程,下面我詳解一下其中的含義:

首先OpenGL有個世界坐標系,我們渲染的物體就是在世界坐標系中,我們的模型需要放到世界坐標系中,那麼當我們還沒放的時候,模型就和世界坐標系沒有聯繫,它就還處於自己的坐標系中,我們叫做模型坐標系、局部空間、局部坐標系,也就是圖中的LOCAL SPACE。

當我們把模型放到世界坐標系中,模型就在世界坐標系裡有了坐標,也就是原來在LOCAL SPACE中的那些坐標值,變成了世界坐標系中的坐標值,幫助我們完成這個變換的就是模型矩陣,對應圖中的MODEL MATRIX,於是這樣我們就把模型放到了圖中的世界坐標系WORLD SPACE中

放到世界坐標系後,是不是就確定了我們渲染出來看到的樣子?還沒有,大家可以想像一下,我把一個東西放在世界坐標系的某個地方,我可以從近處看觀察它,也可以從遠處觀察它,還可以從上下左右觀察它,甚至還可以倒著觀察它,因些還需要確定我們觀察它的狀態。OpenGL裡幫我們虛擬出了一個Camera(特別注意,這裡的Camera不是指我們硬體的Camera),從API的層面上看,我們只需要設置Camera的位置、朝向的點坐標、以及Camera的上方向向量就能將觀察狀態定下來,而這些設置最終會轉換成OpenGL中的視圖矩陣,對應圖中的VIEW MATRIX

經過View Matrix的變換後,我們觀察它的結果就確定了,圖中是從距離它一定的距離、上往下觀察它,這時候的點坐標就來到了視圖坐標系下,對應圖中的VIEW SPACE

這時候,我們能看到什麼東西,基本已經確定了,不過還有一步投影變換,這是什麼東西?大家想像一下,我們看到同一個東西,是不是通常都是近大遠小?那麼如何實現近大遠小?就要靠投影變換,OpenGL提供正交投影和透視投影,正交投影沒有近大遠小的效果,不管在什麼距離上看,都一樣大,透視投影則有近大遠小的效果,也是符合我們實際生活的一種效果,透視投影應用得比較多,可看下面這張經典圖:



        完成投影變換就需要靠投影矩陣,即圖中的PROJECTION MATRIX

我們看可以從圖中看到經過投影變換後就到了裁剪坐標系CLIP SPACE,什麼?裁剪坐標系?我們不是投影嗎?裁剪了什麼東西?實際上,我們的投影操作也順帶做了裁剪,所謂裁剪就是說把那些我們視野內看不到的東西去掉,什麼是視野?就是我們在生成投影矩陣時會設置近平面、遠平面、視角,這些東西會構成一個可見的空間,對應上圖中的虛線和近平面、遠平面包圍起來的空間

下一步就是上屏(如果是離屏渲染就是到一個frame buffer上),這些坐標畢竟只是OpenGL坐標系下的坐標,那麼最終以什麼樣的大小呈現在屏幕上呢?就要通過視口變換映射到屏幕上


以上就是一個完整的矩陣變換過程,裡面最重要的就是MVP三個矩陣,M即模型矩陣(Model Matrix),V即視圖矩陣(View Matrix),P即投影矩陣(Projection Matrix),本文將針對這三個矩陣的來由詳解其中的數學推導,其中投影矩陣只講解透視投影矩陣,因此它比較常用且其推導過程比正交投影矩陣複雜得多。


模型矩陣(Model Matrix)推導

相信大家在數學中都學過平移、縮放、旋轉三種基本變換,將模型放到世界坐標系中就是利用這三種變換的組合來實現的,我們來看一下平移、縮放、旋轉三種變換對應的矩陣:



縮放變換




旋轉變換

        1)繞x軸旋轉




         2)繞y軸旋轉



          3)繞z軸旋轉





大家可以看到旋轉變換有三個矩陣?為什麼不寫成一個,注意繞軸旋轉的先後順序不同,最終的結果可能是不一樣的,因此有三個獨立的矩陣,根據實際情況組合。

模型矩陣相對來說簡單一些,相信大家還能回憶起來之前學數學時的知識,就是通過將平移、縮放、旋轉三種矩陣的組合實現將模型以某種姿態、某種大小放到世界坐標系的某個地方。


視圖矩陣(View Matrix)推導

前面提到過,視圖矩陣對應Camera的位置、朝向的點坐標、以及Camera的上方向向量,我們先來看一張圖:




下面我們來看看怎樣通過Camera的位置、朝向的點坐標、以及Camera的上方向向量得到對應的View Matrix,首先給Camera定一個坐標系:



NUV這三個向量是怎麼來的呢?我們將Camera的坐標記為eye,朝向的點坐標記為lookat,上方向向量記為up,那麼:

N向量: eye - lookat

U向量:up X N並歸一化

V向量:N X U並歸一化

我們要把Camera以某種姿態放在世界坐標系中的某個地方,這個放的過程就是對應Camera的旋轉和平移,這裡表示為TR,其中T表示平穩變換矩陣,R表示旋轉變換矩陣。

我們雖然設置的是Camera,但最終動的是點坐標,因為Camera壓根就不存在,是一個假想的東西。假設我們不動攝像機,動坐標點,那麼對坐標點的變換就應該是對相機變換的逆變換T^-1R^-1(就是對TR整體求逆矩陣),注意,這裡的T^-1R^-1看起來貌不驚人,實際上就是我們要求的View Matrix。


根據前面的知識,我們能很容易得到T^-1:



這個直觀上也好理解,比如本來是平移Tx,逆過來就是平移-Tx,依此類推。
再回顧一下我們的目標T^-1R^-1,現在還差R^-1,現在再次回到我們假想的Camera,前面說要對它做TR,當做完R後,Camera會旋轉至某個姿態:

XYZ和UVN都可以看成是一組基,根據線性代數公式可將一個點在XYZ基下的坐標轉成在UVN基下的坐標,R就相當於是把基XYZ變換成UVN的變換矩陣,其中:



假設:



則有:



於是:



由於R是正交矩陣,有性質:R^-1=R^T(R^T代表R的轉置),為什麼R是正交矩陣?Tips:方陣A正交的充要條件是A的行(列) 向量組是單位正交向量組。
於是:



現在我們T^-1和R^-1都有了,T^-1R^-1也就是最終的View Matrix可以很容易地計算出來了,因為OpenGL中坐標是4維的,所以這裡將矩陣寫成4*4的:



投影矩陣(Projection Matrix)推導

下面是投影矩陣的推導,是最為複雜的一個矩陣,前面提到,投影矩陣是由視野決定的,而視野又是由近平面、遠平面和視角決定的,我們把視野在坐標系中畫出來,請看下圖:



簡單起見,我們不妨把Camera擺在原點,讓它朝z軸負方向來討論問題。
h表示近平面高度
w表示近平面寬度
n表示Camera到近平面的距離
f表示Camera到遠平面的距離
P代表視野中的一個點
那麼接下來要求的投影矩陣,就是能將P點正確地投影到近平面上,設P(x0, y0, z0),我們從y軸正嚮往負向看,即看xoz平面,看到的畫面是這樣的:



假設投影后的x坐標為x1 ,由三角形相似原理則易得:


同理有:



設l和r分別為近平面左、右邊框的x坐標,則有l=-w/2,r=w/2,投影歸一化後坐標範圍為-1~1,最左邊是-1,最右邊是1,l和r歸一化至-1~1是線性變換,於是列一個kx+b類型的方程組並解得k和b:


令xn表示點P的x坐標投影歸一化後的值,代入kx+b得:



同理可得點P的y坐標投影歸一化後的值yn:



下面我們來構造帶有未知數的投影矩陣然後求解它們,設待投影點為(x0,y0,z0,1),我們先來構造投影矩陣的第一第二行:



這裡強調一個細節,投影矩陣僅幫我們完成投影變換,不會歸一化,上面的x2、y2、z2指的是投影后歸一化前的值,還記得前面計算的xn和yn嗎?我們用一個括號把其中一個部分括了起來,外面乘了一個因子(-1/z0),後面會說這個因子是什麼東西,現在只需要知道,x2、y2實際上就是前面括號裡那堆東西,所以上面投影矩陣的第一行和第二行就自然能輕鬆地構造出來。


接下來就構造第三第四行,我們先看第四行,第四行計算的結果是投影后的第四維坐標,也就是w,前面提到了歸一化,而OpenGL的歸一化操作就是通過將坐標除以其對應的w值來完成的,再回頭看我們前面計算的xn和yn,它們是歸一化後的值。
還記得括號外面乘了一個因子(-1/z0)嗎?乘(-1/z0)可以看成是除以-z0,因此希望w就是-z0,於是構造第四行讓w的計算結果為-z0:



接下來就是最複雜的第三行,如何去構造第三行?第三行有4個值,現在都不知道是什麼,我們需要構造4個未知數嗎?對於解方程來說,在能解決問題的情況下,未知數能少就儘量少,不然只會徒增煩惱。
這裡其實不需要4個未知數,為什麼呢?那就要理解z2這個值是什麼東西,它就是投影之後未歸一化的深度值,而深度和x0、y0沒有關係,這個如何理解?就是說我把一個東西放在左,上邊,還是右邊,不影響它的深度,要改變深度需要前後移動。
既然z2和x0、y0沒有關係,那麼x0、y0不管是什麼值,都不會影響z2的值,因此用0去乘x0、y0,即第三行的第一第二個元素是0。
再看第三行的第三第四個元素,我們假設第三個元素是0,會發生是什麼?那麼z2就等於B,而B最後求出來放到矩陣中肯定是一個定值,這就意味著z2也是定值,於是z2就無法表示不同的點的不同深度,這不是我們想要的結果,因此第三個元素不能是0,是一個待求的未知數。同理,我們假設第四個元素是0會發生什麼?這樣投影矩陣第四列全是0,根據線性代數的知識,這個矩陣行列式等0,它必定不可逆,而我們希望投影矩陣是可逆的,這樣我們可以對坐標做一些逆變換來實現一些特殊的功能,因此第四個元素也不能是0,於是設它為一個未知數。
這樣,我們就構造出了一個包含未知數A和B的投影矩陣:



下面就是求解A和B:
我們將z0為-f和-n代進去,-f就是遠平面,-n就是近平面,求歸一化後的坐標,-f最遠,深度最深,歸一化後是1,反之,-n代進去後是-1,注意,深度是值越大越深,於是有:



可解得:



於是投影矩陣為:


總結

至此,我們就完成了模型矩陣(Model Matrix)、視圖矩陣(View Matrix)和投影矩陣(Projection Matrix)的數學推導,可以看到裡面的變換還是很精彩的,原來神秘的矩陣變換過程已經清晰可見,希望能對大家有幫助!謝謝!


作者簡介:kenney, 天天P圖Android工程師


文章後記
天天P圖是由騰訊公司開發的業內領先的圖像處理,相機美拍的APP。歡迎掃碼或搜索關注我們的微信公眾號:「天天P圖攻城獅」,那上面將陸續公開分享我們的技術實踐,期待一起交流學習!

    

加入我們
天天P圖技術團隊長期招聘:

(1) 深度學習(圖像處理)研發工程師(上海)

工作職責

開展圖像/視頻的深度學習相關領域研究和開發工作;

負責圖像/視頻深度學習算法方案的設計與實現;

支持社交平臺部產品前沿深度學習相關研究。

工作要求

計算機等相關專業碩士及以上學歷,計算機視覺等方向優先;

掌握主流計算機視覺和機器學習/深度學習等相關知識,有相關的研究經歷或開發經驗;

具有較強的編程能力,熟悉C/C++、python;

在人臉識別,背景分割,體態跟蹤等技術方向上有研究經歷者優先,熟悉主流和前沿的技術方案優先;

寬泛的技術視野,創造性思維,富有想像力;

思維活躍,能快速學習新知識,對技術研發富有激情。

(2) AND / iOS 開發工程師 

(3) 圖像處理算法工程師 

期待對我們感興趣或者有推薦的技術牛人加入我們(base 上海)!聯繫方式:ttpic_dev@qq.com


相關焦點

  • 【Tips】Matrices(矩陣)分享
    大概除了一些大神,沒人敢說自己搞懂了矩陣,實在是太龐雜太複雜了,加之它的計算方法,使用方法和通常的數學常識和習慣有很大的不同,導致基本上矩陣給人的感覺就是學的時候是一個層級,用的時候就是另一個層級了。我們就來談一下矩陣在計算機圖形學中進行位移,旋轉,縮放操作上的應用,個人認為對於CG的特效製作而言應該是需要了解的知識。
  • 2019考研數學:初等變換與初等矩陣的考點分析
    線性代數是考研數學必考的小學科,所佔分值為34分左右。矩陣是該門學科的研究對象,地位相當於高數中的函數,後續研究工作都是圍繞矩陣展開的,因此,我們不僅需要掌握矩陣最基本的運算,還需掌握矩陣的初等變換以及初等矩陣的相關知識點。
  • 電磁學相關定理數學公式推導
    想學好物理,就得先學好數學才可以,萬物皆可數學。
  • 2016考研數學:用初等變換求逆矩陣及乘積的方法
    在考研數學線性代數中,初等變換是一種非常重要的方法,被廣泛地用於很多題型的求解之中,如行列式的計算、矩陣的求逆、線性方程組的求解、矩陣秩的計算、化二次型為標準型等。初等變換包括初等行變換和初等列變換,具體說有三種:互換兩行(列)、某行(列)乘以一個非零數、某行(列)乘以一個數加到另一行(列)。
  • 2016考研數學線性代數重點詳解:行列式與矩陣
    線性代數是數學的一個分支,它的研究對象是向量,向量空間(或稱線性空間),線性變換和有限維的線性方程組。由於科學研究中的非線性模型通常可以被近似為線性模型,使得線性代數被廣泛地應用於自然科學和社會科學中。接下來就為大家總結一下線代在考研數學中的重點內容及題型。
  • 詳解Winograd變換矩陣生成原理
    本文首發於我的知乎,地址為:https://zhuanlan.zhihu.com/p/102351953目錄1.1、Convolution和Correlation的區別3.2、Winograd F(2,3)變換矩陣推導3.3、Winograd F(4,3)變換矩陣推導0、前言其實網上已經有不少從數學原理的角度去解說Winograd
  • 徹底剖析考研中矩陣的初等變換
    矩陣的初等變換與行列式的初等變換有兩個共同點,一是初等變換的形式相同;二是都是源於線性方程組。不同點:行列式是一組排列有序的數據根據一定的運算法則得出的一個數值,而矩陣就是一組排列有序的數據。在矩陣中,非常重要的一點就是初等變換。本文將對矩陣的初等變換進行詳盡地闡述。
  • OpenGL ES和坐標變換概述
    本文所要探討的主題,將主要圍繞上述第二個方面的知識,也就是坐標變換。這部分涉及到一點數學知識,顯得更難理解一些,並且網上的資料也散落在各處,很少有系統而詳盡的描述。嚴格來說,這部分理論知識並不完全屬於OpenGL規範所規定的範圍,但卻與之有著非常密切的關係。
  • 原創 | 學好opengl走遍天下都不怕系列《基礎篇》
    前言最近本來是想認真學習下《opengl es第三版》這本書,無奈內容過於生澀,有點看不下去,偶遇opengl-tutorial.org
  • 終端圖像處理系列 - OpenGL ES 2.0 - 3D基礎(矩陣投影)
    矩陣運算矩陣簡介數學上,一個 m x n 的矩陣是一個m行n列元素排列成的矩形陣列。以下是一個由6個數字元素構成的3行3列的矩陣:單位矩陣在OpenGL中,由於大部分的向量都是4分量 (x,y,z,w),所以我們通常使用 4x4 的變換矩陣。當中最簡單的變換矩陣是單位矩陣。單位矩陣是一個除了對角線以外都是0的NxN矩陣。
  • OpenGl著色器簡單範例
    首先會使用vs2013英語版創建C++的ConsoleApplication並且會增加相關的文件.cpp或者.h (特別說明,添加完也可以通過對增加的文件rename/重命名 修改後綴,比如下面的.vert就是那樣修改的),相關經驗已經發布;依次增加5個文件,五個文件明明如圖(這裡的名字是因為這些文檔中相關調用是這樣的,如果要自己命名,就要知道到哪裡改,代碼後面依次放出)其次,下載完需要的opengl
  • 線性回歸的求解:矩陣方程和梯度下降、數學推導及NumPy實現
    求解過程會用到一些簡單的微積分,因此複習一下微積分中偏導數部分,有助於理解機器學習的數學原理。各個向量和矩陣形狀如下面公式所示。用通俗的話來講,樣本中的數據必須足夠豐富,且有足夠的代表性,矩陣方程才有唯一解,否則矩陣方程會有多組解。如果特徵有上萬維,但只有幾十個樣本來訓練,我們很難得到一個滿意的最優解。
  • BP反向傳播矩陣推導圖示詳解​
    在深度學習領域,BP 的重要程度在怎麼強調也不為過,本文會從矩陣的視角對 BP 進行詳細推導,為了更好地理解 BP 的工作原理,本文也畫了大量的示意圖幫助理解。本文的公式經過自己很多次的推導打磨,盡力做到準確無誤,每一張圖也是反覆的捉摸力求精準表達。本文的閱讀難度確實很大,但是因為其重要,我覺得反覆抄寫下面的推導,也會有很多收穫。
  • 吳大正教材拉氏變換公式的推導
    同時, 作為數位訊號處理這門課程學習的參考書, 對於學習該課程的學生, 對講授該課程的教師的備課、習題講解和測試, 也有很高的參考價值.本書最初的一章對五套近年的數位訊號處理考研真題進行了詳細解答, 接著的八章分別介紹了數位訊號處理的重要內容: 離散時間信號與系統、z變換與離散時間傅立葉變換、離散傅立葉變換、快速傅立葉變換、數字濾波器的基本結構、IIR濾波器的設計、FIR濾波器的設計、信號的抽取與插值
  • 用向量推導三角恆等變換,看完還需要死記硬背嗎?
    設向量a的終點對應角度為α,向量b的終點對應角度為β,那麼對應的終點坐標為易知,向量之間的夾角θ與兩個角度的關係為由這個等式進行簡單變換便可得到其他三角恆等變換的表達式。將等式(2)中的β替換成-β,可得將等式(4)中的β替換成-β,可得我們從向量的夾角公式,結合任意角在單位圓中的定義,經過簡單變換,推導出了等式(2)(3)(4)(5)四個三角恆等變換公式。
  • CSharpGL(0)一個易學易用的C#版OpenGL
    你可以:繪製模型你可以用legacy opengl(glVertex)或modern opengl(VBO+Shader)繪製模型。當然這是最基本的功能。CSharpGL提供一個GLCanvas控制項供你進行繪製。使用紋理(貼圖)你可以用legacy opengl(glVertex)或modern opengl(VBO+Shader)為模型貼上貼圖。
  • 洛倫茲推導出了洛倫茲變換,為什麼卻沒發展出相對論?
    相信很多人剛開始學習狹義相對論的時候都有這樣一個疑問,愛因斯坦從光速不變原理和相對性原理出發,根據同時性的定義推出的公式為什麼要叫做「洛倫茲變換」呢?事實上,在1989年,洛倫茲在他關於物質結構理論的「電子論」就已經推出了洛倫茲變換的數學形式,比愛因斯坦早了10多年。
  • 數學運算、矩陣和圖像
    那個時候沒有接觸微積分、沒有接觸矩陣論、沒有接觸圖像處理,也沒有接觸機器學習。我們不知道我們未來做什麼。但我們的目的是搭建一個體系,這個體系中基本上都是最基礎的知識,主要以數學為主。 從最開始的加、減、乘、除開始,有很多種數學運算,還有加權和、開方、指數、以e為底、log等運算。再到後面,有均值、方差、標準差、最值等。這些都是數學專業最最基礎的運算。
  • 推導完整本書的所有(數學)公式,也不能幫助我找到個女朋友!
    【編者按】學數學(理科)要不要推導公式?我們發布兩位具有較好理科學術背景的作者的文章,一位是剛參加工作不久的副教授,一位是參加工作近20多年的博導,看看兩位關於數學學習公式推導的看法。讀大學那會兒,閒得無聊的時候就喜歡去推導書本上的公式。
  • 一篇文章搞定矩陣相關概念及意義--通俗解釋匯總
    比如對於單純的平移矩陣,我們可以將它變換成初等矩陣,這樣我們很容易看出它的平移關係。對於旋轉來說變換較為麻煩,但是原理相似,看官可以自行推導。 上面使用了一種變換工具,初等變換。通過上面的例子,相信我們就不難理解,書本上為什麼要花費不少篇幅來講解初等變換和初等矩陣了。矩陣為什麼支持分塊計算?我們該怎麼分塊。 先從簡單的劃分開始: