unity shader 入门精要阅读笔记-----渲染流水线


1渲染流水线

这一部分的重点就是渲染流水线,理解渲染流水线对于后面的学习来说至关重要,这也是图形学的一个重要的基础知识环节。

我们探讨空间中的模型是怎样一步一步转换到我们的屏幕坐标,然后显示在我们的面前的。
我们把步骤分成四个步骤,顶点处理阶段–光栅化阶段–片元处理阶段–输出合并阶段

1.1顶点处理阶段

这一阶段的任务就是把我们的顶点坐标从模型空间转换到裁剪空间。

1.1.1 矩阵变换

虽然大学我们都学过线性代数,但是学的时候只是单纯的应试教育,很大程度上没有理解其几何意义,因此,这里有必要重新去理解一遍。

平移、旋转、缩放

旋转和缩放变换被称为线性变换,平移变换和线性变换被称为仿射变换,对于一个三维空间中的点来说,线性变换可以用一个三维的矩阵来表示,但是平移变换是加法运算,无法通过右乘一个三维矩阵来实现,只能把三维点坐标拓展成四维坐标,然后右乘就可以实现平移变换,
在这里插入图片描述
如图所示,三维点坐标只需要把第四个坐标值变成1就可以实现平移变换,如果第四个值为0的话,最后的结果还是原来的点坐标,这种情况正好适用于方向向量,因为方向是没有位置的概念的。

坐标系变换 (很重要)

通过矩阵来把点从当前坐标系转换到另外一个坐标系比想象中还是要简单一点的。
如果我们知道坐标系C的xyz轴在P中的表示以及知道C的原点坐标以及C中某一点的坐标,在我们的脑海中简单的想象一下,便可以自然的得到其在P中的坐标
在这里插入图片描述这个写成矩阵的形式就是
在这里插入图片描述
没错,这就是我们坐标转换的矩阵,往往当我们只需要对方向向量进行变换的时候,就可以进行完全的线性变换,直接忽略掉最后一行,把三个坐标系的表示分别按列形成矩阵就可以得到变换矩阵了。

在后面shader的编写中,我们可以通过调用系统的unity_ObjectToWorld矩阵直接将顶点从模型空间变换到世界空间,这个变换大部分的shader都会用得到。

值得一提的是,如果进行了非均匀的缩放变换,也就是xyz三个坐标轴变换的比例不同,我们得到的变换矩阵不能直接用来变换法线。需要用其逆转置矩阵来进行变换,我们知道其原变换矩阵是unity_ObjectToWorld,其逆矩阵是unity_WorldToObject,我们再将其转置一下,我们这里只需要调换一下其在mul函数中的顺序即可,大家可能会想到直接转置不就不满足矩阵乘法的公式了,没法运算的呀,实际上,使用矩阵数学来进行矩阵x左乘矩阵y的运算,要求矩阵x的列数与矩阵y的行数相等。如果x是一个向量,那么它将被解释为行向量。如果y是一个向量,那么它将被解释为列向量。
因此后面我们会看到这样变换法线
fixed3 worldNormal = normalize(mul(v.normal),(float3x3)unity_WorldToObject);

1.1.2从模型空间变换到世界空间

我们学会了上面的坐标变换方法了,但是这里却不能直接那样做,因为我们不能直接获得模型空间三个坐标轴在世界空间中的表示,这里可以换一种思路去做,直接通过仿射变换,移动模型空间的坐标系和世界空间重合,得到的这个变换矩阵就是世界矩阵。

1.1.3从世界空间到观察空间

实际上,这一步的变换和上面一步的原理是一模一样的,但是最后因为观察空间是右手坐标系,和前面的左手坐标系不同,这里需要把最后结果的z分量取反。
unity内置的观察变换矩阵是unity_MatrixV

1.1.4从观察空间到裁剪空间

同上面的变化相比,这一部分要算是非常复杂的,具体的推导细节可以参考3D Graphics for Game Programming这本书。下面简单的贴一下书里面关于投影矩阵的推导以及一些我自己的理解与困惑。
在这里插入图片描述如上图所示,这个变化的实质就是把物体从视椎体变换到一个立方体中,立方体的范围为:x和y的范围都是-1到1,z的范围在opengl中也是-1到1,在Direct3D中的范围是0到1,这种差异会直接导致推导的结果不一样,不过过程差别也不大。
在推导的时候我们需要以下几个参数:
1.FovY 表示Y轴方向的视野区域的角度
2.z=-n z=-f 表示近裁剪面和远裁剪面
3.aspect 表示视截体底面宽度和高度的比值
在这里插入图片描述投影过程如上图所示,发射若干条指向摄像机的射线,这些射线在最后的投影平面内都是平行的,他们构成投影平面内的场景图像,因此,也很好理解,为什么越远的地方会变得越小了,图中的两条线段尽管长度不相同,但是投影到了右图中就变成一样长了。
在这里插入图片描述横截面如上图所示,我们定义投影平面为z=-cot(fovY/2),这样可以保证在投影后y’的范围在(-1,1),然后由相似三角形可以得到y’的表达式。
在这里插入图片描述同理,把y替换成x就可以得到x’的表达式
在这里插入图片描述但是我们不能直接得到fovx的值,需要通过aspect来推导出来
在这里插入图片描述由上图我们能得到
在这里插入图片描述因此我们可以把坐标映射表示成如下:
在这里插入图片描述D为cot(fovY/2),A为aspect
因为其为齐次坐标,我们直接把所有的项乘上一个-z,就可以得到下面的公式,这样做是为了方便写成矩阵变换的形式
在这里插入图片描述在这里插入图片描述
至此,矩阵中只有m1,m2,m3,m4是未知的,其他的我们都已经推导出来了,m1,m2,m3,m4有以下关系,
m1* x+m2* y+m3* z+m4 = -zz’
我们可以想象,任何一个平行于投影平面的平面,其z值都是一样的,也就是投影后的z’值与x和y没有关系,这样的话,m1和m2的值理所当然也就是0了。
我们先拿D3D的坐标来举例子,z’的范围是[-1,0],对于远裁剪面-f其值是-1,近裁剪面-n其值是0,然后代入两点,就可以解出来m3和m4的值
这样我们就能得到最终的投影矩阵
这里的第三行的所有分量都已经取反了,因为在后面的光栅化阶段,裁剪空间是左手坐标系,而从观察空间传递来的坐标是基于右手坐标系的,因此这里还需要进行取反操作。
在这里插入图片描述
对于opengl来说也只是最后代入的两点坐标的不同而已,计算方法也是很类似的。

1.2 光栅化阶段

1.2.1 裁剪操作

这个步骤是硬件中实现的,用户无法改变,其目的就是把在裁剪空间外面的三角形全部剔除,部分在裁剪空间内的三角形进行裁剪。

1.2.2 透视除法

上面我们得到的投影矩阵乘过一个-z,这样就会导致xyz会超出其应该的范围,我们这里还需要把xyz全部除以w的值,得到归一化的设备坐标(NDC)

1.2.3 背面剔除操作

把所有背向于摄像机的三角形全部剔除掉,可以理解为把被遮挡住的三角形全部剔除掉。unity会默认剔除掉背面,我们也可以通过Cull Off来关闭背面剔除的效果,使得物体能渲染两个面。

1.2.4 视口变换

这个变换就是把归一化的设备坐标映射到具体的屏幕坐标上面
下图是加上了齐次除法的视口变换的公式,理解起来也很容易,unity里面使用的是opengl的裁剪空间,也就是z的范围是-1到1,这样的话,我们首先需要把-1到1映射到0~1,然后再乘上屏幕分辨率即可,也就是(clipx/clipw+1)/2* pixelWidth,就是下图里面的公式。
在这里插入图片描述

发布了31 篇原创文章 · 获赞 4 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_43813453/article/details/100929990