UnityShader——基础篇之学习Shader所需的数学基础——下

裁剪空间

  顶点接下来要从观察空间转换到裁剪空间(也被称为齐次裁剪空间) 中,这个用于变换的矩阵叫做裁剪矩阵,也被称为投影矩阵

  裁剪空间的目标是能够方便地对渲染图元进行裁剪:完全位于这块空间内部的图元将会被保留,完全位于这块空间外部的图元将会被剔除,而与这块空间边界相交的图元就会被裁剪。这块空间是由视锥体来决定

  视锥体指的是空间中的一块区域,这块区域决定了摄像机可以看到的空间。视锥体由六个平面鲍威尔成,这些平面也被称为裁剪平面。视锥体有两种类型,这涉及两种投影类型:一种是正交投影,一种是透视投影。下图显示了从同一位置、同一角度渲染同一个场景的两种摄像机的渲染结果

在这里插入图片描述
  从图中可以发现,在透视投影中,地板上的平行线并不会保持平行,离摄像机越近网格越大,离摄像机越远网格越小。而在正交投影中,所有的网格大小都一样,而且平行线会一直保持平行。可以注意到,透视投影模拟了人眼看世界的方式,而正交投影则完全保留了物体的距离和角度。因此,在追求真实感的3D游戏中往往会使用透视投影,而在一些2D游戏或渲染小地图等其他HUD元素时会使用正交投影

  在视锥体的六块裁剪平面中,有两块裁剪平面比较特殊,它们分别被称为近剪裁平面远剪裁平面。它们决定了摄像机可以看到的深度范围。正交投影和透视投影的视锥体如下图所示:

在这里插入图片描述
  由上图可以看出,透视投影的视锥体是一个金字塔型,侧面的四个裁剪平面将会在摄像机处相交。它更符合视锥体这个词语。正交投影的视锥体是一个长方体。前文提到,需求是根据视锥体围成的区域对图元进行裁剪,但是,如果直接使用视锥体定义的空间来进行裁剪,那么不同的视锥体就需要不同的处理过程,而且对于透视投影的视锥体来说,想要判断一个顶点是否处于一个金字塔内部是比较麻烦的。因此,需要一种更加通用、方便和整洁的方式来进行裁剪的工作,这种方式就是通过一个投影矩阵把顶点转换到一个裁剪空间中。

  投影矩阵有两个目的:

  • 首先是为投影做准备。虽然投影矩阵的名称包含了投影二字,但是它并没有进行真正的投影工作,而是在为投影做准备。真正的投影发生在后面的齐次除法过程中。而经过投影矩阵的变换后,顶点的 w w w分量将会具有特殊的意义
  • 其次是对 x , y , z x,y,z x,y,z分量进行缩放,前文说过直接使用视锥体的六个裁剪平面来进行裁剪会比较麻烦。而经过投影矩阵的缩放后,可以直接使用 w w w分量作为一个范围值,如果 x , y , z x,y,z x,y,z分量都位于这个范围内,就说明该顶点位于裁剪空间内

透视投影

  视锥体的意义在于定义了场景中的一块三维空间。所有位于这块空间内的物体将会被渲染,否则就会被剔除或裁剪。前文说到,这块区域由六个裁剪平面定义,在Unity中,它们由Camera 组件中的参数和 Game 试图的横纵比共同决定,如下图所示:

在这里插入图片描述
  由上图可以看出,通过 Camera 组建的 Field of View(简称FOV)属性来改变视锥体垂直方向的张开角度,而 Clipping Planes 中的 Near 和 Far 参数可以控制视锥体的近裁剪平面和远裁剪平面的远近。这样可以求出视锥体近裁剪平面和远裁剪平面的高度,也就是:

n e a r C l i p P l a n e H e i g h t = 2 ⋅ N e a r ⋅ tan ⁡ F O V 2 f a r C l i p P l a n e H e i g h t = 2 ⋅ F a r ⋅ tan ⁡ F O V 2 nearClipPlaneHeight = 2\cdot Near\cdot \tan\frac{FOV}{2}\\ farClipPlaneHeight=2\cdot Far\cdot\tan\frac{FOV}{2} nearClipPlaneHeight=2Neartan2FOVfarClipPlaneHeight=2Fartan2FOV

  现在还缺少缺乏横向的信息。这可以通过摄像机的横纵比得到。在Unity中,一个摄像机的横纵比由Game视图得到横纵比和Viewport Rect中的W和H属性共同决定(实际上,Unity允许在脚本里通过Camera.aspect进行更改,但这里不做讨论)。假设,当前摄像机的横纵比为Aspect,定义为:

A s p e c t = n e a r C l i p P l a n e W i d t h n e r a C l i p P l a n e H e i g h t Aspect = \frac{nearClipPlaneWidth}{neraClipPlaneHeight} Aspect=neraClipPlaneHeightnearClipPlaneWidth
A s P e c t = f a r C l i p P l a n e W i d t h f a r C l i p P l a n e H H e i g h t AsPect=\frac{farClipPlaneWidth}{farClipPlaneHHeight} AsPect=farClipPlaneHHeightfarClipPlaneWidth

  现在可以根据已知的Near、Far、FOV和Aspect的值来确定透视投影的投影矩阵。如下:

M f r u s t u m = [ c o t F O V 2 A s p e c t 0 0 0 0 c o t F O V 2 2 0 0 0 0 − F a r + N e a r F a r − N e a r − 2 ⋅ N e a r ⋅ F a r F a r − N e a r 0 0 − 1 0 ] \mathbf{M}_{frustum}=\begin{bmatrix} \frac{cot\frac{FOV}{2}}{Aspect}&0&0&0\\ 0&\frac{cot\frac{FOV}{2}}{2}&0&0\\ 0&0&-\frac{Far+Near}{Far-Near}&-\frac{2\cdot Near\cdot Far}{Far-Near}\\ 0&0&-1&0 \end{bmatrix} Mfrustum= Aspectcot2FOV00002cot2FOV0000FarNearFar+Near100FarNear2NearFar0

  需要注意的是,这里的投影矩阵是建立在Unity对坐标系的假定上面的,也就是说,针对的是观察空间和右手坐标系,使用列矩阵在矩阵右侧进行相乘,且变换后 z z z分量范围将在 [ − w , w ] [-w,w] [w,w]之间的情况。而在类似Direct X这样的图形接口中,它们希望变换后 z z z分量范围将在 [ 0 , w ] [0,w] [0,w]之间,因此就需要对上面的透视矩阵进行一个更改。这不在本文的谈论范围之内

  而一个顶点和上述投影矩阵相乘后,可以由观察空间变换到裁剪空间中,结果如下:

P c l i p = M f r u s t u m P v i e w = [ c o t F O V 2 A s p e c t 0 0 0 0 c o t F O V 2 2 0 0 0 0 − F a r + N e a r F a r − N e a r − 2 ⋅ N e a r ⋅ F a r F a r − N e a r 0 0 − 1 0 ] [ x y z 1 ] = [ x c o t F O V 2 2 y c o t F O V 2 − z F a r + N e a r F a r − N e a r − 2 ⋅ N e a r ⋅ F a r F a r − N e a r − z ] \begin{aligned} \mathbf{P}_{clip} &= \mathbf{M}_{frustum}\mathbf{P}_{view}\\ &=\begin{bmatrix} \frac{cot\frac{FOV}{2}}{Aspect}&0&0&0\\ 0&\frac{cot\frac{FOV}{2}}{2}&0&0\\ 0&0&-\frac{Far+Near}{Far-Near}&-\frac{2\cdot Near\cdot Far}{Far-Near}\\ 0&0&-1&0 \end{bmatrix}\begin{bmatrix} x\\y\\z\\1 \end{bmatrix}\\ &=\begin{bmatrix} x\frac{cot\frac{FOV}{2}}{2}\\ ycot\frac{FOV}{2}\\ -z\frac{Far+Near}{Far-Near} - \frac{2\cdot Near \cdot Far}{Far-Near}\\ -z \end{bmatrix} \end{aligned} Pclip=MfrustumPview= Aspectcot2FOV00002cot2FOV0000FarNearFar+Near100FarNear2NearFar0 xyz1 = x2cot2FOVycot2FOVzFarNearFar+NearFarNear2NearFarz

  从结果可以看出,这个投影矩阵本质就是对 x , y , z x,y,z x,y,z分量进行了不同程度的缩放(当然, z z z分量还做了一个平移),缩放的目的是为了裁剪。可以注意到,这时顶点的 w w w分量不再是1,二十原先 z z z分量的取反结果。现在就可以按如下不等式来判断一个变换后的顶点是否位于视锥体内。如果一个顶点在视锥体内,那么它变化后的坐标必须满足:

− w ≤ x ≤ w − w ≤ y ≤ w − w ≤ z ≤ w -w\leq x \leq w\\ -w\leq y \leq w\\ -w\leq z \leq w wxwwywwzw

  任何不满足上述条件的图元都需要被剔除或者裁剪。下图显示了经过上述投影矩阵后视锥体的变化

在这里插入图片描述
  在透视投影钟,投影矩阵对顶点进行了缩放。图中标注了4个关键点经过投影矩阵变换后的结果。从这些结果可以看出 x , y , z x,y,z x,y,z w w w分量的范围发生的变化

  上图还可以注意到,裁剪矩阵会改变空间的旋向性:空间从右手坐标系变换到了左手坐标系。这意味着,离摄像机越远, z z z值越大

正交投影

  和透视投影类似,在Unity中,正交投影的六个裁剪平面也是由Camera组件中的参数和Game视图的横纵比共同决定,如下图所示:

在这里插入图片描述

  正交投影的视锥体是一个长方体,因此计算上相比透视投影来说更加简单。由上图可以看出,可以通过Camera组件的Size属性来改变视锥体竖直方向上高度的一般,而Clipping Planes中的Near和Far参数可以控制是重罪提的近裁剪平面和远裁剪平面距离摄像机的远近。这样就可以求出视锥体近裁剪平面和远裁剪平面的高度,也就是:

n e a r C l i p P l a n e H e i g h t = 2 ⋅ S i z e f a r C l i p P l a n e H e i g h t = n e a r C l i p P l a n e H e i g h t nearClipPlaneHeight=2\cdot Size\\ farClipPlaneHeight=nearClipPlaneHeight nearClipPlaneHeight=2SizefarClipPlaneHeight=nearClipPlaneHeight

  现在还缺乏横向的信息。同样可以通过摄像机的横纵比得到。假设,当前摄像机的横纵比为Aspect,那么:

n e a r C l i p P l a n e W i d t h = A s p e c t ⋅ n e a r C l i p P l a n e H e i g h t f a r C l i p P l a n e W i d t h = n e a r C l i p P l a n e W i d t h nearClipPlaneWidth=Aspect\cdot nearClipPlaneHeight\\ farClipPlaneWidth=nearClipPlaneWidth nearClipPlaneWidth=AspectnearClipPlaneHeightfarClipPlaneWidth=nearClipPlaneWidth

  现在可以根据已知的Near、Far、Size和Aspect的值来确定正交投影的裁剪矩阵。如下:

M o r t h o = [ 1 A s p e c t ⋅ S i z e 0 0 0 0 1 S i z e 0 0 0 0 − 2 F a r − N e a r − F a r + N e a r F a r − N e a r 0 0 0 1 ] \mathbf{M}_{ortho}=\begin{bmatrix} \frac{1}{Aspect\cdot Size}&0&0&0\\ 0&\frac{1}{Size}&0&0\\ 0&0&-\frac{2}{Far-Near}&-\frac{Far+Near}{Far-Near}\\ 0&0&0&1 \end{bmatrix} Mortho= AspectSize10000Size10000FarNear2000FarNearFar+Near1

  同样,这里的投影是建立在 Unity 对坐标系的假定上面的

  一个顶点和上述投影相乘后的结果如下:

P c l i p = M o r t h o P v i e w = [ 1 A s p e c t ⋅ S i z e 0 0 0 0 1 S i z e 0 0 0 0 − 2 F a r − N e a r − F a r + N e a r F a r − N e a r 0 0 0 1 ] [ x y z 1 ] = [ x A s p e c t ⋅ S i z e y S i z e − 2 z F a r − N e a r − F a r + N e a r F a r − N e a r 1 ] \begin{aligned} \mathbf{P}_{clip}&=\mathbf{M}_{ortho}\mathbf{P}_{view}\\ &=\begin{bmatrix} \frac{1}{Aspect\cdot Size}&0&0&0\\ 0&\frac{1}{Size}&0&0\\ 0&0&-\frac{2}{Far-Near}&-\frac{Far+Near}{Far-Near}\\ 0&0&0&1 \end{bmatrix}\begin{bmatrix} x\\y\\z\\1 \end{bmatrix}\\ &=\begin{bmatrix} \frac{x}{Aspect\cdot Size}\\\frac{y}{Size}\\-\frac{2z}{Far-Near}-\frac{Far+Near}{Far-Near}\\1 \end{bmatrix} \end{aligned} Pclip=MorthoPview= AspectSize10000Size10000FarNear2000FarNearFar+Near1 xyz1 = AspectSizexSizeyFarNear2zFarNearFar+Near1

  可以注意到,和透视投影不同的是,使用正交投影的投影矩阵对顶点进行变换后,其 w w w分量仍然为1。本质是因为投影矩阵最后一行的不同,透视投影的投影矩阵的最后一行是 [ 0 0 − 1 0 ] \begin{bmatrix}0&0&-1&0\end{bmatrix} [0010]。这样的选择是有原因的,是为了为齐次除法做准备。

  判断一个变换后的顶点是否位于视锥体内使用的不等式和透视投影中的一样,这种通用性也是为什么要使用投影矩阵的原因之一,下图显示了经过上述投影矩阵后,正交投影的视锥体的变化

在这里插入图片描述
  同样,裁剪矩阵改变了空间的旋向性。可以注意到,经过正交投影变换后的顶点实际已经位于一个立方体内了

屏幕空间

  经过投影矩阵的变换后,可以进行裁剪操作。当完成了所有的裁剪工作后,就需要进行真正的投影了,也就是说需要把视锥体投影到屏幕空间中。经过这一步变换会得到真正的像素位置,而不是虚拟的三维坐标

  屏幕空间是一个二维空间,因此必须把顶点从裁剪空间投影到屏幕空间中,来生成对应的2D坐标。这个过程可以理解成有两个步骤

  首先需要进行标准齐次除法,也被称为透视除法。虽然这个步骤听起来很陌生,但是他实际上非常简单,也就是用齐次坐标系的 w w w分量去除 x , y , z x,y,z x,y,z分量,在OpenGL中,把这一步得到的坐标叫做归一化的设备坐标。经过这一步,可以把坐标从齐次裁剪坐标空间转换到NDC中。经过透视投影变换后的裁剪空间,经过齐次除法后变换到一个立方体内。按照 OpenGL 的传统,这个立方体的 x , y , z x,y,z x,y,z分量的范围都是[-1,1]。但在 DirectX 这样的 API 中, z z z分量的范围会是[0,1]。而Unity选择了OpenGL这样的齐次裁剪空间,如下图所示

在这里插入图片描述
  对于正交投影来说,它的裁剪空间实际已经是一个立方体了,而且由于经过正交投影矩阵变换后的顶点的 w w w分量是1,因此齐次除法并不会对顶点的 x , y , z x,y,z x,y,z坐标产生影响。如下图所示

在这里插入图片描述
  经过齐次除法后,透视投影和正交投影的视锥体都变换到一个相同的立方体内。现在可以根据变换后的 x x x y y y坐标来映射输出窗口的对应像素坐标

  在Unity中,屏幕空间的左下角的像素坐标是(0,0),右上角的像素是(pixelWidth,pixelHeight)。由于当前 x x x y y y坐标都是[-1,1]。因此这个映射的过程就是一个缩放的过程

  齐次除法和屏幕映射的过程可以使用下面的公式来总结:

s c r e e n x = c l i p x ⋅ p i x e l W i d t h 2 ⋅ c l i p w + p i x e l W i d t h 2 s c r e e n y = c l i p y ⋅ p i x e l H e i g h t 2 ⋅ c l i p w + p i x e l H e i g h t 2 screen_x=\frac{clip_x\cdot pixelWidth}{2\cdot clip_w}+\frac{pixelWidth}{2}\\ screen_y=\frac{clip_y\cdot pixelHeight}{2\cdot clip_w}+\frac{pixelHeight}{2} screenx=2clipwclipxpixelWidth+2pixelWidthscreeny=2clipwclipypixelHeight+2pixelHeight

  通常 z z z分量会被用于深度缓冲。一个传统的方式是把 c l i p z c l i p w \frac{clip_z}{clip_w} clipwclipz的值直接存进深度缓冲中,但这并不是必须的。通常驱动生产商会根据硬件来选择最好的存储格式。此时 c l i p w clip_w clipw也不会被抛弃,尽管它已经完成了它的主要工作——在齐次除法中作为分母来得到NDC,但它仍然会在后续的一些工作中起到重要的作用,例如进行透视矫正插值

  在Unity中,从裁剪空间到屏幕空间的转换是由底层实现的,顶点着色器只需要把顶点转换到裁剪空间即可

总结

  下图总结了这些空间和用于变换的矩阵

在这里插入图片描述
  顶点着色器的最基本的任务就是把顶点坐标从模型空间转换到裁剪空间中。这对应了上图中的前三个顶点变换过程。而在片元着色器中通常也可以得到该片元在屏幕空间的像素位置。

  在Unity中,坐标系的旋向性也随着变化发生了改变。下图总结了Unity中各个空间使用的坐标系旋向性

在这里插入图片描述
  上图可以发现,只有在观察空间中Unity使用了右手坐标系

  需要注意的是,这里仅仅给出的是一些最重要的坐标空间。还有一些空间在实际开发中也会遇到,例如切线空间。切线空间通常用于法线映射

法线变换

  法线,也被称为法矢量,法线是需要特殊处理的一种方向矢量。在游戏中,模型的一个顶点往往会携带额外的信息,而顶点法线就是其中一种信息。当变换一个模型的时候,不仅需要变换它的顶点,还需要变换顶点法线,以便在后续处理(如片元着色器)中计算光照等

  一般来说,点和绝大部分方向矢量都可以使用 4 × 4 4\times 4 4×4 3 × 3 3\times 3 3×3的变换矩阵 M A − > B \mathbf{M}_{A->B} MA>B把其从坐标空间 A \mathbf{A} A变换到坐标空间 B \mathbf{B} B中。但在变换法线的时候,如果使用同一个变换矩阵,可能就无法确保维持发现的垂直性

  另一种方向适量——切线,也被称为切矢量。与法线类似,切线往往也是模型顶点携带的一种信息。它通常与纹理空间对齐,而且与法线方向垂直,如下图所示

在这里插入图片描述
  由于切线是由两个顶点之间的差值计算得到的,因此可以直接使用用于变换顶点的变换矩阵来变换切线。建设使用 3 × 3 3\times 3 3×3的变换矩阵 M A − > B \mathbf{M}_{A->B} MA>B来变换顶点(注意,这里涉及的变换矩阵都是 3 × 3 3\times 3 3×3的矩阵,不考虑平移变换。这是因为切线和法线都是方向矢量,不会受平移的影响),可以由下面的式子直接得到变换后的切线:

T B = M A − > B T A \mathbf{T}_B=\mathbf{M}_{A->B}\mathbf{T}_A TB=MA>BTA

  其中 T A \mathbf{T}_A TA T B \mathbf{T}_B TB分别表示在坐标空间A下和坐标空间B下的切线方向。但如果直接使用 M A − > B \mathbf{M}_{A->B} MA>B来变换法线,得到的新的发现方向可能就不会与表面垂直了,如下图所示

在这里插入图片描述
  应该使用哪个矩阵来变换法线呢?可以由数学约束条件来推出这个矩阵。因为同一个顶点的切线 T A \mathbf{T}_A TA和法线 N A \mathbf{N}_A NA必须满足垂直条件,即 T A ⋅ N A \mathbf{T}_A\cdot\mathbf{N}_A TANA。给定变换矩阵 M A − > B \mathbf{M}_{A->B} MA>B,已经知道 T B = M A − > B T A \mathbf{T}_B=\mathbf{M}_{A->B}\mathbf{T}_A TB=MA>BTA。现在想要找到一个矩阵 G \mathbf{G} G来变换法线 N A \mathbf{N}_A NA,使得变换后的法线仍然与切线垂直。即:

T B ⋅ N B = ( M A − > B T A ) ⋅ ( G N A ) = 0 \mathbf{T}_B\cdot\mathbf{N}_B=(\mathbf{M}_{A->B}\mathbf{T}_A)\cdot(\mathbf{GN}_A)=0 TBNB=(MA>BTA)(GNA)=0

  对上式进行一些推导后可得

( M A − > B T A ) ⋅ ( M A − > B T A ) T ( G N A ) = T A T M A − > B T G N A = T A T ( M A − > B T G ) N A = 0 (\mathbf{M}_{A->B}\mathbf{T}_A)\cdot(\mathbf{M}_{A->B}\mathbf{T}_A)^T(\mathbf{GN}_A)=\mathbf{T}_A^T\mathbf{M}_{A->B}^T\mathbf{GN}_A=\mathbf{T}_A^T(\mathbf{M}_{A->B}^T\mathbf{G})\mathbf{N}_A=0 (MA>BTA)(MA>BTA)T(GNA)=TATMA>BTGNA=TAT(MA>BTG)NA=0

  由于 T A ⋅ N A \mathbf{T}_A\cdot\mathbf{N}_A TANA,因此如果 M A − > B T G = I \mathbf{M}_{A->B}^T\mathbf{G}=\mathbf{I} MA>BTG=I,那么上式即可成立。也就是说,如果 G = ( M A − > B − 1 ) T \mathbf{G}=(\mathbf{M}_{A->B}^{-1})^T G=(MA>B1)T,即使用原变换矩阵的逆转矩阵来变换法线就可以得到正确的结果

  值得注意的是,如果变换矩阵 M A − > B \mathbf{M}_{A->B} MA>B是正交矩阵,那么 M A − > B − 1 = M A − > B T \mathbf{M}_{A->B}^{-1}=\mathbf{M}_{A->B}^T MA>B1=MA>BT,因此 ( M A − > B T ) − 1 = M A − > B (\mathbf{M}_{A->B}^T)^{-1}=\mathbf{M}_{A->B} (MA>BT)1=MA>B,也就是说可以使用用于变换顶点的变换矩阵来直接变换法线。如果变换只包含旋转变换,那么这个变换矩阵就是正交矩阵。而如果变换只包含旋转和统一缩放,而不包含非统一缩放,利用统一缩放系数 k k k来得到变换矩阵 M A − > B \mathbf{M}_{A->B} MA>B的逆转置矩阵 ( M A − > B T ) − 1 = 1 k M A − > B (\mathbf{M}_{A->B}^T)^{-1}=\frac{1}{k}\mathbf{M}_{A->B} (MA>BT)1=k1MA>B。这样就可以避免计算逆矩阵的过程。如果变换中包含了非统一变换,那么就必须要求解逆矩阵来得到变换法线的矩阵

Unity Shader 的内置变量(数学篇)

变换矩阵

  首先是用于坐标空间变换的矩阵。下表给出了Unity5.2版本提供的所有内置变换矩阵。下面所有的矩阵都是 float 4 × 4 4\times 4 4×4 类型的

Unity 内置的变换矩阵
变量名描述
UNITY_MATRIX_MVP当前的模型观察投影矩阵,用于将顶点/方向矢量从模型空间变换到裁剪空间
UNITY_MATRIX_MV当前的模型观察矩阵,用于将顶点/方向矢量从模型空间变换到观察空间
UNITY_MATRIX_V当前的观察矩阵,用于将顶点/方向矢量从世界空间变换到观察空间
UNITY_MATRIX_P当前的投影矩阵,用于将顶点/方向矢量从观察空间变换到裁剪空间
UNITY_MATRIX_VP当前的观察投影矩阵,用于将顶点/方向矢量从世界空间变换到裁剪空间
UNITY_MATRIX_T_MVUNITY_MATRIX_MV的转置矩阵
UNITY_MATRIX_IT_MVUNITY_MATRIX_MV的逆转置矩阵,用于将法线从模型空间变换到观察空间,也可用于得到UNITY_MATRIX_MV的逆矩阵
_Object2World当前的模型矩阵,用于将顶点/方向矢量从模型空间变换到世界空间
_World2Object_Object2World的逆矩阵,用于将顶点/方向矢量从世界空间变换到模型空间

摄像机和屏幕参数

  Unity提供了一些内置变量来允许访问当前正在渲染的摄像机参数信息。这些参数对应了摄像机上的Camera组件中的属性值。下表给出了Unity5.2 版本提供的这些变量

Unity内置的摄像机和屏幕参数
变量名类型描述
_WorldSpaceCameraPosfloat3该摄像机在世界空间中的位置
_ProjectionParamsfloat4 x x x=1.0(或-1.0,如果正在使用一个反转的投影矩阵进行渲染), y = N e a r , z = F a r , w = 1.0 + 1.0 / F a r y=Near,z=Far,w=1.0+1.0/Far y=Nearz=Far,w=1.0+1.0/Far,其中Near和Far分别是近裁剪平面和远裁剪平面和摄像机的距离
_ScreenParamsfloat4 x = w i d t h , y = h e i g h t , z = 1.0 + 1.0 / h e i g h t x=width,y=height,z=1.0+1.0/height x=width,y=height,z=1.0+1.0/height,其中 width 和 height 分别是该摄像机的渲染目标的像素宽度和高度
_ZBufferParamsfloat4 x = 1 − F a r / N e a r , y = F a r / N e a r , z = x / F a r , w = y / F a r x=1-Far/Near,y=Far/Near,z=x/Far,w=y/Far x=1Far/Near,y=Far/Near,z=x/Far,w=y/Far,该变量用于线性化 Z 缓存中的深度值
unity_OrthoParamsfloat4 x = w i d t h , y = h e i g h t , z x=width,y=height,z x=width,y=height,z没有定义, w = 1.0 w=1.0 w=1.0(该摄像机是正交摄像机)或 w = 0.0 w=0.0 w=0.0(该摄像机是透视摄像机),其中width和height是正交投影摄像机的宽度和高度
unity_CameraProjectionfloat4 x 4该摄像机的投影矩阵
unity_CameraInvProjectionfloat4 x 4该摄像机的投影矩阵的逆矩阵
unity_CameraWorldClipPlanes[6]float4该摄像机的6个裁剪平面在世界空间下的等式,按如下顺序:左、右、下、上、近、远裁剪平面

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/57749.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[Redis] Redis数据持久化

🌸个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 🏵️热门专栏: 🧊 Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm1001.2014.3001.5482 🍕 Collection与…

【设计模式系列】装饰器模式

目录 一、什么是装饰器模式 二、装饰器模式中的角色 三、装饰器模式的典型应用场景 四、装饰器模式在BufferedReader中的应用 一、什么是装饰器模式 装饰器模式是一种结构型设计模式,用于在不修改对象自身的基础上,通过创建一个或多个装饰类来给对象…

数据结构与算法分析:你真的理解排序算法吗——计数排序(代码详解)

一、算法描述 一个会计师负责对一个小饭店的账本进行审核。每天晚上饭店打洋时,饭店主人记录白 天的总销售额,然后打印出有总额和日期的收据。这些收据存放在一个大盒子里面.每 年年终,会计师审核盒子中的这些收据,检查是否有的已…

Java.6--多态-设计模式-抽象父类-抽象方法

一、多态 1.定义--什么是多态? a.同一个父类的不同子类对象,在做同一行为的时候,有不同的表现形式,这就是多态。(总结为:一个父类下的不同子类,同一行为,不同表现形式。&#xff0…

【力扣】GO解决子序列相关问题

文章目录 一、引言二、动态规划方法论深度提炼子序列问题的通用解法模式 三、通用方法论应用示例:最长递增子序列(LeetCode题目300)Go 语言代码实现 四、最长连续递增序列(LeetCode题目674)Go 语言代码实现 五、最长重…

目前最新 Reflector V11.1.0.2067版本 .NET 反编译软件

目前最新 Reflector V11.1.0.2067版本 .NET 反编译软件 一、简介二、.NET Reflector的主要功能包括:1. **反编译**: 反编译是将已编译的.NET程序集(如.dll或.exe文件)转换回可读的源代码。这使得开发者可以查看和学习第三方库的实现细节&…

手机淘宝自动下单退货自动化RPA脚本机器人

使用手机集线器连接多个手机并发运行。 脚本分3个部分(读取本地连接下单,退货获取退货地址信息,填写快递单号) 脚本部分图结构看下面的图片 部分数据统计展示

基于vue框架的的高校设备信息管理系统的设计与实现tx6d7(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。

系统程序文件列表 项目功能:设备管理员,设备维护员,设备类别,设备,设备入库,设备分发,设备调拨,定期维护,维护任务,设备运行记录 开题报告内容 基于Vue框架的高校设备信息管理系统的设计与实现开题报告 一、项目背景及意义 随着高校教育事业的蓬勃发展&#xff…

线性代数学习

1.标量由只有一个元素的张量表示 import torchx torch.tensor([3,0]) y torch.tensor([2,0])x y, x * y, x / y, x**y 2.可以将向量视为标量值组成的列表 x torch.arange(4) x 3.通过张量的索引访问任一元素 x[3] 4.访问张量长度 len(x) 5.只有一个轴的张量&#xff0c…

gin入门教程(10):实现jwt认证

使用 github.com/golang-jwt/jwt 实现 JWT(JSON Web Token)可以有效地进行用户身份验证,这个功能往往在接口前后端分离的应用中经常用到。以下是一个基本的示例,演示如何在 Gin 框架中实现 JWT 认证。 目录结构 /hello-gin │ ├── cmd/ …

Could not retrieve mirrorlist http://mirrorlist.centos.org错误解决方法

文章目录 背景解决方法 背景 今天在一台新服务器上安装nginx,在这个过程中需要安装相关依赖,在使用yum install命令时,发生了以下报错内容: Could not retrieve mirrorlist http://mirrorlist.centos.org/?release7&archx8…

【永中软件-注册/登录安全分析报告】

前言 由于网站注册入口容易被黑客攻击,存在如下安全问题: 暴力破解密码,造成用户信息泄露短信盗刷的安全问题,影响业务及导致用户投诉带来经济损失,尤其是后付费客户,风险巨大,造成亏损无底洞…

第二十九篇:TCP的报文格式,TCP系列三

TCP是协议,是协议就有协议格式。 1.源端口和目的端口 TCP源端口(Source Port):源计算机上的应用程序的端口号,占 16 位。 TCP目的端口(Destination Port):目标计算机的应用程序端口…

政安晨【零基础玩转各类开源AI项目】基于本地Ubuntu (Linux ) 系统应用Gradio-Lite:无服务器 Gradio 完全在浏览器中运行

目录 简介 什么是@gradio/lite? 入门 1.导入 JS 和 CSS 2. 创建标签 3. 在标签内编写你的 Gradio 应用程序 更多示例:添加其他文件和要求 多个文件 其他要求 SharedWorker 模式 代码和演示playground 1.无服务器部署 2.低延迟 3. 隐私和安全 限制 尝试一下!…

基于Python+SQL Server2008实现(GUI)快递管理系统

快递业务管理系统的设计与实现 摘要: 着网络新零售的到来,传统物流在网购的洗礼下迅速蜕变,在这场以互联网为基础的时代变革中,哪家企业能率先转变其工作模式就能最先分得一杯羹,物流管理也不例外。传统的物流管理模式效率低下&a…

同城分类信息网站源码系统 PHP+MySQL组合开发 带完整的安装代码包以及搭建部署教程

系统概述 该系统采用PHP作为后端开发语言,MySQL作为数据库管理系统。PHP是一种广泛使用的开源脚本语言,特别适合于Web开发,具有跨平台、易于学习、性能稳定等优点。MySQL则是一款轻量级的关系型数据库管理系统,具有体积小、速度快…

DHorse v1.6.0 发布,基于 k8s 的发布平台

版本说明 新增特性 支持Codeup(阿里云云效)代码仓库;支持环境的自动部署; 优化特性 管理员角色部署环境部需要审批;优化页面展示; 升级指南 升级指南 DHorse介绍 DHorse是一个轻量级、简单易用的云…

微服务网关Zuul

一、Zuul简介 Zuul是Netflix开源的微服务网关,包含对请求的路由和过滤两个主要功能。 1)路由功能:负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础。 2)过滤功能:负责对请求的过程…

crc, md5 和 sha的区别

效率不同: 直接看代码 import zlib import hashlib import timewith open(rD:\data., rb) as f:x f.read()s time.time() for i in range(100000):d zlib.crc32(x) print(time.time() - s)s time.time() for i in range(100000):m hashlib.md5()m.update(x)d m.hexdige…

【CSS】纯CSS Loading动画组件

<template><div class"ai-loader-box"><!-- AI loader --><div class"ai-loader"><div class"text"><p>AI智能分析中....</p></div><div class"horizontal"><div class&quo…