關於Unity中Camera的Aspect


  一直以來對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.選擇指定分辨率(XXXX:YYYY):

    第二鍾情況的另一種表現方式類似,Camera的Aspect也會被設置成XXXX:YYYY,同樣無論你怎么改變Game窗口大小,Camera的Aspect都是不會變的。Scene中視椎體也不會變。

  很多人也許就奇怪了為什么我無論怎么去改變Game窗口的Aspect選項,我在屏幕上看到的渲染后的畫面並沒有發生太大變化(比如被拉伸或者縮小).這就是因為Game的Aspect一直和Camera的Aspect保持一直所導致的。這其中涉及到投影變換,透視除法以及最終投影到屏幕空間。說起來很亂,我也怕自己說錯。大家如果發現不對的地方歡迎指正,首先為了裁剪的方便,最終需要把物體的坐標從相機空間轉換到一個長方體裁剪空間,這一步是通過投影變換來完成的,一般會引擎會在這一步之后進行空間裁剪(也有的會選擇在透視除法之后進行裁剪)。然后再經過透視除法,對坐標進行歸一,變換到所謂的規范化設備空間,坐標的大小被限定在一個[-1~1]的空間內,可以看成一個長方體被壓縮成了一個正方體(也可能不是正方體,DX和OpenGL是不同的)。想要詳細了解的同學不妨參看Twinsen前輩blog中相關文章。

  那我們就可以想象,加入我在攝像機的視椎體內放置了了一個寬高比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的一些見解。由於本人數學功底不高,很多理論性的東西無法講的很透徹,甚至可能理解錯誤。所以希望大家只做參考。如果發現我哪里寫的有問題,請務必指出。

 

   尊重他人智慧成果,歡迎轉載,請注明作者esfog,原文地址 http://www.cnblogs.com/Esfog/p/4172896.html 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM