在鸿蒙(HarmonyOS)应用开发中,AR Engine 提供了强大的增强现实能力,支持平面检测、图像追踪、深度感知和网格重建等核心功能。基于 ArkTS 和 ArkGraphics3D 框架,开发者可以快速构建虚实融合的交互体验。

一、 核心架构与开发准备

构建 AR 应用需要依赖特定的系统 Kit,并正确配置 AR 会话的生命周期。

  1. 核心模块导入
    开发 AR 应用需导入 @kit.AREngine(AR引擎)、@kit.ArkGraphics3D(3D渲染)等模块。
  2. 会话生命周期管理
    通过 ARViewContext 管理整个 AR 会话的生命周期。必须在页面显示时调用 resume() 恢复 AR,页面隐藏时调用 pause() 暂停,页面销毁时调用 destroy() 释放资源,防止内存泄漏。
  3. 配置 AR 参数
    在初始化时,需配置 AR 类型(如 ARType.WORLD)、平面检测模式(如水平与垂直面 HORIZONTAL_AND_VERTICAL)、功耗模式及对焦模式等。
// ARSessionManager.ets
import { arEngine, arViewController } from '@kit.AREngine';
import { Scene } from '@kit.ArkGraphics3D';

export class ARSessionManager {
    private arContext: arViewController.ARViewContext | null = null;

    // 1. 初始化 AR 会话与场景
    public async initARView(scene: Scene): Promise<void> {
        let viewContext = new arViewController.ARViewContext();
        viewContext.scene = scene;
        viewContext.callback = new ARViewCallbackImpl(); // 绑定帧更新回调
        
        // 配置 AR 参数
        viewContext.config = {
            type: arEngine.ARType.WORLD,
            planeFindingMode: arEngine.ARPlaneFindingMode.HORIZONTAL_AND_VERTICAL,
            powerMode: arEngine.ARPowerMode.NORMAL,
            focusMode: arEngine.ARFocusMode.AUTO
        };
        
        await viewContext.init();
        this.arContext = viewContext;
    }

    // 2. 严格的会话生命周期管理
    public resume() { this.arContext?.resume(); }
    public pause() { this.arContext?.pause(); }
    public destroy() { 
        this.arContext?.destroy(); 
        this.arContext = null;
    }
}

二、 环境平面检测与渲染

平面检测是 AR 场景中最基础的能力,用于让虚拟物体“站”在现实世界中。

  1. 获取平面数据
    通过实现 ARViewCallback 的 onFrameUpdate 方法,在每帧更新时调用 session.getAllTrackables(ARTrackableType.PLANE) 获取当前识别到的所有平面。
  2. 提取平面几何信息
    通过 ARPlane.getPolygonXZ() 获取平面的 2D 顶点数组,结合 ARTrackable.getPose().getMatrix() 获取位姿矩阵,即可计算出平面在 3D 空间中的真实顶点坐标。
  3. 平面可视化
    使用 SceneResourceFactory 创建自定义材质和 Shader,结合计算出的顶点数据生成 Mesh,即可在屏幕上渲染出半透明的平面覆盖层。
// PlaneDetection.ets
import { arEngine, arViewController } from '@kit.AREngine';
import { Matrix4 } from '@kit.ArkUI';
import { Vec3 } from '@kit.ArkGraphics3D';

class ARViewCallbackImpl extends arViewController.ARViewCallback {
    // 每帧更新回调
    onFrameUpdate(ctx: arViewController.ARViewContext, sysBootTs: number): void {
        if (!ctx.session) return;
        try {
            let frame = ctx.session.getFrame();
            let camera = frame.getCamera();
            
            // 仅在相机正常追踪时获取平面
            if (camera.state === arEngine.ARTrackingState.TRACKING) {
                let planes = ctx.session.getAllTrackables(arEngine.ARTrackableType.PLANE);
                
                for (let plane of planes) {
                    // 获取平面的 2D 顶点和位姿矩阵
                    let polygonXZ = (plane as arEngine.ARPlane).getPolygonXZ();
                    let matrix = plane.getPose().getMatrix();
                    
                    // 将 2D 顶点转换为 3D 空间坐标
                    let vertices = this.getVertices(matrix, polygonXZ);
                    // TODO: 将 vertices 传入 SceneResourceFactory 生成 Mesh 并渲染
                }
            }
        } catch (error) {
            console.error('获取平面数据失败:', error);
        }
    }

    // 坐标转换工具函数
    private getVertices(mat: Matrix4, point: number[]): Vec3[] {
        let result: Vec3[] = [];
        for (let i = 0; i < point.length; i += 2) {
            result.push({
                x: mat[2] * point[i] + mat[10] * point[i + 1] + mat[14],
                y: mat[1] * point[i] + mat[9] * point[i + 1] + mat[13],
                z: -(mat[0] * point[i] + mat[8] * point[i + 1] + mat[12])
            });
        }
        return result;
    }
}

三、 虚实融合:物体放置与交互

将虚拟物体精准地锚定在现实平面上,是 AR 应用的核心交互。

  1. 射线检测(HitTest)
    监听用户的屏幕点击事件,将 2D 屏幕坐标转换为 3D 射线,调用 frame.hitTest(x, y) 检测射线与现实中平面的交点。
  2. 边界与有效性校验
    获取 ARHitResult 后,需通过 hitPlane.isPoseInPolygon(hitPose) 检查交点是否落在平面的有效多边形边界内,并校验距离是否大于 0。
  3. 创建锚点与放置物体
    校验通过后,调用 hitResult.createAnchor() 创建空间锚点。随后使用 CubeGeometry 或加载外部 .gltf 模型,将其位置设置为锚点的 translation 坐标,即可完成虚拟物体的放置。
// ObjectPlacement.ets
import { arEngine } from '@kit.AREngine';
import { Vec3 } from '@kit.ArkGraphics3D';

export class ObjectPlacement {
    // 监听屏幕点击事件
    public handleScreenTap(x: number, y: number, frame: arEngine.ARFrame): Vec3 | null {
        try {
            // 1. 射线检测
            let hitResults = frame.hitTest(x, y);
            if (!hitResults || hitResults.length === 0) return null;

            for (let hitResult of hitResults) {
                let trackable = hitResult.getTrackable();
                // 2. 确保命中目标是平面
                if (trackable.type === arEngine.ARTrackableType.PLANE) {
                    let hitPlane = trackable as arEngine.ARPlane;
                    let hitPose = hitResult.getHitPose();
                    
                    // 3. 边界与有效性校验
                    let inPolygon = hitPlane.isPoseInPolygon(hitPose);
                    let distance = hitResult.distance;
                    
                    if (inPolygon && distance > 0) {
                        // 4. 创建空间锚点并返回坐标
                        let anchor = hitResult.createAnchor();
                        return anchor.getPose().translation;
                    }
                }
            }
        } catch (error) {
            console.error('命中检测失败:', error);
        }
        return null;
    }
}

四、 性能优化

  1. 及时清理旧节点
    在 onFrameUpdate 中,每帧都应清除上一帧渲染的平面网格和临时几何体,避免节点无限累积导致严重的内存泄漏和渲染卡顿。
  2. 硬件兼容性检查
    AR 平面识别能力依赖设备的 SLAM 硬件特性。在应用启动前,应检查设备是否支持 ARENGINE_FEATURE_TYPE_SLAM,避免在不支持的设备上强行运行导致崩溃。
  3. 权限与省电模式
    必须在 module.json5 中声明 CAMERA 权限并在运行时动态申请。此外,部分设备在开启“省电模式”时会禁用后台摄像头,导致 AR 预览黑屏,需引导用户关闭省电模式或将应用加入白名单。
// ARSafetyCheck.ets
import { arEngine } from '@kit.AREngine';
import { abilityAccessCtrl, common } from '@kit.AbilityKit';

export class ARSafetyCheck {
    // 1. 硬件兼容性检查
    public static async checkARSupport(context: common.UIAbilityContext): Promise<boolean> {
        try {
            // 检查设备是否支持 SLAM 及平面识别特性
            let isSupported = arEngine.isSupport(context);
            if (!isSupported) {
                console.warn('当前设备不支持 AR Engine 平面识别能力');
            }
            return isSupported;
        } catch (err) {
            console.error('AR 能力检测异常:', err);
            return false;
        }
    }

    // 2. 运行时动态申请相机权限
    public static async requestCameraPermission(context: common.UIAbilityContext): Promise<boolean> {
        try {
            let atManager = abilityAccessCtrl.createAtManager();
            let result = await atManager.requestPermissionsFromUser(context, ['ohos.permission.CAMERA']);
            return result.authResults[0] === 0;
        } catch (err) {
            console.error('相机权限申请失败:', err);
            return false;
        }
    }
}

五、 AR 引擎实战:ArkTS 平面检测与网格渲染

在鸿蒙的 ArkTS 开发中,平面检测与渲染是构建虚实融合场景的基础。通过 ARViewCallback 的帧更新回调,可以实时获取环境中的平面数据并进行可视化。

  1. 实时获取平面数据
    在 onFrameUpdate 回调中,当相机处于 TRACKING 状态时,通过 arSession.getAllTrackables(arEngine.ARTrackableType.PLANE) 获取当前帧识别到的所有平面。
  2. 自定义网格生成与渲染
    由于平面是由三角形拼接而成的多边形,需要编写自定义方法将 2D 顶点数组转换为 3D 空间顶点坐标(getVertices),生成三角形索引(generateMeshIndex),并打包为 Mesh 数据(generateMeshInput),最终交由 ArkGraphics3D 引擎进行渲染。
// PlaneMeshRenderer.ets
import { arEngine, arViewController } from '@kit.AREngine';
import { Matrix4 } from '@kit.ArkUI';
import { Vec3 } from '@kit.ArkGraphics3D';

class ARViewCallbackImpl extends arViewController.ARViewCallback {
    // 每帧更新回调
    onFrameUpdate(ctx: arViewController.ARViewContext, sysBootTs: number): void {
        if (!ctx.session) return;
        try {
            let frame = ctx.session.getFrame();
            let camera = frame.getCamera();
            
            // 仅在相机正常追踪时获取平面
            if (camera.state === arEngine.ARTrackingState.TRACKING) {
                let planes = ctx.session.getAllTrackables(arEngine.ARTrackableType.PLANE);
                
                for (let plane of planes) {
                    // 获取平面的 2D 顶点和位姿矩阵
                    let polygonXZ = (plane as arEngine.ARPlane).getPolygonXZ();
                    let matrix = plane.getPose().getMatrix();
                    
                    // 将 2D 顶点转换为 3D 空间坐标
                    let vertices = this.getVertices(matrix, polygonXZ);
                    // TODO: 将 vertices 传入 SceneResourceFactory 生成 Mesh 并渲染
                }
            }
        } catch (error) {
            console.error('获取平面数据失败:', error);
        }
    }

    // 坐标转换工具函数
    private getVertices(mat: Matrix4, point: number[]): Vec3[] {
        let result: Vec3[] = [];
        for (let i = 0; i < point.length; i += 2) {
            result.push({
                x: mat[2] * point[i] + mat[10] * point[i + 1] + mat[14],
                y: mat[1] * point[i] + mat[9] * point[i + 1] + mat[13],
                z: -(mat[0] * point[i] + mat[8] * point[i + 1] + mat[12])
            });
        }
        return result;
    }
}

六、 进阶场景:命中检测与虚拟物体摆放

将虚拟物体精准放置在现实世界中,需要结合屏幕坐标与空间射线检测(HitTest)。

  1. 命中检测(HitTest)
    当用户点击屏幕时,获取屏幕坐标并调用 arFrame.hitTest(x, y)。系统会从相机位置向屏幕点击点发射一条射线,返回射线与真实世界平面的交点列表(ARHitResult)。
  2. 创建空间锚点(Anchor)
    从命中结果中提取交点的位置与姿态(Pose),并调用 createAnchor() 在现实世界中“钉”下一个锚点。
  3. 驱动 3D 节点
    在 onAnchorAdd 回调中,获取新创建的 Anchor,将其位姿矩阵绑定到 3D 场景中的虚拟物体节点(Node)上,从而实现虚拟物体与现实世界的完美融合与持续跟踪。
// VirtualObjectPlacer.ets
import { arEngine, arViewController } from '@kit.AREngine';
import { Vec3, Node } from '@kit.ArkGraphics3D';

export class VirtualObjectPlacer {
    // 1. 命中检测与锚点创建
    public handleScreenTap(x: number, y: number, frame: arEngine.ARFrame): Vec3 | null {
        try {
            // 射线检测
            let hitResults = frame.hitTest(x, y);
            if (!hitResults || hitResults.length === 0) return null;

            for (let hitResult of hitResults) {
                let trackable = hitResult.getTrackable();
                // 确保命中目标是平面
                if (trackable.type === arEngine.ARTrackableType.PLANE) {
                    let hitPlane = trackable as arEngine.ARPlane;
                    let hitPose = hitResult.getHitPose();
                    
                    // 边界与有效性校验
                    let inPolygon = hitPlane.isPoseInPolygon(hitPose);
                    let distance = hitResult.distance;
                    
                    if (inPolygon && distance > 0) {
                        // 创建空间锚点并返回坐标
                        let anchor = hitResult.createAnchor();
                        return anchor.getPose().translation;
                    }
                }
            }
        } catch (error) {
            console.error('命中检测失败:', error);
        }
        return null;
    }

    // 2. 锚点回调:驱动 3D 节点
    onAnchorAdd(ctx: arViewController.ARViewContext, node: Node, anchor: arEngine.ARAnchor): void {
        // 将新创建的 Anchor 位姿绑定到 3D 场景中的虚拟物体节点上
        let pose = anchor.getPose();
        node.position = pose.translation;
        // TODO: 设置旋转和缩放等
    }
}

AR 应用对设备性能和内存管理要求极高,工程化落地时必须遵循以下规范:

  1. 严格的资源释放
    FrameTrackableAnchor 等对象本质上是底层资源的句柄,而非纯数据。在每帧处理完毕后,必须及时调用相应的 release() 方法释放资源,否则极易引发内存泄漏和严重的掉帧。
  2. 按需启用高级能力
    AR Engine 提供了深度估计(Depth)、环境 Mesh 扫描、高精几何重建(HPG)等高级能力。这些能力功耗极高且仅部分高端机型支持。建议在工程上采用“能力探测 + 优雅降级”策略,仅在用户明确触发(如点击“3D扫描”)时按需开启,用完即关。
  3. 锚点池管理
    不要无限制地创建锚点。每个锚点都会带来持续的跟踪计算成本。对于不需要长期保留的虚拟物体,应及时调用 detach() 和 release() 回收锚点资源。
// ARResourceGuard.ets
import { arEngine, arViewController } from '@kit.AREngine';

export class ARResourceGuard {
    private anchorPool: arEngine.ARAnchor[] = [];
    private readonly MAX_ANCHORS = 10; // 锚点上限

    // 1. 严格的资源释放(必须在 onFrameUpdate 末尾调用)
    public safeReleaseFrame(frame: arEngine.ARFrame) {
        try {
            frame.release(); // 释放帧数据,防止底层缓冲区耗尽
        } catch (err) {
            console.error('Frame 释放失败:', err);
        }
    }

    // 2. 锚点池管理:防止无限制创建锚点
    public manageAnchorPool(newAnchor: arEngine.ARAnchor) {
        this.anchorPool.push(newAnchor);
        
        // 超过上限时,移除并释放最早的锚点
        if (this.anchorPool.length > this.MAX_ANCHORS) {
            const oldAnchor = this.anchorPool.shift();
            if (oldAnchor) {
                oldAnchor.detach(); // 从追踪中移除
                oldAnchor.release(); // 释放底层资源
            }
        }
    }

    // 3. 页面销毁时清理所有锚点
    public destroyAllAnchors() {
        for (let anchor of this.anchorPool) {
            anchor.detach();
            anchor.release();
        }
        this.anchorPool = [];
    }
}

 

 

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐