web-dev-qa-db-fra.com

Comment créer un écran de chargement dans three.js?

J'ai une grande quantité de textures et de modèles à charger dans mon projet. J'essaie d'afficher une barre de progression pendant que tout se charge. Je pense que le LoadingManager fait exactement ce dont j'ai besoin car il suit la progression de tous les actifs chargés.

J'utilise JSONLoader et TextureLoader.

Si quelqu'un pouvait me montrer comment implémenter cela dans l'exemple de code, ce serait génial.

enter image description here

var camera, scene, renderer;

 init();
 animate();

 function init() {

   camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000);
   camera.position.z = 400;

   scene = new THREE.Scene();


   // Load Textures 
   var modeltexture1 = new THREE.TextureLoader().load('modeltexture1.jpg');
   var modeltexture2 = new THREE.TextureLoader().load('modeltexture2.jpg');
   var modeltexture3 = new THREE.TextureLoader().load('modeltexture3.jpg');
   var modeltexture4 = new THREE.TextureLoader().load('modeltexture4.jpg');
  
   // Materials
   var material = {
     "texture1": new THREE.MeshPhongMaterial({ map: modeltexture1 }),
     "texture2": new THREE.MeshPhongMaterial({ map: modeltexture2 }),
     "texture3": new THREE.MeshPhongMaterial({ map: modeltexture3 }),
     "texture4": new THREE.MeshPhongMaterial({ map: modeltexture4 }),
   }

   // Lights
   scene.add(new THREE.AmbientLight(0xcccccc));
   pointLight = new THREE.PointLight(0xff4400, 5, 30);
   pointLight.position.set(5, 0, 0);
   scene.add(pointLight);
  
   var loader = new THREE.JSONLoader();


   // Model1
   var model1 = new THREE.Object3D;
   scene.add(model1);

   loader.load("model1.js", function(geometry) {
     var mainmodel1 = new THREE.Mesh(geometry, material["texture1"]);
     model1.add(mainmodel1);
   }); 
   
   // Model2
   var model2 = new THREE.Object3D;
   scene.add(model2);

   loader.load("model2.js", function(geometry) {
     var mainmodel2 = new THREE.Mesh(geometry, material["texture2"]);
     model2.add(mainmodel2);
   });
   
   // Model3
   var model3 = new THREE.Object3D;
   scene.add(model3);

   loader.load("model3.js", function(geometry) {
     var mainmodel3 = new THREE.Mesh(geometry, material["texture3"]);
     model3.add(mainmodel3);
   });   
   
   // Model4
   var model4 = new THREE.Object3D;
   scene.add(model4);

   loader.load("model4.js", function(geometry) {
     var mainmodel4 = new THREE.Mesh(geometry, material["texture4"]);
     model4.add(mainmodel4);
   });  
   
   renderer = new THREE.WebGLRenderer();
   renderer.setPixelRatio(window.devicePixelRatio);
   renderer.setSize(window.innerWidth, window.innerHeight);
   document.body.appendChild(renderer.domElement);

   //

   window.addEventListener('resize', onWindowResize, false);

 }

 function onWindowResize() {

   camera.aspect = window.innerWidth / window.innerHeight;
   camera.updateProjectionMatrix();

   renderer.setSize(window.innerWidth, window.innerHeight);

 }

 function animate() {

   requestAnimationFrame(animate);
   renderer.render(scene, camera);

 }
body {
  margin: 0;
}
canvas {
  width: 100%;
  height: 100%
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r74/three.min.js"></script>
16
Alexander Hein

Mise à jour de LoadingManager

comme le souligne @ 2pha, ce qui suit est inutile car TROIS implémente déjà un gestionnaire de chargement - ce qui suit restera pour la postérité, mais jetez un œil à ceci:

https://threejs.org/docs/?q=loading#api/loaders/managers/LoadingManager

Peasy facile:

var progress = document.createElement('div');
var progressBar = document.createElement('div');

progress.appendChild(progressBar);

document.body.appendChild(progress);

var manager = new THREE.LoadingManager();
manager.onProgress = function ( item, loaded, total ) {
  progressBar.style.width = (loaded / total * 100) + '%';
};

function addRandomPlaceHoldItImage(){
  var r = Math.round(Math.random() * 4000);
  new THREE.ImageLoader(manager).load('http://placehold.it/' + r + 'x' + r);
}

for(var i = 0; i < 10; i++) addRandomPlaceHoldItImage();
div {
  width: 200px;
  height: 20px;
  background: #000;
  border: 2px solid #000;
}
div > div {
  width: 200px;
  height: 20px;
  background: red;
  border: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r74/three.min.js"></script>

Message d'origine

Quelque chose comme ci-dessous devrait faire l'affaire - sachez que je viens de le tester rapidement et qu'il y a probablement quelques améliorations à apporter (y compris l'utilisation du rappel progress dans TROIS js). Je ne l'ai pas entièrement affiné non plus, mais cela vous donne une mise en œuvre générale de la façon de procéder. Je n'ai pas non plus implémenté de gestionnaire d'erreurs, etc ... Ce sont des choses qui prennent un peu plus de temps à faire, mais vous avez l'idée générale.

Vous ajoutez de nouveaux types de chargeurs (conformes au type + Loader sur TROIS) et leurs sources, puis lancer le chargement en appelant simplement load.

J'espère que ça aide.

(J'ai également créé une fonction aléatoire pour obtenir des images aléatoires de placehold.it puisque l'utilisation répétée des mêmes résultats signifie que la mise en cache tue le serveur.)

function LoaderProgress(callback){
  this.length     = -1;
  this.complete   = 0;
  this.domElement = document.createElement('div');
  this.progress   = document.createElement('div');
  
  this.domElement.appendChild(this.progress);
  this.domElement.className = 'loader-progress-wrapper';
  this.progress.className   = 'loader-progress-progress';
  this.progress.style.width = '0%';
  
  this.callback = callback || function(){};
}
LoaderProgress.prototype.add = function(type, src){
  var result = this[++this.length] = {
    loader: new THREE[type + 'Loader'],
    complete: false,
    source: src
  };
  return result;
}
LoaderProgress.prototype.load = function(){
  this.complete = 0;
  for(var i = 0; i < this.length; i++){
    var current = this[i];
    if(!current.complete){
      current.loader.load(this[i].source, function(result){
        current.complete = result;
        this.complete++;
        this.update();
      }.bind(this));
    }
  }
  return this;
}
LoaderProgress.prototype.update = function(a){
  var progress = this.complete / this.length;
  this.progress.style.width = (progress * 100) + '%';
  if(progress === 1) this.callback(this);
  console.log(progress);
  return progress;
}

var loader = new LoaderProgress(function(){
  // Execute code that needfs the loaded elements here
  alert('All are loaded');
});

document.body.appendChild(loader.domElement);

function addRandomPlaceHoldItImage(){
  var r = Math.round(Math.random() * 4000);
  loader.add('Image', 'http://placehold.it/' + r + 'x' + r);
}

for(var i = 0; i < 10; i++)
  addRandomPlaceHoldItImage();

loader.load();
.loader-progress-wrapper {
  width: 200px;
  height: 20px;
  background: #000;
  border: 2px solid #000;
}
.loader-progress-progress {
  width: 200px;
  height: 20px;
  background: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r74/three.min.js"></script>
16
somethinghere

Documents: http://threejs.org/docs/#Reference/Loaders/TextureLoader

Exemple: http://threejs.org/examples/#webgl_loader_obj

Code:

  var sphereMaterial = new THREE.MeshBasicMaterial();

  var onProgress = function ( xhr ) {
    if ( xhr.lengthComputable ) {
      var percentComplete = xhr.loaded / xhr.total * 100;
      console.log( Math.round(percentComplete, 2) + '% downloaded' );
    }
  };

  var loader = new THREE.TextureLoader();
  var texture = loader.load("___4MB_IMAGE.JPG___", undefined, onProgress);
  sphereMaterial.map = texture;

Il résout un problème similaire que j'avais - une texture de 4 Mo qui prend un certain temps à charger sur le fil ...

4
Mars Robertson