Three.js: THREE.TextureLoader() loads the same texture multiple times (i.e. 20 times or more)

Created on 5 Oct 2016  ·  3Comments  ·  Source: mrdoob/three.js

Description of the problem

The THREE.TextureLoader() behaves in an unexpected and erroneous way. The load() function of the class tries/loads the same assets multiple times (i.e. 20 times or more).

Below in figure is illustrated this behaviour using the browser console:
bdhg9

The code used to load and use textures follows next:

var Element = function (texture) {
    this.texture = texture;
};
Element.prototype.createShaderMaterial = function (uniforms, vertexShader, fragmentShader) {
    var loader = new THREE.TextureLoader();
    uniforms.texture.value = loader.load(this.texture);

    return new THREE.ShaderMaterial({
        uniforms: uniforms,
        vertexShader: vertexShader,
        fragmentShader: fragmentShader,
        wireframe: true
    });
};

For debugging purposes you can also find a live preview here: https://alexprut.github.io/earth-defender/ and the game code here: https://github.com/alexprut/earth-defender/tree/master/client/js

Three.js version
  • [ ] Dev
  • [ ] r81
  • [x] r80

    Browser
  • [x] All of them

  • [ ] Chrome
  • [ ] Firefox
  • [ ] Internet Explorer

    OS
  • [x] All of them

  • [ ] Windows
  • [ ] Linux
  • [ ] Android
  • [ ] IOS
    Hardware Requirements (graphics card, VR Device, ...)
Help (please use the forum)

Most helpful comment

XHRLoader which is called from TextureLoader via ImageLoader uses global object Cache
but default Cache.enabled is false.

If you set THREE.Cache.enabled = true; right after loading three.js
it'd work.

https://github.com/mrdoob/three.js/blob/2f469f327a10c7780c9bc69f876f9ed5049587f2/src/loaders/XHRLoader.js#L22

https://github.com/mrdoob/three.js/blob/f65e669af99feb518e31756d793a9688a2578fbd/src/loaders/Cache.js#L9

6834

All 3 comments

I believe the loader is working in the intended way. Its loading the meteor textures 200 times because you are asking it to. The loader could be modified to automatically cache and return assets that you ask it to load multiple times, but this may not be wanted in all cases. The browser will be managing the cache so if all the headers on the image file are correct, the browser will return cached versions of the image each time.

In your code:

this.maxMeteorietes = config.maxMeteorietes || 200;

Game.prototype.createMeteorites = function (numMeteorites) {
    var meteorites = new THREE.Object3D();
    for (var i = 0; i < numMeteorites; i++) {
        var meteorite = new Meteorite().create(
            this.createUniforms(),
            this.createVertexShader(),
            this.createFragmentShader()
        );

    ....

}

Meteorite.prototype.create = function (uniforms, vertexShader, fragmentShader) {
    return new THREE.Mesh(
        new THREE.SphereGeometry(5, 5, 5),
//This line is called 200 times, and as such your loader.load() function will be called 200 times.
        this.createShaderMaterial(uniforms, vertexShader, fragmentShader)
    );
};

I would consider adding something like the following:

var cache = [];
var loader = new THREE.TextureLoader();  //don't need a local version of this object

Element.prototype.createShaderMaterial = function (uniforms, vertexShader, fragmentShader) {
    if(cache[this.texture]){
        return cache[this.texture]; //cache[this.texture].clone();
    }
    uniforms.texture.value = loader.load(this.texture);

    var shader = new THREE.ShaderMaterial({
        uniforms: uniforms,
        vertexShader: vertexShader,
        fragmentShader: fragmentShader,
        wireframe: true
    });

    cache[this.texture] = shader;

    return shader;
};

So you manage a cache of materials that get loaded from an array rather than regenerated everytime. This will work well if all the asteroids have the same material, or use the .clone() method if you want the materials to be different.

See if that helps out.

@calrk thanks it solves the problem.

I still believe THREE.TextureLoader() behaves in an unexpected way, it should ask you if you want to cache or not the textures, e.g.:

var loader = THREE.TextureLoader();
loader(texture, cache = false);

I think by default the loader should cache the textures for performance reasons (if I'm wrong please let me know why).


Below is a simple solution (Singleton pattern / Module pattern) cache module:

var TextureLoader = (function () {
    var _instance = null;

    var Loader = function () {
        var _loader = new THREE.TextureLoader();
        var _cache = [];

        function _cachePush(elem, val) {
            _cache.push({
                element: elem,
                value: val
            });
        }

        function _cacheSearch(elem) {
            for (var i = 0; i < _cache.length; i++) {
                if (_cache[i].element === elem) {
                    return _cache[i].value;
                }
            }

            return false;
        }

        function load(texture) {
            var match = _cacheSearch(texture);

            if (match) {
                return match;
            }

            var val = _loader.load(texture);
            _cachePush(texture, val);

            return val;
        }

        return {
            load: load
        }
    };

    function getInstance() {
        return (_instance) ? _instance : _instance = Loader();
    }

    return {
        getInstance: getInstance
    }
})();

To use and cache the texture you need to call:

TextureLoader.getInstance().load(texture);

XHRLoader which is called from TextureLoader via ImageLoader uses global object Cache
but default Cache.enabled is false.

If you set THREE.Cache.enabled = true; right after loading three.js
it'd work.

https://github.com/mrdoob/three.js/blob/2f469f327a10c7780c9bc69f876f9ed5049587f2/src/loaders/XHRLoader.js#L22

https://github.com/mrdoob/three.js/blob/f65e669af99feb518e31756d793a9688a2578fbd/src/loaders/Cache.js#L9

6834

Was this page helpful?
0 / 5 - 0 ratings

Related issues

stonexjr picture stonexjr  ·  3Comments

Horray picture Horray  ·  3Comments

jlaquinte picture jlaquinte  ·  3Comments

makc picture makc  ·  3Comments

ghost picture ghost  ·  3Comments