【问题标题】:How to decrease and increase the size of an image on an object - ThreeJS如何减小和增加对象上图像的大小 - ThreeJS
【发布时间】:2017-01-16 06:01:01
【问题描述】:

我正在使用 THREE.ImageUtils.loadTexture 函数加载图像,然后将其粘贴到场景中的每个对象上,如下所示:

_.forEach(bags, (bag) => {
  if(bag['children'][0] && bag['children'][0].material) {
    let material = new THREE.MeshPhongMaterial({map: tex});
    bag['children'][0].material = material;
  }
});

我已经查看了tex.scale.x、tex.scale.y 和tex.offset.x、tex.offset.y,但这些似乎不是我需要的。这是对象目前的样子

但我对每一侧都进行了更严格的定位。像这样的东西(伪代码)会将左上角放置在该位置所需的一侧。

sideA.image.setSize(x,y);

有人有这方面的例子吗?

编辑

感谢到目前为止的反馈。 @Martin 我已经设置了一个基本的PlaneGeometry 形状来测试它。所以这个形状的脸是基本的两个相互连接的三角形;我附上了一张你发布的 uv 设置的图片:

因此,根据您的说法,我无法扩展其中一个三角形,因为这会破坏所使用的几何形状,因此我需要操纵两个三角形以正确移动图像。对吗?

【问题讨论】:

  • 需要了解您以哪种方式加载 obj(mesh) 以及您对纹理文件的控制程度...您可以为每一面制作一个带有纹理的网格,然后进行调整根据需要,或者您需要制作自己的 uv 坐标试试我正在做的事情stackoverflow.com/questions/29877032/…

标签: three.js


【解决方案1】:

不幸的是,在 three.js 中没有简单的方法可以做到这一点。

为了正确控制非平凡网格的纹理,您可能需要先对模型进行一些UV-unwrapping。这通常应该由提供网格的人来完成,因为在 blender、3dstudio、cinema4d 等工具中更容易做到这一点。

但是,我假设您由于某种原因没有那个人可用,并且需要在 javascript 中执行此操作。这种方式更有趣。让我们进一步假设,我们只想将图像映射到包的正面和背面,内部和边缘不会被纹理化。

在 three.js 和大多数其他 3D 应用程序中,纹理的精确映射是通过为每个面的每个顶点提供纹理坐标(也称为 UV 坐标)来实现的。这些 UV 坐标(范围从 0 到 1)指定纹理中属于顶点的位置。 (0, 0) 是图像的左下角,(1, 1) 是右上角,与图像的纵横比无关。

因此,对于由两个三角形组成的简单四边形,这将是这样的:

const geometry = new THREE.Geometry();
const vertices = [
   // the four vertices ABCD
   [0,0,0], [0,1,0], [1,0,0], [1,1,0]
].map(v => new THREE.Vector3(...v));

// setup two triangles ADB and ACD:
//
// B---D
// | / |
// A---C

const normal = new THREE.Vector3(0,0,1);
const faces = [
  new THREE.Face3(0, 3, 1, normal),
  new THREE.Face3(0, 2, 3, normal)
];

geometry.vertices = vertices;
geometry.faces = faces;

所以,这只是几何本身,但重要的是要知道它是如何为下一步构建的 - UV 坐标:

const uvs = geometry.faceVertexUvs[0];
const vertexUvs = [
  [0,0], [0,1], [1,0], [1,1]
].map(p => new THREE.Vector2(...p));

// we need to set the UV-coordinates for both faces separately, using 
// the same scheme as before:
uvs[0] = [vertexUvs[0], vertexUvs[3], vertexUvs[1]];
uvs[1] = [vertexUvs[0], vertexUvs[2], vertexUvs[3]];

基本上就是这样。这大致就是THREE.PlaneGeometry 所做的。关于这一点的更多观点:

  • three.js 能够处理两组不同的 uv 坐标,这就是为什么会有 geometry.faceVertexUvs[0]。
  • 该数组中的索引对应于 faces-array 中的索引,因此uvs[0] 是第一个面,uvs[1] 是第二个。
  • 其中每一个又是一个长度为 3 的数组,对应于该面的顶点。

现在,对于您的包,应该可以找出哪两个面是正面和背面。您需要找到它们并编辑它们的 UV 坐标以匹配您想要查看的图像中的位置(我不知道这些模型的外观如何,所以在这里我无法为您提供更多帮助)。所有其他 UV 坐标都可以设置为某个安全值,例如 (0, 0),然后所有不是正面或背面的东西都将具有纹理左下角像素的颜色。

如果您也想对它们进行纹理处理,您真的应该考虑在适当的 3D 编辑软件中制作纹理贴图。

编辑 (2017-01-15)

查看带有 angular-logo 的图像,左上角的三角形被翻转。这是因为three.js 使用的面顺序或顶点顺序与我在示例中使用的顺序不同。

如果您只是在浏览器控制台或调试器中使用它(这就是我刚刚所做的),那么理解这些东西会很有帮助。据此,来自three.js的平面几何具有以下结构:

// A---B
// | / |
// C---D

vertices = [A, B, C, D]
faces = [Face3(A, C, B), Face3(C, B, D)]

值得注意的是,无论出于何种原因,第一个面是逆时针的,第二个面是顺时针的(我认为这是一种不好的做法,三角形应该始终是逆时针的)。查看几何体的 faceVertexUVs,看看它在这种情况下应该是什么样子(只需在浏览器控制台中输入 new THREE.PlaneGeometry(1,1).faceVertexUvs[0])

现在来说另一点,缩放纹理。您可以尝试的一件事是对顶点使用大于 1 的 UV 值。这将导致纹理绘制得更小。但是,只有在

  • 纹理环绕模式是钳制到边缘 (texture.wrapS = texture.wrapT = THREE.ClampToEdgeWrapping;)
  • 所有纹理都有一个 1 像素宽的中性色边框

否则(这是我推荐的方式)您应该使用画布作为纹理并在实际使用之前准备图像。我这里做了一个简单的例子:http://codepen.io/usefulthink/pen/GrjmrM?editors=0010

【讨论】:

  • 也许这个模型的纹理坐标被定位为允许蒙皮 - 使用包裹整个对象的一个​​纹理 - 而不是 4 或 5 个分离的纹理。看看 Lithunwrap 或其他一些(和更新的)建模软件。
  • 好吧,我们必须根据 OP 给出的图像进行猜测,这表明使用了一些非常简单的立方体映射,使用了单个纹理。不过可能对小的重复纹理有好处。我也同意应该为此使用单个纹理,但需要正确展开才能用于未失真的图像。
  • 感谢马丁的回答,很抱歉我花了这么长时间才回复你。我相信您所描述的本质上是我所追求的,但结果不正确,因为添加图像的边由两个三角形组成,因此图像将分别展开而不是集体展开。如果您想跟进stackoverflow.com/questions/41673535/…,我已经开始了一个新问题
  • 好的 - 我刚刚在那里看到你的编辑,我会试一试并报告。
  • @MartinSchuhfuß 嗨马丁 - 有没有办法在画布/纹理位于对象上时动态移动图像?或者每次我们尝试移动它的位置时,它真的需要添加一个新纹理吗?
猜你喜欢
  • 2020-08-26
  • 1970-01-01
  • 2017-04-07
  • 1970-01-01
  • 2012-08-09
  • 1970-01-01
  • 1970-01-01
  • 2020-06-14
  • 1970-01-01
相关资源
最近更新 更多