Skip to content

Latest commit

 

History

History
246 lines (108 loc) · 7.08 KB

22 WebGPU空间体系:z 坐标的奥秘.md

File metadata and controls

246 lines (108 loc) · 7.08 KB

22 WebGPU空间体系:z 坐标的奥秘

本文将学习 WebGPU 中的 3D 空间体系,预计通过 2 篇文章,来全面了解 从原始的顶点坐标 如何一步一步映射为屏幕坐标。


WebGPU中的XYZ坐标系

在之前的文章中,我们简单提到过 WebGPU 的坐标系,再来复习一下。

无论此时你看的是手机屏幕,还是电脑显示器,我们假设它是个正方形,那么:

**X轴:**屏幕的水平方向为 x 轴,最左侧 x 值为 -1,中间位置 x 值为 0,最右侧 x 值为 1

x 的取值范围为 -1 到 1,即 -1<= x <= 1

**Y轴:**屏幕的上下方向为 y 轴:最上面 y 值为 1,中间位置 y 值为 0,最下面 y 值为 -1

y 的取值范围为 -1 到 1,即 -1<= y <= 1

coordinate_xy

**Z轴:**想象有一个长度为 1 的线,它从屏幕正中间以垂直的方式向屏幕背后延伸,这根线为 z 轴。紧贴屏幕的那一端 z 值为 0,远离屏幕的那一端 z 值为 1

z 的取值范围为 0 到 1,即 0<= z <=1

coordinate_z


补充说明:在很多 3D 软件中,例如 Three.js 或 Blender,通常约定

  1. X 轴使用 红色线 表示
  2. Y 轴使用 绿色线 表示
  3. Z 轴使用 蓝色线 表示

刚好就是 RGB 中的 Red、Green、Blue 这 3 个颜色顺序。

这样无论当前 XYZ 轴如何旋转,我们都可以通过颜色快速对它们进行区分。


值的大小与显示屏的位置关系:

对于 x,y,z 而言,其值越接近于 0 则表示它们越接近屏幕的中心点。

屏幕的中心点位置,相当于这套坐标系的原点,即原点坐标为 (0,0,0)。


上面这段关于 WebGPU xyz 空间坐标体系的讲解,实际上理解起来并不难。

但是我知道你心中可能一直有一个疑惑,那就是:

为什么这套坐标体系和 Three.js 中的坐标体系不一样?


学习过 Three.js 的人都知道,Three.js 采用右手坐标系:

  1. 伸出右手,五指张开,让手掌垂直于屏幕,掌心朝向自己
  2. 蜷缩住小拇指、无名指
  3. 伸直中指并指向自己,此时 中指垂直于 食指

那么就构成了一个右手 3D 空间坐标体系:

  1. 大拇指方向为 x 正轴方向
  2. 食指方向为 y 轴正轴方向
  3. 中指方向为 z 轴正轴方向

右手 3D 空间坐标系 与 我们本文学习的 WebGPU 坐标系最明显的不同点在于 z 轴。

这两套坐标系的 z 轴都是垂直于屏幕,但:

  1. 右手坐标系中的 z 轴既朝着屏幕前延伸,也朝着屏幕后延伸,而 WebGPU 中的 z 轴仅为 朝着 屏幕后 延伸

  2. 对于 Three.js 中的坐标系中 x,y,z 的值,取值范围都是 负无穷大 到 正无穷大,而 WebGPU 中他们的取值是有范围的。

    再说一遍:x,y 取值范围 -1<= x, y <=1,z 取值范围 0<= z <=1


“屏幕”这个词仅仅是为我们为了方便空间想象而使用的,你可以把 “屏幕” 替换成 “坐标系原点” 再重新读一遍。


WebGPU 中的这套坐标系是它独创的吗?

不是的,3D 引擎或软件底层几乎都采用这种坐标系,例如 虚幻引擎、Unity。


这种空间坐标系有一个正经的名称:标准化设备坐标系(Normalized Device Coordinates),简称 NDC。

当然 Normalized 也可以翻译为 “归一化”,因此我们也可以将 NDC 称呼为 “归一化设备坐标系”。


就我个人喜好而言,我更倾向于使用 “归一化设备坐标系” 这个称呼。


如果在我以后的文章中出现 “NDC坐标”,你明白它是指 “归一化设备坐标系中的一个坐标”。

不过我个人写作风格是能用中文就不用英文。


我们暂时先停止对 归一化设备坐标系 的讲解,回到我们之前写过的那个绘制三角形的示例中。


在之前绘制一个简单三角形的示例中,假设我把顶点的 z 值由 0 修改为其他值,那绘制结果会发生什么变化呢?

请记得我们之前说过,z 值的取值范围应该是 0<= z <= 1

如果你把之前绘制三角形的 z 值由 0.0 修改为 0.5 或者 1,那是不是更远离屏幕,绘制的三角形会显得更小一些呢?

实际运行你会发现 绘制出来的三角形 无论是位置还是大小,都没有任何变化!


为什么???


因为对于 WebGPU 默认的 归一化设备坐标系 而言,他执行的是 正交投影。


透视投影:就像是我们人眼看到的那样,物体 远小近大。

正交投影:物体无论远近,其投影的大小是相同的。


这就是为什么刚才我们修改了 顶点的 z 值,但是最终呈现的大小却没有丝毫变化。

那怎么设置才可以让 z 值生效?

答:在顶点着色器中,通过矩阵转换,将原本的 正交投影 转换成 透视投影,此时表示前后深度的 z 坐标就会生效并体现出来。


这里引申出一个问题:顶点着色器通常用来做什么事情?

这是我们想成为 着色器高手的第一个正式问题


顶点着色器通常用来承担以下几个任务:

  1. 接收传递进来的顶点坐标(模型数据)
  2. 将接收到的顶点数据进行适当的转换,并最终输出转化后的顶点信息
  3. 顶点信息除了坐标外,还可以包含其他信息,例如:纹理UV坐标,插值、以及其他要传递给片元着色的数据

其中 “适当的转换” 就包括我们上面提到的 通过矩阵转换 将 正交投影 变为 透视投影。

目前我们还未接触 相机(camera),我们知道通过更改相机角度可以改变最终渲染的视图结果。

而 相机 本质就是一种矩阵转换。


我们反思另外一个问题:WebGPU 中 xyz 的坐标值究竟取值范围是多少?我们之前反复说的取值范围 -1<= x,y <=1,0<= z <=1 是对的吗?

答:错误的,根本不是那么回事。


之前提到的 -1<= x,y <=1,0<= z <=1 这种取值范围,它只是 归一化设备坐标系(NDC) 下,我们希望顶点完全可见情况下的安全取值范围。

如果我们设置的坐标值超出了所谓的安全取值范围,那么多出来的地方就会被裁切掉,看不见,WebGPU 也不会报错,仅此而已。


但是,假设我们不再是直接将 NDC 坐标直接渲染,而是在顶点着色器中增加了适当的转换(例如矩阵转换),那么意味着写入到顶点缓冲区的 xyz 坐标值是多大都行,根本没有取值范围一说。


至此,我们有 2 个急迫的问题想知道答案:

  1. 怎么设置可以让 z 坐标生效,让最终渲染呈现出 透视效果?
  2. 顶点着色器中如何添加 矩阵转换?

这两个问题实际上是同一个问题:正交投影如何转化为透视投影?


本文到此结束,精彩就在下一篇文章中。