【UnityShader入门精要学习笔记】第四章(5)坐标空间

在这里插入图片描述
本系列为作者学习UnityShader入门精要而作的笔记,内容将包括:

  • 书本中句子照抄 + 个人批注
  • 项目源码
  • 一堆新手会犯的错误
  • 潜在的太监断更,有始无终

总之适用于同样开始学习Shader的同学们进行有取舍的参考。


文章目录

  • 复习
    • 知识点复习
      • 什么是变换
      • 齐次坐标
      • 分解基础变换矩阵
      • 平移矩阵
      • 缩放矩阵
      • 旋转矩阵
      • 复合变换
    • 练习题答案
  • 坐标空间
    • 为什么使用这么多不同的坐标空间?
    • 坐标空间的转换
    • 顶点的坐标空间变换
      • 模型空间
      • 世界空间
      • 观察空间
      • 裁剪空间
      • 正交投影
      • 透视投影
      • 举个例子
      • 屏幕空间
  • MVP变换


(该系列笔记中大多数都会复习前文的知识,特别是前文知识非常重要的时候,这是为了巩固记忆,诸位可以直接通过目录跳转)

复习

知识点复习

什么是变换

变换(transform),指的是我们将一些数据,如点、方向向量甚至是颜色等,通过某种方式进行转换的过程。

什么是线性变换(Linear transform),线性变换指的是那些可以保留向量加和标量乘的变换,用公式来表示就是:
f ( x ) + f ( y ) = f ( x + y ) k f ( x ) = f ( k x ) f(x)+f(y)=f(x+y)\newline kf(x)=f(kx) f(x)+f(y)=f(x+y)kf(x)=f(kx)

所以变换本质上是对函数花里胡哨的另一种表示,我们可以简单的理解为一种函数,也就是:
x → f ( x ) → y x\to f(x) \to y xf(x)y,向量x经过变换 f ( x ) f(x) f(x)变为向量y。

原点不变,网格平行,单元格大小一致,这样的变换才叫做线性变换。

实质上,线性变换可以视为我们对基向量的变换,对基向量的变换会应用到整个张成空间上。


如果在线性变换之后,我们再添加一个平移变换,例如 f ( x ) = x + ( 1 , 2 , 3 ) f(x)=x+(1,2,3) f(x)=x+(1,2,3),这个变换就不是一个线性变换,因为既没有标量的乘除,也不满足向量的加减( f ( x ) + f ( x ) ≠ f ( x + x ) f(x)+f(x) \neq f(x+x) f(x)+f(x)=f(x+x)

在几何上的表现就是整个向量空间进行了平移。那么原点位置改变就不是线性变换,我们将这种变换称为仿射变换(affine transfrom)仿射变换是线性变换的父集,它包含了平移变换和线性变换),那么一个三维空间的仿射变换就无法用3阶矩阵来进行描述(因为非线性),我们需要将向量扩展到四维空间下,这个四维空间被称为齐次坐标空间(homogeneous space)


齐次坐标

假设一个二维空间上的仿射变换,其线性矩阵需要从2x2拓展到3x3,从几何意义上来看就是由二维空间拓展到了三维空间。也就是说对于原向量的变换被我们放在了三维空间中进行处理

那么一个低维的仿射变换,放到高一维度的空间就成为了线性变换,我们就可以进行线性代数的计算了。最后我们只需降维投影到原来的低维空间,就得到了最终的变换结果。

我们将仿射变换产生的高维矩阵称为齐次坐标(homogeneous coordinate),在三维软件中这个齐次坐标通常是四维齐次坐标。我们将第四维上的分量称为 w 分量 w分量 w分量,因此实际上任意变换我们都可以通过乘以四维齐次坐标来实现,当仅仅是线性变换的时候我们可以使得w分量为0,使其依然保持在二维平面上(仅表示缩放,旋转)。

分解基础变换矩阵

我们用一个4x4的矩阵来表示平移、旋转和缩放。我们将表示纯平移、纯旋转和纯缩放的变换矩阵称为基础变换矩阵,我们可以将一个基础变换矩阵分解成四个组成部分:

y = A x + b → [ y 1 ] = [ A b 0 1 ] [ x 1 ] y=Ax+b \to \begin{bmatrix}y\\ 1 \end{bmatrix}=\textcolor{blue}{\begin{bmatrix}A&b\\ 0&1 \end{bmatrix}}\begin{bmatrix}x\\ 1 \end{bmatrix} y=Ax+b[y1]=[A0b1][x1]

上图中蓝色矩阵就是基础变换矩阵,其中:

[ M 3 × 3 t 3 × 1 0 1 × 3 1 ] \begin{bmatrix}M_{3×3}&t_{3×1}\\ 0_{1×3}&1 \end{bmatrix} [M3×301×3t3×11]

左上角的三阶矩阵A表示旋转和缩放,右上角b表示平移,左下角0是零矩阵,右下角元素是标量1

平移矩阵

当令缩放大小等于1时,也就是不旋转或缩放,仅仅进行平移变换:

在这里插入图片描述

缩放矩阵

同样的,我们可以用四维矩阵描述一个缩放变换:
在这里插入图片描述
如果 k x = k y = k z k_x=k_y=k_z kx=ky=kz,则说明在各个分量上缩放了相同的倍率,我们将其称之为统一缩放,否则为非统一缩放 。统一缩放是对空间的整体缩放,因此不会改变角度和比例信息,而非统一缩放会。

旋转矩阵

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

复合变换

我们可以将平移,旋转和缩放组合起来,形成一个复杂的变换过程:

P n e w = M t r a n s l a t i o n M r o t a t i o n M s c a l e P o l d P_{new}=M_{translation}M_{rotation}M_{scale}P_{old} Pnew=MtranslationMrotationMscalePold

运用一下矩阵计算的结合律,实际上我们的计算顺序是先缩放,再旋转,在平移,记住这个约定俗成的计算顺序。

当然我们也可以将三种变换合并为一个复合矩阵:

在这里插入图片描述
(如果矩阵的运算顺序不同,得到的复合矩阵也不同)

在三维空间中的旋转可以分别描述为绕x轴、y轴、z轴的旋转。在Unity中,旋转的顺序是zxy,这意味着,当给定 ( θ x , θ y , θ z ) (\theta_x,\theta_y,\theta_z) (θx,θy,θz)这样的旋转角度时,得到的组合旋转变换矩阵是:

在这里插入图片描述
而上述矩阵运算下实际的旋转顺序是YXZ而非我们刚才讲的ZXY,原因是Unity中实际上有两种旋转坐标系:

  • 绕原坐标系E下的z轴旋转 θ z \theta_z θz,绕原坐标系E的y轴旋转 θ y \theta_y θy,绕原坐标系E下的x轴旋转 θ x \theta_x θx,即进行一次旋转时不一起旋转当前坐标系。
  • 绕坐标系E下的z轴旋转 θ z \theta_z θz,得到心坐标系 E ′ E' E,绕 E ′ E' E的y轴旋转 θ y \theta_y θy,得到新坐标系 E ′ ′ E'' E′′,再绕 E ′ ′ E'' E′′的x轴旋转 θ \theta θ,即在旋转式将坐标轴一起转动。

显而易见的,这两种旋转顺序计算的结果不同,但如果将它们的旋转顺序颠倒一下,其结果就是相同的!也就是Unity中虽然旋转顺序是zxy,属于第一种。但如果我们进行复合旋转矩阵计算,那么每次应用一种旋转矩阵后其坐标轴都会变换为新的坐标轴,属于第二种。

因此,矩阵运算顺序YXZ实际和Unity中的ZXY是相同的。

而如果采用第二种方式,就会发生一种特殊的现象,当中间的轴x旋转90°后,z轴和y轴会重合。此时物体就丢失了一条坐标轴,这种现象被我们称为欧拉角的万向死锁现象。

练习题答案

设物体在x轴上旋转 θ ° \theta° θ°,在y轴上旋转 π 2 \frac{\pi}{2} 2π,在z轴上旋转 γ ° \gamma° γ°

【UnityShader入门精要学习笔记】第四章(5)坐标空间_第1张图片
得证万向死锁现象


坐标空间

在第二章渲染流水线中,我们学习到了坐标空间的变换,例如顶点着色器的功能就是将模型的顶点位置转到齐次裁剪坐标系,再转到NDC中,最后几何化阶段中会转换到屏幕坐标。

渲染游戏的过程就是这样的一个个顶点经过层层转换最终转换到屏幕上的过程

为什么使用这么多不同的坐标空间?

之所以会引申出这么多不同的坐标空间,是因为在不同坐标系下我们描述物体的方式是不一样的,某些概念描述只有在特定的坐标空间下才有意义。并且在同一坐标空间下也方便我们对不同物体进行规范。

坐标空间的转换

在渲染流水线中,我们是如何将一个点或者一个向量从一个坐标空间转换到另一个坐标空间的?

一个坐标空间中必须包含的就是其原点位置和坐标系。但是不同空间的原点位置也是不一样的,因此如果我们想要用统一的数值描述两个不同的坐标空间,就需要以其中一个坐标空间为参考。因此,坐标空间之间也就存在了层级关系,我们将作为参考系的坐标空间称为父空间 ,以父空间 为参考的称为子空间

因此,每个坐标空间都可以描述为另一个坐标空间(父空间)的子空间。对坐标空间的变换实质上就是在父空间和子空间之间对点和向量进行变换 。由于不同空间的点和向量在坐标上的描述一定是唯一对应的,我们也将其称之为空间映射关系

这种空间映射函数实质上很简单: a → f ( x ) → b a\to f(x) \to b af(x)b a a a是父空间下一个点(以父空间的坐标进行描述), f ( x ) f(x) f(x)代表了将点从父空间映射到子空间的映射关系函数, b b b代表了映射到子空间上的点(以父空间的坐标进行描述)

那么空间到空间的这种变换函数关系,我们可以用上节学习的四阶矩阵来进行描述。设 父空间为 P ,子空间为 C 父空间为P,子空间为C 父空间为P,子空间为C,那么 P → C P \to C PC的映射矩阵就是 T P → C T_{P\to C} TPC,反之 C → P C \to P CP的映射矩阵就是 T C → P T_{C \to P} TCP

A P = T P → C A C A_P = T_{P\to C}A_C AP=TPCAC
B C = T C → P B P B_C = T_{C \to P}B_P BC=TCPBP

如何求出这个映射矩阵?原文是通过对子空间的原点位置进行平移来求 M C → P M_{C \to P} MCP的,但是我有疑问?平移原点如何表示子空间相对于父空间的旋转关系?原文的证明过程完全默认子空间和父空间是相同坐标系下,并且不发生旋转,这样是不符合实际的。

我们把基础的变换分为旋转,缩放和平移,对于旋转缩放可以合并为一个线性矩阵,不妨以父空间原点为参照坐标系,则在父空间中的基底对应会在子空间产生旋转缩放变换,分别设该线性矩阵为 α , β , γ \alpha,\beta,\gamma α,β,γ,平移距离为 A , B , C A,B,C A,B,C,则有 i ^ c = α i ^ p + A , j ^ c = β i ^ p + B , k ^ c = γ k ^ p + C \hat i_c=\alpha \hat i_p+A,\hat j_c=\beta \hat i_p+B,\hat k_c=\gamma \hat k_p +C i^c=αi^p+Aj^c=βi^p+B,k^c=γk^p+C

单位向量 P c = ( i ^ c , j ^ c , k ^ c ) 单位向量P_c=(\hat i_c,\hat j_c,\hat k_c) 单位向量Pc=(i^c,j^c,k^c),变换之后的基向量 i ^ c \hat i_c i^c由于可能发生旋转,因此对应的坐标不再是 ( 1 , 0 , 0 ) (1,0,0) (1,0,0),设其坐标为 ( i ^ c x , i ^ c y , i ^ c z ) (\hat i_{cx},\hat i_{cy},\hat i_{cz}) (i^cx,i^cy,i^cz),其余同理

P c = ( i ^ c , j ^ c , k ^ c ) = [ i ^ c x j ^ c x k ^ c x i ^ c y j ^ c y k ^ c y i ^ c z j ^ c z k ^ c z ] = [ i ^ p x j ^ p x k ^ p x i ^ p y j ^ p y k ^ p y i ^ p z j ^ p z k ^ p z ] [ α β γ ] + ( A , B , C ) = ( i ^ p , j ^ p , k ^ p ) [ α β γ ] + ( A , B , C ) P_c=(\hat i_c,\hat j_c,\hat k_c)=\begin{bmatrix}\hat i_{cx} & \hat j_{cx} &\hat k_{cx}\\ \hat i_{cy} &\hat j_{cy} &\hat k_{cy}\\ \hat i_{cz} &\hat j_{cz} &\hat k_{cz}\end{bmatrix}=\begin{bmatrix}\hat i_{px} & \hat j_{px} &\hat k_{px}\\ \hat i_{py} &\hat j_{py} &\hat k_{py}\\ \hat i_{pz} &\hat j_{pz} &\hat k_{pz}\end{bmatrix}\begin{bmatrix}\alpha \\ \beta \\ \gamma\end{bmatrix} +(A,B,C)=(\hat i_p,\hat j_p,\hat k_p)\begin{bmatrix}\alpha \\ \beta \\ \gamma\end{bmatrix} +(A,B,C) Pc=(i^c,j^c,k^c)= i^cxi^cyi^czj^cxj^cyj^czk^cxk^cyk^cz = i^pxi^pyi^pzj^pxj^pyj^pzk^pxk^pyk^pz αβγ +(A,B,C)=(i^p,j^p,k^p) αβγ +(A,B,C)

将上式拓展到齐次坐标空间则有:

= [ i ^ p x j ^ p x k ^ p x 0 i ^ p y j ^ p y k ^ p y 0 i ^ p z j ^ p z k ^ p z 0 0 0 0 1 ] [ α β γ 1 ] + ( A , B , C , 0 ) = [ i ^ p x j ^ p x k ^ p x A i ^ p y j ^ p y k ^ p y B i ^ p z j ^ p z k ^ p z C 0 0 0 1 ] [ α β γ 1 ] =\begin{bmatrix}\hat i_{px} & \hat j_{px} &\hat k_{px} & 0\\ \hat i_{py} &\hat j_{py} &\hat k_{py}& 0\\ \hat i_{pz} &\hat j_{pz} &\hat k_{pz}& 0\\ 0 &0 &0& 1 \end{bmatrix}\begin{bmatrix}\alpha \\ \beta \\ \gamma\\ 1\end{bmatrix} +(A,B,C,0)=\begin{bmatrix}\hat i_{px} & \hat j_{px} &\hat k_{px} & A\\ \hat i_{py} &\hat j_{py} &\hat k_{py}& B\\ \hat i_{pz} &\hat j_{pz} &\hat k_{pz}& C\\ 0 &0 &0& 1 \end{bmatrix}\begin{bmatrix}\alpha \\ \beta \\ \gamma\\ 1\end{bmatrix} = i^pxi^pyi^pz0j^pxj^pyj^pz0k^pxk^pyk^pz00001 αβγ1 +(A,B,C,0)= i^pxi^pyi^pz0j^pxj^pyj^pz0k^pxk^pyk^pz0ABC1 αβγ1

所以我们就求出了复合变换矩阵 T P → C T_{P\to C} TPC,同时得到了代表旋转和缩放的矩阵 M P → C M_{P\to C} MPC即为左上角的三阶线性矩阵,同理也可以求出 T C → P T_{C\to P} TCP,上式中的基向量可以被我们替换为坐标空间中的任意一点的坐标。

且由于坐标系是正交的,因此只需要知道 M P → C M_{P\to C} MPC就可以得出 M C → P M_{C\to P} MCP,即 M P → C \newline M_{P\to C} MPC= M C → P − 1 M C → P = M P → C T M_{C\to P}^{-1}\newline M_{C\to P}=M_{P\to C}^{T} MCP1MCP=MPCT,因为两者在变换上是逆矩阵的关系,且正交矩阵的转置就是该矩阵的逆矩阵


顶点的坐标空间变换

我们证明了空间映射关系其实就是应用了一次齐次变换矩阵。那么让我们仔细研究一下,顶点着色器中,一个顶点究竟是如何变换到最终的屏幕空间的:

模型空间

模型空间(model space) 代表了模型自身所处的坐标空间,或者我们也可以称其为对象空间(object space)或者局部空间(local space) ,每个模型都有属于自己的独立的模型空间,当模型发生变换时,其模型空间也随之发生变换。
【UnityShader入门精要学习笔记】第四章(5)坐标空间_第2张图片
模型中的顶点坐标是四维的,需要被拓展到齐次坐标系下。

世界空间

世界空间(world space) 用于描述模型的绝对位置,即在Unity场景中的世界坐标系位置,世界空间的原点就是游戏场景的中心。

在Unity中,物体有Transfrom组件的Position属性来表示它的坐标。这个Position表示的是物体相对于父物体(parent)为原点的坐标,如果一个GameObject没有任何父节点,则Transfrom代表了物体在世界空间中的坐标。Transfrom的Rotation和Scale也同理。

顶点变换的第一步就是从模型空间变换到世界空间
【UnityShader入门精要学习笔记】第四章(5)坐标空间_第3张图片
【UnityShader入门精要学习笔记】第四章(5)坐标空间_第4张图片
(注意变换顺序,先缩放,再旋转,再平移,矩阵是从右到左)
【UnityShader入门精要学习笔记】第四章(5)坐标空间_第5张图片
所以得到了鼻子上的点P的世界坐标(9,4,18.072)


观察空间

观察空间(view space)或者称视图是摄像头视角下的空间坐标,也称为摄像机空间(camera space)

观察空间决定了摄像机渲染游戏使用的视角。摄像机的位置代表了原点,而我们在之前讲过,观察空间使用的是右手坐标系,也就是屏幕到人眼的方向为正坐标。

现在我们想要将世界空间的坐标变换到观察空间中,这个变换称为观察变换(view transfrom)

【UnityShader入门精要学习笔记】第四章(5)坐标空间_第6张图片
上一步我们求出了鼻子的世界空间坐标,现在我们根据摄像机的世界空间坐标,想要将鼻子从世界空间转换到摄像机的观察空间。

现在已知Transform信息,也就是世界空间坐标信息,我们知道这个坐标是观察空间乘以变换矩阵得到的世界空间。因此找到世界空间到观察空间的逆矩阵,就可以将鼻子坐标从世界空间变换到观察空间。

【UnityShader入门精要学习笔记】第四章(5)坐标空间_第7张图片
(潦草手写,反正就是一个简单的思路)

那么矩阵的逆变换通过Transform组件可知,就是缩放1/1倍,绕x轴旋转-30°,最后平移(0,-10,10)

【UnityShader入门精要学习笔记】第四章(5)坐标空间_第8张图片

并且注意,世界空间转换到观察空间,左手坐标系变为右手坐标系,则z轴方向翻转,所以z轴坐标取反:
【UnityShader入门精要学习笔记】第四章(5)坐标空间_第9张图片
这个就是我们最终使用的世界空间转观察空间的变换矩阵,乘以鼻子的世界空间坐标:

【UnityShader入门精要学习笔记】第四章(5)坐标空间_第10张图片
因此鼻子在观察空间的坐标为(9,8.84,-27.31)


裁剪空间

在观察空间转换为屏幕坐标之前还需要经历一些步骤,就是几何阶段的顶点经过齐次裁剪空间再到NDC上的过程。

接下来我们要将观察空间转换到裁剪空间(clip space ,也被称为齐次裁剪空间)。这个用于变换的矩阵叫做裁剪矩阵clip matrix,也叫投影矩阵(projection matrix)

裁剪空间的目的是确定那些图元需要被渲染,图元如果完全在裁剪空间内则保留;如果部分在裁剪空间内,就根据边缘对图元裁剪,保留空间内的部分进行渲染;如果图元完全不在空间内则被舍弃。而裁剪空间的大小则是由视锥体(view frustum) 决定。

视锥体指的是空间中的一块区域,这块区域决定了摄像机可以看到的空间。视锥体决定了摄像机能看到的视野范围,视锥体是一个六面体,它的每个面被称为裁剪平面clip planes
【UnityShader入门精要学习笔记】第四章(5)坐标空间_第11张图片

摄像机有两者投影模式,一种是透视投影(perspective projection) ,另一种是正交投影(orthographic projection)

透视投影模拟了正常人眼视角下的随距离产生的透视效果,而正交投影则是不带透视效果的渲染。
【UnityShader入门精要学习笔记】第四章(5)坐标空间_第12张图片
在视锥体的所有裁剪平面中,有两个裁剪平面比较特殊,分别被称为近裁剪平面(near clip plane)远裁剪平面(far clip plane) 。他们决定了摄像机可以看到的深度范围。

由于透视投影的视锥是一个金字塔形,因此如果通过点在空间上的位置判断是否在视锥内从而实现裁剪是相当麻烦的。因此,我们想用一种更通用的方式来进行裁剪,这种方式就是通过一个投影矩阵把顶点转换到一个裁剪空间中。

投影矩阵的目的有两个:

  • 为投影做准备,虽然投影矩阵名称中包含了投影,但没有真正进行投影的工作,真正的投影发生在齐次除法过程中
  • 其次是对xyz分量进行缩放,通过投影矩阵可以将w分量作为一个范围值,若xyz分量都位于这个范围中则说明该顶点位于裁剪空间内。

在之前讲齐次坐标的时候,我们使用了w分量,不过通常w分量是固定值,点的w分量为1,而方向向量的w分量为0,经过投影矩阵变换后我们将赋予w分量更深的含义。


正交投影

想要理解透视投影,首先要理解正交投影,因为透视投影本质上是将透视锥体(或者正式中文名平截头体frustum)拉伸成正交投影下的长方体,然后对其应用了正交投影的矩阵变换。

正交投影经历了什么?首先是摄像机空间到齐次裁剪空间(由于这个空间是四维的,我们只能通过矩阵运算得到),然后将齐次裁剪空间映射到NDC,也就是目的是想要摄像机空间转为NDC:

【UnityShader入门精要学习笔记】第四章(5)坐标空间_第13张图片
上图中最左边代表了正交视图的长方体,那么我们需要经过平移和缩放将其转化为NDC的单位正方体(没有考虑旋转),超出正方体的都是被裁剪的图元。

我们通过定义该长方体中的xyz轴范围来描述这个长方体,即为: [ l , r ] , [ b , t ] , [ f , n ] [l,r],[b,t],[f,n] [l,r],[b,t],[f,n],我们想将其转换到NDC的 [ − 1 , 1 ] , [ − 1 , 1 ] , [ − 1 , 1 ] [-1,1],[-1,1],[-1,1] [1,1],[1,1],[1,1]中。

那么这个变换可以被我们描述为立方体中心坐标位置的变换,目标就是将原来的中心坐标移动到最终的原点位置并实现缩放,原中心坐标点为 ( l + r 2 , b + t 2 , f + n 2 ) (\frac{l+r}2,\frac{b+t}2,\frac{f+n}2) (2l+r,2b+t,2f+n)因此齐次矩阵在变换上可以描述为:

M o r t h o = M s c a l e M t r a n s = [ 2 r − l 0 0 0 0 2 t − b 0 0 0 0 2 n − f 0 0 0 0 1 ] [ 1 0 0 − l + r 2 0 1 0 − b + t 2 0 0 1 − f + n 2 0 0 0 1 ] = [ 2 r − l 0 0 l + r l − r 0 2 t − b 0 b + t b − t 0 0 2 n − f f + n f − n 0 0 0 1 ] M_{ortho}=M_{scale}M_{trans} = \begin{bmatrix}\frac{2}{r-l} &0&0 & 0\\ 0 &\frac{2}{t-b}&0& 0\\ 0 &0 &\frac{2}{n-f}& 0\\ 0 &0 &0& 1 \end{bmatrix}\begin{bmatrix}1 &0&0 & -\frac{l+r}2\\ 0 &1&0& -\frac{b+t}2\\ 0 &0 &1& -\frac{f+n}2\\ 0 &0 &0& 1 \end{bmatrix}= \begin{bmatrix}\frac{2}{r-l} &0&0 & \frac{l+r}{l-r}\\ 0 &\frac{2}{t-b}&0& \frac{b+t}{b-t}\\ 0 &0 &\frac{2}{n-f}& \frac{f+n}{f-n}\\ 0 &0 &0& 1 \end{bmatrix} Mortho=MscaleMtrans= rl20000tb20000nf200001 1000010000102l+r2b+t2f+n1 = rl20000tb20000nf20lrl+rbtb+tfnf+n1

【UnityShader入门精要学习笔记】第四章(5)坐标空间_第14张图片
如果转化为Unity中的参数,则有 S i z e = t − b 2 , A s p e c t = r − l t − b , F a r = f , N e a r = n Size=\frac{t-b}{2},Aspect=\frac{r-l}{t-b},Far=f,Near=n Size=2tb,Aspect=tbrl,Far=f,Near=n,由于以摄像头坐标为原点,因此 l = − r , b = − t l = -r,b= -t l=r,b=t(注意,由于Unity中用于描述的坐标值是左手坐标系,而观察空间为右手坐标系,应当对z轴取反)。

因此上式转化为:

【UnityShader入门精要学习笔记】第四章(5)坐标空间_第15张图片
【UnityShader入门精要学习笔记】第四章(5)坐标空间_第16张图片

在经过上述投影矩阵对点的计算后,点已经被映射到齐次裁剪空间了,等待齐次除法完成最后的投影。


透视投影

参考[图形学笔记]推导投影矩阵

在理解了正交投影之后,我们就可以理解透视投影了,其实透视投影和正交投影是一样的,唯一多了一步就是先将透视投影转化为正交投影:

【UnityShader入门精要学习笔记】第四章(5)坐标空间_第17张图片
在把视锥体压缩成长方体的过程中,我们规定三个原则

1.近平面的所有点坐标不变(因为近平面就是正交矩阵的面)

2.远平面的所有点坐标z值不变,都是f(视锥体映射为立方体,z深度保持不变)

3.远平面的中心点坐标值不变,为(0,0,f)(变换过程中,中心位置始终不变)
【UnityShader入门精要学习笔记】第四章(5)坐标空间_第18张图片
根据相似三角形,我们有以下推断:

近平面的点 y ′ = n z y , x ′ = n z x y'=\frac{n}{z}y,x'=\frac{n}{z}x y=zny,x=znx

近平面上的点其实就是正交投影下的点,所以最终结果就是对近平面的点进行正交投影矩阵变换。需要将远平面的点映射为近平面上的坐标点使得 P ( x , y , z ) → f ( d o t ) → P ′ ( x ′ , y ′ , z ′ ) P(x,y,z)\to f(dot) \to P'(x',y',z') P(x,y,z)f(dot)P(x,y,z)

也就是存在矩阵 M p e r s p → o r t h o , 使得 M p e r s p → o r t h o P = P ′ M_{persp\to ortho},使得M_{persp\to ortho}P=P' Mpersportho,使得MpersporthoP=P

则有 M p e r s p → o r t h o ( 4 × 4 ) ( x , y , z , 1 ) T = ( n x z , n y z , u n k n o w , 1 ) T M_{persp\to ortho}^{(4×4)}(x,y,z,1)^T=(\frac{nx}{z},\frac{ny}{z},unknow,1)^T Mpersportho(4×4)(x,y,z,1)T=(znx,zny,unknow,1)T

这里需要用到一个重要的性质,在齐次坐标系下, (x,y,z,1) 与 (kx,ky,kz,k) (k≠0) 等价

对点的坐标乘以 z z z,则有 M p e r s p → o r t h o ( 4 × 4 ) ( x , y , z , 1 ) T = ( n x , n y , u n k n o w , z ) T M_{persp\to ortho}^{(4×4)}(x,y,z,1)^T=(nx,ny,unknow,z)^T Mpersportho(4×4)(x,y,z,1)T=(nx,ny,unknow,z)T
【UnityShader入门精要学习笔记】第四章(5)坐标空间_第19张图片
易得 M p e r s p → o r t h o M_{persp\to ortho} Mpersportho有上述元素。

由于近平面上的点应用该变换后依然保持在近平面上,因此(打公式太累了,直接用知乎里的图吧):
【UnityShader入门精要学习笔记】第四章(5)坐标空间_第20张图片

将上述矩阵乘法描述为线性方程组,则对于第一二四行,我们写出等式:

n x + 0 y + 0 n + 0 ∗ 1 = x nx+0y+0n+0*1=x nx+0y+0n+01=x

0 x + n y + 0 n + 0 ∗ 1 = y 0x+ny+0n+0*1=y 0x+ny+0n+01=y

0 x + 0 y + 1 n + 0 ∗ 1 = 1 0x+0y+1n+0*1=1 0x+0y+1n+01=1

很明显这是有问题的,因为n应该是任意常数,但是现在只有在n等于1时,一二四行的运算才成立

对。因此我们再次对齐次坐标系中的点进行转换,对其乘以n倍:

【UnityShader入门精要学习笔记】第四章(5)坐标空间_第21张图片
则有:
n x + 0 y + 0 n + 0 ∗ 1 = n x , nx+0y+0n+0*1=nx, nx+0y+0n+01=nx,

0 x + n y + 0 n + 0 ∗ 1 = n y , 0x+ny+0n+0*1=ny, 0x+ny+0n+01=ny,

0 x + 0 y + 1 n + 0 ∗ 1 = n 0x+0y+1n+0*1=n 0x+0y+1n+01=n
此时等式两倍恒等了。我们可以求解第三行了:

设第三行的四个数分别为ABCD
可以获得等式 A x + B y + C n + D = n ∗ n Ax+By+Cn+D = n*n Ax+By+Cn+D=nn
显然 A = 0 , B = 0 A=0,B=0 A=0,B=0,但是C,D并不能唯一确定一个解。

最后我们使用第三个原则: 远平面的中心点坐标值不变 为(0,0,f)

【UnityShader入门精要学习笔记】第四章(5)坐标空间_第22张图片
对结果乘以f倍。
联立方程组 C n + D = n ∗ n C f + D = f ∗ f C = n + f D = − n f \newline Cn+D = n*n\newline Cf+D = f*f\newline C = n+f\newline D = -nf Cn+D=nnCf+D=ffC=n+fD=nf
【UnityShader入门精要学习笔记】第四章(5)坐标空间_第23张图片
太不容易了,但是还没完,我们还需要将其和 M o r t h o M_{ortho} Mortho矩阵相乘才能得到 M p e r s e M_{perse} Mperse
[ 2 r − l 0 0 l + r l − r 0 2 t − b 0 b + t b − t 0 0 2 n − f f + n f − n 0 0 0 1 ] [ n 0 0 0 0 n 0 0 0 0 n + f − n f 0 0 1 0 ] = [ 2 n r − l 0 l + r l − r 0 0 2 n t − b b + t b − t 0 0 0 n + f n − f 2 n f f − n 0 0 1 0 ] \begin{bmatrix}\frac{2}{r-l} &0&0 & \frac{l+r}{l-r}\\ 0 &\frac{2}{t-b}&0& \frac{b+t}{b-t}\\ 0 &0 &\frac{2}{n-f}& \frac{f+n}{f-n}\\ 0 &0 &0& 1 \end{bmatrix}\begin{bmatrix}n &0&0 & 0\\ 0 &n&0& 0\\ 0 &0 &n+f&-nf\\ 0 &0 &1& 0 \end{bmatrix}=\begin{bmatrix}\frac{2n}{r-l} &0&\frac{l+r}{l-r} & 0\\ 0 &\frac{2n}{t-b}&\frac{b+t}{b-t}& 0\\ 0 &0 &\frac{n+f}{n-f}&\frac{2nf}{f-n}\\ 0 &0 &1& 0 \end{bmatrix} rl20000tb20000nf20lrl+rbtb+tfnf+n1 n0000n0000n+f100nf0 = rl2n0000tb2n00lrl+rbtb+tnfn+f100fn2nf0

【UnityShader入门精要学习笔记】第四章(5)坐标空间_第24张图片

如果转化为Unity中的参数,则有 A s p e c t = r − l t − b , t a n F O V 2 = t − b 2 n , c o t F O V 2 = 2 n t − b , F a r = f , N e a r = n , Aspect=\frac{r-l}{t-b},tan{\frac{FOV}{2}}=\frac{t-b}{2n},cot{\frac{FOV}{2}}=\frac{2n}{t-b},Far=f,Near=n, Aspect=tbrl,tan2FOV=2ntb,cot2FOV=tb2n,Far=f,Near=n,,由于以摄像头坐标为原点,因此 l = − r , b = − t l = -r,b= -t l=r,b=t。(注意,由于Unity中用于描述的坐标值是左手坐标系,而观察空间为右手坐标系,应当对z轴取反)。

因此上式转化为:

[ 2 n r − l 0 0 0 0 2 n t − b 0 0 0 0 n + f n − f 2 n f f − n 0 0 1 0 ] \begin{bmatrix}\frac{2n}{r-l} &0&0 & 0\\ 0 &\frac{2n}{t-b}&0& 0\\ 0 &0 &\frac{n+f}{n-f}&\frac{2nf}{f-n}\\ 0 &0 &1& 0 \end{bmatrix} rl2n0000tb2n0000nfn+f100fn2nf0
【UnityShader入门精要学习笔记】第四章(5)坐标空间_第25张图片
【UnityShader入门精要学习笔记】第四章(5)坐标空间_第26张图片


此时,如果一个顶点在视锥内,那么满足:

− w < = x < = w − w < = y < = w − w < = z < = w -w<=x<=w \newline -w<=y<=w \newline -w<=z<=w w<=x<=ww<=y<=ww<=z<=w

【UnityShader入门精要学习笔记】第四章(5)坐标空间_第27张图片
【UnityShader入门精要学习笔记】第四章(5)坐标空间_第28张图片


举个例子

以上图的透视投影为例,计算鼻子是否会被齐次坐标裁剪:

【UnityShader入门精要学习笔记】第四章(5)坐标空间_第29张图片
【UnityShader入门精要学习笔记】第四章(5)坐标空间_第30张图片


屏幕空间

现在我们把坐标转换到了齐次裁剪坐标系,并根据对xyz和w分量的判断,得知了那些需要被裁剪。现在我们可以真正的实现投影到屏幕空间(screen space) 了。

由于屏幕空间是二维空间,因此我们需要从齐次裁剪空间投影到二维空间,一下子要降两个维度了。这个过程分为两步:

  • 进行标准齐次除法(homogeneous division),也称为透视除法(perspective division)。这一步很简单,其实就是对齐次坐标系的xyzw分量分别除以w分量,使得w分量变为1即可将其投射回三维空间,这个新得到的坐标空间被我们称为归一化的设备坐标NDC
  • 现在从齐次裁剪空间转换到了NDC,我们会得到分量在[-1,1]的单位正方体。然后我们在渲染管线的几何化阶段使用这个NDC的坐标来计算屏幕空间上的坐标即可,只需简单缩放就可以完成。

【UnityShader入门精要学习笔记】第四章(5)坐标空间_第31张图片
【UnityShader入门精要学习笔记】第四章(5)坐标空间_第32张图片
由于Unity中,从裁剪空间到屏幕空间的转换是底层完成的,因此我们的顶点着色器只需要将顶点转换到齐次裁剪空间即可。

MVP变换

上述空间转换过程被我们称为一个MVP变换,M代表model,V代表view,P代表projection投影。

【UnityShader入门精要学习笔记】第四章(5)坐标空间_第33张图片
顶点着色器最基本的任务就是将顶点从模型空间转换到裁剪空间,也就是需要在顶点着色器中实现MVP变换。

你可能感兴趣的:(学习,笔记,算法)