'Three.js: Make image texture fit object without distorting or repeating

I'm loading a .png file and displaying it as a texture on a rectangular surface. The aspect ratio of the .png and the aspect ratio of the surface are not the same. I need the texture to fit the object

  • without repeating
  • without distorting, i.e. maintaining its aspect ratio
  • positioned at the center
  • The height of the texture should be scaled up or down to the height of the object.

(For those familiar to CSS, I'm trying to achieve the equivalent of background-size: auto 100%; background-repeat: no-repeat; background-position: center;.)

So far I am doing

tex1.wrapS = THREE.ClampToEdgeWrapping
tex1.wrapT = THREE.ClampToEdgeWrapping
repeatX = (clothWidth * textureSetting.h / (clothHeight * textureSetting.w))
repeatY = 1
tex1.repeat.set repeatX, repeatY

clothHeight and clothWidth are the dimensions of the object, textureSetting.w and textureSetting.h are the dimensions of the texture.

The texture is distorted and offset to the right.



Solution 1:[1]

I got it to work just as @WestLangley suggested. Here's the solution in CoffeeScript:

tex1.wrapS = THREE.ClampToEdgeWrapping
tex1.wrapT = THREE.RepeatWrapping
repeatX = (clothWidth * textureSetting.h / (clothHeight * textureSetting.w))
repeatY = 1
tex1.repeat.set repeatX, repeatY
tex1.offset.x = (repeatX - 1) / 2 * -1

And for anyone who prefers vanilla JavaScript, here is the JS Version:

var repeatX, repeatY;
tex1.wrapS = THREE.ClampToEdgeWrapping;
tex1.wrapT = THREE.RepeatWrapping;
repeatX = clothWidth * textureSetting.h / (clothHeight * textureSetting.w);
repeatY = 1;
tex1.repeat.set(repeatX, repeatY);
tex1.offset.x = (repeatX - 1) / 2 * -1;

Solution 2:[2]

after years ! I change the code of @bootsmaat

It takes care if image is landscape or portrait.

const texture = new THREE.TextureLoader().load(images[index].url, texture => {
var repeatX, repeatY;
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
if (texture.source.data.height < texture.source.data.width) {
  repeatX = squareWidth * texture.source.data.height / (squareWidth * texture.source.data.width);
  repeatY = 1;
  texture.repeat.set(repeatX, repeatY);
  texture.offset.x = (repeatX - 1) / 2 * -1;
} else {
  repeatX = 1;
  repeatY = squareWidth * texture.source.data.width / (squareWidth * texture.source.data.height);
  texture.repeat.set(repeatX, repeatY);
  texture.offset.y = (repeatY - 1) / 2 * -1;
}});

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 bootsmaat
Solution 2