一直以來對camera的aspect和game視窗的aspect都是一知半解,某天從一本書中看到了對camera的api講解,但是總覺得對aspect講解的有問題。於是就認真的思考起了這個問題,還發現設定完cmera.aspect之後,scene視窗的視椎體竟然不同步, 也不知其原因。苦惱了很久。經過一番研究並與同事討論有所收穫,便寫下此文。一方面為了強化自己的理解,一方面也為了分享給更多人。
言歸正傳,大家都知道我們在場景中放置的物體最終渲染到螢幕上都是離不開我們的攝像機。對於透視攝像機(perspective projection)來說,在unity中你會在scene場景中看到乙個白色邊框的錐形體,也就是常說的視椎體,在整個渲染流程中這個視椎體是非常重要的,它會參與到透視投影矩陣的計算以及裁剪等處。
如下圖:
視椎體有4個非常重要的引數,乙個是field of view,他是表示的相機在y方向上上頂面和下底面的夾角的一半;另外乙個是相機的aspect,他代表的是相機視椎的寬高比.在最終的投影矩陣中,還有兩個是近平面和遠平面。這4個引數是很重要的。
unity中的攝像機有個camera元件,上面是直接可以調節fieldofview,單位是度。但你在上面是找不到aspect屬性的。可能有人會去拖動scene中相機遠平面上的四個白點去改變視椎體的形狀,你可以觀察一下camera元件的變化,你發現變化的啟示是fieldofview,無論你去調整寬還是高,unity都會按照aspect去相應的調整高或者寬。以保證你的調整不會影響相機的aspect.
那麼相機的aspect是怎麼設定的呢?你當然可以再**裡用camera.aspect = x這種方式去改變他的值.但是為什麼我們每次即使沒有去自己寫**為aspect賦值也沒感覺有什麼不便呢?那是因為unity會根據game視窗的設定去自動設定它。(如下圖)
可以看到game視窗也有個aspect的東西,但是這個aspect可並不是指相機的aspect,而是指最終遊戲螢幕的寬高比.可以看到大體上這些可選的設定中可以分為三大類,一類是free aspect,一類是給出寬高比,另一類是指定解析度.分別說明一下。
1.選擇free aspect時候(預設選項):
螢幕的寬高比實際上就是你game視窗的寬高比,你可以手動去拖動來調節他,而這時候unity也會實時的去修改場景中對應攝像機的aspect值,使它和game視窗的aspect保持一致。你可以一遍改變game視窗大小,一邊看scene視窗中相機視椎體的變化。
2.選擇x:y的時候:
螢幕的寬高比被固定在x:y這個大小上,而camera的aspect也會被設定成x:y,這時候無論你怎麼去改變game視窗的大小,camera的aspect都是不會改變的.scene中視椎體不會變。
3.選擇指定解析度(***x:yyyy):
第二鐘情況的另一種表現方式類似,camera的aspect也會被設定成***x:yyyy,同樣無論你怎麼改變game視窗大小,camera的aspect都是不會變的。scene中視椎體也不會變。
很多人也許就奇怪了為什麼我無論怎麼去改變game視窗的aspect選項,我在螢幕上看到的渲染後的畫面並沒有發生太大變化(比如被拉伸或者縮小).這就是因為game的aspect一直和camera的aspect保持一直所導致的。這其中涉及到投影變換,透視除法以及最終投影到螢幕空間。說起來很亂,我也怕自己說錯。大家如果發現不對的地方歡迎指正,首先為了裁剪的方便,最終需要把物體的座標從相機空間轉換到乙個長方體裁剪空間,這一步是通過投影變換來完成的,一般會引擎會在這一步之後進行空間裁剪(也有的會選擇在透視除法之後進行裁剪)。然後再經過透視除法,對座標進行歸一,變換到所謂的規範化裝置空間,座標的大小被限定在乙個[-1~1]的空間內,可以看成乙個長方體被壓縮成了乙個正方體(也可能不是正方體,dx和opengl是不同的)。想要詳細了解的同學不妨參看twinsen前輩
那我們就可以想象,加入我在攝像機的視椎體內放置了了乙個寬高比2:1的平面(如下圖).在透視變換後的裁剪空間中我們看到的應該依然還是乙個(2:1)的平面,但是經過透視除法的規範化之後,大家想象一下視椎體從乙個2:1(相機的aspect)的長方體被壓縮成(1:1)正方體的過程.在規範化裝置空間中我們的長方形平面應該是被壓成了1:1的正方形平面了(實際上不是圖形在變,只是頂點的座標變了而已)。如果直接把這樣的結果投影到平面顯然不是我們想要的結果。這時候就需要game視窗的aspect出馬了。
在透視除法把座標歸一化之後,還要在規範化裝置空間上進行一次視口變換才把物體真正的對映到螢幕空間,而這一步中實際上主要是解決之前投影變換和透視除法造成的失真(就像我們的長方行被壓扁了),具體做法就是把規範化後的x,y座標按照螢幕的aspect來進行一次調節,那麼如果螢幕的aspect和攝像機的aspect保持一致也是2:1,我們被壓扁的長方形就又被拉回到了原來的2:1了。最後再經過光柵化處理,也就得到了我們在螢幕中最終看到的結果了。
所以說之所以螢幕的aspect和camera的aspect始終保持一致,就是為了保證透視投影的正確。那麼如果我們強行讓兩者不一致的話,那麼在透視變換和透視除法造成的影象失真就無法得到修正,也就會使我們最終渲染的螢幕的影象是錯的。比如說你在camera上掛個指令碼在它的start方法中寫上camera.aspect = 2(也就是2:1).而設定game視口的aspect是1:1那麼你會看到螢幕上是個正方形.如果設定game視口的aspect是1:2,你會發現原來螢幕上的長方形寬高比從原來的2:1變成了1:2了。這正驗證了我們剛才所說的。
我在做這個例子的時候還發現了乙個問題,就是說你在**裡去修改相機的aspect之後,你會發現scene視窗裡的相機視椎體並不會同步到你設定的值.而且如果這時候去修改game視窗的aspect,scene中的視椎體竟然會跟著變化,但是你在camera剛才的指令碼裡update輸出camera.aspect發現還是我們剛才設定的值。並且從渲染效果上觀察也確實使用的是我們設定的值。所以我覺得一旦你在**裡設定了camera.aspect之後,scene視窗中的相機視椎體不會同步到新值,而且也失去了參考價值。不知道這是不是unity的乙個bug.或者也許這個白色椎體並不是代表的視椎體而是有著其它的含義?如果有知道的同學,請一定告訴我。
在最後還有兩個地方需要提及:
1.當我們把攝像機的內容渲染到的是rendertexture上而不是螢幕上時,那麼相機的aspect缺省會設定成和rendertexture的解析度一樣.不過最終如果把rendertexture作為貼圖貼到模型上去的時候還是會被由於被uv拉伸和縮小的。
2.對於camera元件的的viewport rect屬性也就是所謂的視口,他會影響實際上最終渲染的螢幕視窗大小,最終渲染視窗的aspect實際上是由game視窗的aspect和viewport rect的aspect相乘得到的結果。這點要注意。
以上就是我對camera.aspect的一些見解。
由於本人數學功底不高,很多理論性的東西無法講的很透徹,甚至可能理解錯誤。所以希望大家只做參考。如果發現我**寫的有問題,請務必指出。
關於如何使用Unity的Camera(1)
unity的camera是非常重要的一部分,對於一款遊戲來說,攝像機就像是玩家的眼睛,攝像機中有什麼,玩家都能看到什麼。而攝像機控制著讓什麼樣的東西進入玩家的視野,可以使用不同的攝像機渲染不同的東西。舉個例子,在遊戲過程中,有一些對話方塊,和遊戲中的人物場景什麼的,對話方塊和這些人物場景就可以用不同...
關於如何使用Unity的Camera(2)
今天開始使用2個不同的camera,最後乙個個屬性對照文件看了一下,發現渲染是有先後之分的,需要使ui最後渲染。這樣保證在最上層。而渲染的順序是由攝像機的深度來決定的,攝像機的深度越小,就越先渲染。關於tk2duicamera的一點小心得 tk2duicamera,老版本的tk2dcamera將會由...
關於如何使用Unity的Camera(3)
camera 代表了乙個手機的螢幕,知道了手機螢幕的大小就可以設定遊戲的ui布局來適應不同的螢幕,其實這就是手機應用很重要的乙個不份 螢幕適配。我在使用2dtools時候螢幕的適配可以借用 tk2dcameraanchor來做,使用這個指令碼,可以使控制項根據攝像機的大小來設定位置,可選的位置有9個...