三个.js中单个网格的多个UV/纹理



我有一个使用四种纹理的 OBJ。 文件中定义的 UV 范围为

(0, 0) 到 (2, 2),因此 (0.5, 0.5) 是指第一个纹理中的坐标,(0.5, 1.5) 是第二个纹理中的 UV 坐标,(1.5, 0.5) 是第三个纹理中的坐标,(1.5, 1.5) 是最后一个纹理中的坐标。我已经有了正确的三个.js几何或对象。 但是,我现在需要能够将正确的纹理贴图应用于这些对象。

在代码中:

我有一个具有正确几何形状的THREE.Mesh(具有 UV 坐标,例如 U = [0, 2], V = [0, 2])和一个虚拟占位符材料。 我目前加载一个纹理,如下所示:

var texture = new THREE.TextureLoader().load('tex_u1_v1.png', function() {
object.material.map = texture;
object.material.map.needsUpdate = true;
});

正如预期的那样,四分之一的网格被正确纹理化。 我还有三个纹理文件,tex_u1_v2.pngtex_u2_v1.pngtex_u2_v2.png。 我希望能够将这些纹理也应用于object(THREE.js网格),以便网格中的每个有效UV都有一个纹理。

但是,我不知道如何在创建后向object添加多种材料。 此外,我不知道如何向网格指定tex_u1_v2.png,例如,应该用于范围内的 UV(U = [0, 2], V = [1, 2])。

Three 中的标准材质将只接受单个纹理对象作为各种贴图参数(纹理对象将只容纳单个图像),因此为了在对象上使用多个纹理,您必须使用多种材质或创建自己的多纹理材质. 如果您有着色器编程的经验,则后一种方法可能会获得最佳性能(假设您有足够的视频内存用于大型纹理),因为您可以在单个绘制调用中绘制整个网格,而无需加载新的着色器或纹理。

要创建自己的着色器,您可以使用ShaderMaterialRawShaderMaterial,为您需要的每个纹理(在您的情况下为四个)提供一个纹理均匀,然后在着色器代码中根据坐标选择正确的纹理进行采样。

要使对象使用多个材质,您可以将material属性设置为材质数组(在使用构造函数参数创建期间,或者只是在稍后阶段手动替换它)。

const myMaterials = [tex1Material, tex2Material, tex3Material, tex4Material];
const myMesh = new THREE.Mesh(myGeometry, myMaterials);
//Or:
myMesh.materials = myMaterials;

然后,要使网格的不同部分使用适当的材质,您必须创建groups(如果它是BufferGeometry);或者如果您使用的是Geometry,则必须设置面的materialIndex。材料索引(在组和面中)是上面显示的mesh.material数组中材料的索引。

现在,网格的不同部分使用了不同的材质,您可以为每个材质赋予自己的纹理。

  • 获取正确的 UV 坐标的最简单方法 纹理将只将每个部分保持在 [0,1] 区间内。因为 网格的每个部分都使用您不必担心的独特材料 关于重叠坐标。

如果您不想修改现有的坐标,有两种替代方法:

  • 将纹理包装设置为THREE.RepeatWrapping

    myTexture.wrapS = THREE.RepeatWrapping;
    myTexture.wrapT = THREE.RepeatWrapping;
    

    这将使纹理重复超过标准 [0-1] UV 间隔。

  • 另一种方法是使用纹理的offset属性来 将其推回 [0-1] 区间。用于放置纹理 U[0,1], V[1,2] 间隔 您将设置 偏移量 v 坐标 通过 -1:

    myTexture.offset = new THREE.Vector2(0, -1);
    

下面是演示这些方法的 jsfiddle 的链接: https://jsfiddle.net/xfehvmb4/

我有一个三。使用正确的几何体进行网格划分(使用 UV 坐标使得 U = [0, 2], V = [0, 2])

在这种情况下,您对"正确"的理解可能是不正确的。让我们考虑一个四面体,并映射它,产生 4 个三角形。让我们将每个三角形放入它在紫外线空间中自己的象限中。四个象限将是:

02--12--22
| B | C |
01--11--21
| A | D |
00--10--20

我不完全确定如何最好地传达这一点,但如果有意义的话,纹理的查找总是在四边形 A (0011) 中完成。一旦一个值低于 0 或高于 1,它就会环绕并查找相同的空间。所以,这里的所有4个三角形(ABCD)实际上是重叠的。在此范围之外不存在纹理。您要么夹在边缘像素上,要么环绕(或镜像)。

可能有充分的理由让 UV 超出此范围,但这在您的情况下没有多大意义。我想你没有任何三角形穿过这些边界,所以你不妨只有 4 个不同的网格,在 0,1 域中有自己的 UV,使用它们自己的纹理。

也可以通过使用一系列材料和设置组来实现另一个答案。这就是您所需要的一切。当它渲染网格的uv都在1,1,2,2中时,它将与它们在0,0,1,1中完全相同。

@Jave的回答对我来说看起来不错,但如果你想走ShaderMaterial路线,你会怎么做:

// Make the material
var material = new new THREE.ShaderMaterial({
uniforms: {
u_tex1: {value: null},
u_tex2: {value: null},
u_tex3: {value: null},
u_tex4: {value: null},
},
vertexShader: `
varying vec2 v_uv;
varying float v_textureIndex;
void main() {
// This maps the uvs you mentioned to [0, 1, 2, 3]
v_textureIndex = step(0.5, uv.x) + step(0.5, uv.y) * 2.0;
v_uv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
varying vec2 v_uv;
varying float v_textureIndex;
uniform sampler2D u_tex1;
uniform sampler2D u_tex2;
uniform sampler2D u_tex3;
uniform sampler2D u_tex4;
void main() {
vec4 color = texture2D(u_tex1, v_uv);
// These lines make sure you get the right texture
color = mix(color, texture2D(u_tex2, v_uv), step(0.5, v_textureIndex));
color = mix(color, texture2D(u_tex3, v_uv), step(1.5, v_textureIndex));
color = mix(color, texture2D(u_tex4, v_uv), step(2.5, v_textureIndex));
gl_FragColor = color;
}
`,
});
var texture1 = new THREE.TextureLoader().load('tex_u1_v1.png', function() { material.uniforms.u_tex1.value = texture1 });
var texture2 = new THREE.TextureLoader().load('tex_u2_v1.png', function() { material.uniforms.u_tex2.value = texture2 });
var texture3 = new THREE.TextureLoader().load('tex_u1_v2.png', function() { material.uniforms.u_tex3.value = texture3 });
var texture4 = new THREE.TextureLoader().load('tex_u2_v2.png', function() { material.uniforms.u_tex4.value = texture4 });

这效率略低,因为您正在执行 4 个纹理样本,但非常灵活。

例如,也可以为一个醪液组合不同类型的纹理

  • 凹凸图
  • 弥漫 性
  • 法线贴图 ...等

我下载了 @types/3,它对我理解 MeshPhongMaterial 的属性有很大帮助,它实例化了我的网格材料。有道具bumpMapmapnormalMap等。还有一个color属性可以改变纹理的颜色。

一周后尝试调试代码,我找到了这个解决方案:

  1. 如果加载了 3D 对象,则在任何 3D 软件(示例搅拌机)中将其设置为 uv 到 Modal,这将在三个代码中为对象添加更多网格并导出。然后加载 Threejs。

  2. 您可以通过控制台记录 3D 对象来获取网格详细信息:

loader.load(
'./../../assets/img/3d/1/tshirtv4.glb',
gltf => {
let tshirtObj = gltf.scene;
tshirtObj.position.set(0, 0, 0);
// text canvas as Texture function
console.log('Modal', tshirtObj);
let MeshCommon = tshirtObj.getObjectByName('t-shirt002_1');
let MeshBack = tshirtObj.getObjectByName('t-shirt002_2');
let MeshSleeve = tshirtObj.getObjectByName('t-shirt002_3');

const texturePatchUV2 = tshirtObj.getObjectByName('t-shirt002_3').geometry.attributes.uv2.array;
tshirtObj.getObjectByName('t-shirt002_3').geometry.attributes.uv.array = texturePatchUV2;
let texturePatchUV1 = tshirtObj.getObjectByName('t-shirt002_3').geometry.attributes.uv.array
**console.log('Patchuv', texturePatchUV1)**
function sceneUpdate() {
//console.log('Mesh Patch material', MeshSleeve.material);
const material = materialV2();
gltf.scene.traverse(child => {
if (child.isMesh) {
//child.material = material[0];
MeshCommon.material = material[0];
MeshBack.material = material[1];
MeshSleeve.material = material[2];
// child.material = materialV2();
}
});
scene.add(tshirtObj);
}
sceneUpdate();
jsNameInput.addEventListener('input', sceneUpdate);
jsNumberInput.addEventListener('input', sceneUpdate);
},
// called while loading is progressing
function (xhr) {
// console.log((xhr.loaded / xhr.total) * 100 + '% loaded');
},
// called when loading has errors
function (error) {
console.error(error);
}
);
  1. 然后将UV贴图替换为所需的uvmap,我将uv2分配给uv,如屏幕截图所示:
const texturePatchUV2= tshirtObj.getObjectByName('t-shirt002_3').geometry.attributes.uv2.array;
tshirtObj.getObjectByName('t-shirt002_3').geometry.attributes.uv.array = texturePatchUV2;
let texturePatchUV1 = tshirtObj.getObjectByName('t-shirt002_3').geometry.attributes.uv.array
console.log('Patchuv', texturePatchUV1)

最新更新