Unity 2017 Game Optimization 读书笔记 Dynamic Graphics(2)
Lighting and Shadowing
現(xiàn)代的游戲中,基本沒有物體能在一步就完成渲染,這是因?yàn)橛泄庹蘸完幱暗年P(guān)系。光照和陰影的渲染在Fragment Shader中需要額外的pass。
首先要設(shè)置場景中的Shadow Casters和Shadow Receivers,Shadow Casters投射陰影,Shadow Receivers接收陰影。之后每一個(gè)Shadow Receiver渲染時(shí),GPU就會在燈光的位置和角度渲染Shadow Caster物體,將深度信息存儲到一張貼圖中。這張貼圖就是常說的ShadowMap,它用來實(shí)時(shí)的渲染陰影。光照渲染和陰影渲染在渲染管線中開銷非常大,需要每個(gè)頂點(diǎn)都要提供法線信息以及額外的頂點(diǎn)顏色信息等。因?yàn)镕ragment Shader需要多個(gè)passes完成最終的渲染,Backe End在Fill Rate(許許多多的像素都需要繪制,重新繪制和合并)和 Memory BandWidth(額外的texture需要來回獲取,比如LightMaps和ShadowMaps)兩方面都會壓力很大,這也是為什么實(shí)時(shí)陰影是非常昂貴的,也會給Draw Call帶來很大開銷。
但是光照渲染和陰影渲染應(yīng)該是現(xiàn)在游戲中最重要的兩部分,的的確確能給游戲帶來效果上質(zhì)的提升,出色的光照和陰影效果能使得平庸的場景搖身一變。有兩種渲染管線,Forward Rendering 和?Deferred Rendering,在?Rendering? Edit |Project Settings | Player | Other Settings | Rendering 下可以進(jìn)行設(shè)置,接下來介紹下這兩部分。
Forward Rendering
同樣的Shader多個(gè)Pass渲染,一共有多少個(gè)pass取決于光源的數(shù)量和光源的距離和光源的亮度。在Unity中,前向渲染規(guī)則如下:一個(gè)場景中多光照情形,按光源的重要程度排序,比如ABCD四盞燈用效果最好的逐像素方式渲染光照,DEFG用逐頂點(diǎn)方式渲染光照,GH則用球諧光照方式計(jì)算光照。(關(guān)于球諧光的一些知識,可以參考我的這篇文章https://blog.csdn.net/yinfourever/article/details/90205890)
Unity中正向渲染分為一個(gè)Base Pass和多個(gè)Additional Passes.
?
Base Pass: 用逐像素的方式渲染一個(gè)directional light, 所有球諧光和逐頂點(diǎn)光。在這個(gè)pass里也會計(jì)算lightmap等,directional light可以計(jì)算陰影,要注意如果使用了lightmap照亮的物體不會被球諧光照亮。
Additional Passes:每個(gè)其他需要用逐像素渲染的光源將會用一個(gè)Addtional pass渲染,默認(rèn)情況下這些燈光不會有陰影除非使用了?multi_compile_fwdadd_fullshadows?variant shortcut 更詳細(xì)的信息可以參考Unity官方文檔中關(guān)于Forward Rendering的介紹https://docs.unity3d.com/2019.2/Documentation/Manual/RenderTech-ForwardRendering.html
可以通過Edit | Project Settings| Quality | Pixel Light Count 設(shè)置像素光的數(shù)量,但是會被每個(gè)Render Mode設(shè)置為Important的Lights覆蓋。
ForwardRendering當(dāng)場景中有大量點(diǎn)光源時(shí)由于Render States需要不斷的設(shè)置會產(chǎn)生大量Draw Call,跟場景光源數(shù)量正相關(guān)。
Deferred Rendering
Deferred Rendering通過G-Buffer工作,是將光照計(jì)算延后進(jìn)行處理的一種渲染方法。
延遲渲染的優(yōu)點(diǎn)
Deferred Rendering 的最大的優(yōu)勢就是將光源的數(shù)目和場景中物體的數(shù)目在復(fù)雜度層面上完全分開。也就是說場景中不管是一個(gè)三角形還是一百萬個(gè)三角形,最后的復(fù)雜度不會隨光源數(shù)目變化而產(chǎn)生巨大變化。
復(fù)雜度僅O(n+m)。
只渲染可見的像素,節(jié)省計(jì)算量。
用更少的shader。
對后處理支持良好。
在大量光源的場景優(yōu)勢尤其明顯。
?
延遲渲染的缺點(diǎn)
內(nèi)存開銷較大。
讀寫G-buffer的內(nèi)存帶寬用量是性能瓶頸。
對透明物體的渲染存在問題。在這點(diǎn)上需要結(jié)合正向渲染進(jìn)行渲染。
對多重采樣抗鋸齒(MultiSampling Anti-Aliasing, MSAA)的支持不友好,主要因?yàn)樾栝_啟MRT。
由于Deferred Shading的Deferred階段是在完全基于G-Buffer的屏幕空間進(jìn)行,這也導(dǎo)致了物體材質(zhì)信息的缺失,這樣在處理多變的渲染風(fēng)格時(shí)就需要額外的操作。
更詳細(xì)的有關(guān)渲染模式的知識請查看我的這篇文章(墻裂建議看)
https://blog.csdn.net/yinfourever/article/details/90263638
Vertex Lit Shading (legacy)
已被遺棄
Global Illumination
全局光,是烘焙光照貼圖的一部分。光照貼圖是提前烘焙好的,因此可以有足夠的時(shí)間生成高質(zhì)量的光照貼圖,會節(jié)省大量Draw Call。也正是因?yàn)樘崆昂姹汉玫?#xff0c;所以它不是實(shí)時(shí)的,所以它對Static物體生效,動態(tài)物體需要通過LightProbe才能受到影響。LightProbe并不是像素級精準(zhǔn)的(使用球諧光),并且會生成額外的LightProbe Maps,對Memory BandWitdh有開銷,但是確實(shí)會對渲染效果帶來很大的提升。
對于全局光,不僅計(jì)算直接光照的影響,還會將物體間反彈的光考慮進(jìn)去。老版本的GI系統(tǒng)是Enlighten,它不僅提供了靜態(tài)的GI,還提供了一種Pre-computed Real-time GI,可以用來制造實(shí)時(shí)渲染的假象,比如晝夜更替系統(tǒng)。目前Unity最新的GI系統(tǒng)是Progressive LightMapper。
關(guān)于這部分知識,我寫過另一篇文章幫助理解https://blog.csdn.net/yinfourever/article/details/105151596
Multithreaded Rendering
多線程渲染在大多數(shù)平臺中是被默認(rèn)開啟的,比如PC或者其他CPU支持多核的。對于Android,Edit | Project Settings | Player | Other Settings |Multithreaded Rendering可以進(jìn)行設(shè)置,對于IOS可以在Edit | Project Settings | Player| Other Settings | Graphics API設(shè)置。
對于場景中的一個(gè)物體,要進(jìn)行渲染的話有三件事需要處理。決定這個(gè)物體是否需要渲染,生成渲染指令,通過相關(guān)圖形API發(fā)送指令到GPU。如果沒有Multithreaded Rendering,這些工作都要由CPU主線程承擔(dān),開啟Multithreaded Rendering,把渲染指令推送給GPU的工作將由render線程承擔(dān),其他例如剪裁等工作將由其他一些worker 線程承擔(dān)。這給CPU主線程大大減壓,可以給物理運(yùn)算以及腳本邏輯運(yùn)算留出更多空間。
開啟Multithreaded Rendering后,關(guān)于CPU-Bound會有一些影響,沒開啟的時(shí)候,所有工作都是由CPU主線程做,因此無論那部分性能的優(yōu)化都會對CPU-Bound帶來改善。開啟Multithreaded Rendering后,因?yàn)楣ぷ鞅环稚⒌礁鱾€(gè)線程,即使對主線程做出一些優(yōu)化,對CPU-Bound的減輕程度可能也會變得很小。
是否開啟Multithreaded Rendering對GPU沒影響,GPU已經(jīng)總是用多線程渲染。
Low-level rendering APIs
通過CommandBuffer可以使用高級別的API對渲染管線進(jìn)行一些控制,但是可用的內(nèi)容和范圍還是太少,因此可以用C++代碼直接控制圖形API,將C++代碼打成一個(gè)Plugin,hook到Unity的渲染管線中。
當(dāng)然,現(xiàn)在Unity已經(jīng)推出了SRP(可編程渲染管線),已經(jīng)可以完全自定義渲染管線,十分方便,因此這一小節(jié)提到的方法可能已經(jīng)基本不會使用了。關(guān)于SRP,可以看我這篇文章https://blog.csdn.net/yinfourever/article/details/89364090
Detecting performance issues
Profiling rendering issues
通過Unity的Profiler 可以快速定位是瓶頸是在CPU和是GPU.
CPU-bound
下圖是一個(gè)CPU瓶頸的例子,這個(gè)場景有成千的簡單Cube物體,但是沒有使用batching并且有陰影的渲染,這導(dǎo)致CPU需要生成大量的Draw Call,但是GPU實(shí)際上渲染任務(wù)很少。
如何定位問題的呢?下圖中CPU平均需要25ms處理一個(gè)循環(huán),而GPU只需要4ms以內(nèi),這就說明了瓶頸在CPU端,應(yīng)該想辦法使用一些優(yōu)化CPU端的技術(shù)。
?GPU-bound
下圖的測試條件為很少的Draw Call,但是使用非常復(fù)雜的Shader進(jìn)行渲染。如果想測試是否是GPU端的瓶頸,需要注意的是要關(guān)掉V Sync(垂直同步),不然會影響Profiler。
從圖中我們可以看到CPU和GPU都消耗29ms左右,但是再深層次查看一下,CPU的時(shí)間都花在了?Gfx.WaitForPresent函數(shù)上,這是在CPU在等待GPU完成這幀操作而浪費(fèi)掉的時(shí)間,即使開啟多線程渲染,CPU也必須是等待渲染管線完成后才能開始下一幀。Gfx.WaitForPresent也同時(shí)會被Vertical Sync使用,因此要關(guān)掉Vertical Sync來避免干擾。
Brute-force testing?
?如果從Profiler中沒辦法定位問題,我們可能就得使用蠻力測試。比如從場景中去除一些物體,看看性能有沒有很大改善,如果很小的改動會帶來很大提升,那其就定位了問題。
降低屏幕分辯率和降低貼圖的分辯率來進(jìn)行測試是兩種定位瓶頸是FillRate還是Memory Bandwidth的好方法。
降低屏幕分辨率可以有效降低Frill Rate的消耗(比如2560 x 1440 降為?800 x 600大概會降低8倍),如果性能有很大的提升,說明Fill Rate有可能是我們應(yīng)該首要考慮優(yōu)化的地方。
類似的,如果降低貼圖的分辨率后性能提升非常大,則說明Memory BandWidth可能是瓶頸。
GPU的瓶頸通常是FillRate和Memory Bandwidth,很少是Front End的原因,Vertex Shader如果有問題,只能是因?yàn)閭魅肓颂鄮缀误w需要處理或者使用了很復(fù)雜的Geometry Shader。
?
?
總結(jié)
以上是生活随笔為你收集整理的Unity 2017 Game Optimization 读书笔记 Dynamic Graphics(2)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 平安信用卡审核会打家人电话吗?什么情况会
- 下一篇: Unity 2017 Game Opti