The following code loads a texture and applies it to a simple mesh. It works perfectly for images that do not contain an alpha channel.
toJSON() {
let output = super.toJSON();
output["geometry"] = this.geometry;
output['imageURL'] = this.mesh.toJSON().images[0]["url"];
return output;
}
fromJSON(data) {
super.fromJSON(data);
this.geometry = data["geometry"];
this.image_path = data["imageURL"];
this.refreshImage();
}
refreshImage() {
const this_obj = this;
const image_texture = new THREE.TextureLoader().load(
//image to load
this.image_path,
//onLoad callback to create the material only once the texture is loaded and its dimensions are available,
//this will ensure aspect ratio is based on the actual texture loaded.
(texture) => {
this_obj.changeGeometry(texture.image.width / texture.image.height)
},
//not required
undefined,
//onError callback
(err) => {
alert("An error occurred while attempting to load image");
}
);
this.mesh.material.map.dispose();
this.mesh.material.dispose();
this.mesh.material = new THREE.MeshPhongMaterial({map: image_texture, side: THREE.DoubleSide,
transparent: true})
this.mesh.material.color.set(this.color);
this.mesh.material.needsUpdate = true;
}
Unfortunately, it does not work properly for images with alpha channel, because transparent areas in the source image are rendered in black opaque color.
Does anyone know why this happens and how best to achieve the desired result?
I got an answer to my question when I realized that the issue is coming from the Mesh.toJSON call.
The method is a recursive one that is a real rabbit-hole. But at the bottom of the rabbit-hole you find that texture images are converted to base64 by drawing the image onto an temporary internal canvas. This happens in the ImageUtils.js module inside the getDataURL() function
The issue is that texture images larger than 2048 in width or height are converted into compressed “jpeg” format rather than “png” format that retains the alpha component.
This explains everything.
You can load any image, apply it to a material using TextureLoader, but as soon as you call toJSON to serialize your mesh, the alpha component is lost if the underlying image is larger than 2048 wide or long.
The solution in my case is to write my own function that draws to a canvas and converts the image to base64, but supports larger image sizes. Ofcourse one would have to warn the user that it may take some time to perform the conversion.