Search code examples
javascriptthree.jstexturestexture-mapping

How do I repeat textures without stretching in Three.js?


No matter what I do, my textures seem to be getting stretched / scaled to the size of the mesh I am applying them to. I've read a lot of answers to this question, none of the solutions seem to have fixed things for me so I'm posting a new one. Just a bit of info,

  • My textures are all 64x64 pixels
  • I am preloading all my textures
  • I am using the Web GL renderer

Here is my code

makeTestObject:function()
{
    var scope = this,
        tileGeometry = new THREE.BoxGeometry(TILE_SIZE , TILE_HEIGHT , TILE_SIZE),
        texture = new THREE.Texture(preloadedImageObject),
        textureMaterial = new THREE.MeshLambertMaterial({map:texture}),
        tile = new THREE.Mesh(tileGeometry , new THREE.MeshFaceMaterial(
        [
            textureMaterial, // +x
            textureMaterial, // -x
            textureMaterial, // +y
            textureMaterial, // -y
            textureMaterial, // +z
            textureMaterial // -z
        ]));

    texture.wrapS = THREE.RepeatWrapping;
    texture.wrapT = THREE.RepeatWrapping;

    tile.position.y = BASE_HEIGHT * 2;
    tile.castShadow = true;
    tile.receiveShadow = true;
    texture.needsUpdate = true;
    scope.scene.add(tile);
}

If I do texture.repeat.set(x , x) and set x to any kind of value, the texture just seems to disappear and I'm left with a flat colour.

Any idea what I am doing wrong?


Solution

  • Okay, so for a standard box geometry (square or rectangular) the solution is this;

    makeTestObject:function()
    {
        var scope = this,
            tileGeometry = new THREE.BoxGeometry(TILE_SIZE , TILE_HEIGHT , TILE_SIZE),
            texture = new THREE.Texture(preloadedImageObject),
            textureMaterial = new THREE.MeshLambertMaterial({map:texture}),
            tile = new THREE.Mesh(tileGeometry , new THREE.MeshFaceMaterial(
            [
                textureMaterial, // +x
                textureMaterial, // -x
                textureMaterial, // +y
                textureMaterial, // -y
                textureMaterial, // +z
                textureMaterial // -z
            ]));
    
        texture.wrapS = THREE.RepeatWrapping;
        texture.wrapT = THREE.RepeatWrapping;
        tile.geometry.computeBoundingBox();
    
        var max = tile.geometry.boundingBox.max;
        var min = tile.geometry.boundingBox.min;
        var height = max.y - min.y;
        var width = max.x - min.x;
    
        texture.repeat.set(width / TEXTURE_SIZE , height / TEXTURE_SIZE);
    
        texture.needsUpdate = true;
        scope.scene.add(tile);
    }
    

    The key is setting the ratios correctly for the repetition of the texture. You also might want to create a new material for each face rather than reference the same material object over and over.