android - AR Android Sceneform SDK 仅在地板上展示模型
问题描述
我正在使用适用于 Android 版本的 Sceneform SDK
implementation 'com.google.ar.sceneform.ux:sceneform-ux:1.15.0'
我需要我的 3D 模型只显示在地板上。例如,我有一个 3D 笼子(简单的透明长方体),我需要将此 3D 模型放置在真实对象上。现在,如果我的真实对象有足够大的表面,模型将被放置在它的顶部而不是越过它,我需要避免行为。
这是这里的一些代码。
初始化 ArFragment 并在相机中心显示模型的逻辑。每当帧发生变化时,我都会在设备摄像头的中心进行 HitTest。
private fun initArFragment() {
arFragment.arSceneView.scene.addOnUpdateListener {
arFragment.arSceneView?.let { sceneView ->
sceneView.arFrame?.let { frame ->
if (frame.camera.trackingState == TrackingState.TRACKING) {
val hitTest =
frame.hitTest(sceneView.width / 2f, sceneView.height / 2f)
val hitTestIterator = hitTest.iterator()
if (hitTestIterator.hasNext()) {
val hitResult = hitTestIterator.next()
val anchor = hitResult.createAnchor()
if (anchorNode == null) {
anchorNode = AnchorNode()
anchorNode?.setParent(sceneView.scene)
transformableNode =
DragTransformableNode(arFragment.transformationSystem)
transformableNode?.setParent(anchorNode)
boxNode = createBoxNode(.4f, .6f, .4f) // Creating cuboid
boxNode?.setParent(transformableNode)
}
anchorNode?.anchor?.detach()
anchorNode?.anchor = anchor
}
}
}
}
}
}
我认为这是预期的行为,因为 HitTest 也会击中真实物体的表面。但不知道如何避免这种行为。有没有办法忽略真实物体并将 3D 模型始终放在地板上?
更新 我试图遵循@Mick 的建议。我正在尝试对所有 HitTestResult 进行分组。当 HitTest 完成后,我会列出所有可见平面的所有 HitResults。我按圆形 Y 轴对它们进行分组。 示例 {1.35 -> [Y 是 1.36776767,Y 是 1.35434343,Y 是 1.35999999,Y 是 1.37723278]} {1.40 -> [Y 是 1.4121212,Y 是 1.403232323,Y 是 1.44454545,Y 是 1.40001]}锚点我使用 FIRST HitResult 和从数组排序的键从 MIN 到 MAX 键在示例中它是 1.35。对于 Y 锚点,我得到一个 MIN 组元素的数组并得到它的平均值。
val hitResultList = hitTestIterator.asSequence().toList().groupBy { round(it.hitPose.ty() * 20) / 20 }.minBy { it.key }?.value
val hitResult = hitResultList?.first()!!
val averageValueOfY = hitResultList?.map { it.hitPose.ty() }?.average()
createModel(hitResult, averageValueOfY)
创建模型的方法
private fun createModel(newHitResult: HitResult, averageValueOfY: Double) {
try {
val newAnchorPose = newHitResult.createAnchor().pose
anchorNode?.anchor?.detach()
anchorNode?.anchor = arFragment.arSceneView.session?.createAnchor(Pose(floatArrayOf(newAnchorPose.tx(),
averageValueOfY.toFloat(), newAnchorPose.tz()), floatArrayOf(0f, 0f, 0f, 0f)))
isArBagModelRendered = true
transformableNode?.select()
} catch (exception: Exception) {
Timber.d(exception)
}
}
此代码更新有助于获得我试图实现的行为,但我注意到有时我的 Y 锚点在地下,看起来好像在地板下检测到 MIN 平面 :( 我现在不知道如何解决这个问题.
解决方案
实际上,我认为您的用例可能会面临两个不同的问题:
- 正如您在问题中突出显示的那样,对象被放置在顶面上
- 当模型实际放置在正确的位置时,遮挡,或不显示应该隐藏在对象后面的模型部分。
第一个问题的简单解决方案,以便您可以检查第二个问题,或者更准确的解决方法,可能是简单地让用户将对象放在真实对象的前面,即上面示例中的情况,然后移动它回来,直到它正是他们想要的地方。
如果您保持飞机突出显示,即显示检测到飞机的位置的网格线,用户可能更直观地“击中”地板而不是对象的顶部。
如果遮挡问题实际上是更严重的问题,这将允许您在您走得太远之前快速测试。
一个更复杂的解决方案是遍历平面并尝试比较每个平面中心的“姿势”,看看你是否能找到可靠的方法来确定哪个是地板 - 该方法是 Plane 类的一部分:
公共姿势 getCenterPose ()
返回检测到的平面中心的位姿。姿势变换后的 +Y 轴将法线指向平面外,+X 和 +Z 轴指向边界矩形的范围。
如果您确定地板始终是最大的平面,还有一些方法可以获取平面的宽度或深度:
公共浮动 getExtentX ()
返回沿以平面为中心的坐标空间的局部 X 轴测量的平面边界矩形的长度。
公共浮动 getExtentZ ()
返回沿以平面为中心的坐标系的局部 Z 轴测量的平面边界矩形的长度。
不幸的是,我认为没有任何现有的方便的帮助功能,例如“获取最低平面”或“获取最大平面”等。
请注意遮挡问题,有些框架和库确实提供了某些形式的基于软件的遮挡,即不需要设备具有额外的深度传感器,因此可能也值得探索一下。
推荐阅读
- debugging - 跟踪已部署的 AppStore iOS 应用程序中的活动
- python - 如何分别为训练集和测试集标记编码多个列
- r - R:仅当同一列中的两行中的值为真时才将值添加到 [row,column]
- docker - Docker容器到容器命令执行
- ajax - 如何在受公共用户保护的 wp-admin root 中使用 wp-ajax?
- node.js - 在 Mongoosejs 中查询嵌套关系
- php - 注意:未定义的索引:变量
- angular - 如何导航到ionic3中的页面
- devexpress - 使用 EventToCommand、Devexpress 将数据传递给 ViewModel
- java - getActionview 在 android 中使用 Searchview 返回 null