
在本指南中,您将学习如何将3D模型添加到现实世界中。 Google的ARCore库可让您将完整的3D模型添加到2D图像(图片或视频)中。
您需要为系统提供某种参考图像,ARCore将在现实世界中寻找该参考图像,以便在图像的基础上添加3D模型。 增强现实已经被广泛使用,例如在书籍,报纸,杂志等中。
在开始学习本教程之前,您应该熟悉本主题的前两篇文章,它们将向您介绍基本的AR术语:
什么是增强现实图像?
根据开发人员的文档 ,ARCore中的增强现实图像使您可以创建可以“动画化” 2D图像(例如海报或产品包装)的增强现实应用程序。
您将一些参考图像上传到ARCore,然后告诉您有关在AR会话期间(例如,拍摄视频时)检测到的参考图像。 并且此信息用于将3D模型定位在2D图像上。
使用增强现实图像的局限性
使用增强现实图像时,可能会遇到一些限制:
- ARCore一次最多只能处理20张参考图像。
- 现实世界中的物理平面应平坦,其面积应大于15厘米x 15厘米。
- ARCore无法跟踪运动图像和对象。
选择正确的参考图像
以下是为ARCore选择良好参考图像的一些技巧:
- 增强现实图像支持PNG,JPEG和JPG格式。
- 图像是彩色还是黑白都没有关系,主要是要具有高对比度。
- 图像分辨率必须至少为300 x 300像素。
- 使用高分辨率图像并不意味着性能会得到改善。
- 应避免使用重复图案(例如图案或圆点)的图像。
- 使用arcoreimg工具评估图像的效果。 建议评分至少为75分。
如何使用arcoreimg工具:
- 从此链接下载适用于Android的ARCore SDK。
- 将文件内容解压缩到任何位置。
- 在解压缩的文件夹中,转到工具> arcoreimg>窗口 (即使您使用的是Linux或macOS)。
- 在此目录中打开命令提示符。
- 并输入以下命令:
arcoreimg.exe eval-img --input_image_path=dog.png
将dog.png替换为图像的完整路径。
增强现实应用程序入门
既然您已经熟悉了ARCore并选择了75以上的好图像,那么该开始编写应用程序代码了。
片段创建
我们将创建一个片段并将其添加到我们的Activity中。 创建一个名为CustomArFragment
的类,并从ArFragment
继承它。 这是CustomArFragment
的代码:
package com.ayusch.augmentedimages; import android.util.Log; import com.google.ar.core.Config; import com.google.ar.core.Session; import com.google.ar.sceneform.ux.ArFragment; public class CustomArFragment extends ArFragment { @Override protected Config getSessionConfiguration(Session session) { getPlaneDiscoveryController().setInstructionView(null); Config config = new Config(session); config.setUpdateMode(Config.UpdateMode.LATEST_CAMERA_IMAGE); session.configure(config); getArSceneView().setupSession(session); return config; } }
首先,我们关闭平面检测。 通过这样做,我们从片段初始化后立即出现的屏幕中删除了手形图标,并告诉用户需要移动智能手机来找到飞机。 我们不再需要这个了,因为我们发现的不是随机平面,而是具体的图像。
然后,我们为LATEST_CAMERA_IMAGE
会话设置更新模式。 这样可以确保无论何时更新相机框架,我们都会收到有关图像更新的通知。
图像数据库设置
将选定的参考图像(要在物理世界中找到的参考图像)添加到资产文件夹(如果尚未创建,则创建它)。 现在我们可以将图像添加到数据库中。
片段创建后,我们将立即创建此数据库。 在日志中,我们显示此操作的结果:
if ((((MainActivity) getActivity()).setupAugmentedImagesDb(config, session))) { Log.d("SetupAugImgDb", "Success"); } else { Log.e("SetupAugImgDb","Faliure setting up db"); }
这是CustomArFragment
样子:
package com.ayusch.augmentedimages; import android.util.Log; import com.google.ar.core.Config; import com.google.ar.core.Session; import com.google.ar.sceneform.ux.ArFragment; public class CustomArFragment extends ArFragment { @Override protected Config getSessionConfiguration(Session session) { getPlaneDiscoveryController().setInstructionView(null); Config config = new Config(session); config.setUpdateMode(Config.UpdateMode.LATEST_CAMERA_IMAGE); session.configure(config); getArSceneView().setupSession(session); if ((((MainActivity) getActivity()).setupAugmentedImagesDb(config, session))) { Log.d("SetupAugImgDb", "Success"); } else { Log.e("SetupAugImgDb","Faliure setting up db"); } return config; } }
很快,我们将setupAugmentedImagesDb
方法添加到MainActivity
。 现在,将CustomArFragment
添加到我们的activity_main.xml
:
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <fragment android:id="@+id/sceneform_fragment" android:name="com.ayusch.augmentedimages.CustomArFragment" android:layout_width="match_parent" android:layout_height="match_parent" /> </android.support.constraint.ConstraintLayout>
将图像添加到数据库
现在,我们将建立图像数据库,在现实世界中找到参考图像,并向该图像添加3D模型。
让我们从设置数据库开始。 在MainActivity
类中创建一个公共setupAugmentedImagesDb
方法:
public boolean setupAugmentedImagesDb(Config config, Session session) { AugmentedImageDatabase augmentedImageDatabase; Bitmap bitmap = loadAugmentedImage(); if (bitmap == null) { return false; } augmentedImageDatabase = new AugmentedImageDatabase(session); augmentedImageDatabase.addImage("tiger", bitmap); config.setAugmentedImageDatabase(augmentedImageDatabase); return true; } private Bitmap loadAugmentedImage() { try (InputStream is = getAssets().open("blanket.jpeg")) { return BitmapFactory.decodeStream(is); } catch (IOException e) { Log.e("ImageLoad", "IO Exception", e); } return null; }
我们还创建了一个loadAugmentedImage
方法,该方法从资源文件夹中加载图像并返回位图。
在setupAugmentedImagesDb
我们首先为当前会话初始化数据库,然后将图像添加到该数据库。 我们把形象命名为老虎 。 然后,我们将此数据库安装在config中,并返回true
,指示已成功添加映像。
真实参考图像检测
现在,我们将开始发现现实世界中的参考图像。 为此,我们将创建一个侦听器,每次更新视频帧时都会调用该侦听器,并将分析该帧中是否存在参考图像。
将此行添加到MainActivity
的onCreate()
方法中:
arFragment.getArSceneView().getScene().addOnUpdateListener(this::onUpdateFrame);
现在将onUpdateFrame
方法添加到MainActivity
:
@RequiresApi(api = Build.VERSION_CODES.N) private void onUpdateFrame(FrameTime frameTime) { Frame frame = arFragment.getArSceneView().getArFrame(); Collection<AugmentedImage> augmentedImages = frame.getUpdatedTrackables(AugmentedImage.class); for (AugmentedImage augmentedImage : augmentedImages) { if (augmentedImage.getTrackingState() == TrackingState.TRACKING) { if (augmentedImage.getName().equals("tiger") && shouldAddModel) { placeObject(arFragment, augmentedImage.createAnchor(augmentedImage.getCenterPose()), Uri.parse("Mesh_BengalTiger.sfb")); shouldAddModel = false; } } } }
在第一行中,我们获取框架本身。 帧可以想象成视频中的常规屏幕截图。 如果您熟悉视频的工作原理,那么您就会知道这只是一系列图像,它们之间的变化非常迅速,给人以动人的印象。 我们只是拍其中一张照片。
收到框架后,我们将对其进行分析以了解其上是否存在参考图像。 我们使用frame.getUpdatedTrackables
跟踪的所有项目的列表。 然后我们对其进行迭代,并检查框架中是否存在我们的老虎图像。
如果找到匹配项,那么我们只需将3D模型放置在检测到的图像上方即可。
注意事项 使用shouldAddModel
标志shouldAddModel
以便我们仅添加一次3D模型。
将3D模型放置在参考图像上
现在,我们已经在现实世界中找到了参考图像,我们可以在其上面添加3D模型。 添加placeObject
和addNodeToScene
:
placeObject
:此方法用于使用给定的Uri
构建呈现的对象。 渲染完成后,将对象传递到addNodeToScene
方法,在此方法中将对象附加到节点,然后将该节点放置在场景中。addNodeToScene
:此方法从生成的锚点创建一个节点,创建可视化对象加入的另一个节点,然后将该节点添加到锚点节点并将其放置在舞台上。
这是MainActivity
样子:
package com.ayusch.augmentedimages; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; import android.os.Build; import android.support.annotation.RequiresApi; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.widget.Toast; import com.google.ar.core.Anchor; import com.google.ar.core.AugmentedImage; import com.google.ar.core.AugmentedImageDatabase; import com.google.ar.core.Config; import com.google.ar.core.Frame; import com.google.ar.core.Session; import com.google.ar.core.TrackingState; import com.google.ar.sceneform.AnchorNode; import com.google.ar.sceneform.FrameTime; import com.google.ar.sceneform.rendering.ModelRenderable; import com.google.ar.sceneform.rendering.Renderable; import com.google.ar.sceneform.ux.ArFragment; import com.google.ar.sceneform.ux.TransformableNode; import java.io.IOException; import java.io.InputStream; import java.util.Collection; public class MainActivity extends AppCompatActivity { ArFragment arFragment; boolean shouldAddModel = true; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); arFragment = (CustomArFragment) getSupportFragmentManager().findFragmentById(R.id.sceneform_fragment); arFragment.getPlaneDiscoveryController().hide(); arFragment.getArSceneView().getScene().addOnUpdateListener(this::onUpdateFrame); } @RequiresApi(api = Build.VERSION_CODES.N) private void placeObject(ArFragment arFragment, Anchor anchor, Uri uri) { ModelRenderable.builder() .setSource(arFragment.getContext(), uri) .build() .thenAccept(modelRenderable -> addNodeToScene(arFragment, anchor, modelRenderable)) .exceptionally(throwable -> { Toast.makeText(arFragment.getContext(), "Error:" + throwable.getMessage(), Toast.LENGTH_LONG).show(); return null; } ); } @RequiresApi(api = Build.VERSION_CODES.N) private void onUpdateFrame(FrameTime frameTime) { Frame frame = arFragment.getArSceneView().getArFrame(); Collection<AugmentedImage> augmentedImages = frame.getUpdatedTrackables(AugmentedImage.class); for (AugmentedImage augmentedImage : augmentedImages) { if (augmentedImage.getTrackingState() == TrackingState.TRACKING) { if (augmentedImage.getName().equals("tiger") && shouldAddModel) { placeObject(arFragment, augmentedImage.createAnchor(augmentedImage.getCenterPose()), Uri.parse("Mesh_BengalTiger.sfb")); shouldAddModel = false; } } } } public boolean setupAugmentedImagesDb(Config config, Session session) { AugmentedImageDatabase augmentedImageDatabase; Bitmap bitmap = loadAugmentedImage(); if (bitmap == null) { return false; } augmentedImageDatabase = new AugmentedImageDatabase(session); augmentedImageDatabase.addImage("tiger", bitmap); config.setAugmentedImageDatabase(augmentedImageDatabase); return true; } private Bitmap loadAugmentedImage() { try (InputStream is = getAssets().open("blanket.jpeg")) { return BitmapFactory.decodeStream(is); } catch (IOException e) { Log.e("ImageLoad", "IO Exception", e); } return null; } private void addNodeToScene(ArFragment arFragment, Anchor anchor, Renderable renderable) { AnchorNode anchorNode = new AnchorNode(anchor); TransformableNode node = new TransformableNode(arFragment.getTransformationSystem()); node.setRenderable(renderable); node.setParent(anchorNode); arFragment.getArSceneView().getScene().addChild(anchorNode); node.select(); } }
现在运行您的应用程序。 您应该看到如下所示的屏幕。 将手机稍微移到参考对象上方。 并且,一旦ARCore在现实世界中检测到参考图像,它就会向其中添加3D模型。

另请阅读: 创建您的第一个ARCore应用程序