张掖市网站建设_网站建设公司_响应式开发_seo优化
2025/12/23 21:20:55 网站建设 项目流程

【技术美术】渲染空间变换概述

GPU渲染管线中,我们只要知道如何在屏幕上画画就行,在一个2D坐标系上表示2D物体是很简单的。但在引擎渲染管线中,为了在屏幕上显示一个仿真的游戏世界,该任务就变成了如何将3D画面,映射到2D屏幕中。

为此人们发明了一套空间变换流程,利用多个矩阵来实现3D到2D的变换。(提示:矩阵=空间=变换,不同的术语只是从不同的角度理解而已)

渲染空间

渲染空间

渲染管线中的空间变换共经历以下几个空间:

  1. 物体空间(模型空间):模型文件的坐标系,以每个模型的自身为准。
  2. 世界空间:场景中的坐标系,所有物体都需要放在该空间。
  3. 视图空间(观察空间):以相机视角为准的坐标系。
  4. 裁剪空间(DNC 标准设备坐标系):图形接口绘图所用的坐标系。

其中“物体空间”、“世界空间”、“视图空间”是引擎开发者自己定义的空间,这些空间通常都是由 TRS 矩阵表示的,其矩阵参数跟随物体的变换状态而改变。

“裁剪空间”则是确实存在于图形接口的一个固定范围的方形空间,用于对应到实际的输出附件(如自定义附件或屏幕使用的附件)。在不同的图形接口中,以及根据接口使用者的设置,其空间参数各种各样

物体空间

在游戏引擎中,顶点不再是屏幕空间,而是基于物体空间的,物体空间就是以物体自身为世界原点的坐标系。因为游戏中,大部分物体是复用的,或需要实时变换的,所以建模师都是以物体为单位来建模,自然的这样建出来的模型顶点,都是以模型的原点为基点。

物体空间是基于TRS矩阵实现的,利用物体的位置、旋转、缩放即可制作一个TRS矩阵:

【技术美术】TRS矩阵

世界空间

由于模型本身的顶点是不考虑其他物体的,但物体间的交互、相对位置是存在的,因此需将其统一到一个新的空间坐标系,即“世界空间”,这样子才能批量统一的处理网格数据,并使不同物体间的数据可以互通。

世界空间对应没有矩阵实现,因为世界空间是最根源的基空间,物体空间的基向量也是基于世界空间的,所以硬要说的话,世界空间等于单位矩阵。

视图空间

游戏是仿真的,有了被观察的物体,自然需要观察他们的眼睛,而视图空间就是以眼睛为坐标系的空间。因为最终的屏幕画面是相对眼睛的,所以需要将所有物体先转到视图空间,然后才能方便的转换到裁剪空间。

就视图空间本身来讲,其实不过是相机的物体空间的花哨说法,所以其就是一个物体空间,只是拿的是相机的位置、旋转、缩放。但要注意的是,Unity 的视图空间是基于 OpenGL 的,和默认的左手坐标系相比,其 Z 轴是翻转的!

剪辑空间(NDC空间)

剪辑空间和NDC空间本身是同一种空间,只是其空间下坐标的表示方式不同,前者表示坐标是基于齐次坐标,后者则是齐次坐标归一化的结果。齐次坐标主要是为了兼容深度测试、以及TRS矩阵,使顶点可以很方便的实现位移、深度除法等计算。

NDC空间就是GPU渲染管线中讲到的,存在于GPU中的“2D”空间坐标系(当然他实际上是3D的,有一个额外的z轴来实现深度测试的功能),它相当于是最终的屏幕空间坐标系。NDC空间根据图形引擎的不同、用户设置的不同,其设定也不一样。

以屏幕中心为参考坐标系,常见的一些 NDC 空间定义如下:

图形接口 X 轴 Y 轴 Z 轴 z 与深度关系
默认 D3D 向右 (-1,1) 向上 (-1,1) 向前 (0,1) 越大越深
默认 Vulkan 向右 (-1,1) 向下 (-1,1) 向前 (0,1) 越大越深
Unity OpenGL(Unity 默认) 向右 (-1,1) 向上 (-1,1) 向后 (-1,1) 越小越深
Unity D3D 向右 (-1,1) 向上 (-1,1) 向前 (1,0) 越小越深

提示:表格内容表示轴向和值域。

在 Unity 中官方默认推荐的形式是“Unity OpenGL”,因此当切换到与其不一致的环境时,需要利用宏进行特殊处理,具体见后文。

渲染空间变换

从一个空间变换到下一个空间,都是由矩阵实现的,具体实现方式如下:

  • 物体到世界:就是物体空间矩阵本身。
  • 世界到视图:就是相机的物体空间矩阵的逆矩阵。
  • 视图到裁剪(投影变换):分正交和透视两种变换方案,具体见投影变换章节。

物体到世界

物体到世界变换矩阵等价于物体空间矩阵,因此直接用即可。

世界到视图

视图空间相当于把所有世界中的物体重新以相机的坐标系来表示。如果把相机也看成物体,那该矩阵恰巧是“物体到世界”的反向操作,所以只要把矩阵求逆,另外再加上z轴翻转即可。

视图到剪辑

【技术美术】投影矩阵和线性深度推导

NDC空间兼容性

前文说到 NDC 空间变体很多,这在 Unity 中也是一样,因此在不同的图像接口环境下,会出现一些特殊情况:

z轴翻转

在D3D平台,z轴是01翻转的,即从近到远为 (1,0)。越近的z越大,越远的z越小,这很反直觉,不过这种翻转的深度可以更好的利用浮点数的分布情况,所以被广泛使用。在Unity中可以使用UNITY_REVERSED_Z来判断是否是翻转z轴的平台。

y轴翻转

在D3D平台,如果在片段着色器使用NDC坐标,会发现y轴是翻转的,为什么?

这和Unity读写纹理的方式有关,因为除了NDC空间,哪怕纹理坐标在不同图形接口下也是不一致的,OpenGL中纹理空间以左下角为零点,而D3D中以左上角为原点。那为什么在Unity中我们一直都是直接用OpenGL的方式读写纹理,却不会导致颠倒?

因为Unity在D3D平台会故意颠倒存储纹理。这种方式同样应用于渲染纹理,正好渲染纹理无论是作为材质输入,还是复制到屏幕,使用时都要进行采样,颠倒存储加颠倒采样,负负得正反而对了。

因此如果你在着色器里半路截胡,就会发现Unity为了颠倒存储而翻转了y轴。对于翻转y轴的平台,同样也可以利用对应的宏 UNITY_UV_STARTS_AT_TOP 来判断。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询