这篇作为上一篇的补充介绍,主要讲Unity里面的投影矩阵的问题:

上篇的链接写给VR手游开发小白的教程:(三)UnityVR插件CardboardSDKForUnity解析(二)

关于Unity中的Camera,圣典里面对每一项属性都做了简要的介绍,没看过的小伙伴传送门在下面

http://www.ceeger.com/Components/class-Camera.html

一、裁剪面

先从这个专业的词汇开始,以下是圣典对裁剪面的介绍:

The Near and Far Clip Plane properties determine where the Camera's view begins and ends. The planes are laid out perpendicular to the Camera's direction and are measured from the its position. The Near plane is the closest location that will be rendered, and the Far plane is the furthest.

近裁剪面及远裁剪面属性确定相机视野的开始和结束。平面是布置在与相机的方向垂直的位置,并从它的位置测量。近裁剪面是最近的渲染位置,远平面是最远的渲染位置。

这句话感觉说了和没说差不多,因为我们没有看到过裁剪面,所以了解裁剪面的第一步,我们需要在Unity当中去直观的看看它

下图,当我们近距离观察Camera的时候,会发现一个用白线画的金字塔(四棱锥),这很好理解,他表示了Camera的视野范围,奇怪的是这个金字塔(四棱锥)少了一个角,从而金字塔不仅有了底面,还有一个顶面。

相信猜也能猜到了,金字塔的顶部这个面,是近裁剪面(near clip planes),底面,则是远裁剪面(near clip planes)

那么说了半天,裁剪面有什么用呢?

我们继续在Unity的摄像机中改变Clipping Planes的值,看看变化,首先把Clipping Planes的near和far分别调为2和6

如下图预览界面,在近裁剪面和远裁剪面之间没有包含物体,渲染的图像里是不会有物体的

增加far的值,现在立方体的很小一块包含进去了,但是我们看预览界面,并看不出它是立方体,只能看出平面效果

好的,继续增加,现在整个立方体都包含进来了,看预览,终于可以明显看出是正方体了

这个例子说明了Camera似乎只渲染近裁剪面与远裁剪面之间的物体,这个原理就好比平面图的渲染,我们需要对图片进行裁剪完成后,程序才知道要渲染的范围,无论这张图是全部需要还是只需要一部分,第一步,都是裁剪。现在,3D的渲染也需要裁剪,于是近裁剪面与远裁剪面就诞生了,只不过裁剪的范围并不是平面的,而是立体的(被切掉顶端的金字塔),这个立体的形状,我们称之为视裁剪体。

二、视裁剪体

视裁剪体,专业的叫法是视锥体(fusum),它由6个面构成,上下,左右,前后,先看圣典里面的介绍:

http://www.ceeger.com/Manual/UnderstandingFrustum.html

The outer edges of the image are defined by the diverging lines that correspond to the corners of the image. If those lines were traced backwards towards the camera, they would all eventually converge at a single point. In Unity, this point is located exactly at the camera's transform position and is known as the centre of perspective. The angle subtended by the lines converging from the top and bottom centres of the screen at the centre of perspective is called the field of view (often abbreviated to FOV).

在影像的边缘被称为对应影像角落的偏离线。如果被描绘的那些线向相机的后方转,他们最终将汇聚在一个点上。在Unity, 这个点恰好位于被称为视图中心的变换位置上。在视图中,屏幕中顶部的中心和底部的中心汇聚的线的夹角,被称为视野(通常缩写成FOV)。

As stated above, anything that falls outside the diverging lines at the edges of the image will not be visible to the camera, but there are also two other restrictions on what it will render. The near and far clipping planes are parallel to the camera's XY plane and each set at a certain distance along its centre line. Anything closer to the camera than the near clipping plane and anything farther away than the far clipping plane will not be rendered.

如上所述, 任何超出影像边缘的偏离先之外的东西都是看不见的。渲染还有另外两个限制条件。近裁剪面和远裁剪面是与相机的XY平面平行的,并且每个裁剪面离中心线有一定的距离。任何在近裁剪面的之内和超出远裁剪面之外的物体都不会被渲染。

我们在上面已经了解了远近裁剪面,即前后,那么上下左右四个面又是怎么定义的呢?上面的介绍已经涉及到了,就是FOV的概念。

继续回到Unity当中,看下FOV的具体效果,修改Field of View的值,30,视锥体收缩,正方体不再内部

FOV修改为60,明显感觉视锥体扩张,预览又能看到正方体了

从上面看FOV的值似乎决定了上下左右四个面的夹角,而且其大小是用度来表示的,这里的60即表示60度

好了,现在一个视锥体的所有参数都已经明确了,已知Camera的坐标,只要知道远近裁剪面的值,FOV的值即可定义一个唯一的视锥体

说了半天,视锥体要怎么使用?ok,接下来开始正题,投影变换。

三、投影变换

Unity中Camera的投影变换分为两种:透视投影和正交投影。

简要说明两者的区别,正交投影的观察体是长方体,是规则的,它使用一组平行投影将三维对象投影到投影平面上去,相信对Unity了解比较深入的同学都知道正交投影的功能,距离Camera的远近并不会影响物体的缩放,比如说距离10m和1000m的实际大小相同的物体,呈现在画面里的大小也是相同的,这显然是我们不希望的,3D游戏模拟的是现实生活,而在现实生活当中,离我们远的物体,看起来当然比较小,而即使是一部手机,放在眼睛前方的时候,看起来,却会硕大无比。于是正交投影在3D游戏当中的使用就非常有限了。

接下来是透视投影,这是3D游戏中广为使用的一种技术,它使用一组由投影中心产生的放射投影线,将三维对象投影到投影平面上去。透视投影的观察体就是以上一直在说的视锥体。它具有通过物体远近来缩放的能力,现在,需要把视锥体包含的物体投影成画面,这个过程,需要做的变换,就是投影变换

那么为什么要变换呢?

视锥体实际上不是一个规则体,这对于我们做裁剪很不利,从3D物体到画面的过程,通常需要经历以下三步:

1. 用透视变换矩阵把顶点从视锥体中变换到裁剪空间的规则观察体(CVV)中
2. 使用CVV进行裁剪
3. 屏幕映射:将经过前述过程得到的坐标映射到屏幕坐标系上。

这个过程,可以用一张图来表示(图摘自它处)

从视锥体变换到立方体的过程中,近裁剪面被放大变成了立方体的前表面,远裁剪面被缩小变成了立方体的后表面,这就解释了为什么透视投影可以将物体的远近很直观表达出来的原因,很简单,因为它放大了近处的物体,缩小了远处的物体。

那么怎么做这个变换呢,我们可以理解为视锥体中某一个点(x,y,z,1)与某一个矩阵相乘得到的新点(x1,y1,z1,1)即为对应CVV中的点,这样把视锥体中所有的点与该矩阵相乘,获得的就是一个CVV。而这个矩阵,就是透视投影矩阵。

直接亮出这个矩阵的值,想看详细推导的同学,给个链接:

http://blog.csdn.net/popy007/article/details/1797121

这里有很多参数的意义用下图来表示

对于一个视锥体,我们取它的截面一般有如下两种方法,不过一般都取yz面作为截面来计算参数,这里我们要取FOV,near,far,botton,top,right,left的值,其中botton,top,right,left是投影平面的上下左右边界值,投影平面,就是近裁剪面。

四、修改投影矩阵建立一个非标准投影

我们继续回归到Unity当中,Unity关于Camera投影矩阵的文档相当相当的少,唯一可用的就是Camera.projectionMatrix的API里面零星的介绍,链接:

http://www.ceeger.com/Script/Camera/Camera.projectionMatrix.html

但至少我们是可以输出投影矩阵看一下的

print(Camera.main.projectionMatrix); //这句话输出主摄像机的投影矩阵

上图看到了投影矩阵的值,FOV=60,near=0.3,far=1000的情况下,进行计算,发现除了第一个值有问题其他都正确。

第一个值为什么是1.08878?

经过研究,我发现Unity有一个特性,无论怎么修改窗口的比例,m【1,1】的值总是不变,固定为1.73205但是,只要改变FOV,它就会改变,所以Unity一定是把FOV定义为投影平面的上边缘与下边缘的夹角,即top=near*tan(FOV/2),而right就不能通过right=near*tan(FOV/2)来计算了,而是要用right=top*aspect这条公式,我们调节屏幕尺寸的时候,实际上改变了m【0,0】的结果而不会改变其他值。

我们写一个脚本去改变投影矩阵的值,看看效果

using UnityEngine;
using System.Collections;

public class example : MonoBehaviour {
public Matrix4x4 originalProjection;
void Update() {
//改变原始矩阵的某些值
Matrix4x4 p = originalProjection;
Camera.main.projectionMatrix = p;
}
public void Awake() {
originalProjection = Camera.main.projectionMatrix;
print(Camera.main.projectionMatrix);
}
}

这里取E01=0.5,发现远近裁剪面变成平行四变形,相应的画面也斜了

其他的我就不演示了,改变其他的值会得到相应的效果

原因很简单,还是要贴出之前推导出来的公式

M矩阵的m01我们把他从0改到了0.5,影响的是x坐标变换的结果,本来x坐标是与y无关的,现在随着y的增加,x也会增加

如下图,相当于本来正方形中的每一个像素与y都无关,现在每一个像素在y不为0的时候都会向右平移0.5y的距离,这样,就导致看起来像平行四边形了

其他的就不一一推导了,反正VR在做投影的时候会涉及到这一块,这样以后涉及到投影矩阵的时候大家就不会那么迷茫了

最后贴一串代码,是在圣典上发现的,实现画面像水一像波动的特效,也是通过修改投影矩阵的方式实现的

复制黏贴后,加在主摄像机上就可以实现了,这么强大的特效居然几行代码就可以搞定,实在觉得不可思议。

using UnityEngine;
using System.Collections;

//让相机以流行的方式晃动
public class example : MonoBehaviour {
public Matrix4x4 originalProjection;
void Update() {
//改变原始矩阵的某些值
Matrix4x4 p = originalProjection;
p.m01 += Mathf.Sin(Time.time * 1.2F) * 0.1F;
p.m10 += Mathf.Sin(Time.time * 1.5F) * 0.1F;
Camera.main.projectionMatrix = p;
}
public void Awake() {
originalProjection = Camera.main.projectionMatrix;
}
}

后面会继续回归VR的主题,继续去详细解释脚本里面的东西。

介绍Unity中相机的投影矩阵与剪切图像、投影概念的更多相关文章

  1. 【转】UNITY中相机空间,投影空间的正向问题

    原文链接1:https://www.cnblogs.com/wantnon/p/4570188.html 原文链接2:https://www.cnblogs.com/hefee/p/3820610.h ...

  2. Unity中几种简单的相机跟随

    #unity中相机追随 固定相机跟随,这种相机有一个参考对象,它会保持与该参考对象固定的位置,跟随改参考对象发生移动 using UnityEngine; using System.Collectio ...

  3. OpenGL中投影矩阵基础知识

    投影矩阵元素Projection Matrix 投影矩阵构建: 当f趋向于正无穷时: 一个重要的事实是,当f趋于正无穷时,在剪裁空间中点的z坐标跟w坐标相等.计算方法如下: 经过透视除法后,z坐标变为 ...

  4. Unity 中的坐标系

    说明: 注意几点: 0 行向量右乘矩阵与列向量左乘矩阵,两个矩阵互为逆矩阵 1 法线转换与mul,mul函数左乘矩阵当列矩阵计算,右乘当行矩阵计算 2 叉乘与左右手系,左手系用左手,右手系用右手,ax ...

  5. Unity编程标准导引-Unity中的基本概念-2.1界面概览

    Unity中的基本概念 本文我们介绍Unity中的基本概念,包括:场景.游戏对象.组件.预制件.资源等. 2.1.界面概览 打开Unity之后,我们大概可以看到以上画面,以上画面中即显示了我们最常用到 ...

  6. 如何在Unity中创造真实的水

    你将要创造什么 Unity是由Unity Technologies开发的多平台游戏引擎,用于为控制台,移动设备,计算机甚至网站等多种设备创建视频游戏和应用程序.Unity的核心优势在于其稳健性,可移植 ...

  7. 3D中的相机 - 投影矩阵和视图矩阵

    3D中的相机 - 投影矩阵和视图矩阵 3d游戏中,一般通过相机的设置来计算投影矩阵和视图矩阵,比如untiy和cocos,一般情况下我们不用关注如何计算, 可以直接在可视化的编辑器中调整参数就可以了, ...

  8. unity中的透视投影矩阵

    一,unity中的Matrix4x4 例如一个矩阵的数据是: 0.9758,0,0,0,0,1.73205,0,0,0,0,-2.25,-16.25,0,0,-1,0 则实际矩阵是: M= m00 m ...

  9. cocos设置 相机矩阵和投影矩阵 源码浅析

    在cocos中,最后设置视口大小,相机矩阵,裁剪矩阵是在setProjection方法中,源码如下: void Director::setProjection(Projection projectio ...

随机推荐

  1. add printer driver error 1802修复说明

    1.重启电脑后 ,将服务"Print Spooler"服务重新启动2.srclient.dll文件拷贝到c盘 windows/system32目录下3.连接好打印机USB接口,重装 ...

  2. linux shell 删除指定文件夹下面 名称不包含指定字符的文件

    find /app/jenkins/jenkins/jobs/scam/* ! -name config.xml | xargs rm -rf 删除/app/jenkins/jenkins/jobs/ ...

  3. django中cookies和session

    django中cookies和session是两个经常使用的用户认证工具.都是类似于字典的数据类型,都是request的内部属性 cookies的读写方法 cookies读,比如username us ...

  4. mysql-5.7 saving and restore buffer pool state 详解

    一.mysql 重启要面临的问题: 由于重启后之前innodb buffer pool中缓存的数据就都没有了,如果这个时候业务SQL来临,mysql就只能是从磁盘中 读取数据到内存:可能要经过数个小时 ...

  5. 分享九:php易混淆的语法

    一:php后期静态绑定 从php5.3开始,php增加了一个叫后期绑定的功能,用于在继承范围内引用静态调用的类 该功能从语言内部角度考虑北命名为“后期静态绑定”:“后期绑定”意思说:static::不 ...

  6. angular.js 中同步视图和模型数据双向绑定,$watch $digest $apply 机制

    Angular.js 中的特性,双向绑定. 让视图的改变直接反应到数据中,数据的改变又实时的通知到视图,如何做到的? 这要归功于 scope 下面3个重要的方法: $watch $digest $ap ...

  7. Notes on PEP333 (Python Web Server Gateway Interface)

    This note is about PEP3333- Python Web Server Gateway Interface. Refer to (Source: http://legacy.pyt ...

  8. sqlite时间戳转时间语句(时间转时间戳)实例

    sqlite时间戳转时间.时间转时间戳的方法 实现代码: sqlite, 'unixepoch', 'localtime'); +----------------------------------- ...

  9. js实现精确统计网站访问量的代码分享

    JS 精确统计网站访问量. 代码如下: /** * vlstat 浏览器统计脚本 */ var statIdName = "vlstatId"; var xmlHttp; /** ...

  10. Hibernate 的一级缓存和二级缓存总结

    缓存:缓存是什么,解决什么问题? 位于速度相差较大的两种硬件/软件之间的,用于协调两者数据传输速度差异的结构,均可称之为缓存Cache.缓存目的:让数据更接近于应用程序,协调速度不匹配,使访问速度更快 ...