2

我已经成功地在 AR 场景中的两个向量之间画了一条线。

我的代码:

private void addLineBetweenPoints(Scene scene, Vector3 from, Vector3 to) {
        // prepare an anchor position
        Quaternion camQ = scene.getCamera().getWorldRotation();
        float[] f1 = new float[]{to.x, to.y, to.z};
        float[] f2 = new float[]{camQ.x, camQ.y, camQ.z, camQ.w};
        Pose anchorPose = new Pose(f1, f2);

        // make an ARCore Anchor
        Anchor anchor = mCallback.getSession().createAnchor(anchorPose);
        // Node that is automatically positioned in world space based on the ARCore Anchor.
        AnchorNode anchorNode = new AnchorNode(anchor);
        anchorNode.setParent(scene);

        // Compute a line's length
        float lineLength = Vector3.subtract(from, to).length();

        // Prepare a sampler
        Texture.Sampler sampler = Texture.Sampler.builder()
                .setMinFilter(Texture.Sampler.MinFilter.LINEAR_MIPMAP_LINEAR)
                .setMagFilter(Texture.Sampler.MagFilter.LINEAR)
                .setWrapModeR(Texture.Sampler.WrapMode.REPEAT)
                .setWrapModeS(Texture.Sampler.WrapMode.REPEAT)
                .setWrapModeT(Texture.Sampler.WrapMode.REPEAT)
                .build();

        // 1. Make a texture
        Texture.builder()
                .setSource(() -> getContext().getAssets().open("textures/aim_line.png"))
                .setSampler(sampler)
                .build().thenAccept(texture -> {
                    // 2. make a material by the texture
                    MaterialFactory.makeTransparentWithTexture(getContext(), texture)
                        .thenAccept(material -> {
                            // 3. make a model by the material
                            ModelRenderable model = ShapeFactory.makeCylinder(0.0025f, lineLength,
                                    new Vector3(0f, lineLength / 2, 0f), material);
                            model.setShadowReceiver(false);
                            model.setShadowCaster(false);

                            // make node
                            Node node = new Node();
                            node.setRenderable(model);
                            node.setParent(anchorNode);

                            // set rotation
                            final Vector3 difference = Vector3.subtract(to, from);
                            final Vector3 directionFromTopToBottom = difference.normalized();
                            final Quaternion rotationFromAToB =
                                    Quaternion.lookRotation(directionFromTopToBottom, Vector3.up());
                            node.setWorldRotation(Quaternion.multiply(rotationFromAToB,
                                    Quaternion.axisAngle(new Vector3(1.0f, 0.0f, 0.0f), 90)));
                    });
        });
    }

它工作得很好,但我有一个错误的纹理。在文件“textures/aim_line.png”中包含PNG:(线的一半是透明的,另一半是橙色的。)

瞄准线.png

我目前的结果:

当前结果

但我期待下一个结果:

预期结果

因此,我使用了写有“WrapMode.REPEAT”的采样器,但纹理不会重复,只会拉伸。

如何在 Android ArCore Sceneform API 中的对象上设置重复纹理?

4

1 回答 1

5

查看圆柱模型,它有一个 0 到 1 的 UV 贴图。这用于将纹理映射到网格上。0,0 是纹理的左下角,1,1 是右上角。只有当模型上的 UV 坐标 > 1.0 时,才会使用采样器上的环绕配置。在这种情况下,它会根据设置进行夹紧或重复。由于圆柱体已被约束为 0,1,因此纹理始终被拉伸。

解决此问题的替代方法是为您自己的圆柱体建模并根据需要设置 UV 坐标,或者在采样之前使用自定义材料来操纵 UV 坐标。

您可以使用 Blender 或 Maya 或其他 3D 建模工具制作模型。

自定义材质特定于 Sceneform,因此步骤如下:

  1. 创建一个虚拟模型以在加载自定义材料时使用
  2. 编写重复纹理的自定义材质
  3. 在运行时加载虚拟模型并获取材质
  4. 在自定义材质上设置参数。

创建一个虚拟模型

我使用了我身边的飞机 OBJ 模型。模型是什么并不重要,我们只需要它来加载材料。app/sampledata/materials在命名中创建一个文件dummy.obj

o Plane
v 0.500000 0.500000 0.000000
v  -0.500000 0.500000 0.000000
v  0.500000 -0.500000 0.000000
v  -0.500000 -0.500000 0.000000
vt 0.000000 1.000000
vt 0.000000 0.000000
vt 1.000000 0.000000
vt 1.000000 1.000000
vn 0.0000 0.0000 1.0000
usemtl None
s off
f 1/1/1 2/2/1 4/3/1 3/4/1

编写自定义材料

定义材料参考描述了 中的每个元素repeating_texture.mat

// Sample material for repeating a texture.
//
// the repeating factor is given as repeat_x,
// repeat_y as a factor multipled by the UV
// coordinate.
material {
    "name" : "RepeatingTexture",
   parameters : [
   {
      type : sampler2d,
      name : texture
   },

    {
        type: float,
        name:"repeat_x"
    },
    {
            type: float,
            name: "repeat_y"
    }
   ],
   requires : [
       "position",
       "uv0"
   ],

}
fragment {
    void material(inout MaterialInputs material) {
        prepareMaterial(material);

        vec2 uv = getUV0();
        uv.x = uv.x * materialParams.repeat_x;
        uv.y = uv.y * materialParams.repeat_y;

        material.baseColor = texture(materialParams_texture, uv).rgba;
    }
}

将模型和材料添加到构建中

这增加了将模型和材料编译到.sfb文件中的步骤。补充app/build.gradle

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

sceneform.asset('sampledata/materials/dummy.obj',
        "sampledata/materials/repeating_texture.mat",
        'sampledata/materials/dummy.sfa',
        'src/main/res/raw/material_holder')

您还需要将 Sceneform 添加到顶层的 buildscript 类路径中build.gradle

    dependencies {
        classpath 'com.android.tools.build:gradle:3.2.1'
        classpath 'com.google.ar.sceneform:plugin:1.5.1'
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

在运行时加载材质

onCreate()通话中:

ModelRenderable.builder().setSource(this, R.raw.material_holder) .build().thenAccept(modelRenderable -> repeatingMaterial = modelRenderable.getMaterial());

这将材料存储在成员字段中repeatingMaterial

在材质上设置参数

将您的原始代码修改为:

  private void addLineBetweenPoints(AnchorNode from, Vector3 to) {
    // Compute a line's length
    float lineLength = Vector3.subtract(from.getWorldPosition(), to).length();
    // repeat the pattern every 10cm
    float lengthCM = lineLength * 100;

    repeatingMaterial.setFloat("repeat_x", lengthCM/10);
    repeatingMaterial.setFloat("repeat_y", lengthCM/10);
                // 3. make a model by the material
                ModelRenderable model = ShapeFactory.makeCylinder(0.0025f, lineLength,
                        new Vector3(0f, lineLength / 2, 0f), repeatingMaterial);
                model.setShadowReceiver(false);
                model.setShadowCaster(false);
                // make node
                Node node = new Node();
                node.setRenderable(model);
                node.setParent(from);
                // set rotation
                final Vector3 difference = Vector3.subtract(from.getWorldPosition(), to);
                final Vector3 directionFromTopToBottom = difference.normalized();
                final Quaternion rotationFromAToB =
                        Quaternion.lookRotation(directionFromTopToBottom, Vector3.up());
                node.setWorldRotation(Quaternion.multiply(rotationFromAToB,
                        Quaternion.axisAngle(new Vector3(1.0f, 0.0f, 0.0f), 90)));
  }
于 2018-11-26T18:16:46.537 回答