跨平臺(tái)渲染引擎之路:撥云見日

前言

最近在工作中越來越多地接觸到一些3D以及相比常見特性更酷炫的效果翰灾,因此萌發(fā)了想要自己從0開始打造一個(gè)渲染引擎的念頭穆役,一方面是為了更好地實(shí)現(xiàn)公司業(yè)務(wù)的需求,另一方面則是可以學(xué)到整個(gè)渲染流水線上的方方面面站楚。

因?yàn)橹白龅拇蟛糠侄际窃谔匦н@塊单起,對OpenGL會(huì)比較熟悉一些,但是放大到渲染引擎上就很多方面不熟悉了蛀柴,甚至有些是完全0基礎(chǔ)螃概,在給自己今年定下這么一個(gè)目標(biāo)時(shí)也是一頭霧水,有種瞬間不知道該向哪里邁出第一步的感覺鸽疾。因此這幾天基本都在尋找和渲染引擎甚至游戲引擎相關(guān)的一些基本信息和資料吊洼,自己給自己提問題,再從問題出發(fā)去尋找答案制肮,一步步地去理清自己的思路冒窍,從而形成了這一份文章。

文章里面的很多內(nèi)容都是自己整理各位大神的文章/回答中的摘要而來(所有引用均會(huì)注明出處豺鼻,在此對各位大神表示感謝和仰望)综液,而且只靠一篇文章是肯定不足以完全講述清楚整個(gè)引擎所需要了解到的內(nèi)容的,只是從我自己的角度來說儒飒,這篇文章之于之后構(gòu)建渲染引擎之路來說谬莹,無異于撥云見日的作用。

另外這系列文章假定各位對OpenGL具有一定的基礎(chǔ)桩了,因此不會(huì)過多地介紹OpenGL的一些基礎(chǔ)知識(shí)如渲染流程附帽、著色器用法等。

從問題出發(fā)

且不論游戲引擎這樣的龐然巨物井誉,就只說渲染引擎所需要涉及的內(nèi)容就非常蕉扮,你可能要考慮怎么去封裝渲染API,還要考慮怎么去整合各類效果系統(tǒng)颗圣,初版效果出來了慢显,要不要考慮搞搞Vulkan搞搞Metal爪模,什么樣的方案才是最佳的等等欠啤。

因此如果沒有一個(gè)方向就開始尋找資料的話和大海撈針沒有什么區(qū)別荚藻,也容易因?yàn)橐粫r(shí)間接收的資訊過多進(jìn)而打擊信心和興趣,因此在開始找資料之前我給自己定了幾個(gè)問題洁段,以這個(gè)幾個(gè)問題為方向去找資料為自己解答:

  • 一個(gè)渲染引擎是由哪些系統(tǒng)組成的应狱?這些系統(tǒng)都是不可或缺的嗎?
  • 實(shí)現(xiàn)一個(gè)渲染引擎需要點(diǎn)亮哪些技能點(diǎn)祠丝?哪些是必須技疾呻?哪些可以邊做邊點(diǎn)亮?
  • 渲染引擎的基本渲染流程是什么樣的写半?
  • 目前有哪些出色的渲染引擎岸蜗?怎么篩選參考引擎?參考哪些叠蝇?

而在尋找答案的過程中璃岳,自然而然就也會(huì)接觸到其他方面的內(nèi)容,比如一些自己之前見都沒見過的名詞悔捶,或者是大多數(shù)引擎都在用的成熟方案等等铃慷,這些也都會(huì)一并記錄下來。

啟程

其實(shí)這篇文章是有一個(gè)檢驗(yàn)收獲的方式的蜕该,這個(gè)方式在一開始是沒有的犁柜,但是在找資料的過程中看到知乎大佬的回答有所感悟,在這篇文章結(jié)束時(shí)再回過頭來看看下面這句話堂淡,如果大致明白了馋缅,那么這篇文章就起到作用了:

渲染流程概要

而這段話正是這一輪資料搜尋下來個(gè)人覺得最精煉的對渲染流程的描述了,至于里面具體各個(gè)名詞所指則會(huì)在后面的內(nèi)容進(jìn)行補(bǔ)充绢淀,當(dāng)然各個(gè)渲染引擎的流程在細(xì)節(jié)之處甚至一些環(huán)節(jié)上都會(huì)做出有所區(qū)別萤悴,這些則會(huì)在后面針對一些參考引擎做分析的時(shí)候再逐步整理出來。

那么更啄,啟程稚疹!

渲染引擎的技能樹

先貼一下找到的 知乎上的回答

你需要具備的技能

是不是有種看一眼就想放棄的沖動(dòng) ?(╯°Д°)╯︵ ┻━┻

但是正如我們在日常開發(fā)中突然遇到一個(gè)新的技術(shù)需求的時(shí)候一樣祭务,一般我們也不會(huì)把一個(gè)技術(shù)學(xué)到精通來才開始落地内狗,而是會(huì)先預(yù)研一下方案,做下對比义锥,掌握好基礎(chǔ)柳沙,對一些風(fēng)險(xiǎn)提前做好功課,保證好大方向上的正確性拌倍,然后就會(huì)開始逐步落地赂鲤,在過程中慢慢打磨噪径。

類比到上面的內(nèi)容也是如此,按照我自己的想法数初,我覺得首先C++的基本語法找爱、內(nèi)存管理、標(biāo)準(zhǔn)庫的使用等基礎(chǔ)內(nèi)容以及OpenGL的渲染管線和基本的glsl泡孩、矩陣這些是在開始之前必須要先打好底子的车摄,否則可能會(huì)連參考第三方開源引擎都成問題,更不要說自己去寫了仑鸥,而在自己寫項(xiàng)目或?qū)W習(xí)第三方開源項(xiàng)目的過程中則可以對遇到的不明白或者不完全理解的內(nèi)容進(jìn)行進(jìn)一步的學(xué)習(xí)吮播,不求快但求吃透每一點(diǎn),以這樣的方式去持續(xù)擴(kuò)大或加深自己的技能眼俊,在這個(gè)過程中盡量以點(diǎn)及面意狠,每一個(gè)細(xì)節(jié)盡可能多去了解一些邊邊角角涉及到的地方,以便自己能夠盡快完成一個(gè)又一個(gè)的閉環(huán)疮胖,最終對整個(gè)領(lǐng)域的理解越來越清晰环戈。

其次就是合理安排出一部分時(shí)間來進(jìn)行理論方面的系統(tǒng)學(xué)習(xí),比如計(jì)算機(jī)圖形學(xué)等获列,系統(tǒng)性的學(xué)習(xí)個(gè)人還是比較推薦通過書籍來進(jìn)行谷市,雖然周期更長,但是可以讓你對這個(gè)領(lǐng)域的方方面面能夠有一個(gè)更清晰的認(rèn)識(shí)击孩,從而形成一個(gè)更大的閉環(huán)迫悠。

而至于dx、metal巩梢、編譯知識(shí)這些如果暫時(shí)不需要做這方面的需求的話倒不是非常關(guān)鍵创泄,可以放到上面的技能學(xué)習(xí)之后,甚至可能很長一段時(shí)間你都不會(huì)使用到括蝠,當(dāng)然也可能你現(xiàn)在是需要用dx而不需要opengl鞠抑,那么就是dx的基礎(chǔ)知識(shí)是必須的opengl排到后面,總之因人而異吧忌警。

3D引擎著色方式的演化史

這部分內(nèi)容是我在尋找渲染流程過程中的額外收獲搁拙,雖然說的是演化史,但是里面的內(nèi)容正好是對 啟程 章節(jié)里對渲染引擎主要流程的描述的擴(kuò)展法绵,通過對多種著色方式的了解箕速,可以間接對渲染流程里的幾個(gè)主要步驟有一個(gè)初步的感知。

該章節(jié)的內(nèi)容起源是3D渲染引擎著色方式的演化史朋譬,原文對各個(gè)著色方式的介紹比較簡明扼要盐茎,在閱讀完這篇文章后我又自己去對里面的各個(gè)名詞和許多不理解的地方進(jìn)行了搜索,因此大綱上還是會(huì)按照這篇文章的幾大部分進(jìn)行徙赢,在內(nèi)容里面再補(bǔ)上自己找到的額外的資料字柠,主要來源于 實(shí)時(shí)渲染中常用的幾種Rendering Path 和其他針對其中細(xì)節(jié)講解的文章探越。

這部分個(gè)人認(rèn)為在這篇文章的階段不需要深究里面的實(shí)現(xiàn)細(xì)節(jié),這個(gè)可以在后續(xù)分析開源引擎流程以及自主實(shí)現(xiàn)的時(shí)候去深入研究窑业,在這里更多地是對這些Render path有一個(gè)印象并且知道他們的大致渲染原理钦幔、彼此之間的區(qū)別以及能解決什么樣的問題,從而能在后面的引擎中因地制宜地使用不同的著色方式数冬。

Rendering Path

Rendering Path 其實(shí)指的就是渲染場景中光照的方式节槐。由于場景中的光源可能很多瘫筐,甚至是動(dòng)態(tài)的光源排抬。所以怎么在速度效果上達(dá)到一個(gè)最好的結(jié)果確實(shí)很困難皮迟。以當(dāng)今的顯卡發(fā)展為契機(jī),人們才衍生出了這么多的 Rendering Path 來處理各種光照秸架。

在介紹各種光照渲染方式之前,首先必須介紹一下現(xiàn)代的圖形渲染管線咆蒿。這是下面提到的幾種 Rendering Path 的技術(shù)基礎(chǔ)东抹。

圖形渲染管線

現(xiàn)代的渲染管線也稱為可編程管線(Programmable Pipeline),簡單點(diǎn) 說就是將以前固定管線寫死的部分(比如頂點(diǎn)的處理沃测,像素顏色的處理等等)變成在 GPU 上可以進(jìn)行用戶自定義編程的部分缭黔,好處就是用戶可以自由發(fā)揮的空間增大,缺點(diǎn)就是必須用戶自己實(shí)現(xiàn)很多功能蒂破。

下面簡單介紹下可編程管線的流程馏谨。以 OpenGL 繪制一個(gè)三角形舉例。首先用戶指定三 個(gè)頂點(diǎn)傳給 Vertex Shader附迷。然后用戶可以選擇是否進(jìn)行Tessellation Shader(曲面細(xì)分可能會(huì)用到)和 Geometry Shader(可以在 GPU 上增刪幾何信息)惧互。緊接著進(jìn)行光柵化,再將光柵化后的結(jié)果傳給 Fragment Shader 進(jìn)行 pixel 級(jí)別的處理喇伯。 最后將處理的像素傳給 FrameBuffer 并顯示到屏幕上喊儡。

名詞解釋

Geometry:即我們所要渲染的一個(gè)幾何圖形

Vertex Shader:頂點(diǎn)著色器,處理每個(gè)頂點(diǎn)稻据,將頂點(diǎn)的空間位置投影在屏幕上艾猜,即計(jì)算頂點(diǎn)的二維坐標(biāo)。

Tessellation Shader:曲面細(xì)分著色器捻悯,是一個(gè)可選的著色器匆赃,用于細(xì)分圖元

Geometry Shader:幾何著色器,是一個(gè)可選的著色器秋度,用于逐圖元的著色炸庞,可以產(chǎn)生更多的圖元

Fragment Shader:片段著色器,也稱為像素著色器(Pixel Shader)荚斯,用于計(jì)算“片段”的顏色和其它屬性埠居,此處的“片段”通常是指單獨(dú)的像素

后面文章內(nèi)容中出現(xiàn)的VS查牌、TS、GS滥壕、FS(PS)即對應(yīng)上圖中的Vertex Shader纸颜、Tessellation Shader、Geometry Shader绎橘、Fragment Shader

FrameBuffer:幀緩沖存儲(chǔ)器胁孙,簡稱幀緩存或顯存,它是屏幕所顯示畫面的一個(gè)直接映象称鳞,又稱為位映射圖(Bit Map)或光柵涮较。幀緩存的每一存儲(chǔ)單元對應(yīng)屏幕上的一個(gè)像素,整個(gè)幀緩存對應(yīng)一幀圖像冈止。

Forward rendering

Forward rendering

這是最初始的渲染方式狂票,原理是以mesh為單位進(jìn)行渲染,在光柵化后熙暴,對每個(gè)PS進(jìn)行計(jì)算時(shí)闺属,根據(jù)光照進(jìn)行著色計(jì)算,所以這種方式稱為前向著色周霉。

Forward Rendering 是絕大數(shù)引擎都含有的一種渲染方式掂器。要使用 Forward Rendering,一般在 Vertex ShaderFragment Shader 階段對每個(gè)頂點(diǎn)或每個(gè)像素進(jìn)行光照計(jì)算俱箱,并且是對每個(gè)光源進(jìn)行計(jì)算產(chǎn)生最終結(jié)果国瓮。下面是 Forward Rendering 的核心偽代碼:

For each light:
    For each object affected by the light:
        framebuffer += object * light

比如在 Unity3D 4.x 引擎中,對于下圖中的圓圈(表示一個(gè) Geometry)匠楚,進(jìn)行 Forward Rendering 處理:

Unity3D 4.x 圓圈

將得到下面的處理結(jié)果:

Unity3D 4.x 圓圈 著色結(jié)果

也就是說巍膘,對于 ABCD 四個(gè)光源我們在 Fragment Shader 中我們對每個(gè) pixel 處理光照, 對于 DEFG 光源我們在 Vertex Shader 中對每個(gè) vertex 處理光照芋簿,而對于 GH 光源峡懈,我們采用球調(diào)和(SH)函數(shù)進(jìn)行處理。

這種方式存在以下弊端:

  • 如果像素被其他像素遮蔽了与斤,就浪費(fèi)了寶貴的處理結(jié)果
  • 光源多起來后管理很麻煩肪康,Shader也不好寫。

因此撩穿,Deferred rendering就應(yīng)運(yùn)而生了磷支。

很明顯,對于 Forward Rendering食寡,光源數(shù)量對計(jì)算復(fù)雜度影響巨大雾狈,所以比較適合戶外這種光源較少的場景(一般只有太陽光)。

但是對于多光源抵皱,我們使用 Forward Rendering 的效率會(huì)極其低下善榛。因?yàn)槿绻?Vertex Shader 中計(jì)算光照辩蛋,其復(fù)雜度將是O(num_geometry_vertexes ? num_lights),而如果在 Fragment Shader 中計(jì)算光照移盆,其復(fù)雜度為O(num_geometry_fragments ? num_lights) 悼院。可見光源數(shù)目和復(fù)雜度是成線性增長的咒循。

對此据途,我們需要進(jìn)行必要的優(yōu)化。比如

  • 多在 Vertex Shader 中進(jìn)行光照處理叙甸,因?yàn)橛幸粋€(gè)幾何體有 10000 個(gè)頂點(diǎn)颖医,那么對于 n 個(gè)光源,至少要在 Vertex Shader 中計(jì)算 10000n 次蚁署。而對于在 Fragment Shader 中進(jìn)行處理便脊,這種消耗會(huì)更多,因?yàn)閷τ谝粋€(gè)普通的 1024x768 屏幕光戈,將近有 8 百萬的像素 要處理。所以如果頂點(diǎn)數(shù)小于像素個(gè)數(shù)的話遂赠,盡量在 Vertex shader 中進(jìn)行光照久妆。

  • 如果要在 fragment shader 中處理光照,我們大可不必對每個(gè)光源進(jìn)行計(jì)算時(shí)跷睦,把所有像素都對該光源進(jìn)行處理一次筷弦。因?yàn)槊總€(gè)光源都有其自己的作用區(qū)域。比如點(diǎn)光源的作用區(qū)域是一個(gè)球體抑诸,而平行光的作用區(qū)域就是整個(gè)空間了烂琴。對于不在此光照作用區(qū)域的像素就不進(jìn)行處理。但是這樣做的話蜕乡,CPU 端的負(fù)擔(dān)將加重奸绷。

  • 對于某個(gè)幾何體,光源對其作用的程度是不同层玲,所以有些作用程度特別小的光源可以不進(jìn)行考慮号醉。典型的例子就是 Unity 中只考慮重要程度最大的 4 個(gè)光源。

名詞解釋

Mesh:網(wǎng)格辛块,任何一個(gè)模型都是由若干網(wǎng)格面組成畔派,而每一個(gè)面又有若干個(gè)三角形組成,也就是說润绵,模型是由若干個(gè)三角形面組成的

Deferred rendering

Deferred rendering

Deferred Rendering(延遲渲染)顧名思義线椰,就是將光照處理這一步驟延遲一段時(shí)間再處理。

具體做法就是將光照放在已經(jīng)將三維物體生成二維圖片之后進(jìn)行處理尘盼。也就是說將物空間的光照處理放到了像空間進(jìn)行處理憨愉。要做到這一步烦绳,需要一個(gè)重要的輔助工具——G-Buffer

G-Buffer 主要是用來存儲(chǔ)每個(gè)像素對應(yīng)的 Position莱衩,Normal爵嗅,Diffuse Color 和其他 Material parameters。根據(jù)這些信息笨蚁,我們就可以在像空間中對每個(gè)像素進(jìn)行光照處理睹晒。

下面是 Deferred Rendering 的核心偽代碼。

For each object:
    Render to multiple targets
For each light:
    Apply light as a 2D postprocess

這種渲染方式相比 Forward rendering 就是在渲染mesh時(shí)括细,并不進(jìn)行光照計(jì)算伪很,而是按照以下步驟進(jìn)行:

  • 將深度、法線奋单、Diffuse锉试、Specular等材質(zhì)屬性分別輸出到GBuffer里(其實(shí)就是幾張RT

  • 然后GBuffer里的深度和法線信息,累加所有光照的強(qiáng)度到一張光照強(qiáng)度RT

  • 根據(jù)GBuffer里的Diffuse和Specular信息览濒,以及光照強(qiáng)度RT呆盖,進(jìn)行著色計(jì)算

名詞解釋

GBuffer:指Geometry Buffer,亦即“物體緩沖”贷笛。區(qū)別于普通的僅將顏色渲染到紋理中应又,G-Buffer指包含顏色、法線乏苦、世界空間坐標(biāo)的緩沖區(qū)株扛,亦即指包含顏色、法線汇荐、世界空間坐標(biāo)的紋理洞就。由于G-Buffer需要的向量長度超出通常紋理能包含的向量的長度,通常在游戲開發(fā)中掀淘,使用多渲染目標(biāo)技術(shù)來生成G-Buffer旬蟋,即在一次繪制中將顏色、法線繁疤、世界空間坐標(biāo)分別渲染到三張浮點(diǎn)紋理中

下面簡單舉個(gè)例子:

首先我們用存儲(chǔ)各種信息的紋理圖咖为。比如下面這張 Depth Buffer,主要是用來確定該像 素距離視點(diǎn)的遠(yuǎn)近的稠腊。

Depth Buffer

根據(jù)反射光的密度/強(qiáng)度分度圖來計(jì)算反射效果躁染。

Specular Intensity/Power

下圖表示法向數(shù)據(jù),這個(gè)很關(guān)鍵架忌。進(jìn)行光照計(jì)算最重要的一組數(shù)據(jù)吞彤。

Normal Buffer

下圖使用了 Diffuse Color Buffer。

Diffuse Color Buffer

這是使用 Deferred Rendering 最終的結(jié)果。

Deferred Lighting Results

Deferred rendering 的最大的優(yōu)勢就是將光源的數(shù)目和場景中物體的數(shù)目在復(fù)雜度層面上完全分開饰恕,也就是說場景中不管是一個(gè)三角形還是一百萬個(gè)三角形挠羔,最后的復(fù)雜度不會(huì)隨光源數(shù)目變化而產(chǎn)生巨大變化。從上面的偽代碼可以看出 Deferred rendering 的復(fù)雜度為 O(screen_resolution + num_lights)埋嵌。

這種渲染方式也有一些弊端:

  • 由于硬件限制或者性能限制破加,GBuffer里保存的材質(zhì)信息有限,對于特殊材質(zhì)來說雹嗦,例如人的皮膚范舀、翡翠等,渲染結(jié)果很不好
  • 延遲計(jì)算光照會(huì)大幅增加紋理帶寬和幀緩沖區(qū)帶寬的開銷
  • 當(dāng)光源數(shù)量很多時(shí)了罪,光源會(huì)不斷對光照強(qiáng)度RT進(jìn)行累加锭环,也會(huì)大幅增加幀緩沖區(qū)帶寬開銷
  • 由于硬件限制或者性能限制,不能使用硬件支持的MSAA泊藕,只能使用類似后期處理的FXAA或者Temporal AA

名詞解釋

MSAA辅辩、FXAA、Temporal AA都是抗鋸齒(Anti-Aliasing)技術(shù)娃圆,鋸齒的來源是因?yàn)閳鼍暗亩x在三維空間中是連續(xù)的玫锋,而最終顯示的像素則是一個(gè)離散的二維數(shù)組。所以判斷一個(gè)點(diǎn)到底沒有被某個(gè)像素覆蓋的時(shí)候單純是一個(gè)“有”或者“沒有"問題讼呢,丟失了連續(xù)性的信息景醇,導(dǎo)致鋸齒。

具體區(qū)別可見FXAA吝岭、FSAA與MSAA有什么區(qū)別?

Deferred Rendering 局限性是顯而易見的吧寺。比如我在 G-Buffer 存儲(chǔ)以下數(shù)據(jù):

G-Buffer 數(shù)據(jù)格式

這樣的話窜管,對于一個(gè)普通的 1024x768 的屏幕分辨率≈苫總共得使用 1024x768x128bit=20MB幕帆, 對于目前的動(dòng)則上 GB 的顯卡內(nèi)存可能不算什么,但是使用 G-Buffer 耗費(fèi)的顯存還是很多的赖条。一方面失乾,對于低端顯卡,這么大的顯卡內(nèi)存確實(shí)很耗費(fèi)資源纬乍;另一方面碱茁,如果要渲染更酷的特效,使用的 G-Buffer 大小將增加仿贬,并且其增加的幅度也是很可觀的纽竣;并且存取 G-Buffer 耗費(fèi)的帶寬也是一個(gè)不可忽視的缺陷。

對于 Deferred Rendering 的優(yōu)化也是一個(gè)很有挑戰(zhàn)的問題。 下面簡單介紹幾種降低 Deferred Rendering 存取帶寬的方式蜓氨。最簡單也是最容易想到的就是將存取的 G-Buffer 數(shù)據(jù)結(jié)構(gòu)最小化聋袋,這也就衍生除了 Light Pre-Pass 方法。另一種方式是將多個(gè)光照組成一組穴吹,然后一起處理幽勒,這種方法衍生了 Tile-based deferred Rendering

Light Pre-Pass / Deferred Lighting

這個(gè)技術(shù)是CryTek這個(gè)團(tuán)隊(duì)(該團(tuán)隊(duì)開發(fā)了CryENGINE游戲引擎港令,即下面簡稱的CE啥容,如果還不熟悉的話,那么這個(gè)團(tuán)隊(duì)開發(fā)了《孤島危機(jī)》缠借、《孤島驚魂》等游戲)原創(chuàng)的干毅,由 Wolfgang Engel 在他的 博客 中提到的,也用于解決Deferred rendering渲染方式里的第一個(gè)弊端泼返。原理跟Deferred rendering差不多硝逢,只是有幾處不同:

  • GBuffer中只有深度(Z)和法線(Normal)數(shù)據(jù),對比 Deferred Rendering绅喉,少了 Diffuse Color渠鸽, Specular Color 以及對應(yīng)位置的材質(zhì)索引值

  • 在 FS 階段利用上面的 G-Buffer 計(jì)算出所必須的 Light properties,比如 Normal * LightDir, LightColor, Specular 等 Light properties柴罐,將這些計(jì)算出的光照進(jìn)行 alpha-blend 并存入 LightBuffer(就是用來存儲(chǔ) Light properties 的 buffer)

  • 著色過程不是Deferred rendering中類似于后處理的方式徽缚,而是渲染mesh,即將結(jié)果送到 Forward rendering 渲染方式計(jì)算最后的光照效果

相對于傳統(tǒng)的 Deferred Render革屠,使用 Light Pre-Pass 可以對每個(gè)不同的幾何體使用不同 的 Shader 進(jìn)行渲染凿试,所以每個(gè)物體的 Material properties 將有更多變化。這里我們可以看出對于傳統(tǒng)的 Deferred Rendering似芝,它的第二步是遍歷每個(gè)光源那婉,這樣就增加了光源設(shè)置的靈活性,而 Light Pre-Pass 第三步使用的其實(shí)是 Forward rendering党瓮,所以可以對每個(gè) mesh 設(shè)置其材質(zhì)详炬,這兩者是相輔相成的,有利有弊寞奸。

另一個(gè) Light Pre-Pass 的優(yōu)點(diǎn)是在使用 MSAA 上很有利呛谜。雖然并不是 100%使用上了 MSAA(除非使用 DX10/11 的特性),但是由于使用了 Z 值和 Normal 值枪萄,就可以很容易找到邊緣隐岛,并進(jìn)行采樣。

下面這兩張圖呻引,上邊是使用傳統(tǒng) Deferred Render 繪制的礼仗,下邊是使用 Light Pre-Pass 繪 制的。這兩張圖在效果上不應(yīng)該有太大區(qū)別。

Deferred Renderer Result
Light Pre-Pass Result

其實(shí)這種方式也有弊端:

  • 由于不透明物體在主視口中被渲染了兩次元践,會(huì)大幅增加渲染批次韭脊,不過好在CE對狀態(tài)切換管理的非常好,所以渲染批次的承載力很高

  • 由于某些特殊材質(zhì)需要對光照進(jìn)行特殊處理单旁,比如說樹葉的背光面也會(huì)有一定的光照沪羔,所以這種方式也不太完美

印象里貌似CE對主光,例如太陽光象浑,不累加進(jìn)光照強(qiáng)度RT蔫饰,而是著色時(shí)單獨(dú)處理,這樣的話效果會(huì)提升不少愉豺,至少室外場景是完全能夠解決問題的篓吁;而對于點(diǎn)光源比較多的室內(nèi)場景,主光著色好看了就會(huì)效果很好了蚪拦,畢竟其他光照的影響占比比較小杖剪。

Tile-based deferred rendering

這個(gè)方案是對Deferred rendering渲染方式里的第三個(gè)弊端進(jìn)行優(yōu)化的。原理就是:

  • 先將整個(gè)光照強(qiáng)度RT分成很多個(gè)正方形區(qū)域驰贷,計(jì)算每個(gè)區(qū)域受哪些光源影響盛嘿,并保存起來
  • 然后以每個(gè)區(qū)域?yàn)閱挝唬谝粋€(gè)批次里累加所有的光照

這樣就能減少對光照強(qiáng)度RT上某個(gè)像素頻繁讀寫的次數(shù)括袒。

TBDR 主要思想就是將屏幕分成一個(gè)個(gè)小塊 tile次兆,然后根據(jù)這些 Depth 求得每個(gè) tile 的 bounding box。對每個(gè) tile 的 bounding box 和 light 進(jìn)行求交锹锰,這樣就得到了對該 tile 有作用 的 light 的序列芥炭。最后根據(jù)得到的序列計(jì)算所在 tile 的光照效果。

對比 Deferred Render恃慧,之前是對每個(gè)光源求取其作用區(qū)域 light volume蚤认,然后決定其作用的的 pixel,也就是說每個(gè)光源要求取一次糕伐。而使用 TBDR,只要遍歷每個(gè) pixel蘸嘶,讓其所屬 tile 與光線求交良瞧,來計(jì)算作用其上的 light,并利用 G-Buffer 進(jìn)行 Shading训唱。一方面這樣做減少 了所需考慮的光源個(gè)數(shù)褥蚯,另一方面與傳統(tǒng)的 Deferred Rendering 相比,減少了存取的帶寬况增。

一篇文章 中提到目前所有的移動(dòng)設(shè)備都使用的是 Tile-Based Deferred Rendering(TBDR) 的渲染架構(gòu)赞庶,,里面還提及了使用TBDR的一些注意事項(xiàng),感興趣的可以看看歧强,以及 針對移動(dòng)端TBDR架構(gòu)GPU特性的渲染優(yōu)化 澜薄,移動(dòng)GPU渲染原理的流派——IMR、TBR及TBDR

名詞解釋

tile:區(qū)塊摊册,即將需要渲染的畫面分成一個(gè)個(gè)的區(qū)塊

bounding box:邊界框肤京,是一個(gè)矩形框,可以由矩形左上角的xx和yy軸坐標(biāo)與右下角的xx和yy軸坐標(biāo)確定茅特。從技術(shù)上講忘分,邊界框是包含一個(gè)物體的最小矩形

light volume:體積光,散射是一種非常美麗的自然現(xiàn)象白修,在自然界中光穿過潮濕或者含有雜質(zhì)的介質(zhì)時(shí)產(chǎn)生散射妒峦,散射的光線進(jìn)入人眼,讓這些介質(zhì)看起來像攏住了光線一樣兵睛,也就是所謂的體積光肯骇。可見 游戲開發(fā)相關(guān)實(shí)時(shí)渲染技術(shù)之體積光

Hybrid deferred rendering

為了解決Deferred lighting里面的第一個(gè)弊端卤恳,從CE3的某個(gè)版本開始累盗,換成了這種方式。理由是突琳,對于大多數(shù)物體來說若债,Deferred rendering的方式就很好了,而對于特殊材質(zhì)拆融,則使用Deferred lighting的方式蠢琳。這樣,既能保持很好的渲染效果镜豹,又能避免渲染批次激增傲须。

更詳細(xì)的內(nèi)容可見 Hybrid-Deferred-Rendering.pdf

Forward+

有時(shí)候,你轉(zhuǎn)了很大一個(gè)圈以后趟脂,發(fā)現(xiàn)又回到了原點(diǎn)泰讽。

好,那這就到了終極方式了——前向著色的改進(jìn)版昔期。這個(gè)方案是ATI(著名顯卡生產(chǎn)商已卸,06年被AMD收購)發(fā)明的,已經(jīng)應(yīng)用于Ogre 2.1(開源的面向?qū)ο蟮?D引擎)硼一。UE4(大名鼎鼎的虛幻引擎)正在針對VR研發(fā)前向著色累澡,不知道是不是也是這個(gè)。

原理也很簡單:

  • 先用Tile-based deferred rendering里的方式計(jì)算好每個(gè)區(qū)域受哪些光照影響

  • 然后像傳統(tǒng)的前向著色一樣渲染每個(gè)mesh——當(dāng)然般贼,要去光照列表里查找影響當(dāng)前區(qū)域的所有光照愧哟,并著色

這種方式只有上述提到的一個(gè)缺點(diǎn)奥吩,那就是可能和Deferred lighting一樣需要渲染兩遍場景,不過以后應(yīng)該會(huì)有優(yōu)化的方案蕊梧。優(yōu)點(diǎn)則有:

  • 渲染效果好

  • 帶寬開銷低霞赫,尤其適用于VR這種每幀需要渲染兩遍場景的應(yīng)用

  • 可以使用硬件支持的MSAA,質(zhì)量最高望几。

Forward+的優(yōu)勢還有很多绩脆,其實(shí)大多就是傳統(tǒng) Forward Rendering 本身的優(yōu)勢,所以 Forward+更像一個(gè)集各種 Rendering Path 優(yōu)勢于一體的 Rendering Path橄抹。

Forward+ = Forward + Light Culling靴迫。Forward+ 很類似 Tiled-based Deferred Rendering。 其具體做法就是先對輸入的場景進(jìn)行 z-prepass楼誓,也就是說關(guān)閉寫入 color玉锌,只向 z-buffer 寫入 z 值。注意此步驟是 Forward+必須的疟羹,而其他渲染方式是可選的主守。接下來的步驟和 TBDR 很類似,都是劃分 tiles榄融,并計(jì)算 bounding box参淫。只不過 TBDR 是在 G-Buffer 中完成這一步驟 的,而 Forward+是根據(jù) Z-Buffer愧杯。最后一步其實(shí)使用的是 Forward rendering 方式涎才,即在 FS 階段對每個(gè) pixel 根據(jù)其所在 tile 的 light 序列計(jì)算光照效果。而 TBDR 使用的是基于 G-Buffer 的 Deferred rendering力九。

實(shí)際上耍铜,forward+比 deferred 運(yùn)行的更快。我們可以看出由于 Forward+只要寫深度緩存 就可以跌前,而 Deferred Rendering 除了深度緩存棕兼,還要寫入法向緩存。而在 Light Culling 步驟抵乓, Forward+只需要計(jì)算出哪些 light 對該 tile 有影響即可伴挚。而 Deferred Rendering 還在這一部分把光照處理給做了。而這一部分灾炭,F(xiàn)orward+是放在 Shading 階段做的章鲤。所以 Shading 階段 Forward+ 耗費(fèi)更多時(shí)間。但是對目前硬件來說咆贬,Shading 耗費(fèi)的時(shí)間沒有那么多。

以下是 Forward+Deferred Rendering 的對比圖:

Forward+ vs Deferred Rendering

感興趣的可以再額外看看 forward框架的逆襲:解析forward渲染 這篇文章帚呼。

名詞解釋

Light Culling:剔除光照

渲染/游戲引擎調(diào)查

渲染引擎屬于游戲引擎中的一部分掏缎,本章節(jié)主要簡要整理一下找到的一些渲染引擎和游戲引擎皱蹦,具體內(nèi)在區(qū)別后續(xù)進(jìn)一步深入了解的時(shí)候再整理補(bǔ)上。

渲染引擎

游戲引擎

在Wiki上也已經(jīng)有整理了目前為止市面上已有的大量游戲引擎:Game Engine

Github上統(tǒng)計(jì)的開源游戲引擎:game-engines

參考引擎

通過上面的調(diào)查我們發(fā)現(xiàn)現(xiàn)在市面上的大小引擎數(shù)不勝數(shù)眷蜈,一個(gè)個(gè)地去看的話時(shí)間周期估計(jì)要以年為單位沪哺,首先我們要先從自身的需求出發(fā)定出一些對參考引擎所需要具備的特性的要求,然后再根據(jù)要求來篩選出幾個(gè)比較貼合我們需求的深入研究酌儒。

以我自身的角度出發(fā)辜妓,我列出來了以下一些要求:

  • 開源,但是項(xiàng)目規(guī)模還未到非常龐大的程度忌怎,避免研究周期過長
  • 具備一定規(guī)模的使用人數(shù)和影響力
  • 保持更新籍滴,所用方案不至于落后行業(yè)太久
  • 使用C++語言編寫,具備跨平臺(tái)特性
  • 支持2D/3D渲染榴啸,實(shí)現(xiàn)粒子系統(tǒng)孽惰、光源、動(dòng)畫系統(tǒng)鸥印、后處理等多項(xiàng)功能中的幾種
  • 具備多平臺(tái)自動(dòng)切換渲染驅(qū)動(dòng)的話更好

我從上面調(diào)查后的引擎列表里整理出了以下幾個(gè)符合語言勋功、使用人數(shù)、持續(xù)更新库说、支持效果等方面都比較符合的引擎來優(yōu)先作為研究的對象狂鞋,后續(xù)的分析系列文章也會(huì)先以這些引擎來作為目標(biāo):

  • 渲染引擎

    • bgfx
      • 可實(shí)現(xiàn)2D以及文字繪制,3D渲染潜的,光照等效果
      • 可自動(dòng)切換Metal等渲染驅(qū)動(dòng)
    • OGRE 3D
      • 老牌渲染引擎骚揍,除了渲染之外還包含動(dòng)畫系統(tǒng)和粒子系統(tǒng)
    • OpenSceneGraph
      • 中文文檔,粒子系統(tǒng)等功能可通過第三方插件實(shí)現(xiàn)
  • 游戲引擎

    • godot
    • Urho3D
      • 歷史久遠(yuǎn)使用人數(shù)比Godot少很多浑塞,各方面表現(xiàn)比較中庸
      • 輕量級(jí)項(xiàng)目,支持在該引擎的基礎(chǔ)上方便地?cái)U(kuò)展各種效果組件

總結(jié)

至此我們完成了在邁出跨平臺(tái)渲染引擎第一步之前的鋪墊工作政己,我們梳理了渲染引擎的一個(gè)大致流程酌壕,以及這個(gè)流程里面的關(guān)于 Rendering path 等方面的細(xì)節(jié)信息,對這些內(nèi)容有了一個(gè)初步的印象歇由,同時(shí)列舉了以下令人望而卻步的技能樹卵牍,但是我們可以一步一步地吃成胖子,重要地是邁出這第一步沦泌,最后我們整理了一下渲染/游戲引擎列表糊昙,并按照自身要求從中梳理了幾個(gè)引擎來作為下一步分析研究的目標(biāo)。

接下來就是技術(shù)活了谢谦,下一篇《跨平臺(tái)渲染引擎之路:bgfx分析》將針對 bgfx 開始第一步學(xué)習(xí)研究释牺,分享其內(nèi)部的渲染流程以及分析思路等萝衩。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市没咙,隨后出現(xiàn)的幾起案子猩谊,更是在濱河造成了極大的恐慌,老刑警劉巖祭刚,帶你破解...
    沈念sama閱讀 221,273評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件牌捷,死亡現(xiàn)場離奇詭異,居然都是意外死亡涡驮,警方通過查閱死者的電腦和手機(jī)暗甥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評論 3 398
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來遮怜,“玉大人淋袖,你說我怎么就攤上這事【饬海” “怎么了即碗?”我有些...
    開封第一講書人閱讀 167,709評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長陌凳。 經(jīng)常有香客問我剥懒,道長,這世上最難降的妖魔是什么合敦? 我笑而不...
    開封第一講書人閱讀 59,520評論 1 296
  • 正文 為了忘掉前任初橘,我火速辦了婚禮,結(jié)果婚禮上充岛,老公的妹妹穿的比我還像新娘保檐。我一直安慰自己,他們只是感情好崔梗,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,515評論 6 397
  • 文/花漫 我一把揭開白布夜只。 她就那樣靜靜地躺著,像睡著了一般蒜魄。 火紅的嫁衣襯著肌膚如雪扔亥。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,158評論 1 308
  • 那天谈为,我揣著相機(jī)與錄音旅挤,去河邊找鬼。 笑死伞鲫,一個(gè)胖子當(dāng)著我的面吹牛粘茄,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播秕脓,決...
    沈念sama閱讀 40,755評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼柒瓣,長吁一口氣:“原來是場噩夢啊……” “哼瘪菌!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起嘹朗,我...
    開封第一講書人閱讀 39,660評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎诵肛,沒想到半個(gè)月后屹培,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,203評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡怔檩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,287評論 3 340
  • 正文 我和宋清朗相戀三年褪秀,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片薛训。...
    茶點(diǎn)故事閱讀 40,427評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡媒吗,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出乙埃,到底是詐尸還是另有隱情闸英,我是刑警寧澤,帶...
    沈念sama閱讀 36,122評論 5 349
  • 正文 年R本政府宣布介袜,位于F島的核電站甫何,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏遇伞。R本人自食惡果不足惜辙喂,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,801評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望鸠珠。 院中可真熱鬧巍耗,春花似錦、人聲如沸渐排。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽飞盆。三九已至娄琉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間吓歇,已是汗流浹背孽水。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留城看,地道東北人女气。 一個(gè)月前我還...
    沈念sama閱讀 48,808評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像测柠,于是被迫代替她去往敵國和親炼鞠。 傳聞我的和親對象是個(gè)殘疾皇子缘滥,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,440評論 2 359

推薦閱讀更多精彩內(nèi)容