在幾乎所有現代化汽車上,數字儀表盤從某種形式上來說屬于標準功能,它們第一次出現是在20世紀80年代,最近幾年它們又重新流行起來,這在很大程度上要歸功于現代GPU更加強大的圖形功能。雖然低配的汽車可能只有里程計算的數字顯示,但是高端車型則設計了完全數字化的儀表盤,包括表盤。相比傳統的物理顯示,完全數字化的儀表盤有很多的優勢,提供的信息更加的精確、精細甚至更加的清晰。它們可以調節并且動態顯示,準確的顯示駕駛員可能隨時需要看的信息,甚至還可以自定義顯示從而滿足駕駛員的個人喜好。它們的外觀也更加的時尚、現代化,這也是吸引車主的原因。但是在我們的研究中,我們發現數字儀表盤因為幾何圖形的原因設計得并不是很好,因此無法有效的利用嵌入式GPU的性能,這導致在選擇GPU時做出糟糕的決定,供應商錯誤的認為小型的GPU無法勝任這樣的需求從而指定其他硬件。因此當我們在開發汽車組合儀表時,我們的主要目的是展示在不影響質量的情況下如何可以高效的對汽車儀表進行渲染,例如使用最小型的PowerVR Series8XE內核。
在本文中,我們將會列出各種相關的優化技術,目的是確保汽車儀表盤設計師能夠充分利用他們所采用的硬件。并非所有列出的技術都在我們開發儀表盤過程中都使用過,要么是出于設計原因,要么僅僅是因為達到了性能和質量的要求。但是由于某些實際應用程序可能會更加的復雜,我們還介紹了其他一些有用且必要的技術。
1、組合儀表盤用戶界面(UI)
1.1 表盤的渲染因為表盤是圓形的,想渲染得很好往往是有一定難度的。要實現完美的圓形通常是向網格中添加更多的三角形(幾何),直到看起來足夠的好為止。這種方法在大型且離散的GPU上運用效率是非常低的,因此,對于在汽車上通常采用的小型嵌入式GPU上是不建議這樣做的,因為這意味著幾乎不可能實現良好的性能。幸好有一些明智的解決方案可以將性能提升到可接受的水平,而無需去降低質量。
對于表盤來說,實際上很容易用一個四邊形近似實現:alpha透明度可以使其變為圓形,而法線貼圖可以實現表面細節的錯覺:
表盤的紋理四邊形和法線貼圖顯然這是首選的集合方法,但是從透明度利用而言這確是一種浪費:中心是完全不透明的但也要使用透明度,周圍的空白部分是完全透明的也要進行柵格化處理。考慮到法線貼圖在分片階段是非常耗資源的,最好是減少它占用的像素數。不用太夸張的縮小幾何圖形而是近似接近表盤形狀幾何,對于這個復雜幾何形狀我們可以劃分為兩個網格,中央部分是不透明的(有助于減少場景透支),外圍使用alpha混合處理(使其形狀變得更圓)。
不透明的中心和透明的外環(橙色高亮部分)分離設計對法線貼圖和切線有了一些了解之后,我們可以做一些事情進一步提高紋理的利用率。本質上不是將整個形狀烘焙為紋理,我們僅提取楔形的法線貼圖,然后將其循環復制。因為法線貼圖與切線是相對的,切線會隨著幾何形狀旋轉,從數學角度證明這是對的,一切都如期望的那樣。
使用可復制的楔形可以更好的利用紋理資源在這里使用alpha混合的一個巧妙的副作用是我們可以稍微修飾表盤的邊緣,輕松的去掉鋸齒。下圖是不同MSAA(多重采樣抗鋸齒)情況下的對比:
為了證明該技術在性能上是可行的,我們已經在其中一款最小型的GPU(PowerVR GE8300)上進行了測試,并且使用PVRTune工具比較了性能。測試場景包括相同的表盤網格,以1080P的分辨率每幀重復24次。
下圖來自我們的PVRTune性能分析工具,顯示的是在進行渲染時GPU的時間是如何分布的。下面的方框代表的是執行的任務,根據幀序號用顏色進行了標注。幾何任務處理的是包括執行頂點著色器、剔除表面或屏幕外三角形等產生的幾何圖形。渲染任務代表的是在像素級別執行的操作,例如三角形柵格化、執行像素著色器。
正如預期的那樣,即使GPU相當多的時間用來處理幾何圖形,這對移動端GPU來說也仍然是可以接受的。更令人驚訝的是,將幾何圖形轉為像素的時間是19ms。假設目標幀率要求整個幀在16.6ms以內,那么該性能水平是不可接受的,尤其考慮到在這種情況下使用的分片著色器是微不足道的,那么問題來了,究竟是什么原因占用了這么長時間呢?很簡單,光柵化根本無法很好的處理長且細的三角形,從上面的圖表中可以看出:像素處理負載計數器顯示像素處理時間只有一部分用來執行像素著色器,在正常情況下該值預計將接近100%。然而在這種情況下甚至沒有達到60%,這意味著GPU卡住了做其他事情,大多數情況下可能很難將形狀不好的幾何圖形柵格化。使用4X MSAA(多重采樣抗鋸齒)情況變得更糟,應用程序性能從50fps下降到30fps,現在以30fps進行全高清(Full-HD)渲染并非不可接受,而且還是很多3D應用程序和游戲的標準配置,但是對于重要的汽車組件我們希望實現更高的目標。現在我們談談優化后的表盤渲染,從外觀上看線框似乎很合理,最后一幀看起來很完美。
優化后的表盤看起來與原始網格相同
然而,正如我們從下圖中看到的,優化后它能夠以完全的V向同步60fps來運行,從而為應用程序中的其他功能和內容提供了空間。
大大縮短了渲染時間并提高了GPU利用率
有趣的是,通過為GPU提供更高要求的分片著色器,像素渲染任務現在執行得更快——有時候優化可能與直覺是相反的。
1.2 文本渲染
正如我們在表盤上發現的那樣,我們已經看到很多測試程序和應用使用鑲嵌幾何體來渲染文本,但是原因不清楚。將文本渲染為網格而不是紋理四邊形的唯一理由可能與放大紋理時的過濾質量有關。然而事實證明,這個問題在10年前就可以通過DFF(distance field fonts,距離場字體)技術解決了,不受傳統的雙線性過濾假象所影響,我們建議采用這項技術。
與常規字體紋理不同DFF可以很好的處理放大倍數
DFF看起來與傳統字體略有不同
距離場的一般做法是將距離值映射為不透明值,最基本的解決方案就是一個簡單的閾值:
// 從紋理中提取距離場值float dist = texture(sdfTexture, uv).a;// 將距離轉換為alpha值float alpha = step(_BaseThreshold, dist);
通過修改閾值,文本可以變得更粗或更細,這非常有用。“硬閾值”將會導致文本邊緣很硬,這無疑是令人討厭的,而且任何MSAA(多重采樣抗鋸齒)都無法修復:
“硬閾值”導致文字邊緣很硬
但是,這時可以通過非常簡單且方便的方法進行補救,將“step”函數換成“smooth step”函數,同時引入一個很小的平滑邊距參數:
float alpha= smoothstep(_BaseThreshold-smoothing, _BaseThreshold+smoothing, dist);
更加平滑的閾值會使得字體的邊緣更好
雖然這會使放大的文字看起來令人滿意,但放大文字從遠處看起來效果很差,不過可以通過改變平滑值來修復,這與UV坐標的每個像素的倒數有關,如下所示:
float _BaseThreshold = 0.5; // 修改此值可使字體變粗float _Filtering = 16.0; // 抗鋸齒因子float _MinSmoothness = 0.01; // 接近0的值可使文本更清晰float _MaxSmoothness = 0.4; // 防止小字體過度模糊float getFilteringSMoothness(vec2 uv, float intensity, float maxSmoothing){vec2 size = fwidth(uv);float smoothingBias = min(max(size.x, size.y)*intensity, maxSmoothing);return smoothingBias;}float getOpacity(vec2 uv){// 從紋理中提取距離場值float dist = texture(sdfTexture, uv).a;// 將距離轉換為Alpha值float smoothing = _MinSmoothness + getFilteringSMoothness(uv, _Filtering,_MaxSmoothness);float alpha = smoothstep(_BaseThreshold – smoothing, _BaseThreshold +smoothing, dist);return alpha;
這樣可以使圖像比使用標準透明字體和幾何字體這兩種方法都更加的清晰,這項技術不僅可以用于字體,而且適用于任意類型的2D UI元素,此外,這也是面向未來UI的好方法。當使用傳統紋理時,渲染分辨率大幅提高,UI元素的分辨率也要進行類似的提升,這樣才能都有更好的效果,但無論如何放大距離場都可以保持清晰度,因此提高分辨率不是問題,而且不需要其他額外的操作。在性能方面,相比傳統紋理,DFF性能更好,但它仍然比過度細分的網格渲染的更快。
三種文字渲染方法的渲染時間(單位ms)
在性能測試中,全幾何文字使用3dsMax默認設置創建(包括基礎的網格優化),紋理字體由單個四邊形字符組成,
文本渲染對比測試
和表盤一樣,采用簡單的透明度方法而不是復雜的不透明網格可以顯著的提升性能。基本的紋理可能不適用所有情況,例如一個文本元素被放大,使用DFF方式可以以非常低的代價緩解所有過濾問題。
1.3 使用預乘的alpha值將CPU的開銷最小化
有時有必要將alpha混合與相加混合一起使用,在我們的案例中就是這樣做的,因為我們有霓虹燈設計——主要元素使用alpha透明度,周圍的輝光采用相加混合的方式來實現。
傳統上你必須分兩次進行渲染,引入GL狀態變量,實質上讓繪圖調用次數翻倍。對于大多數應用來說這不是一個重要的問題,但是該解決方案非常簡單,值得一提的是預乘alpha值,只需要一個GL狀態變量就可以創建:
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
這在軟件方面是必需的,下一步是創建紋理并利用。
這個圖標設計顯示了相加混合和alpha混合特性
RGB通道應該包含黑色背景以及輝光的常規顏色信息,并且alpha層應該僅代表圖標的不透明部分。完成后輝光像素會彼此融合,而alpha層會正確覆蓋背景。這意味著現在可以直接渲染大部分UI元素,無需引入其他狀態或著色器。
2、汽車渲染
隨著儀表盤用戶界面(UI)的發展,現在是時候解決最具挑戰的部分了:在一款小型的嵌入式GPU上渲染整個車身,并細節化處理。
精細的汽車模型要可以從不同的角度環繞查看,駕駛員可以在儀表上看到他們的汽車模型,以及車輛周圍的真實場景,這要借助車載攝像頭、雷達或者激光雷達(LiDAR)。汽車廠商以及車主都希望儀表上顯示的是真實的車輛,越精確越好,而不是簡單的汽車模型。與表盤上的文本一樣,在汽車渲染問題上省去很多的三角形無疑是一種簡便的方式,但是想要獲得更好的效果還有更好的方法,下面我們來詳細介紹一下。
2.1 紋理的一些技巧
鑒于紋理在解決UI幾何瓶頸方面取得的成功,由此推測它們在其他方面也會非常有用。以這個看起來還不錯的汽車內飾為例。
但是這是通過簡單的紋理技巧實現的,使得實際的幾何形狀非常簡單。這是怎么實現的呢?這是法線貼圖與烘焙光照的簡單組合,可能需要花費一定的時間來創建,但是帶來的成果確實巨大的。
最明顯的想法是將小的細節繪制到紋理中。然而這種方法的結果不足以讓人信服,必須更進一步讓紋理可以控制光照。2.1.1 法線貼圖法線貼圖是降低儀表盤渲染成本的便捷方法,不幸的是應用到汽車時,我們遇到兩個主要的問題:① 汽車是三維的(與表盤不一樣,表盤幾乎是平的),如果要讓車體的輪廓非常的光滑,則需要非常密集的網格。② 法線貼圖在精度上是有限的,包括紋理精度以及深度精度,這會導致紋理外觀并不理想以及出現一些條帶狀的假象。
這是否意味著我們應該完全忽略法線貼圖呢?絕對不是!它們在很多地方都非常有用,不僅僅是汽車車身。例如在儀表案例中,我們將車輪壓縮到其原始尺寸的1.5%,而且沒有明顯的質量損失:
法線貼圖可以大大減少汽車很多部位幾何圖形的數量
2.1.2 烘焙光照(Baked Lighting)將高質量的網格光照投射到用于渲染的較低質量對象上是仿造細節的另一個好方法。環境光遮蔽(Ambient Occlusion)通常是首選的照明方式:它完全可以取代動態光照,甚至可以更好的提升動態光照,減少幾何圖形的散光程度,在場景中創建讓人舒服的柔和陰影。
環境光遮蔽(Ambient Occlusion)用于漫反射以及反射效果調試是非常有效的,而且通常也是非常有用的工具。
整個汽車的環境光遮蔽烘焙貼圖2.1.3 插曲:UV管理如果將整個車身轉換成紋理(大概是帶有環境光遮蔽的情況),那么需要進行完全的UV展開。但是車體是相對比較大的對象,使用4K紋理囊括所有的細節,這在數量上是有限制的。好消息是車身是對稱的,而且環境光遮蔽是完全獨立的。這意味著車身一側的UV可以與另一面的重合,合并成一個,從而可以節省大量的紋理空間。如果我們可以提前知道車身的哪部分不需要細致的渲染,我們也可以縮小這部分UV,把空間分給更重要的部分。
巧妙的打包策略,在UV中將重復的對稱部分進行重疊(紅色高亮部分)
2.1.4 浮動
還需要渲染最小的按鈕,螺釘和螺栓?好吧,這里有一個非常簡單的方法,可以處理超級小和重復的細節,而不用創建超大的紋理資源。這個方法通常稱為“浮動”——創建包含很多細節的紋理組合,在原始網格上渲染一層透明的網格。
基本的儀表板渲染,3萬個三角形
采用“浮動”技術的儀表板:1138個三角形
顯而易見,由于所有細節都在法線貼圖中模擬,因此優化后的版本缺乏深度,除了這個缺點,它與整個模型對于光照和反射的方式是一樣的。注:在上面的示例中,此優化版本的1138個三角形中,超過1000個用于金屬旋鈕,這顯然是浪費,應該進一步優化。
僅浮動網格的儀表板
由于這些元素都是重復和映射的,紋理空間中它們是最小化的,如下使用“浮動”方法的256 x 256分辨率的法線貼圖所示:
“浮動”的法線貼圖
為了變得更好,從一定距離來看紋理技巧往往非常穩定,僅限幾何圖形的系統肯定不是這種情況,如下圖所示,呈現了難以接受的不同級別的鋸齒現象:
原始的網格 - 沒有MSAA(多重采樣抗鋸齒)處理
從靜止圖像上看起來可能很差,但是在動態情況下會下降一個數量級,尤其是當大部分像素以非常激進且分散的方式進行閃爍。使用8倍MSAA可以極大的改善,但是不夠完美,鑒于這種技術的成本,這實在令人遺憾:
原始的網格 - 8倍MSAA處理
然而,采用基于紋理的解決方案,相比MSAA我們可以獲得更清晰的圖像,并且運動時的穩定性接近完美。
優化后的網格,無MSAA
2.2 幾何的一些技巧
現在,我們已經整理好了車內部分和一些小的細節,我們仍然需要解決最大的問題:車身。考慮到它定義了汽車的輪廓,因此顯然需要比其他部分的幾何圖形要更加的密集。
65000個三角形的車身網格
2.2.1 改善法線有趣的是,在某種程度上,由3D藝術家設計的很多幾何圖形是不必要的:現代工具和渲染管道在過去幾年中已經取得了很大的發展,但是一些舊的習慣已經不適宜了,如果還遵循過時的最佳做法,最終效果可能適得其反。舉個這種不良作法的例子,在幾何圖形中添加支撐邊來改善法線差值,當制作工具可以完全引入法線時,這很有意義,也有可能根本不產生平滑的邊緣或者邊緣非常粗糙。在差值邊緣添加支撐邊可以幫助按需要的方式確定法線的方向,從而使光照能夠更緊密的匹配密集網格的外觀。但是任何現代且好用的制作工具都應該支持局部加權法線,無需額外的幾何圖形就可以產生非常好的效果。下圖顯示了局部加權法線與密集網格陰影效果的匹配程度:
為了處理得更好,因為所做的只是改變法線的計算方式,因此不需要消耗性能,也不需要任何工程操作就可以集成到渲染器中。盡管它們可以幫助消除很多網格中存在的一些邊緣環,但是局部加權法線不會以任何方式解決輪廓的問題,所以我們仍然需要數量相對較多的三角形來實現。2.2.2 幾何格式表面上雖然是這樣,但是有很多方法可以使用PowerVR SDK工具來降低幾何圖形的渲染成本,PVRGeoPod可用于3D創作程序包的導出,它是最受歡迎的場景導出工具,可以用來微調幾何圖形的表示方式。
POD導出工具的推薦幾何設置
我們需要查看的第一個選項是索引。有了索引就可以通過索引緩存訪問三角形的頂點,這個緩存可以讓同一個頂點使用多次,從而避免復制多個三角形頂點的需要。我們在PowerVR GE8300 GPU上進行了測試,顯示從標準的三角形列表轉換為索引列表使得頂點處理的性能翻倍。接下來是容易忽略但又很容易解決的問題:三角形和頂點排序。這里的目的是最大程度利用緩存,即確保我們不會遍歷內存獲取所有幾何圖形數據,而是盡可能采用最佳連續的方式對其進行排序。雖然不像索引一樣盡如人意,但是對三角形和頂點進行排序確實提升了27%的性能。2.2.3 剔除和細節程度通常最好不要將不必要的工作提交給GPU來處理,盡管PowerVR基于分片的延遲渲染(TBDR)架構非常適合降低陰影隱藏幾何體的著色成本,但是GPU仍然必須先處理所有的頂點,如果知道某個物體對象(被遮擋或在屏幕外)不會影響圖像的質量,那么不應該提交給GPU來處理。小的三角形(僅占用幾個像素)往往會破壞GPU的并行性,因此會增加性能成本但是幾乎沒有視覺差異。避免這種情形的常用方法是使用不同層次幾何密度的網格,根據它在屏幕的尺寸渲染最相關的部分。這對于車身渲染不是很有用,因為車身在尺寸上不會出現千差萬別的現象,但是對于一些潛在的更小元素仍然是非常有用的。
2.3 清理渲染
鋸齒現象通常是不可接受的,MSAA(多重采樣抗鋸齒)是常用的方法,可以減輕其影響。但是通常將采樣數量設置為硬件支持的最大值,這樣會導致圖像質量提升有限同時性能急劇下降。為了正確的消除鋸齒,我們必須知道原因,僅僅認為它只是源于幾何體邊緣的失真,這種認識是非常淺顯的。我們應該考慮以下幾種鋸齒類型:2.3.1 紋理鋸齒可能是最容易修復的一種:對所有的紋理進行mipmaps(多級映射)處理,這可能導致一些紋理過于軟化,使用各向異性過濾通常是修復這種問題的建議做法,這種方法更加的正確,但是代價也更高一些,因此需要慎重的考慮。代價更低的方法是為紋理指令引入第三個參數,簡單的進行mipmap層級估計從而降低這種現象。
lowp vec3 colour = texture(tex, uv, -1.0).rgb;
2.3.2 幾何鋸齒
在這種情況下,MSAA是處理這種問題的正確方式,它本身就是非常的精確。雖然可以解決這種問題,但是代價比較高尤其是采樣數量高,因此對于低端設備應避免使用2x MSAA。這聽起來很嚴格,但是如果你已經采用了前面提到的所有小技巧,那么車身的細節部分應該已經非常清晰了。FXAA(快速近似抗鋸齒)是另一種可能性,它采用后處理方式對高對比部分(即可能是由鋸齒引起的)進行了模糊處理,與MSAA不同,這種方式不會使圖像清晰,但是確實能夠讓圖像看起來更干凈。適中的圖像質量帶來的好處并不一定會超過圖像質量的損失,在MSAA基礎上使用它可以產生更清晰的圖像。還應該指出的是文本應該在FXAA(快速近似抗鋸齒)后進行渲染,否則產生的模糊效果會使其可讀性大大降低。
2x MSAA 與輕量級FXAA(快速近似抗鋸齒)的幾何抗鋸齒效果對比
2.3.3 陰影鋸齒這種類型的鋸齒稍微更復雜一些,產生的原因也更多。可能是著色器的條件控制導致相鄰像素間的不連續或更可能是高頻的表面細節導致像素光照和反射情況的急劇變化。對于車身的渲染,大量的金屬表面或非常光亮的表面將會在一些小的部位或彎曲的區域產生大量的鋸齒,基于局部曲率可以減少反射的平滑度,如本演示中所解釋的那樣(幻燈片43)。
幾何體曲率
標準的光亮車身顯示出許多鋸齒
反射粗糙度受曲率的影響,可以使車身變得更加清楚
陰影鋸齒在改善反射效果上是有效的,計算成本是非常低的,而且也容易實現。表2:使用和不使用曲率抗鋸齒的渲染時間對比(ms)
該技術非常適合中等密度的網格,比如車身。對于其他網格若采用基于紋理的工作流程,也可以保護高質量網格的曲率使用低分辨率,使用它來偏置表面的反射率,從而避免高頻反射的不穩定性。
高質量網格的曲率投射到儀表板浮動幾何體上
原始粗糙度(左)和修改后的(右),越暗的地方越光亮
使用原始(左)和修改后(右)的粗糙度貼圖進行渲染,顯示出光亮金屬表面鋸齒的明顯差異
對于其他類型的鋸齒,FXAA(快速近似抗鋸齒)應該能夠消除它們。作為圖像的過濾器它通過單程著色能夠清除所有類型的鋸齒。
3、結合在一起
我們這個儀表板案例最重要的設計決策是將渲染的中心部分(比如3D車身)劃分為單獨的渲染目標。這是出于以下原因:
1. 它支持復雜的過渡變換(縮放、平移、淡入淡出等)2. 與儀表板UI不同,中心部分需要一系列的抗鋸齒處理,若應用到整體會非常的浪費3. 如果沒有任何變化,則可以渲染暫停緩存(盡管我們實際上并沒有使用過,因為我們想展示GPU的性能極限)或者在不同的頻率下運行
4. 可以按不同的分辨率渲染,從而可以在一系列GPU上進行不同的縮放處理(出于類似的原因實際上我們也未使用)
儀表盤的幾何圖形包含最多600個三角形,沒有這些復雜的紋理整體就不夠美觀
移動端GPU適用于對帶寬敏感的場合,因此對于大型且單獨的渲染目標,渲染成本是非常高的。第一步(也是最顯要的)是使整個渲染帶寬的成本最小化,確定好合適的帶寬:如果中心部分只占顯示的一半尺寸,那么按照一半的分辨率進行渲染效果也是非常明顯的。但是最重要的是告訴GPU需要寫入或讀取哪些內容,這部分很容易就會被忽視,因為關于渲染的大部分規則最是針對臺式機GPU設計的,它們不需要考慮這一點,只有最新的圖形API提供了適當的機制。
對于典型的渲染操作每一幀都應該做的兩件事:
1) 開始時清除之前的所有渲染
2) 在每一幀的末尾告知驅動程序舍棄深度緩沖區的內容
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// 渲染操作執行到這里…GLenum depthAttachment = GL_DEPTH_ATTACHMENT;glInvalidateFramebuffer(GL_FRAMEBUFFER, 1, &depthAttachment);
這種簡單的結構將大大減少渲染到紋理的帶寬成本,而且在運行應用程序時能夠大大降低功耗
責任編輯:pj
-
控制
+關注
關注
4文章
1013瀏覽量
122716 -
儀表板
+關注
關注
0文章
35瀏覽量
9151
發布評論請先 登錄
相關推薦
評論