在上一篇文章中,我討論了一個(gè)解決方案,即通過(guò)使用單個(gè)緩沖帶渲染減少VR中的延遲。我提到,VR對(duì)圖像質(zhì)量的要求相當(dāng)高,因?yàn)镚PU相比傳統(tǒng)的移動(dòng)應(yīng)用程序需要做更多的工作。
工作負(fù)載增加的其中一個(gè)原因是對(duì)象內(nèi)容需要進(jìn)行兩次渲染(每單位矩陣渲染一次)。另一個(gè)原因是GPU最后一次渲染使用了桶形失真過(guò)濾(每單位矩陣再渲染一次)。這使得圖像處理器的工作負(fù)荷特別大,因此我們需要思考良策以降低負(fù)荷。
為何使用透鏡?
您可能會(huì)有疑惑:當(dāng)今的移動(dòng)設(shè)備功能已十分強(qiáng)大,為什么我們還需要這些學(xué)校里習(xí)得的老舊的光學(xué)技巧?
VR耳機(jī)使用透鏡有兩大主要原因:
1. 透鏡可以拓寬視野
2. 透鏡可以使屏幕與你的眼睛離得更近
例如,如果您的手機(jī)屏幕是11.5cm x 6.5cm,那么每只眼睛水平位置可視的屏幕寬度為5.75cm。由于屏幕與人臉?lè)浅?拷虼讼啾热搜蹖?shí)際可視的寬度,這個(gè)寬度還遠(yuǎn)遠(yuǎn)不夠。在兩個(gè)屏幕之間放置一塊透鏡,那么不管是水平視角還是垂直視角,可視范圍均會(huì)增加。
使用透鏡,我們可以使虛擬世界比實(shí)際世界看起來(lái)大很多。
其次,有了透鏡,即使用戶的眼睛與屏幕非常接近,但其看到的屏幕效果比實(shí)際上的卻遠(yuǎn)很多,這樣使得用戶體驗(yàn)更為輕松。
顯然,也存在一些缺陷。由于失真,圖像的某些部分將被壓縮,從而將失去一些信息;而剩下的部分則意味著要用更高的分辨率進(jìn)行填充。同時(shí),透鏡導(dǎo)致的一些色差也需要被修正。
桶形失真
由于存在桶形失真,我們需要應(yīng)用反向轉(zhuǎn)換,這樣屏幕發(fā)出的光線才比較適宜。而這種轉(zhuǎn)換的完成主要取決于物理透鏡的設(shè)計(jì)。
在VR中,透鏡設(shè)計(jì)相當(dāng)復(fù)雜。Oculus Rift CV1是一款做的非常好的VR案例。顯然,所有這些設(shè)計(jì)的決策都會(huì)影響到修正圖像所需的算法。
桶形失真最常見(jiàn)的方法是使用。當(dāng)然,還有很多其他可用的失真模型——如使用多項(xiàng)式函數(shù)或曲線。一個(gè)常見(jiàn)的問(wèn)題是創(chuàng)建的是非零逆函數(shù)(non-trival)。為簡(jiǎn)單起見(jiàn),我們將使用以下模型用于桶形失真,其逆函數(shù)可以進(jìn)行計(jì)算:
α定義了我們想要應(yīng)用的失真量,而α由理論透鏡設(shè)計(jì)進(jìn)行界定。p中的輸入值x和y在[-1, +1]之間是標(biāo)準(zhǔn)化的。
p(x, 0)的正向和反向失真函數(shù),α=0.3
α呈增長(zhǎng)態(tài)勢(shì)的桶形失真圖
基于像素的修正 VS 基于網(wǎng)格的修正
一個(gè)方法便是,在顯示最后圖像之前,在后期處理片段著色器中進(jìn)行最后渲染的修正。假設(shè)將VR內(nèi)容渲染至每矩陣的幀緩沖對(duì)象(FBO)中。使用OpenGL ES 3.0,頂點(diǎn)著色器和片段著色器便如下所示:
#version 300 es
in highp vec4 posVtx;
uniform mat4 mvpM;
void main(void)
{
gl_Position = mvpM * posVtx;
}
#version 300 es
in highp vec2 texFrg;
out highp vec4 frgCol;
uniform highp vec2 centre;
uniform highp sampler2D texSampler;
void main(void)
{
highp vec4 col = vec4(0.0, 0.0, 0.0, 1.0); /* base colour */
highp float alpha = 0.2; /* lens parameter */
/* Left/Right eye are slightly off centre */
/* Normalize to [-1, 1] and put the centre to "centre" */
highp vec2 p1 = vec2(2.0 * texFrg - 1.0) - centre;
/* Transform */
highp vec2 p2 = p1 / (1.0 - alpha * length(p1));
/* Back to [0, 1] */
p2 = (p2 + centre + 1.0) * 0.5;
if (all(greaterThanEqual(p2, vec2(0.0))) &&
all(lessThanEqual(p2, vec2(1.0))))
{
col = texture(texSampler, p2);
}
frgCol = col;
}
centre 允許將失真集中的部分稍微移出FBO的中間位置,通常透鏡并不完全集中到屏幕的左邊或右邊。
這是我們可以使用的用來(lái)計(jì)算失真的最準(zhǔn)確的方法。不過(guò)就GPU的使用而言,這種方法的成本也非常高。由于我們僅處理6個(gè)點(diǎn)(兩個(gè)三角形到輸出一個(gè)矩形),因此頂點(diǎn)著色器運(yùn)行很快。另外,片段著色器需要在屏幕上為每個(gè)像素進(jìn)行轉(zhuǎn)換。所以對(duì)于1920 x 1080像素的顯示,要進(jìn)行2073600次運(yùn)算及紋理查找。
當(dāng)然,我們可以做得更好!每個(gè)程序員都知道,良好性能的關(guān)鍵在于預(yù)計(jì)算和近似值。如果觀察游戲的對(duì)象內(nèi)容,可以看到這些對(duì)象中有很多近似值。像級(jí)聯(lián)陰影貼圖那樣進(jìn)行陰影計(jì)算。不過(guò)在未來(lái)有了光線追蹤,這將得到極大的改善。
看著以上數(shù)字,很明顯我們需要在片段著色器之外進(jìn)行轉(zhuǎn)換。我們可以使用多個(gè)矩形連接組成網(wǎng)格,而不是在顯示FBO時(shí)形成一個(gè)矩形。如果現(xiàn)在對(duì)網(wǎng)格進(jìn)行預(yù)轉(zhuǎn)換,便可以得到以下信息:
這種方法的優(yōu)點(diǎn)是,在初始化時(shí)間以上工作只需要做一次。因?yàn)橥哥R參數(shù)不會(huì)隨時(shí)間變化,因此我們可以反復(fù)利用每一幀上的網(wǎng)格。這是優(yōu)化的第一部分——預(yù)計(jì)算。
第二部分是近似值:在本例中,由網(wǎng)格分辨率對(duì)其進(jìn)行定義。網(wǎng)格點(diǎn)之間可以進(jìn)行插補(bǔ)。網(wǎng)格分辨率越低產(chǎn)生的圖像質(zhì)量則越高,而網(wǎng)格分辨率越高則運(yùn)行速度會(huì)越快。所以使用網(wǎng)格的優(yōu)勢(shì)在哪里呢?使用32 x 32像素的網(wǎng)格可以生成1920 x 1080像素的顯示:
(1920, 1080) / 32 = (60, 33.75)
因此,有2040個(gè)矩形和12240個(gè)點(diǎn)組成了頂點(diǎn)。頂點(diǎn)著色器要處理的是12240個(gè)點(diǎn),而不是6個(gè)。不過(guò),上述相同的頂點(diǎn)著色器也可以使用。可以簡(jiǎn)化片段著色器,如下所示:
#version 300 es
in highp vec2 texFrg;
out highp vec4 frgCol;
uniform highp sampler2D texSampler;
void main(void)
{
highp vec4 col = vec4(0.0, 0.0, 0.0, 1.0); /* base colour */
if (all(greaterThanEqual(texFrg, vec2(0.0))) &&
all(lessThanEqual(texFrg, vec2(1.0))))
{
col = texture(texSampler, texFrg);
}
frgCol = col;
}
這還需要進(jìn)行紋理查找,但可以避免轉(zhuǎn)換工作。我們節(jié)省了大量的功耗,且分辨率更高將節(jié)省更多的功耗。不過(guò),在 PowerVR GPU上,還有另一大優(yōu)勢(shì)。由于頂點(diǎn)著色器在TA階段運(yùn)行 (可以參考PowerVR架構(gòu)一文),它可以在3D階段獨(dú)立運(yùn)行,而3D階段由于要進(jìn)行光柵化,因此要執(zhí)行片段著色器。如果對(duì)象填充率有限,將有更多的空間可以確保按時(shí)完成對(duì)象的渲染。這里,我再次總結(jié)了全高清顯示節(jié)省功耗的示例:
接下來(lái)用一個(gè)視頻來(lái)展示這兩種方法:
可以找出不同之處么?我也找不出不同點(diǎn)。第一個(gè)渲染在片段著色器中進(jìn)行了轉(zhuǎn)換,而第二個(gè)使用了預(yù)計(jì)算的網(wǎng)格。
質(zhì)量評(píng)估
看看以上視頻,似乎近似法行之有效。但是千萬(wàn)不要被我們的雙眼蒙蔽!讓我們嘗試進(jìn)行量化。對(duì)于每一個(gè)近似,最重要(最困難)的部分是找到合適的參數(shù)。在我們的例子中,這個(gè)參數(shù)即網(wǎng)格的大小。我使用的網(wǎng)格大小是32 x 32像素,這個(gè)大小極好地權(quán)衡了輸出質(zhì)量和速度。以下視頻展示的是使用160 x 160像素作為網(wǎng)格大小時(shí)的效果:
當(dāng)立方體停止旋轉(zhuǎn)時(shí),看看其左邊的部分,可以看到它是如何循著網(wǎng)格的軌跡。在矩形邊緣點(diǎn)之間,紋理的查找只是線性插補(bǔ)。另一個(gè)問(wèn)題是動(dòng)畫(huà)效果是搖擺的,因?yàn)樗鼈儾荒茏裱д娴那€。所以,可以確定160 x 160像素不是最佳選擇。但看看靜態(tài)圖像,并將之與片段著色器制中的輸出轉(zhuǎn)換進(jìn)行比較。首先,我為每個(gè)輸出圖像創(chuàng)建了向前轉(zhuǎn)換圖像(在軟件中完成,不借助GPU)。即,僅使用片段著色器時(shí),輸出的網(wǎng)格大小為32px和160px。我還針對(duì)向前轉(zhuǎn)換圖像和原始非轉(zhuǎn)換圖像輸出的不同創(chuàng)建了差值圖像,如下左所示:
圖像質(zhì)量對(duì)比圖
可以看到,在差值圖像中有更多紅色。但可以用數(shù)值進(jìn)行表述。在視頻編解碼器的圖像對(duì)比中,PSNR值(峰值信噪比)是已知的標(biāo)準(zhǔn)。通過(guò)使用ImageMagick,并計(jì)算差值圖像和PSNR,可以得到:
PSNR值更高意味兩個(gè)圖像更接近。可以清楚地看到,使用32px作為圖像的網(wǎng)格參數(shù)與使用片段渲染生成的圖像非常接近。
總結(jié)
透鏡修正是VR流水線一個(gè)至關(guān)重要的部分。通過(guò)使用正確的技巧,我們可以降低這個(gè)特定部分的GPU需求,并同時(shí)保持圖像的高質(zhì)量。這使應(yīng)用程序得以創(chuàng)建更豐富的對(duì)象或設(shè)備,以通過(guò)縮短喚醒GPU的時(shí)間,從而節(jié)省功耗。
評(píng)論
查看更多