ARCore Sceneform框架使用(1)

3,805 阅读3分钟

Sceneform

Sceneform是google的ARCore开发团队提供的一个方便使用ARCore进行开发的框架(目前已停止更新),利用Sceneform,无需学习OpenGL即可轻松的在AR和非AR应用中渲染逼真的3D场景,其提供了ArFragment来显示可以渲染虚拟物体的场景,以及相关的一些如模型导入、平面处理、材质自定义、手势控制等功能。

使用说明

ARCore由于对性能的要求较高,而且需要针对不同机型的相机做出适配来提高AR的水平,所以并不是所有手机都是支持的,官网有支持机型的列表,而且需要android版本至少为24。由于华为被封锁的原因,华为的新机型均不支持。此外,运行ARCore的必须软件为Google Play Services for AR,手机应用商店可下载,如无,则需要Google Play.

项目配置

1.build.gradle(project)中需要有google的maven仓库

allprojects {
    repositories {
        google()
        …

2.在build.gradle(app)中添加ARCore和Sceneform的依赖项

android {
    …
    defaultConfig {
        // Sceneform 最低android adk的版本为24(N)
        minSdkVersion 24
        …
    }
    // Sceneform libraries use language constructs from Java 8.
    // Add these compile options if targeting minSdkVersion < 26.
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies {
    …
    // arcore的版本,如仅使用sceneform可不导入
    implementation 'com.google.ar:core:1.5.0'

    // sceneform库,1.7.1为最新版本但是与1.5功能一致,支持通过插件导入obj文件,1.6版本支持gltf格式的导入
    implementation 'com.google.ar.sceneform.ux:sceneform-ux:1.5.0'
    implementation 'com.google.ar.sceneform:core:1.5.0'
}

引入ArFragment

在需要用到AR的页面的xml文件中引入ArFragment。ArFragment是Sceneform自带的用于显示虚拟场景的Fragment,封装好了apk请求安装、权限请求、找平动画、平面显然、手势控制、点击事件等。

<fragment android:name="com.google.ar.sceneform.ux.ArFragment"
    android:id="@+id/ux_fragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
...//可以添加相关工具组件,如返回按钮、拍照按钮、分享按钮等。

几行代码就算是完成了一大步了,这里事实上可以直接使用了,进去会请求相机权限,请求安装 Google Play Services for AR 的APK,会进行找平,但是因为没有导入模型以及设置监听,并不能点击放置模型。

模型导入

如使用1.5版本的Scenefomr或者1.7版本的Sceneform,模型的源文件是obj、fbx、gltf等格式时,是无法直接使用的,需要转化为sfb二进制文件才能引入项目使用。对此,sceneform提供了一个很方便的AS插件。

1.导入Google Sceneform Tools(Beta)插件

在Android Studio中,打开Plugins导入方式:

  • windows : File > Settings > Plugins > MarketPlace
  • macOS : Android Studio > Preferences > Plugins 2.导入文件 在app目录下创建sampledata文件夹,右键点击app文件夹,然后选择:New > Sample Data Directory。 如右键没有也可建立一个directory命名为sampledata。 然后将模型相关的所有文件,如mtl、bin、png、jpg等都复制到sampledata文件夹中

3.Gradle 配置

在build.gradle(app)中添加如下代码

apply plugin: 'com.google.ar.sceneform.plugin'

sceneform.asset('sampledata/models/andy.obj', // 'Source Asset Path' specified during import.
                'default',                    // 'Material Path' specified during import.
                'sampledata/models/andy.sfa', // '.sfa Output Path' specified during import.
                'src/main/res/raw/andy')      // '.sfb Output Path' specified during import.

4.点击build

编译完成后在相应目录下会生成*.sfa和*.sfb文件,sfa文件时sfb的说明文件,修改sfa里的相应配置然后再编译的话可以修改生成的sfb文件的内容,这个之后再说

UI文件编写

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (!checkIsSupportedDeviceOrFinish(this)) {
            return;
        }

        setContentView(R.layout.activity_ux);
        arFragment = (ArFragment) getSupportFragmentManager().findFragmentById(R.id.ux_fragment);

        // 创建Renderable
        ModelRenderable.builder()
                .setSource(this, R.raw.andy) //通过id读取raw下的sfb文件,也可通过Uri读取网络或本地文件
                .build()
                .thenAccept(renderable -> andyRenderable = renderable)
                .exceptionally(
                        throwable -> {
                            Toast toast =
                                    Toast.makeText(this, "Unable to load andy renderable",
                                            Toast.LENGTH_LONG);
                            toast.setGravity(Gravity.CENTER, 0, 0);
                            toast.show();
                            return null;
                        });
        //顾名思义,只有你点击平面才会触发这个监听
        arFragment.setOnTapArPlaneListener(
                (HitResult hitResult, Plane plane, MotionEvent motionEvent) -> {
                    if (andyRenderable == null) {
                        return;
                    }

                    // 创建锚点,确定在空间中的坐标以及姿态
                    Anchor anchor = hitResult.createAnchor();
                    AnchorNode anchorNode = new AnchorNode(anchor);
                    anchorNode.setParent(arFragment.getArSceneView().getScene());

                    // 创建TransformableNode,其封装了缩放、移动、旋转等手势
                    TransformableNode andy = new TransformableNode(
                            arFragment.getTransformationSystem());
                    // 每一个点都需要设置父节点
                    andy.setParent(anchorNode);
                    andy.setRenderable(andyRenderable);
                    andy.select();
                });
    }
    // android 版本检查以及OpenGL版本检查
    public static boolean checkIsSupportedDeviceOrFinish(final Activity activity) {
        if (Build.VERSION.SDK_INT < VERSION_CODES.N) {
            Log.e(TAG, "Sceneform requires Android N or later");
            Toast.makeText(activity, "Sceneform requires Android N or later", Toast.LENGTH_LONG)
                    .show();
            activity.finish();
            return false;
        }
        String openGlVersionString =
                ((ActivityManager) activity.getSystemService(Context.ACTIVITY_SERVICE))
                        .getDeviceConfigurationInfo()
                        .getGlEsVersion();
        if (Double.parseDouble(openGlVersionString) < MIN_OPENGL_VERSION) {
            Log.e(TAG, "Sceneform requires OpenGL ES 3.0 later");
            Toast.makeText(activity, "Sceneform requires OpenGL ES 3.0 or later", Toast.LENGTH_LONG)
                    .show();
            activity.finish();
            return false;
        }
        return true;
    }

PS

到这里其实已经初体验AR了,这是官方的Demo,接下来我会说明一些自定义的方式。