如何在重力下将GLTF定位在Android Sceneform中

问题描述 投票:3回答:1

因此,我一直努力将垂直放置的3D模型GLB格式正确放置在垂直表面上。

请注意,我并不是指确定垂直表面的困难,这本身就是另外一个问题。

删除安装程序的通用样板以最小化此职位。

我正在使用扩展ARFragment的片段。

class SceneFormARFragment: ArFragment() {

然后,我当然对配置进行了一些调整。

override fun getSessionConfiguration(session: Session?): Config {
    val config = super.getSessionConfiguration(session)
    // By default we are not tracking and tracking is driven by startTracking()
    config.planeFindingMode = Config.PlaneFindingMode.DISABLED
    config.focusMode = Config.FocusMode.AUTO
    return config
}

并且为了开始和停止我的AR体验,我在片段中编写了一些方法,如下所示。

private fun startTracking() = viewScope.launchWhenResumed {
    try {
        arSceneView.session?.apply {
            val changedConfig = config
            changedConfig.planeFindingMode = Config.PlaneFindingMode.HORIZONTAL_AND_VERTICAL
            configure(changedConfig)
        }

        logv("startTracking")
        planeDiscoveryController.show()
        arSceneView.planeRenderer.isVisible = true
        arSceneView.cameraStreamRenderPriority = 7
    } catch (ex: Exception) {
        loge("error starting ar session: ${ex.message}")
    }
}
private fun stopTracking() = viewScope.launchWhenResumed {
    try {
        arSceneView.session?.apply {
            val changedConfig = config
            changedConfig.planeFindingMode = Config.PlaneFindingMode.DISABLED
            configure(changedConfig)
        }

        logv("stopTracking")
        planeDiscoveryController.hide()
        arSceneView.planeRenderer.isVisible = false
        arSceneView.cameraStreamRenderPriority = 0
    } catch (ex: Exception) {
        loge("error stopping ar session: ${ex.message}")
    }
}

[如果您想知道“启动和停止” AR体验的原因是为了最大化在此重叠屏幕上很繁重的其他UX交互的GPU周期,那么我们将根据当前的实时数据状态等待启动或停止其他正在发生的事情。

继续前进。

让我们回顾一下HitResult处理:在这种方法中,我做了几件事:

  1. 从云中加载TV 3d模型的两个变体(壁挂式和立式安装)
  2. 如果他们点击了一个新区域,我将删除所有活动模型
  3. 从hitresult创建锚节点,并为其指定名称以供日后删除
  4. 向其添加TVTransformableNode并为其指定名称以供日后检索和处理它
  5. 确定水平支架3D模型电视的外观方向,并将anchorNode的worldRotation设置为新的lookRotation。 (注意*,我觉得应该将旋转应用于TVNode,但无论出于何种原因,只有在将其应用于AnchorNode时,它才似乎起作用。)此摄像头位置数学方法似乎也有助于垂直壁挂电视表面朝外,正确锚定。 (我已经查看了GLB模型,并且知道它们已经从墙模型的背面和地板模型的底部正确锚定了]
  6. 然后,我将节点的平面运动限制为其自身的平面类型,以使地板模型不会向上滑动到墙壁,并且壁模型不会向下滑动到地板。

就是这样。水平放置效果很好,但垂直放置始终是随机的。

下面的OnTapArPlane代码:

 private fun onARSurfaceTapped() {
    setOnTapArPlaneListener { hitResult, plane, _ ->
        var isHorizontal = false
        val renderable = when (plane.type) {
            Plane.Type.HORIZONTAL_UPWARD_FACING -> {
                isHorizontal = true
                standmountTVRenderable
            }
            Plane.Type.VERTICAL                 -> wallmountTVRenderable
            else                                -> {
                activity?.toast("Do you want it to fall on your head really?")
                return@setOnTapArPlaneListener
            }
        }

        lastSelectedPlaneOrientation = plane.type
        removeActive3DTVModel()

        val anchorNode = AnchorNode(hitResult.createAnchor())
        anchorNode.name = TV_ANCHOR_NAME
        anchorNode.setParent(arSceneView.scene)

        val tvNode = TransformableNode(this.transformationSystem)
        tvNode.scaleController.isEnabled = false
        tvNode.setParent(anchorNode)
        tvNode.name = TV_NODE_NAME
        tvNode.select()

        // Set orientation towards camera
        // Ref: https://github.com/google-ar/sceneform-android-sdk/issues/379
        val cameraPosition = arSceneView.scene.camera.worldPosition
        val tvPosition = anchorNode.worldPosition
        val direction = Vector3.subtract(cameraPosition, tvPosition)

        if(isHorizontal) {
            tvNode.translationController.allowedPlaneTypes.clear()
            tvNode.translationController.allowedPlaneTypes.add(Plane.Type.HORIZONTAL_UPWARD_FACING)
        } else {
            tvNode.translationController.allowedPlaneTypes.clear()
            tvNode.translationController.allowedPlaneTypes.add(Plane.Type.VERTICAL)
        }

        val lookRotation = Quaternion.lookRotation(direction, Vector3.up())
        anchorNode.worldRotation = lookRotation

        tvNode.renderable = renderable

        addVideoTo3DModel(renderable)
    }
}

忽略addvideoTo3dModel调用,因为它可以正常工作,我对此进行了评论只是为了确保它不起作用。

我尝试过的事情。

  1. [here所述,提取没有旋转的平移,足够有趣,它的确使电视每次看起来都与地板齐平,但是随后始终将电视安装为好像锚点位于底部而不是中央靠背。因此很糟糕。
  2. 我曾尝试查看各种帖子,并将Unity或ARCore的内容直接翻译成Sceneform,但没有得到任何影响结果的信息。 example
  3. 我尝试根据answer中的指示从飞机和姿势创建锚,但没有运气
  4. 我已经审查了此link,但从未发现有用的东西
  5. 我已经跟踪了此issue,并尝试了线程中人们推荐的解决方案,但没有运气
  6. 我尝试的最后一件事,这有点令人尴尬。我在堆栈溢出中打开了所有带有“ SceneForm”标签的256个标签,并检查了其中每个标签中是否有任何有帮助的方法。

所以我用尽了互联网。我剩下的就是问社区,当然也要向Android的SceneForm团队发送帮助。

任何帮助或建议将不胜感激。

我的最佳猜测是我需要执行Quaternion.axisRotation(Vector3,Float),但是我所猜测或尝试并出错的所有方法均无效。我想我需要使用手机的xyz的worldPostion值来设置localRotation,以帮助识别重力。我真的不知道了,哈哈。

非常感谢任何可以提供帮助的人。我知道Sceneform相当新,文档是HORRIBLE,并且由于缺少内容或文档标头而可能不存在。开发人员必须确实不希望人们使用它,但我正在猜测:(。

最后,我要说的是,除了旋转的垂直放置,其他所有东西在我当前的实现中都运行良好。只是为了避免在此讨论中遇到麻烦,我没有任何其他问题。

哦,我注意到的最后一条线索。根据我轻按的位置,电视似乎几乎围绕垂直平面的中心旋转,底部似乎几乎指向平面的任意中心,如果这样可以帮助任何人找出来。

哦,是的,我知道GLB中缺少我的纹理,我包装不正确,打算以后再修复。

附上屏幕截图。enter image description hereenter image description here

android kotlin orientation arcore sceneform
1个回答
0
投票
最终结果看起来像:enter image description here

当然,它在一定程度上取决于您如何握住手机以及对周围环境的了解,但是现在在接近水平的情况下总是相当不错,而我所做的横向和纵向测试都没有失败。

所以这就是我所学到的。

在anchorNode上设置worldRotation将有助于使3DModel稍加减法即可朝向cameraview。

val cameraPosition = arSceneView.scene.camera.worldPosition val tvPosition = anchorNode.worldPosition val direction = Vector3.subtract(cameraPosition, tvPosition) val lookRotation = Quaternion.lookRotation(direction, Vector3.up()) anchorNode.worldRotation = lookRotation

但是,这不能解决垂直放置的方向问题。我发现,如果对外观旋转进行X旋转90度递减,则每次都能正常工作。根据您的3D模型,它可能会有所不同,但是我的锚点位于中后中间位置,因此我不确定它如何确定向上的方向。但是,我注意到每当在tvNode上设置worldRotation时,它将放置电视级别,但会向前倾斜90度。因此,在进行了各种旋转之后,我终于得到了答案。

val tvRotation = Quaternion.axisAngle(Vector3(1f, 0f, 0f), 90f)
tvNode.worldRotation = tvRotation

这解决了我的问题。因此,onSurfaceTap和放置的最终结果是:

   setOnTapArPlaneListener { hitResult, plane, _ ->
                var isHorizontal = false
                val renderable = when (plane.type) {
                    Plane.Type.HORIZONTAL_UPWARD_FACING -> {
                        isHorizontal = true
                        standmountTVRenderable
                    }
                    Plane.Type.VERTICAL -> wallmountTVRenderable
                    else -> {
                        activity?.toast("Do you want it to fall on your head really?")
                        return@setOnTapArPlaneListener
                    }
                }

                lastSelectedPlaneOrientation = plane.type
                removeActive3DTVModel()

                val anchorNode = AnchorNode(hitResult.createAnchor())
                anchorNode.name = TV_ANCHOR_NAME
                anchorNode.setParent(arSceneView.scene)

                val tvNode = TransformableNode(this.transformationSystem)
                tvNode.scaleController.isEnabled = false //disable scaling
                tvNode.setParent(anchorNode)
                tvNode.name = TV_NODE_NAME
                tvNode.select()

                val cameraPosition = arSceneView.scene.camera.worldPosition
                val tvPosition = anchorNode.worldPosition
                val direction = Vector3.subtract(cameraPosition, tvPosition)

                //restrict moving node to active surface orientation
                if (isHorizontal) {
                    tvNode.translationController.allowedPlaneTypes.clear()
                    tvNode.translationController.allowedPlaneTypes.add(Plane.Type.HORIZONTAL_UPWARD_FACING)
                } else {
                    tvNode.translationController.allowedPlaneTypes.clear()
                    tvNode.translationController.allowedPlaneTypes.add(Plane.Type.VERTICAL)

                    //x 90 degree rotation to flat mount TV vertical with gravity
                    val tvRotation = Quaternion.axisAngle(Vector3(1f, 0f, 0f), 90f)
                    tvNode.worldRotation = tvRotation
                }

                //set anchor nodes world rotation to face the camera view and up
                val lookRotation = Quaternion.lookRotation(direction, Vector3.up())
                anchorNode.worldRotation = lookRotation

                tvNode.renderable = renderable
                viewModel.updateStateTo(AriaMainViewModel.ARFlowState.REPOSITIONING)
            }

到目前为止,已经对肖像和风景进行了彻底的测试,没有任何问题。我仍然有Sceneform的其他问题,例如即使有有效的表面,点也只出现大约一半的时间,当然,对于当前的SDK,如果墙上没有图片,则无法在单色墙上进行垂直检测或其他可以区分墙壁的东西。

也执行屏幕截图效果不好,因为它不包含3D模型,因此所需的自定义Pixel Copy工作和我的屏幕截图有些慢,但是至少可以使用,不用了SDK。

所以他们还有很长的路要走,令人沮丧的是他们的产品,缺乏文档,对客户服务以及GitHub记录的问题都缺乏响应能力,这令人沮丧,但是,至少我明白了,我希望这可以帮助其他人。

快乐编码!

© www.soinside.com 2019 - 2024. All rights reserved.