70 votes

Utilisation des textures dans THREE.js

Je commence avec THREE.js, et j'essaie de dessiner un rectangle avec une texture dessus, éclairé par une seule source de lumière. Je pense que c'est aussi simple que possible (HTML omis pour des raisons de brièveté) :

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);
}

Le problème est que je ne vois rien. Si je change le matériau et que j'utilise celui qui a été commenté, un carré apparaît comme je m'y attendais. Notez que

  • La texture est de 256x256, donc ses côtés sont des puissances de deux.
  • La fonction est en fait appelée lorsque le corps est chargé ; elle fonctionne en effet avec un matériau différent.
  • Cela ne fonctionne pas, même si je sers le fichier à partir d'un serveur Web. Il ne s'agit donc pas d'un problème de politique inter-domaines qui ne permet pas de charger l'image.

Qu'est-ce que je fais de mal ?

73voto

Andrea Points 7050

Au moment où l'image est chargée, le moteur de rendu a déjà dessiné la scène, il est donc trop tard. La solution consiste à modifier

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

en

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

9 votes

De nos jours, la réponse est de se débarrasser THREE.ImageUtils.loadTexture (qui est déprécié) et utiliser THREE.TextureLoader.load à la place. ImageUtils n'a pas du tout fonctionné pour moi, mais TextureLoader a fonctionné parfaitement la première fois que je l'ai essayé. Docs : threejs.org/docs/index.html#api/loaders/TextureLoader

27voto

Mustafah ELBanna Points 2310

Andrea solution est tout à fait juste, je vais juste écrire une autre implémentation basée sur la même idée. Si vous avez jeté un coup d'oeil à la méthode THREE.ImageUtils.loadTexture() source vous verrez qu'il utilise l'objet Image de javascript. L'événement $(window).load est déclenché une fois que toutes les images sont chargées ! Ainsi, à cet événement, nous pouvons rendre notre scène avec les textures déjà chargées...

  • 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);
        });
    });

Merci...

23voto

Sky Yip Points 777

Dans la version r75 de three.js, vous devez utiliser :

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);
});

0 votes

Excellente réponse ! Je suppose que l'API a changé depuis la réponse originale. Votre réponse est fonctionnelle ! Merci beaucoup !

0 votes

Vous auriez probablement dû indiquer à quelle révision de three.js vous vous êtes référé en tant que dernier site .

6voto

Marcs Points 1

Dans la version r82 de Three.js TextureLoader est l'objet à utiliser pour charger une texture.

Chargement d'une texture ( code source , Démonstration )

Extrait ( 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);

Chargement de textures multiples ( code source , Démonstration )

Dans cet exemple, les textures sont chargées à l'intérieur du constructeur du maillage, plusieurs textures sont chargées en utilisant Promesses .

Extrait ( Globe.js ) :

Créez un nouveau conteneur en utilisant Object3D pour avoir deux maillages dans le même conteneur :

var Globe = function (radius, segments) {

  THREE.Object3D.call(this);

  this.name = "Globe";

  var that = this;

  // instantiate a loader
  var loader = new THREE.TextureLoader();

Une carte appelée textures où chaque objet contient le url d'un fichier de texture et val pour le stockage de la valeur d'un Three.js texture objet.

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

Le tableau de promesses, pour chaque objet dans la carte appelée textures pousser une nouvelle Promesse dans le tableau texturePromises chaque promesse appelle loader.load . Si la valeur de entry.val est valide THREE.Texture puis résoudre la promesse.

  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 prend le tableau de promesses texturePromises comme argument. En procédant ainsi, le navigateur attend que toutes les promesses soient résolues, et lorsqu'elles le sont, nous pouvons charger la géométrie et le matériau.

  // 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);
  });

Pour la sphère de nuages, une seule texture est nécessaire :

  // 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;

1voto

Hitesh Sahu Points 12561

Manipulation sans erreur

//Load background texture
 new THREE.TextureLoader();
loader.load('https://images.pexels.com/photos/1205301/pexels-photo-1205301.jpeg' , function(texture)
            {
             scene.background = texture;  
            });

Avec la gestion des erreurs

// 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 (error) {
  console.log('An error happened'+error);
};

//Function  called when load completes.
var onLoad = function (texture) {
  var objGeometry = new THREE.BoxGeometry(30, 30, 30);
  var objMaterial = new THREE.MeshPhongMaterial({
    map: texture,
    shading: THREE.FlatShading
  });

  var boxMesh = new THREE.Mesh(objGeometry, objMaterial);
  scene.add(boxMesh);

  var render = function () {
    requestAnimationFrame(render);
    boxMesh.rotation.x += 0.010;
    boxMesh.rotation.y += 0.010;
      sphereMesh.rotation.y += 0.1;
    renderer.render(scene, camera);
  };

  render();
}

//LOAD TEXTURE and on completion apply it on box
var loader = new THREE.TextureLoader();
    loader.load('https://upload.wikimedia.org/wikipedia/commons/thumb/9/97/The_Earth_seen_from_Apollo_17.jpg/1920px-The_Earth_seen_from_Apollo_17.jpg', 
                onLoad, 
                onProgress, 
                onError);

Résultat :

enter image description here

https://codepen.io/hiteshsahu/pen/jpGLpq/

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X