首页 > 解决方案 > 如何在运行时在谷歌 ar 核心中的 ArSceneView 中切换 2D 纹理

问题描述

我们都在 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 中)。发现了一个类似的问题(对于 3D ),但没有答案。

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

标签: androidaugmented-realityarcoresceneform

解决方案


我在Kristina Simakova开发的这个kotlin 应用程序中找到了解决方案。

看来我是在正确的轨道上。我错过的是该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变量,该变量根据单击的按钮获取值。


推荐阅读