鸿蒙开发--AREngine-C
HarmonyOS AR 引擎:用 C++ 实现空间感知能力
什么是 AR 引擎
AR(增强现实)技术让你可以在真实世界上叠加虚拟物体。比如你用手机摄像头对着桌子,屏幕上可以看到一个虚拟的杯子放在桌子上,就像真的一样。
HarmonyOS 的 AR Engine 提供了以下核心能力:
- 运动跟踪:实时获取设备在空间中的位置和姿态
- 环境跟踪:识别周围的平面(比如桌面、地面)
- 深度估计:获取场景中物体的距离信息
- 图像跟踪:识别现实中的图片或物体
- 命中检测:通过点击屏幕与虚拟物体交互
这篇我们用 C++ 来实现这些能力。
环境搭建
硬件要求
- 设备类型:支持 AR Engine 的华为设备
软件要求
- DevEco Studio 版本:DevEco Studio 6.0.0 Release 及以上
- HarmonyOS SDK 版本:HarmonyOS 6.0.0 Release SDK 及以上
搭建步骤
- 安装 DevEco Studio:去华为开发者官网下载安装
- 配置开发环境:确保网络环境正常
- 设备调试:使用真机进行调试
项目结构
├── entry/src/main
│ ├── cpp
│ │ ├── ar_world // AR世界感知
│ │ │ ├── ar_world_app.h/cpp // AR世界应用
│ │ │ ├── world_render_manager.h/cpp // 渲染管理
│ │ │ └── ...
│ │ ├── ar_depth // 深度估计
│ │ │ ├── depth_render_manager.h/cpp
│ │ │ └── ...
│ │ ├── ar_image // 图像跟踪
│ │ │ ├── ar_image_app.h/cpp
│ │ │ └── ...
│ │ ├── common // 通用代码
│ │ │ ├── background_renderer.h/cpp // 背景渲染
│ │ │ └── ...
│ │ └── napi_manager.h/cpp // NAPI管理
│ ├── ets
│ │ ├── entryability
│ │ │ └── EntryAbility.ts
│ │ └── pages
│ │ └── index.ets
这个项目结构比较复杂,分为几个模块:
ar_world:世界感知,包括运动跟踪和平面检测ar_depth:深度估计ar_image:图像跟踪common:通用代码,比如背景渲染
第一步:创建 AR 会话
使用 AR Engine 的第一步是创建一个 AR 会话。会话就像一个"连接",把你和 AR 引擎连接起来。
// 创建一个新的AREngine_ARSession会话。
CHECK(HMS_AREngine_ARSession_Create(nullptr, nullptr, &mArSession));
HMS_AREngine_ARSession_Create 创建一个新的 AR 会话。第一个参数是配置,第二个参数是回调,都传 nullptr 表示使用默认值。
// 配置AREngine_ARSession。
AREngine_ARConfig *arConfig = nullptr;
CHECK(HMS_AREngine_ARConfig_Create(mArSession, &arConfig));
// 设置预览画面尺寸。
CHECK(HMS_AREngine_ARConfig_SetPreviewSize(mArSession, arConfig, 1440, 1080));
// 设置画面更新模式。
CHECK(HMS_AREngine_ARConfig_SetUpdateMode(mArSession, arConfig, ARENGINE_UPDATE_MODE_LATEST));
CHECK(HMS_AREngine_ARSession_Configure(mArSession, arConfig));
HMS_AREngine_ARConfig_Destroy(arConfig);
配置 AR 会话:
SetPreviewSize:设置预览画面的尺寸SetUpdateMode:设置画面更新模式,ARENGINE_UPDATE_MODE_LATEST表示总是使用最新的画面
// 创建一个新的AREngine_ARFrame对象。
CHECK(HMS_AREngine_ARFrame_Create(mArSession, &mArFrame));
// 设置显示的高和宽(以像素为单位)。
CHECK(HMS_AREngine_ARSession_SetDisplayGeometry(mArSession, mDisplayRotation, mWidth, mHeight));
创建 AR 帧对象,并设置显示几何信息。
第二步:运动跟踪
运动跟踪是 AR 的基础。它让你知道设备在空间中的位置和朝向。
// 获取当前帧的相机参数。
AREngine_ARCamera *arCamera = nullptr;
CHECK(HMS_AREngine_ARFrame_AcquireCamera(arSession, arFrame, &arCamera));
先获取当前帧的相机对象。相机对象包含了设备的位置、朝向等信息。
// 获取最新帧中相机的视图矩阵。
CHECK(HMS_AREngine_ARCamera_GetViewMatrix(arSession, arCamera, glm::value_ptr(*viewMat), 16));
获取视图矩阵。视图矩阵描述了相机的位置和朝向,用它来确定虚拟物体应该画在哪里。
// 获取投影矩阵。
CHECK(HMS_AREngine_ARCamera_GetProjectionMatrix(arSession, arCamera, {0.1f, 100.f}, glm::value_ptr(*projectionMat), 16));
获取投影矩阵。投影矩阵用来把 3D 坐标转换成 2D 屏幕坐标。{0.1f, 100.f} 是近裁剪面和远裁剪面的距离。
// 获取摄像机的当前跟踪状态。
AREngine_ARTrackingState cameraTrackingState = ARENGINE_TRACKING_STATE_STOPPED;
CHECK(HMS_AREngine_ARCamera_GetTrackingState(arSession, arCamera, &cameraTrackingState));
获取跟踪状态。如果状态不是 ARENGINE_TRACKING_STATE_TRACKING,说明设备还没有成功跟踪,不应该渲染虚拟物体。
第三步:平面检测
平面检测可以识别现实中的平面,比如桌面、地面。有了平面信息,你就可以把虚拟物体"放"在这些平面上。
// 创建一个可跟踪对象列表。
AREngine_ARTrackableList *planeList = nullptr;
CHECK(HMS_AREngine_ARTrackableList_Create(arSession, &planeList));
// 获取所有指定类型的可跟踪对象集合。
AREngine_ARTrackableType planeTrackedType = ARENGINE_TRACKABLE_PLANE;
CHECK(HMS_AREngine_ARSession_GetAllTrackables(arSession, planeTrackedType, planeList));
获取所有检测到的平面。ARENGINE_TRACKABLE_PLANE 表示只获取平面类型的可跟踪对象。
// 获取此列表中的可跟踪对象的数量。
int32_t planeListSize = 0;
CHECK(HMS_AREngine_ARTrackableList_GetSize(arSession, planeList, &planeListSize));
获取平面数量。
for (int i = 0; i < planeListSize; ++i) {
AREngine_ARTrackable *arTrackable = nullptr;
// 从可跟踪列表中获取指定index的对象。
CHECK(HMS_AREngine_ARTrackableList_AcquireItem(arSession, planeList, i, &arTrackable));
AREngine_ARPlane *arPlane = reinterpret_cast<AREngine_ARPlane *>(arTrackable);
// 获取当前可跟踪对象的跟踪状态。
AREngine_ARTrackingState outTrackingState;
CHECK(HMS_AREngine_ARTrackable_GetTrackingState(arSession, arTrackable, &outTrackingState));
// 只有跟踪状态为TRACKING时才进行绘制。
if (AREngine_ARTrackingState::ARENGINE_TRACKING_STATE_TRACKING != outTrackingState) {
continue;
}
// 渲染平面
mPlaneRenderer.Draw(projectionMat, viewMat, arSession, arPlane, color);
}
遍历所有平面,只渲染跟踪状态正常的平面。
第四步:深度估计
深度估计可以获取场景中每个像素离相机有多远。这个信息可以用于遮挡、碰撞检测等场景。
// 获取深度图像。
AREngine_ARImage *depthImage = nullptr;
AREngine_ARStatus status = HMS_AREngine_ARFrame_AcquireDepthImage(arSession, arFrame, &depthImage);
获取当前帧的深度图像。深度图像中每个像素的值表示该点离相机的距离。
if (depthImage != nullptr) {
uint32_t depthWidth = 0;
uint32_t depthHeight = 0;
// 获取深度图像的宽度和高度。
HMS_AREngine_ARImage_GetWidth(arSession, depthImage, &depthWidth);
HMS_AREngine_ARImage_GetHeight(arSession, depthImage, &depthHeight);
// 获取深度图像数据。
uint8_t *depthData = nullptr;
HMS_AREngine_ARImage_GetPlaneData(arSession, depthImage, 0, &depthData, &dataSize);
// 获取中心点的深度值。
uint16_t *data = reinterpret_cast<uint16_t *>(depthData);
auto midDistance = (data[depthHeight * depthWidth / 2 + depthWidth / 2]) / 1000.f;
// 释放深度图像。
HMS_AREngine_ARImage_Release(depthImage);
}
获取深度图像的详细信息,包括宽度、高度和数据。这里还计算了画面中心点的深度值。
第五步:图像跟踪
图像跟踪可以识别现实中的图片。比如你用手机扫描一张海报,AR 应用可以在海报上叠加虚拟内容。
添加跟踪图像
首先需要告诉 AR Engine 你要跟踪哪些图片。
// 创建图像数据库。
AREngine_ARAugmentedImageDatabase *database = nullptr;
CHECK(HMS_AREngine_ARAugmentedImageDatabase_Create(arSession, &database));
// 添加图像到数据库。
AREngine_ARImage *arImage = nullptr;
// ... 加载图片数据
CHECK(HMS_AREngine_ARAugmentedImageDatabase_AddImage(arSession, database, "image_name", arImage, &imageIndex));
// 配置AR会话使用图像数据库。
AREngine_ARConfig *arConfig = nullptr;
CHECK(HMS_AREngine_ARConfig_Create(arSession, &arConfig));
CHECK(HMS_AREngine_ARConfig_SetAugmentedImageDatabase(arSession, arConfig, database));
CHECK(HMS_AREngine_ARSession_Configure(arSession, arConfig));
创建图像数据库,添加要跟踪的图片,然后配置 AR 会话使用这个数据库。
获取跟踪结果
// 获取所有增强图像可跟踪对象。
AREngine_ARTrackableList *imageTrackableList = nullptr;
CHECK(HMS_AREngine_ARTrackableList_Create(arSession, &imageTrackableList));
CHECK(HMS_AREngine_ARSession_GetAllTrackables(arSession, ARENGINE_TRACKABLE_AUGMENTED_IMAGE, imageTrackableList));
int32_t listSize = 0;
CHECK(HMS_AREngine_ARTrackableList_GetSize(arSession, imageTrackableList, &listSize));
for (int i = 0; i < listSize; ++i) {
AREngine_ARTrackable *trackable = nullptr;
CHECK(HMS_AREngine_ARTrackableList_AcquireItem(arSession, imageTrackableList, i, &trackable));
AREngine_ARAugmentedImage *augmentedImage = reinterpret_cast<AREngine_ARAugmentedImage *>(trackable);
// 获取图像的跟踪状态。
AREngine_ARTrackingState trackingState;
CHECK(HMS_AREngine_ARTrackable_GetTrackingState(arSession, trackable, &trackingState));
if (trackingState == ARENGINE_TRACKING_STATE_TRACKING) {
// 获取图像的中心位姿。
AREngine_ARPose *pose = nullptr;
CHECK(HMS_AREngine_ARPose_Create(arSession, nullptr, &pose));
CHECK(HMS_AREngine_ARAugmentedImage_GetCenterPose(arSession, augmentedImage, pose));
// 获取图像的四个角点坐标。
float extentX = 0.0f;
float extentZ = 0.0f;
CHECK(HMS_AREngine_ARAugmentedImage_GetExtentX(arSession, augmentedImage, &extentX));
CHECK(HMS_AREngine_ARAugmentedImage_GetExtentZ(arSession, augmentedImage, &extentZ));
}
}
遍历所有跟踪到的图像,获取它们的位置和大小。
第六步:命中检测
命中检测让你可以通过点击屏幕与虚拟物体交互。
// 获取屏幕点击坐标。
float pixeLX = mTouchEvent.touchPoints[0].x;
float pixeLY = mTouchEvent.touchPoints[0].y;
// 获取命中检测结果对象列表。
AREngine_ARHitResultList *hitResultList = nullptr;
CHECK(HMS_AREngine_ARHitResultList_Create(mArSession, &hitResultList));
// 射线与系统跟踪的平面产生交点。
CHECK(HMS_AREngine_ARFrame_HitTest(mArSession, mArFrame, pixeLX, pixeLY, hitResultList));
HMS_AREngine_ARFrame_HitTest 从屏幕坐标发射一条射线,检测它与哪些平面或物体相交。结果按照距离从近到远排序。
// 获取命中检测结果数量。
int32_t hitResultListSize = 0;
CHECK(HMS_AREngine_ARHitResultList_GetSize(mArSession, hitResultList, &hitResultListSize));
for (int32_t i = 0; i < hitResultListSize; ++i) {
AREngine_ARHitResult *hitResult = nullptr;
CHECK(HMS_AREngine_ARHitResultList_AcquireItem(mArSession, hitResultList, i, &hitResult));
// 获取命中点的位姿。
AREngine_ARPose *pose = nullptr;
CHECK(HMS_AREngine_ARPose_Create(mArSession, nullptr, &pose));
CHECK(HMS_AREngine_ARHitResult_GetHitPose(mArSession, hitResult, pose));
// 获取命中的可跟踪对象。
AREngine_ARTrackable *trackable = nullptr;
CHECK(HMS_AREngine_ARHitResult_AcquireTrackable(mArSession, hitResult, &trackable));
// 如果命中的是平面,可以在该位置放置虚拟物体。
AREngine_ARTrackableType trackableType;
CHECK(HMS_AREngine_ARTrackable_GetType(mArSession, trackable, &trackableType));
if (trackableType == ARENGINE_TRACKABLE_PLANE) {
// 在命中点放置虚拟物体
}
}
遍历所有命中结果,找到第一个平面命中点,在那里放置虚拟物体。
第七步:渲染虚拟物体
获取到平面和命中点信息后,就可以在上面渲染虚拟物体了。
// 渲染物体。
void WorldRenderManager::RenderObject(AREngine_ARSession *arSession,
const glm::mat4 &viewMat,
const glm::mat4 &projectionMat,
const std::vector<ColoredAnchor> &anchors) {
for (const auto &coloredAnchor : anchors) {
AREngine_ARAnchor *anchor = coloredAnchor.anchor;
AREngine_ARTrackingState trackingState;
CHECK(HMS_AREngine_ARTrackable_GetTrackingState(arSession,
reinterpret_cast<AREngine_ARTrackable *>(anchor), &trackingState));
if (trackingState == ARENGINE_TRACKING_STATE_TRACKING) {
// 获取锚点的位姿矩阵。
glm::mat4 modelMat;
CHECK(HMS_AREngine_ARAnchor_GetPose(arSession, anchor, modelMat));
// 使用位姿矩阵渲染虚拟物体。
mVirtualObject.Draw(projectionMat, viewMat, modelMat, coloredAnchor.color);
}
}
}
对于每个锚点(虚拟物体的放置点),获取它的位姿矩阵,然后用这个矩阵来渲染虚拟物体。这样虚拟物体就会"固定"在现实世界中的某个位置。
NAPI 封装
为了让 ArkTS 层能调用 C++ 的 AR 功能,需要做 NAPI 封装。
// 注册NAPI接口。
static napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor desc[] = {
{"init", nullptr, Init, nullptr, nullptr, nullptr, napi_default, nullptr},
{"start", nullptr, Start, nullptr, nullptr, nullptr, napi_default, nullptr},
{"stop", nullptr, Stop, nullptr, nullptr, nullptr, napi_default, nullptr},
{"show", nullptr, Show, nullptr, nullptr, nullptr, napi_default, nullptr},
{"hide", nullptr, Hide, nullptr, nullptr, nullptr, napi_default, nullptr},
};
napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
return exports;
}
封装了 init、start、stop、show、hide 等接口,供 ArkTS 层调用。
ArkTS 层调用
在 ArkTS 层,通过 XComponent 来显示 AR 画面,并调用 NAPI 接口控制 AR 服务。
XComponent({ id: this.xComponentId, type: XComponentType.SURFACE, libraryname: 'entry' })
.width('100%')
.height('100%')
.onAppear(() => {
arEngineDemo.init(this.resMgr);
let config: Int32Array = new Int32Array([1, this.rotation]);
arEngineDemo.start(this.xComponentId, config);
})
.onWillDisappear(() => {
arEngineDemo.stop(this.xComponentId);
})
.onShown(() => {
arEngineDemo.show(this.xComponentId);
})
.onHidden(() => {
arEngineDemo.hide(this.xComponentId);
})
XComponent 是 HarmonyOS 提供的原生渲染组件,AR 画面通过它来显示。在不同的生命周期调用对应的 AR 接口。
适用场景
AR Engine 适合以下场景:
- AR 游戏:在现实世界中放置虚拟游戏角色
- AR 购物:在房间里预览家具摆放效果
- AR 导航:在现实场景中叠加导航箭头
- AR 教育:在现实物体上叠加说明信息
- AR 测量:测量现实物体的尺寸
注意事项
- 设备支持:不是所有设备都支持 AR Engine,要先确认设备兼容性
- 光线条件:AR 跟踪需要足够的光线,太暗的环境会影响效果
- 特征点:环境需要有足够的特征点(纹理、边缘),纯色墙面很难跟踪
- 性能:AR 应用比较耗资源,要注意性能优化
- 权限:需要相机权限才能使用 AR 功能
- 初始化时间:AR 服务初始化需要时间,要给用户适当的提示
核心流程图
AR Engine C++ 版本的整体开发流程:
命中检测与虚拟物体放置的流程:
总结
AR Engine 是一个功能强大的 AR 开发框架,核心流程:
- 创建 AR 会话并配置
- 实现运动跟踪,获取相机位姿
- 实现平面检测,识别现实中的平面
- 实现深度估计,获取场景深度信息
- 实现图像跟踪,识别现实中的图片
- 实现命中检测,支持屏幕点击交互
- 在检测到的平面上渲染虚拟物体
掌握了这些,你就能在 HarmonyOS 应用中实现各种 AR 功能,打造沉浸式的增强现实体验。
更多推荐


所有评论(0)