如何在 Android ArCore Sceneform API 中为对象设置重复纹理?
How I can set repeated texture on an object in Android ArCore Sceneform API?
我已经成功地在 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:
(线的一半是透明的,另一半是橙色的。)
我目前的成绩:
但我期待下一个结果:
所以,我使用了写在 "WrapMode.REPEAT" 处的采样器,但纹理不会重复,只会被拉伸。
如何在 Android ArCore Sceneform API 中的对象上设置重复纹理?
看看圆柱体模型,它有一个 0 到 1 的 UV 贴图。这用于将纹理映射到网格上。 0,0 是纹理的左下角,1,1 是右上角。采样器上的环绕配置仅在模型上的 UV 坐标 > 1.0 时使用。在这种情况下,它会根据设置进行钳制或重复。由于圆柱体已被限制为 0,1,因此纹理始终被拉伸。
解决此问题的替代方法是为您自己的圆柱体建模并根据需要设置 UV 坐标,或者使用自定义 material 在采样前操纵 UV 坐标。
您可以使用 Blender 或 Maya 或其他 3D 建模工具制作模型。
自定义 material 特定于 Sceneform,因此步骤如下:
- 创建一个虚拟模型以在加载自定义模型时使用 material
- 编写重复纹理的自定义material
- 在运行时加载虚拟模型并获得 material
- 设置自定义参数material。
创建虚拟模型
我使用了我周围的飞机 OBJ 模型。模型是什么并不重要,我们只需要它来加载 material。在 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
写自定义material
custom material reference描述了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;
}
}
添加模型并material构建
这增加了将模型和 material 编译成 .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 添加到顶层的构建脚本 class 路径 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
}
}
在运行时加载 material
在onCreate()
调用:
ModelRenderable.builder().setSource(这, R.raw.material_holder)
.build().thenAccept(
modelRenderable -> repeatingMaterial = modelRenderable.getMaterial());
这会将 material 存储在成员字段 repeatingMaterial
中。
在material
上设置参数
将您的原始代码修改为:
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)));
}
我已经成功地在 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: (线的一半是透明的,另一半是橙色的。)
我目前的成绩:
但我期待下一个结果:
所以,我使用了写在 "WrapMode.REPEAT" 处的采样器,但纹理不会重复,只会被拉伸。
如何在 Android ArCore Sceneform API 中的对象上设置重复纹理?
看看圆柱体模型,它有一个 0 到 1 的 UV 贴图。这用于将纹理映射到网格上。 0,0 是纹理的左下角,1,1 是右上角。采样器上的环绕配置仅在模型上的 UV 坐标 > 1.0 时使用。在这种情况下,它会根据设置进行钳制或重复。由于圆柱体已被限制为 0,1,因此纹理始终被拉伸。
解决此问题的替代方法是为您自己的圆柱体建模并根据需要设置 UV 坐标,或者使用自定义 material 在采样前操纵 UV 坐标。
您可以使用 Blender 或 Maya 或其他 3D 建模工具制作模型。
自定义 material 特定于 Sceneform,因此步骤如下:
- 创建一个虚拟模型以在加载自定义模型时使用 material
- 编写重复纹理的自定义material
- 在运行时加载虚拟模型并获得 material
- 设置自定义参数material。
创建虚拟模型
我使用了我周围的飞机 OBJ 模型。模型是什么并不重要,我们只需要它来加载 material。在 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
写自定义material
custom material reference描述了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;
}
}
添加模型并material构建
这增加了将模型和 material 编译成 .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 添加到顶层的构建脚本 class 路径 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
}
}
在运行时加载 material
在onCreate()
调用:
ModelRenderable.builder().setSource(这, R.raw.material_holder) .build().thenAccept( modelRenderable -> repeatingMaterial = modelRenderable.getMaterial());
这会将 material 存储在成员字段 repeatingMaterial
中。
在material
上设置参数将您的原始代码修改为:
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)));
}