用 AI 让低分辨率画面变清晰——GLES 神经网络超分实战
想象一下这个场景:你做了一个画面很精美的 3D 游戏,但手机性能有限,跑不了原生高分辨率。你有两个选择:一是降低分辨率让游戏流畅运行,但画面会模糊;二是硬扛高分辨率,但帧率惨不忍睹。
有没有第三种选择?有——用神经网络超分。
简单说就是:你用低分辨率渲染(性能好),然后通过 AI 算法把画面"放大"到高分辨率。因为 AI 能够智能地补充细节,所以最终效果比传统的双线性插值好得多。这就是 XEngine 的神经网络超分(Neural Upscale)做的事情。
神经网络超分整体流程
使用 GLES 神经网络超分的完整流程如下:
#mermaid-svg-gqpPD72e7oBJFSJv{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-gqpPD72e7oBJFSJv .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-gqpPD72e7oBJFSJv .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-gqpPD72e7oBJFSJv .error-icon{fill:#552222;}#mermaid-svg-gqpPD72e7oBJFSJv .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-gqpPD72e7oBJFSJv .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-gqpPD72e7oBJFSJv .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-gqpPD72e7oBJFSJv .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-gqpPD72e7oBJFSJv .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-gqpPD72e7oBJFSJv .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-gqpPD72e7oBJFSJv .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-gqpPD72e7oBJFSJv .marker{fill:#333333;stroke:#333333;}#mermaid-svg-gqpPD72e7oBJFSJv .marker.cross{stroke:#333333;}#mermaid-svg-gqpPD72e7oBJFSJv svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-gqpPD72e7oBJFSJv p{margin:0;}#mermaid-svg-gqpPD72e7oBJFSJv .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-gqpPD72e7oBJFSJv .cluster-label text{fill:#333;}#mermaid-svg-gqpPD72e7oBJFSJv .cluster-label span{color:#333;}#mermaid-svg-gqpPD72e7oBJFSJv .cluster-label span p{background-color:transparent;}#mermaid-svg-gqpPD72e7oBJFSJv .label text,#mermaid-svg-gqpPD72e7oBJFSJv span{fill:#333;color:#333;}#mermaid-svg-gqpPD72e7oBJFSJv .node rect,#mermaid-svg-gqpPD72e7oBJFSJv .node circle,#mermaid-svg-gqpPD72e7oBJFSJv .node ellipse,#mermaid-svg-gqpPD72e7oBJFSJv .node polygon,#mermaid-svg-gqpPD72e7oBJFSJv .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-gqpPD72e7oBJFSJv .rough-node .label text,#mermaid-svg-gqpPD72e7oBJFSJv .node .label text,#mermaid-svg-gqpPD72e7oBJFSJv .image-shape .label,#mermaid-svg-gqpPD72e7oBJFSJv .icon-shape .label{text-anchor:middle;}#mermaid-svg-gqpPD72e7oBJFSJv .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-gqpPD72e7oBJFSJv .rough-node .label,#mermaid-svg-gqpPD72e7oBJFSJv .node .label,#mermaid-svg-gqpPD72e7oBJFSJv .image-shape .label,#mermaid-svg-gqpPD72e7oBJFSJv .icon-shape .label{text-align:center;}#mermaid-svg-gqpPD72e7oBJFSJv .node.clickable{cursor:pointer;}#mermaid-svg-gqpPD72e7oBJFSJv .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-gqpPD72e7oBJFSJv .arrowheadPath{fill:#333333;}#mermaid-svg-gqpPD72e7oBJFSJv .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-gqpPD72e7oBJFSJv .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-gqpPD72e7oBJFSJv .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-gqpPD72e7oBJFSJv .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-gqpPD72e7oBJFSJv .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-gqpPD72e7oBJFSJv .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-gqpPD72e7oBJFSJv .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-gqpPD72e7oBJFSJv .cluster text{fill:#333;}#mermaid-svg-gqpPD72e7oBJFSJv .cluster span{color:#333;}#mermaid-svg-gqpPD72e7oBJFSJv div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-gqpPD72e7oBJFSJv .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-gqpPD72e7oBJFSJv rect.text{fill:none;stroke-width:0;}#mermaid-svg-gqpPD72e7oBJFSJv .icon-shape,#mermaid-svg-gqpPD72e7oBJFSJv .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-gqpPD72e7oBJFSJv .icon-shape p,#mermaid-svg-gqpPD72e7oBJFSJv .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-gqpPD72e7oBJFSJv .icon-shape .label rect,#mermaid-svg-gqpPD72e7oBJFSJv .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-gqpPD72e7oBJFSJv .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-gqpPD72e7oBJFSJv .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-gqpPD72e7oBJFSJv :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
否
是
检查设备支持
支持神经网络超分?
退出
创建 OH_NativeBuffer 纹理
绑定输出帧缓冲
设置输入参数
设置 NativeBuffer Handle 必填
设置裁剪窗口 可选
设置锐化度 可选
执行超分渲染
输出高分辨率画面
使用前的准备
跟其他 XEngine 特性一样,你得先确认设备支持这个能力:
const GLubyte* extensions = HMS_XEG_GetString(XEG_EXTENSIONS);
if (strstr((const char*)extensions, "XEG_neural_upscale") == NULL) {
return; // 不支持神经网络超分
}
注意,这个特性推荐的超分倍率是 1.0 到 1.5 倍。也就是说,你可以把 720p 的画面超分到 1080p,但不建议直接从 480p 超到 1080p(倍率太大效果会差)。
神经网络超分需要哪些输入
神经网络超分需要一个关键的输入:由 OH_NativeBuffer 创建的纹理。为什么呢?因为 AI 推理需要用到底层的硬件加速,而 OH_NativeBuffer 提供了高效的内存共享机制,让 GPU 和 AI 加速器都能访问同一块内存。
除了纹理本身,你还需要把纹理关联的 OH_NativeBuffer handle 告诉超分算法。这是神经网络超分和普通 GPU 超分的一个重要区别。
第一步:设置输入参数
通过 HMS_XEG_NeuralUpscaleParameter 函数来设置参数。这个函数有两个参数:pname 是参数名,param 是参数值的指针。
设置 OH_NativeBuffer handle(必填)
这是神经网络超分最关键的参数。你必须把输入纹理对应的 OH_NativeBuffer handle 设置进去:
// nativeBufferHandle 是与输入纹理关联的 OH_NativeBuffer handle
HMS_XEG_NeuralUpscaleParameter(XEG_NEURAL_UPSCALE_INPUT_HANDLE, &nativeBufferHandle);
XEG_NEURAL_UPSCALE_INPUT_HANDLE 的值是 0x4U。这个参数是必选的,如果不设置,超分渲染会产生未定义行为(效果不对或者直接崩溃)。
需要注意的是,你传入的 handle 必须和后面 HMS_XEG_RenderNeuralUpscale 函数传入的 inputTexture 是对应关系——也就是说,这个 handle 对应的 OH_NativeBuffer 就是用来创建那个纹理的。
设置裁剪窗口(可选)
你可以通过 XEG_NEURAL_UPSCALE_SCISSOR 来指定对输入图像的采样区域:
GLuint scissor[4] = {100, 100, 800, 600}; // x, y, width, height
HMS_XEG_NeuralUpscaleParameter(XEG_NEURAL_UPSCALE_SCISSOR, scissor);
XEG_NEURAL_UPSCALE_SCISSOR 的值是 0x1U。传入的是长度为 4 的无符号整数数组,依次为裁剪窗口左下角的 x、y 坐标和窗口的宽高。
这个参数有什么用呢?如果你只想对画面的某个区域做超分(比如游戏主画面,不包括 UI),就可以用裁剪窗口来指定。如果不设置,默认是整个输入纹理。
设置锐化度(可选)
超分之后的画面可能会有点"软",你可以通过 XEG_NEURAL_UPSCALE_SHARPNESS 来调节锐化程度:
GLfloat sharpness = 0.3f; // 取值范围 [0, 1],默认 0.2
HMS_XEG_NeuralUpscaleParameter(XEG_NEURAL_UPSCALE_SHARPNESS, &sharpness);
XEG_NEURAL_UPSCALE_SHARPNESS 的值是 0x2U。值越大锐化效果越强,默认是 0.2。
但要注意,锐化值不是越大越好。太大了会出现"振铃效应"——物体边缘会出现亮边或者噪点。建议从默认值 0.2 开始,慢慢调。不同风格的画面需要的锐化值不一样:写实风格的游戏可能 0.2 就够了,卡通风格的可能需要 0.4-0.5。
第二步:执行超分渲染
参数设好之后,调用 HMS_XEG_RenderNeuralUpscale 来执行超分:
HMS_XEG_RenderNeuralUpscale(inputTexture);
inputTexture 是超分的输入纹理,它有几个严格的要求:
超分的结果会直接渲染到当前绑定的帧缓冲上。所以你需要在调用之前确保正确绑定了输出目标。
完整的使用示例
把上面的步骤串起来:
// 1. 检查支持
const GLubyte* extensions = HMS_XEG_GetString(XEG_EXTENSIONS);
if (strstr((const char*)extensions, "XEG_neural_upscale") == NULL) {
return;
}
// 2. 假设你已经用 OH_NativeBuffer 创建了一个纹理
// inputTexture 是纹理 ID,nativeBufferHandle 是对应的 handle
// 3. 绑定输出帧缓冲(超分结果会渲染到这里)
glBindFramebuffer(GL_FRAMEBUFFER, outputFBO);
// 4. 设置必选参数:OH_NativeBuffer handle
HMS_XEG_NeuralUpscaleParameter(XEG_NEURAL_UPSCALE_INPUT_HANDLE, &nativeBufferHandle);
// 5. 设置可选参数:锐化度
GLfloat sharpness = 0.3f;
HMS_XEG_NeuralUpscaleParameter(XEG_NEURAL_UPSCALE_SHARPNESS, &sharpness);
// 6. 执行超分
HMS_XEG_RenderNeuralUpscale(inputTexture);
// 7. 超分完成,outputFBO 里的颜色附件就是超分后的结果
两种超分方案对比
下面是神经网络超分和空域 GPU 超分的对比:
#mermaid-svg-GKcrTZOwbPGuldjd{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-GKcrTZOwbPGuldjd .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-GKcrTZOwbPGuldjd .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-GKcrTZOwbPGuldjd .error-icon{fill:#552222;}#mermaid-svg-GKcrTZOwbPGuldjd .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-GKcrTZOwbPGuldjd .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-GKcrTZOwbPGuldjd .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-GKcrTZOwbPGuldjd .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-GKcrTZOwbPGuldjd .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-GKcrTZOwbPGuldjd .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-GKcrTZOwbPGuldjd .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-GKcrTZOwbPGuldjd .marker{fill:#333333;stroke:#333333;}#mermaid-svg-GKcrTZOwbPGuldjd .marker.cross{stroke:#333333;}#mermaid-svg-GKcrTZOwbPGuldjd svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-GKcrTZOwbPGuldjd p{margin:0;}#mermaid-svg-GKcrTZOwbPGuldjd .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-GKcrTZOwbPGuldjd .cluster-label text{fill:#333;}#mermaid-svg-GKcrTZOwbPGuldjd .cluster-label span{color:#333;}#mermaid-svg-GKcrTZOwbPGuldjd .cluster-label span p{background-color:transparent;}#mermaid-svg-GKcrTZOwbPGuldjd .label text,#mermaid-svg-GKcrTZOwbPGuldjd span{fill:#333;color:#333;}#mermaid-svg-GKcrTZOwbPGuldjd .node rect,#mermaid-svg-GKcrTZOwbPGuldjd .node circle,#mermaid-svg-GKcrTZOwbPGuldjd .node ellipse,#mermaid-svg-GKcrTZOwbPGuldjd .node polygon,#mermaid-svg-GKcrTZOwbPGuldjd .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-GKcrTZOwbPGuldjd .rough-node .label text,#mermaid-svg-GKcrTZOwbPGuldjd .node .label text,#mermaid-svg-GKcrTZOwbPGuldjd .image-shape .label,#mermaid-svg-GKcrTZOwbPGuldjd .icon-shape .label{text-anchor:middle;}#mermaid-svg-GKcrTZOwbPGuldjd .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-GKcrTZOwbPGuldjd .rough-node .label,#mermaid-svg-GKcrTZOwbPGuldjd .node .label,#mermaid-svg-GKcrTZOwbPGuldjd .image-shape .label,#mermaid-svg-GKcrTZOwbPGuldjd .icon-shape .label{text-align:center;}#mermaid-svg-GKcrTZOwbPGuldjd .node.clickable{cursor:pointer;}#mermaid-svg-GKcrTZOwbPGuldjd .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-GKcrTZOwbPGuldjd .arrowheadPath{fill:#333333;}#mermaid-svg-GKcrTZOwbPGuldjd .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-GKcrTZOwbPGuldjd .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-GKcrTZOwbPGuldjd .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-GKcrTZOwbPGuldjd .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-GKcrTZOwbPGuldjd .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-GKcrTZOwbPGuldjd .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-GKcrTZOwbPGuldjd .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-GKcrTZOwbPGuldjd .cluster text{fill:#333;}#mermaid-svg-GKcrTZOwbPGuldjd .cluster span{color:#333;}#mermaid-svg-GKcrTZOwbPGuldjd div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-GKcrTZOwbPGuldjd .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-GKcrTZOwbPGuldjd rect.text{fill:none;stroke-width:0;}#mermaid-svg-GKcrTZOwbPGuldjd .icon-shape,#mermaid-svg-GKcrTZOwbPGuldjd .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-GKcrTZOwbPGuldjd .icon-shape p,#mermaid-svg-GKcrTZOwbPGuldjd .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-GKcrTZOwbPGuldjd .icon-shape .label rect,#mermaid-svg-GKcrTZOwbPGuldjd .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-GKcrTZOwbPGuldjd .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-GKcrTZOwbPGuldjd .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-GKcrTZOwbPGuldjd :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
是
否
超分方案选择
神经网络超分
空域 GPU 超分
AI 推理算法
画质更好
需要 OH_NativeBuffer
推荐倍率 1.0-1.5
传统图像处理
速度更快
普通纹理即可
无明确倍率限制
选择建议
追求画质?
和传统超分(Spatial Upscale)的区别
你可能会问,XEngine 不是还有个"空域 GPU 超分"吗?它和神经网络超分有什么区别?
简单来说:
- 空域 GPU 超分(Spatial Upscale):用传统的图像处理算法(比如锐化、边缘增强)来放大画面。速度快,但效果一般。
- 神经网络超分(Neural Upscale):用 AI 模型来放大画面。速度稍慢,但效果好得多,能补充更多细节。
选择哪个取决于你的需求:如果对性能要求极高、对画质要求不那么苛刻,用 GPU 超分;如果希望画面尽可能清晰,用神经网络超分。
另外,神经网络超分有一个限制:输入纹理必须由 OH_NativeBuffer 创建,而 GPU 超分没有这个要求。这是由 AI 推理的硬件加速机制决定的。
注意事项
输入宽度限制:纹理宽度必须在 [448, 1728] 范围内。超出这个范围可能导致 AI 推理出错。
OH_NativeBuffer 是必须的:神经网络超分的输入纹理必须由 OH_NativeBuffer 创建,并且你需要在调用超分之前把对应的 handle 设置好。
推荐超分倍率:1.0 到 1.5 倍。超过 1.5 倍效果会明显下降。
锐化度要适度:从 0.2 开始调,不要一上来就设很高。
神经网络超分是一个"用少量性能开销换取明显画质提升"的好东西。特别是对于那些需要在中低端设备上运行的游戏,用 720p 渲染然后超分到 1080p,既流畅又清晰,用户体验会好很多。

