【授權翻譯】如何開始學習圖形學編程

【授權翻譯】如何開始學習圖形學編程

原文作者:Eric Arneb?ck

原文鏈接:erkaman.github.io/posts

有關作者:Playdead 的圖形程序工程師(Playdead 目前的作品有 INSIDE LIMBO )

相關鏈接:作者的 Gmail,Github,Linkedin,Twitter,YouTube

譯者:Angus

前段時間,我在推特開放個人私信,邀請所有人來問我有關計算機圖形學的問題。自那以來,我最常被問到的問題就是「我該如何開始學習圖形學編程呢?」。鑒於這個問題最常被問到,我也重複回答了這一問題許多次了,我決定在這篇文章中總結我的所有建議。

建議1:從光線追蹤(Raytracing)和光柵化(Rasterization)開始

近年來,出現了許多針對 GPU 硬體進行編碼的圖形 API: Direct3D、OpenGL、Vulkan、Metal、WebGL 等等。從這些 API 開始學習圖形學編程的話,可能會很難上手,因為它們通常需要很多「樣板代碼(boilerplate code)」,而且我認為它們根本不適合初學者。對於一個完全的圖形學初學者來說,使用這些 API,即使是「繪製第一個三角形」也是一項艱巨的任務。當然,我們也可以選擇使用 Unity 和 Unreal Engine 這樣的遊戲引擎。在這種情況下,遊戲引擎將為您完成與圖形 API 「對話」的繁瑣工作。但是我認為對於一個完全的初學者來說,即使是一個遊戲引擎也有太多的東西需要去學習,而這些時間應該花在一些更簡單的事情上。

因此,我建議初學者試著自己編寫 Raytracer 或 Software Rasterizer(或者兩個都試一試!)。簡單地說,Raytracer (對應光線追蹤)是一個這樣的程序:它通過從屏幕上的每個像素髮出光線來渲染 3D 場景,並進行大量的求交計算和物理光照計算,以計算出每個像素的最終顏色。而 Software Rasterizer (對應光柵化)呢,它是這樣渲染 3D 場景(這些場景在大多數情況下只是一堆 triangle,即三角形)的:對於每一個我們要在屏幕上繪製的三角形,我們先找出這個三角形涵蓋了屏幕上的哪些像素,然後對於其中的每一個像素,我們計算出光線是怎麼與這個像素對應的 三角形上的點進行交互的。通過這種光線的交互計算,我們得到了像素的最終顏色。光柵化要比光線追蹤快得多,也是現代 GPU 繪製 3D 場景的演算法。Software Rasterization 簡單的說就是我們在 CPU 上做這個 Rasterization,而不是在 GPU 上。

光線追蹤和光柵化實際上是兩種很簡單的演算法,對於初學者來說,實現它們要比理解現代圖形 API 容易得多。此外,通過親自實現這些演算法,初學者將了解計算機圖形學的許多基本概念,如點積、叉積、變換矩陣、攝像機等等,無需把時間浪費在與現代圖形 API 搏鬥。我相信這些圖形 API 已經勸退了許多圖形初學者,而把 Software Rasterizer 或 Raytracer 作為您的第一個圖形學編程項目是戰勝這個攔路虎的好方法。

我注意到,在學習圖形 API 之前先學著寫一個 Software Rasterizer,比起直接學習圖形 API 有一個巨大優勢——那就是在你使用 API 時,遇到錯誤你會感覺更容易 debug 了。因為這些 API 基本上只是提供一個到基於 GPU 的 Rasterizer 的介面,如果你知道了這些 API 在幕後是如何工作的(即一個 Rasterizer 是如何工作的),那麼 debug 代碼就會變得容易得多。

對於如何寫一個 Raytracer,我總會推薦別人去閱讀 Peter Shirley 撰寫的三本書(現已完全免費)。

對於如何寫一個 Software Rasterizer,請參閱這些:1,2,3,4。

建議2:學習必要的數學知識

我的下一條建議是,你應該學習一些圖形學編程必備的數學知識。別擔心,我作為一個圖形程序員,在日常工作中使用到的數學概念和數學方法其實出乎意料的少,所以你要學的數學知識可能沒你想像的那麼多。當你剛開始學習圖形學時,「線性代數」將是你主要用到的數學工具。一些會被你經常用到的線性代數中的概念如下:

  • 點積。
  • 叉積。
  • 球面坐標。
  • 變換矩陣(提示:作為圖形程序員,你將主要使用 4x4 的矩陣,所以不要花任何時間研究大型矩陣)。
  • 旋轉矩陣、縮放矩陣、平移矩陣、齊次坐標、四元數。
  • 標準正交基矩陣。
  • 求交計算。大部分是像求光線與一個球體、一個平面或一個三角形的交點這種求交運算。
  • 列主序和行主序。這是很多初學者會被絆倒的小概念,請確保你能完全理解它。看 這篇文章
  • 如何用觀察矩陣、透視變換矩陣表示一個攝像機。這是一個難倒很多初學者的知識點,所以這是一個值得你仔細學和深度學的知識點。對於透視變換矩陣,請參閱 這個教程。對於觀察矩陣,請參閱 這個 。

從初學者到中級水平,除了上述的數學知識,你基本上不會遇到其他的數學知識。但一旦你進入像「基於物理的著色」這樣的主題,一個被稱為「微積分」的數學領域也變得有用,但這是另一個故事了:-)。

我會在下面羅列一些優秀的線性代數學習教程。Immersive linear algebra 是一個優秀的在線版的數學書。Essence of linear algebra 是一個可以讓你可視化地學習很多概念的視頻系列。OpenGL tutorial 也解釋了一些簡單卻有用的線性代數概念。另一個優秀的學習資源是 Morgan McGuire 的 The Graphics Codex(譯者也強烈推薦,IOS 上可以找到同名 APP)。

建議3:「繪製第一個三角形」時的一些 debugging 技巧

「繪製第一個三角形」程序截圖

一旦你寫過一個 Raytracer 或 Rasterizer,你在學習圖形 API 時就會感覺更自信。圖形 API 的「Hello World」是「繪製第一個三角形」 ,然而要用圖形 API 繪製出第一個三角形可能會出人意料的難,這是因為通常我們必須要先寫一大堆樣板代碼,而且 debug 圖形學代碼對初學者來說會很難。如果你在繪製第一個三角形時遇到問題,並且得到的是黑屏而不是三角形,我將在下面列出一些 debugging 建議。這是我遇到相同問題時,用來排查錯誤的方法步驟,你可以逐條試驗:

  • 通常來說,問題會出在透視矩陣和觀察矩陣上, 因為這倆非常容易錯。在頂點著色器中,對每個頂點來說,你首先把它乘上模型矩陣,然後是觀察矩陣、透視矩陣,最後做一次透視除法(儘管這個最後的除法是在幕後自動進行的,通常不需要你親手做)。嘗試手動完成這個過程,以檢查你所用矩陣的正確性。如果你期望一個頂點是可見的,那麼在透視除法之後,這個頂點的坐標就應該是標準化設備坐標(Normalized Device Coordinates)了——x 的範圍應該在[-1, +1],y 的範圍在[-1, +1],z 的範圍也在[-1, +1](OpenGL 是如此,但對 Direct3D 來說,z 的範圍在[0, 1])。如果你期望可見的這個頂點的坐標值不在這些範圍中,那麼這個頂點是不可見的(因為所有坐標不在這個範圍內的頂點都將被硬體裁剪),你的這些矩陣可能哪出錯了。

  • 你是否記得把深度緩存清理為有意義的值?比如說,如果你使用 Direct3D 的深度測試函數D3DCMP_LESS,接著你把深度緩存都清理為 0,然後你會發現屏幕上沒有任何繪製結果,這是因為所有頂點都沒有通過深度測試。總之,確保你自己完全理解了深度測試,並配置合理的深度測試設置。

  • 確保你正確地傳遞了矩陣(如透視矩陣、觀察矩陣)給 GPU。 數據沒有被正確地傳遞給 GPU 這種事時有發生。你可以用一些 GPU 的 debugging 工具 例如 RenderDoc 來檢查你傳遞的矩陣。同樣的,確保你傳遞的頂點數據都是正確的。只傳遞了部分頂點數據是一個常犯的錯誤。

  • 背面剔除(Backface Culling)是另一個讓很多初學者感到棘手的細節。例如,在 OpenGL 中,默認情況下所有的背面三角形都被剔除,不渲染。但如果你不小心製作了一個背面三角形(例如你頂點順序搞反了)並嘗試渲染它,它將不會被渲染出來。我的建議是,當你試圖渲染你的第一個三角形時,暫時禁用背面剔除。

  • 檢查圖形 API 函數返回的所有錯誤代碼,因為它們可能包含有用的信息。如果你使用的 API (比如 Vulkan)能夠訪問某種 Debugging Layer,那麼你應該試著用一下。

  • 對於做任何有關圖形學的 debugging,我強烈推薦你學習使用一些 GPU 的 debugging 工具,比如 RenderDoc 或 Nsight。對於你的圖形應用程序運行的每一步,這些工具都會提供一個 GPU 當前狀態的預覽給你。它們能讓你輕鬆愉快地檢查矩陣有沒有被正確傳遞,深度緩存正不正確,深度測試、背面剔除的設置正不正確等等。你在圖形 API 中能進行的所有設置,都能在這些工具中進行檢查。RenderDoc 的另一個我非常喜歡且經常使用的特性是,它允許你逐步遍歷像素的片段著色器(不過在編寫本文時,這個特性似乎是 Direct3D 獨有的)。你只需單擊一個像素,RenderDoc 允許您逐步通過片段著色器進行計算,並為像素賦予其當前顏色值。下面的動圖展示了這個過程:我單擊一個橙色的像素,然後逐步執行片段著色器中的計算,從而最終使得像素被賦予這個橙色。如果你想知道 RenderDoc 還有哪些功能和特性,可以去 Baldur Karlsson的 YouTube 頻道 看看。

RenderDoc 使用演示

建議4:適合初學者練手的項目

在我看來,要學好圖形學編程的最好方法是自己去實現各種渲染技術。我將在下面給出一個適合初學者實現和學習的項目的列表。

  • 使用球面坐標製作一個球面網格(Mesh),並渲染它。
  • 實現簡單的漫反射(Diffuse)和光澤反射(Specular)著色器。
  • 實現方向光(Directional Lights),點光(Point Lights)和聚光燈(Spot Lights)。
  • 高度圖(Heightmap)渲染。
  • 為簡單的網格格式(如Wavefront.obj)編寫一個簡單的解析器(Parser),將其導入程序並進行渲染。特別是,嘗試一下導入並使用紋理(Textures)渲染網格物體。
  • 實現一個簡單的 Minecraft 風格的渲染器。渲染一個Minecraft-like 的世界其實出乎意料的簡單,而且很具有學習價值。
  • 使用立方體貼圖(Cubemaps) 渲染反射效果。
  • 使用陰影貼圖(Shadow Maps) 渲染陰影。
  • 實現視體剔除(View Frustum Culling)。這是一種簡單但非常實用的優化技術。
  • 實現粒子系統的渲染。
  • 學習如何實現伽馬矯正(Gamma Correction)。
  • 實現法線貼圖(Normal Mapping)。
  • 學習如何使用實例渲染(Instanced Rendering)有效地渲染大量網格。
  • 給網格蒙皮(Skinning),然後讓它動起來。

接下來是一些進階的高級技術:

  • 實現各種後處理(Post-processing)效果。像 輝光(Bloom,使用高斯模糊),環境光遮蔽(Ambient Occlusion)與 屏幕空間環境光遮蔽(SSAO),反走樣(Anti-aliasing)與快速近似反走樣(FXAA)。
  • 實現延遲著色(Deferred Shading),這是一種用於渲染許多光源的技術。

文章到此結束。這就是我對「如何開始學習圖形學編程?」這個問題的所有建議。


本文排版遵循 中文文案排版指北 。

因譯者水平有限,部分專業術語翻譯若有問題,歡迎大家在評論區指正:D。


推薦閱讀:

TAG:計算機圖形學 | 渲染 |