UE5 材质伪球体:用可平铺贴图伪造行星表面

引言
如果我告诉你,这个星球实际上只是一张带有普通可平铺纹理的平面卡片,你会怎么想?场景中没有球形网格,也没有预先准备成球形贴图的纹理。几何体从不弯曲,也不会变成半球。整个体积感的错觉都是在材质中创建的:World Position Offset(世界位置偏移)让平面卡片始终面向摄像机,而经过变换的 UV 则让图像看起来像是映射到了曲面上。这个效果源自 TYBALT 的研发,这是一款由 Kamishibai Interactive 开发的即将推出的单人叙事驱动射击游戏,灵感来自《命运》、《马拉松》、《女巫之火》、《SOMA》和《网络奇兵》。
星球视觉的目标之一是让美术师能够使用程序化和可平铺纹理,而无需将它们转换为球形贴图,也无需为每个变体专门准备球形 UV 布局。在探索如何让该工作流更灵活的过程中,我开发了这种基于材质的技术。在本文中,我将拆解该实验背后的材质和蓝图逻辑。
在材质中创建伪球体
为了扭曲纹理,我使用了一个半球的球极平面投影。首先,将卡片 UV 围绕卡片中心重新映射,并把它们当作面向摄像机的半球的 X 和 Y 坐标。材质根据这两个坐标重建第三个坐标:该点在一个半球上应具有的深度。
然后,这个 3D 半球位置会被球极平面投影到用于采样纹理的 2D 坐标中。这些坐标在中心附近只会略微变化,但越靠近边缘,扭曲就越强。在边缘附近,这会压缩图像,使其看起来像是包裹在一个背离观察者弯曲的凸面上。网格本身并未变形。材质只是改变了用于采样一张普通可平铺 2D 纹理的坐标。仅此一点就足以营造出基本的体积错觉。
世界位置偏移的作用
世界位置偏移只有一个任务:让扁平卡片始终面向摄像机。该材质利用摄像机朝向,在相应的摄像机基中重建顶点位置。即使行星在画面中偏离中心,卡片也保持平坦且与屏幕平行。我在文章UE5 中基于材质的网格旋转:数学与逻辑中更详细地描述了这项技术。
处理纹理移动
相机需要围绕星球自由环绕,同时纹理的表现要如同真正附着在 3D 表面上一样。星球本身也可以旋转,因此该系统必须对相机移动和星球朝向的变化都做出一致响应。
在远离两极的区域,这种移动很容易拆解为两个 UV 偏移。沿赤道的移动可以用水平偏移表示,而沿子午线的移动则成为垂直偏移。然而在两极附近,仅靠偏移已经不够了。图像还必须在卡片上旋转。
纹理移动使用偏移和旋转的组合。垂直偏移处理沿子午线的移动。在赤道附近,水平移动也主要靠偏移来处理,但当相机接近极点时,该偏移逐渐失去影响,而围绕计算出的枢轴的旋转则接管。
为什么变换必须累积
相机可以沿两条不同的路径从点 A 移动到点 D。
在第一种情况下,相机直接沿赤道移动。在整个路径上,水平偏移承担了大部分工作,在 D 点我们看到纹理的某个特定区域。
在第二种情况下,相机先沿一条经线从 A 移动到 B。这一段主要由垂直偏移表示,因此纹理逐渐平移。随后它沿一条靠近极点的上方纬线从 B 移动到 C。在这里,围绕可见极点位置的旋转开始占据主导。最后,相机沿另一条经线从 C 向下移动到 D。
尽管最后这一段同样对应垂直移动,但纹理已经被路径前面的部分平移和旋转过了。新的垂直移动不能简单地应用到原始 UV 上,而必须在已经旋转过的坐标系中加入,同时保持当前的纹理位置不变。
两条路径最终都让相机停在同一个 D 点,但它们显示的却是纹理的不同部分。这并不是真正对球体进行 3D 旋转,而是用可平铺的 2D 纹理构建出的一种近似。
由于最终的纹理状态取决于路径,系统必须记住当前状态,并在其基础上应用每一次新的更改。
材质不会在帧与帧之间保留这种状态,因此我将该过程拆分到蓝图和材质中。蓝图保存移动历史,而材质接收最终累积的 2D 变换。
确定纹理移动
蓝图会存储两类信息。第一,累积的纹理偏移和旋转。第二,相对于星球的上一次相机位置数据:相机位于赤道上方或下方多远,以及它正在观察星球的哪一侧。每次更新时,蓝图都会将之前的状态与当前状态进行比较,确定视点如何变化,并将相应的偏移或旋转添加到累积变换中。
蓝图首先计算从星球中心到相机的方向,并将其变换到 Actor 的局部空间。这使得纹理移动不仅取决于相机位置,还取决于星球当前的朝向。然后,该方向会被拆分为纬度和经度。
纬度基于相机方向相对于局部 +Z 轴向上或向下指向的程度。当前纬度与存储纬度之间的差值会成为垂直纹理偏移。
对于经度,首先通过移除相机方向沿极轴的分量,将其投影到赤道平面上。剩下的就是一个水平方向,显示相机正从星球的哪一侧进行观察。蓝图会将其与上一次更新时的方向进行比较,并测量它们之间绕局部 +Z 轴的角度。该角度的符号给出移动方向,而其大小则给出经度的变化量。
靠近极点时,赤道投影会变得非常短,而在极点本身处则会完全消失。所有子午线都在那里交汇,因此经度无法再被可靠地确定。在该区域,系统会暂时停止更新经度,并保留最后一个有效的赤道方向。保留最后一个有效方向可以防止相机经过极地区域时纹理发生随机跳变。
经度变化的使用方式因纬度而异。远离两极时,它主要产生水平方向的偏移。当相机接近极点时,该偏移的影响会减弱,经度分量逐渐转变为纹理旋转。
垂直偏移符号
在当前版本中,本地 +Z 被用作南北极轴,而本地 +X 定义初始经度方向。当相机环绕到行星顶部或底部上方时,其屏幕空间的上方向可能最终指向与行星本地 +Z 轴相反的方向。如果纬度变化保持相同的符号,纹理就会开始以错误的方向在屏幕上移动。
蓝图将 CameraUp 与行星本地 +Z 轴的世界空间方向进行比较。当它们指向相反方向时,它会反转垂直偏移。这样在相机越过极点后,沿子午线的运动在视觉上保持连续。
寻找旋转轴心
为了让纹理旋转看起来正确,系统需要知道极点位于卡片上的哪个位置。通过将「从星球中心指向相机的方向」与星球的 Local Z 进行比较,系统可以判断相机位于哪个半球,从而选出可见的极点方向:即纹理应围绕其旋转的北极或南极。
选定的极点方向首先被变换到相机基空间中(该基空间直接由相机朝向构建,且始终与屏幕平行),得到的点即对应极点在屏幕上的位置。随后,使用与材质创建伪球面 UV 时相同的立体投影原理,将该点投影到卡片的 2D 空间中。
最终得到一个 2D 点,它表示可见极点在卡片上的当前位置,并成为纹理旋转的轴心。该轴心并非固定不变:当相机移动或 Actor 旋转时,它会持续重新计算,并将累积的偏移与旋转考虑在内。
组合偏移与旋转
新的偏移和旋转不能简单地分别加到之前的值上。如果纹理已经旋转过,那么下一次偏移必须遵循它当前的朝向。由于相机移动、Actor 旋转以及已经累积的偏移,可见的极点位置也可能发生变化。
系统将整个运动历史存储为单个累积的 2D 变换。它由总旋转角度和相对于 UV 原点的总平移组成。
例如,纹理可以绕某个点 A 旋转 30°。同样的结果也可以通过以下方式表示:先绕 (0, 0) 旋转 30°,再应用一个平移,将其移回相同的最终位置。
首先,系统会在应用现有的旋转和平移之后,确定当前极点位于纹理中的哪个位置。然后,它计算当前帧的微小经度和纬度变化。
新的偏移量最初是在原始 UV 轴上计算的。由于纹理可能已经旋转过,因此在将该偏移方向加入总 Translation 之前,会先按累计角度对其进行旋转。
如果部分经度变化需要在极点附近转换为旋转,系统会添加一个微小的旋转角度,同时调整平移,使旋转围绕计算出的极点位置进行。
每次更新后,结果都会再次以相同的统一形式存储,材质则接收最终状态:
- RotationTurns:围绕 UV 原点累计的总旋转量;
- Translation:总二维偏移,既包括常规的纹理移动,也包括此前所有围绕不同极点位置的旋转所引入的平移。
简化版本
该效果被拆分为几个独立的层:
- 世界位置偏移(World Position Offset)使平面卡片始终面向摄像机。
- 材质中的 UV 变换营造出体积的错觉。
- 蓝图负责连续表面运动:它跟踪摄像机移动和 Actor 旋转,存储累积的表面状态,并将 LocalOffset 和 RotationTurns 传递给材质。
我还有一个简化版本,只使用水平和垂直偏移。它不会累积状态,不会在两极附近旋转纹理,也不需要任何单独的蓝图逻辑。所有的移动都直接在材质中计算。
简化版本非常适合远处的背景行星、装饰性物体、UI 场景,以及其他相机在有限范围内移动、且从不会越过两极的情况。当你只需要轻微的表面运动感,而不需要纹理保持其之前的精确轨迹时,它也同样有用。
完整版本更适合相机可以围绕物体自由环绕、接近极点,或者当旋转行星本身必须保持纹理连续运动时使用。简化版本灵活性较低,但设置起来容易得多,并且完全保留在材质内部。
这种效果在哪里有用
纹理不需要提前准备成球面贴图。
对于普通球体,表面图像必须匹配球体的 UV 布局。而在这里,材质可以使用任意常规的可平铺 2D 纹理。它不需要转换为等距柱状投影贴图,也不需要专门为两极和经线做适配。你可以直接使用现有的可平铺纹理、程序化图案,或快速搭建的材质作为行星表面。
相机仍然可以环绕物体并从不同侧面观察它。随着纹理的偏移和旋转,它会营造出图像实际附着在 3D 表面上的印象。
该效果通过移动纹理来模拟行星可见侧的变化,但并不会创建真正的球面几何体。因此,最终的纹理状态取决于相机的移动历史。两条不同的路径可能最终停在相同的相机位置,却显示纹理的不同区域。这种路径依赖性正是底层 2D 模拟的一部分。
Alina Ledeneva,UE5 技术美术
相关链接
原文链接:https://80.lv/articles/faking-a-spherical-planet-with-a-tileable-texture-in-ue5
喜欢这篇内容吗?