HarmonyOS 时域 AI 超分:用 Vulkan 让多帧融合变清晰
HarmonyOS 时域 AI 超分:用 Vulkan 让多帧融合变清晰
什么是时域 AI 超分
前面我们介绍了空域超分和空域 AI 超分,它们都是基于单帧图像来放大。但还有一种更厉害的超分方式:时域 AI 超分。它不仅看当前帧,还会利用前面几帧的信息来重建高清图像。
打个比方:空域超分就像一个画家看着一张小照片画大画;时域超分则像一个画家同时看好几张角度略有不同的小照片,然后综合所有信息画出一张更清晰的大画。因为有多帧信息,细节会更丰富,抗锯齿效果也更好。
时域超分的一个关键概念是"相机抖动"(Jitter)。每帧渲染时,相机的采样位置会微微偏移一点,这样连续几帧就能采集到不同位置的像素信息。这些信息融合起来,就相当于用更高的采样率来渲染,画质自然更好。
环境搭建
硬件要求
- 设备类型:请参考 XEngine 开发指南的硬件要求
软件要求
- DevEco Studio 版本:DevEco Studio 6.0.0 Release 及以上
- HarmonyOS SDK 版本:HarmonyOS 6.0.0 Release SDK 及以上
搭建步骤
- 安装 DevEco Studio:去华为开发者官网下载安装
- 配置开发环境:确保网络环境正常
- 设备调试:使用真机进行调试
项目结构
├── entry/src/main // 代码区
│ ├── cpp
│ │ ├── types
│ │ │ └── libnativerender
│ │ │ └── index.d.ts // native层接口注册文件
│ │ ├── napi_init.cpp // native api层接口的具体实现函数
│ │ ├── CMakeLists.txt // native层编译配置
│ │ ├── 3rdParty // 三方件
│ │ ├── common // 通用接口
│ │ ├── file // 文件管理
│ │ ├── libs // 三方动态库
│ │ ├── manager // native&arkts交互
│ │ ├── render // 渲染
│ │ └── vulkanbase // vulkan基础能力封装
│ └── ets
│ ├── entryability
│ │ └── EntryAbility.ts // 程序入口类
│ └── pages
│ └── index.ets // 主界面展示类
└── resources // 资源文件目录
├── base
│ └── media
│ └── icon.png // 图片资源
└── rawfile
└── Sponza
└── sponza.obj // 模型资源
第一步:引入头文件
#include <algorithm>
#include <string>
#include <vector>
#include "xengine/xeg_vulkan_temporal_upscale.h"
#include "xengine/xeg_vulkan_extension.h"
和空域 AI 超分类似,但用的是时域超分的头文件 xeg_vulkan_temporal_upscale.h。
第二步:配置 CMakeLists.txt
find_library(
xengine-lib
xengine
)
target_link_libraries(nativerender PUBLIC
${hilog-lib} ${libace-lib} ${libnapi-lib} ${libuv-lib} libnative_window.so libc++.a libktx librawfile.z.so libassimp ${xengine-lib})
这里链接了 xengine 库以及其他必要的依赖库。
第三步:查询设备是否支持
VkPhysicalDevice physicalDevice;
std::vector<std::string> supportedExtensions;
uint32_t pPropertyCount;
HMS_XEG_EnumerateDeviceExtensionProperties(physicalDevice, &pPropertyCount, nullptr);
if (pPropertyCount > 0) {
std::vector<XEG_ExtensionProperties> pProperties(pPropertyCount);
if (HMS_XEG_EnumerateDeviceExtensionProperties(physicalDevice, &pPropertyCount,
&pProperties.front()) == VK_SUCCESS) {
for (auto ext : pProperties) {
supportedExtensions.push_back(ext.extensionName);
}
}
}
if (std::find(supportedExtensions.begin(), supportedExtensions.end(), XEG_TEMPORAL_UPSCALE_EXTENSION_NAME) ==
supportedExtensions.end()) {
exit(1); //return error;
}
和其他 Vulkan 扩展一样,先查询设备支持的扩展列表,然后检查是否支持时域 AI 超分。注意这里的扩展名是 XEG_TEMPORAL_UPSCALE_EXTENSION_NAME。
第四步:创建时域 AI 超分实例
XEG_TemporalUpscale xegTemporalUpscale;
声明一个时域 AI 超分实例句柄。
uint32_t highResWidth = 800;
uint32_t highResHeight = 600;
uint32_t lowResWidth = 1200;
uint32_t lowResHeight = 900;
const uint32_t jitterNum = 8;
VkDevice device;
定义一些参数:
highResWidth、highResHeight:输入图像的尺寸(低分辨率渲染的结果)lowResWidth、lowResHeight:输出图像的尺寸(超分后的高分辨率)jitterNum:相机抖动的周期数,表示每 8 帧为一个循环device:Vulkan 逻辑设备
XEG_TemporalUpscaleCreateInfo createInfo;
createInfo.inputSize = {highResWidth, highResHeight};
createInfo.outputSize = {lowResWidth, lowResHeight};
createInfo.outputFormat = VK_FORMAT_R8G8B8A8_UNORM;
createInfo.jitterNum = jitterNum;
createInfo.isDepthReversed = true;
配置创建参数:
inputSize:输入图像的尺寸outputSize:输出图像的尺寸outputFormat:输出图像的颜色格式,VK_FORMAT_R8G8B8A8_UNORM表示 RGBA 四通道,每个通道 8 位jitterNum:相机抖动的周期数isDepthReversed:深度值是否反转。有些渲染管线的深度值是反转的(近处值大,远处值小),设为true表示需要反转
VkResult res = HMS_XEG_CreateTemporalUpscale(device, &createInfo, &xegTemporalUpscale);
if (res != VK_SUCCESS) {
exit(1); //return error;
}
调用 HMS_XEG_CreateTemporalUpscale 创建实例。如果创建失败,退出程序。
第五步:计算相机抖动(Jitter)
时域超分的核心之一是相机抖动。每帧渲染时,相机的采样位置会微微偏移,这样连续几帧就能采集到不同位置的像素信息。
Halton 序列
Halton 序列是一种低差异序列,生成的点分布很均匀。我们用它来计算每帧的抖动量。
float GetHaltonSequence(uint32_t index, uint32_t base) {
float result = 0.0;
float fraction = 1.0 / base;
while (index > 0) {
result += fraction * (index % base);
index /= base;
fraction /= base;
}
return result;
}
这个函数生成 Halton 序列的第 index 个值,base 是基数。用 base=2 生成 X 方向的抖动,用 base=3 生成 Y 方向的抖动。
计算每帧的 Jitter 值
uint64_t frameNum = 0;
float jitterX = 0.0;
float jitterY = 0.0;
frameNum 是当前帧数,需要每帧加 1。jitterX 和 jitterY 是当前帧的抖动量。
jitterX = GetHaltonSequence((frameNum % jitterNum) + 1, 2) - 0.5;
jitterY = GetHaltonSequence((frameNum % jitterNum) + 1, 3) - 0.5;
先用 Halton 序列生成 [0, 1] 范围的值,然后减去 0.5,变成 [-0.5, 0.5] 范围。
jitterX = jitterX / highResWidth;
jitterY = jitterY / highResHeight;
再除以输入图像的分辨率,得到 UV 坐标下的 Jitter 值。这样抖动量就和图像尺寸成比例了。
Jitter 计算的整体流程如下:
第六步:执行时域 AI 超分
XEG_TemporalUpscaleDescription xegDescription;
VkImageView inputImageView = VK_NULL_HANDLE;
VkImageView motionVectorImageView = VK_NULL_HANDLE;
VkImageView depthImageView = VK_NULL_HANDLE;
VkImageView dynamicMaskImageView = VK_NULL_HANDLE;
VkImageView outputImageView = VK_NULL_HANDLE;
VkCommandBuffer commandBuffer = VK_NULL_HANDLE;
定义超分描述结构体和各种图像视图:
inputImageView:输入图像(低分辨率渲染结果)depthImageView:深度图像,记录每个像素离相机有多远motionVectorImageView:运动矢量图像,记录每个像素的运动方向和距离dynamicMaskImageView:动态遮罩图像,标记哪些物体是运动的outputImageView:输出图像(超分后的高分辨率结果)
xegDescription.inputImage = inputImageView;
xegDescription.depthImage = depthImageView;
xegDescription.motionVectorImage = motionVectorImageView;
xegDescription.dynamicMaskImage = dynamicMaskImageView;
xegDescription.outputImage = outputImageView;
把各种图像设置到描述结构体中。
xegDescription.jitterX = -jitterX;
xegDescription.jitterY = -jitterY;
设置抖动值。注意这里取了负号,因为抖动是应用在采样坐标上的,方向相反。
xegDescription.resetHistory = (frameNum == 0) ? true : false;
设置是否重置历史帧数据。第一帧时设为 true,因为没有历史数据可用。之后的帧设为 false,让引擎使用历史数据来提升画质。
xegDescription.steadyLevel = 0.5;
设置画面偏向当前帧还是历史帧的平衡程度。取值范围 [0.0, 1.0]:
- 值越小,越偏向当前帧,画面更"新鲜",但可能有噪点
- 值越大,越偏向历史帧,画面更稳定,但可能有拖影
- 0.5 是一个平衡值
HMS_XEG_CmdRenderTemporalUpscale(commandBuffer, xegTemporalUpscale, &xegDescription);
最后调用 HMS_XEG_CmdRenderTemporalUpscale 执行时域 AI 超分。这个命令会被记录到命令缓冲区里,等 GPU 执行时才会真正运行。
时域 AI 超分的执行过程涉及多个输入数据的协同处理:
第七步:销毁实例
if (xegTemporalUpscale) {
HMS_XEG_DestroyTemporalUpscale(xegTemporalUpscale);
}
不需要时域 AI 超分功能时,销毁实例,释放内存资源。
时域 AI 超分的完整工作流程如下:
时域超分和空域超分的区别
你可能会问:时域超分和空域超分有什么区别?
| 方面 | 时域超分 | 空域超分 |
|---|---|---|
| 输入数据 | 多帧图像 + 深度 + 运动矢量 | 单帧图像 |
| 抗锯齿 | 有,效果很好 | 没有 |
| 画质 | 更好,细节更丰富 | 一般 |
| 性能开销 | 更大 | 较小 |
| 实现复杂度 | 更高,需要管理历史帧 | 较低 |
简单来说:时域超分效果更好,但更复杂、开销更大。如果你的应用对画质要求很高,而且能承受额外的性能开销,时域超分是更好的选择。
适用场景
时域 AI 超分特别适合以下场景:
- 3D 游戏:画面复杂,有很多运动物体,时域超分能有效抗锯齿
- VR/AR 应用:对画质和帧率都有很高要求
- 视频渲染:连续帧之间有很强的时域相关性,超分效果最好
注意事项
- 设备支持:不是所有设备都支持时域 AI 超分,一定要先查询扩展
- Jitter 采样范围:Jitter 的分布范围要足够大,才能有效减轻锯齿
- 运动矢量:运动矢量要准确,否则超分结果会有重影
- 历史帧管理:第一帧要重置历史数据,否则会有错误的融合结果
- steadyLevel 调节:要根据画面运动程度来调整,运动剧烈时要偏向当前帧
- 深度值方向:注意
isDepthReversed的设置,不同渲染管线的深度值方向可能不同
总结
时域 AI 超分是一个更高级的超分技术,它利用多帧信息来重建高清图像。核心流程:
- 查询设备是否支持时域 AI 超分
- 创建时域 AI 超分实例,配置参数
- 每帧计算 Jitter 值(使用 Halton 序列)
- 准备输入数据(颜色图、深度图、运动矢量、动态遮罩)
- 调用
HMS_XEG_CmdRenderTemporalUpscale执行超分 - 不需要时销毁实例
如果你对画质有很高要求,时域 AI 超分是一个值得尝试的技术。
更多推荐



所有评论(0)