Unity 透视投影矩阵变换的可视化

本文深入探讨Unity中相机透视投影矩阵的变换过程,通过可视化手段帮助理解从相机空间到规则观察体的转换。介绍了相机视锥的概念、参数及其在空间中的表示,以及如何利用投影矩阵判断物体是否在相机视野内。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

本文章演示Demo已上传GithubCameraProjectionMatix

3D渲染流水线中,物体某一个点从三维空间中映射到二维的屏幕上,通常使用MVP变换矩阵,而这三个字母分别代指不同坐标空间转换的三个矩阵,即:

  • M(Model):从本地空间转换到世界空间
  • V(View):世界空间转换到相机空间
  • P(Projection):相机空间转换到规则观察体

在之前的文章中,有对相机不同坐标空间的转换矩阵做过具体的描述,其中关于物体本地坐标转换到世界坐标的介绍,同样适用于世界空间转换为相机空间,如果感兴趣,可以查看该链接:

在前篇文章的基础上,本文章会介绍到渲染过程中最重要的P变换,即相机空间转换到规则观察体的过程(也可以理解为将相机的透视空间转换到正交空间),过程如图(图片来源于网络)所示:

相机空间视锥转CVV

关于投影矩阵推导有很多大佬在理论上做过的详细描述,不过通常是面向图形学的公式解析,比较难懂的同时对于工程项目上的应用提及较少。为了可以简单的理解投影的变换过程并可以实际应用,本文章会基于Unity引擎做一个详细的变换可视化,尽可能简单的拆解整个过程

相机投影矩阵的意义

1、相机透视投影概念:

在绘画理论中,用透视来表示平面或曲面上描绘物体的空间关系的方法或技术,通常来讲,在平面上在线空间感、立体感会通过三个属性来表示:

  • 物体的透视形(轮廓线),即上、下、左、右、前、后不同距离形的变化和缩小的原因
  • 距离造成的色彩变化,即色彩透视和空气透视的科学化
  • 物体在不同距离上的模糊程度,即隐形透视

以上信息来自百度百科对于透视的解释。简单的理解就是,在物体的透视形上,由于眼睛张角的存在,使得我们观察物体是总会有近大远小的感受,长此以往,由于经验的逐渐积累,这种现象间接的帮助人类完成的立体空间的构造

回到游戏引擎,没有什么是比模拟人眼成像更好的方式的,为了可以让计算机正确模拟人类感官的渲染出透视画面,相机在透视模式下,通过具有张角的锥形标识其取景范围,而且通常该锥形的顶部的尖会被消除,被称为视锥

虽然使用视锥的概念可以很好的解决透视的问题,但是对其后续计算带来了一定的问题。简单的来说,一个矩形可以通过中心点与其各边的长度非常方便的划分其空间范围,同时压缩某一轴达到三维到二维投影的目的。但是视锥是一个不同轴方向的长度不等的锥体,很难对范围做出界定

既然难以直接对视锥做空间判定,优秀的程序员或数学家就想到了将视锥规则化的数学变换公式,并以矩阵的形式参与运算,这就是相机的透视投影矩阵

2、通过Gizmos可视化相机视锥空间:

与相机的正交投影模式不同,透视投影为了得到近大远小的画面,会根据深度信息做平切面的画面缩放,而这些平切面连续组合起来就组成了相机的视锥空间,可以通过Unity提供的绘制工具Gizmos来表示出相机的渲染空间范围,绘制代码为:

	public void OnDrawGizmos()
    {
   
        //相机投影矩阵绘制
        Matrix4x4 start = Gizmos.matrix;
        Gizmos.matrix = Matrix4x4.TRS(transform.position, transform.rotation, Vector3.one);
        Gizmos.color = Color.yellow;
        Gizmos.DrawFrustum(Vector3.zero, cam.fieldOfView, cam.farClipPlane, 0, cam.aspect);
        Gizmos.color = Color.red;
        Gizmos.DrawFrustum(Vector3.zero, cam.fieldOfView, cam.farClipPlane, cam.nearClipPlane, cam.aspect);      
        Gizmos.matrix = start;

        //坐标辅助线绘制
        Gizmos.color = Color.red;
        Gizmos.DrawLine(cam.transform.position, cam.transform.position + cam.transform.right * 10);
        Gizmos.color = Color.green;
        Gizmos.DrawLine(cam.transform.position, cam.transform.position + cam.transform.up * 10);
        Gizmos.color = Color.blue;
        Gizmos.DrawLine(cam.transform.position, cam.transform.position + cam.transform.forward * 10);
    }

在编辑完成上面的代码后,可以在Unity引擎的Scene窗口可以看到下图中的辅助线,其中红框划定的立体图形即为相机投影视锥:

在这里插入图片描述

投影矩阵变换过程

通过视锥可以划定出相机的渲染空间,但是如何通过数学方式表示以便于机器理解与运算呢,要想得到结论,需要先明确条件。在开始推到前需要得到所拥有的信息条件,查阅Unity官方文档,可以得到与相机视角有关的参数信息:

  • Near Clip Planes:近裁剪平面,代表物体将要渲染的最近位置
  • Far Clip Planes:远裁剪平面,代表物体将要渲染的最远位置
  • 相机FOV:相机张角代表视野的宽高比例
  • 屏幕的宽高比:根据该比例可以得到另一方向相机的FOV

1、标识投影视锥八个顶点:

要标识一个空间的范围,通常会利用线段组合成对应形状的线框,例用模型的网格。而要决定线段的长度位置,就需要先得到顶点。所以我们第一步会基于上面的相机的一些基本参数来计算处相机视锥对应的八个顶点,来为后续的CVV空间划定获取做基础

为了简化推导过程,将三维空间问题映射到二维来考虑。以Y轴为法线,相机视锥的形状如下图所示,以X轴与Z轴构成的二维视角下的相机视锥空间范围标识线,并标识一些关键点的代名词,根据前面的相机关键参数,可以得到以下的已知信息:

  • AB的长度:为相机的近裁剪平面nearClipPlane
  • AC的长度:为相机的远裁剪平面farClipPlane
  • 角BAD的角度:为相机FOVhoriziontalvertical方向)的一半对应弧度

在这里插入图片描述

以D点的坐标获取为例,通过上面的图中所构造的三角形,已知AB的长度为近裁剪平面距离相机的长度,角BAD的度数为相机FOV的一半,利用三角函数可以计算出BD的长度,这样就得到了D点在Z轴与X轴的坐标长度AB与BD

至于Z轴的长度,对应相机在同样可以通过近裁剪平面的长度与相机在另外一个轴方向的FOV(可以使用Camera的静态方法VerticalToHorizontalFieldOfView求得)的数值

循环上面的求值过程,得到相机视锥八个顶点的坐标,并在对应位置分别放置一个物体来实例化这些坐标点,代码如下:

 	List<Vector3> GetPosLocation()
    {
   
        List<Vector3> backList=new List<Vector3>();
        for (int z = 0; z < 2; z++)
        {
   
           
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

心之凌儿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值