I load multiple textures in my three.js app, which are equirectangular images. I try to follow three.js documentation to get a reference to each texture I load so I can dispose() it when not needed to free up memory. However, renderer.info.memory seems to continue showing disposed textures count as if it does not dispose my texture from memory.
Here is a simplified and shortened code from my app.
var allTextures=[
{path: a, textureRef: null, texture: null},
{path: b, textureRef: null, texture: null}
];
function loadTexture(textureObj){
var loader = new THREE.TextureLoader();
textureObj.textureRef = loader.load(textureObj.path, function(texture){
textureObj.texture = texture;
}, function ( xhr ) {}, function ( xhr ) {});
}
// load first texture
var t = allTextures[0]
loadTexture(t);
// dispose first texture
setTimeout(function () {
var _t = allTextures[0];
_t.textureRef.dispose();
_t.textureRef = null;
console.log(renderer.info.memory); // ex: {geometries: 15, textures: 20}
},5000);
As a result, my app works poorly on the mobile device refreshing the page when around 20 textures are being shown loaded in memory.
Am I correctly disposing the textures? What else can be verified to understand why textures are not offloaded?
Related
I'm using THREE.OBJLoader to load my 3D objects in Three.js r100 and, at a certain moment, I need to load some dynamic textures with THREE.TextureLoader().
Then I simply create a new material with THREE.MeshBasicMaterial() and set this to my obj.
This is the code:
//this contains the texture loaded
let texture = await new Promise((resolve, rejects) => loadGeneralTexture(resolve, rejects, url));
texture.minFilter = THREE.LinearFilter;
texture.needsUpdate = true;
texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
var material = new THREE.MeshBasicMaterial({
map: texture
});
//this loop set the new material with Texture
el.traverse(child => {
if (child instanceof THREE.Mesh) {
child.material = material;
}
});
The result is:
But the image loaded is:
I fix this error only with gizmo (3dMax tool) by "rotate" the texture but I can't do the same fix with Threejs.
The same problem with two other 3d objects but this time it's even worse
Edit: obj files are our client'file (so i didn't create It myself), i already checked the various "faces" and they are equal. Can i change uvmapping myself with threejs?
I use THREE.js Loading Manager to check the object or texture is loaded .
var Mesh;
var TLoader = new THREE.TextureLoader(manager);
var manager = new THREE.LoadingManager();
manager.onProgress = function ( item, loaded, total ) {
console.log( item, loaded, total );
};
manager.onLoad = function()
{
console.log(Renderer.domElement.toDataURL());
}
function renderModel(path,texture) {
var Material = new THREE.MeshPhongMaterial({shading: THREE.SmoothShading});
Material.side = THREE.DoubleSide;
var Loader = new THREE.JSONLoader(manager);
Loader.load(path,function(geometry){
geometry.mergeVertices();
geometry.computeFaceNormals();
geometry.computeVertexNormals();
TLoader.load(texture,function(texture){
Mesh = new THREE.Mesh(geometry, Material);
Mesh.material.map =texture;
Scene.add(Mesh);
});
});
}
and i just call the renderModel function in a loop .
But the console.log(Renderer.domElement.toDataURL()) output from the manager.onload function is giving only image of the some 3d models not all the ones in the scene
I just want to get 'Renderer.domElement.toDataURL()' when all the 3d models are rendered in the scene
now only the image of 2 or 3 models are getting ,but in scene all the items are loaded.
The renderer renders an image each frame. When the loading of all the objects is completed, the onLoad method of the manager is called immediately. So, the last objects were added to the scene and you retrieve the image data without giving the renderer a chance to render a new image. You need to wait for a new frame. Maybe a timeout with e.g. 200 milliseconds will help.
EDIT
You could also call the render method in your onLoad, so the renderer draws a new image before you call console.log.
manager.onLoad = function()
{
Renderer.render( Scene, camera );
console.log(Renderer.domElement.toDataURL());
}
i wanted to ask if there is a simple solution to preload Textures and Images in ThreeJs.
I load them all into an array: myTextureArray.push(new THREE.ImageUtils.loadTexture('../textures/floor_red.jpg');
How can i check if and when all Textures are loaded successfully ?
Here is an older Version Link: http://museum.baraq.de/
Thanks for help!
There is a Loading Manager in Three.js you can use to keep track of your loaded Textures or Objects.
Example implementation:
var textureManager = new THREE.LoadingManager();
textureManager.onProgress = function ( item, loaded, total ) {
// this gets called after any item has been loaded
};
textureManager.onLoad = function () {
// all textures are loaded
// ...
};
var textureLoader = new THREE.ImageLoader( textureManager );
var myTextureArray = [];
var myTexture = new THREE.Texture();
myTextureArray.push( myTexture );
textureLoader.load( 'my/texture.jpg', function ( image ) {
myTexture.image = image;
} );
Tested in Three.js r71.
I'm using the pixi.js render engine for my current project in javascript. I'm loading a spritesheet defined in json, using the assetloader. Problem is I need to create sprites or movieclips well after the onComplete event that the assetloader uses, finishes. However the texture cache seems to not be accessible after that point. Here is some of the code below demonstrating the problem I'm coming across.
var spriteSheet = [ "test.json" ];
loader = new PIXI.AssetLoader(spriteSheet); // only using the flappy bird sprite sheet as a test
loader.onComplete = OnAssetsLoaded;
loader.load();
function OnAssetsLoaded() {
var sprite = PIXI.Sprite.fromFrame("someFrame.png"); //this works
}
var sprite2 = PIXI.Sprite.fromFrame("someFrame2.png"); //This does not work, tells me "someFrame2" is not in the texture cache
The sprite sheet must be fully loaded before the images get stored in the cache. Once the Sprite sheet has loaded, those assets will exist in the cache until you delete them.
The reason your code above fails is because the line var sprite2... executes before the sprite sheet has finished loading.
This example will continuously add a new Sprite to the stage every second.
//build stage
var stage = new PIXI.Stage(0x66FF99);
var renderer = PIXI.autoDetectRenderer(400, 300);
document.body.appendChild(renderer.view);
//update renderer
requestAnimFrame( animate );
function animate() {
requestAnimFrame( animate );
renderer.render(stage);
}
//Flag will prevent Sprites from being created until the Sprite sheet is loaded.
var assetsReadyFlag = false;
//load sprite sheet
var loader = new PIXI.AssetLoader([ "test.json" ]);
loader.onComplete = function(){
assetsReadyFlag = true;
};
loader.load();
//Add a new bird every second
setInterval( addBird, 1000);
function addBird()
{
//assets must be loaded before creating sprites
if(!assetsReadyFlag) return;
//create Sprite
var bird = PIXI.Sprite.fromFrame("bird.png");
bird.anchor.x = bird.anchor.y = 0.5;
bird.x = Math.random() * stage.width;
bird.y = Math.random() * height;
stage.addChild(bird);
};
I'm using three.js to create a minecraft texture editor, similar to this. I'm just trying to get the basic click-and-paint functionality down, but I can't seem to figure it out. I currently have textures for each face of each cube and apply them by making shader materials with the following functions.
this.createBodyShaderTexture = function(part, update)
{
sides = ['left', 'right', 'top', 'bottom', 'front', 'back'];
images = [];
for (i = 0; i < sides.length; i++)
{
images[i] = 'img/'+part+'/'+sides[i]+'.png';
}
texCube = new THREE.ImageUtils.loadTextureCube(images);
texCube.magFilter = THREE.NearestFilter;
texCube.minFilter = THREE.LinearMipMapLinearFilter;
if (update)
{
texCube.needsUpdate = true;
console.log(texCube);
}
return texCube;
}
this.createBodyShaderMaterial = function(part, update)
{
shader = THREE.ShaderLib['cube'];
shader.uniforms['tCube'].value = this.createBodyShaderTexture(part, update);
shader.fragmentShader = document.getElementById("fshader").innerHTML;
shader.vertexShader = document.getElementById("vshader").innerHTML;
material = new THREE.ShaderMaterial({fragmentShader: shader.fragmentShader, vertexShader: shader.vertexShader, uniforms: shader.uniforms});
return material;
}
SkinApp.prototype.onClick =
function(event)
{
event.preventDefault();
this.change(); //makes texture file a simple red square for testing
this.avatar.remove(this.HEAD);
this.HEAD = new THREE.Mesh(new THREE.CubeGeometry(8, 8, 8), this.createBodyShaderMaterial('head', false));
this.HEAD.position.y = 10;
this.avatar.add(this.HEAD);
this.HEAD.material.needsUpdate = true;
this.HEAD.dynamic = true;
}
Then, when the user clicks any where on the mesh, the texture file itself is update using canvas. The update occurs, but the change isn't showing up in the browser unless the page is refreshed. I've found plenty of examples of how to change the texture image to a new file, but not on how to show changes in the same texture file during runtime, or even if it's possible. Is this possible, and if not what alternatives are there?
When you update a texture, whether its based on canvas, video or loaded externally, you need to set the following property on the texture to true:
If an object is created like this:
var canvas = document.createElement("canvas");
var canvasMap = new THREE.Texture(canvas)
var mat = new THREE.MeshPhongMaterial();
mat.map = canvasMap;
var mesh = new THREE.Mesh(geom,mat);
After the texture has been changed, set the following to true:
cube.material.map.needsUpdate = true;
And next time you render the scene it'll show the new texture.
Here is all the basics of what you have to know about "updating" stuff in three.js: https://threejs.org/docs/#manual/introduction/How-to-update-things