关于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
关于Unity中Camera的Aspect的更多相关文章
- unity中camera摄像头控制详解
目录 1. 缘起 2. 开发 2.1. 建立项目 2.2. 旋转 2.2.1. 四元数 2.3. 移动 2.3.1. 向量操作 2.4. 镜头拉伸 2.5. 复位 2.6. 优化 1 缘起 我们的产品 ...
- unity中Camera.ScreenToWorldPoint
Camera.ScreenToWorldPointVector3 ScreenToWorldPoint(Vector3 position); 将屏幕坐标转换为世界坐标. 如何转换?假如给定一个所谓的屏 ...
- 介绍Unity中相机的投影矩阵与剪切图像、投影概念
这篇作为上一篇的补充介绍,主要讲Unity里面的投影矩阵的问题: 上篇的链接写给VR手游开发小白的教程:(三)UnityVR插件CardboardSDKForUnity解析(二) 关于Unity中的C ...
- 移植UE4的模型操作到Unity中
最近在Unity上要写一个东东,功能差不多就是在Unity编辑器上的旋转,移动这些,在手机上也能比较容易操作最好,原来用Axiom3D写过一个类似的,有许多位置并不好用,刚好在研究UE4的源码,在模型 ...
- Unity中Mesh分解与边缘高亮加上深度检测
一个比较简单的需求,不过遇到些坑,记录下. 房间有多个模型,每个模型可能多个SubMesh,点击后,需要能具体到是那个SubMesh,并且在这个SubMesh上显示边缘高光,以及能个性这单个SubMe ...
- Unity中制作游戏的快照游戏支持玩家拍快照
Unity中制作游戏的快照游戏支持玩家拍快照 有些游戏支持玩家“拍快照”,也就是将游戏的精彩瞬间以图片的形式记录下来的功能.这个功能比较有趣,而且以后的用途也会很广,为此本节打算介绍:截取矩形区域内游 ...
- 3D语音天气球(源码分享)——在Unity中使用Android语音服务
转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持! 开篇废话: 这个项目准备分四部分介绍: 一:创建可旋转的"3D球":3 ...
- 在Unity中高效工作(下)
原地址:http://www.unity蛮牛.com/thread-20005-1-1.html Tips for Creating Better Games and Working More Eff ...
- Unity中的屏幕坐标:ComputeScreenPos/VPOS/WPOS
[Unity中的屏幕坐标:ComputeScreenPos/VPOS/WPOS] 1.通过 VPOS / WPOS 语义获取. VPOS 是 HLSL 中 对 屏幕 坐标 的 语义, 而 WPOS 是 ...
随机推荐
- ASP.NET使用Razor语法无法正确识别.cshtml文件
ASP.NET使用WebPage编程的好处之一是可以使用强大的Razor语法, 但初次使用Razor语法会碰到一个比较头疼的问题就是无法直接写一个.cshtml让浏览器去识别,查资料也没有找到相关问题 ...
- JavaScript本地对象 内置对象 宿主对象
在ECMAScript中,所有对象并非同等创建的. 一般来说,可以创建并使用的对象有3种:本地对象.内置对象和宿主对象. 1. 本地对象 ECMA-262把本地对象(native obje ...
- HTTP Referer 防外链
HTTP Referer是header的一部分,当浏览器向web服务器发送请求的时候,一般会带上Referer,告诉服务器我是从哪个页面链接过来的,服务器籍此可以获得一些信息用于处理. if (Req ...
- android通知-Notification
android中,当app需要向发送一些通知,让使用者注意到你想要告知的信息时,可以用Notification.下面,就来讨论一下,Notification的用法,我们从实际的小例子来进行学习. 1. ...
- asp.net导出word(word2007)
1.只能导出成word2007格式(.docx),可直接导出到客户端 2.服务器上不需要装任何东西,也没有权限限制,比较适合导出表格(支持图片) 3.需要一个国外的DocX.dll插件 4.需要添加引 ...
- web前端入门:一小时学会写页面
一小时学会写页面 作为一个懒癌晚期患者,总是习惯找各种简单的解决问题的方法,也习惯性把问题简单化,所以今天想分享给大家简单的web前端入门方法.既然题目已经定了一个小时那么废话就不多说了,计时开始 1 ...
- RABBITMQ/JAVA 客户端测试(再补:利用文件流)
(一)这里可以先复习一下java输入输出流和文件操作--- 1.File类保存文件或目录的各种元数据信息,包括文件名.文件长度.最后修改时间.是否可读.获取当前文件的路径名.判断指定文件是否存在.获取 ...
- 国产AR SDK介绍
说到VR,大家都知道虚拟现实有多火.可是VR之后呢,还有AR.相较于VR,AR的应用意义更加的强大. 相信在不久的将来AR和VR将会融为一体,把现实世界的数据信息完全联通在我们的眼前.这其中的领头羊莫 ...
- 用meta-data配置参数
在接入第三方渠道SDK的时候,经常会看到其配置文件AndroidManifest.xml有类似如下的定义: <!-- appid --> <meta-data android:nam ...
- 给JavaScript初学者的24条最佳实践(share)
不错的文章,留个备份 原文链接: net.tutsplus 翻译: 伯乐在线- yanhaijing译文链接: http://blog.jobbole.com/53199/ 作为“30 HTML和 ...