상세 컨텐츠

본문 제목

[WebGL] Three.js - Texture 로드, HREE.TextureLoader(), THREE.ImageUtils.loadTexture 관련

WEB/WebGL

by AlrepondTech 2017. 11. 6. 11:45

본문

반응형

 

 

 

=================================

=================================

=================================

 

 

 

 

 

 

출처: https://stackoverflow.com/questions/35540880/three-textureloader-is-not-loading-images-files

 

 

So I've been messing around with THREE and Node for a while, and up until now, I have been loading textures using the TextureLoader class as follows:

var loader = new THREE.TextureLoader; loader.crossOrigin = '';  function createSphere ( radius , segments , rings ) {    var geometry = new THREE.SphereGeometry(radius , segments , rings);    var material = new THREE.MeshPhongMaterial({          map: loader.load('./images/...'),         bumpMap: loader.load('./images/...'),         ect...    });    return new THREE.Mesh ( geometry, material ) }

It was working fine, and then I decided that instead of loading the textures when I created the material that I would like to pre-load all my textures, so I made a a small utility to do just that:

var loader = new THREE.TextureLoader; loader.crossOrigin = '';  var textureMap = {  earthTex : {file : 'earth_no_ice_clouds.jpg', message : 'Loading Global Land Masses'}, earthBumpTex : {file : 'earth_bump.jpg', message : 'Loading Global Depth'}, earthSpecTex : {file : 'earth_specular.png', message : 'Loading Water'}, earthCloudTex : {file : 'earth_clouds.png', message : 'Loading Clouds'}, earthCultureTex : {file : 'earth_cultural.png', message :'Loading Country Borders'}, earthLookUpTex : {file : 'earth_lookup.jpg', message :'Loading Country Projections'}, moonTex : {file : 'moon.jpg', message :'Loading Lunar Surface'}, moonBumpTex : {file : 'moon_bump.jpg', message : 'Loading Lunar Depth'}, skyDomeTex : {file : 'galaxy_3.png', message : 'Loading Galaxy'}  };  Object.size = function(obj) {     var size = 0, key;     for (key in obj) {         if (obj.hasOwnProperty(key)) size++;     }     return size; }  function loadTextures(  ) {      var path = "./images/";     var size = Object.size(textureMap);     var count = 0;      for(var key in textureMap) {         var textureItem = textureMap[key];         var texture = loader.load(path + textureItem.file);         console.log(texture);     } }  loadTextures();

Whenever I run this in my browser (Chrome) with the developer tools open, the THREE.texture object that ends up being logged has its image property set to 'undefined' and its src property set to " ". I do not understand why this is happening, and I have not changed any of the paths for my .png or .jpg files nor the location of the file in which I am using my the TextureLoader.

When I try to run the first snippet of code up top, the error also occurs and now I cannot seem to load any textures into my application.

Any help in understanding why this has happened all of a sudden would be greatly appreciated.

EDIT

I edited the first code block in an attempt to make my issue more clear. I wasn't using promises or callbacks or anything of the sort before. I was just calling my 'createSphere' function and similar functions from my scene initialization function. It used to work fine, and I had no problems loading textures.

Now, using the same program structure from before, the first code block doesn't work.

 

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

 

You're not calling it correctly. TextureLoader.load() returns immediately (with an empty, still to be populated THREE.Texture object ... it populates only when the request completes), and it doesn't wait for the image to be loaded.

Instead, as you can read in its documentation here you can feed it with callback methods which will be called when loading is complete, finished, or in progress.

Taking the example from the documentation:

// instantiate a loader var loader = new THREE.TextureLoader();  // load a resource loader.load(     // resource URL     'textures/land_ocean_ice_cloud_2048.jpg',     // Function when resource is loaded     function ( texture ) {         // do something with the texture         var material = new THREE.MeshBasicMaterial( {             map: texture          } );     },     // Function called when download progresses     function ( xhr ) {         console.log( (xhr.loaded / xhr.total * 100) + '% loaded' );     },     // Function called when download errors     function ( xhr ) {         console.log( 'An error happened' );     } );

If you want to wait for all the textures to be loaded, that's going to get more complicated. One way is to use Promises, here is an answer to a very similar question I've given before: Is there a way to wait for THREE.TextureLoader.load() to finish?

 

 

 

=================================

=================================

=================================

 

 

 

출처: https://stackoverflow.com/questions/21728279/three-js-texture-is-not-loading

 

 

by trying a tutorial ( http://learningthreejs.com/blog/2013/09/16/how-to-make-the-earth-in-webgl/ ) the needed texture is not loading. Here is my trying code in the index.html script area

<script src="js/jquery.js"></script> <script src="js/threejs/build/three.js"></script> <script>     var camera = new THREE.PerspectiveCamera(45, window.innerWidth /     window.innerHeight, 0.01, 1000 );     camera.position.z = 1.5;      var texture = THREE.ImageUtils.loadTexture('images/earthmap1k.jpg');     texture.anisotropy = 16;     var material = new THREE.MeshPhongMaterial( { map: texture } );      /*material.bumpMap = THREE.ImageUtils.loadTexture('images/earthbump1k.jpg');     material.bumpScale = 0.05;*/     var scene = new THREE.Scene();     var light = new THREE.AmbientLight(0x888888);     scene.add(light);     var light = new THREE.DirectionalLight( 0xCCCCCC, 1 );     light.position.set(5,3,5);     scene.add(light);       var earthMesh = new THREE.Mesh(new THREE.SphereGeometry(0.5, 32, 32), material);     scene.add(earthMesh);      var renderer = new THREE.WebGLRenderer( { antialias: true } );     renderer.setSize(window.innerWidth, window.innerHeight);     var $container = $('#container');     $container.append(renderer.domElement);      renderer.render(scene, camera);  </script> 

so what I'm doing wrong here???

thanks for your help in advance

best regards Karsten

 

------------------------------------------------------------------------------------------------------------------

 

 

try this..
var tUrl = "images/earthbump1k.jpg"; var texture = THREE.ImageUtils.loadTexture(tUrl, {}, createMesh); function createMesh(texture){ var material = new THREE.MeshPhongMaterial( { map: texture } ); var earthMesh = new THREE.Mesh(new THREE.SphereGeometry(0.5, 32, 32), material); scene.add(earthMesh); }
shareimprove this answer

 

 

That doesn't work too. The same feature. If I'm alerting something one time then the earth is shown but without alerting all is black. – kasche Feb 12 '14 at 14:19
    
here the creeteMesh() is onload function...once your texture loaded the onload fuunction is called by callback... can you share your full code.. – user2089677 Feb 12 '14 at 14:24
    
so now i fixed it by using a function named animate and doing requestAnimationFrame, then it works without the alerts :) – kasche Feb 12 '14 at 14:28
    
Code is too long for comments and i can write an answer to my question first in 8h. – kasche Feb 12 '14 at 14:32
    
oh missed requestAnimationFrame... and its good you identified it.. – user2089677 Feb 

 

 

 

 

 

=================================

=================================

=================================

 

 

 

 

 

출처: https://stackoverflow.com/questions/7919516/using-textures-in-three-js

 

 

I am starting with THREE.js, and I am trying to draw a rectangle with a texture on it, lit by a single source of light. I think this is as simple as it gets (HTML omitted for brevity):

function loadScene() {     var world = document.getElementById('world'),         WIDTH = 1200,         HEIGHT = 500,         VIEW_ANGLE = 45,         ASPECT = WIDTH / HEIGHT,         NEAR = 0.1,         FAR = 10000,          renderer = new THREE.WebGLRenderer(),         camera = new THREE.Camera(VIEW_ANGLE, ASPECT, NEAR, FAR),         scene = new THREE.Scene(),         texture = THREE.ImageUtils.loadTexture('crate.gif'),         material = new THREE.MeshBasicMaterial({map: texture}),         // material = new THREE.MeshPhongMaterial({color: 0xCC0000});         geometry = new THREE.PlaneGeometry(100, 100),         mesh = new THREE.Mesh(geometry, material),         pointLight = new THREE.PointLight(0xFFFFFF);      camera.position.z = 200;         renderer.setSize(WIDTH, HEIGHT);     scene.addChild(mesh);     world.appendChild(renderer.domElement);     pointLight.position.x = 50;     pointLight.position.y = 50;     pointLight.position.z = 130;     scene.addLight(pointLight);      renderer.render(scene, camera); }

The problem is, I cannot see anything. If I change the material and use the commented one, a square appears as I would expect. Note that

  • The texture is 256x256, so its sides are power of two
  • The function is actually called when the body is loaded; indeed it works with a different material.
  • It does not work even if I serve the file from a webserver, so it is not an issue of cross-domain policy not allowing to load the image.

What I am I doing wrong?

 

--------------------------------------------------------------------

 

 

By the time the image is loaded, the renderer has already drawn the scene, hence it is too late. The solution is to change

texture = THREE.ImageUtils.loadTexture('crate.gif'),

into

texture = THREE.ImageUtils.loadTexture('crate.gif', {}, function() {     renderer.render(scene); }),

 

------------------------------------------------------------------------------------------------------------------------------------------------------------------------

 

 

Andrea solution is absolutely right, I will just write another implementation based on the same idea. If you took a look at the THREE.ImageUtils.loadTexture() source you will find it uses the javascript Image object. The $(window).load event is fired after all Images are loaded ! so at that event we can render our scene with the textures already loaded...
  • CoffeeScript
  • $(document).ready -> material = new THREE.MeshLambertMaterial(map: THREE.ImageUtils.loadTexture("crate.gif")) sphere = new THREE.Mesh(new THREE.SphereGeometry(radius, segments, rings), material) $(window).load -> renderer.render scene, camera
  • JavaScript
  • $(document).ready(function() { material = new THREE.MeshLambertMaterial({ map: THREE.ImageUtils.loadTexture("crate.gif") }); sphere = new THREE.Mesh(new THREE.SphereGeometry(radius, segments, rings), material); $(window).load(function() { renderer.render(scene, camera); }); });
Thanks...
 

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

 

 

In version r75 of three.js, you should use:

var loader = new THREE.TextureLoader(); loader.load('texture.png', function ( texture ) {   var geometry = new THREE.SphereGeometry(1000, 20, 20);   var material = new THREE.MeshBasicMaterial({map: texture, overdraw: 0.5});   var mesh = new THREE.Mesh(geometry, material);   scene.add(mesh); });
shareimprove this answer

 

 

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

 

 

In version r82 of Three.js TextureLoader is the object to use for loading a texture.

Loading one texture (source codedemo)

Extract (test.js):

var scene = new THREE.Scene(); var ratio = window.innerWidth / window.innerHeight; var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight,   0.1, 50);  var renderer = ...  [...]  /**  * Will be called when load completes.  * The argument will be the loaded texture.  */ var onLoad = function (texture) {   var objGeometry = new THREE.BoxGeometry(20, 20, 20);   var objMaterial = new THREE.MeshPhongMaterial({     map: texture,     shading: THREE.FlatShading   });    var mesh = new THREE.Mesh(objGeometry, objMaterial);    scene.add(mesh);    var render = function () {     requestAnimationFrame(render);      mesh.rotation.x += 0.010;     mesh.rotation.y += 0.010;      renderer.render(scene, camera);   };    render(); }  // Function called when download progresses var onProgress = function (xhr) {   console.log((xhr.loaded / xhr.total * 100) + '% loaded'); };  // Function called when download errors var onError = function (xhr) {   console.log('An error happened'); };  var loader = new THREE.TextureLoader(); loader.load('texture.jpg', onLoad, onProgress, onError);

Loading multiple textures (source codedemo)

In this example the textures are loaded inside the constructor of the mesh, multiple texture are loaded using Promises.

Extract (Globe.js):

Create a new container using Object3D for having two meshes in the same container:

var Globe = function (radius, segments) {    THREE.Object3D.call(this);    this.name = "Globe";    var that = this;    // instantiate a loader   var loader = new THREE.TextureLoader();

A map called textures where every object contains the url of a texture file and val for storing the value of a Three.js texture object.

  // earth textures   var textures = {     'map': {       url: 'relief.jpg',       val: undefined     },     'bumpMap': {       url: 'elev_bump_4k.jpg',       val: undefined     },     'specularMap': {       url: 'wateretopo.png',       val: undefined     }   };

The array of promises, for each object in the map called textures push a new Promise in the array texturePromises, every Promise will call loader.load. If the value of entry.val is a valid THREE.Texture object, then resolve the promise.

  var texturePromises = [], path = './';    for (var key in textures) {     texturePromises.push(new Promise((resolve, reject) => {       var entry = textures[key]       var url = path + entry.url        loader.load(url,         texture => {           entry.val = texture;           if (entry.val instanceof THREE.Texture) resolve(entry);         },         xhr => {           console.log(url + ' ' + (xhr.loaded / xhr.total * 100) +             '% loaded');         },         xhr => {           reject(new Error(xhr +             'An error occurred loading while loading: ' +             entry.url));         }       );     }));   }

Promise.all takes the promise array texturePromises as argument. Doing so makes the browser wait for all the promises to resolve, when they do we can load the geometry and the material.

  // load the geometry and the textures   Promise.all(texturePromises).then(loadedTextures => {      var geometry = new THREE.SphereGeometry(radius, segments, segments);     var material = new THREE.MeshPhongMaterial({       map: textures.map.val,       bumpMap: textures.bumpMap.val,       bumpScale: 0.005,       specularMap: textures.specularMap.val,       specular: new THREE.Color('grey')     });      var earth = that.earth = new THREE.Mesh(geometry, material);     that.add(earth);   });

For the cloud sphere only one texture is necessary:

  // clouds   loader.load('n_amer_clouds.png', map => {     var geometry = new THREE.SphereGeometry(radius + .05, segments, segments);     var material = new THREE.MeshPhongMaterial({       map: map,       transparent: true     });      var clouds = that.clouds = new THREE.Mesh(geometry, material);     that.add(clouds);   }); }  Globe.prototype = Object.create(THREE.Object3D.prototype); Globe.prototype.constructor = Globe;
shareimprove this answer

 

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

 

 

=================================

=================================

=================================

 

 

 

반응형


관련글 더보기

댓글 영역