web-dev-qa-db-fra.com

Utiliser des textures dans THREE.js

Je commence par THREE.js et j'essaie de dessiner un rectangle avec une texture, éclairé par une seule source de lumière. Je pense que c'est aussi simple que cela devient (HTML omis pour la 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 de matériau et utilise celui commenté, un carré apparaît comme je le pensais. Notez que

  • La texture est 256x256, donc ses côtés sont la puissance de deux
  • La fonction est appelée lorsque le corps est chargé. En effet, cela fonctionne 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 entre domaines qui ne permet pas de charger l'image.

Qu'est-ce que je fais mal?

67
Andrea

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 est de changer

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

dans

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

La solution Andrea est tout à fait juste, je vais juste écrire une autre implémentation basée sur la même idée . Si vous jetez un coup d’oeil à THREE.ImageUtils.loadTexture () source vous verrez qu’il utilise l’objet javascript Image. L'événement $ (window) .load est déclenché après le chargement de toutes les images! ainsi, lors de 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...

27
Mustafah

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);
});
20
Sky Yip

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

Chargement d'une texture ( code source , démo )

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 plusieurs textures ( code source , démo )

Dans cet exemple, les textures sont chargées dans le constructeur du maillage. Plusieurs textures sont chargées à l'aide de Promises .

Extrait ( Globe.js ):

Créez un nouveau conteneur en utilisant Object3D pour avoir deux mailles 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 la url d'un fichier de texture et val pour stocker la valeur d'un objet Three.js texture .

  // 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 de la carte appelé textures Poussez une nouvelle promesse dans le tableau texturePromises, chaque promesse appellera loader.load. Si la valeur de entry.val est un objet THREE.Texture valide, résolvez 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. Cela oblige le navigateur à attendre que toutes les promesses soient résolues, ce qui nous permet de 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 nuage, 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;
6
Marcs

Sans traitement d'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 traitement 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/

0
Hitesh Sahu