I have just learnt how to apply texture to my 3D-models in WebGL using .mtl files (and .obj files). Applying texture works great when the image is saved on my computer. Here is an example of what my .mtl file looks like:
newmtl Earth_MATERIAL
Ns 96.078431
Ka 1.000000 1.000000 1.000000
Kd 0.640000 0.640000 0.640000
Ks 0.000000 0.000000 0.000000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 1
map_Kd Earth.png
This works very well. However I want to publish my simulation and therefore I have to refer to the image somehow. My first thought was to upload the image to DropBox and use that link, but this did not work:
...
map_Kd https://dl.dropbox.com/s/t4cm3vzsbx21crc/Earth.png?dl=0
The error I get when I run this code is:
Error: WebGL warning: texImage2D: Element is write-only, thus cannot be uploaded.
To load the texture and model I use an MTLloader and OBJloader. Here are the loaders I am using:
MTLloader: link
OBJloader: link
I also use ThreeJS Library:
ThreeJS: link
EDIT: Problem solved thanks to Jave! Here is the result for those who want to see: https://code.sololearn.com/WWY9cXN6OVBX/
The MTLLoader has a method named setTexturePath
which you can use to set the base path from which to load your textures. Since you are using an external source for the files (dropbox) you might also have to call setCrossOrigin(true)
. I have attached a commented example of using these methods to load your texture.
Some things you might want to consider:
Earth.png
or textures/Earth.png
).const canvas = document.getElementById("canvas");
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(45, 1, 1, 10);
const renderer = new THREE.WebGLRenderer({ canvas });
const light = new THREE.AmbientLight();
scene.add(light);
camera.position.set(0, 0, 6);
const mesh = new THREE.Mesh(new THREE.SphereBufferGeometry( 1, 32, 32 ));
scene.add(mesh);
function update(){
scene.rotation.y = 0.001 * performance.now();
renderer.render(scene, camera);
requestAnimationFrame(update);
}
update();
//This is just creating a data-url from the div containing the mtl-data, it is not really neccessary, but it will make the example more similiar to loading a file.
const data = document.getElementById("mtlplaceholder").textContent;
const dataurl = "data:text/plain;base64," + btoa(data);
const loader = new THREE.MTLLoader();
//To load texture files not in the same folder as the mtl-file, we need to call setTexurePath with the proper base path. Note that every texture will have to be available at this same base url, which might not be the case when shared from dropbox.
//The complete URL is https://dl.dropbox.com/s/t4cm3vzsbx21crc/Earth.png
loader.setTexturePath("https://dl.dropbox.com/s/t4cm3vzsbx21crc/");
//To use cross-origin loading of textures, call setCrossOrigin(true):
loader.setCrossOrigin(true);
//Finally. load the mtl file (in this case the "file" is the dataurl created above):
loader.load(dataurl, res => {
//MTLLoader.load creates a MTLLoader.MaterialCreator, to get the actual material we have to call create with the name of the material we want.
//If you are using the OBJLoader, use preload instead and then objloader.setMaterials.
const loadedMat = res.create("Earth_MATERIAL");
//Finally, we set the material on the mesh.
mesh.material = loadedMat;
}, e => console.log("error", e));
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/92/three.js"></script>
<script src="https://rawgit.com/mrdoob/three.js/53838a2/examples/js/loaders/MTLLoader.js"></script>
<canvas id="canvas" width="400" height="400"></canvas>
<div id="mtlplaceholder" style:"white-space:pre">
newmtl Earth_MATERIAL
Ns 96.078431
Ka 1.000000 1.000000 1.000000
Kd 0.640000 0.640000 0.640000
Ks 0.000000 0.000000 0.000000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 1
map_Kd Earth.png
</div>