HarmonyOS 时域 AI 超分:用 Vulkan 让多帧融合变清晰
2026/6/23 1:10:11 网站建设 项目流程

HarmonyOS 时域 AI 超分:用 Vulkan 让多帧融合变清晰

什么是时域 AI 超分

前面我们介绍了空域超分和空域 AI 超分,它们都是基于单帧图像来放大。但还有一种更厉害的超分方式:时域 AI 超分。它不仅看当前帧,还会利用前面几帧的信息来重建高清图像。

打个比方:空域超分就像一个画家看着一张小照片画大画;时域超分则像一个画家同时看好几张角度略有不同的小照片,然后综合所有信息画出一张更清晰的大画。因为有多帧信息,细节会更丰富,抗锯齿效果也更好。

时域超分的一个关键概念是"相机抖动"(Jitter)。每帧渲染时,相机的采样位置会微微偏移一点,这样连续几帧就能采集到不同位置的像素信息。这些信息融合起来,就相当于用更高的采样率来渲染,画质自然更好。

环境搭建

硬件要求

  • 设备类型:请参考 XEngine 开发指南的硬件要求

软件要求

  • DevEco Studio 版本:DevEco Studio 6.0.0 Release 及以上
  • HarmonyOS SDK 版本:HarmonyOS 6.0.0 Release SDK 及以上

搭建步骤

  1. 安装 DevEco Studio:去华为开发者官网下载安装
  2. 配置开发环境:确保网络环境正常
  3. 设备调试:使用真机进行调试

项目结构

├── 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_tpPropertyCount;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(autoext: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_thighResWidth=800;uint32_thighResHeight=600;uint32_tlowResWidth=1200;uint32_tlowResHeight=900;constuint32_tjitterNum=8;VkDevice device;

定义一些参数:

  • highResWidthhighResHeight:输入图像的尺寸(低分辨率渲染的结果)
  • lowResWidthlowResHeight:输出图像的尺寸(超分后的高分辨率)
  • 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 序列是一种低差异序列,生成的点分布很均匀。我们用它来计算每帧的抖动量。

floatGetHaltonSequence(uint32_tindex,uint32_tbase){floatresult=0.0;floatfraction=1.0/base;while(index>0){result+=fraction*(index%base);index/=base;fraction/=base;}returnresult;}

这个函数生成 Halton 序列的第index个值,base是基数。用 base=2 生成 X 方向的抖动,用 base=3 生成 Y 方向的抖动。

计算每帧的 Jitter 值

uint64_tframeNum=0;floatjitterX=0.0;floatjitterY=0.0;

frameNum是当前帧数,需要每帧加 1。jitterXjitterY是当前帧的抖动量。

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 计算的整体流程如下:

获取当前帧号 frameNum

计算帧号对 jitterNum 取模

用 Halton 序列 base=2 生成 X 方向值

用 Halton 序列 base=3 生成 Y 方向值

减去 0.5 映射到 -0.5, 0.5

除以输入分辨率得到 UV 坐标偏移

取负号作为最终 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 超分的执行过程涉及多个输入数据的协同处理:

输入图像 低分辨率渲染结果

HMS_XEG_CmdRenderTemporalUpscale

深度图像 像素到相机距离

运动矢量图像 像素运动方向与距离

动态遮罩图像 标记运动物体

Jitter 值 相机抖动偏移

输出图像 高分辨率超分结果

历史帧数据

steadyLevel 平衡参数

第七步:销毁实例

if(xegTemporalUpscale){HMS_XEG_DestroyTemporalUpscale(xegTemporalUpscale);}

不需要时域 AI 超分功能时,销毁实例,释放内存资源。

时域 AI 超分的完整工作流程如下:

查询设备是否支持时域AI超分

创建时域AI超分实例

配置输入输出尺寸和参数

每帧计算 Jitter 值

准备输入数据

设置 Jitter 和稳态参数

是否为首帧?

重置历史帧数据

使用历史帧融合

执行时域AI超分

输出高分辨率图像

时域超分和空域超分的区别

你可能会问:时域超分和空域超分有什么区别?

方面时域超分空域超分
输入数据多帧图像 + 深度 + 运动矢量单帧图像
抗锯齿有,效果很好没有
画质更好,细节更丰富一般
性能开销更大较小
实现复杂度更高,需要管理历史帧较低

简单来说:时域超分效果更好,但更复杂、开销更大。如果你的应用对画质要求很高,而且能承受额外的性能开销,时域超分是更好的选择。

适用场景

时域 AI 超分特别适合以下场景:

  • 3D 游戏:画面复杂,有很多运动物体,时域超分能有效抗锯齿
  • VR/AR 应用:对画质和帧率都有很高要求
  • 视频渲染:连续帧之间有很强的时域相关性,超分效果最好

注意事项

  1. 设备支持:不是所有设备都支持时域 AI 超分,一定要先查询扩展
  2. Jitter 采样范围:Jitter 的分布范围要足够大,才能有效减轻锯齿
  3. 运动矢量:运动矢量要准确,否则超分结果会有重影
  4. 历史帧管理:第一帧要重置历史数据,否则会有错误的融合结果
  5. steadyLevel 调节:要根据画面运动程度来调整,运动剧烈时要偏向当前帧
  6. 深度值方向:注意isDepthReversed的设置,不同渲染管线的深度值方向可能不同

总结

时域 AI 超分是一个更高级的超分技术,它利用多帧信息来重建高清图像。核心流程:

  1. 查询设备是否支持时域 AI 超分
  2. 创建时域 AI 超分实例,配置参数
  3. 每帧计算 Jitter 值(使用 Halton 序列)
  4. 准备输入数据(颜色图、深度图、运动矢量、动态遮罩)
  5. 调用HMS_XEG_CmdRenderTemporalUpscale执行超分
  6. 不需要时销毁实例

如果你对画质有很高要求,时域 AI 超分是一个值得尝试的技术。

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

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

立即咨询