物体摆放（C/C++）
===========

本章节给出了关键开发步骤，完整代码可以参考[示例代码](https://gitcode.com/harmonyos_samples/arengine_-sample-code_-clientdemo_cpp)。

#### 约束与限制

从5.0.0(12)开始，物体摆放能力支持部分Phone、Tablet设备。请参考[硬件要求](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arengine-preparations#硬件要求)判断设备是否支持运动跟踪及平面识别特性（[ARENGINE\_FEATURE\_TYPE\_SLAM](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arengine-capi-arengine#arengine_featuretype)）。

#### 接口说明

以下接口为AR物体摆放相关接口。详细接口和说明，请参考[AR Engine API参考](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arengine-capi-arengine)。

| 接口名 | 描述 |
| :-- | :-- |
| [HMS\_AREngine\_ARSession\_Create](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arengine-capi-arengine#hms_arengine_arsession_create) | 创建一个新的[AREngine\_ARSession](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arengine-capi-arengine#arengine_arsession)会话。 |
| [HMS\_AREngine\_ARSession\_Update](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arengine-capi-arengine#hms_arengine_arsession_update) | 更新AR Engine的计算结果。 |
| [HMS\_AREngine\_ARSession\_Configure](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arengine-capi-arengine#hms_arengine_arsession_configure) | 配置[AREngine\_ARSession](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arengine-capi-arengine#arengine_arsession)会话。 |
| [HMS\_AREngine\_ARFrame\_Create](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arengine-capi-arengine#hms_arengine_arframe_create) | 创建一个新的[AREngine\_ARFrame](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arengine-capi-arengine#arengine_arframe)对象，将指针存储到中\*outFrame。 |
| [HMS\_AREngine\_ARSession\_SetDisplayGeometry](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arengine-capi-arengine#hms_arengine_arsession_setdisplaygeometry) | 设置显示的高和宽（以Pixel为单位）。该高度和宽度是显示视图的高度和宽度，如果不一致，会导致显示相机预览出错。 |
| [HMS\_AREngine\_ARSession\_SetCameraGLTexture](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arengine-capi-arengine#hms_arengine_arsession_setcameragltexture) | 设置可用于存储相机预览流数据的openGL纹理。 |
| [HMS\_AREngine\_ARSession\_GetAllTrackables](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arengine-capi-arengine#hms_arengine_arsession_getalltrackables) | 获取所有指定类型的可跟踪对象集合。 |
| [HMS\_AREngine\_ARTrackableList\_AcquireItem](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arengine-capi-arengine#hms_arengine_artrackablelist_acquireitem) | 从可跟踪列表中获取指定index的对象。 |
| [HMS\_AREngine\_ARPlane\_GetCenterPose](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arengine-capi-arengine#hms_arengine_arplane_getcenterpose) | 获取从平面的局部坐标系到世界坐标系转换的位姿信息。 |
| [HMS\_AREngine\_ARFrame\_HitTest](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arengine-capi-arengine#hms_arengine_arframe_hittest) | 根据屏幕上兴趣点位置获取命中检测结果。 |
| [HMS\_AREngine\_ARHitResultList\_GetSize](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arengine-capi-arengine#hms_arengine_arhitresultlist_getsize) | 获取命中检测结果对象列表中包含的对象数。 |
| [HMS\_AREngine\_ARHitResultList\_GetItem](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arengine-capi-arengine#hms_arengine_arhitresultlist_getitem) | 在命中检测结果列表中获取指定索引的命中检测结果对象。 |
| [HMS\_AREngine\_ARHitResult\_Create](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arengine-capi-arengine#hms_arengine_arhitresult_create) | 创建一个空的命中检测结果对象。 |
| [HMS\_AREngine\_ARHitResult\_AcquireNewAnchor](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arengine-capi-arengine#hms_arengine_arhitresult_acquirenewanchor) | 在碰撞命中位置创建一个新的锚点。 |
| [HMS\_AREngine\_ARHitResult\_AcquireTrackable](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arengine-capi-arengine#hms_arengine_arhitresult_acquiretrackable) | 获取被命中的可追踪对象。 |
| [HMS\_AREngine\_ARFrame\_AcquireCamera](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arengine-capi-arengine#hms_arengine_arframe_acquirecamera) | 获取当前帧的相机参数对象。 |
| [HMS\_AREngine\_ARPose\_Create](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arengine-capi-arengine#hms_arengine_arpose_create) | 分配并初始化一个新的位姿对象。 |
| [HMS\_AREngine\_ARCamera\_GetPose](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arengine-capi-arengine#hms_arengine_arcamera_getpose) | 获取当前相机对象在AR世界空间中的位姿。 |

#### 开发步骤

#### \[h2\]声明Native接口

ArkTS接口声明。

import { resourceManager } from '@kit.LocalizationKit';

export const start: (id: string, params: Int32Array) => void;
export const show: (id: string) => void;
export const hide: (id: string) => void;
export const update: (id: string) => number;
export const stop: (id: string) => void;
export const init: (resmgr: resourceManager.ResourceManager) => void;
export const getDistance: (id: string) => string;
export const initImage: (id: string, width: number, height: number, buffer: ArrayBuffer) => number;
export const setPath: (id: string, path: string) => void;
export const saveImageDataBaseToLocal: (id: string, path: string) => void;
export const getImageCount: (id: string) => number;
export const getVolume: (id: string) => string;

建立ArkTS接口与C++接口之间的映射。

napi\_property\_descriptor desc\[\] = {
    {"init", nullptr, Global::Init, nullptr, nullptr, nullptr, napi\_default, nullptr},
    {"start", nullptr, NapiManager::NapiOnPageAppear, nullptr, nullptr, nullptr, napi\_default, nullptr},
    {"show", nullptr, NapiManager::NapiOnPageShow, nullptr, nullptr, nullptr, napi\_default, nullptr},
    {"hide", nullptr, NapiManager::NapiOnPageHide, nullptr, nullptr, nullptr, napi\_default, nullptr},
    {"update", nullptr, NapiManager::NapiOnPageUpdate, nullptr, nullptr, nullptr, napi\_default, nullptr},
    {"stop", nullptr, NapiManager::NapiOnPageDisappear, nullptr, nullptr, nullptr, napi\_default, nullptr},
    {"getDistance", nullptr, NapiManager::NapiGetDistance, nullptr, nullptr, nullptr, napi\_default, nullptr},
    {"initImage", nullptr, NapiManager::NapiInitImage, nullptr, nullptr, nullptr, napi\_default, nullptr},
    {"setPath", nullptr, NapiManager::NapiSetPath, nullptr, nullptr, nullptr, napi\_default, nullptr},
    {"saveImageDataBaseToLocal", nullptr, NapiManager::NapiSaveImageDataBaseToLocal, nullptr, nullptr, nullptr,
     napi\_default, nullptr},
    {"getImageCount", nullptr, NapiManager::NapiGetImageCount, nullptr, nullptr, nullptr, napi\_default, nullptr},
    {"getVolume", nullptr, NapiManager::NapiGetVolume, nullptr, nullptr, nullptr, napi\_default, nullptr}
};

#### \[h2\]创建UI界面

创建一个UI界面，使用[XComponent](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-basic-components-xcomponent)组件用于显示相机预览画面，并定时触发每一帧绘制。

// 此代码可参考示例代码：ARSample/entry/src/main/ets/pages/ARWorld.ets。
import { PromptAction } from '@kit.ArkUI';
import { deviceInfo } from '@kit.BasicServicesKit';
import { resourceManager } from '@kit.LocalizationKit';
import arEngineDemo from 'libentry.so';

@Builder
export function ARWorldBuilder() {
  ARWorld();
}

@Component
struct ARWorld {
  pageInfo: NavPathStack = new NavPathStack();
  private currentMillisecond: number = 0;
  private interval: number = -1;
  private isUpdate: boolean = true;
  private xComponentId: string = 'ARWorld';
  @State context: Context = this.getUIContext().getHostContext() as Context;
  private resMgr: resourceManager.ResourceManager = this.context.resourceManager;
  @State numberOfPlans: number = 0;
  @State rotation: number = deviceInfo.deviceType === 'tablet' ? 3 : 0;

  build(): void {
    NavDestination() {
      RelativeContainer() {
        XComponent({ id: this.xComponentId, type: XComponentType.SURFACE, libraryname: 'entry' })
          .width('100%')
          .height('100%')
          .alignRules({
            center: { anchor: '\_\_container\_\_', align: VerticalAlign.Center },
            middle: { anchor: '\_\_container\_\_', align: HorizontalAlign.Center }
          })
          .onLoad(() => {
            this.interval = setInterval(() => {
              if (this.isUpdate) {
                // 每一帧通过调用AR Engine的Native API update来更新计算结果
                this.numberOfPlans = arEngineDemo.update(this.xComponentId);
                this.planeNum();
              }
            }, 33) // 将帧速率设置为30fps（每33ms刷新一次帧）
          })
          .onDestroy(() => {
            clearInterval(this.interval);
          })
      }
    }
    .onAppear(() => {
      arEngineDemo.init(this.resMgr);
      let config: Int32Array = new Int32Array(\[1, this.rotation\]);
      arEngineDemo.start(this.xComponentId, config);
    })
    .onWillDisappear(() => {
      arEngineDemo.stop(this.xComponentId);
    })
    .onShown(() => {
      this.isUpdate = true;
      arEngineDemo.show(this.xComponentId);
    })
    .onHidden(() => {
      this.isUpdate = false;
      arEngineDemo.hide(this.xComponentId);
    })
    .onReady((context: NavDestinationContext) => {
      this.pageInfo = context.pathStack;
    })
    .hideTitleBar(true)
    .hideBackButton(true)
    .hideToolBar(true)
  }

  private messageNotification(): void {
    let promptAction: PromptAction = this.getUIContext().getPromptAction();
    promptAction.showToast({
      message: '当前特征点较少，无法识别平面，请移动相机。',
      bottom: 300
    })
  }

  private planeNum(): void {
    if (this.numberOfPlans < 1) {
      // 平面数量少于1
      let tempMillisecond: number = new Date().getTime();
      // 为首次启动的时间分配一个值
      if (this.currentMillisecond === 0) {
        this.currentMillisecond = tempMillisecond;
        return;
      }
      // 当识别平面时间超过10s，则展示一个弹窗
      if (tempMillisecond - this.currentMillisecond > 10000) {
        this.messageNotification();
        this.currentMillisecond = 0;
      }
    } else {
      this.currentMillisecond = 0;
    }
  }
}

#### \[h2\]引入AR Engine

开发者可参考管理AR会话章节的[引入AR Engine](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arengine-c-arsession#引入ar-engine)。

#### \[h2\]创建AR场景

1.  调用[HMS\_AREngine\_ARSession\_Create](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arengine-capi-arengine#hms_arengine_arsession_create)函数创建[AREngine\_ARSession](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arengine-capi-arengine#arengine_arsession)会话。您可以参考[管理AR会话](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arengine-c-arsession)创建ARSession。
    
2.  配置AR会话及预览尺寸。
    
    // 【可选】创建一个拥有合理默认配置的配置对象。
    AREngine\_ARConfig \*arConfig = nullptr;
    HMS\_AREngine\_ARConfig\_Create(arSession, &arConfig);
    // 【可选】配置AREngine\_ARSession会话。
    HMS\_AREngine\_ARSession\_Configure(arSession, arConfig);
    // 【可选】释放指定的配置对象的内存空间。
    HMS\_AREngine\_ARConfig\_Destroy(arConfig);
    
    // 创建一个新的AREngine\_ARFrame对象。
    AREngine\_ARFrame \*arFrame = nullptr;
    HMS\_AREngine\_ARFrame\_Create(arSession, &arFrame);
    // 预览区域的实际宽高，如使用xComponent组件显示，则该宽和高是xComponent的宽和高，如果不一致，会导致显示相机预览出错。
    int32\_t width = 1440;
    int32\_t height = 1080;
    // 显示旋转常量，值为AREngine\_ARPoseType中定义的枚举值。
    AREngine\_ARPoseType displayRotation = ARENGINE\_POSE\_TYPE\_IDENTITY;
    // 设置显示的宽和高（以Pixel为单位）。
    HMS\_AREngine\_ARSession\_SetDisplayGeometry(arSession, displayRotation, width, height);
    
3.  通过OpenGL接口获取纹理ID。
    
    // 通过openGL接口获取纹理ID。
    GLuint textureId = 0;
    glGenTextures(1, &textureId);
    
4.  设置OpenGL纹理，存储相机预览流数据。
    
    // 设置可用于存储相机预览流数据的openGL纹理。
    HMS\_AREngine\_ARSession\_SetCameraGLTexture(arSession, textureId );
    

#### \[h2\]获取平面

1.  调用[HMS\_AREngine\_ARSession\_Update](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arengine-capi-arengine#hms_arengine_arsession_update)函数更新当前[AREngine\_ARFrame](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arengine-capi-arengine#arengine_arframe)对象。
    
    // 获取帧数据AREngine\_ARFrame。
    HMS\_AREngine\_ARSession\_Update(arSession, arFrame);
    
2.  获取相机的视图矩阵和相机的投影矩阵，用于后续渲染。
    
    // 根据AREngine\_ARFrame对象可以获取相机对象AREngine\_ARCamera。
    AREngine\_ARCamera \*arCamera = nullptr;
    HMS\_AREngine\_ARFrame\_AcquireCamera(arSession, arFrame, &arCamera);
    // 获取最新帧中相机的视图矩阵。
    HMS\_AREngine\_ARCamera\_GetViewMatrix(arSession, arCamera, glm::value\_ptr(\*viewMat), 16);
    // 获取用于在相机图像上层渲染虚拟内容的投影矩阵，可用于相机坐标系到裁剪坐标系转换。Near (0.1) Far (100)。
    HMS\_AREngine\_ARCamera\_GetProjectionMatrix(arSession, arCamera, {0.1f, 100.f}, glm::value\_ptr(\*projectionMat), 16);
    
    ![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/79/v3/SXGEcMzqRQu_ouu3GmB_bg/note_3.0-zh-cn.png?HW-CC-KV=V1&HW-CC-Date=20260612T073531Z&HW-CC-Expire=86400&HW-CC-Sign=66FA1E09B832F64220395F4B0582995C447D6C707AE328B80067B1989E99C18A)
    
    这里直接获取相机的视图矩阵和相机的投影矩阵，是为了便于渲染。获取相机运动中的位姿变化，还可以调用[HMS\_AREngine\_ARCamera\_GetPose](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arengine-capi-arengine#hms_arengine_arcamera_getpose)函数配合[HMS\_AREngine\_ARPose\_GetPoseRaw](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arengine-capi-arengine#hms_arengine_arpose_getposeraw)函数进行获取。详细可参考[获取设备当前位姿](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arengine-c-get-pose#获取设备当前位姿)。
    
3.  调用[HMS\_AREngine\_ARSession\_GetAllTrackables](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arengine-capi-arengine#hms_arengine_arsession_getalltrackables)函数获取平面列表。详细可参考[检测环境中的平面](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arengine-c-get-plane)章节。
    
    // 获取当前检测到的平面列表。
    AREngine\_ARTrackableList \*planeList = nullptr;
    // 创建一个可跟踪对象列表。
    HMS\_AREngine\_ARTrackableList\_Create(arSession, &planeList);
    // 获取所有指定类型为ARENGINE\_TRACKABLE\_PLANE的可跟踪对象集合。
    AREngine\_ARTrackableType planeTrackedType = ARENGINE\_TRACKABLE\_PLANE;
    HMS\_AREngine\_ARSession\_GetAllTrackables(arSession, planeTrackedType, planeList);
    int32\_t planeListSize = 0;
    // 获取此列表中的可跟踪对象的数量。
    HMS\_AREngine\_ARTrackableList\_GetSize(arSession, planeList, &planeListSize);
    for (int i = 0; i < planeListSize; ++i) {
        AREngine\_ARTrackable \*arTrackable = nullptr;
        // 从可跟踪列表中获取指定index的对象。
        HMS\_AREngine\_ARTrackableList\_AcquireItem(arSession, planeList, i, &arTrackable);
        AREngine\_ARPlane \*arPlane = reinterpret\_cast<AREngine\_ARPlane\*>(arTrackable);
        // 获取当前可跟踪对象的跟踪状态。如果状态为：ARENGINE\_TRACKING\_STATE\_TRACKING（可跟踪状态）才进行绘制。
        AREngine\_ARTrackingState outTrackingState;
        HMS\_AREngine\_ARTrackable\_GetTrackingState(arSession, arTrackable, &outTrackingState);
        AREngine\_ARPlane \*subsumePlane = nullptr;
        // 获取平面的父平面（一个平面被另一个平面合并时，会产生父平面），如果无父平面返回为NULL。
         HMS\_AREngine\_ARPlane\_AcquireSubsumedBy(arSession, arPlane, &subsumePlane);
        if (subsumePlane != nullptr) {
            HMS\_AREngine\_ARTrackable\_Release(reinterpret\_cast<AREngine\_ARTrackable\*>(subsumePlane));
            // 如果当前平面有父平面，则当前平面不进行展示。否则会出现双平面。
            continue;
        }
        // 跟踪状态为：ARENGINE\_TRACKING\_STATE\_TRACKING时才进行绘制。
        if (AREngine\_ARTrackingState::ARENGINE\_TRACKING\_STATE\_TRACKING != outTrackingState) {
            continue;
        }
        // 进行平面绘制。
    }
    HMS\_AREngine\_ARTrackableList\_Destroy(planeList);
    planeList = nullptr;
    
4.  调用[HMS\_AREngine\_ARPlane\_GetPolygon](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arengine-capi-arengine#hms_arengine_arplane_getpolygon)函数获取平面的二维顶点坐标数组，用于绘制平面边界。
    
    // 获取检测到平面的二维顶点数组大小。
    int32\_t polygonLength = 0;
    HMS\_AREngine\_ARPlane\_GetPolygonSize(arSession, arPlane, &polygonLength);
    
    // 获取检测到平面的二维顶点数组，格式为\[x1，z1，x2，z2，...\]。
    const int32\_t verticesSize = polygonLength / 2;
    std::vector<glm::vec2> raw\_vertices(verticesSize);
    HMS\_AREngine\_ARPlane\_GetPolygon(arSession, arPlane, glm::value\_ptr(raw\_vertices.front()), polygonLength);
    
    // 局部坐标系顶点坐标。
    for (int32\_t i = 0; i < verticesSize; ++i) {
        vertices.emplace\_back(raw\_vertices\[i\].x, raw\_vertices\[i\].y, 0.75f);
    }
    
    ![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/db/v3/lgJV_DHmScC_1o2FoF-tCQ/note_3.0-zh-cn.png?HW-CC-KV=V1&HW-CC-Date=20260612T073531Z&HW-CC-Expire=86400&HW-CC-Sign=DBBC10784EE20E80717C0C4FBE991DB84DDFB9343636CFF6F42FA9C0B59E1B14)
    
    调用[HMS\_AREngine\_ARPlane\_GetPolygon](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arengine-capi-arengine#hms_arengine_arplane_getpolygon)函数获取平面的二维顶点坐标数组格式为\[x1，z1，x2，z2，...\]。这些值均在平面局部坐标系的x-z平面中定义，须先调用[HMS\_AREngine\_ARPlane\_GetCenterPose](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arengine-capi-arengine#hms_arengine_arplane_getcenterpose)函数获取从平面的局部坐标系到世界坐标系转换的位姿数据，然后调用[HMS\_AREngine\_ARPose\_GetMatrix](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arengine-capi-arengine#hms_arengine_arpose_getmatrix)函数将位姿数据转换成4X4的矩阵，该矩阵与局部坐标系的坐标点做乘法，可以得到局部坐标系到世界坐标系的转换。
    
5.  将平面的二维顶点坐标转换到世界坐标系，并绘制平面。
    
    // 获取从平面的局部坐标系到世界坐标系转换的位姿信息。
    AREngine\_ARPose \*scopedArPose = nullptr;
    HMS\_AREngine\_ARPose\_Create(arSession, nullptr, 0, &scopedArPose);
    HMS\_AREngine\_ARPlane\_GetCenterPose(arSession, arPlane, scopedArPose);
    
    // 将位姿数据转换成4X4的矩阵，outMatrixColMajor4x4为存放数组，其中的数据按照列优先存储。
    // 该矩阵与局部坐标系的坐标点做乘法，可以得到局部坐标系到世界坐标系的转换。
    HMS\_AREngine\_ARPose\_GetMatrix(arSession, scopedArPose, glm::value\_ptr(modelMat), 16);
    HMS\_AREngine\_ARPose\_Destroy(scopedArPose);
    
    // 构筑绘制渲染平面所需的数据。
    // 生成三角形。
    for (int i = 1; i < verticesSize - 1; ++i) {
        triangles.push\_back(0);
        triangles.push\_back(i);
        triangles.push\_back(i + 1);
    }
    // 生成平面包围线。
    for (int i = 0; i < verticesSize; ++i) {
        lines.push\_back(i);
    }
    

#### \[h2\]点击屏幕

1.  用户点击屏幕后，基于点击事件获取屏幕坐标。可参考[Native XComponent](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-oh-nativexcomponent-native-xcomponent)。
    
    添加头文件：native\_interface\_xcomponent.h。
    
    #include <ace/xcomponent/native\_interface\_xcomponent.h>
    
    通过点击事件获取屏幕点击坐标。
    
    float pixelX= 0.0f;
    float pixelY= 0.0f;
    int32\_t ret = OH\_NativeXComponent\_GetTouchEvent(component, window, &mTouchEvent);
    
    if (ret == OH\_NATIVEXCOMPONENT\_RESULT\_SUCCESS) {
        if (mTouchEvent.type == OH\_NATIVEXCOMPONENT\_DOWN) {
            pixelX= mTouchEvent.touchPoints\[0\].x;
            pixelY= mTouchEvent.touchPoints\[0\].y;
        } else {
            return;
        }
    }
    
2.  调用[HMS\_AREngine\_ARFrame\_HitTest](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arengine-capi-arengine#hms_arengine_arframe_hittest)函数进行碰撞检测，结果存放在碰撞检测结果列表中。
    
    // 创建一个命中检测结果对象列表，arSession为创建AR场景步骤中创建的会话对象。
    AREngine\_ARHitResultList \*hitResultList = nullptr;
    HMS\_AREngine\_ARHitResultList\_Create(arSession, &hitResultList);
    
    // 获取命中检测结果对象列表，arFrame为创建AR场景步骤中创建的帧对象，pixelX/pixelY为屏幕点坐标。
    HMS\_AREngine\_ARFrame\_HitTest(arSession, arFrame, pixelX, pixelY, hitResultList);
    
    ![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/4b/v3/5QMQ7IYzRr6DANYpX66Eng/note_3.0-zh-cn.png?HW-CC-KV=V1&HW-CC-Date=20260612T073531Z&HW-CC-Expire=86400&HW-CC-Sign=6D19993D1695CA80532E9B2372B9B4FFDB41A1A7211DE92D4A06E785CE3FA305)
    
    碰撞结果按照交点与设备的距离从近到远进行排序，存放在碰撞结果列表中。
    

#### \[h2\]放置虚拟物体

1.  调用[HMS\_AREngine\_ARHitResultList\_GetItem](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arengine-capi-arengine#hms_arengine_arhitresultlist_getitem)函数遍历碰撞检测结果列表，获取命中的可跟踪对象。
    
    // 创建命中检测结果对象。
    AREngine\_ARHitResult \*arHit = nullptr;
    HMS\_AREngine\_ARHitResult\_Create(arSession, &arHit);
    
    // 获取第一个命中检测结果对象。
    HMS\_AREngine\_ARHitResultList\_GetItem(arSession, hitResultList, 0, arHit);
    
    // 获取被命中的可追踪对象。
    AREngine\_ARTrackable \*arHitTrackable = nullptr;
    HMS\_AREngine\_ARHitResult\_AcquireTrackable(arSession, arHit, &arHitTrackable);
    
2.  判断碰撞结果是否存在于平面内部。
    
    AREngine\_ARTrackableType ar\_trackable\_type = ARENGINE\_TRACKABLE\_INVALID;
    HMS\_AREngine\_ARTrackable\_GetType(arSession, arTrackable, &ar\_trackable\_type);
    if (ARENGINE\_TRACKABLE\_PLANE == ar\_trackable\_type) {
        AREngine\_ARPose \*arPose = nullptr;
        HMS\_AREngine\_ARPose\_Create(arSession, nullptr, 0, &arPose);
        HMS\_AREngine\_ARHitResult\_GetHitPose(arSession, arHit, arPose);
        // 判断位姿是否位于平面的多边形范围内。0表示不在范围内，非0表示在范围内。
        HMS\_AREngine\_ARPlane\_IsPoseInPolygon(arSession, arPlane, arPose, &inPolygon);
        HMS\_AREngine\_ARPose\_Destroy(arPose);
        if (!inPolygon) {
            // 不在平面内，就跳过当前平面。
            continue;
        }
    }
    
3.  在碰撞结果位置创建一个新的锚点，并基于此锚点放置虚拟模型。
    
    // 在碰撞命中位置创建一个新的锚点。
    AREngine\_ARAnchor \*anchor = nullptr;
    HMS\_AREngine\_ARHitResult\_AcquireNewAnchor(arSession, arHitResult, &anchor);
    
    // 判断锚点的可跟踪状态
    AREngine\_ARTrackingState trackingState = ARENGINE\_TRACKING\_STATE\_STOPPED;
    HMS\_AREngine\_ARAnchor\_GetTrackingState(arSession, anchor, &trackingState);
    if (trackingState != ARENGINE\_TRACKING\_STATE\_TRACKING) {
        HMS\_AREngine\_ARAnchor\_Release(anchor);
        return;
    }
    
4.  绘制模型。
    
    调用[HMS\_AREngine\_ARAnchor\_GetPose](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arengine-capi-arengine#hms_arengine_aranchor_getpose)函数获取锚点位姿，并基于该位姿绘制虚拟模型。
    
    // 获取锚点的位姿。
    AREngine\_ARPose \*pose = nullptr;
    HMS\_AREngine\_ARPose\_Create(arSession, nullptr, 0, &pose);
    HMS\_AREngine\_ARAnchor\_GetPose(arSession, anchor, pose);
    // 将位姿数据转换成4X4的矩阵modelMat。
    HMS\_AREngine\_ARPose\_GetMatrix(arSession, pose, glm::value\_ptr(modelMat), 16);
    HMS\_AREngine\_ARPose\_Destroy(pose);
    // 绘制虚拟模型。
    // 开发者可以使用OpenGL进行模型绘制，可参考示例代码：world\_render\_manager.cpp及world\_object\_renderer.cpp。