U3D和Shader初学者必看:投影矩阵(投影变换)解惑

背景

投影矩阵的推导曾经让我困惑了很久,反思可能是自己数学知识的浅薄,所以很多大神写的关于投影矩阵的推导很明晰还是看不懂,好在经过两周的努力学习和思考,终于弄明白了这个问题,特此做一个总结和大家分享一下,关于本篇的标题本来想起《大话投影矩阵》后来考虑推导的过程需要大量的数据知识,难度还是很大,所以取《投影矩阵解惑》这个标题比较合适,本篇准备采用问答的形式来书写。

 

下面我就以自问自答的形式来,解惑投影矩阵了。问题有这些

 

1、具体我们是在什么地方使用了投影矩阵?

2、我们为什么要知道“投影矩阵”和投影变换?

3、投影矩阵是什么,长的什么样子?

4、图说投影变换是干什么的?

5、投影矩阵推导的大神文章在哪里?

6、投影矩阵在投影变换中起了什么作用呢?他又是如何操作的?

7、投影变换中投影矩阵乘法的输入和输出又是什么呢

8、投影矩阵推导的核心难点在哪里?大神们解释清楚了?

9、学会了投影变换和投影矩阵的原理后,能给我们带来什么好处,我们又可以继续学习什么呢?

因为本篇可能会有很多数据知识,在正式开始之前,请读者自己回答这些问题,来验证下对于投影变换的了解程度,是一知半解还是已经掌握了。

解惑

1、具体我们是在什么地方使用了投影矩阵(投影变换)?

 

正式的说法是这样我们在渲染管线中使用了投影变换,投影变换是渲染管线中非常重要的一个步骤,实际上正是这个步骤实现了从3d 模型到 图像的转换。

 

 

具体不太明白的读者自己恶补吧

 

这里剽窃了两张图片

 

 

 

具体点,就是我们在Shader Vertex中使用了顶点的投影变换,具体代码如下,我想大家都见过类似的代码

 

其中 mul(UNITY_MATRIX_MVP, IN.vertex);就完成了投影变换的过程,其中UNITY_MATRIX_MVP是一个复合矩阵,MVP中的P就是投影变换的意思。

 

2、我们为什么要知道投影矩阵和投影变换?

 

既然及其他3D引擎的底层已经为我们封装好了,那我们为什么要知道投影矩阵和投影变换,

 

A、根据以上的介绍我们知道投影变换是渲染管线的核心变换,搞明白了它也就基本弄明白了渲染管线,这样可以为Shader开发打下坚实的基础;

 

B、在Shader开发中的,涉及到深度缓存使用,深度缓存重建世界坐标,image effect等高级Shader开发需要扎实的投影变换的知识,并在其基础上进行反推导。(不然可能给你一个Shader源码,你可能完全看不懂)

 

3、图说投影变换是干什么的?

 

3d 模型到 2d 图像的转换是一个复杂的过程,但基本的原理用一张图可以说清楚

 

 

看这张图基本能说明3D成像或者投影变换的过程和目标。

 

这里只要强调一点,世界本来就是3D的,所有的物体都是在世界坐标中原封不动,只是由于人眼或者相机成像(光学成像)的原理,照成了远小近大的视觉效果,才有了视锥这个东西,但是最终我们在显示器上看到的是一个平面,实际上3D系统是一个模拟系统。

 

4、投影矩阵是什么,长的什么样子?

 

 

n,近平面位置 f,远平面位置 α,垂直视角 fovy r,横纵比(aspect ratio

 

实际上一切都是纸老虎,把它当做一个普通的矩阵来看待,实际上它只是一个缩放和z上平移的矩阵而已。

 

5、投影矩阵在投影变换中起了什么作用呢?他又是如何操作的?

 

投影矩阵是如何操作的呢?这个问题简单的很,就是这句OUT.vertex = mul(UNITY_MATRIX_MVP, IN.vertex);其实就是做了一个矩阵乘法。那这个矩阵乘法起到了什么作用呢?矩阵乘法的本质是线性运动的一种描述,说白了是构建一种变换的坐标系,矩阵的列就是构建坐标系的轴向量。

 

6、投影变换中投影矩阵乘法的输入和输出又是什么呢

 

既然矩阵乘法,那乘法之前的样子和相乘之后的样子,也就是输入和输出了,用图来来看

矩阵乘法之前

 

矩阵乘法之后

 

这里需要注意的红色的视椎体和红色矩形,其实是用来推导透视矩阵标定时候用的,实际上变换的是蓝色的世界坐标物体

 

7、投影矩阵推导的核心难点在哪里?大神们解释清楚了?

 

A、核心难点是透视变换是否能脱离渲染流水线来单独说明白

 

实际上我觉得是不能的,一定要对渲染管线有整体的认识以后理解起来能简单一些,但如果将这两个问题绕在一起说,再结合大量的数学问题,我觉得对于初学者去看真是一头雾水

 

 

 

这可能是我发现的最好将可编程渲染管线的图了

实际上在vertex中 关于透视变换我们只是做了一次透视矩阵的乘法,也就是问题6中的变换,没有什么裁剪和透视除法的操作

 

B、将一个视锥体变换成一个立方体的透视矩阵的Z轴是如何构建的

 

实际上关于透视变换的原理大家都能明白,就是远小近大,这个谁都能理解

说白了就是视锥体中的成像平面中,x,y的投影值与z值成反比,越远越小

 

虽然我现在知道这个z值是如何推导的,但是大神的这种说法实在让人不能信服,怎么推导出来这个 az+b简直太奇葩了。

不过慕容小匹夫的文章还是比较中肯的,记住它是一个伪深度

 

如何用白话来说这个过程呢,为什么要用这个伪深度呢,这是另一篇神文中的比喻。

实际上投影矩阵要做的事情其实就是一个,将视矩阵中的视椎体变换成为一个x,y,z为(111)的标准CCV空间(作用是做视锥剔除,通过立方体进行剔除要简单多),实际上这个就是投影变换中的难点所在,很不好理解。我们用现实中的例子来思考下这个问题,如果将一个方形椎体钉入到一个立方体中,实际上不按照深度进行挤压是不可能的。

而事实上,这个矩阵的推导却要做到这个在线性空间不可能做到的事情。这也就是让我们阅读和理解的时候特别痛苦的事情。

 

那么他们是怎么做到的呢?

 

实际上我们要解决的问题是,如何将一条非线性的曲线拉直(转换成线性的)的问题,

 

方案A:直接用非线性空间不就得了嘛,拉伸曲线为直线变得简单easy,但由于我们需要使用矩阵乘法做线性空间的变换,所以这个方案是不可取的。

 

方案B:用一条直线模拟曲线,实际在有限的范围内是完全可能的,实际也是如此,而且这东西在计算机图形图像学中是广泛应用,也就是各种插值,推导中使用的就是线性插值,也就是我们用一条直线来替代了本来的曲线。所以为什么是大神会说是伪深度,这个深度值本来就不是准确的,当然为了消除x,y轴与深度z成反比的问题,我们还引入了齐次坐标系,升了一个维度。

 

8、学会了投影变换和投影矩阵的原理后,能给我们带来什么好处,我们又可以继续学习什么呢?

 

如何你真的搞明白了投影变换和投影矩阵的原理,那么:

 

A、关于什么是矩阵,以及矩阵变换相关的知识应用应该没有问题了;

 

B、对于渲染管线的流程应该有了一个更加直观的认识;

 

C、可以看懂和应用一些,深度缓存及深度缓存重建的Shader高级技术的编程了。

 

写在最后

说实话由于数学知识比较薄弱,这个知识点曾经我困惑了很久,现在终于给甩到身后去了,特写了本文,希望对于3DShader开发的初学者有点帮助,不过由于本人的能力有限,也有可能讲的不够清楚,望海涵。