Hec*_*tor 2 png android arcore sceneform
我正在调查Android上的增强现实.
我在Android应用程序中使用ARCore和Sceneform.
我已经尝试了示例项目,现在想开发自己的应用程序.
我想要实现的一个效果是将图像(例如.jpeg或.png)与来自摄像机设备的实时馈送组合/叠加.
图像将具有透明背景,允许用户同时查看实时馈送和图像
但是我不希望覆盖的图像是固定/静态水印,当用户放大,缩小或平移覆盖的图像时,还必须放大,缩小和平移等.
我不希望被夸大的图像变成3d或任何那种性质.
Sceneform可以实现这种效果吗?或者我是否需要使用其他第三方库和/或工具来实现所需的结果.
UPDATE
用户正在画一张白纸.纸张定向使得使用者舒适地拉伸(左手或右手).用户可以在完成图像时自由移动纸张.
Android设备位于纸张上方,拍摄用户绘制所选图像.
现场摄像机正在投射到大型电视或监视器屏幕.
为了帮助用户,他们选择了静态图像来"跟踪"或"复制".
此图像在Android设备上选择,并与Android应用程序中的实时摄像头流组合.
用户可以放大和缩小他们的绘图,并且组合的实时流和选定的静态图像也将放大和缩小,这将使用户能够通过绘制"自由手"来精确复制所选择的静态图像.
当用户直接看纸张时,他们只能看到他们的图纸.
当用户在电视或监视器上观看他们的演员实况流时,他们会看到他们的绘图和叠加的所选静态图像.用户可以控制静态图像的透明度,以帮助他们准确地复制静态图像.
我认为您正在寻找的是使用AR来显示图像,以便图像保持在原位,例如在一张纸上,以便作为在纸张上绘制图像副本的指南.
这有两个部分.首先是找到纸张,第二个是将图像放在纸张上,并在手机四处移动时将图像放在纸张上.
只需用纸张检测平面即可完成纸张的定位(有一些对比度,或图案或某些东西与纯白纸张有帮助),然后点击页面中心的位置.这是在HelloSceneform示例中完成的.
如果您想要更准确地定义纸张边界,可以点击纸张的4个角,然后在那里创建锚点.要做到这一点,请注册一个平面轻敲的侦听器onCreate()
arFragment.setOnTapArPlaneListener(this::onPlaneTapped);
Run Code Online (Sandbox Code Playgroud)
然后在onPlaneTapped中,创建4个anchorNodes.获得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();
}
}
Run Code Online (Sandbox Code Playgroud)
要初始化绘图,请从位图或drawable创建一个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);
});
}
Run Code Online (Sandbox Code Playgroud)
保持纹理的模型只是一个扁平的四边形,大小与角之间的最小尺寸.这与使用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);
}
Run Code Online (Sandbox Code Playgroud)
最后一部分是将四边形定位在角落的中心,并创建一个可变形节点,以便可以将图像轻推到位,旋转或缩放为完美尺寸.
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);
}
Run Code Online (Sandbox Code Playgroud)