google ar core 在运行时如何在 ArSceneView 中切换 2D 纹理

How to switch a 2D texture in ArSceneView in google ar core at runtime

我们都在 Instagram 或 Snapchat 或任何其他应用程序上使用过增强面部纹理。我正在尝试开发一个示例应用程序,可以在其中尝试在他们的脸上尝试一些 ar 对象。到目前为止,我已经能够在脸上显示该对象。

然后我在下方(在屏幕上)显示项目列表,以便用户可以将对象切换到其他对象(类似于在 instagram/snapchat 上试用新过滤器)。

这是一个对象的默认代码:

         Texture.builder()
               .setSource(this, R.drawable.fox_face_mesh_texture)
                .build()
                .thenAccept(texture -> faceMeshTexture1 = texture);

        ArSceneView sceneView = arFragment.getArSceneView();
        sceneView.setCameraStreamRenderPriority(Renderable.RENDER_PRIORITY_FIRST);
        Scene scene = sceneView.getScene();
        scene.addOnUpdateListener(new Scene.OnUpdateListener() {
            @Override
            public void onUpdate(FrameTime frameTime) {
                if (faceMeshTexture1 == null)
                    return;

                Collection<AugmentedFace> faceList =
                        sceneView.getSession().getAllTrackables(AugmentedFace.class);

                for (AugmentedFace face : faceList) {
                    if (!faceNodeMap.containsKey(face)) {
                        AugmentedFaceNode faceNode = new AugmentedFaceNode(face);
                        faceNode.setParent(scene);
                        faceNode.setFaceMeshTexture(faceMeshTexture1);
                        faceNodeMap.put(face, faceNode);
                    }
                }

                Iterator<Map.Entry<AugmentedFace, AugmentedFaceNode>> iter = 
                    faceNodeMap.entrySet().iterator();
                    while (iter.hasNext()) {
                        Map.Entry<AugmentedFace, AugmentedFaceNode> entry = iter.next();
                        AugmentedFace face = entry.getKey();
                        if (face.getTrackingState() == TrackingState.STOPPED) {
                            AugmentedFaceNode faceNode = entry.getValue();
                            faceNode.setParent(null);
                            iter.remove();
                        }
                    }
                }
        });

我试过的

在按钮 onClick 中 - 我通过 Texture.builder().setSource ...

创建了一个新的纹理

1) 我试图只写 for 循环 for (AugmentedFace face : faceList) { ... } 但它什么也没做。

2) 我试图包含 onUpdate() 函数的全部内容。但这也行不通。

3) 然后我尝试创建 2 个纹理并使用布尔变量通过反转布尔值(在单击按钮时)在这些纹理之间切换(在 onUpdate 方法中)。它看起来像这样:

         Texture.builder()
               .setSource(this, R.drawable.fox_face_mesh_texture)
                .build()
                .thenAccept(texture -> faceMeshTexture1 = texture);
        Texture.builder()
                .setSource(this, R.drawable.red_lipstick)
                .build()
                .thenAccept(texture -> faceMeshTexture2 = texture);



        ArSceneView sceneView = arFragment.getArSceneView();

        sceneView.setCameraStreamRenderPriority(Renderable.RENDER_PRIORITY_FIRST);
        Scene scene = sceneView.getScene();
        scene.addOnUpdateListener(new Scene.OnUpdateListener() {
            @Override
            public void onUpdate(FrameTime frameTime) {
                if (faceMeshTexture1 == null || faceMeshTexture2 == null)
                    return;

                Collection<AugmentedFace> faceList =
                        sceneView.getSession().getAllTrackables(AugmentedFace.class);

                for (AugmentedFace face : faceList) {
                    if (!faceNodeMap.containsKey(face)) {
                        AugmentedFaceNode faceNode = new AugmentedFaceNode(face);
                        faceNode.setParent(scene);
                        if( !redOrFox ) {
                            faceNode.setFaceMeshTexture(faceMeshTexture1);
                            Log.d(TAG, "onUpdate: redorfox:" + redOrFox);
                        }
                        else {
                            faceNode.setFaceMeshTexture(faceMeshTexture2);
                            Log.d(TAG, "onUpdate: else redorfox:" + redOrFox);
                        }
                        faceNodeMap.put(face, faceNode);
                    }
                }

                Iterator<Map.Entry<AugmentedFace, AugmentedFaceNode>> iter = faceNodeMap.entrySet().iterator();
                while (iter.hasNext()) {
                    Map.Entry<AugmentedFace, AugmentedFaceNode> entry = iter.next();
                    AugmentedFace face = entry.getKey();
                    if (face.getTrackingState() == TrackingState.STOPPED) {
                        AugmentedFaceNode faceNode = entry.getValue();
                        faceNode.setParent(null);
                        iter.remove();
                    }
                }
            }
        });

        mBtnBlueLip.setOnClickListener(v -> {
            redOrFox = true;
            Log.d(TAG, "onCreate: blue clicked");
        });

        mBtnRedLip.setOnClickListener(v -> {
            redOrFox = false;
            Log.d(TAG, "onCreate: red clicked");
        });

但这也没有做任何事情。在这一点上,我不知道该怎么做。几乎没有任何关于增强面孔的文档和一些示例应用程序(但在 Kotlin 中)。 找到了类似的 question(对于 3D)但没有答案。

编辑: 同样在调试之后,似乎只调用了一次 onUpdate 函数。我不明白这是怎么回事?

我在这个 kotlin app developed by Kristina Simakova 中找到了解决方案。

看来我走对了。我错过的是 face 对象已经在 faceList 数组中所以它根本不会进入这个 if 条件:

if (!faceNodeMap.containsKey(face)) { ... }

因为我没有别的,所以什么也没有发生。

解决方案是使用另一个布尔变量来检查按钮是否被点击!

Boolean isFox = false, changeModel = false;

...

scene.addOnUpdateListener(frameTime -> { 

...
  for (AugmentedFace face : faceList) {
    if (!faceNodeMap.containsKey(face)) {
        AugmentedFaceNode faceNode = new AugmentedFaceNode(face);
        faceNode.setParent(scene);
        faceNode.setFaceMeshTexture(faceMeshTexture1);
        faceNodeMap.put(face, faceNode);
    }
    else if (changeModel) {    // check whether we want to change texture
        if(isFox)
            faceNodeMap.get(face).setFaceMeshTexture(faceMeshTexture1);
        else
            faceNodeMap.get(face).setFaceMeshTexture(faceMeshTexture2);
    }
  }
changeModel = false; // after the for loop ends


...


    mBtnBlueLip.setOnClickListener(v -> {
        isFox = true;
        changeModel = true;
    });

    mBtnRedLip.setOnClickListener(v -> {
        changeModel = true;
        isFox = false;
    });

克里斯蒂娜使用了类似的方法。因为我正在测试应用程序并且只有 2 个纹理,所以我使用了一个布尔变量 isFox。您还可以维护一组纹理并仅保留一个 faceMeshTexture 变量,该变量根据单击的按钮获取值。