2

我正在研究 Android 上的增强现实。

我在 Android 应用程序中使用 ARCore 和 Sceneform。

我已经尝试了示例项目,现在想开发自己的应用程序。

我想要实现的一种效果是将图像(例如 .jpeg 或 .png)与来自车载摄像头的设备的实时馈送结合/叠加。

图像将具有透明背景,允许用户同时查看实时提要和图像

但是我不希望覆盖的图像是固定/静态水印,当用户放大、缩小或平移时,覆盖的图像还必须放大、缩小和平移等。

我不希望过度播放的图像变成 3d 或任何类似性质的东西。

使用 Sceneform 可以实现这种效果吗?还是我需要使用其他 3rd 方库和/或工具来达到预期的结果。

更新

用户正在一张白纸上绘图。纸张被定向,以便用户舒适地绘图(左手或右手)。用户在完成图像时可以自由移动纸张。

一个安卓设备被放在一张纸上,拍摄用户绘制他们选择的图像。

实时摄像机源正在投射到大型电视或监视器屏幕上。

为了帮助用户,他们选择了要“跟踪”或“复制”的静态图像。

此图像是在 Android 设备上选择的,并且正在与 Android 应用程序中的实时摄像头流相结合。

用户可以放大和缩小他们的绘图,并且组合的直播流和选定的静态图像也会放大和缩小,这将使用户能够通过绘制“手绘”来精确复制选定的静态图像。

当用户直接看这张纸时,他们只会看到他们的图画。

当用户在电视或监视器上观看他们的演员直播时,他们会看到他们的绘画和叠加的所选静态图像。用户可以控制静态图像的透明度,以帮助他们制作准确的副本。

4

2 回答 2

5

我认为您正在寻找的是使用 AR 来显示图像,以便图像保持原位,例如在一张纸上,以便作为在纸上绘制图像副本的指南。

这有两个部分。首先是定位纸张,其次是将图像放在纸上并在手机四处移动时将其保留在那里。

只需通过检测纸张的平面(与一张普通的白纸相比,有一些对比度、图案或其他东西会有所帮助),然后点击页面中心应该在的位置,就可以找到这张纸。这是在 HelloSceneform 示例中完成的。

如果您想要更准确的纸张边界,您可以点击纸张的 4 个角,然后在此处创建锚点。为此,请在其中注册一个平面窃听器onCreate()

   arFragment.setOnTapArPlaneListener(this::onPlaneTapped);

然后在 onPlaneTapped 中,创建 4 个锚节点。一旦你有 4,初始化要显示的图形。

private void onPlaneTapped(HitResult hitResult, Plane plane, MotionEvent event) {
    if (cornerAnchors.size() != 4) {
        AnchorNode corner = createCornerNode(hitResult.createAnchor());
        arFragment.getArSceneView().getScene().addChild(corner);
        cornerAnchors.add(corner);
    }

    if (cornerAnchors.size() == 4 && drawingNode == null) {
        initializeDrawing();
    }
}

要初始化绘图,请从位图或可绘制对象创建一个 Sceneform 纹理。这可以来自资源或文件 URL。您希望纹理显示整个图像,并随着持有它的模型调整大小而缩放。

private void initializeDrawing() {
    Texture.Sampler sampler = Texture.Sampler.builder()
            .setWrapMode(Texture.Sampler.WrapMode.CLAMP_TO_EDGE)
            .setMagFilter(Texture.Sampler.MagFilter.NEAREST)
            .setMinFilter(Texture.Sampler.MinFilter.LINEAR_MIPMAP_LINEAR)
            .build();
    Texture.builder()
            .setSource(this, R.drawable.logo_google_developers)
            .setSampler(sampler)
            .build()
            .thenAccept(texture -> {
                MaterialFactory.makeTransparentWithTexture(this, texture)
                        .thenAccept(this::buildDrawingRenderable);
            });
}

保存纹理的模型只是一个平面四边形,大小为角之间的最小尺寸。这与使用 OpenGL 布局四边形的逻辑相同。

private void buildDrawingRenderable(Material material) {

    Integer[] indices = {
            0, 1, 3, 3, 1, 2
    };

    //Calculate the center of the corners.
    float min_x = Float.MAX_VALUE;
    float max_x = Float.MIN_VALUE;
    float min_z = Float.MAX_VALUE;
    float max_z = Float.MIN_VALUE;
    for (AnchorNode node : cornerAnchors) {
        float x = node.getWorldPosition().x;
        float z = node.getWorldPosition().z;
        min_x = Float.min(min_x, x);
        max_x = Float.max(max_x, x);
        min_z = Float.min(min_z, z);
        max_z = Float.max(max_z, z);
    }

    float width = Math.abs(max_x - min_x);
    float height = Math.abs(max_z - min_z);
    float extent = Math.min(width / 2, height / 2);

    Vertex[] vertices = {
            Vertex.builder()
                    .setPosition(new Vector3(-extent, 0, extent))
                    .setUvCoordinate(new Vertex.UvCoordinate(0, 1)) // top left
                    .build(),
            Vertex.builder()
                    .setPosition(new Vector3(extent, 0, extent))
                    .setUvCoordinate(new Vertex.UvCoordinate(1, 1)) // top right
                    .build(),
            Vertex.builder()
                    .setPosition(new Vector3(extent, 0, -extent))
                    .setUvCoordinate(new Vertex.UvCoordinate(1, 0)) // bottom right
                    .build(),
            Vertex.builder()
                    .setPosition(new Vector3(-extent, 0, -extent))
                    .setUvCoordinate(new Vertex.UvCoordinate(0, 0)) // bottom left
                    .build()
    };

    RenderableDefinition.Submesh[] submeshes = {
            RenderableDefinition.Submesh.builder().
                    setMaterial(material)
                    .setTriangleIndices(Arrays.asList(indices))
                    .build()
    };

    RenderableDefinition def = RenderableDefinition.builder()
            .setSubmeshes(Arrays.asList(submeshes))

            .setVertices(Arrays.asList(vertices)).build();

    ModelRenderable.builder().setSource(def)
            .setRegistryId("drawing").build()
            .thenAccept(this::positionDrawing);
}

最后一部分是将四边形定位在角落的中心,并创建一个可变形节点,以便图像可以轻推到位、旋转或缩放到完美尺寸。

private void positionDrawing(ModelRenderable drawingRenderable) {


    //Calculate the center of the corners.
    float min_x = Float.MAX_VALUE;
    float max_x = Float.MIN_VALUE;
    float min_z = Float.MAX_VALUE;
    float max_z = Float.MIN_VALUE;
    for (AnchorNode node : cornerAnchors) {
        float x = node.getWorldPosition().x;
        float z = node.getWorldPosition().z;
        min_x = Float.min(min_x, x);
        max_x = Float.max(max_x, x);
        min_z = Float.min(min_z, z);
        max_z = Float.max(max_z, z);
    }

    Vector3 center = new Vector3((min_x + max_x) / 2f,
            cornerAnchors.get(0).getWorldPosition().y, (min_z + max_z) / 2f);

    Anchor centerAnchor = null;
    Vector3 screenPt = arFragment.getArSceneView().getScene().getCamera().worldToScreenPoint(center);
    List<HitResult> hits = arFragment.getArSceneView().getArFrame().hitTest(screenPt.x, screenPt.y);
    for (HitResult hit : hits) {
        if (hit.getTrackable() instanceof Plane) {
            centerAnchor = hit.createAnchor();
            break;
        }
    }

    AnchorNode centerNode = new AnchorNode(centerAnchor);
    centerNode.setParent(arFragment.getArSceneView().getScene());

    drawingNode = new TransformableNode(arFragment.getTransformationSystem());
    drawingNode.setParent(centerNode);
    drawingNode.setRenderable(drawingRenderable);
}
于 2018-05-17T20:45:58.260 回答
0

预期的 AR 参考图像可以使用 ARobjects 进行缩放,作为用户调整模板大小的点。

更复杂的 AR 图像不容易工作,因为 AR 图像覆盖在用户跟踪的顶部,这会阻碍他们的笔/铅笔的尖端。

我的解决方案是对白皮书进行染色。这将用所选图像或实时源替换白皮书。除非您有跟踪纸张位置的方法,否则按照您的指定移动纸张将是一个问题。

正如您在此示例中看到的,AR 对象在前面,而 ​​chromakey 是背景。跟踪表面(纸)将位于中心。

此示例的参考位于下面的链接中。

RJ

于 2018-05-19T02:07:40.090 回答