如何在不点击屏幕的情况下放置对象
Posted
技术标签:
【中文标题】如何在不点击屏幕的情况下放置对象【英文标题】:How to place a object without tapping on the screen 【发布时间】:2019-01-11 09:54:06 【问题描述】:我正在尝试使用 ARCore 在不点击屏幕的情况下显示一个对象。在 Google 提供的 ARCore Sceneform 基本示例中,您需要在检测到表面后点击屏幕。
我想实现这一点,AR 无需点击屏幕即可显示对象。
Anchor newAnchor;
for (Plane plane : mSession.getAllTrackables(Plane.class))
if (plane.getType() == Plane.Type.HORIZONTAL_UPWARD_FACING
&& plane.getTrackingState() == TrackingState.TRACKING)
newAnchor = plane.createAnchor(plane.getCenterPose());
break;
我尝试在不点击屏幕的情况下进行显示。
如果有人知道如何做到这一点,请帮助我。提前致谢
【问题讨论】:
@ARGeo 请不要在问题中添加不相关的标签。这与 Kotlin 无关,标签应该用于与与问题相关的语言和工具,不知道平台支持标签或仅仅因为答案使用它。标记问题,而不是答案或背景背景 好的@Zoe。我这样做是为了方便 Java 和 Kotlin 代码的可读性。 @ARGeo android 标签为两者添加了自动渲染。如果自动系统发生故障(或仅作为故障保护),可以在代码块前面使用<!-- language: lang-kotlin -->
(/lang-java
,或者在使用代码围栏时打开三个反引号后)来显式突出显示
【参考方案1】:
虽然我建议您在用户点击时放置对象以及他/她在屏幕上点击的位置,但您的要求可以这样实现。 (这个例子是在 Kotlin 中)
在开始放置对象之前,您需要创建一个ModelRenderable
。全局声明一个@Nullable
。
private var modelRenderable: ModelRenderable? = null
//Create the football renderable
ModelRenderable.builder()
//get the context of the ARFragment and pass the name of your .sfb file
.setSource(fragment.context, Uri.parse("FootBall.sfb"))
.build()
//I accepted the CompletableFuture using Async since I created my model on creation of the activity. You could simply use .thenAccept too.
//Use the returned modelRenderable and save it to a global variable of the same name
.thenAcceptAsync modelRenderable -> this@MainActivity.modelRenderable = modelRenderable
编程的主要部分必须在框架的onUpdate
方法上完成。所以你像这样为帧更新附加一个监听器
fragment.arSceneView.scene.addOnUpdateListener(this@MainActivity) //You can do this anywhere. I do it on activity creation post inflating the fragment
现在您处理在侦听器上添加一个对象。
override fun onUpdate(frameTime: FrameTime?)
//get the frame from the scene for shorthand
val frame = fragment.arSceneView.arFrame
if (frame != null)
//get the trackables to ensure planes are detected
val var3 = frame.getUpdatedTrackables(Plane::class.java).iterator()
while(var3.hasNext())
val plane = var3.next() as Plane
//If a plane has been detected & is being tracked by ARCore
if (plane.trackingState == TrackingState.TRACKING)
//Hide the plane discovery helper animation
fragment.planeDiscoveryController.hide()
//Get all added anchors to the frame
val iterableAnchor = frame.updatedAnchors.iterator()
//place the first object only if no previous anchors were added
if(!iterableAnchor.hasNext())
//Perform a hit test at the center of the screen to place an object without tapping
val hitTest = frame.hitTest(frame.screenCenter().x, frame.screenCenter().y)
//iterate through all hits
val hitTestIterator = hitTest.iterator()
while(hitTestIterator.hasNext())
val hitResult = hitTestIterator.next()
//Create an anchor at the plane hit
val modelAnchor = plane.createAnchor(hitResult.hitPose)
//Attach a node to this anchor with the scene as the parent
val anchorNode = AnchorNode(modelAnchor)
anchorNode.setParent(fragment.arSceneView.scene)
//create a new TranformableNode that will carry our object
val transformableNode = TransformableNode(fragment.transformationSystem)
transformableNode.setParent(anchorNode)
transformableNode.renderable = this@MainActivity.modelRenderable
//Alter the real world position to ensure object renders on the table top. Not somewhere inside.
transformableNode.worldPosition = Vector3(modelAnchor.pose.tx(),
modelAnchor.pose.compose(Pose.makeTranslation(0f, 0.05f, 0f)).ty(),
modelAnchor.pose.tz())
我使用了一种扩展方法
//A method to find the screen center. This is used while placing objects in the scene
private fun Frame.screenCenter(): Vector3
val vw = findViewById<View>(android.R.id.content)
return Vector3(vw.width / 2f, vw.height / 2f, 0f)
这是最终结果
【讨论】:
感谢您的帮助,我想要您在 GIF 中显示的输出。但是你能帮我弄清楚这个说法吗fragment.arSceneView.arFrame
fragment.arSceneView.arFrame
调用ArSceneView
类中的java 方法getArFrame()
。它用于从相机源中检索最新的帧,用于执行我们的命中测试或其他任何事情。 Java 等价物是 <fragmentName>.getArSceneView().getArFrame()
我这样做只是为了展示。不要再拥有它了。
好的,但是当我尝试将这个 fragment.arSceneView.scene.addOnUpdateListener(this@MainActivity)
添加为 fragment.getArSceneView().getArFrame().addOnUpdateListener(MainActivity.this)
时它不起作用,我也在 kotlin 中尝试过这个
嗨@Clinkz 我试过你的例子,效果很好。除非多次添加 3d 模型(我猜在每个平面检测中)。所以我在 onUpdate 方法的开头添加了一个条件,所以它只在放置第一个模型时调用一次。唯一的问题是当我这样做时,有时我的屏幕上会出现黑色平面。我看起来像这样:casimages.com/i/181216031121578258.jpg.html你能帮我吗?【参考方案2】:
如果您不想使用hit-testing
或button's action
方法将对象放置在真实环境中,则可以使用例如相机的 Pose 进行自动放置(请记住,您必须添加ARAnchor
,其对应的Node
和ARCamera前面的Renderable
即沿-Z
方向):
if (this.anchorNode == null)
Session session = arFragment.getArSceneView().getSession();
float[] position = 0, 0, -0.75 ; // 75 cm away from camera
float[] rotation = 0, 0, 0, 1 ;
Anchor anchor = session.createAnchor(new Pose(position, rotation));
anchorNode = new AnchorNode(anchor);
anchorNode.setRenderable(yourModelRenderable);
anchorNode.setParent(arFragment.getArSceneView().getScene());
希望这会有所帮助。
【讨论】:
【参考方案3】:如果你只想放置一个对象,这里是代码 sn-p.. 谢谢@clinkz
override fun onUpdate(frameTime: FrameTime?)
arFragment?.let fragment ->
fragment.arSceneView?.let sceneView ->
sceneView.arFrame?.let frame ->
if (!placed)
val trackable = frame.getUpdatedTrackables(Plane::class.java).iterator()
if (trackable.hasNext())
val plane = trackable.next() as Plane
if (plane.trackingState == TrackingState.TRACKING)
fragment.planeDiscoveryController?.hide()
val hitTest =
frame.hitTest(frame.screenCenter().x, frame.screenCenter().y)
val hitTestIterator = hitTest.iterator()
if (hitTestIterator.hasNext())
val hitResult = hitTestIterator.next()
val modelAnchor = plane.createAnchor(hitResult.hitPose)
val anchorNode = AnchorNode(modelAnchor)
anchorNode.setParent(sceneView.scene)
val transformableNode =
TransformableNode(fragment.transformationSystem)
transformableNode.setParent(anchorNode)
transformableNode.renderable = this@ARActivity.modelRenderable
transformableNode.worldPosition = Vector3(
modelAnchor.pose.tx(),
modelAnchor.pose.compose(
Pose.makeTranslation(
0f,
0.05f,
0f
)
).ty(),
modelAnchor.pose.tz()
)
placed = true
private fun Frame.screenCenter(): Vector3
val vw = findViewById<View>(android.R.id.content)
return Vector3(vw.width / 2f, vw.height / 2f, 0f)
【讨论】:
以上是关于如何在不点击屏幕的情况下放置对象的主要内容,如果未能解决你的问题,请参考以下文章
如何在不点击链接的情况下在 javascript 中打开新页面