unity shader development[4]

56 篇文章 2 订阅

变换坐标空间

  在过去的章节中,我们多次提到坐标空间之间的变换,但我们将更详细的解释推迟到本章。

  如第一章所述,理论上您可以选择每个计算将驻留在哪个坐标空间中。在实时渲染的情况下,图形管道已经为您做出了大部分选择。它期望值(顶点位置、法线等)在过程的某些步骤中位于某个坐标空间中。

  上一章中的一个例子是,当顶点着色器输出顶点位置时,它应该是在剪辑空间中。这就是为什么我们将v2f数据结构中的顶点设置为UnityObjectToClipPos(v.vertex)。

  本章介绍了图形管道中常用的每个空间。对于每个,我们将介绍如何转换它们以及它们通常在哪些管道阶段使用。

坐标空间谁是谁

  让我们先来介绍一下实时渲染中常用的每一个坐标空间。我们将考虑六个空间。对象空间、世界空间、相机空间、剪辑空间、归一化设备坐标和屏幕空间。

  这主要是它们在图形管道中的使用顺序。传递给顶点着色器的数据在对象空间中,其中一些需要转换到世界空间(用于照明计算),其中一些将继续转换到相机空间,最后到剪辑空间(顶点位置)等

Object Space

  我们要讨论的第一个坐标空间是对象空间(见图 4-1)。对象空间是一个 3D 坐标系,其原点通常设置在输入组装阶段发送到渲染管道的网格的底部或中心。原点可能是用于对网格进行建模的 3D 建模软件中的枢轴点。

在这里插入图片描述
  它也可以称为局部空间或模型空间。但是,在 Autodesk Maya 中,局部空间用于指代不同的坐标系,因此请注意其中可能存在的混淆。当顶点位置存入appdata时,为了传递给顶点着色器,它在对象空间中。

World Space

继续,接下来是世界空间。在世界空间中,参考框架不是单个网格,而是整个场景(见图4-2)。

在这里插入图片描述
  原点在世界空间中的具体位置取决于你如何将场景放在一起。在Unity中,这是你的Unity场景所处的空间,在你的GameObjects变换中使用的坐标应该是在这个坐标空间。

空间之间的变换

  为了在不同坐标空间之间进行转换,通常使用矩阵乘法。矩阵的一个便利特性是它们可以组合。因此,一个矩阵可以让我们一路从对象空间到剪辑空间。

  Unity 包含许多内置函数,它们为某些类型的值实现了最常用的变换,例如位置、法线、光线方向等。以下是一些将在 World Space 中获得某个值的函数:

  • float3 UnityObjectToWorldDir( in float3 dir ) 在对象空间中取一个方向并将其转换为世界空间中的一个方向
  • float3 UnityObjectToWorldNormal( in float3 norm ) 将对象空间中的法线转换为世界空间中的法线;对光照计算很有用
  • float3 UnityWorldSpaceViewDir( in float3 worldPos ) 在世界空间中取顶点位置并返回世界空间中的视图方向;对光照计算很有用
  • float3 UnityWorldSpaceLightDir( in float3 worldPos ) 在世界空间中取一个顶点位置并返回世界空间中的光方向;对光照计算很有用

  这些函数隐藏了实现细节,您只需要关心将正确的值传递给它们并在适当的地方使用它们。

  如前所述,从一个坐标空间到另一个坐标空间的转换是通过使用特定矩阵来完成的。如果您在听到“矩阵”这个词时退缩,使用这些实用函数可以实现很多目标,而不必过多担心矩阵。

  多亏了它们,您通常不再需要用矩阵处理这些转换,即使这仍然在幕后发生。您可以在 UnityShaderVariables.cginc、UnityShaderUtilities.cginc 和 UnityCG.cginc 中找到许多可用于在空间之间转换坐标的函数。

  您可以更多地动手并直接使用矩阵,自己设置矩阵乘法。在此用例中,Unity 包含许多可用于空间变换的内置矩阵,但与 OpenGL 等中的相同选项相比,它们具有不同的名称。

  以下是一些用于从对象空间和到对象空间的转换的内置 Unity 矩阵:

  • unity_ObjectToWorld,这是一个从Object Space转换到World Space的矩阵
  • unity_WorldToObject,上面的逆,是从World Space到Object Space的变换矩阵

  例如,让我们将顶点位置从对象空间转换到世界空间:

float4 vertexWorld = mul(unity_ObjectToWorld, v.vertex);

  我们基本上是将我们的矩阵和我们想要转换的值传递给 mul 函数。如果没有内置矩阵可以满足您的需要,您也可以构建自己的矩阵。

Camera Space

  接下来是Camera Space,也称为Eye Space 或View Space(见图4-3)。此坐标空间包含与世界空间坐标系相同的场景,但从您渲染的相机的角度来看。
在这里插入图片描述
  需要相机空间,因为这是输出剪辑空间的必要步骤,但这主要由标准着色器基础设施负责。
  相机空间有几个内置矩阵:

  • unity_WorldToCamera,从世界空间转换到相机空间
  • unity_CameraToWorld,上面的逆,从Camera Space转换到World Space

还有一个内置函数:

  • float4 UnityViewToClipPos( in float3 pos ) 将位置从视图空间转换为裁剪空间
Clip Space

  我们要讨论的下一个坐标空间是 Clip Space(见图 4-4)。

在这里插入图片描述
  渲染管线的顶点后处理阶段包括裁剪。剪裁会移除不在剪裁空间边界内的任何基元部分。剪辑空间坐标范围从 -1 到 1。

  这里发生了一件不直观的事情。您可能希望我们到目前为止列出的坐标空间(包括剪辑空间)具有三个坐标——一个用于 x 轴,一个用于 y 轴,一个用于 z 轴。但是在 OpenGL(和其他 API)中,我们列出的 3D 空间不使用三个坐标;他们使用四个:(x、y、z 和 w)。

  这个w坐标是怎么来的,为什么要用它?事实证明,使用直角坐标空间进行三维渲染有一个问题:两条平行线不能相互交汇,这使得它无法表现透视。为了解决这个问题,这个额外的坐标是必要的,这被称为齐次坐标。我们把这个额外的坐标w保留下来,然后在适当的时候(从剪辑空间到归一化设备坐标),我们把其他所有的坐标都除以它。这样做,我们就可以表示透视。

  因此,对象、世界、视图和剪辑空间使用这四个坐标表示 3D 空间。在除剪辑空间之外的所有空间中,w 都是 1。然后,w 被用于从视图转换到剪辑空间的矩阵更改为一个不同于 1 的数字。该矩阵在 OpenGL 中称为投影矩阵。然后 w 坐标用于确定是否应裁剪顶点。

  要设置投影矩阵,您需要使用来自视域(也称为截头锥体)的信息。视锥体因您要使用的投影类型而异;常见的有透视投影和正交投影。

  在透视投影中(见图 4-5),截头体由一个近平面和一个远平面组成,其中近平面小于远平面,因为距离相机较远的物体在透视中会显得更小。视野定义了近平面和远平面之间的比例。改变它会改变我们要渲染的场景量。

在这里插入图片描述
Clip Space 没有内置矩阵,但有一些内置函数:

  • float4 UnityWorldToClipPos( in float3 pos ),将位置从世界空间转换到剪辑空间

  • float4 UnityViewToClipPos( in float3 pos ),将位置从 View Space 转换为 Clip Space

  • float4 UnityObjectToClipPos(in float3 pos),将一个顶点位置从Object Space转换到Clip Space

Normalized Device Coordinates

  接下来是标准化设备坐标 (NDC)。这是一个独立于特定屏幕或图像分辨率的 2D 空间。 NDC中的坐标是通过将Clip space除以w得到的,这个过程叫做透视分割。同样,在 OpenGL 中,NDC 坐标范围从 -1 到 1。 NDC 使用三个数字而不是两个数字,正如您所期望的那样,但在这种情况下,z 坐标用于深度缓冲区,而不是一个齐次坐标。

Screen Space

  最后,屏幕空间(见图 4-6)是 2D 渲染目标的坐标空间。这可能是屏幕缓冲区、单独的渲染目标或图像。它是通过将 NDC 转换和缩放为视口分辨率而获得的。最后,这些顶点的屏幕坐标被传递给光栅化器,光栅化器将使用它们来生成片段。

在这里插入图片描述

在内置函数之下

  到目前为止,本书已经提到了许多内置函数,现在我们更详细地看看其中一个是如何工作的。来自上一章的示例着色器:

v2f vert (appdata v)
{
	v2f o;
	o.vertex = UnityObjectToClipPos(v.vertex);
    o.color = v.color;
    return o;
}

  UnityObjectToClipPos 是一个代表 mul(UNITY_MATRIX_MVP,*) 的函数,它是一个矩阵乘法,从对象空间到剪辑空间。矩阵通常通过彼此相乘来组合,因此 MVP 代表模型矩阵 * 视图矩阵 * 投影矩阵。换句话说,我们从对象空间通过世界和视图空间快速前进,仅使用这一行就可以到达剪辑空间坐标。

  在下一章中,您将看到更多在坐标空间之间转换值的有用应用示例。

总结

  本章介绍了图形管道和着色中常用的各种坐标空间,并讨论了在它们之间进行转换的方法。它还显示了 Unity 中已经包含哪些函数可以在它们之间进行转换。

  下一章从基础开始,开始学习光照着色器的旅程。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值