(中國(guó)傳媒大學(xué) 北京 100024)
Unity是一款2005年在蘋果的全球開發(fā)者大會(huì)上發(fā)布的游戲引擎,支持跨平臺(tái)的2D/3D的開發(fā),截止到2018年,該引擎已經(jīng)支持了包含Windows、MacOS、任天堂的Switch、安卓、IOS等27個(gè)開發(fā)平臺(tái)。它具備可視化的交互界面,讓開發(fā)者不需要有非常強(qiáng)大的編程基礎(chǔ)就可以使用,采用了層級(jí)化的開發(fā)環(huán)境,提供了非常詳細(xì)的屬性編輯器,并且可以在場(chǎng)景中實(shí)時(shí)動(dòng)態(tài)的編輯和預(yù)覽。它可以根據(jù)項(xiàng)目中的資源自動(dòng)導(dǎo)入,并且支持資源的熱更新,支持了大部分主流的第三方平臺(tái)的建模格式、音頻格式等素材。著色器語言使用了ShaderLab,并且提供了三種類型的著色器供開發(fā)者選擇使用,讓開發(fā)者可以根據(jù)需求靈活的處理。可以采用第三方提供的多人聯(lián)網(wǎng)功能。內(nèi)置了地形編輯器,能夠非常容易的編輯出想要的地形[1]?;谶@些特性,開發(fā)者們可以快速的制作出游戲原型或其他應(yīng)用原型,因而被,被廣泛用于到游戲、工業(yè)、建筑、醫(yī)療、教育等各個(gè)領(lǐng)域中。
但由于Unity引擎中原生的攝像機(jī)組件,在獲取較大視場(chǎng)角的場(chǎng)景圖像時(shí)會(huì)對(duì)圖像產(chǎn)生較大的變形,這是因?yàn)閁nity原生的攝像機(jī)采用了透視投影的方法,視場(chǎng)角較大時(shí)將對(duì)圖像產(chǎn)生較大的變形。本文將應(yīng)用方位等距投影的方式來獲取場(chǎng)景,以消除圖像的變形。
(一)Unity中的頂點(diǎn)變換
頂點(diǎn)變換的整體過程如圖2-1所示。
首先從模型空間中的坐標(biāo)開始,對(duì)于每個(gè)模型而言,它們的坐標(biāo)在建模過程中就已被確定。模型空間到世界空間坐標(biāo)需要使用變換矩陣,通過一定的縮放、旋轉(zhuǎn)、平移操作,將獲得最終的變換矩陣。世界空間將轉(zhuǎn)成觀察空間,需要注意的是Unity中的觀察空間使用的是右手坐標(biāo)系,所以需要將z軸進(jìn)行反轉(zhuǎn)。同樣,也需要一個(gè)變換矩陣進(jìn)行坐標(biāo)的處理。觀察空間到裁剪空間將要使用到投影模型,不同的投影模型將使用不同的變換矩陣。最后將裁剪空間變換到屏幕空間,需要用到齊次除法并獲取到最終的像素位置[2]。
圖 2-1 Unity中頂點(diǎn)變換過程
(二)著色器系統(tǒng)
著色器系統(tǒng)(shader)是一種對(duì)圖像進(jìn)行處理的計(jì)算機(jī)程序,可以進(jìn)行顏色、光照、亮度等相關(guān)屬性進(jìn)行修改。一般著色器是利用圖形處理器(Graphics Processing Unit,縮寫為GPU)來進(jìn)行處理[2]。Unity游戲引擎中包含三類著色器。
第一類是固定功能著色器(Fixed Function Shader),主要用于低版本顯卡的兼容。第二類是表面著色器(Surface Shader),由Unity預(yù)制了很多光照模型,供開發(fā)者使用。第三類是頂點(diǎn)著色器和片段著色器(Vertex and Fragment Shader)頂點(diǎn)著色器(Vertex Shader)是處理三角面片的頂點(diǎn),為最終渲染像素做鋪墊的程序
片段著色器(Fragment Shader)又叫像素著色器(Pixel Shader),通過計(jì)算”片段”(單獨(dú)的像素)的光照、亮度等屬性。片段著色器的輸出可以只有顏色,也可以附加一些例如高光、透明效果等。由于片段著色器可以獲取屏幕的坐標(biāo),因而可以將紋理作為輸入,生成更高級(jí)的后期特效[3]。
(三)投影
投影是對(duì)平面中的點(diǎn)和三維立體空間中的點(diǎn)進(jìn)行相互轉(zhuǎn)化的一種映射,通過利用數(shù)學(xué)方法來將其進(jìn)行精確的轉(zhuǎn)換。如果投影的表面是一個(gè)不能夠完全戰(zhàn)平的曲面,那么轉(zhuǎn)換過程將帶來變形進(jìn)而造成誤差。但可以根據(jù)使用需求,去縮小相應(yīng)的誤差,選擇使用不同的投影模型來進(jìn)行不同的數(shù)學(xué)方法,可以得到不同的最終結(jié)果。
由于分類標(biāo)準(zhǔn)的差異,投影模型分為不同的類別:
1.通過構(gòu)成方法,投影模型包含了(1)幾何投影。幾何投影包含了三種投影模型,分別分為方位投影、圓柱投影、圓錐投影等。(2)非幾何投影。
2.通過變形性質(zhì),投影模型包含了(1)等積投影;(2)等角投影;(3)任意投影
幾何投影模型:幾何投影是將立體空間投影到輔助的投影平面上,再通過展開來變成平面的一種投影方法。幾何投影方法的特點(diǎn)在于,其將立體空間投影到圓錐或者圓柱上,通過將其展開,可以得到投影平面。根據(jù)不同的幾何模型,分為了不同的投影模型,比如圓柱、圓錐、方位等投影。
方位投影模型:方位投影在投影屏幕上由投影中心向各個(gè)方向的方位角與真實(shí)空間中的相等??梢苑譃閮纱箢?,分別為透視方位投影和非透視方位投影。透視方位投影由于視點(diǎn)位置的不同,會(huì)得到不同的投影,比如正射、球心、球面等投影。而非透視方位投影可以分為三大類,是根據(jù)變形性質(zhì)的不同,有等積、等角、等距投影等。方位投影對(duì)于圓形區(qū)域的平面,更具優(yōu)勢(shì)。
方位等距投影由德國(guó)制圖家麥卡托提出,通過將球體與投影的平面相切,等距離的將經(jīng)緯線投影在平面上。方位等距投影最顯著的特點(diǎn)是在投影平面上到任意一點(diǎn)的距離都是真實(shí)的,所以在投影平面中的圓與三維空間中的真實(shí)地點(diǎn)是等距離的。不僅如此,從中心出發(fā)的方向也是與三維空間相同的[3]。
本文將使用的投影就是方位等距投影模型。
本設(shè)計(jì)采用結(jié)合方位等距投影模型,使用Unity中的腳本和著色器來構(gòu)建一個(gè)大視場(chǎng)相機(jī)。場(chǎng)景的獲取主要由五個(gè)C#腳本和兩個(gè)著色器腳本完成,分別為CameraProjector、CPGrabber、CPMasks、CPPartial、CPSetup、CPUtility、ProjecterIncludes、ProjecterShaderCubic。最終通過將CameraProjector腳本掛靠到攝像機(jī)上進(jìn)行調(diào)用。
程序?qū)崿F(xiàn)的主函數(shù)梳理如下:
1.首先調(diào)用Reset函數(shù)中將首先判斷當(dāng)前場(chǎng)景面向是左眼、右眼或者非立體環(huán)境。之后也會(huì)根據(jù)相應(yīng)的情況,調(diào)用類CPSetup中的Setup函數(shù)完成對(duì)于相應(yīng)相機(jī)的初始位置。
2.然后對(duì)當(dāng)前環(huán)境進(jìn)行檢測(cè),查看是否支持RenderToCubemap這個(gè)API,如果支持則進(jìn)行下一步,并調(diào)用reset函數(shù),防止腳本并非第一次調(diào)用而未進(jìn)行初始化;如果不支持否則發(fā)出提示該設(shè)備并不支持。
3.然后調(diào)用OnPreCull函數(shù),該函數(shù)在相機(jī)剔除場(chǎng)景之前會(huì)被觸發(fā)。相機(jī)可見的對(duì)象取決于剔除。首先調(diào)用了GrabTextures函數(shù),然后利用一些變量保存了攝像機(jī)的cullingMask;、clearFlags、depthTextureMode等值,并對(duì)當(dāng)前這些值進(jìn)行清空,為的是在對(duì)圖像處理完之后的還原。
4.其中GrabTextures函數(shù)將根據(jù)賦予腳本的參數(shù)計(jì)算出后面將要調(diào)用的RenderToCubemap的參數(shù)中的mask,mask是一個(gè)二進(jìn)制數(shù)字,用于確定渲染六個(gè)面中的哪些面。并根據(jù)當(dāng)前是否開啟了VR模式,對(duì)左右眼分別調(diào)用了類Grabber中的Render函數(shù)。如果計(jì)算過程中出現(xiàn)錯(cuò)誤信息,則將調(diào)用reset函數(shù)將設(shè)置還原。
5.Render函數(shù)將首先調(diào)用類CameraProjector中的ValidateRenderTexture函數(shù)對(duì)紋理進(jìn)行修改,然后調(diào)用RenderToCubemap函數(shù)將,攝像機(jī)渲染的圖像渲染到該紋理上。
6.ValidateRenderTexture函數(shù)將設(shè)定紋理的類型設(shè)置、分辨率、紋理的過濾模式、抗鋸齒模式,然后創(chuàng)建一個(gè)新的紋理。
7.RenderToCubemap函數(shù)將利用之前 GrabTextures函數(shù)計(jì)算出的mask值來創(chuàng)建一份相應(yīng)場(chǎng)景的立方體紋理。
8.至此OnPreull函數(shù)調(diào)用完畢。開始調(diào)用OnRenderImage函數(shù),首先判斷是否設(shè)置了背景,然后根據(jù)是否開啟了VR模式,而對(duì)類CPGrabber中的Blit函數(shù)進(jìn)行調(diào)用。
9.在Blit函數(shù)中,首先調(diào)用類CameraProjector中的ValidateMaterial函數(shù),然后調(diào)用Graphics.Blit。
10.ValidateMaterial函數(shù)將新建一個(gè)材質(zhì),并由類CPUtility中的shaderblitShaderCubic來進(jìn)行處理,并對(duì)其調(diào)用類CPUtility中的函數(shù)對(duì)材質(zhì)進(jìn)行屬性的修改。
11.shaderblitShaderCubic是CameraProjecterShaderCubic文件中定義的shader。該shader在應(yīng)用了方位等距投影模型。
12.最后回到Blit函數(shù)中,調(diào)用Graphics.Blit,以之前生成的紋理和處理過的材質(zhì)為參數(shù),將最終的圖像渲染到屏幕上。
其中RenderToCubemap函數(shù)是Unity中一個(gè)自帶的內(nèi)部函數(shù),該函數(shù)首先獲取到當(dāng)前攝像機(jī)拍攝的畫面,并把畫面渲染到指定的貼圖上。該函數(shù)可以傳入兩個(gè)參數(shù),分別為渲染目標(biāo)的貼圖和渲染的面數(shù)。
核心步驟的偽代碼如下:
新建貼圖紋理:
Texture newTex;
利用RenderToCubemap函數(shù)獲取到相機(jī)位置的貼圖:
RenderToCubemap(newTex)
應(yīng)用方位等距投影:
half3 mapEquidistant(half2 uv)
{
half c=length(uv);
return half3(uv.x*sin(c),-uv.y*sin(c),c*cos(c));
};
(一)使用過程與實(shí)現(xiàn)效果
將該設(shè)計(jì)賦予到場(chǎng)景中的主攝像機(jī)上,并通過設(shè)置不同的視場(chǎng)角來獲取不同的圖像。將設(shè)計(jì)設(shè)置完畢之后,通過將攝像機(jī)置于一個(gè)包含六個(gè)相同面的盒子中,進(jìn)行最終效果的對(duì)比,如圖4-1所示,是使用了Unity原生的攝像機(jī)獲取的場(chǎng)景圖像。圖4-2是使用了本設(shè)計(jì),獲取到了更大視場(chǎng)的場(chǎng)景圖像。
圖 4-1 Unity原攝像機(jī)
圖 4-2 使用了本設(shè)計(jì)的攝像機(jī)
(二)性能分析
通過衡量該設(shè)計(jì)在場(chǎng)景中每秒的幀率,來評(píng)估該設(shè)計(jì)的運(yùn)行效率。實(shí)驗(yàn)的測(cè)試環(huán)境的硬件信息為:處理器是I7-6700HQ@2.60GHz,顯示適配器是NVIDIA GeForce GTX 950M。分別將設(shè)計(jì)放于含有七十萬面的場(chǎng)景、六十萬面的場(chǎng)景、五十萬面的場(chǎng)景、四十萬面的場(chǎng)景、三十萬面的場(chǎng)景、二十萬面的場(chǎng)景、十萬面的場(chǎng)景、一千面的場(chǎng)景。測(cè)試結(jié)果如表4.1和圖4-4所示。
表4.1 不同面數(shù)中的幀率的對(duì)比
圖 4-4 幀率對(duì)比圖
可以發(fā)現(xiàn),該設(shè)計(jì)在不同面數(shù)的場(chǎng)景中的幀率,雖然低于沒有使用該設(shè)計(jì)的場(chǎng)景,但幀率降低的較小,并且屬于在人眼較為舒適的范圍內(nèi),因而可以接受。
本設(shè)計(jì)是結(jié)合了方位等距投影模型實(shí)現(xiàn)的一個(gè)Unity的插件,使用了腳本技術(shù)和著色器技術(shù)來構(gòu)建可以獲得更大視場(chǎng)角的攝像機(jī)。然而由于需要對(duì)于最終渲染結(jié)果進(jìn)行處理,所以需要進(jìn)行較為繁瑣的計(jì)算,性能上會(huì)有一定的開銷,而本文未對(duì)性能方面加以優(yōu)化,未來可以在性能的優(yōu)化上進(jìn)行進(jìn)一步的探索。